一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

用Direct Sound為MP3解碼器libmad播放輸出

 啟蒙彩魂 2010-12-30
用Direct Sound為MP3解碼器libmad播放輸出 收藏
WIN32平臺下madplay默認(rèn)采用WaveOut播放輸出,本文實(shí)現(xiàn)為其增加Direct Sound輸出。在Windows XP,VC++ 6.0測試通過。
(一)優(yōu)秀的MP3解碼器libmad簡介
libmad是跨平臺的基于命令行的MP3播放解碼器,使用定點(diǎn)解碼,可用于沒有浮點(diǎn)運(yùn)算的嵌入式系統(tǒng)。
(二)下載及測試
1、從官方網(wǎng)站ftp://ftp.mars.org/pub/mpeg/下載madplay-0.15.2b.tar.gz,libmad-0.15.1b.tar.gz,libid3tag-0.15.1b.tar.gz,還要從ftp://ftp.mars.org/pub/mpeg/extra/下載libz-1.1.4.tar.gz,共四個(gè)文件。將四個(gè)壓縮包保存在同一目錄,例如d:\madplay。
2、用WINRAR“解碼壓到當(dāng)前目錄”方法將四個(gè)壓縮包解壓,在d:\madplay下將有四個(gè)文件夾:madplay-0.15.2b,libmad-0.15.1b,libid3tag-0.15.1b,libz-1.1.4。
3、用VC++ 6.0打開D:\madplay\madplay-0.15.2b\msvc++下的madplay工程。修改VC++ 6.0的編譯環(huán)境:Tools -> Options -> Directories標(biāo)簽 -> Directories框下增加如下兩行:
D:\MADPLAY\LIBMAD-0.15.1B\MSVC++
D:\MADPLAY\LIBID3TAG-0.15.1B
點(diǎn)擊OK保存設(shè)置退出。再為編譯連接后的可執(zhí)行文件指定運(yùn)行參數(shù):Project -> settings -> 點(diǎn)中左側(cè)madplay -> 右側(cè)Program arguments框內(nèi)填入你硬盤上保存的一個(gè)MP3文件,例如:D:\MP3\test.mp3,單擊OK保存設(shè)置退出。
按Ctrl+F5編譯連接運(yùn)行,就可以聽到播放你選擇的MP3歌曲了。
(三)為madplay增加Direct Sound輸出
madplay采用WaveOut作為播放輸出,用DS輸出的優(yōu)點(diǎn)不再我說了吧。
1、在audio.h中查找到“audio_ctlfunc_t audio_win32;”這一行,在下面增加一行:
audio_ctlfunc_t audio_dsound;
2、在config.h中找到“#define AUDIO_DEFAULT audio_win32”這一行,將這一行注釋掉,然后在下面增加一行:
#define AUDIO_DEFAULT audio_dsound
3、在工作區(qū)中選擇FileView標(biāo)簽,點(diǎn)擊madplay files下的Source Files,添加文件aduio_dsound.c
我寫的aduio_dsound.c的內(nèi)容如下,添加了比較詳細(xì)的注釋。
 
/*
aduio_dsound.c
功能: 用 Direct Sound 為MP3解碼器libmad播放輸出.兼容 VC++ 6.0 的directx 7.0
接口函數(shù): audio_dsound()
email: ly2697@sina.com (請注明主題 mp3 decoder)
2008.08
*/
/*
libmad解碼器頭文件
*/
#include "audio.h"
/*
Direct Sound 的頭文件和庫
*/
#include<dsound.h>
#pragma comment( lib, "dsound.lib" )
/*
高精定時(shí)器的頭文件和庫
*/
#include<mmsystem.h>
#pragma comment( lib, "winmm.lib")
/*
DS_N: 緩沖區(qū)塊數(shù),設(shè)為8塊.
DS_ONEBUF: 一塊的長度.設(shè)解碼16幀的PCM數(shù)據(jù)長度,4608*16=73728(72KB).
DS_BUFSIZE: Direct sound緩沖區(qū)長度,DS_N*DS_ONEBUF=589824(576KB).
            最小4,最大0xfffffff,在dsound.h中由DSBSIZE_MIN和DSBSIZE_MAX定義.
每一幀的時(shí)長26ms,一塊的時(shí)間長度為26*16=416ms;緩沖區(qū)總時(shí)間長度8*416=3328ms.
*/
#define DS_N   8
#define DS_ONEBUF  73728
#define DS_BUFSIZE  589824
/*
pcm_data[] -- 暫存寫入到時(shí)DSound Buffer的數(shù)據(jù),該數(shù)據(jù)是由audio_pcm()解碼MP3幀得
到的PCM數(shù)據(jù),不同的解碼器(如mpg123,libmad等)解碼一幀的函數(shù)都采用向外部提供的接收
緩沖區(qū)寫入解碼得到的PCM數(shù)據(jù),將本模塊略作改寫可用于不同的MP3解碼器播放輸出.
*/
static unsigned char pcm_data[DS_ONEBUF+4608];
static int pcm_length=0;
static audio_pcmfunc_t *audio_pcm;
static LPDIRECTSOUND   ds_lpDS=0;
static LPDIRECTSOUNDBUFFER  ds_lpDSB=0;
static HWND   ds_hWndMain=0;
static HANDLE  ds_hSemaphoreNotify=0;
static DWORD  ds_dwWriteCursor=0;
static DWORD  ds_dwPlayCursorOld=0;
static MMRESULT  ds_timerID=0;
static int   ds_iWriteTimes=0;
//#define DBGOUTPUT
#ifdef DBGOUTPUT
  #include <stdio.h>
#endif
/*
DirectSoundBuffer 通知機(jī)制引發(fā)的問題:
若采用DirectSoundBuffer通知機(jī)制,發(fā)現(xiàn)運(yùn)行其它使用相同通知機(jī)制的播放程序時(shí),會造成
本程序的通知事件誤觸發(fā),如何識別是本程序還是其它程序引發(fā)的通知事件? 如何解決?
本程序采用定時(shí)器回調(diào)函數(shù)定時(shí)判斷播放位置、發(fā)信號(設(shè)置通知事件).
ds_dwWriteCursor: 寫入光標(biāo),指向播放一塊后釋放的一個(gè)空閑塊首址,跟隨于播放光標(biāo)后.
ds_dwPlayCursor: 播放光標(biāo),取值(0..n)*DS_ONEBUF
ds_dwPlayCursorOld: 暫存播放光標(biāo)先前值,取值(0..n-1)*DS_ONEBUF, 若使用寫入光標(biāo),
     由于本函數(shù)中“讀”與audio_play()中“寫”不同步而出錯(cuò).
*/
/*
MyPlayPositionNotify()  -- 定時(shí)器回調(diào)函數(shù),播放完一塊發(fā)一次信號.
*/
static void CALLBACK
MyPlayPositionNotify(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
 DWORD dwPlayCursor=0;
 ds_lpDSB->lpVtbl->GetCurrentPosition(ds_lpDSB,&dwPlayCursor, NULL);
 if(dwPlayCursor>ds_dwPlayCursorOld+DS_ONEBUF ||
  (dwPlayCursor<DS_ONEBUF && ds_dwPlayCursorOld==DS_BUFSIZE-DS_ONEBUF)) {
#ifdef DBGOUTPUT
  printf("\nWriteCursor = %6d, PlayCursor = %6d, PlayCursorOld = %6d",
    ds_dwWriteCursor, dwPlayCursor, ds_dwPlayCursorOld);
#endif
  dwPlayCursor /= DS_ONEBUF;
  ds_dwPlayCursorOld = dwPlayCursor*DS_ONEBUF;
  /* 發(fā)信號,使audio_play()內(nèi)的等待函數(shù)返回. */
  ReleaseSemaphore(ds_hSemaphoreNotify,1,NULL);
 }
}
static int audio_open(int nChannels,long nSamplesPerSec,unsigned int depth)
{
 DSBUFFERDESC dsbd;
 WAVEFORMATEX wvOut;
 ds_dwWriteCursor=0;
 ds_dwPlayCursorOld=0;
 ds_iWriteTimes=0;
 if(nSamplesPerSec == -1)
  return -1;
 
 /* Create DirectSound */
 if ( DirectSoundCreate(NULL, &ds_lpDS, NULL) != DS_OK ) {
  return -1;
 }
 if( ds_hWndMain == 0 )
  ds_hWndMain=GetDesktopWindow();
 /* Set Cooperative Level */
 if ( ds_lpDS->lpVtbl->SetCooperativeLevel(ds_lpDS,ds_hWndMain, DSSCL_PRIORITY) != DS_OK ) {
  return -1;
 }
 wvOut.wFormatTag      = WAVE_FORMAT_PCM;
 wvOut.wBitsPerSample  = depth;
 wvOut.nChannels       = nChannels;
 wvOut.nSamplesPerSec  = nSamplesPerSec;
 wvOut.nAvgBytesPerSec = wvOut.nSamplesPerSec * wvOut.nChannels * wvOut.wBitsPerSample/8;
 wvOut.nBlockAlign     = wvOut.nChannels * wvOut.wBitsPerSample/8;
 /* Create Secondary Sound Buffer */
 
 ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
 dsbd.dwSize   = sizeof(DSBUFFERDESC);
 dsbd.dwFlags  = DSBCAPS_GLOBALFOCUS
      | DSBCAPS_CTRLVOLUME
      | DSBCAPS_GETCURRENTPOSITION2;
 dsbd.dwBufferBytes = DS_BUFSIZE;
 dsbd.lpwfxFormat = &wvOut;
 if ( ds_lpDS->lpVtbl->CreateSoundBuffer(ds_lpDS,&dsbd, &ds_lpDSB, NULL) != DS_OK ) {
  return -1;
 }
 ds_hSemaphoreNotify = CreateSemaphore(NULL,DS_N,DS_N,NULL);
 if( ds_hSemaphoreNotify == NULL )
  return -1;
 return 0;
}
/*
audio_play() -- 播放一塊,每解碼一幀被調(diào)用一次
返回值: 寫入到 SoundBuffer 的字節(jié)數(shù),失敗返回0
*/
static int audio_play(struct audio_play *play)
{
 LPVOID lpDSBuf;
 DWORD dwDSLockSize;
 UINT len=0;
 HRESULT hr;
 /* 1. 調(diào)用audio_pcm()解碼一幀,得到的PCM寫入到暫存區(qū)audio_pcm[] */
 len = audio_pcm(pcm_data+pcm_length, play->nsamples,play->samples[0],
     play->samples[1], play->mode, play->stats);
 pcm_length += len;
 if (pcm_length < DS_ONEBUF)
  return 0;
 /* 2. 若audio_pcm[]已寫滿DS_ONEBUF字節(jié),等待播放完一塊后釋放一個(gè)空閑塊 */
 WaitForSingleObject(ds_hSemaphoreNotify,INFINITE);
 /* 3. 將audio_pcm[]寫入到 SoundBuffer,使完成播放一塊 */
 hr = ds_lpDSB->lpVtbl->Lock(ds_lpDSB,ds_dwWriteCursor,pcm_length,&lpDSBuf,&dwDSLockSize,NULL,0,0);
 if(hr == DSERR_BUFFERLOST) {
  ds_lpDSB->lpVtbl->Restore(ds_lpDSB);
  ds_lpDSB->lpVtbl->Lock(ds_lpDSB,ds_dwWriteCursor,pcm_length,&lpDSBuf,&dwDSLockSize,NULL,0,0);
 }
 if(FAILED(hr))
  return 0;
 CopyMemory(lpDSBuf,pcm_data,dwDSLockSize);
 ds_lpDSB->lpVtbl->Unlock(ds_lpDSB,lpDSBuf,dwDSLockSize,NULL,0);
 /* 4. 更新相關(guān)變量! */
 pcm_length=0;
 ds_dwWriteCursor += dwDSLockSize;
 if( ds_dwWriteCursor >= DS_BUFSIZE)
  ds_dwWriteCursor -= DS_BUFSIZE;
 /* 5. 首次寫滿Direct SoundBuffer后,啟動定時(shí)器,開始播放 */
 if(ds_iWriteTimes < DS_N) {
  ds_iWriteTimes++;
  if(ds_iWriteTimes == DS_N) {
   UINT uRs = DS_ONEBUF / 4608 / 2 * 26;
   ds_timerID = timeSetEvent( uRs/2, uRs, (LPTIMECALLBACK)MyPlayPositionNotify, 0,
            TIME_PERIODIC | TIME_CALLBACK_FUNCTION );
   ds_lpDSB->lpVtbl->SetCurrentPosition(ds_lpDSB,0);
   ds_lpDSB->lpVtbl->Play(ds_lpDSB,0, 0, DSBPLAY_LOOPING);
  }
#ifdef DBGOUTPUT
  printf("\nWriteTimes= %2d, WriteBytes= %d",ds_iWriteTimes,dwDSLockSize);
#endif
 }
 return dwDSLockSize;
}
static int audio_init(struct audio_init *init)
{
  SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
  return 0;
}
static int audio_config(struct audio_config *config)
{
  unsigned int bitdepth;
  bitdepth = config->precision & ~7;
  if (bitdepth == 0)
    bitdepth = 16;
  else if (bitdepth > 32)
    bitdepth = 32;
  audio_close();
  if (audio_open(config->channels,config->speed, bitdepth) == -1)
   return -1;
  switch (config->precision = bitdepth) {
  case 8:
    audio_pcm = audio_pcm_u8;
    break;
  case 16:
    audio_pcm = audio_pcm_s16le;
    break;
  case 24:
    audio_pcm = audio_pcm_s24le;
    break;
  case 32:
    audio_pcm = audio_pcm_s32le;
    break;
  }
  return 0;
}
static int audio_pause(int bPause)
{
 static int paused;
 if(ds_lpDSB == 0)
  return -1;
 if (bPause && !paused) {
  ds_lpDSB->lpVtbl->Stop(ds_lpDSB);
  return 0;
 }
 else if (!bPause && paused) {
  ds_lpDSB->lpVtbl->Play(ds_lpDSB,0, 0, DSBPLAY_LOOPING);
  return 0;
 }
 paused = bPause;
 return 0;
}
static int audio_stop()
{
 if(ds_lpDSB == 0)
  return -1;
 ds_lpDSB->lpVtbl->Stop(ds_lpDSB);
 return 0;
}
/*
audio_flush() -- 播放完Direct Sound緩沖區(qū)ds_lpDSB中的數(shù)據(jù),DS_N-1塊
*/
static void audio_flush()
{
 int i;
 for( i=1; i<DS_N; i++ )
  WaitForSingleObject(ds_hSemaphoreNotify,INFINITE);
}
static int audio_close()
{
 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
 SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
 if (ds_lpDSB != 0) {
  ds_lpDSB->lpVtbl->Stop(ds_lpDSB);
  ds_lpDSB->lpVtbl->Release(ds_lpDSB);
  ds_lpDSB = 0;
 }
 if (ds_timerID != 0) {
  timeKillEvent(ds_timerID);
  ds_timerID = 0;
 }
 if (ds_hSemaphoreNotify != 0) {
  CloseHandle(ds_hSemaphoreNotify);
  ds_hSemaphoreNotify = 0;
 }
 if(ds_lpDS != 0) {
  ds_lpDS->lpVtbl->Release(ds_lpDS);
  ds_lpDS = 0;
 }
 ds_dwWriteCursor = 0;
 ds_dwPlayCursorOld = 0;
 ds_iWriteTimes = 0;
 return 0;
}
int audio_dsound(union audio_control *control)
{
  audio_error = 0;
  switch (control->command) {
  case AUDIO_COMMAND_INIT:
    return audio_init(&control->init);
  case AUDIO_COMMAND_CONFIG:
    return audio_config(&control->config);
  case AUDIO_COMMAND_PLAY:
    return audio_play(&control->play);
  case AUDIO_COMMAND_STOP:
 if (control->stop.flush)
  return audio_stop();
    return audio_pause(1);
  case AUDIO_COMMAND_FINISH:
 audio_flush();
    return audio_close();
  }
  return 0;
}

本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/lfp001/archive/2008/08/03/2762693.aspx

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    在线观看日韩欧美综合黄片| 少妇肥臀一区二区三区| 国产av一区二区三区久久不卡| 亚洲香艳网久久五月婷婷| 中文字幕欧美视频二区| 午夜日韩在线观看视频| 激情亚洲内射一区二区三区| 亚洲一区二区三区熟女少妇| 又大又紧又硬又湿又爽又猛| 亚洲综合伊人五月天中文| 国产欧美一区二区另类精品| 欧美日韩综合在线精品| 色婷婷日本视频在线观看| 一二区不卡不卡在线观看| 在线观看中文字幕91| 精品人妻一区二区三区免费看| 国产av精品高清一区二区三区| 热久久这里只有精品视频| 欧美乱视频一区二区三区| 观看日韩精品在线视频| 加勒比东京热拍拍一区二区| 欧美日韩国内一区二区| 婷婷亚洲综合五月天麻豆| 日系韩系还是欧美久久| 欧美尤物在线视频91| 亚洲日本韩国一区二区三区| 国产自拍欧美日韩在线观看| 久久99精品国产麻豆婷婷洗澡| 精品国产品国语在线不卡| 中文字幕久久精品亚洲乱码| 国产一区二区三中文字幕 | 国产美女精品午夜福利视频| 亚洲一区二区精品国产av| 国产丝袜女优一区二区三区| 日韩午夜老司机免费视频| 色婷婷视频在线精品免费观看| 国产精品福利精品福利| 九九热精彩视频在线免费| 成人综合网视频在线观看| 99久久婷婷国产亚洲综合精品| 日韩中文无线码在线视频|