刚好最近接触了一些DirectSound,就写了一个小程序练练手,可以用来添加播放基本的wav和mp3音频文件的播放器。界面只是简单的GDI,dxsdk只使用了DirectSound8相关的接口。
images/loading.gif' data-original="http://images0.cnblogs.com/blog2015/707063/201508/111819168179111.png" />
DirectSound的使用步骤很简单
1 HRESULT DirectSoundCreate8(2 LPCGUID lpcGuidDevice,3 LPDIRECTSOUND8 * ppDS8,4 LPUNKNOWN pUnkOuter5 )
当然要确保已安装了DXSDK,并在工程中设置了相关路径,包含dsound.lib。
lpcGuidDevice是声音设备对象GUID的地址,设置为NULL,表示默认设备;
ppDS8是返回的IDirectSound8接口指针,接下来就通过它来调用相关接口了;
pUnkOuter必须设置为NULL。
1 HRESULT SetCooperativeLevel(2 HWND hwnd,3 DWORD dwLevel4 )
hwnd是应用程序窗口句柄;
dwLevel是请求的协作级别,可选的值包括
DSSCL_NORMAL:正常级别,其他程序可共享设备;
DSSCL_PRIORITY:优先级别,设备为当前程序独占;
DSSCL_EXCLUSIVE:对于DX8.0及之后的版本,具有和DSSCL_PRIORITY相同的效果;
DSSCL_WRITEPRIMARY:当前程序具有主缓冲区的写权限,同时副缓冲区不能进行播放。
1 HRESULT CreateSoundBuffer(2 LPCDSBUFFERDESC pcDSBufferDesc,3 LPDIRECTSOUNDBUFFER * ppDSBuffer,4 LPUNKNOWN pUnkOuter5 )
pcDSBufferDesc是一个DSBUFFERDESC结构的指针,用来描述要创建的声音缓冲区的地址;
ppDSBuffer是返回的IDirectSoundBuffer接口对象的指针;
pUnkOuter必须设置为NULL。
其中DSBUFFERDESC定义如下
1 typedef struct DSBUFFERDESC {2 DWORD dwSize;3 DWORD dwFlags;4 DWORD dwBufferBytes;5 DWORD dwReserved;6 LPWAVEFORMATEX lpwfxFormat;7 GUID guid3DAlgorithm;8 } DSBUFFERDESC;
dwSize:结构的大小,用字节表示;
dwFlags:指定缓冲区的功能标志,常用的有DSBCAPS_GLOBALFOCUS(缓冲区为全局)、DSBCAPS_CTRLVOLUME(缓冲区具有音量控制功能)、DSBCAPS_CTRLPOSITIONNOTIFY(缓冲区具有位置通知功能)等。
dwBufferBytes:将要创建的缓冲区大小;
dwReserved:保留参数,必须为0;
lpwfxFormat:指向一个WAVEFORMATEX结构的指针,该结构用来指定缓冲区的波形格式。
通过使用IDirectSoundBuffer的QueryInterface方法,GUID设置为IID_IDirectSoundBuffer8,得到一个IDirectSoundBuffer8接口对象,用来控制播放等。
1 IDirectSoundBuffer8* m_pBuffer8 = NULL;2 ppDSBuffer->QueryInterface( IID_IDirectSoundBuffer8, (void**)&m_pBuffer8 );
到这里创建m_pBuffer8成功的话,就可以调用Play(),Stop()的方法控制基本的播放了。
为了更好的控制缓冲区播放,我们还可以创建缓冲区通知对象,通过IDirectSoundBuffer8的QueryInterface方法,GUID设置为IID_IDirectSoundNotify,得到一个IDirectSoundNotify8接口对象。
1 IDirectSoundNotify8* pNotify = NULL;2 m_pBuffer8->QueryInterface( IID_IDirectSoundNotify,(void**)&pNotify );
接口获取成功后,就可以设置通知位置了,也就是说当缓冲区播放到相应位置时,系统就会触发对应的事件,可用来填充新的数据。
1 HRESULT SetNotificationPositions(2 DWORD dwPositionNotifies,3 LPCDSBPOSITIONNOTIFY pcPositionNotifies4 )
dwPositionNotifies:DSBPOSITIONNOTIFY结构的数量;
pcPositionNotifies:指向DSBPOSITIONNOTIFY结构的指针。
其中DSBPOSITIONNOTIFY结构如下
1 typedef struct DSBPOSITIONNOTIFY {2 DWORD dwOffset;3 HANDLE hEventNotify;4 } DSBPOSITIONNOTIFY;
dwOffset:触发位置,即缓冲区开始起的偏移量;
hEventNotify:触发事件句柄。
另外,在填充缓冲区的操作,必须在IDirectSoundBuffer8的lock和unlock方法之间进行:
1 HRESULT Lock(2 DWORD dwOffset,3 DWORD dwBytes,4 LPVOID * ppvAudioPtr1,5 LPDWORD pdwAudioBytes1,6 LPVOID * ppvAudioPtr2,7 LPDWORD pdwAudioBytes2,8 DWORD dwFlags9 )
dwOffset:要锁定的缓冲区起始位置,即从缓冲区首地址起的偏移量,用字节表示;
dwBytes:希望锁定的缓冲区内存大小,用字节表示;
ppvAudioPtr1:返回指向该锁定缓冲区的第一部分指针;
pdwAudioBytes1:ppvAudioPtr1指向地址的大小;
ppvAudioPtr2:返回指向该锁定缓冲区的第二部分指针,如果传入NULL,则该锁定区域全部返回到ppvAudioPtr1;
pdwAudioBytes2:ppvAudioPtr2指向地址的大小,如果ppvAudioPtr2传入NULL,该值则应传入0;
dwFlags:修改锁定事件的标志,一般不使用设为0。
1 HRESULT Unlock(2 LPVOID pvAudioPtr1,3 DWORD dwAudioBytes1,4 LPVOID pvAudioPtr2,5 DWORD dwAudioBytes26 )
填充完锁定缓冲区内存后,用来取消该锁定区域,参数可以参看lock中的介绍。
至此,IDirectSound8中主要用到的接口就这些了。
使用DirectSound播放PCM的重点就在于解析相应的音频文件格式获取相应信息,来填充WAVEFORMATEX结构。
1 typedef struct tWAVEFORMATEX {2 WORD wFormatTag;3 WORD nChannels;4 DWORD nSamplesPerSec;5 DWORD nAvgBytesPerSec;6 WORD nBlockAlign;7 WORD wBitsPerSample;8 WORD cbSize;9 } WAVEFORMATEX, *PWAVEFORMATEX, *LPWAVEFORMATEX;
下面详细介绍一下此结构:
wFormatTag:波形音频格式类型,在这里都设置为WAVE_FORMAT_PCM;
nChannels:音频声道数;
nSamplesPerSec:采样率;
nAvgBytesPerSec:平均传输率,如果音频格式设置为WAVE_FORMAT_PCM,该值则必须等于nSamplesPerSec和nBlockAlign的乘积;
nBlockAlign:以字节为单位的块对齐,是wFormatTag对应的最小原子单位,如果是WAVE_FORMAT_PCM,该值必须等于nChannels和wBitsPerSample的乘积除以8;
wBitsPerSample:每次采样的比特数,即量化位数;
cbSize:需要附加的额外信息大小,以字节为单位,这里设置为0。
关于DirectSound相关的内容就介绍到这里,接下来就该考虑怎么将wav和mp3文件信息解析并填充WAVEFORMATEX结构了。
1、解析wav文件,这就需要稍微了解一下基本的wav文件格式,wav文件相应的非数据信息存储在文件头部分,常见的几种文件头格式:
8KHz采样、16比特量化的线性PCM语音信号的WAV文件头格式表(共44字节)
偏移地址 | 字节数 | 数据类型 | 内容 | 文件头定义为 |
00H | 4 | Char | "RIFF" | char riff_id[4]="RIFF" |
04H | 4 | long int | 文件总长-8 | long int size0=文总长-8 |
08H | 8 | Char | "WAVEfmt " | char wave_fmt[8] |
10H | 4 | long int | 10 00 00 00H(PCM) | long int size1=0x10 |
14H | 2 | Int | 01 00H | int fmttag=0x01 |
16H | 2 | Int | Int | channel=1 或2 |
18H | 4 | long int | 采样率 | long int samplespersec |
1CH | 4 | long int | 每秒播放字节数 | long int bytepersec |
20H | 2 | int | 采样一次占字节数 | int blockalign=声道数*量化数/8 |
22H | 2 | int | 量化数 | int bitpersamples=8或16 |
24H | 4 | Char | "data" | char data_id="data" |
28H | 4 | long int | 采样数据字节数 | long int size2=文长-44 |
2CH | | | | 到文尾 char 采样数据 |
8KHz采样、8比特A律量化的PCM语音信号的WAV文件头格式表(共58字节)
偏移地址 | 字节数 | 数据类型 | 内容 | 文件头定义为 |
00H | 4 | char | "RIFF" | char riff_id[4]="RIFF" |
04H | 4 | long int | 文件总长-8 | long int size0=文总长-8 |
08H | 8 | char | "WAVEfmt " | char wave_fmt[8] |
10H | 4 | long int | 12000000H(ALAW) | long int size1=0x12 |
14H | 2 | int | 06 00H | int fmttag=0x06 |
16H | 2 | Int | 声道数 | int channel=1 或2 |
18H | 4 | long int | 采样率 | long int samplespersec |
1CH | 4 | long int | 每秒播放字节数 | long int bytepersec |
20H | 2 | int | 采样一次占字节数 | int blockalign=0x01 |
22H | 4 | long int | 量化数 | long int bitpersamples=8 |
26H | 4 | char | "fact" | char wave_fact="fact" |
2AH | 8 | char | 0400000000530700H定 | char temp |
32H | 4 | char | "data" | char wave_data="data" |
36H | 4 | long int | 采样数据字节数 | lont int size2=文长-58 |
只要构建字节对应的结构,然后从wav文件起始位置读取相应长度到结构即可,例如
1 struct WAVE_HEADER //44bytes 2 { 3 char riff_sig[4]; 4 long waveform_chunk_size; 5 char wave_sig[4]; 6 char format_sig[4]; 7 long format_chunk_size; 8 short format_tag; 9 short channels;10 long sample_rate;11 long bytes_per_sec;12 short block_align;13 short bits_per_sample;14 char data_sig[4];15 long data_size;16 };17 18 19 struct WAVE_HEADER_FACT //58bytes20 {21 char riff_sig[4];22 long waveform_chunk_size;23 char wave_sig[4];24 char format_sig[4];25 long format_chunk_size;26 short format_tag;27 short channels;28 long sample_rate;29 long bytes_per_sec;30 short block_align;31 short bits_per_sample;32 short bits_per_sample2;33 char fact_sig[4];34 short fact_size;35 short fact_size2;36 char fact_data[4];37 char data_sig[4];38 long data_size;39 };
这里构建了两种类型的wav格式头,从文件中读取信息到结构,然后直接赋值给WAVEFORMATEX即可
1 WAVEFORMATEX wave_format;2 WAVE_HEADER wave_header;3 fread( &wave_header, 1, sizeof(WAVE_HEADER), fp);4 wave_format.wFormatTag = WAVE_FORMAT_PCM;5 wave_format.nChannels = wave_header.channels;6 wave_format.nSamplesPerSec = wave_header.sample_rate;7 wave_format.wBitsPerSample = wave_header.bits_per_sample;8 wave_format.nBlockAlign = wave_header.bits_per_sample / 8 * wave_header.channels;9 wave_format.nAvgBytesPerSec = wave_header.sample_rate * wave_format.nBlockAlign;
2、解析mp3文件选择使用了libmpg123库中提供的方法,mpg123解码器是全部开源的,可以在http://www.mpg123.de/ 下载获得。
在编译libmpg123的过程中可能会遇到若干问题,这里大致罗列一下,进入ports\MSVC++选择对应的vs版本工程,这里选择了2010版。
由于工程中在预链接事件中使用了yasm汇编器,所以需要下载配置yasm到vs工程中,在http://yasm.tortall.net/Download.html 有32位和64位系统对应的vs2010版本。
下载后解压,根据readme文档添加配置:将yasm.exe放置vs主目录bin中;将yasm.props、yasm.targets、yasm.
选择Debug_X86或Release_x86配置,编译中可能会提示link无法打开输入文件xxx.o,将附加依赖和预链接命令中的.o全部替换为.obj。
将生成的libmpg123.lib添加到工程中,并将ports\MSVC++下的mpg123.h也引入到自己的工程中,接下来就可以使用libmpg123相关的方法来解析mp3文件了。
首先需要创建一个mpg123_handle句柄指针,过程如下
1 mpg123_handle* m_mpghandle = NULL;2 int ret = MPG123_OK;3 if( (ret = mpg123_init()) != MPG123_OK || (m_mpghandle = mpg123_new(NULL, &ret)) == NULL )4 return -1;
之后打开mp3文件并获取相关信息
1 long rate; //频率2 int channels; //声道数3 int encoding; //编码格式4 if( mpg123_open(m_mpghandle, "youfile.mp3") != MPG123_OK || mpg123_getformat(m_mpghandle, &rate, &channels, &encoding) != MPG123_OK )5 return -1;
通过判断encoding来设置量化数,并赋值WAVEFORMATEX结构,代码如下
1 WAVEFORMATEX wave_format; 2 int perbits = 16; //量化数 3 if((encoding & MPG123_ENC_16) == MPG123_ENC_16) 4 perbits = 16; 5 else if((encoding & MPG123_ENC_32) == MPG123_ENC_32) 6 perbits = 32; 7 else 8 perbits = 8; 9 10 wave_format.wFormatTag = WAVE_FORMAT_PCM;11 wave_format.nChannels = channels;12 wave_format.nSamplesPerSec = rate;13 wave_format.wBitsPerSample = perbits;14 wave_format.nBlockAlign = perbits / 8 * channels;15 wave_format.nAvgBytesPerSec = rate * perbits / 8 * channels;
另外再说一下如何获取mp3文件的时长和比特率,mp3文件是由帧所构成的,想要知道播放时长,就可以通过总帧数 * 每一帧的时长来推算获得。
总帧数可通过mpg123_tellframe获取,而每一帧时长 = 每一帧的采样个数 * 每一采样的时长 = 每一帧的采样个数 * (1/每一帧的采样频率),
而无论哪种编码的mp3文件每一帧的采样个数都是1152,所以计算时长代码如下:
1 long frameNum;2 long fileTime;3 mpg123_seek( m_mpghandle, 0, SEEK_END );4 frameNum = mpg123_tellframe( m_mpghandle ); //获取总帧数5 fTime = (long)( frameNum * 1152/ rate );
而计算比特率,需要区别编码格式,如果是CBR,则比特率是固定的;
如果是VBR,由于比特率不固定,所以只能大概取一个平均比特率,计算公式为 平均比特率 = 文件大小(字节)*8 / 总时长 /1000,计算比特率代码如下:
1 mpg123_frameinfo mpginfo; 2 int bitrate; 3 long filesize; 4 FILE* tmpfp = NULL; 5 if( mpg123_info( m_mpghandle, &mpginfo) != MPG123_OK ) 6 return -1; 7 if(mpginfo.layer != 3) 8 return -1; 9 if( mpginfo.vbr == MPG123_CBR )10 bitrate = mpginfo.bitrate;11 else if( mpginfo.vbr == MPG123_VBR )12 {13 fopen_s( &tmpfp, "youfile.mp3", "rb" );14 fseek( tmpfp, 0, SEEK_END );15 filesize = ftell( tmpfp );16 fcolse( tmpfp );17 tmpfp = NULL;18 bitrate = (filesize * 8)/(fTime*1000);19 }
以下是全部代码:
D3DPlayer.h
1 #ifndef __D3D_PLAYER__ 2 #define __D3D_PLAYER__ 3 4 5 #include "resource.h" 6 7 #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } 8 #define SAFE_DELETE(p) { if(p) {delete (p); (p)=NULL; } } 9 10 #endif
D3DPlayer.h
D3DPlayer.cpp
1 #include "stdafx.h" 2 #include "D3DPlayer.h" 3 #include "D3DSound.h" 4 5 #define MAX_LOADSTRING 100 6 #define WS_MYPLAYERWINDOW (WS_OVERLAPPED | \ 7 WS_CAPTION | \ 8 WS_SYSMENU | \ 9 WS_MINIMIZEBOX | \ 10 WS_MAXIMIZEBOX) 11 #define MYWINWIDTH 700 12 #define MYWINHEIGHT 300 13 14 HINSTANCE hInst; // 当前实例 15 TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 16 TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 17 extern myD3DSound* g_pmySound; 18 19 BOOL PreTranslateMessage(LPMSG pMsg); 20 ATOM MyRegisterClass(HINSTANCE hInstance); 21 BOOL InitInstance(HINSTANCE, int); 22 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 23 INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); 24 25 int APIENTRY _tWinMain(HINSTANCE hInstance, 26 HINSTANCE hPrevInstance, 27 LPTSTR lpCmdLine, 28 int nCmdShow) 29 { 30 UNREFERENCED_PARAMETER(hPrevInstance); 31 UNREFERENCED_PARAMETER(lpCmdLine); 32 33 MSG msg; 34 HACCEL hAccelTable; 35 36 myD3DSound* pDXSound = new myD3DSound; 37 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 38 LoadString(hInstance, IDC_D3DPLAYER, szWindowClass, MAX_LOADSTRING); 39 MyRegisterClass(hInstance); 40 41 if (!InitInstance (hInstance, nCmdShow)) 42 { 43 return FALSE; 44 } 45 46 hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_D3DPLAYER)); 47 48 while (GetMessage(&msg, NULL, 0, 0)) 49 { 50 if(!PreTranslateMessage(&msg)) 51 { 52 if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 53 { 54 TranslateMessage(&msg); 55 DispatchMessage(&msg); 56 } 57 } 58 } 59 60 SAFE_DELETE(g_pmySound); 61 return (int) msg.wParam; 62 } 63 64 ATOM MyRegisterClass(HINSTANCE hInstance) 65 { 66 WNDCLASSEX wcex; 67 68 wcex.cbSize = sizeof(WNDCLASSEX); 69 70 wcex.style = CS_HREDRAW | CS_VREDRAW; 71 wcex.lpfnWndProc = WndProc; 72 wcex.cbClsExtra = 0; 73 wcex.cbWndExtra = 0; 74 wcex.hInstance = hInstance; 75 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYPLAYER)); 76 wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 77 wcex.hbrBackground = (HBRUSH)( COLOR_WINDOW - 1 ); 78 wcex.lpszMenuName = MAKEINTRESOURCE(IDC_D3DPLAYER); 79 wcex.lpszClassName = szWindowClass; 80 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_MYSMALL)); 81 82 return RegisterClassEx(&wcex); 83 } 84 85 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 86 { 87 HWND hWnd; 88 int iWidth; 89 int iHeight; 90 hInst = hInstance; // 将实例句柄存储在全局变量中 91 iWidth=GetSystemMetrics(SM_CXSCREEN); 92 iHeight=GetSystemMetrics(SM_CYSCREEN); 93 94 hWnd = CreateWindow(szWindowClass, szTitle, WS_MYPLAYERWINDOW, (iWidth-MYWINWIDTH)/2, 95 (iHeight-MYWINHEIGHT)/2, MYWINWIDTH, MYWINHEIGHT, NULL, NULL, hInstance, NULL); 96 97 if (!hWnd) 98 { 99 return FALSE;100 }101 //102 g_pmySound->myInit( hWnd );103 104 ShowWindow(hWnd, nCmdShow);105 UpdateWindow(hWnd);106 107 return TRUE;108 }109 110 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)111 {112 int wmId, wmEvent;113 PAINTSTRUCT ps;114 HDC hdc;115 RECT rect;116 LRESULT res;117 UINT CtrlId;118 DRAWITEMSTRUCT *dis;119 HFONT hfont;120 NMHDR *pNMHDR;121 122 switch (message)123 {124 case WM_NOTIFY:125 {126 pNMHDR = (LPNMHDR)lParam;127 switch( pNMHDR->code )128 {129 case NM_DBLCLK:130 {131 if( pNMHDR->idFrom == IDB_SONGLIST )132 {133 int i = SendMessage(g_pmySound->m_listview, LVM_GETNEXTITEM, -1, LVNI_SELECTED);134 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;135 if(pNMListView && pNMListView->iItem >= 0 && pNMListView->iItem < g_pmySound->mySongNum() )136 { 137 g_pmySound->mySetPlayInfo(pNMListView,TRUE);138 g_pmySound->myPlay();139 GetClientRect( hWnd, &rect );140 InvalidateRect(hWnd,&rect,TRUE);141 }142 }143 }break;144 case NM_CLICK:145 {146 if( pNMHDR->idFrom == IDB_SONGLIST )147 {148 SendMessage(g_pmySound->m_listview, LVM_GETNEXTITEM, -1, LVNI_SELECTED);149 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;150 if(pNMListView && pNMListView->iItem >= 0 && pNMListView->iItem < g_pmySound->mySongNum() )151 { 152 g_pmySound->mySetPlayInfo(pNMListView,FALSE);153 LVITEM vitem;154 LVITEM* pvitem;155 HANDLE hProcess;156 DWORD PID;157 GetWindowThreadProcessId(hWnd, &PID);158 hProcess=OpenProcess(PROCESS_ALL_ACCESS,false,PID);159 vitem.iItem = pNMListView->iItem;160 vitem.state = LVIS_SELECTED|LVIS_FOCUSED;161 vitem.stateMask = LVIS_SELECTED|LVIS_FOCUSED;162 pvitem=(LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof(LVITEM),MEM_COMMIT, PAGE_READWRITE);163 WriteProcessMemory(hProcess, pvitem, &vitem, sizeof(LVITEM), NULL);164 SendMessage(g_pmySound->m_listview, LVM_SETITEMSTATE, (WPARAM)pNMListView->iItem, (LPARAM)pvitem);165 GetClientRect( hWnd, &rect );166 InvalidateRect(hWnd,&rect,TRUE);167 168 }169 }170 else if( pNMHDR->idFrom == 12 )171 {172 int i = 0;173 }174 }break;175 default:176 break;177 }178 break;179 }180 case WM_COMMAND:181 {182 wmId = LOWORD(wParam);183 wmEvent = HIWORD(wParam);184 // 分析菜单选择:185 switch (wmId)186 {187 case IDM_ABOUT:188 DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);189 break;190 case IDM_EXIT:191 res = MessageBox( hWnd, TEXT("是否关闭"), TEXT("退出"), MB_YESNO );192 if( res == IDYES )193 {194 DestroyWindow( hWnd );195 }196 break;197 case IDB_OPEN:198 g_pmySound->myOpenFile();199 GetClientRect( hWnd, &rect );200 InvalidateRect(hWnd,&rect,TRUE);201 break;202 case IDB_CLOSE:203 g_pmySound->myCloseFile();204 GetClientRect( hWnd, &rect );205 InvalidateRect(hWnd,&rect,TRUE);206 break;207 case IDB_PLAY:208 g_pmySound->myPlay();209 GetClientRect( hWnd, &rect );210 InvalidateRect(hWnd,&rect,TRUE);211 break;212 case IDB_STOP:213 g_pmySound->myStop();214 GetClientRect( hWnd, &rect );215 InvalidateRect(hWnd,&rect,TRUE);216 break;217 case IDB_PAUSE:218 g_pmySound->myPause();219 break;220 default:221 return DefWindowProc(hWnd, message, wParam, lParam);222 }223 break;224 }225 case WM_PAINT:226 {227 hdc = BeginPaint(hWnd, &ps);228 // TODO: 在此添加任意绘图代码...229 EndPaint(hWnd, &ps);230 break;231 }232 case WM_DRAWITEM:233 {234 CtrlId = (UINT)wParam;235 dis = (LPDRAWITEMSTRUCT)lParam;236 int lw,lh,fw,fh,len; //ctrl width,ctrl height,font w,font h,font size237 WCHAR txt[MAX_PATH];238 lw=(dis->rcItem.right)-(dis->rcItem.left);239 lh=(dis->rcItem.bottom)-(dis->rcItem.top);240 fh=lh;241 len=GetWindowText(dis->hwndItem,txt,MAX_PATH);242 txt[len] = 0;243 fw=lw/(len+1);244 if( IDB_SONGTEXT == CtrlId || IDB_PLAYING == CtrlId )245 {246 fh = 16;247 fw = 8;248 }249 else if( IDB_SONGINFO == CtrlId )250 {251 fw = 7;252 }253 else if( IDB_SONGTIME == CtrlId || IDB_TIMESHOW == CtrlId )254 {255 fh = 14;256 fw = 7;257 }258 hfont=CreateFont( fh, fw, 0, 0, 500, FALSE, FALSE, FALSE, 259 GB2312_CHARSET, OUT_DEFAULT_PRECIS,260 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 261 DEFAULT_PITCH, TEXT("宋体") );262 263 switch( CtrlId )264 {265 case IDB_PLAYING:266 case IDB_SONGTEXT:267 {268 hfont = (HFONT)SelectObject( dis->hDC, hfont );269 SetBkMode( dis->hDC, TRANSPARENT );270 SetTextColor( dis->hDC, RGB(0,0,0) );271 TextOut( dis->hDC, 0, 0, txt,len+1 );272 hfont = (HFONT)SelectObject(dis->hDC, hfont );273 DeleteObject(hfont);274 }break;275 case IDB_TIMESHOW:276 {277 SetTimer( hWnd, 200, 1000, NULL );278 hfont = (HFONT)SelectObject( dis->hDC, hfont );279 SetBkMode( dis->hDC, TRANSPARENT );280 SetTextColor( dis->hDC, RGB(0,0,0) );281 TextOut( dis->hDC, 0, 0, txt,len+1 );282 hfont = (HFONT)SelectObject(dis->hDC, hfont );283 DeleteObject(hfont);284 }break;285 case IDB_SONGINFO:286 {287 hfont = (HFONT)SelectObject( dis->hDC, hfont );288 SetBkMode( dis->hDC, TRANSPARENT );289 SetTextColor( dis->hDC, RGB(0,0,0) );290 TextOut( dis->hDC, 0, 0, txt,len+1 );291 hfont = (HFONT)SelectObject(dis->hDC, hfont );292 DeleteObject(hfont);293 }break;294 case IDB_SONGTIME:295 {296 hfont = (HFONT)SelectObject( dis->hDC, hfont );297 SetBkMode( dis->hDC, TRANSPARENT );298 SetTextColor( dis->hDC, RGB(0,0,0) );299 TextOut( dis->hDC, 0, 0, txt,len+1 );300 hfont = (HFONT)SelectObject(dis->hDC, hfont );301 DeleteObject(hfont);302 //GetClientRect( hWnd, &rect );303 //InvalidateRect(hWnd,&rect,TRUE);304 }break;305 }306 break;307 }308 case WM_DESTROY:309 { PostQuitMessage(0);310 break;311 }312 case WM_CREATE:313 {314 g_pmySound->myCreateWin(hWnd);315 g_pmySound->myInitList();316 break;317 }318 case WM_SIZE:319 {320 g_pmySound->myChangeSize( hWnd );321 break;322 }323 case WM_TIMER:324 {325 GetClientRect( hWnd, &rect );326 InvalidateRect(hWnd,&rect,TRUE);327 SYSTEMTIME time;328 if( wParam == 200 )329 { 330 if( ! g_pmySound->isPlay() )331 {332 GetLocalTime( &time );333 g_pmySound->mySetTimer( time );334 SetWindowText( g_pmySound->m_timeshow, g_pmySound->myGetTimer() );335 }336 }337 break;338 }339 case WM_CLOSE:340 {341 res = MessageBox( hWnd, TEXT("是否关闭"), TEXT("退出"), MB_YESNO );342 if( res == IDYES )343 {344 DestroyWindow( hWnd );345 }346 break;347 }348 case WM_INITDIALOG:349 {350 break;351 } 352 case WM_HSCROLL:353 {354 //no use355 break;356 }357 case WM_LBUTTONUP:358 {359 //no use360 break;361 }362 default:363 return DefWindowProc(hWnd, message, wParam, lParam);364 }365 return 0;366 }367 368 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)369 {370 UNREFERENCED_PARAMETER(lParam);371 switch (message)372 {373 case WM_INITDIALOG:374 return (INT_PTR)TRUE;375 376 case WM_COMMAND:377 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)378 {379 EndDialog(hDlg, LOWORD(wParam));380 return (INT_PTR)TRUE;381 }382 break;383 }384 return (INT_PTR)FALSE;385 }386 387 388 BOOL PreTranslateMessage(LPMSG pMsg)389 {390 if( pMsg->message == WM_LBUTTONUP )391 {392 if( (HWND)pMsg->hwnd == g_pmySound->m_scrollbar )393 {394 g_pmySound->mySetScrollPos( TRUE, 0 );395 //return TRUE;396 }397 else if( (HWND)pMsg->hwnd == g_pmySound->m_volumebar )398 {399 g_pmySound->mySetVolumePos( TRUE, 0 );400 //return TRUE;401 }402 else403 {404 return FALSE;405 }406 }407 return FALSE;408 }
D3DPlayer.cpp
D3DSound.h
1 #ifndef __D3D_SOUND__ 2 #define __D3D_SOUND__ 3 4 #include "D3DPlayer.h" 5 #include "stdafx.h" 6 #include "mpg123.h" 7 8 #define MAX_AUDIO_BUF 4 9 #define BUFFERNOTIFYSIZE 192000 10 #define IDB_OPEN 1001 11 #define IDB_CLOSE 1002 12 #define IDB_PLAY 1003 13 #define IDB_PAUSE 1004 14 #define IDB_STOP 1005 15 #define IDB_PLAYING 2001 16 #define IDB_TIMESHOW 2002 17 #define IDB_SONGINFO 2003 18 #define IDB_SONGTEXT 2004 19 #define IDB_SONGTIME 2005 20 #define IDB_SONGLIST 2006 21 22 struct WAVE_HEADER //44bytes 23 { 24 char riff_sig[4]; 25 long waveform_chunk_size; 26 char wave_sig[4]; 27 char format_sig[4]; 28 long format_chunk_size; 29 short format_tag; 30 short channels; 31 long sample_rate; 32 long bytes_per_sec; 33 short block_align; 34 short bits_per_sample; 35 char data_sig[4]; 36 long data_size; 37 }; 38 39 struct WAVE_HEADER_FACT //58bytes 40 { 41 char riff_sig[4]; 42 long waveform_chunk_size; 43 char wave_sig[4]; 44 char format_sig[4]; 45 long format_chunk_size; 46 short format_tag; 47 short channels; 48 long sample_rate; 49 long bytes_per_sec; 50 short block_align; 51 short bits_per_sample; 52 short bits_per_sample2; 53 char fact_sig[4]; 54 short fact_size; 55 short fact_size2; 56 char fact_data[4]; 57 char data_sig[4]; 58 long data_size; 59 }; 60 61 struct myListMember 62 { 63 int m_idx; 64 WCHAR m_name[100]; 65 WCHAR m_time[10]; 66 WCHAR m_type[10]; 67 WCHAR m_bits[20]; 68 WCHAR m_path[MAX_PATH]; 69 }; 70 71 static DWORD ThreadNotifyEvent( LPVOID thread_data ); 72 static DWORD ThreadNotifyEvent2( LPVOID thread_data ); 73 static DWORD ThreadNotifyEvent3( LPVOID thread_data ); 74 void ChartoWCHAR( const char*, WCHAR* ); 75 void WCHARtoChar( const WCHAR*, char* ); 76 77 class myD3DSound 78 { 79 private: 80 OPENFILENAME opfn; 81 bool isPlaying; 82 DWORD m_ds_dwBuffSize; //dxsound缓冲区大小 83 HANDLE m_hThread; //控制buffer 84 DWORD m_thread_id; 85 HANDLE m_hThread2; //滚动条显示 86 DWORD m_thread_id2; 87 HANDLE m_hThread3; //剩余时间显示 88 DWORD m_thread_id3; 89 FILE* m_fp; 90 mpg123_handle* m_mpghandle; 91 DWORD m_dwPlayPos; 92 WCHAR m_wstrTime[60]; 93 WCHAR m_wSongTime[30]; //总时长 94 WCHAR m_wSongLeave[30]; //剩余时长 95 typedef std::vector<myListMember> MY_SONG_LIST; 96 MY_SONG_LIST m_list; 97 WCHAR m_wSongPath[MAX_PATH]; //正在播放歌曲 98 WCHAR m_wSongName[MAX_PATH]; 99 WCHAR m_wSongPathPre[MAX_PATH]; //单击选中歌曲100 WCHAR m_wSongNamePre[MAX_PATH];101 102 bool m_bmpg;103 bool m_factwav;104 public:105 HWND m_father;106 //button107 HWND m_openbtn;108 HWND m_closebtn;109 HWND m_playbtn;110 HWND m_pausebtn;111 HWND m_stopbtn;112 //static113 HWND m_txtplaying;114 HWND m_songtxt;115 HWND m_songinfo;116 HWND m_timeshow;117 HWND m_songtime;118 //119 HWND m_scrollbar;120 HWND m_volumebar;121 HWND m_listview;122 123 LPDIRECTSOUND8 m_pDirectSound;124 125 //playing file info126 LPDIRECTSOUNDBUFFER8 m_pBuffer8;127 HANDLE m_hEvents[MAX_AUDIO_BUF];128 #ifdef __MAX_BUFFER__129 DWORD m_ds_dwFileSize; //文件大小130 #endif131 DWORD m_ds_dwFileTime; //文件时长(s)132 DWORD m_ds_dwFilebps; //文件传输率133 DWORD m_ds_dwPos; //文件偏移量134 DWORD m_ds_dwLeave; //文件剩余量135 int m_iScrollPos; //进度条位置136 int m_iVolumePos; //音量条位置137 138 enum eFAIL_CODE{139 EFAIL_NOFILE = 1,140 EFAIL_NOTSUPPORT,141 EFAIL_PCMBUFFERERR,142 EFAIL_MPGBUFFERERR,143 EFAIL_OPENFILEERR,144 EFAIL_FORMATERR,145 };146 147 private:148 int mySetSoundType();149 int myGetWAVFormat( DWORD* dwSize, DWORD* dwCycle, FILE* fp, WAVEFORMATEX** wfx );150 int myGetMP3Format( char* filestr, DWORD* dwSize, DWORD* dwCycle, int* bitrate, WAVEFORMATEX** wfx, bool isClose = TRUE );151 HRESULT myCreatePCMBuffer( WAVEFORMATEX* wfx, DWORD* dwBuffSize );152 HRESULT myCreateMPGBuffer( WAVEFORMATEX* wfx, DWORD* dwBuffSize );153 void myUpdateList();154 void myCleanBuffer();155 void cleanup(); //clear mpg123 handle156 public:157 myD3DSound();158 ~myD3DSound();159 HRESULT myInit( HWND );160 void myCreateWin( HWND );161 void myChangeSize( HWND );162 void mySetList( WCHAR* );163 void myInitList();164 void mySetPlayInfo( NM_LISTVIEW* pNMListView, bool DBClick );165 void mySetTimer( SYSTEMTIME );166 bool myReadBuffer( long lock_pos, long lock_size );167 bool myReadMPGBuffer( long lock_pos, long lock_size );168 void mySetScrollPos( bool isUp, DWORD dPos );169 void mySetVolumePos( bool isUp, DWORD dPos );170 171 //button172 void myOpenFile();173 void myCloseFile();174 int myPlay();175 int myStop();176 int myPause();177 178 int mySongNum();179 WCHAR* myGetTimer();180 bool isPlay(){ return isPlaying; }181 void SetPlaying( bool flags ){ isPlaying = flags; }182 int GetBufferValue( FILE** fp, mpg123_handle** mpghandle, DWORD* BuffSize );183 bool IsMPG3(){return m_bmpg;}184 };185 186 187 #endif
D3DSound.h
D3DSound.cpp
1 #include "D3DSound.h" 2 3 const WCHAR* g_cwErrorMsg[7] = { 4 TEXT(""), 5 TEXT("Pelase choose a file!"), 6 TEXT("Only supports .mp3 and .wav file!"), 7 TEXT("Create PCM buffer fail!"), 8 TEXT("Create MPG buffer fail!"), 9 TEXT("Cannot play the file!"), 10 TEXT("File format error!") 11 }; 12 WCHAR* g_cwSongList[6] = { 13 TEXT("序号"), 14 TEXT("名称"), 15 TEXT("持续时间"), 16 TEXT("扩展名"), 17 TEXT("比特率"), 18 TEXT("文件路径") 19 }; 20 21 22 23 myD3DSound* g_pmySound = NULL; 24 25 26 /////////////////////// 27 //public function 28 /////////////////////// 29 30 HRESULT myD3DSound::myInit( HWND hWnd ) 31 { 32 m_father = hWnd; 33 if( FAILED( DirectSoundCreate8(NULL, &m_pDirectSound, NULL) ) ) 34 return E_FAIL; 35 36 if( FAILED( m_pDirectSound->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ) ) ) 37 return E_FAIL; 38 39 return S_OK; 40 } 41 42 myD3DSound::myD3DSound(): 43 m_pDirectSound(NULL), 44 m_pBuffer8(NULL), 45 m_father(NULL), 46 m_openbtn(NULL), 47 m_closebtn(NULL), 48 m_playbtn(NULL), 49 m_pausebtn(NULL), 50 m_stopbtn(NULL), 51 m_txtplaying(NULL), 52 m_songtxt(NULL), 53 m_timeshow(NULL), 54 m_songinfo(NULL), 55 m_songtime(NULL), 56 m_scrollbar(NULL), 57 m_volumebar(NULL), 58 m_listview(NULL), 59 m_fp(NULL), 60 isPlaying(FALSE), 61 m_bmpg(FALSE), 62 m_factwav(FALSE), 63 m_hThread(NULL), 64 m_hThread2(NULL), 65 m_mpghandle(NULL) 66 { 67 68 m_ds_dwBuffSize = 0; 69 #ifdef __MAX_BUFFER__ 70 m_ds_dwFileSize = 0; 71 #endif 72 m_ds_dwFileTime = 0; 73 m_ds_dwFilebps = 0; 74 m_ds_dwPos = 0; 75 m_ds_dwLeave = 0; 76 m_iScrollPos = 0; 77 m_iVolumePos = 50; 78 79 m_thread_id = 0; 80 m_dwPlayPos = 0; 81 ZeroMemory(&opfn, sizeof(OPENFILENAME)); 82 memset( m_wstrTime, 0, sizeof(m_wstrTime) ); 83 memset( m_wSongTime, 0, sizeof(m_wSongTime) ); 84 memset( m_wSongLeave, 0, sizeof(m_wSongLeave) ); 85 memset( m_wSongPath, 0, sizeof(m_wSongPath) ); 86 memset( m_wSongName, 0, sizeof(m_wSongName) ); 87 memset( m_wSongPathPre, 0, sizeof(m_wSongPathPre) ); 88 memset( m_wSongNamePre, 0, sizeof(m_wSongNamePre) ); 89 m_list.clear(); 90 g_pmySound = this; 91 } 92 93 myD3DSound::~myD3DSound() 94 { 95 SAFE_RELEASE(m_pBuffer8); 96 SAFE_RELEASE(m_pDirectSound); 97 ZeroMemory(&opfn, sizeof(OPENFILENAME)); 98 memset( m_wstrTime, 0, sizeof(m_wstrTime) ); 99 memset( m_wSongTime, 0, sizeof(m_wSongTime) ); 100 memset( m_wSongLeave, 0, sizeof(m_wSongLeave) ); 101 memset( m_wSongPath, 0, sizeof(m_wSongPath) ); 102 memset( m_wSongName, 0, sizeof(m_wSongName) ); 103 memset( m_wSongPathPre, 0, sizeof(m_wSongPathPre) ); 104 memset( m_wSongNamePre, 0, sizeof(m_wSongNamePre) ); 105 if( m_fp ) 106 { 107 fclose( m_fp ); 108 m_fp = NULL; 109 } 110 m_list.clear(); 111 cleanup(); 112 } 113 114 115 116 void myD3DSound::myCreateWin( HWND hWnd ) 117 { 118 RECT dynamic_rc; 119 RECT button_rc; 120 RECT static_rc; 121 RECT list_rc; 122 GetClientRect( hWnd, &dynamic_rc ); 123 LONG Width = dynamic_rc.right - dynamic_rc.left; 124 LONG Height = dynamic_rc.bottom - dynamic_rc.top; 125 126 button_rc.left = 15; 127 button_rc.top = 15; 128 m_openbtn = CreateWindow( TEXT("BUTTON"), TEXT("Add"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON, 129 button_rc.left, button_rc.top, 50, 20, hWnd, (HMENU)IDB_OPEN, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 130 131 button_rc.left += 60; 132 m_closebtn = CreateWindow( TEXT("BUTTON"), TEXT("Del"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON, 133 button_rc.left, button_rc.top, 50, 20, hWnd, (HMENU)IDB_CLOSE, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 134 135 button_rc.left += 60; 136 m_playbtn = CreateWindow( TEXT("BUTTON"), TEXT("Play"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON, 137 button_rc.left, button_rc.top, 50, 20, hWnd, (HMENU)IDB_PLAY, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 138 139 button_rc.left += 60; 140 m_pausebtn = CreateWindow( TEXT("BUTTON"), TEXT("Pause"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON, 141 button_rc.left, button_rc.top, 50, 20, hWnd, (HMENU)IDB_PAUSE, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 142 143 button_rc.left += 60; 144 m_stopbtn = CreateWindow( TEXT("BUTTON"), TEXT("Stop"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON, 145 button_rc.left, button_rc.top, 50, 20, hWnd, (HMENU)IDB_STOP, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 146 147 button_rc.left += 60; 148 m_scrollbar = CreateWindow( TRACKBAR_CLASS, TEXT(""), WS_VISIBLE|WS_CHILD|WS_BORDER|TBS_HORZ|TBS_TOOLTIPS, 149 button_rc.left, button_rc.top, 275, 20, hWnd, (HMENU)12, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 150 //static 151 static_rc.left = 15; 152 static_rc.top = 50; 153 m_txtplaying = CreateWindow(TEXT("STATIC"), TEXT("playing now:"), WS_VISIBLE|WS_CHILD|SS_CENTER|SS_OWNERDRAW, 154 static_rc.left, static_rc.top, 90, 20, hWnd, (HMENU)IDB_PLAYING, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 155 156 static_rc.left += ( 90 + 10 ); 157 m_songtxt = CreateWindow( TEXT("STATIC"), TEXT("无文件"), WS_VISIBLE|WS_CHILD|WS_BORDER|SS_LEFT|SS_OWNERDRAW, 158 static_rc.left, static_rc.top, 560, 20, hWnd, (HMENU)IDB_SONGTEXT, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 159 160 static_rc.left = 15; 161 static_rc.top = Height - 5 - 16; 162 m_songinfo = CreateWindow( TEXT("STATIC"), TEXT("请选择wav文件进行播放。"), WS_VISIBLE|WS_CHILD|SS_CENTER|WS_BORDER|SS_OWNERDRAW, 163 static_rc.left, static_rc.top, 350, 16, hWnd, (HMENU)IDB_SONGINFO, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 164 165 static_rc.left += (350 + 10); 166 m_songtime = CreateWindow( TEXT("STATIC"), TEXT("00:00 / 00:00"), WS_VISIBLE|WS_CHILD|SS_CENTER|WS_BORDER|SS_OWNERDRAW, 167 static_rc.left, static_rc.top, 140, 16, hWnd, (HMENU)IDB_SONGTIME, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 168 169 static_rc.left = Width - 15 - 150; 170 m_timeshow = CreateWindow( TEXT("STATIC"), TEXT(""), WS_VISIBLE|WS_CHILD|SS_CENTER|SS_OWNERDRAW, 171 static_rc.left, static_rc.top, 150, 16, hWnd, (HMENU)IDB_TIMESHOW, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 172 173 static_rc.left = Width - 90; 174 static_rc.top = 15; 175 m_volumebar = CreateWindow( TRACKBAR_CLASS, TEXT(""), WS_VISIBLE|WS_CHILD|WS_BORDER|TBS_HORZ|TBS_TOOLTIPS, 176 static_rc.left, static_rc.top, 80, 20, hWnd, (HMENU)13, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 177 SendMessage( m_volumebar, TBM_SETPOS, TRUE, (LPARAM)50 ); 178 179 list_rc.left = 30; 180 list_rc.top = 80; 181 m_listview = CreateWindowEx( LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT, WC_LISTVIEW, TEXT(""), 182 WS_VISIBLE|WS_CHILD|WS_BORDER|WS_VSCROLL|WS_HSCROLL|LVS_REPORT|LVS_SHOWSELALWAYS|LVS_SINGLESEL, 183 list_rc.left, list_rc.top, 640, 145, hWnd, (HMENU)IDB_SONGLIST, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); 184 } 185 186 187 188 void myD3DSound::myChangeSize( HWND hWnd ) 189 { 190 RECT dynamic_rc; 191 GetClientRect( hWnd, &dynamic_rc ); 192 LONG Width = dynamic_rc.right - dynamic_rc.left; 193 LONG Height = dynamic_rc.bottom - dynamic_rc.top; 194 195 MoveWindow( m_songinfo, 15, Height - 5 - 16, 350, 16, TRUE ); 196 197 MoveWindow( m_songtime, 15 + 350 + 10, Height - 5 - 16, 140, 16, TRUE ); 198 199 MoveWindow( m_timeshow, Width - 15 - 150, Height - 5 - 16, 150, 16, TRUE ); 200 201 MoveWindow( m_songtxt, 115, 50, Width - 115 - 15, 20, TRUE ); 202 203 MoveWindow( m_scrollbar, 315, 15, Width - 315 - 15 - 90, 20, TRUE ); 204 205 MoveWindow( m_volumebar, Width - 90, 15, 80, 20, TRUE ); 206 207 MoveWindow( m_listview, 30, 80, Width - 60, Height - 80 - 10 - 21, TRUE ); 208 209 } 210 211 212 void myD3DSound::mySetList( WCHAR* wcFilename ) 213 { 214 myListMember tmplist; 215 //m_path 216 StrCpyW( tmplist.m_path, wcFilename ); 217 //m_name 218 char cName[100]; 219 char tmpstr[MAX_PATH]; 220 memset(cName,0,100); 221 memset(tmpstr,0,MAX_PATH); 222 223 WCHARtoChar( wcFilename, tmpstr ); 224 int len = strlen(tmpstr); 225 char* p = &tmpstr[len]; 226 int len_name = 0; 227 bool isname = 0; 228 bool bk = 0; 229 while( !bk ) 230 { 231 if( !isname && (*p == '.') ) 232 { 233 isname = true; 234 p--; 235 continue; 236 } 237 if( isname ) 238 { 239 if(*p == '\\' ) 240 { 241 bk = true; 242 p++; 243 continue; 244 } 245 len_name++; 246 } 247 p--; 248 } 249 memcpy( cName, p, len_name ); 250 ChartoWCHAR( cName, tmplist.m_name ); 251 252 //type 253 LPWSTR lwType; 254 char ctmpType[5]; 255 char cType[4]; 256 lwType = PathFindExtension( wcFilename ); 257 WCHARtoChar( lwType, ctmpType); 258 sprintf_s( cType, "%s", ctmpType+1); 259 ChartoWCHAR( cType, tmplist.m_type ); 260 261 if( StrCmpW( lwType, TEXT(".wav") ) == 0 || StrCmpW( lwType, TEXT(".WAV") ) == 0 ) 262 { 263 //m_bits 264 FILE* tmpfp = NULL; 265 char cBits[20]; 266 DWORD tmpSize; 267 DWORD tmpCycle; 268 WAVEFORMATEX* pwfx = NULL; 269 memset(cBits,0,20); 270 if( fopen_s( &tmpfp, tmpstr, "rb" ) == 0 ) 271 { 272 myGetWAVFormat( &tmpSize, &tmpCycle, tmpfp, &pwfx ); 273 if(pwfx != NULL) 274 { 275 sprintf_s( cBits, 20, "%d kbps", (pwfx->wBitsPerSample * pwfx->nChannels * pwfx->nSamplesPerSec)/1000 ); 276 } 277 fclose(tmpfp); 278 } 279 ChartoWCHAR( cBits, tmplist.m_bits ); 280 //time 281 char cTime[10]; 282 memset(cTime,0,10); 283 sprintf_s( cTime, "%02d:%02d", tmpCycle/60, tmpCycle%60 ); 284 ChartoWCHAR( cTime, tmplist.m_time ); 285 } 286 else if( StrCmpW( lwType, TEXT(".mp3") ) == 0 || StrCmpW( lwType, TEXT(".MP3") ) == 0 ) 287 { 288 char cBits[20]; 289 int bits; 290 DWORD tmpSize; 291 DWORD tmpCycle; 292 FILE* tmpfp = NULL; 293 WAVEFORMATEX* pwfx = NULL; 294 memset(cBits,0,20); 295 if( myGetMP3Format( tmpstr, &tmpSize, &tmpCycle, &bits, &pwfx ) == 0 ) 296 { 297 sprintf_s( cBits, 20, "%d kbps", bits); 298 } 299 ChartoWCHAR( cBits, tmplist.m_bits ); 300 //time 301 char cTime[10]; 302 memset(cTime,0,10); 303 sprintf_s( cTime, "%02d:%02d", tmpCycle/60, tmpCycle%60 ); 304 ChartoWCHAR( cTime, tmplist.m_time ); 305 } 306 307 m_list.push_back( tmplist ); 308 309 myUpdateList(); 310 } 311 312 313 314 void myD3DSound::myUpdateList() 315 { 316 ListView_DeleteAllItems(m_listview); 317 318 int iSize = m_list.size(); 319 LVITEM vitem; 320 vitem.mask = LVIF_TEXT; 321 for( int i = 0; i< iSize; i++ ) 322 { 323 char cIdx[100]; 324 WCHAR wIdx[100]; 325 memset(cIdx,0,100); 326 memset(wIdx,0,100); 327 328 vitem.iItem = i; 329 m_list[i].m_idx = i+1; 330 sprintf_s( cIdx, "%d", i+1 ); 331 ChartoWCHAR( cIdx, wIdx ); 332 vitem.pszText = wIdx; 333 vitem.iSubItem = 0; 334 ListView_InsertItem(m_listview, &vitem); 335 336 vitem.iSubItem = 1; 337 vitem.pszText = m_list[i].m_name; 338 ListView_SetItem( m_listview, &vitem); 339 vitem.iSubItem = 2; 340 vitem.pszText = m_list[i].m_time; 341 ListView_SetItem(m_listview, &vitem); 342 vitem.iSubItem = 3; 343 vitem.pszText = m_list[i].m_type; 344 ListView_SetItem(m_listview, &vitem); 345 vitem.iSubItem = 4; 346 vitem.pszText = m_list[i].m_bits; 347 ListView_SetItem(m_listview, &vitem); 348 vitem.iSubItem = 5; 349 vitem.pszText = m_list[i].m_path; 350 ListView_SetItem(m_listview, &vitem); 351 } 352 } 353 354 355 356 int myD3DSound::mySongNum() 357 { 358 return m_list.size(); 359 } 360 361 362 363 void myD3DSound::myInitList() 364 { 365 LVCOLUMN vcl; 366 vcl.mask = LVCF_SUBITEM | LVCF_WIDTH | LVCF_TEXT | LVCF_FMT; 367 vcl.fmt = LVCFMT_LEFT; 368 for( int i = 0; i < 6; i++ ) 369 { 370 vcl.pszText = g_cwSongList[i]; 371 vcl.cx = 80; 372 if( i == 5 ) 373 vcl.cx = 800; 374 vcl.iSubItem = i; 375 ListView_InsertColumn( m_listview, i, &vcl ); 376 } 377 } 378 379 380 381 void myD3DSound::mySetTimer( SYSTEMTIME time ) 382 { 383 char strtime[60]; 384 memset( strtime, 0, 60 ); 385 sprintf_s(strtime,"%04d-%02d-%02d %02d:%02d:%02d", 386 time.wYear,time.wMonth,time.wDay,time.wHour,time.wMinute,time.wSecond); 387 ChartoWCHAR(strtime,m_wstrTime); 388 } 389 390 391 392 WCHAR* myD3DSound::myGetTimer() 393 { 394 return m_wstrTime; 395 } 396 397 398 399 void myD3DSound::mySetScrollPos( bool isUp, DWORD dPos ) 400 { 401 if(isUp) 402 { 403 int iPos; 404 iPos = SendMessage( m_scrollbar, TBM_GETPOS, 0, 0 ); 405 if( g_pmySound->isPlay() && g_pmySound->m_ds_dwFileSize <= DSBSIZE_MAX ) 406 { 407 DWORD dNewPos; 408 dNewPos = iPos * (g_pmySound->m_ds_dwFileSize / 100); 409 g_pmySound->m_pBuffer8->SetCurrentPosition( dNewPos ); 410 g_pmySound->m_pBuffer8->Play(0, 0, DSBPLAY_LOOPING); 411 } 412 else 413 { 414 iPos = SendMessage( m_scrollbar, TBM_SETPOS, TRUE, (LPARAM)0 ); 415 } 416 417 } 418 else 419 { 420 421 } 422 } 423 424 425 426 427 void myD3DSound::mySetVolumePos( bool isUp, DWORD dPos ) 428 { 429 LONG vol = 0; 430 double dbValue; 431 int iPos; 432 if( isUp && m_pBuffer8 != NULL ) 433 { 434 iPos = SendMessage( m_volumebar, TBM_GETPOS, 0, 0 ); 435 if( iPos > 0 && iPos <= 100 ) 436 { 437 dbValue = 20.0f * log10( (double)(iPos / 100.0f) ); 438 vol = (LONG)(dbValue * 100.0f); 439 } 440 else if( iPos == 0 ) 441 { 442 vol = DSBVOLUME_MIN; 443 } 444 else 445 { 446 vol = DSBVOLUME_MAX; 447 } 448 m_pBuffer8->SetVolume( vol ); 449 } 450 } 451 452 453 454 455 void myD3DSound::myOpenFile() 456 { 457 WCHAR strFilename[MAX_PATH]; 458 ZeroMemory(&opfn, sizeof(OPENFILENAME)); 459 460 opfn.lStructSize = sizeof(OPENFILENAME); //结构体大小 461 opfn.lpstrFilter = L"所有文件\0*.*\0wav文件\0*.wav\0MP3文件\0*.mp3\0"; //过滤器 462 opfn.nFilterIndex = 1; //过滤索引 463 opfn.lpstrFile = strFilename; //文件名的缓冲,不需要初始化则必须为null 464 opfn.lpstrFile[0] = '\0'; 465 opfn.nMaxFile = sizeof(strFilename); 466 opfn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; 467 468 if( GetOpenFileName(&opfn) ) 469 { 470 mySetList(strFilename); 471 } 472 } 473 474 void myD3DSound::myCloseFile() 475 { 476 ZeroMemory(&opfn, sizeof(OPENFILENAME)); 477 if( StrCmpW(m_wSongPath, m_wSongPathPre) == 0 ) 478 { 479 SetWindowText(m_songtxt, TEXT("")); 480 myStop(); 481 if( m_fp ) 482 { 483 fclose( m_fp ); 484 m_fp = NULL; 485 } 486 } 487 MY_SONG_LIST::iterator Songit; 488 for( Songit = m_list.begin(); Songit != m_list.end(); Songit++ ) 489 { 490 if( StrCmpW(Songit->m_path, m_wSongPathPre) == 0 ) 491 { 492 m_list.erase( Songit ); 493 memset( m_wSongPathPre, 0, MAX_PATH ); 494 memset( m_wSongPath, 0, MAX_PATH ); 495 myUpdateList(); 496 return; 497 } 498 } 499 500 } 501 502 503 504 void myD3DSound::mySetPlayInfo( NM_LISTVIEW* pNMListView, bool DBClick ) 505 { 506 if( DBClick ) 507 { 508 if( StrCmpW(m_list[pNMListView->iItem].m_path, m_wSongPath) ) 509 {//双击新歌则停止当前播放 510 myStop(); 511 } 512 StrCpyW( m_wSongPath, m_list[pNMListView->iItem].m_path ); 513 StrCpyW( m_wSongName, m_list[pNMListView->iItem].m_name ); 514 StrCpyW( m_wSongPathPre, m_list[pNMListView->iItem].m_path ); 515 StrCpyW( m_wSongNamePre, m_list[pNMListView->iItem].m_name ); 516 } 517 else 518 {//单击 519 StrCpyW( m_wSongPathPre, m_list[pNMListView->iItem].m_path ); 520 StrCpyW( m_wSongNamePre, m_list[pNMListView->iItem].m_name ); 521 if( ! isPlay() ) 522 { 523 StrCpyW( m_wSongPath, m_list[pNMListView->iItem].m_path ); 524 StrCpyW( m_wSongName, m_list[pNMListView->iItem].m_name ); 525 } 526 } 527 } 528 529 530 531 int myD3DSound::myPlay() 532 { 533 if( isPlay() ) 534 { 535 if( StrCmpW( m_wSongPath, m_wSongPathPre ) ) 536 { 537 myStop(); 538 StrCpyW( m_wSongName, m_wSongNamePre ); 539 StrCpyW( m_wSongPath, m_wSongPathPre ); 540 } 541 else 542 { 543 return 0; 544 } 545 } 546 if( m_pBuffer8 == NULL ) 547 { 548 int res = 0; 549 res = mySetSoundType(); 550 if( res != 0 ) 551 { 552 MessageBox( m_father, g_cwErrorMsg[res], TEXT("ERROR"), MB_OK ); 553 return 0; 554 } 555 //create notification thread 556 m_hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)ThreadNotifyEvent, NULL, 0, &m_thread_id ); 557 m_hThread2 = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)ThreadNotifyEvent2, NULL, 0, &m_thread_id2 ); 558 m_hThread3 = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)ThreadNotifyEvent3, NULL, 0, &m_thread_id3 ); 559 SetWindowText( m_songtxt, m_wSongName ); 560 } 561 SetPlaying( TRUE ); 562 mySetVolumePos(TRUE,0); 563 m_pBuffer8->SetCurrentPosition( m_dwPlayPos ); 564 m_pBuffer8->Play(0, 0, DSBPLAY_LOOPING); 565 return 0; 566 } 567 568 569 570 int myD3DSound::myStop() 571 { 572 myCleanBuffer(); 573 if( isPlay() ) 574 { 575 CloseHandle( m_hEvents[0] ); 576 577 if( m_hThread != NULL ) 578 { 579 TerminateThread( m_hThread, 0 ); 580 CloseHandle( m_hThread ); 581 } 582 } 583 if( IsMPG3() ) 584 { 585 cleanup(); 586 } 587 SetWindowText( m_songinfo, TEXT("请选择wav文件进行播放。") ); 588 StrCpyW( m_wSongName, TEXT("")); 589 StrCpyW( m_wSongPath, TEXT("")); 590 SetWindowText( m_songtxt, m_wSongName ); 591 SendMessage( g_pmySound->m_scrollbar, TBM_SETPOS, TRUE, (LPARAM)0 ); 592 SetWindowText( g_pmySound->m_songtime, TEXT("00:00 / 00:00") ); 593 return 0; 594 } 595 596 597 598 int myD3DSound::myPause() 599 { 600 if( m_pBuffer8 == NULL ) 601 return -1; 602 if( isPlay() ) 603 { 604 m_pBuffer8->GetCurrentPosition( &m_dwPlayPos, NULL ); 605 SetPlaying( FALSE ); 606 m_pBuffer8->Stop(); 607 } 608 else 609 { 610 SetPlaying( TRUE ); 611 m_pBuffer8->SetCurrentPosition( m_dwPlayPos ); 612 m_pBuffer8->Play(0, 0, DSBPLAY_LOOPING); 613 } 614 return 0; 615 } 616 617 618 619 int myD3DSound::GetBufferValue( FILE** fp, mpg123_handle** mpghandle, DWORD* BuffSize ) 620 { 621 *fp = m_fp; 622 *mpghandle = m_mpghandle; 623 *BuffSize = m_ds_dwBuffSize; 624 return 0; 625 } 626 627 628 /////////////////////// 629 //private 630 /////////////////////// 631 632 int myD3DSound::mySetSoundType() 633 { 634 WCHAR cNameStr[MAX_PATH]; 635 LPWSTR lpNameType; 636 char cType[4]; 637 char cTmpStr[5]; 638 memset( cNameStr, 0, MAX_PATH ); 639 memset( cType, 0, 4 ); 640 memset( cTmpStr, 0, 5 ); 641 642 StrCpyW( cNameStr, m_wSongPath ); 643 644 if( cNameStr[0] == '\0' ) 645 { 646 return EFAIL_NOFILE; 647 } 648 lpNameType = PathFindExtension( cNameStr ); 649 650 WCHARtoChar( lpNameType, cTmpStr); 651 sprintf_s( cType, "%s", cTmpStr+1); 652 if( StrCmpW( lpNameType, TEXT(".mp3") ) == 0 || StrCmpW( lpNameType, TEXT(".MP3") ) == 0 ) 653 { 654 DWORD dwSize; //stream size 655 DWORD dwCycle; 656 DWORD dwBuffSize; 657 int bitrate; 658 WAVEFORMATEX wfx; 659 WAVEFORMATEX* pTmpWfx = NULL; 660 char filepath[MAX_PATH]; 661 memset( filepath, 0, MAX_PATH ); 662 WCHARtoChar( cNameStr, filepath ); 663 if( myGetMP3Format( filepath, &dwSize, &dwCycle, &bitrate, &pTmpWfx, FALSE ) != 0 ) 664 { 665 return EFAIL_FORMATERR; 666 } 667 m_ds_dwFileSize = dwSize; 668 669 m_ds_dwFileTime = dwCycle; 670 m_ds_dwFilebps = pTmpWfx->nAvgBytesPerSec; 671 m_ds_dwPos = 0; //offset position 672 m_ds_dwLeave = dwSize; //leave data size 673 wfx.wFormatTag = pTmpWfx->wFormatTag; 674 wfx.nChannels = pTmpWfx->nChannels; 675 wfx.nSamplesPerSec = pTmpWfx->nSamplesPerSec; 676 wfx.wBitsPerSample = pTmpWfx->wBitsPerSample; 677 wfx.nBlockAlign = pTmpWfx->nBlockAlign; 678 wfx.nAvgBytesPerSec = pTmpWfx->nAvgBytesPerSec; 679 pTmpWfx = NULL; 680 if( FAILED( myCreateMPGBuffer( &wfx, &dwBuffSize ) ) ) 681 { 682 return EFAIL_MPGBUFFERERR; 683 } 684 m_ds_dwBuffSize = dwBuffSize; 685 //song info 686 WCHAR wcStr_info[100]; 687 char cStr_info[100]; 688 char cStr_type[5]; 689 memset( wcStr_info,0,100 ); 690 memset( cStr_info,0,100 ); 691 memset( cStr_type,0,5 ); 692 WCHARtoChar( lpNameType, cStr_type ); 693 sprintf_s( cStr_info, "%s | %d kbps | %d Hz", cStr_type+1, bitrate, wfx.nSamplesPerSec); 694 ChartoWCHAR( cStr_info, wcStr_info ); 695 SetWindowText( m_songinfo, wcStr_info ); 696 m_bmpg = TRUE; 697 } 698 699 else if( StrCmpW( lpNameType, TEXT(".wav") ) == 0 || StrCmpW( lpNameType, TEXT(".WAV") ) == 0 ) 700 { 701 WAVEFORMATEX wfx; 702 DWORD dwSize; //声音文件总大小 703 DWORD dwCycle; 704 705 DWORD dwBuffSize; //创建的缓冲区总大小 706 int inum=WideCharToMultiByte(CP_ACP,0,cNameStr,-1,NULL,0,NULL,0); 707 char* cfilename = NULL; 708 cfilename = (char*)malloc( inum * sizeof(char) ); 709 if( cfilename == NULL ) 710 free(cfilename); 711 memset( cfilename, 0, inum * sizeof(char) ); 712 WideCharToMultiByte(CP_ACP,0,cNameStr,-1,cfilename,inum,NULL,0); 713 714 if( fopen_s( &m_fp, cfilename, "rb" ) ) 715 { 716 return EFAIL_OPENFILEERR; 717 } 718 WAVEFORMATEX* pTmpWfx = NULL; 719 myGetWAVFormat( &dwSize, &dwCycle, m_fp, &pTmpWfx ); 720 721 if( pTmpWfx == NULL ) 722 { 723 return EFAIL_FORMATERR; 724 } 725 m_ds_dwFileSize = dwSize; 726 727 m_ds_dwFileTime = dwCycle; 728 m_ds_dwFilebps = pTmpWfx->nAvgBytesPerSec; 729 if(m_factwav) 730 m_ds_dwPos = sizeof(WAVE_HEADER_FACT); //offset position 731 else 732 m_ds_dwPos = sizeof(WAVE_HEADER); 733 734 m_ds_dwLeave = dwSize; //leave data size 735 wfx.wFormatTag = pTmpWfx->wFormatTag; 736 wfx.nChannels = pTmpWfx->nChannels; 737 wfx.nSamplesPerSec = pTmpWfx->nSamplesPerSec; 738 wfx.wBitsPerSample = pTmpWfx->wBitsPerSample; 739 wfx.nBlockAlign = pTmpWfx->nBlockAlign; 740 wfx.nAvgBytesPerSec = pTmpWfx->nAvgBytesPerSec; 741 pTmpWfx = NULL; 742 if( FAILED( myCreatePCMBuffer( &wfx, &dwBuffSize ) ) ) 743 { 744 return EFAIL_PCMBUFFERERR; 745 } 746 m_ds_dwBuffSize = dwBuffSize; //返回缓冲区大小 747 //songinfo 748 WCHAR wcStr_info[100]; //output info 749 char cStr_info[100]; 750 char cStr_type[5]; 751 memset( wcStr_info,0,100 ); 752 memset( cStr_info,0,100 ); 753 memset( cStr_type,0,5 ); 754 WCHARtoChar( lpNameType, cStr_type ); 755 sprintf_s( cStr_info, "%s | %d kbps | %d Hz", cStr_type+1, 756 (wfx.wBitsPerSample * wfx.nChannels * wfx.nSamplesPerSec)/1000, wfx.nSamplesPerSec); //类型|比特率|频率 757 ChartoWCHAR( cStr_info, wcStr_info ); 758 SetWindowText( m_songinfo, wcStr_info ); 759 m_bmpg = FALSE; 760 } 761 else 762 { 763 return EFAIL_NOTSUPPORT; 764 } 765 766 767 return 0; 768 769 } 770 771 772 773 int myD3DSound::myGetWAVFormat( DWORD* dwSize, DWORD* dwCycle, FILE* fp, WAVEFORMATEX** wfx ) 774 { 775 WAVE_HEADER wave_header; 776 WAVE_HEADER_FACT wave_header2; 777 #ifndef _DEBUG 778 volatile WAVEFORMATEX wave_format; 779 #else 780 WAVEFORMATEX wave_format; 781 #endif 782 char fact[4]; 783 fseek( fp, 38, SEEK_SET ); 784 fread( fact, 1, 4, fp ); 785 fseek( fp, 0, SEEK_SET ); 786 if( memcmp(fact,"fact",4) == 0 ) 787 { 788 fread( &wave_header2, 1, sizeof(WAVE_HEADER_FACT), fp); 789 m_factwav = TRUE; 790 if(memcmp(wave_header2.riff_sig, "RIFF", 4) || 791 memcmp(wave_header2.wave_sig, "WAVE", 4) || 792 memcmp(wave_header2.format_sig, "fmt ", 4) ) 793 { 794 return -1; 795 } 796 wave_format.wFormatTag = WAVE_FORMAT_PCM; 797 wave_format.nChannels = wave_header2.channels; 798 wave_format.nSamplesPerSec = wave_header2.sample_rate; 799 wave_format.wBitsPerSample = wave_header2.bits_per_sample; 800 wave_format.nBlockAlign = wave_header2.bits_per_sample / 8 * wave_header2.channels; 801 wave_format.nAvgBytesPerSec = wave_header2.sample_rate * wave_format.nBlockAlign; 802 *dwSize = wave_header2.data_size; 803 *dwCycle = wave_header2.data_size / wave_format.nAvgBytesPerSec; 804 } 805 else 806 { 807 fread( &wave_header, 1, sizeof(WAVE_HEADER), fp); 808 m_factwav = FALSE; 809 if(memcmp(wave_header.riff_sig, "RIFF", 4) || 810 memcmp(wave_header.wave_sig, "WAVE", 4) || 811 memcmp(wave_header.format_sig, "fmt ", 4) ) 812 { 813 return -1; 814 } 815 wave_format.wFormatTag = WAVE_FORMAT_PCM; 816 wave_format.nChannels = wave_header.channels; 817 wave_format.nSamplesPerSec = wave_header.sample_rate; 818 wave_format.wBitsPerSample = wave_header.bits_per_sample; 819 wave_format.nBlockAlign = wave_header.bits_per_sample / 8 * wave_header.channels; 820 wave_format.nAvgBytesPerSec = wave_header.sample_rate * wave_format.nBlockAlign; 821 *dwSize = wave_header.data_size; 822 *dwCycle = wave_header.data_size / wave_format.nAvgBytesPerSec; 823 } 824 825 *wfx = (WAVEFORMATEX*)&wave_format; 826 return 0; 827 } 828 829 830 831 832 int myD3DSound::myGetMP3Format( char* filestr, DWORD* dwSize, DWORD* dwCycle, int* bitrate, WAVEFORMATEX** wfx, bool isClose ) 833 { 834 int ret = MPG123_OK; 835 int channels = 0; //声道 836 int encoding = 0; //编码格式 837 long rate = 0; //频率 838 int perbits = 16; //bits per second 839 long fTime = 0; 840 long fSize = 0; 841 int simpleNum = 1152; 842 long frameNum; 843 //long streamSize; 844 long streamSize1; 845 long streamSize2; 846 long streamSize3; 847 FILE* tmpfp = NULL; 848 #ifndef _DEBUG 849 volatile WAVEFORMATEX wave_format; 850 #else 851 WAVEFORMATEX wave_format; 852 #endif 853 mpg123_frameinfo mpginfo; 854 855 cleanup(); 856 857 ret = mpg123_init(); 858 if(ret != MPG123_OK || ( m_mpghandle = mpg123_new(NULL, &ret) ) == NULL) 859 { 860 cleanup(); 861 return -1; 862 } 863 if( mpg123_open(m_mpghandle, filestr) != MPG123_OK || mpg123_getformat(m_mpghandle, &rate, &channels, &encoding) != MPG123_OK ) 864 { 865 cleanup(); 866 return -1; 867 } 868 869 if((encoding & MPG123_ENC_16) == MPG123_ENC_16) 870 perbits = 16; 871 else if((encoding & MPG123_ENC_32) == MPG123_ENC_32) 872 perbits = 32; 873 else 874 perbits = 8; 875 876 //wfx 877 wave_format.wFormatTag = WAVE_FORMAT_PCM; 878 wave_format.nChannels = channels; 879 wave_format.nSamplesPerSec = rate; 880 wave_format.wBitsPerSample = perbits; 881 wave_format.nBlockAlign = perbits / 8 * channels; 882 wave_format.nAvgBytesPerSec = rate * perbits / 8 * channels; 883 *wfx = (WAVEFORMATEX*)&wave_format; 884 885 mpg123_seek( m_mpghandle, 0, SEEK_END ); 886 frameNum = mpg123_tellframe( m_mpghandle ); 887 //总帧数法:Total_Time = Total_Frame_Number * (Sample_Number * (1 / Frame_Sample_Rate)) 888 fTime = (long)( frameNum * simpleNum / rate ); 889 890 //time and buffer size 891 *dwCycle = fTime; 892 //data size = ftime * nAvgBytesPerSec = (frameNum*simpleNum/rate)*(rate*perbits/8*channels) 893 *dwSize = frameNum * simpleNum * perbits * channels / 8; 894 895 if( mpg123_info( m_mpghandle, &mpginfo) != MPG123_OK ) 896 { 897 cleanup(); 898 return -1; 899 } 900 if(mpginfo.layer != 3) 901 { 902 cleanup(); 903 return -1; 904 } 905 906 //bit rate 907 if( mpginfo.vbr == MPG123_CBR ) 908 { 909 *bitrate = mpginfo.bitrate; 910 } 911 else if( mpginfo.vbr == MPG123_VBR ) 912 { 913 if( fopen_s( &tmpfp, filestr, "rb" ) == 0 ) 914 { 915 fseek( tmpfp, 0, SEEK_END ); 916 fSize = ftell( tmpfp ); //文件大小 917 fclose( tmpfp ); 918 tmpfp = NULL; 919 *bitrate = (fSize * 8)/(fTime*1000); //(kbits/s) : filesize(bytes)*8(bits)/filetime(s)/1000 920 //平均比特率 = 文件大小/总时间/1000 921 } 922 } 923 if(isClose) 924 { 925 cleanup(); 926 } 927 return 0; 928 } 929 930 931 932 933 HRESULT myD3DSound::myCreatePCMBuffer( WAVEFORMATEX* wfx, DWORD* dwBuffSize ) 934 { 935 DSBUFFERDESC dsbd; 936 WAVEFORMATEX wave; 937 IDirectSound8* lpDS8; 938 LPDIRECTSOUNDBUFFER pTmpBuffer; 939 940 LPDIRECTSOUNDNOTIFY8 pNotify; 941 942 DSBPOSITIONNOTIFY dspNotify; 943 944 DSCAPS caps; 945 946 if( m_pDirectSound == NULL ) 947 return E_FAIL; 948 949 lpDS8 = m_pDirectSound; 950 ZeroMemory(&dsbd, sizeof(dsbd)); 951 952 wave.wFormatTag = WAVE_FORMAT_PCM; 953 if( wfx ) 954 { 955 wave.nChannels = wfx->nChannels; //音频文件的通道数量 956 wave.nSamplesPerSec = wfx->nSamplesPerSec; //采样频率 957 wave.wBitsPerSample = wfx->wBitsPerSample; //每次采样样本的大小 958 } 959 else 960 { 961 wave.nChannels = 2; 962 wave.nSamplesPerSec = 44100; 963 wave.wBitsPerSample = 16; 964 } 965 wave.nBlockAlign = wave.nChannels * wave.wBitsPerSample / 8; 966 wave.nAvgBytesPerSec = wave.nSamplesPerSec * wave.nBlockAlign; 967 wave.cbSize = 0; 968 969 dsbd.dwSize = sizeof(dsbd); 970 dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY; 971 972 if( m_ds_dwFileSize > DSBSIZE_MAX ) 973 dsbd.dwBufferBytes = DSBSIZE_MAX; 974 else 975 dsbd.dwBufferBytes = m_ds_dwFileSize; 976 977 978 *dwBuffSize = dsbd.dwBufferBytes; //返回缓冲区大小 979 dsbd.lpwfxFormat = &wave; 980 981 caps.dwSize = sizeof(DSCAPS); 982 if( SUCCEEDED( lpDS8->GetCaps(&caps) ) ) 983 { 984 if( caps.dwMaxHwMixingStreamingBuffers > 0 ) 985 dsbd.dwFlags |= DSBCAPS_LOCDEFER; 986 else 987 dsbd.dwFlags |= DSBCAPS_STATIC; 988 } 989 990 if( FAILED( lpDS8->CreateSoundBuffer( &dsbd, &pTmpBuffer, NULL ) ) ) 991 return E_FAIL; 992 993 if( FAILED( pTmpBuffer->QueryInterface( IID_IDirectSoundBuffer8, (void**)&m_pBuffer8 ) ) ) 994 return E_FAIL; 995 pTmpBuffer->Release(); 996 997 998 999 if( FAILED( m_pBuffer8->QueryInterface( IID_IDirectSoundNotify,(void**)&pNotify ) ) )1000 return E_FAIL;1001 1002 dspNotify.dwOffset = dsbd.dwBufferBytes - 1;1003 m_hEvents[0] = CreateEvent(NULL,FALSE,FALSE,NULL); 1004 dspNotify.hEventNotify = m_hEvents[0];1005 pNotify->SetNotificationPositions( 1, &dspNotify);1006 pNotify->Release(); 1007 1008 fseek( m_fp, m_ds_dwPos, SEEK_SET );1009 if( myReadBuffer( 0, dsbd.dwBufferBytes ) )1010 {1011 m_ds_dwPos += dsbd.dwBufferBytes;1012 //if(m_ds_dwFileSize <= DSBSIZE_MAX), this m_ds_dwLeave will be 01013 m_ds_dwLeave -= dsbd.dwBufferBytes;1014 1015 return S_OK;1016 }1017 else1018 return E_FAIL;1019 1020 }1021 1022 1023 1024 1025 HRESULT myD3DSound::myCreateMPGBuffer( WAVEFORMATEX* wfx, DWORD* dwBuffSize )1026 {1027 DSBUFFERDESC dsbd;1028 WAVEFORMATEX wave;1029 IDirectSound8* lpDS8;1030 LPDIRECTSOUNDBUFFER pTmpBuffer;1031 1032 LPDIRECTSOUNDNOTIFY8 pNotify;1033 1034 DSBPOSITIONNOTIFY dspNotify;1035 1036 1037 DSCAPS caps;1038 1039 if( m_pDirectSound == NULL )1040 return E_FAIL;1041 1042 lpDS8 = m_pDirectSound;1043 ZeroMemory(&dsbd, sizeof(dsbd));1044 1045 wave.wFormatTag = WAVE_FORMAT_PCM;1046 if( wfx )1047 {1048 wave.nChannels = wfx->nChannels;1049 wave.nSamplesPerSec = wfx->nSamplesPerSec;1050 wave.wBitsPerSample = wfx->wBitsPerSample;1051 }1052 else1053 {1054 wave.nChannels = 2; 1055 wave.nSamplesPerSec = 44100;1056 wave.wBitsPerSample = 16;1057 }1058 wave.nBlockAlign = wave.nChannels * wave.wBitsPerSample / 8;1059 wave.nAvgBytesPerSec = wave.nSamplesPerSec * wave.nBlockAlign;1060 wave.cbSize = 0;1061 1062 dsbd.dwSize = sizeof(dsbd); 1063 dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY;1064 1065 if( m_ds_dwFileSize > DSBSIZE_MAX )1066 dsbd.dwBufferBytes = DSBSIZE_MAX;1067 else1068 dsbd.dwBufferBytes = m_ds_dwFileSize;1069 1070 *dwBuffSize = dsbd.dwBufferBytes; //返回缓冲区大小1071 dsbd.lpwfxFormat = &wave;1072 1073 caps.dwSize = sizeof(DSCAPS);1074 if( SUCCEEDED( lpDS8->GetCaps(&caps) ) )1075 {1076 if( caps.dwMaxHwMixingStreamingBuffers > 0 )1077 dsbd.dwFlags |= DSBCAPS_LOCDEFER;1078 else1079 dsbd.dwFlags |= DSBCAPS_STATIC;1080 }1081 1082 if( FAILED( lpDS8->CreateSoundBuffer( &dsbd, &pTmpBuffer, NULL ) ) )1083 return E_FAIL;1084 1085 if( FAILED( pTmpBuffer->QueryInterface( IID_IDirectSoundBuffer8, (void**)&m_pBuffer8 ) ) )1086 return E_FAIL;1087 pTmpBuffer->Release();1088 1089 1090 if( FAILED( m_pBuffer8->QueryInterface( IID_IDirectSoundNotify,(void**)&pNotify ) ) )1091 return E_FAIL;1092 1093 dspNotify.dwOffset = dsbd.dwBufferBytes - 1;1094 m_hEvents[0] = CreateEvent(NULL,FALSE,FALSE,NULL); 1095 dspNotify.hEventNotify = m_hEvents[0];1096 pNotify->SetNotificationPositions( 1, &dspNotify);1097 pNotify->Release(); 1098 1099 if( m_mpghandle == NULL )1100 {1101 return E_FAIL;1102 }1103 mpg123_seek( m_mpghandle, 0, SEEK_SET );1104 1105 if( myReadMPGBuffer( 0, dsbd.dwBufferBytes ) )1106 {1107 m_ds_dwPos += dsbd.dwBufferBytes;1108 //if(m_ds_dwFileSize <= DSBSIZE_MAX), this m_ds_dwLeave will be 01109 m_ds_dwLeave -= dsbd.dwBufferBytes;1110 1111 return S_OK;1112 }1113 else1114 return E_FAIL;1115 }1116 1117 1118 1119 1120 bool myD3DSound::myReadBuffer( long lock_pos, long lock_size )1121 {1122 if( m_pBuffer8 == NULL || m_fp == NULL )1123 return 0;1124 1125 LPVOID buf = NULL; 1126 DWORD buf_len = 0;1127 if( SUCCEEDED( m_pBuffer8->Lock(lock_pos, lock_size, &buf, &buf_len, NULL, NULL, 0 ) ) )1128 {1129 fread( buf, 1, buf_len, m_fp );1130 m_pBuffer8->Unlock( buf, buf_len, NULL, 0 );1131 }1132 return 1;1133 }1134 1135 1136 1137 bool myD3DSound::myReadMPGBuffer( long lock_pos, long lock_size )1138 {1139 if( m_pBuffer8 == NULL || m_mpghandle == NULL )1140 return 0;1141 1142 LPVOID buf = NULL; 1143 DWORD buf_len = 0;1144 unsigned char* _buffer;1145 size_t outsize;1146 _buffer = (unsigned char*)malloc( lock_size * sizeof(unsigned char) );1147 if( SUCCEEDED( m_pBuffer8->Lock(lock_pos, lock_size, &buf, &buf_len, NULL, NULL, 0 ) ) )1148 {1149 mpg123_read( m_mpghandle, _buffer, lock_size, &outsize);1150 memcpy(buf, _buffer, outsize);1151 m_pBuffer8->Unlock( buf, buf_len, NULL, 0 );1152 }1153 return 1;1154 }1155 1156 1157 1158 1159 void myD3DSound::myCleanBuffer()1160 {1161 if( m_pBuffer8 )1162 {1163 m_pBuffer8->Stop();1164 }1165 SetPlaying( FALSE );1166 m_ds_dwPos = 0;1167 m_ds_dwLeave = 0;1168 m_ds_dwBuffSize =0;1169 m_dwPlayPos = 0;1170 SAFE_RELEASE( m_pBuffer8 );1171 #ifdef __MAX_BUFFER__1172 m_ds_dwFileSize = 0;1173 #endif1174 m_ds_dwFileTime = 0;1175 m_ds_dwFilebps = 0;1176 m_iScrollPos = 0;1177 1178 }1179 1180 void myD3DSound::cleanup()1181 {1182 if( m_mpghandle != NULL )1183 {1184 mpg123_close(m_mpghandle);1185 mpg123_delete(m_mpghandle);1186 m_mpghandle = NULL;1187 mpg123_exit();1188 }1189 }1190 1191 ///////////////////////1192 //global function1193 ///////////////////////1194 1195 1196 DWORD ThreadNotifyEvent( LPVOID thread_data )1197 {1198 DWORD res_msg = 0;1199 DWORD dwBuffsize = 0;1200 FILE* fp = NULL;1201 mpg123_handle* mpghandle = NULL;1202 if( g_pmySound == NULL )1203 return 0;1204 1205 g_pmySound->GetBufferValue( &fp, &mpghandle, &dwBuffsize );1206 if( (!g_pmySound->IsMPG3() && fp == NULL) || dwBuffsize == 0 || (g_pmySound->IsMPG3() && mpghandle == 0) )1207 return 0;1208 while( 1 )1209 {1210 res_msg = WaitForSingleObject( g_pmySound->m_hEvents[0], INFINITE ); 1211 1212 if( res_msg == WAIT_OBJECT_0 )1213 {1214 //update buffer1215 if( g_pmySound->m_ds_dwLeave == 0 )1216 {1217 g_pmySound->myStop();1218 ExitThread( 0 );1219 }1220 if( g_pmySound->IsMPG3() )1221 {1222 mpg123_seek( mpghandle, g_pmySound->m_ds_dwPos, SEEK_SET );1223 if( g_pmySound->m_ds_dwLeave <= dwBuffsize )1224 {1225 g_pmySound->myReadMPGBuffer( 0, g_pmySound->m_ds_dwLeave );1226 g_pmySound->m_ds_dwLeave = 0;1227 }1228 else1229 {1230 g_pmySound->myReadMPGBuffer( 0, dwBuffsize );1231 g_pmySound->m_ds_dwPos += dwBuffsize;1232 g_pmySound->m_ds_dwLeave -= dwBuffsize;1233 }1234 }1235 else1236 {1237 fseek(fp, g_pmySound->m_ds_dwPos, SEEK_SET);1238 if( g_pmySound->m_ds_dwLeave <= dwBuffsize )1239 {1240 g_pmySound->myReadBuffer( 0, g_pmySound->m_ds_dwLeave );1241 g_pmySound->m_ds_dwLeave = 0;1242 }1243 else1244 {1245 g_pmySound->myReadBuffer( 0, dwBuffsize );1246 g_pmySound->m_ds_dwPos += dwBuffsize;1247 g_pmySound->m_ds_dwLeave -= dwBuffsize;1248 }1249 }1250 } 1251 }1252 return 0;1253 }1254 1255 1256 DWORD ThreadNotifyEvent2( LPVOID thread_data )1257 {1258 DWORD d_FileSize = g_pmySound->m_ds_dwFileSize;1259 if( d_FileSize <= DSBSIZE_MAX )1260 {1261 DWORD d_PosFile;1262 int icut = 1;1263 while( 1 )1264 {1265 //update slider1266 if( g_pmySound->m_pBuffer8 == NULL )1267 {1268 SendMessage( g_pmySound->m_scrollbar, TBM_SETPOS, TRUE, (LPARAM)0 );1269 ExitThread(0);1270 }1271 if( FAILED( g_pmySound->m_pBuffer8->GetCurrentPosition( &d_PosFile, NULL ) ) )1272 {1273 SendMessage( g_pmySound->m_scrollbar, TBM_SETPOS, TRUE, (LPARAM)0 );1274 ExitThread(0);1275 }1276 if( d_PosFile >= (d_FileSize/100)* icut )1277 {1278 SendMessage( g_pmySound->m_scrollbar, TBM_SETPOS, TRUE, (LPARAM)icut );1279 icut++;1280 }1281 if( icut >= 100 )1282 {1283 ExitThread(0);1284 }1285 }1286 }1287 return 0;1288 }1289 1290 DWORD ThreadNotifyEvent3( LPVOID thread_data )1291 {1292 DWORD d_FileTime = g_pmySound->m_ds_dwFileTime;1293 DWORD d_Filebps = g_pmySound->m_ds_dwFilebps; //每秒传输字节1294 char ctmpTime[20];1295 WCHAR wtmpTime[20];1296 RECT rect;1297 memset(ctmpTime,0,20 );1298 memset(wtmpTime,0,20 );1299 DWORD d_Nowtime = 0;1300 sprintf_s( ctmpTime, "%02d:%02d / %02d:%02d", d_Nowtime/60, d_Nowtime%60, d_FileTime/60, d_FileTime%60 );1301 ChartoWCHAR( ctmpTime, wtmpTime );1302 SetWindowText( g_pmySound->m_songtime, wtmpTime );1303 while(1)1304 {1305 DWORD d_PosFile;1306 SYSTEMTIME time;1307 memset(ctmpTime,0,20 );1308 memset(wtmpTime,0,20 );1309 if( g_pmySound->m_pBuffer8 == NULL )1310 {1311 SetWindowText( g_pmySound->m_songtime, TEXT("00:00 / 00:00") );1312 ExitThread(0);1313 }1314 if( FAILED( g_pmySound->m_pBuffer8->GetCurrentPosition( &d_PosFile, NULL ) ) )1315 {1316 SetWindowText( g_pmySound->m_songtime, TEXT("00:00 / 00:00") );1317 ExitThread(0);1318 }1319 if( d_PosFile >= d_Filebps *(d_Nowtime+1) )1320 {1321 d_Nowtime++;1322 sprintf_s( ctmpTime, "%02d:%02d / %02d:%02d", d_Nowtime/60, d_Nowtime%60, d_FileTime/60, d_FileTime%60 );1323 ChartoWCHAR( ctmpTime, wtmpTime );1324 SetWindowText( g_pmySound->m_songtime, wtmpTime );1325 GetLocalTime( &time );1326 g_pmySound->mySetTimer( time );1327 SetWindowText( g_pmySound->m_timeshow, g_pmySound->myGetTimer() );1328 GetClientRect( g_pmySound->m_father, &rect );1329 InvalidateRect(g_pmySound->m_father,&rect,TRUE);1330 }1331 if( d_Nowtime == d_FileTime )1332 {1333 ExitThread(0);1334 }1335 }1336 }1337 1338 void ChartoWCHAR( const char* dsc, WCHAR* dst)1339 {1340 int len_c;1341 len_c = MultiByteToWideChar( CP_ACP,0,dsc,-1,NULL,0 );1342 MultiByteToWideChar( CP_ACP,0,dsc,-1,dst,len_c );1343 }1344 void WCHARtoChar( const WCHAR* dsc, char* dst )1345 {1346 int len_w;1347 len_w = WideCharToMultiByte(CP_ACP,0,dsc,-1,NULL,0,NULL,0);1348 WideCharToMultiByte(CP_ACP,0,dsc,-1,dst,len_w,NULL,0);1349 1350 }
D3DSound.cpp
原标题:WIN32下使用DirectSound接口的简单音频播放器(支持wav和mp3)
关键词:win