一、下载及编译
下载源码
wget http://www.mega-nerd.com/libsndfile/files/libsndfile-1.0.31.tar.gz
tar -xvf libsndfile-1.0.31.tar.gz
cd libsndfile-1.0.31
配置、安装
./configure
mkdir build
cd build
cmake ..
make
make install
二、从本地打开wav文件并读取pcm数据
#include <iostream>
#include <sndfile.h>
int main() {
const char* filename = "example.wav"; // WAV 文件的路径
// 打开 WAV 文件
SF_INFO sfinfo;
SNDFILE* sndfile = sf_open(filename, SFM_READ, &sfinfo);
if (!sndfile) {
std::cerr << "Error: Could not open file: " << sf_strerror(sndfile) << std::endl;
return 1;
}
// 输出文件信息
std::cout << "Sample rate: " << sfinfo.samplerate << std::endl;
std::cout << "Channels: " << sfinfo.channels << std::endl;
std::cout << "Frames: " << sfinfo.frames << std::endl;
std::cout << "Format: " << sfinfo.format << std::endl;
// 分配缓冲区以读取 PCM 数据
std::vector<short> buffer(sfinfo.frames * sfinfo.channels);
// 读取 PCM 数据
sf_count_t numFramesRead = sf_readf_short(sndfile, buffer.data(), sfinfo.frames);
if (numFramesRead != sfinfo.frames) {
std::cerr << "Error: Could not read all frames." << std::endl;
sf_close(sndfile);
return 1;
}
// 处理 PCM 数据(此处只是简单地输出前 10 个样本)
for (size_t i = 0; i < 10 && i < buffer.size(); ++i) {
std::cout << buffer[i] << " ";
}
std::cout << std::endl;
// 关闭文件
sf_close(sndfile);
return 0;
}
这段代码的主要步骤包括:
- 打开 WAV 文件。
- 获取并输出文件的基本信息(采样率、通道数、帧数、格式)。
- 分配一个缓冲区来存储 PCM 数据。
- 读取 PCM 数据到缓冲区。
- 输出前 10 个 PCM 样本作为示例。
- 关闭文件。
三、从缓存中读取pcm数据
#include <iostream>
#include <vector>
#include <curl/curl.h>
#include <sndfile.h>
// 用于 libcurl 回调函数,将数据写入到内存缓冲区
size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
((std::vector<char>*)userp)->insert(((std::vector<char>*)userp)->end(), (char*)contents, (char*)contents + size * nmemb);
return size * nmemb;
}
// 自定义 sf_vio 结构的回调函数
sf_count_t get_filelen(void* user_data) {
std::vector<char>* data = (std::vector<char>*)user_data;
return data->size();
}
sf_count_t seek(sf_count_t offset, int whence, void* user_data) {
std::vector<char>* data = (std::vector<char>*)user_data;
static sf_count_t position = 0;
switch (whence) {
case SEEK_SET: position = offset; break;
case SEEK_CUR: position += offset; break;
case SEEK_END: position = data->size() + offset; break;
default: return -1;
}
return position;
}
sf_count_t read(void* ptr, sf_count_t count, void* user_data) {
std::vector<char>* data = (std::vector<char>*)user_data;
static sf_count_t position = 0;
if (position + count > data->size()) {
count = data->size() - position;
}
std::memcpy(ptr, data->data() + position, count);
position += count;
return count;
}
sf_count_t tell(void* user_data) {
std::vector<char>* data = (std::vector<char>*)user_data;
static sf_count_t position = 0;
return position;
}
int main() {
// 下载 WAV 文件到内存缓冲区
const char* url = "http://example.com/example.wav";
CURL* curl;
CURLcode res;
std::vector<char> wavData;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &wavData);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
return 1;
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
// 定义 sf_vio 结构
SF_VIRTUAL_IO sfvirtual;
sfvirtual.get_filelen = get_filelen;
sfvirtual.seek = seek;
sfvirtual.read = read;
sfvirtual.write = nullptr; // 不需要写操作
sfvirtual.tell = tell;
// 使用 sf_open_virtual 打开内存缓冲区中的 WAV 数据
SF_INFO sfinfo;
SNDFILE* sndfile = sf_open_virtual(&sfvirtual, SFM_READ, &sfinfo, &wavData);
if (!sndfile) {
std::cerr << "Error: Could not open virtual file: " << sf_strerror(sndfile) << std::endl;
return 1;
}
// 输出文件信息
std::cout << "Sample rate: " << sfinfo.samplerate << std::endl;
std::cout << "Channels: " << sfinfo.channels << std::endl;
std::cout << "Frames: " << sfinfo.frames << std::endl;
std::cout << "Format: " << sfinfo.format << std::endl;
// 分配缓冲区以读取 PCM 数据
std::vector<short> buffer(sfinfo.frames * sfinfo.channels);
// 读取 PCM 数据
sf_count_t numFramesRead = sf_readf_short(sndfile, buffer.data(), sfinfo.frames);
if (numFramesRead != sfinfo.frames) {
std::cerr << "Error: Could not read all frames." << std::endl;
sf_close(sndfile);
return 1;
}
// 处理 PCM 数据(此处只是简单地输出前 10 个样本)
for (size_t i = 0; i < 10 && i < buffer.size(); ++i) {
std::cout << buffer[i] << " ";
}
std::cout << std::endl;
// 关闭文件
sf_close(sndfile);
return 0;
}
这个示例包括以下步骤:
- 使用
libcurl
从 HTTP 下载 WAV 文件到内存缓冲区wavData
中。 - 实现
sf_vio
结构的回调函数,用于从内存缓冲区读取数据。 - 使用
sf_open_virtual
函数打开内存缓冲区中的 WAV 数据。 - 读取 WAV 文件的信息并输出。
- 分配缓冲区并读取 PCM 数据。
- 处理读取到的 PCM 数据(示例中为输出前 10 个样本)。
- 关闭文件。