Windows 读取wav文件字节流并播放
创始人
2024-11-12 14:08:10

Windows 读取wav文件字节流并播放

使用Windows Wave相关API播放wav文件,实现文件读取进内存,按照一定字节数播放,
对wav文件音频格式进行检测,只能播放48kHz采样率,16bit位深,单通道格式的音频文件。

 * @brief   wav文件读取解析和使用Windows api输出  * @date    2024-08-02  * @author  shentujia@qq.com  */  #include  #include  #include  #include  #include "WAVHeader.h" #include #include  #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::vectorchar_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:" <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; 	} } 

相关内容

热门资讯

裸辞做“一人公司”,我后悔了 去年这个时候,一位以色列程序员正在东南亚旅行。他顺手把一个在脑子里转了很久的想法做成了产品,一个让任...
南京建成国内首个Pre-6G试... 4月21日,2026全球6G技术与产业生态大会在南京开幕。全息互动技术展台前,一名远在北京的工作人员...
超梵求职受邀参加“2025抖音... 超梵求职受邀参加“2025抖音巨量引擎成人教育行业生态大会”,探讨分享优质内容传播,服务万千学员。 ...
摩托罗拉Razr 2026(R... IT之家 4 月 22 日消息,摩托罗拉宣布新一代 Razr 折叠手机将于 4 月 29 日在美国发...
库克卸任,特纳斯领航:苹果新纪... 苹果首席执行官蒂姆·库克将卸任,硬件工程主管约翰·特纳斯将接任,苹果公司今天宣布此事。 库克将在夏季...