mp3是流媒体,所以一个完整文件往往比较大而且不能一次装入sound缓存,所以其buffer管理就成了最大难题,至于解码部分其实还是很简单的,下面是仅关于解码部分的说明
首先应该在自己的工程中包含以下三个库:
class Mp3 : public ::my::Thread { protected: static const DWORD MPEG_BUFSZ = 40000; static const DWORD BLOCK_COUNT = 3; protected: t3d::DSoundPtr m_dsound; WAVEFORMATEX m_wavfmt; t3d::DSBufferPtr m_dsbuffer; t3d::DSNotifyPtr m_dsnotify; DSBPOSITIONNOTIFY m_dsnp[BLOCK_COUNT]; Event m_events[BLOCK_COUNT + 1]; IOStreamPtr m_stream; DWORD m_flags; typedef std::vector<unsigned char> FileBuffer; FileBuffer m_buffer; bool m_loop; CriticalSection m_loopLock; void setLoop(bool loop) { CriticalSectionLock lock(m_loopLock); m_loop = loop; } bool getLoop(void) { CriticalSectionLock lock(m_loopLock); return m_loop; } public: Mp3( t3d::DSoundPtr dsound, IOStreamPtr fstream, DWORD flags = DSBCAPS_CTRLVOLUME | DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE); virtual ~Mp3(void); protected: bool playOnce(void); public: void play(bool loop = false); void stop(void); DWORD onProc(void); };
Mp3::Mp3( t3d::DSoundPtr dsound, IOStreamPtr stream, DWORD flags /*= DSBCAPS_CTRLVOLUME | DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE*/) : m_dsound(dsound) , m_stream(stream) , m_flags(flags) , m_buffer(MPEG_BUFSZ / sizeof(m_buffer[0])) { m_wavfmt.wFormatTag = WAVE_FORMAT_PCM; m_wavfmt.nChannels = 0; m_wavfmt.nSamplesPerSec = 0; // 注意:由于优先级关系,设定第一个事件为停止事件,后面依次作为 m_dsnp 的事件 for(int i = 0; i < _countof(m_dsnp); i++) { m_dsnp[i].dwOffset = 0; m_dsnp[i].hEventNotify = m_events[i + 1].m_hevent; } } Mp3::~Mp3(void) { if(NULL != m_hThread) { stop(); VERIFY(WaitForThreadStopped(INFINITE)); } } bool Mp3::playOnce(void) { DSBUFFERDESC dsbd; audio_dither left_dither, right_dither; std::vector<unsigned char> sbuffer; m_stream->seek(0, my::IOStream::seek_set); mad_stream stream; mad_frame frame; mad_synth synth; mad_stream_init(&stream); mad_frame_init(&frame); mad_synth_init(&synth); // 初始化所有 notify for(size_t i = 0; i < _countof(m_dsnp); i++) { VERIFY(::ResetEvent(m_dsnp[i].hEventNotify)); } // 设置默认已经开始播放的 block VERIFY(::SetEvent(m_dsnp[0].hEventNotify)); bool ret = false; do { // 从 stream 获得原始声音 buffer size_t remain = 0; if(NULL != stream.next_frame) { remain = &m_buffer[0] + MPEG_BUFSZ - stream.next_frame; memmove(&m_buffer[0], stream.next_frame, remain); } int read = m_stream->read(&m_buffer[0] + remain, sizeof(m_buffer[0]), m_buffer.size() - remain); // 如果已经到达 stream 结尾,则结束 if(0 == read) { if(NULL != m_dsbuffer) { // 等待缓存区播完 _ASSERT(sizeof(m_events) == sizeof(HANDLE) * _countof(m_events)); if(WAIT_OBJECT_0 != ::WaitForMultipleObjects(_countof(m_events), reinterpret_cast<HANDLE *>(m_events), FALSE, INFINITE)) { // 正常播放完成,要求继续播放 ret = true; } m_dsbuffer->stop(); } goto end; } // 如果读出来的 buffer 太小,则将 MAD_BUFFER_GUARD 的剩余 buffer 清零 if(read < MAD_BUFFER_GUARD) { _ASSERT(MPEG_BUFSZ - remain > MAD_BUFFER_GUARD); memset(&m_buffer[remain + read], 0, MAD_BUFFER_GUARD - read); read = MAD_BUFFER_GUARD; } // attach buffer to mad stream mad_stream_buffer(&stream, &m_buffer[0], (remain + read) * sizeof(m_buffer[0])); while(true) { // decode audio frame if(-1 == mad_frame_decode(&frame, &stream)) { if(!MAD_RECOVERABLE(stream.error)) { break; } switch(stream.error) { case MAD_ERROR_BADDATAPTR: continue; case MAD_ERROR_LOSTSYNC: { // excute id3 tag frame skipping unsigned long tagsize = id3_tag_query(stream.this_frame, stream.bufend - stream.this_frame); if(tagsize > 0) { mad_stream_skip(&stream, tagsize); } } continue; default: continue; } } // convert frame data to pcm data mad_synth_frame(&synth, &frame); // parse dither linear pcm data to compatible format audio_stats stats; if(2 == synth.pcm.channels) { register signed int sample0, sample1; for(int i = 0; i < (int)synth.pcm.length; i++) { sample0 = audio_linear_dither(16, synth.pcm.samples[0][i], &left_dither, &stats); sample1 = audio_linear_dither(16, synth.pcm.samples[1][i], &right_dither, &stats); sbuffer.push_back(sample0 >> 0); sbuffer.push_back(sample0 >> 8); sbuffer.push_back(sample1 >> 0); sbuffer.push_back(sample1 >> 8); } } else { register int sample0; for(int i = 0; i < (int)synth.pcm.length; i++) { sample0 = audio_linear_dither(16, synth.pcm.samples[0][i], &left_dither, &stats); sbuffer.push_back(sample0 >> 0); sbuffer.push_back(sample0 >> 8); } } // 必要时创建 dsound buffer if(m_wavfmt.nChannels != synth.pcm.channels || m_wavfmt.nSamplesPerSec != synth.pcm.samplerate) { // dsound buffer 应该只被创建一次 _ASSERT(NULL == m_dsnotify); _ASSERT(NULL == m_dsbuffer); _ASSERT(WAVE_FORMAT_PCM == m_wavfmt.wFormatTag); m_wavfmt.nChannels = synth.pcm.channels; m_wavfmt.nSamplesPerSec = synth.pcm.samplerate; m_wavfmt.wBitsPerSample = 16; m_wavfmt.nBlockAlign = m_wavfmt.nChannels * m_wavfmt.wBitsPerSample / 8; m_wavfmt.nAvgBytesPerSec = m_wavfmt.nSamplesPerSec * m_wavfmt.nBlockAlign; m_wavfmt.cbSize = 0; dsbd.dwSize = sizeof(dsbd); dsbd.dwFlags = m_flags | DSBCAPS_CTRLPOSITIONNOTIFY; dsbd.dwBufferBytes = m_wavfmt.nAvgBytesPerSec * BLOCK_COUNT; dsbd.dwReserved = 0; dsbd.lpwfxFormat = &m_wavfmt; dsbd.guid3DAlgorithm = DS3DALG_DEFAULT; // 重新计算每个块的播放 position for(int i = 0; i < _countof(m_dsnp); i++) { m_dsnp[i].dwOffset = i * m_wavfmt.nAvgBytesPerSec; } // 创建 dsound buffer 及 dsound notify m_dsbuffer = m_dsound->createSoundBuffer(&dsbd); m_dsnotify = m_dsbuffer->getDSNotify(); m_dsnotify->setNotificationPositions(_countof(m_dsnp), m_dsnp); } // fill pcm data to dsbuffer _ASSERT(NULL != m_dsbuffer); if(sbuffer.size() > m_wavfmt.nAvgBytesPerSec) { // 等待所有事件处理 _ASSERT(sizeof(m_events) == sizeof(HANDLE) * _countof(m_events)); DWORD wait_res = ::WaitForMultipleObjects(_countof(m_events), reinterpret_cast<HANDLE *>(m_events), FALSE, INFINITE); _ASSERT(WAIT_TIMEOUT != wait_res); if(wait_res == WAIT_OBJECT_0) { // 是停止事件,则直接 out m_dsbuffer->stop(); goto end; } // 计算当前 block DWORD curr_block = wait_res - WAIT_OBJECT_0 - 1; _ASSERT(curr_block < _countof(m_dsnp)); // 计算需要更新 block(curr_block + 1) DWORD next_block = (curr_block + 1) % _countof(m_dsnp); // 拷贝数据缓存 unsigned char * audioPtr1, * audioPtr2; DWORD audioBytes1, audioBytes2; m_dsbuffer->lock(m_dsnp[next_block].dwOffset, m_wavfmt.nAvgBytesPerSec, (LPVOID *)&audioPtr1, &audioBytes1, (LPVOID *)&audioPtr2, &audioBytes2, 0); _ASSERT(audioBytes1 + audioBytes2 <= m_wavfmt.nAvgBytesPerSec); if(audioPtr1 != NULL) { memcpy(audioPtr1, &sbuffer[0], audioBytes1); } if(audioPtr2 != NULL) { memcpy(audioPtr2, &sbuffer[0 + audioBytes1], audioBytes2); } m_dsbuffer->unlock(audioPtr1, audioBytes1, audioPtr2, audioBytes2); // 开始播放 if(!m_dsbuffer->isPlaying()) { // 重新设置当前初播放位置 m_dsbuffer->setCurrentPosition(m_dsnp[next_block].dwOffset); m_dsbuffer->play(0, DSBPLAY_LOOPING); } // 将剩余 buffer 移动到 buffer 头 size_t remain = sbuffer.size() - m_wavfmt.nAvgBytesPerSec; memmove(&sbuffer[0], &sbuffer[m_wavfmt.nAvgBytesPerSec], remain); sbuffer.resize(remain); } } } while(stream.error == MAD_ERROR_BUFLEN); _ASSERT(false); end: mad_synth_finish(&synth); mad_frame_finish(&frame); mad_stream_finish(&stream); return ret; } void Mp3::play(bool loop /*= false*/) { if(NULL != m_hThread) { if(!WaitForThreadStopped(0)) { return; } VERIFY(::CloseHandle(m_hThread)); m_hThread = NULL; } setLoop(loop); m_events[0].ResetEvent(); CreateThread(); ResumeThread(); } void Mp3::stop(void) { m_events[0].SetEvent(); } DWORD Mp3::onProc(void) { try { do { } while(playOnce() && getLoop()); } catch(t3d::Exception & /*e*/) { //::my::Game::getSingleton().m_pwnd->sendMessage(WM_USER + 0, (WPARAM)&e); } return 0; }