阅读量:0
Windows 读取wav文件字节流并播放
使用Windows Wave相关API播放wav文件,实现文件读取进内存,按照一定字节数播放,
对wav文件音频格式进行检测,只能播放48kHz采样率,16bit位深,单通道格式的音频文件。
* @brief wav文件读取解析和使用Windows api输出 * @date 2024-08-02 * @author shentujia@qq.com */ #include <iostream> #include <fstream> #include <string> #include <vector> #include "WAVHeader.h" #include<Windows.h> #include <MMSystem.h> #pragma comment(lib, "winmm.lib") // 48k 16bit 1channels 音频采样100ms数据大小为9600字节 #define WAVE_BUFFER_SIZE 9600 using namespace std; HWAVEOUT hWaveOut; // waveOut设备句柄 WAVEHDR waveOutHdr; // waveOut数据块头 int main() { string audio_file = "rain_48khz_1ch_16bit.wav"; ifstream fin(audio_file, ios::binary); if (!fin) { cout << "open file failed!" << endl; return 1; } WAVHeader header; //读取wav文件头并保存到header对象中 fin.read((char*)&header, sizeof(header)); if (strncmp(header.riff.chunkID, "RIFF", 4) != 0 || strncmp(header.riff.format, "WAVE", 4) != 0 || strncmp(header.fmt.chunkID, "fmt ", 4) != 0 || strncmp(header.data.chunkID, "data", 4) != 0) { cout << "file is not a valid WAV file" << endl; return 1; } //判断音频文件是否为16bit 1channels 采样率为48000的音频文件 if(header.fmt.numChannels != 1 || header.fmt.bitsPerSample != 16||header.fmt.sampleRate!=48000){ cout << "only support 8bit 1channels audio file" << endl; return 1; } WAVEFORMATEX waveFormat; /* WAVEFORMATEX是一种数据结构,用于指定波形音频流的数据格式。它包含以下字段: wFormatTag:设置波形声音的格式。 nChannels:设置音频文件的通道数量,对于单声道的声音,此值为1;对于立体声,此值为2。 nSamplesPerSec:设置每个声道播放和记录时的样本频率。 nAvgBytesPerSec:设置每秒平均字节数。 nBlockAlign:设置数据块的对齐方式,即最小数据的原子大小。 wBitsPerSample:设置每个样本的位数。 cbSize:设置此结构的大小。 */ waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nChannels = header.fmt.numChannels; waveFormat.nSamplesPerSec = header.fmt.sampleRate; waveFormat.nBlockAlign = header.fmt.blickAlign; waveFormat.wBitsPerSample = header.fmt.bitsPerSample; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.wBitsPerSample / 8; waveFormat.cbSize = 0; if (waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, (DWORD_PTR)0, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { cout << "无法打开音频设备" << endl; return 1; } char* pcmData = new char[header.data.chunkSize]; //读取wav文件的pcm数据部分,保存到char 数组中 fin.read(pcmData, header.data.chunkSize); // 计算1毫秒内的样本字节数 int bytesPerMs = (header.fmt.sampleRate / 1000) * (header.fmt.bitsPerSample / 8) * header.fmt.numChannels; WAVEHDR* waveHdr = new WAVEHDR(); std::vector<char*>char_points; for (int i = 0; i < header.data.chunkSize; i += WAVE_BUFFER_SIZE) { int buffersize = min(WAVE_BUFFER_SIZE, header.data.chunkSize - i); char* perFrameData = new char[WAVE_BUFFER_SIZE]; char_points.push_back(perFrameData); memcpy(perFrameData, pcmData + i, WAVE_BUFFER_SIZE); cout << "play " << i << " to " << i + WAVE_BUFFER_SIZE << " bytes" <<",total:" <<header.data.chunkSize<< endl; waveHdr->lpData = perFrameData; waveHdr->dwBufferLength = buffersize; waveHdr->dwFlags = 0; waveHdr->dwLoops = 0; if (waveOutPrepareHeader(hWaveOut, waveHdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { cout << "无法准备音频数据" << endl; break; } if (waveOutWrite(hWaveOut, waveHdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { cout << "无法播放音频" << endl; waveOutUnprepareHeader(hWaveOut, waveHdr, sizeof(WAVEHDR)); break; } Sleep(90); } getchar(); fin.close(); waveOutClose(hWaveOut); //使用new创建的对象需要手动delete delete waveHdr; delete[] pcmData; for (auto& p : char_points) { delete[] p; } }