刚好最近接触了一些DirectSound,就写了一个小程序练练手,可以用来添加播放基本的wav和mp3音频文件的播放器。界面只是简单的GDI,dxsdk只使用了DirectSound8相关的接口。 DirectSound的使用步骤很简单
- 首先你要创建一个DirectSound8设备对象
HRESULT DirectSoundCreate8(
LPCGUID lpcGuidDevice,
LPDIRECTSOUND8 * ppDS8,
LPUNKNOWN pUnkOuter
)
当然要确保已安装了DXSDK,并在工程中设置了相关路径,包含dsound.lib。
lpcGuidDevice是声音设备对象GUID的地址,设置为NULL,表示默认设备;
ppDS8是返回的IDirectSound8接口指针,接下来就通过它来调用相关接口了;
pUnkOuter必须设置为NULL。
- 接下来是设置协作级别
HRESULT SetCooperativeLevel(
HWND hwnd,
DWORD dwLevel
)
hwnd是应用程序窗口句柄;
dwLevel是请求的协作级别,可选的值包括
DSSCL_NORMAL:正常级别,其他程序可共享设备;
DSSCL_PRIORITY:优先级别,设备为当前程序独占;
DSSCL_EXCLUSIVE:对于DX8.0及之后的版本,具有和DSSCL_PRIORITY相同的效果;
DSSCL_WRITEPRIMARY:当前程序具有主缓冲区的写权限,同时副缓冲区不能进行播放。
- 接下来就可以创建缓冲区了
HRESULT CreateSoundBuffer(
LPCDSBUFFERDESC pcDSBufferDesc,
LPDIRECTSOUNDBUFFER * ppDSBuffer,
LPUNKNOWN pUnkOuter
)
pcDSBufferDesc是一个DSBUFFERDESC结构的指针,用来描述要创建的声音缓冲区的地址;
ppDSBuffer是返回的IDirectSoundBuffer接口对象的指针;
pUnkOuter必须设置为NULL。 其中DSBUFFERDESC定义如下
typedef struct DSBUFFERDESC {
DWORD dwSize;
DWORD dwFlags;
DWORD dwBufferBytes;
DWORD dwReserved;
LPWAVEFORMATEX lpwfxFormat;
GUID guid3DAlgorithm;
} DSBUFFERDESC;
dwSize:结构的大小,用字节表示;
dwFlags:指定缓冲区的功能标志,常用的有DSBCAPS_GLOBALFOCUS(缓冲区为全局)、DSBCAPS_CTRLVOLUME(缓冲区具有音量控制功能)、DSBCAPS_CTRLPOSITIONNOTIFY(缓冲区具有位置通知功能)等。
dwBufferBytes:将要创建的缓冲区大小;
dwReserved:保留参数,必须为0;
lpwfxFormat:指向一个WAVEFORMATEX结构的指针,该结构用来指定缓冲区的波形格式。
- 然后是创建副缓冲区
通过使用IDirectSoundBuffer的QueryInterface方法,GUID设置为IID_IDirectSoundBuffer8,得到一个IDirectSoundBuffer8接口对象,用来控制播放等。
IDirectSoundBuffer8* m_pBuffer8 = NULL;
ppDSBuffer->QueryInterface( IID_IDirectSoundBuffer8, (void**)&m_pBuffer8 );
到这里创建m_pBuffer8成功的话,就可以调用Play(),Stop()的方法控制基本的播放了。
- 创建缓冲区通知对象
为了更好的控制缓冲区播放,我们还可以创建缓冲区通知对象,通过IDirectSoundBuffer8的QueryInterface方法,GUID设置为IID_IDirectSoundNotify,得到一个IDirectSoundNotify8接口对象。
IDirectSoundNotify8* pNotify = NULL;
m_pBuffer8->QueryInterface( IID_IDirectSoundNotify,(void**)&pNotify );
接口获取成功后,就可以设置通知位置了,也就是说当缓冲区播放到相应位置时,系统就会触发对应的事件,我们可以用来自己处理,填充数据、播放下一首或是停止等操作。
HRESULT SetNotificationPositions(
DWORD dwPositionNotifies,
LPCDSBPOSITIONNOTIFY pcPositionNotifies
)
dwPositionNotifies:DSBPOSITIONNOTIFY结构的数量;
pcPositionNotifies:指向DSBPOSITIONNOTIFY结构的指针。 其中DSBPOSITIONNOTIFY结构如下
typedef struct DSBPOSITIONNOTIFY {
DWORD dwOffset;
HANDLE hEventNotify;
} DSBPOSITIONNOTIFY;
dwOffset:触发位置,即缓冲区开始起的偏移量;
hEventNotify:触发事件句柄。
- 填充缓冲区
另外,在填充缓冲区的操作,必须在IDirectSoundBuffer8的lock和unlock方法之间进行:
HRESULT Lock(
DWORD dwOffset,
DWORD dwBytes,
LPVOID * ppvAudioPtr1,
LPDWORD pdwAudioBytes1,
LPVOID * ppvAudioPtr2,
LPDWORD pdwAudioBytes2,
DWORD dwFlags
)
dwOffset:要锁定的缓冲区起始位置,即从缓冲区首地址起的偏移量,用字节表示;
dwBytes:希望锁定的缓冲区内存大小,用字节表示;
ppvAudioPtr1:返回指向该锁定缓冲区的第一部分指针;
pdwAudioBytes1:ppvAudioPtr1指向地址的大小;
ppvAudioPtr2:返回指向该锁定缓冲区的第二部分指针,如果传入NULL,则该锁定区域全部返回到ppvAudioPtr1;
pdwAudioBytes2:ppvAudioPtr2指向地址的大小,如果ppvAudioPtr2传入NULL,该值则应传入0;
dwFlags:修改锁定事件的标志,一般不使用设为0。
HRESULT Unlock(
LPVOID pvAudioPtr1,
DWORD dwAudioBytes1,
LPVOID pvAudioPtr2,
DWORD dwAudioBytes2
)
填充完锁定缓冲区内存后,用来取消该锁定区域,参数可以参看lock中的介绍。 至此,IDirectSound8中主要用到的接口就这些了。
- WAVEFORMATEX结构
使用DirectSound播放PCM的重点就在于解析相应的音频文件格式获取相应信息,来填充WAVEFORMATEX结构。
typedef struct tWAVEFORMATEX {
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
WORD wBitsPerSample;
WORD cbSize;
} 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结构了。
- WAV音频文件格式的解析
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文件起始位置读取相应长度到结构即可,例如
struct WAVE_HEADER //44bytes
{
char riff_sig[];
long waveform_chunk_size;
char wave_sig[];
char format_sig[];
long format_chunk_size;
short format_tag;
short channels;
long sample_rate;
long bytes_per_sec;
short block_align;
short bits_per_sample;
char data_sig[];
long data_size;
}; struct WAVE_HEADER_FACT //58bytes
{
char riff_sig[];
long waveform_chunk_size;
char wave_sig[];
char format_sig[];
long format_chunk_size;
short format_tag;
short channels;
long sample_rate;
long bytes_per_sec;
short block_align;
short bits_per_sample;
short bits_per_sample2;
char fact_sig[];
short fact_size;
short fact_size2;
char fact_data[];
char data_sig[];
long data_size;
};
这里构建了两种类型的wav格式头,从文件中读取信息到结构,然后直接赋值给WAVEFORMATEX即可
WAVEFORMATEX wave_format;
WAVE_HEADER wave_header;
fread( &wave_header, , sizeof(WAVE_HEADER), fp);
wave_format.wFormatTag = WAVE_FORMAT_PCM;
wave_format.nChannels = wave_header.channels;
wave_format.nSamplesPerSec = wave_header.sample_rate;
wave_format.wBitsPerSample = wave_header.bits_per_sample;
wave_format.nBlockAlign = wave_header.bits_per_sample / * wave_header.channels;
wave_format.nAvgBytesPerSec = wave_header.sample_rate * wave_format.nBlockAlign;
- mp3音频文件格式的解析
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.xml三个规则文件添加至MSBuild\Microsoft.Cpp\v4.0\BuildCustomizations目录中。
选择Debug_X86或Release_x86配置,编译中可能会提示link无法打开输入文件xxx.o,将附加依赖和预链接命令中的.o全部替换为.obj。
将生成的libmpg123.lib添加到工程中,并将ports\MSVC++下的mpg123.h也引入到自己的工程中,接下来就可以使用libmpg123相关的方法来解析mp3文件了。 首先需要创建一个mpg123_handle句柄指针,过程如下
mpg123_handle* m_mpghandle = NULL;
int ret = MPG123_OK;
if( (ret = mpg123_init()) != MPG123_OK || (m_mpghandle = mpg123_new(NULL, &ret)) == NULL )
return -;
之后打开mp3文件并获取相关信息
long rate; //频率
int channels; //声道数
int encoding; //编码格式
if( mpg123_open(m_mpghandle, "youfile.mp3") != MPG123_OK || mpg123_getformat(m_mpghandle, &rate, &channels, &encoding) != MPG123_OK )
return -;
通过判断encoding来设置量化数,并赋值WAVEFORMATEX结构,代码如下
WAVEFORMATEX wave_format;
int perbits = ; //量化数
if((encoding & MPG123_ENC_16) == MPG123_ENC_16)
perbits = ;
else if((encoding & MPG123_ENC_32) == MPG123_ENC_32)
perbits = ;
else
perbits = ; wave_format.wFormatTag = WAVE_FORMAT_PCM;
wave_format.nChannels = channels;
wave_format.nSamplesPerSec = rate;
wave_format.wBitsPerSample = perbits;
wave_format.nBlockAlign = perbits / * channels;
wave_format.nAvgBytesPerSec = rate * perbits / * channels;
另外再说一下如何获取mp3文件的时长和比特率,mp3文件是由帧所构成的,想要知道播放时长,就可以通过总帧数 * 每一帧的时长来推算获得。
总帧数可通过mpg123_tellframe获取,而每一帧时长 = 每一帧的采样个数 * 每一采样的时长 = 每一帧的采样个数 * (1/每一帧的采样频率),
而无论哪种编码的mp3文件每一帧的采样个数都是1152,所以计算时长代码如下:
long frameNum;
long fileTime;
mpg123_seek( m_mpghandle, , SEEK_END );
frameNum = mpg123_tellframe( m_mpghandle ); //获取总帧数
fTime = (long)( frameNum * / rate );
而计算比特率,需要区别编码格式,如果是CBR,则比特率是固定的;
如果是VBR,由于比特率不固定,所以只能大概取一个平均比特率,计算公式为 平均比特率 = 文件大小(字节)*8 / 总时长 /1000,计算比特率代码如下:
mpg123_frameinfo mpginfo;
int bitrate;
long filesize;
FILE* tmpfp = NULL;
if( mpg123_info( m_mpghandle, &mpginfo) != MPG123_OK )
return -;
if(mpginfo.layer != )
return -;
if( mpginfo.vbr == MPG123_CBR )
bitrate = mpginfo.bitrate;
else if( mpginfo.vbr == MPG123_VBR )
{
if( fopen_s( &tmpfp, "youfile.mp3", "rb" ) == )
{
fseek( tmpfp, , SEEK_END );
filesize = ftell( tmpfp );
fclose( tmpfp );
tmpfp = NULL;
bitrate = (filesize * )/(fTime*);
}
}
将mp3文件解析并创建好缓冲区之后,就可以通过mpg123_read方法将文件数据填充至缓冲区了,代码如下
void* buf = NULL;
DWORD buf_len = ;
unsigned char* _buffer;
size_t outsize;
_buffer = (unsigned char*)malloc( lock_size * sizeof(unsigned char) );
if( SUCCEEDED( m_pBuffer8->Lock(lock_pos, lock_size, &buf, &buf_len, NULL, NULL, ) ) )
{
mpg123_read( m_mpghandle, _buffer, lock_size, &outsize);
memcpy(buf, _buffer, outsize);
m_pBuffer8->Unlock( buf, buf_len, NULL, );
}
其中lock_pos和lock_size 分别是缓冲区的偏移量和锁定大小。
mpg13_read的第2个参数返回实际读取到的数据指针,第3个参数是希望读取的数据长度,第4个参数返回实际读取的数据长度。
以下是全部代码: D3DPlayer.h
#ifndef __D3D_PLAYER__
#define __D3D_PLAYER__ #include "resource.h" #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
#define SAFE_DELETE(p) { if(p) {delete (p); (p)=NULL; } } #endif
D3DPlayer.h
D3DPlayer.cpp
#include "stdafx.h"
#include "D3DPlayer.h"
#include "D3DSound.h" #define MAX_LOADSTRING 100
#define WS_MYPLAYERWINDOW (WS_OVERLAPPED | \
WS_CAPTION | \
WS_SYSMENU | \
WS_MINIMIZEBOX | \
WS_MAXIMIZEBOX)
#define MYWINWIDTH 700
#define MYWINHEIGHT 300 HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
extern myD3DSound* g_pmySound; BOOL PreTranslateMessage(LPMSG pMsg);
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine); MSG msg;
HACCEL hAccelTable; myD3DSound* pDXSound = new myD3DSound;
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_D3DPLAYER, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance); if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
} hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_D3DPLAYER)); while (GetMessage(&msg, NULL, , ))
{
if(!PreTranslateMessage(&msg))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
} SAFE_DELETE(g_pmySound);
return (int) msg.wParam;
} ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = ;
wcex.cbWndExtra = ;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYPLAYER));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)( COLOR_WINDOW - );
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_D3DPLAYER);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_MYSMALL)); return RegisterClassEx(&wcex);
} BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
int iWidth;
int iHeight;
hInst = hInstance; // 将实例句柄存储在全局变量中
iWidth=GetSystemMetrics(SM_CXSCREEN);
iHeight=GetSystemMetrics(SM_CYSCREEN); hWnd = CreateWindow(szWindowClass, szTitle, WS_MYPLAYERWINDOW, (iWidth-MYWINWIDTH)/,
(iHeight-MYWINHEIGHT)/, MYWINWIDTH, MYWINHEIGHT, NULL, NULL, hInstance, NULL); if (!hWnd)
{
return FALSE;
}
//
g_pmySound->myInit( hWnd ); ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd); return TRUE;
} LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
RECT rect;
LRESULT res;
UINT CtrlId;
DRAWITEMSTRUCT *dis;
HFONT hfont;
NMHDR *pNMHDR; switch (message)
{
case WM_NOTIFY:
{
pNMHDR = (LPNMHDR)lParam;
switch( pNMHDR->code )
{
case NM_DBLCLK:
{
if( pNMHDR->idFrom == IDB_SONGLIST )
{
int i = SendMessage(g_pmySound->m_listview, LVM_GETNEXTITEM, -, LVNI_SELECTED);
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if(pNMListView && pNMListView->iItem >= && pNMListView->iItem < g_pmySound->mySongNum() )
{
g_pmySound->mySetPlayInfo(pNMListView,TRUE);
g_pmySound->myPlay();
GetClientRect( hWnd, &rect );
InvalidateRect(hWnd,&rect,TRUE);
}
}
}break;
case NM_CLICK:
{
if( pNMHDR->idFrom == IDB_SONGLIST )
{
SendMessage(g_pmySound->m_listview, LVM_GETNEXTITEM, -, LVNI_SELECTED);
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if(pNMListView && pNMListView->iItem >= && pNMListView->iItem < g_pmySound->mySongNum() )
{
g_pmySound->mySetPlayInfo(pNMListView,FALSE);
LVITEM vitem;
LVITEM* pvitem;
HANDLE hProcess;
DWORD PID;
GetWindowThreadProcessId(hWnd, &PID);
hProcess=OpenProcess(PROCESS_ALL_ACCESS,false,PID);
vitem.iItem = pNMListView->iItem;
vitem.state = LVIS_SELECTED|LVIS_FOCUSED;
vitem.stateMask = LVIS_SELECTED|LVIS_FOCUSED;
pvitem=(LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof(LVITEM),MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, pvitem, &vitem, sizeof(LVITEM), NULL);
SendMessage(g_pmySound->m_listview, LVM_SETITEMSTATE, (WPARAM)pNMListView->iItem, (LPARAM)pvitem);
GetClientRect( hWnd, &rect );
InvalidateRect(hWnd,&rect,TRUE); }
}
else if( pNMHDR->idFrom == )
{
int i = ;
}
}break;
default:
break;
}
break;
}
case WM_COMMAND:
{
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
res = MessageBox( hWnd, TEXT("是否关闭"), TEXT("退出"), MB_YESNO );
if( res == IDYES )
{
DestroyWindow( hWnd );
}
break;
case IDB_OPEN:
g_pmySound->myOpenFile();
GetClientRect( hWnd, &rect );
InvalidateRect(hWnd,&rect,TRUE);
break;
case IDB_CLOSE:
g_pmySound->myCloseFile();
GetClientRect( hWnd, &rect );
InvalidateRect(hWnd,&rect,TRUE);
break;
case IDB_PLAY:
g_pmySound->myPlay();
GetClientRect( hWnd, &rect );
InvalidateRect(hWnd,&rect,TRUE);
break;
case IDB_STOP:
g_pmySound->myStop();
GetClientRect( hWnd, &rect );
InvalidateRect(hWnd,&rect,TRUE);
break;
case IDB_PAUSE:
g_pmySound->myPause();
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
}
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
EndPaint(hWnd, &ps);
break;
}
case WM_DRAWITEM:
{
CtrlId = (UINT)wParam;
dis = (LPDRAWITEMSTRUCT)lParam;
int lw,lh,fw,fh,len; //ctrl width,ctrl height,font w,font h,font size
WCHAR txt[MAX_PATH];
lw=(dis->rcItem.right)-(dis->rcItem.left);
lh=(dis->rcItem.bottom)-(dis->rcItem.top);
fh=lh;
len=GetWindowText(dis->hwndItem,txt,MAX_PATH);
txt[len] = ;
fw=lw/(len+);
if( IDB_SONGTEXT == CtrlId || IDB_PLAYING == CtrlId )
{
fh = ;
fw = ;
}
else if( IDB_SONGINFO == CtrlId )
{
fw = ;
}
else if( IDB_SONGTIME == CtrlId || IDB_TIMESHOW == CtrlId )
{
fh = ;
fw = ;
}
hfont=CreateFont( fh, fw, , , , FALSE, FALSE, FALSE,
GB2312_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH, TEXT("宋体") ); switch( CtrlId )
{
case IDB_PLAYING:
case IDB_SONGTEXT:
{
hfont = (HFONT)SelectObject( dis->hDC, hfont );
SetBkMode( dis->hDC, TRANSPARENT );
SetTextColor( dis->hDC, RGB(,,) );
TextOut( dis->hDC, , , txt,len+ );
hfont = (HFONT)SelectObject(dis->hDC, hfont );
DeleteObject(hfont);
}break;
case IDB_TIMESHOW:
{
SetTimer( hWnd, , , NULL );
hfont = (HFONT)SelectObject( dis->hDC, hfont );
SetBkMode( dis->hDC, TRANSPARENT );
SetTextColor( dis->hDC, RGB(,,) );
TextOut( dis->hDC, , , txt,len+ );
hfont = (HFONT)SelectObject(dis->hDC, hfont );
DeleteObject(hfont);
}break;
case IDB_SONGINFO:
{
hfont = (HFONT)SelectObject( dis->hDC, hfont );
SetBkMode( dis->hDC, TRANSPARENT );
SetTextColor( dis->hDC, RGB(,,) );
TextOut( dis->hDC, , , txt,len+ );
hfont = (HFONT)SelectObject(dis->hDC, hfont );
DeleteObject(hfont);
}break;
case IDB_SONGTIME:
{
hfont = (HFONT)SelectObject( dis->hDC, hfont );
SetBkMode( dis->hDC, TRANSPARENT );
SetTextColor( dis->hDC, RGB(,,) );
TextOut( dis->hDC, , , txt,len+ );
hfont = (HFONT)SelectObject(dis->hDC, hfont );
DeleteObject(hfont);
//GetClientRect( hWnd, &rect );
//InvalidateRect(hWnd,&rect,TRUE);
}break;
}
break;
}
case WM_DESTROY:
{ PostQuitMessage();
break;
}
case WM_CREATE:
{
g_pmySound->myCreateWin(hWnd);
g_pmySound->myInitList();
break;
}
case WM_SIZE:
{
g_pmySound->myChangeSize( hWnd );
break;
}
case WM_TIMER:
{
GetClientRect( hWnd, &rect );
InvalidateRect(hWnd,&rect,TRUE);
SYSTEMTIME time;
if( wParam == )
{
if( ! g_pmySound->isPlay() )
{
GetLocalTime( &time );
g_pmySound->mySetTimer( time );
SetWindowText( g_pmySound->m_timeshow, g_pmySound->myGetTimer() );
}
}
break;
}
case WM_CLOSE:
{
res = MessageBox( hWnd, TEXT("是否关闭"), TEXT("退出"), MB_YESNO );
if( res == IDYES )
{
DestroyWindow( hWnd );
}
break;
}
case WM_INITDIALOG:
{
break;
}
case WM_HSCROLL:
{
//no use
break;
}
case WM_LBUTTONUP:
{
//no use
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return ;
} INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE; case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
} BOOL PreTranslateMessage(LPMSG pMsg)
{
if( pMsg->message == WM_LBUTTONUP )
{
if( (HWND)pMsg->hwnd == g_pmySound->m_scrollbar )
{
g_pmySound->mySetScrollPos( TRUE, );
//return TRUE;
}
else if( (HWND)pMsg->hwnd == g_pmySound->m_volumebar )
{
g_pmySound->mySetVolumePos( TRUE, );
//return TRUE;
}
else
{
return FALSE;
}
}
return FALSE;
}
D3DPlayer.cpp
D3DSound.h
#ifndef __D3D_SOUND__
#define __D3D_SOUND__ #include "D3DPlayer.h"
#include "stdafx.h"
#include "mpg123.h" #define MAX_AUDIO_BUF 4
#define BUFFERNOTIFYSIZE 192000
#define IDB_OPEN 1001
#define IDB_CLOSE 1002
#define IDB_PLAY 1003
#define IDB_PAUSE 1004
#define IDB_STOP 1005
#define IDB_PLAYING 2001
#define IDB_TIMESHOW 2002
#define IDB_SONGINFO 2003
#define IDB_SONGTEXT 2004
#define IDB_SONGTIME 2005
#define IDB_SONGLIST 2006 struct WAVE_HEADER //44bytes
{
char riff_sig[];
long waveform_chunk_size;
char wave_sig[];
char format_sig[];
long format_chunk_size;
short format_tag;
short channels;
long sample_rate;
long bytes_per_sec;
short block_align;
short bits_per_sample;
char data_sig[];
long data_size;
}; struct WAVE_HEADER_FACT //58bytes
{
char riff_sig[];
long waveform_chunk_size;
char wave_sig[];
char format_sig[];
long format_chunk_size;
short format_tag;
short channels;
long sample_rate;
long bytes_per_sec;
short block_align;
short bits_per_sample;
short bits_per_sample2;
char fact_sig[];
short fact_size;
short fact_size2;
char fact_data[];
char data_sig[];
long data_size;
}; struct myListMember
{
int m_idx;
WCHAR m_name[];
WCHAR m_time[];
WCHAR m_type[];
WCHAR m_bits[];
WCHAR m_path[MAX_PATH];
}; static DWORD ThreadNotifyEvent( LPVOID thread_data );
static DWORD ThreadNotifyEvent2( LPVOID thread_data );
static DWORD ThreadNotifyEvent3( LPVOID thread_data );
void ChartoWCHAR( const char*, WCHAR* );
void WCHARtoChar( const WCHAR*, char* ); class myD3DSound
{
private:
OPENFILENAME opfn;
bool isPlaying;
DWORD m_ds_dwBuffSize; //dxsound缓冲区大小
HANDLE m_hThread; //控制buffer
DWORD m_thread_id;
HANDLE m_hThread2; //滚动条显示
DWORD m_thread_id2;
HANDLE m_hThread3; //剩余时间显示
DWORD m_thread_id3;
FILE* m_fp;
mpg123_handle* m_mpghandle;
DWORD m_dwPlayPos;
WCHAR m_wstrTime[];
WCHAR m_wSongTime[]; //总时长
WCHAR m_wSongLeave[]; //剩余时长
typedef std::vector<myListMember> MY_SONG_LIST;
MY_SONG_LIST m_list;
WCHAR m_wSongPath[MAX_PATH]; //正在播放歌曲
WCHAR m_wSongName[MAX_PATH];
WCHAR m_wSongPathPre[MAX_PATH]; //单击选中歌曲
WCHAR m_wSongNamePre[MAX_PATH]; bool m_bmpg;
bool m_factwav;
public:
HWND m_father;
//button
HWND m_openbtn;
HWND m_closebtn;
HWND m_playbtn;
HWND m_pausebtn;
HWND m_stopbtn;
//static
HWND m_txtplaying;
HWND m_songtxt;
HWND m_songinfo;
HWND m_timeshow;
HWND m_songtime;
//
HWND m_scrollbar;
HWND m_volumebar;
HWND m_listview; LPDIRECTSOUND8 m_pDirectSound; //playing file info
LPDIRECTSOUNDBUFFER8 m_pBuffer8;
HANDLE m_hEvents[MAX_AUDIO_BUF];
#ifdef __MAX_BUFFER__
DWORD m_ds_dwFileSize; //文件大小
#endif
DWORD m_ds_dwFileTime; //文件时长(s)
DWORD m_ds_dwFilebps; //文件传输率
DWORD m_ds_dwPos; //文件偏移量
DWORD m_ds_dwLeave; //文件剩余量
int m_iScrollPos; //进度条位置
int m_iVolumePos; //音量条位置 enum eFAIL_CODE{
EFAIL_NOFILE = ,
EFAIL_NOTSUPPORT,
EFAIL_PCMBUFFERERR,
EFAIL_MPGBUFFERERR,
EFAIL_OPENFILEERR,
EFAIL_FORMATERR,
}; private:
int mySetSoundType();
int myGetWAVFormat( DWORD* dwSize, DWORD* dwCycle, FILE* fp, WAVEFORMATEX** wfx );
int myGetMP3Format( char* filestr, DWORD* dwSize, DWORD* dwCycle, int* bitrate, WAVEFORMATEX** wfx, bool isClose = TRUE );
HRESULT myCreatePCMBuffer( WAVEFORMATEX* wfx, DWORD* dwBuffSize );
HRESULT myCreateMPGBuffer( WAVEFORMATEX* wfx, DWORD* dwBuffSize );
void myUpdateList();
void myCleanBuffer();
void cleanup(); //clear mpg123 handle
public:
myD3DSound();
~myD3DSound();
HRESULT myInit( HWND );
void myCreateWin( HWND );
void myChangeSize( HWND );
void mySetList( WCHAR* );
void myInitList();
void mySetPlayInfo( NM_LISTVIEW* pNMListView, bool DBClick );
void mySetTimer( SYSTEMTIME );
bool myReadBuffer( long lock_pos, long lock_size );
bool myReadMPGBuffer( long lock_pos, long lock_size );
void mySetScrollPos( bool isUp, DWORD dPos );
void mySetVolumePos( bool isUp, DWORD dPos ); //button
void myOpenFile();
void myCloseFile();
int myPlay();
int myStop();
int myPause(); int mySongNum();
WCHAR* myGetTimer();
bool isPlay(){ return isPlaying; }
void SetPlaying( bool flags ){ isPlaying = flags; }
int GetBufferValue( FILE** fp, mpg123_handle** mpghandle, DWORD* BuffSize );
bool IsMPG3(){return m_bmpg;}
}; #endif
D3DSound.h
D3DSound.cpp
#include "D3DSound.h" const WCHAR* g_cwErrorMsg[] = {
TEXT(""),
TEXT("Pelase choose a file!"),
TEXT("Only supports .mp3 and .wav file!"),
TEXT("Create PCM buffer fail!"),
TEXT("Create MPG buffer fail!"),
TEXT("Cannot play the file!"),
TEXT("File format error!")
};
WCHAR* g_cwSongList[] = {
TEXT("序号"),
TEXT("名称"),
TEXT("持续时间"),
TEXT("扩展名"),
TEXT("比特率"),
TEXT("文件路径")
}; myD3DSound* g_pmySound = NULL; ///////////////////////
//public function
/////////////////////// HRESULT myD3DSound::myInit( HWND hWnd )
{
m_father = hWnd;
if( FAILED( DirectSoundCreate8(NULL, &m_pDirectSound, NULL) ) )
return E_FAIL; if( FAILED( m_pDirectSound->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ) ) )
return E_FAIL; return S_OK;
} myD3DSound::myD3DSound():
m_pDirectSound(NULL),
m_pBuffer8(NULL),
m_father(NULL),
m_openbtn(NULL),
m_closebtn(NULL),
m_playbtn(NULL),
m_pausebtn(NULL),
m_stopbtn(NULL),
m_txtplaying(NULL),
m_songtxt(NULL),
m_timeshow(NULL),
m_songinfo(NULL),
m_songtime(NULL),
m_scrollbar(NULL),
m_volumebar(NULL),
m_listview(NULL),
m_fp(NULL),
isPlaying(FALSE),
m_bmpg(FALSE),
m_factwav(FALSE),
m_hThread(NULL),
m_hThread2(NULL),
m_mpghandle(NULL)
{ m_ds_dwBuffSize = ;
#ifdef __MAX_BUFFER__
m_ds_dwFileSize = ;
#endif
m_ds_dwFileTime = ;
m_ds_dwFilebps = ;
m_ds_dwPos = ;
m_ds_dwLeave = ;
m_iScrollPos = ;
m_iVolumePos = ; m_thread_id = ;
m_dwPlayPos = ;
ZeroMemory(&opfn, sizeof(OPENFILENAME));
memset( m_wstrTime, , sizeof(m_wstrTime) );
memset( m_wSongTime, , sizeof(m_wSongTime) );
memset( m_wSongLeave, , sizeof(m_wSongLeave) );
memset( m_wSongPath, , sizeof(m_wSongPath) );
memset( m_wSongName, , sizeof(m_wSongName) );
memset( m_wSongPathPre, , sizeof(m_wSongPathPre) );
memset( m_wSongNamePre, , sizeof(m_wSongNamePre) );
m_list.clear();
g_pmySound = this;
} myD3DSound::~myD3DSound()
{
SAFE_RELEASE(m_pBuffer8);
SAFE_RELEASE(m_pDirectSound);
ZeroMemory(&opfn, sizeof(OPENFILENAME));
memset( m_wstrTime, , sizeof(m_wstrTime) );
memset( m_wSongTime, , sizeof(m_wSongTime) );
memset( m_wSongLeave, , sizeof(m_wSongLeave) );
memset( m_wSongPath, , sizeof(m_wSongPath) );
memset( m_wSongName, , sizeof(m_wSongName) );
memset( m_wSongPathPre, , sizeof(m_wSongPathPre) );
memset( m_wSongNamePre, , sizeof(m_wSongNamePre) );
if( m_fp )
{
fclose( m_fp );
m_fp = NULL;
}
m_list.clear();
cleanup();
} void myD3DSound::myCreateWin( HWND hWnd )
{
RECT dynamic_rc;
RECT button_rc;
RECT static_rc;
RECT list_rc;
GetClientRect( hWnd, &dynamic_rc );
LONG Width = dynamic_rc.right - dynamic_rc.left;
LONG Height = dynamic_rc.bottom - dynamic_rc.top; button_rc.left = ;
button_rc.top = ;
m_openbtn = CreateWindow( TEXT("BUTTON"), TEXT("Add"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
button_rc.left, button_rc.top, , , hWnd, (HMENU)IDB_OPEN, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); button_rc.left += ;
m_closebtn = CreateWindow( TEXT("BUTTON"), TEXT("Del"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
button_rc.left, button_rc.top, , , hWnd, (HMENU)IDB_CLOSE, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); button_rc.left += ;
m_playbtn = CreateWindow( TEXT("BUTTON"), TEXT("Play"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
button_rc.left, button_rc.top, , , hWnd, (HMENU)IDB_PLAY, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); button_rc.left += ;
m_pausebtn = CreateWindow( TEXT("BUTTON"), TEXT("Pause"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
button_rc.left, button_rc.top, , , hWnd, (HMENU)IDB_PAUSE, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); button_rc.left += ;
m_stopbtn = CreateWindow( TEXT("BUTTON"), TEXT("Stop"),WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
button_rc.left, button_rc.top, , , hWnd, (HMENU)IDB_STOP, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); button_rc.left += ;
m_scrollbar = CreateWindow( TRACKBAR_CLASS, TEXT(""), WS_VISIBLE|WS_CHILD|WS_BORDER|TBS_HORZ|TBS_TOOLTIPS,
button_rc.left, button_rc.top, , , hWnd, (HMENU), (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL );
//static
static_rc.left = ;
static_rc.top = ;
m_txtplaying = CreateWindow(TEXT("STATIC"), TEXT("playing now:"), WS_VISIBLE|WS_CHILD|SS_CENTER|SS_OWNERDRAW,
static_rc.left, static_rc.top, , , hWnd, (HMENU)IDB_PLAYING, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); static_rc.left += ( + );
m_songtxt = CreateWindow( TEXT("STATIC"), TEXT("无文件"), WS_VISIBLE|WS_CHILD|WS_BORDER|SS_LEFT|SS_OWNERDRAW,
static_rc.left, static_rc.top, , , hWnd, (HMENU)IDB_SONGTEXT, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); static_rc.left = ;
static_rc.top = Height - - ;
m_songinfo = CreateWindow( TEXT("STATIC"), TEXT("请选择wav文件进行播放。"), WS_VISIBLE|WS_CHILD|SS_CENTER|WS_BORDER|SS_OWNERDRAW,
static_rc.left, static_rc.top, , , hWnd, (HMENU)IDB_SONGINFO, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); static_rc.left += ( + );
m_songtime = CreateWindow( TEXT("STATIC"), TEXT("00:00 / 00:00"), WS_VISIBLE|WS_CHILD|SS_CENTER|WS_BORDER|SS_OWNERDRAW,
static_rc.left, static_rc.top, , , hWnd, (HMENU)IDB_SONGTIME, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); static_rc.left = Width - - ;
m_timeshow = CreateWindow( TEXT("STATIC"), TEXT(""), WS_VISIBLE|WS_CHILD|SS_CENTER|SS_OWNERDRAW,
static_rc.left, static_rc.top, , , hWnd, (HMENU)IDB_TIMESHOW, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL ); static_rc.left = Width - ;
static_rc.top = ;
m_volumebar = CreateWindow( TRACKBAR_CLASS, TEXT(""), WS_VISIBLE|WS_CHILD|WS_BORDER|TBS_HORZ|TBS_TOOLTIPS,
static_rc.left, static_rc.top, , , hWnd, (HMENU), (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL );
SendMessage( m_volumebar, TBM_SETPOS, TRUE, (LPARAM) ); list_rc.left = ;
list_rc.top = ;
m_listview = CreateWindowEx( LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT, WC_LISTVIEW, TEXT(""),
WS_VISIBLE|WS_CHILD|WS_BORDER|WS_VSCROLL|WS_HSCROLL|LVS_REPORT|LVS_SHOWSELALWAYS|LVS_SINGLESEL,
list_rc.left, list_rc.top, , , hWnd, (HMENU)IDB_SONGLIST, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL );
} void myD3DSound::myChangeSize( HWND hWnd )
{
RECT dynamic_rc;
GetClientRect( hWnd, &dynamic_rc );
LONG Width = dynamic_rc.right - dynamic_rc.left;
LONG Height = dynamic_rc.bottom - dynamic_rc.top; MoveWindow( m_songinfo, , Height - - , , , TRUE ); MoveWindow( m_songtime, + + , Height - - , , , TRUE ); MoveWindow( m_timeshow, Width - - , Height - - , , , TRUE ); MoveWindow( m_songtxt, , , Width - - , , TRUE ); MoveWindow( m_scrollbar, , , Width - - - , , TRUE ); MoveWindow( m_volumebar, Width - , , , , TRUE ); MoveWindow( m_listview, , , Width - , Height - - - , TRUE ); } void myD3DSound::mySetList( WCHAR* wcFilename )
{
myListMember tmplist;
//m_path
StrCpyW( tmplist.m_path, wcFilename );
//m_name
char cName[];
char tmpstr[MAX_PATH];
memset(cName,,);
memset(tmpstr,,MAX_PATH); WCHARtoChar( wcFilename, tmpstr );
int len = strlen(tmpstr);
char* p = &tmpstr[len];
int len_name = ;
bool isname = ;
bool bk = ;
while( !bk )
{
if( !isname && (*p == '.') )
{
isname = true;
p--;
continue;
}
if( isname )
{
if(*p == '\\' )
{
bk = true;
p++;
continue;
}
len_name++;
}
p--;
}
memcpy( cName, p, len_name );
ChartoWCHAR( cName, tmplist.m_name ); //type
LPWSTR lwType;
char ctmpType[];
char cType[];
lwType = PathFindExtension( wcFilename );
WCHARtoChar( lwType, ctmpType);
sprintf_s( cType, "%s", ctmpType+);
ChartoWCHAR( cType, tmplist.m_type ); if( StrCmpW( lwType, TEXT(".wav") ) == || StrCmpW( lwType, TEXT(".WAV") ) == )
{
//m_bits
FILE* tmpfp = NULL;
char cBits[];
DWORD tmpSize;
DWORD tmpCycle;
WAVEFORMATEX* pwfx = NULL;
memset(cBits,,);
if( fopen_s( &tmpfp, tmpstr, "rb" ) == )
{
myGetWAVFormat( &tmpSize, &tmpCycle, tmpfp, &pwfx );
if(pwfx != NULL)
{
sprintf_s( cBits, , "%d kbps", (pwfx->wBitsPerSample * pwfx->nChannels * pwfx->nSamplesPerSec)/ );
}
fclose(tmpfp);
}
ChartoWCHAR( cBits, tmplist.m_bits );
//time
char cTime[];
memset(cTime,,);
sprintf_s( cTime, "%02d:%02d", tmpCycle/, tmpCycle% );
ChartoWCHAR( cTime, tmplist.m_time );
}
else if( StrCmpW( lwType, TEXT(".mp3") ) == || StrCmpW( lwType, TEXT(".MP3") ) == )
{
char cBits[];
int bits;
DWORD tmpSize;
DWORD tmpCycle;
FILE* tmpfp = NULL;
WAVEFORMATEX* pwfx = NULL;
memset(cBits,,);
if( myGetMP3Format( tmpstr, &tmpSize, &tmpCycle, &bits, &pwfx ) == )
{
sprintf_s( cBits, , "%d kbps", bits);
}
ChartoWCHAR( cBits, tmplist.m_bits );
//time
char cTime[];
memset(cTime,,);
sprintf_s( cTime, "%02d:%02d", tmpCycle/, tmpCycle% );
ChartoWCHAR( cTime, tmplist.m_time );
} m_list.push_back( tmplist ); myUpdateList();
} void myD3DSound::myUpdateList()
{
ListView_DeleteAllItems(m_listview); int iSize = m_list.size();
LVITEM vitem;
vitem.mask = LVIF_TEXT;
for( int i = ; i< iSize; i++ )
{
char cIdx[];
WCHAR wIdx[];
memset(cIdx,,);
memset(wIdx,,); vitem.iItem = i;
m_list[i].m_idx = i+;
sprintf_s( cIdx, "%d", i+ );
ChartoWCHAR( cIdx, wIdx );
vitem.pszText = wIdx;
vitem.iSubItem = ;
ListView_InsertItem(m_listview, &vitem); vitem.iSubItem = ;
vitem.pszText = m_list[i].m_name;
ListView_SetItem( m_listview, &vitem);
vitem.iSubItem = ;
vitem.pszText = m_list[i].m_time;
ListView_SetItem(m_listview, &vitem);
vitem.iSubItem = ;
vitem.pszText = m_list[i].m_type;
ListView_SetItem(m_listview, &vitem);
vitem.iSubItem = ;
vitem.pszText = m_list[i].m_bits;
ListView_SetItem(m_listview, &vitem);
vitem.iSubItem = ;
vitem.pszText = m_list[i].m_path;
ListView_SetItem(m_listview, &vitem);
}
} int myD3DSound::mySongNum()
{
return m_list.size();
} void myD3DSound::myInitList()
{
LVCOLUMN vcl;
vcl.mask = LVCF_SUBITEM | LVCF_WIDTH | LVCF_TEXT | LVCF_FMT;
vcl.fmt = LVCFMT_LEFT;
for( int i = ; i < ; i++ )
{
vcl.pszText = g_cwSongList[i];
vcl.cx = ;
if( i == )
vcl.cx = ;
vcl.iSubItem = i;
ListView_InsertColumn( m_listview, i, &vcl );
}
} void myD3DSound::mySetTimer( SYSTEMTIME time )
{
char strtime[];
memset( strtime, , );
sprintf_s(strtime,"%04d-%02d-%02d %02d:%02d:%02d",
time.wYear,time.wMonth,time.wDay,time.wHour,time.wMinute,time.wSecond);
ChartoWCHAR(strtime,m_wstrTime);
} WCHAR* myD3DSound::myGetTimer()
{
return m_wstrTime;
} void myD3DSound::mySetScrollPos( bool isUp, DWORD dPos )
{
if(isUp)
{
int iPos;
iPos = SendMessage( m_scrollbar, TBM_GETPOS, , );
if( g_pmySound->isPlay() && g_pmySound->m_ds_dwFileSize <= DSBSIZE_MAX )
{
DWORD dNewPos;
dNewPos = iPos * (g_pmySound->m_ds_dwFileSize / );
g_pmySound->m_pBuffer8->SetCurrentPosition( dNewPos );
g_pmySound->m_pBuffer8->Play(, , DSBPLAY_LOOPING);
}
else
{
iPos = SendMessage( m_scrollbar, TBM_SETPOS, TRUE, (LPARAM) );
} }
else
{ }
} void myD3DSound::mySetVolumePos( bool isUp, DWORD dPos )
{
LONG vol = ;
double dbValue;
int iPos;
if( isUp && m_pBuffer8 != NULL )
{
iPos = SendMessage( m_volumebar, TBM_GETPOS, , );
if( iPos > && iPos <= )
{
dbValue = 20.0f * log10( (double)(iPos / 100.0f) );
vol = (LONG)(dbValue * 100.0f);
}
else if( iPos == )
{
vol = DSBVOLUME_MIN;
}
else
{
vol = DSBVOLUME_MAX;
}
m_pBuffer8->SetVolume( vol );
}
} void myD3DSound::myOpenFile()
{
WCHAR strFilename[MAX_PATH];
ZeroMemory(&opfn, sizeof(OPENFILENAME)); opfn.lStructSize = sizeof(OPENFILENAME); //结构体大小
opfn.lpstrFilter = L"所有文件\0*.*\0wav文件\0*.wav\0MP3文件\0*.mp3\0"; //过滤器
opfn.nFilterIndex = ; //过滤索引
opfn.lpstrFile = strFilename; //文件名的缓冲,不需要初始化则必须为null
opfn.lpstrFile[] = '\0';
opfn.nMaxFile = sizeof(strFilename);
opfn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; if( GetOpenFileName(&opfn) )
{
mySetList(strFilename);
}
} void myD3DSound::myCloseFile()
{
ZeroMemory(&opfn, sizeof(OPENFILENAME));
if( StrCmpW(m_wSongPath, m_wSongPathPre) == )
{
SetWindowText(m_songtxt, TEXT(""));
myStop();
if( m_fp )
{
fclose( m_fp );
m_fp = NULL;
}
}
MY_SONG_LIST::iterator Songit;
for( Songit = m_list.begin(); Songit != m_list.end(); Songit++ )
{
if( StrCmpW(Songit->m_path, m_wSongPathPre) == )
{
m_list.erase( Songit );
memset( m_wSongPathPre, , MAX_PATH );
memset( m_wSongPath, , MAX_PATH );
myUpdateList();
return;
}
} } void myD3DSound::mySetPlayInfo( NM_LISTVIEW* pNMListView, bool DBClick )
{
if( DBClick )
{
if( StrCmpW(m_list[pNMListView->iItem].m_path, m_wSongPath) )
{//双击新歌则停止当前播放
myStop();
}
StrCpyW( m_wSongPath, m_list[pNMListView->iItem].m_path );
StrCpyW( m_wSongName, m_list[pNMListView->iItem].m_name );
StrCpyW( m_wSongPathPre, m_list[pNMListView->iItem].m_path );
StrCpyW( m_wSongNamePre, m_list[pNMListView->iItem].m_name );
}
else
{//单击
StrCpyW( m_wSongPathPre, m_list[pNMListView->iItem].m_path );
StrCpyW( m_wSongNamePre, m_list[pNMListView->iItem].m_name );
if( ! isPlay() )
{
StrCpyW( m_wSongPath, m_list[pNMListView->iItem].m_path );
StrCpyW( m_wSongName, m_list[pNMListView->iItem].m_name );
}
}
} int myD3DSound::myPlay()
{
if( isPlay() )
{
if( StrCmpW( m_wSongPath, m_wSongPathPre ) )
{
myStop();
StrCpyW( m_wSongName, m_wSongNamePre );
StrCpyW( m_wSongPath, m_wSongPathPre );
}
else
{
return ;
}
}
if( m_pBuffer8 == NULL )
{
int res = ;
res = mySetSoundType();
if( res != )
{
MessageBox( m_father, g_cwErrorMsg[res], TEXT("ERROR"), MB_OK );
return ;
}
//create notification thread
m_hThread = CreateThread( NULL, , (LPTHREAD_START_ROUTINE)ThreadNotifyEvent, NULL, , &m_thread_id );
m_hThread2 = CreateThread( NULL, , (LPTHREAD_START_ROUTINE)ThreadNotifyEvent2, NULL, , &m_thread_id2 );
m_hThread3 = CreateThread( NULL, , (LPTHREAD_START_ROUTINE)ThreadNotifyEvent3, NULL, , &m_thread_id3 );
SetWindowText( m_songtxt, m_wSongName );
}
SetPlaying( TRUE );
mySetVolumePos(TRUE,);
m_pBuffer8->SetCurrentPosition( m_dwPlayPos );
m_pBuffer8->Play(, , DSBPLAY_LOOPING);
return ;
} int myD3DSound::myStop()
{
myCleanBuffer();
if( isPlay() )
{
CloseHandle( m_hEvents[] ); if( m_hThread != NULL )
{
TerminateThread( m_hThread, );
CloseHandle( m_hThread );
}
}
if( IsMPG3() )
{
cleanup();
}
SetWindowText( m_songinfo, TEXT("请选择wav文件进行播放。") );
StrCpyW( m_wSongName, TEXT(""));
StrCpyW( m_wSongPath, TEXT(""));
SetWindowText( m_songtxt, m_wSongName );
SendMessage( g_pmySound->m_scrollbar, TBM_SETPOS, TRUE, (LPARAM) );
SetWindowText( g_pmySound->m_songtime, TEXT("00:00 / 00:00") );
return ;
} int myD3DSound::myPause()
{
if( m_pBuffer8 == NULL )
return -;
if( isPlay() )
{
m_pBuffer8->GetCurrentPosition( &m_dwPlayPos, NULL );
SetPlaying( FALSE );
m_pBuffer8->Stop();
}
else
{
SetPlaying( TRUE );
m_pBuffer8->SetCurrentPosition( m_dwPlayPos );
m_pBuffer8->Play(, , DSBPLAY_LOOPING);
}
return ;
} int myD3DSound::GetBufferValue( FILE** fp, mpg123_handle** mpghandle, DWORD* BuffSize )
{
*fp = m_fp;
*mpghandle = m_mpghandle;
*BuffSize = m_ds_dwBuffSize;
return ;
} ///////////////////////
//private
/////////////////////// int myD3DSound::mySetSoundType()
{
WCHAR cNameStr[MAX_PATH];
LPWSTR lpNameType;
char cType[];
char cTmpStr[];
memset( cNameStr, , MAX_PATH );
memset( cType, , );
memset( cTmpStr, , ); StrCpyW( cNameStr, m_wSongPath ); if( cNameStr[] == '\0' )
{
return EFAIL_NOFILE;
}
lpNameType = PathFindExtension( cNameStr ); WCHARtoChar( lpNameType, cTmpStr);
sprintf_s( cType, "%s", cTmpStr+);
if( StrCmpW( lpNameType, TEXT(".mp3") ) == || StrCmpW( lpNameType, TEXT(".MP3") ) == )
{
DWORD dwSize; //stream size
DWORD dwCycle;
DWORD dwBuffSize;
int bitrate;
WAVEFORMATEX wfx;
WAVEFORMATEX* pTmpWfx = NULL;
char filepath[MAX_PATH];
memset( filepath, , MAX_PATH );
WCHARtoChar( cNameStr, filepath );
if( myGetMP3Format( filepath, &dwSize, &dwCycle, &bitrate, &pTmpWfx, FALSE ) != )
{
return EFAIL_FORMATERR;
}
m_ds_dwFileSize = dwSize; m_ds_dwFileTime = dwCycle;
m_ds_dwFilebps = pTmpWfx->nAvgBytesPerSec;
m_ds_dwPos = ; //offset position
m_ds_dwLeave = dwSize; //leave data size
wfx.wFormatTag = pTmpWfx->wFormatTag;
wfx.nChannels = pTmpWfx->nChannels;
wfx.nSamplesPerSec = pTmpWfx->nSamplesPerSec;
wfx.wBitsPerSample = pTmpWfx->wBitsPerSample;
wfx.nBlockAlign = pTmpWfx->nBlockAlign;
wfx.nAvgBytesPerSec = pTmpWfx->nAvgBytesPerSec;
pTmpWfx = NULL;
if( FAILED( myCreateMPGBuffer( &wfx, &dwBuffSize ) ) )
{
return EFAIL_MPGBUFFERERR;
}
m_ds_dwBuffSize = dwBuffSize;
//song info
WCHAR wcStr_info[];
char cStr_info[];
char cStr_type[];
memset( wcStr_info,, );
memset( cStr_info,, );
memset( cStr_type,, );
WCHARtoChar( lpNameType, cStr_type );
sprintf_s( cStr_info, "%s | %d kbps | %d Hz", cStr_type+, bitrate, wfx.nSamplesPerSec);
ChartoWCHAR( cStr_info, wcStr_info );
SetWindowText( m_songinfo, wcStr_info );
m_bmpg = TRUE;
} else if( StrCmpW( lpNameType, TEXT(".wav") ) == || StrCmpW( lpNameType, TEXT(".WAV") ) == )
{
WAVEFORMATEX wfx;
DWORD dwSize; //声音文件总大小
DWORD dwCycle; DWORD dwBuffSize; //创建的缓冲区总大小
int inum=WideCharToMultiByte(CP_ACP,,cNameStr,-,NULL,,NULL,);
char* cfilename = NULL;
cfilename = (char*)malloc( inum * sizeof(char) );
if( cfilename == NULL )
free(cfilename);
memset( cfilename, , inum * sizeof(char) );
WideCharToMultiByte(CP_ACP,,cNameStr,-,cfilename,inum,NULL,); if( fopen_s( &m_fp, cfilename, "rb" ) )
{
return EFAIL_OPENFILEERR;
}
WAVEFORMATEX* pTmpWfx = NULL;
myGetWAVFormat( &dwSize, &dwCycle, m_fp, &pTmpWfx ); if( pTmpWfx == NULL )
{
return EFAIL_FORMATERR;
}
m_ds_dwFileSize = dwSize; m_ds_dwFileTime = dwCycle;
m_ds_dwFilebps = pTmpWfx->nAvgBytesPerSec;
if(m_factwav)
m_ds_dwPos = sizeof(WAVE_HEADER_FACT); //offset position
else
m_ds_dwPos = sizeof(WAVE_HEADER); m_ds_dwLeave = dwSize; //leave data size
wfx.wFormatTag = pTmpWfx->wFormatTag;
wfx.nChannels = pTmpWfx->nChannels;
wfx.nSamplesPerSec = pTmpWfx->nSamplesPerSec;
wfx.wBitsPerSample = pTmpWfx->wBitsPerSample;
wfx.nBlockAlign = pTmpWfx->nBlockAlign;
wfx.nAvgBytesPerSec = pTmpWfx->nAvgBytesPerSec;
pTmpWfx = NULL;
if( FAILED( myCreatePCMBuffer( &wfx, &dwBuffSize ) ) )
{
return EFAIL_PCMBUFFERERR;
}
m_ds_dwBuffSize = dwBuffSize; //返回缓冲区大小
//songinfo
WCHAR wcStr_info[]; //output info
char cStr_info[];
char cStr_type[];
memset( wcStr_info,, );
memset( cStr_info,, );
memset( cStr_type,, );
WCHARtoChar( lpNameType, cStr_type );
sprintf_s( cStr_info, "%s | %d kbps | %d Hz", cStr_type+,
(wfx.wBitsPerSample * wfx.nChannels * wfx.nSamplesPerSec)/, wfx.nSamplesPerSec); //类型|比特率|频率
ChartoWCHAR( cStr_info, wcStr_info );
SetWindowText( m_songinfo, wcStr_info );
m_bmpg = FALSE;
}
else
{
return EFAIL_NOTSUPPORT;
} return ; } int myD3DSound::myGetWAVFormat( DWORD* dwSize, DWORD* dwCycle, FILE* fp, WAVEFORMATEX** wfx )
{
WAVE_HEADER wave_header;
WAVE_HEADER_FACT wave_header2;
#ifndef _DEBUG
volatile WAVEFORMATEX wave_format;
#else
WAVEFORMATEX wave_format;
#endif
char fact[];
fseek( fp, , SEEK_SET );
fread( fact, , , fp );
fseek( fp, , SEEK_SET );
if( memcmp(fact,"fact",) == )
{
fread( &wave_header2, , sizeof(WAVE_HEADER_FACT), fp);
m_factwav = TRUE;
if(memcmp(wave_header2.riff_sig, "RIFF", ) ||
memcmp(wave_header2.wave_sig, "WAVE", ) ||
memcmp(wave_header2.format_sig, "fmt ", ) )
{
return -;
}
wave_format.wFormatTag = WAVE_FORMAT_PCM;
wave_format.nChannels = wave_header2.channels;
wave_format.nSamplesPerSec = wave_header2.sample_rate;
wave_format.wBitsPerSample = wave_header2.bits_per_sample;
wave_format.nBlockAlign = wave_header2.bits_per_sample / * wave_header2.channels;
wave_format.nAvgBytesPerSec = wave_header2.sample_rate * wave_format.nBlockAlign;
*dwSize = wave_header2.data_size;
*dwCycle = wave_header2.data_size / wave_format.nAvgBytesPerSec;
}
else
{
fread( &wave_header, , sizeof(WAVE_HEADER), fp);
m_factwav = FALSE;
if(memcmp(wave_header.riff_sig, "RIFF", ) ||
memcmp(wave_header.wave_sig, "WAVE", ) ||
memcmp(wave_header.format_sig, "fmt ", ) )
{
return -;
}
wave_format.wFormatTag = WAVE_FORMAT_PCM;
wave_format.nChannels = wave_header.channels;
wave_format.nSamplesPerSec = wave_header.sample_rate;
wave_format.wBitsPerSample = wave_header.bits_per_sample;
wave_format.nBlockAlign = wave_header.bits_per_sample / * wave_header.channels;
wave_format.nAvgBytesPerSec = wave_header.sample_rate * wave_format.nBlockAlign;
*dwSize = wave_header.data_size;
*dwCycle = wave_header.data_size / wave_format.nAvgBytesPerSec;
} *wfx = (WAVEFORMATEX*)&wave_format;
return ;
} int myD3DSound::myGetMP3Format( char* filestr, DWORD* dwSize, DWORD* dwCycle, int* bitrate, WAVEFORMATEX** wfx, bool isClose )
{
int ret = MPG123_OK;
int channels = ; //声道
int encoding = ; //编码格式
long rate = ; //频率
int perbits = ; //bits per second
long fTime = ;
long fSize = ;
int simpleNum = ;
long frameNum;
//long streamSize;
long streamSize1;
long streamSize2;
long streamSize3;
FILE* tmpfp = NULL;
#ifndef _DEBUG
volatile WAVEFORMATEX wave_format;
#else
WAVEFORMATEX wave_format;
#endif
mpg123_frameinfo mpginfo; cleanup(); ret = mpg123_init();
if(ret != MPG123_OK || ( m_mpghandle = mpg123_new(NULL, &ret) ) == NULL)
{
cleanup();
return -;
}
if( mpg123_open(m_mpghandle, filestr) != MPG123_OK || mpg123_getformat(m_mpghandle, &rate, &channels, &encoding) != MPG123_OK )
{
cleanup();
return -;
} if((encoding & MPG123_ENC_16) == MPG123_ENC_16)
perbits = ;
else if((encoding & MPG123_ENC_32) == MPG123_ENC_32)
perbits = ;
else
perbits = ; //wfx
wave_format.wFormatTag = WAVE_FORMAT_PCM;
wave_format.nChannels = channels;
wave_format.nSamplesPerSec = rate;
wave_format.wBitsPerSample = perbits;
wave_format.nBlockAlign = perbits / * channels;
wave_format.nAvgBytesPerSec = rate * perbits / * channels;
*wfx = (WAVEFORMATEX*)&wave_format; mpg123_seek( m_mpghandle, , SEEK_END );
frameNum = mpg123_tellframe( m_mpghandle );
//总帧数法:Total_Time = Total_Frame_Number * (Sample_Number * (1 / Frame_Sample_Rate))
fTime = (long)( frameNum * simpleNum / rate ); //time and buffer size
*dwCycle = fTime;
//data size = ftime * nAvgBytesPerSec = (frameNum*simpleNum/rate)*(rate*perbits/8*channels)
*dwSize = frameNum * simpleNum * perbits * channels / ; if( mpg123_info( m_mpghandle, &mpginfo) != MPG123_OK )
{
cleanup();
return -;
}
if(mpginfo.layer != )
{
cleanup();
return -;
} //bit rate
if( mpginfo.vbr == MPG123_CBR )
{
*bitrate = mpginfo.bitrate;
}
else if( mpginfo.vbr == MPG123_VBR )
{
if( fopen_s( &tmpfp, filestr, "rb" ) == )
{
fseek( tmpfp, , SEEK_END );
fSize = ftell( tmpfp ); //文件大小
fclose( tmpfp );
tmpfp = NULL;
*bitrate = (fSize * )/(fTime*); //(kbits/s) : filesize(bytes)*8(bits)/filetime(s)/1000
//平均比特率 = 文件大小/总时间/1000
}
}
if(isClose)
{
cleanup();
}
return ;
} HRESULT myD3DSound::myCreatePCMBuffer( WAVEFORMATEX* wfx, DWORD* dwBuffSize )
{
DSBUFFERDESC dsbd;
WAVEFORMATEX wave;
IDirectSound8* lpDS8;
LPDIRECTSOUNDBUFFER pTmpBuffer; LPDIRECTSOUNDNOTIFY8 pNotify; DSBPOSITIONNOTIFY dspNotify; DSCAPS caps; if( m_pDirectSound == NULL )
return E_FAIL; lpDS8 = m_pDirectSound;
ZeroMemory(&dsbd, sizeof(dsbd)); wave.wFormatTag = WAVE_FORMAT_PCM;
if( wfx )
{
wave.nChannels = wfx->nChannels; //音频文件的通道数量
wave.nSamplesPerSec = wfx->nSamplesPerSec; //采样频率
wave.wBitsPerSample = wfx->wBitsPerSample; //每次采样样本的大小
}
else
{
wave.nChannels = ;
wave.nSamplesPerSec = ;
wave.wBitsPerSample = ;
}
wave.nBlockAlign = wave.nChannels * wave.wBitsPerSample / ;
wave.nAvgBytesPerSec = wave.nSamplesPerSec * wave.nBlockAlign;
wave.cbSize = ; dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY; if( m_ds_dwFileSize > DSBSIZE_MAX )
dsbd.dwBufferBytes = DSBSIZE_MAX;
else
dsbd.dwBufferBytes = m_ds_dwFileSize; *dwBuffSize = dsbd.dwBufferBytes; //返回缓冲区大小
dsbd.lpwfxFormat = &wave; caps.dwSize = sizeof(DSCAPS);
if( SUCCEEDED( lpDS8->GetCaps(&caps) ) )
{
if( caps.dwMaxHwMixingStreamingBuffers > )
dsbd.dwFlags |= DSBCAPS_LOCDEFER;
else
dsbd.dwFlags |= DSBCAPS_STATIC;
} if( FAILED( lpDS8->CreateSoundBuffer( &dsbd, &pTmpBuffer, NULL ) ) )
return E_FAIL; if( FAILED( pTmpBuffer->QueryInterface( IID_IDirectSoundBuffer8, (void**)&m_pBuffer8 ) ) )
return E_FAIL;
pTmpBuffer->Release(); if( FAILED( m_pBuffer8->QueryInterface( IID_IDirectSoundNotify,(void**)&pNotify ) ) )
return E_FAIL; dspNotify.dwOffset = dsbd.dwBufferBytes - ;
m_hEvents[] = CreateEvent(NULL,FALSE,FALSE,NULL);
dspNotify.hEventNotify = m_hEvents[];
pNotify->SetNotificationPositions( , &dspNotify);
pNotify->Release(); fseek( m_fp, m_ds_dwPos, SEEK_SET );
if( myReadBuffer( , dsbd.dwBufferBytes ) )
{
m_ds_dwPos += dsbd.dwBufferBytes;
//if(m_ds_dwFileSize <= DSBSIZE_MAX), this m_ds_dwLeave will be 0
m_ds_dwLeave -= dsbd.dwBufferBytes; return S_OK;
}
else
return E_FAIL; } HRESULT myD3DSound::myCreateMPGBuffer( WAVEFORMATEX* wfx, DWORD* dwBuffSize )
{
DSBUFFERDESC dsbd;
WAVEFORMATEX wave;
IDirectSound8* lpDS8;
LPDIRECTSOUNDBUFFER pTmpBuffer; LPDIRECTSOUNDNOTIFY8 pNotify; DSBPOSITIONNOTIFY dspNotify; DSCAPS caps; if( m_pDirectSound == NULL )
return E_FAIL; lpDS8 = m_pDirectSound;
ZeroMemory(&dsbd, sizeof(dsbd)); wave.wFormatTag = WAVE_FORMAT_PCM;
if( wfx )
{
wave.nChannels = wfx->nChannels;
wave.nSamplesPerSec = wfx->nSamplesPerSec;
wave.wBitsPerSample = wfx->wBitsPerSample;
}
else
{
wave.nChannels = ;
wave.nSamplesPerSec = ;
wave.wBitsPerSample = ;
}
wave.nBlockAlign = wave.nChannels * wave.wBitsPerSample / ;
wave.nAvgBytesPerSec = wave.nSamplesPerSec * wave.nBlockAlign;
wave.cbSize = ; dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY; if( m_ds_dwFileSize > DSBSIZE_MAX )
dsbd.dwBufferBytes = DSBSIZE_MAX;
else
dsbd.dwBufferBytes = m_ds_dwFileSize; *dwBuffSize = dsbd.dwBufferBytes; //返回缓冲区大小
dsbd.lpwfxFormat = &wave; caps.dwSize = sizeof(DSCAPS);
if( SUCCEEDED( lpDS8->GetCaps(&caps) ) )
{
if( caps.dwMaxHwMixingStreamingBuffers > )
dsbd.dwFlags |= DSBCAPS_LOCDEFER;
else
dsbd.dwFlags |= DSBCAPS_STATIC;
} if( FAILED( lpDS8->CreateSoundBuffer( &dsbd, &pTmpBuffer, NULL ) ) )
return E_FAIL; if( FAILED( pTmpBuffer->QueryInterface( IID_IDirectSoundBuffer8, (void**)&m_pBuffer8 ) ) )
return E_FAIL;
pTmpBuffer->Release(); if( FAILED( m_pBuffer8->QueryInterface( IID_IDirectSoundNotify,(void**)&pNotify ) ) )
return E_FAIL; dspNotify.dwOffset = dsbd.dwBufferBytes - ;
m_hEvents[] = CreateEvent(NULL,FALSE,FALSE,NULL);
dspNotify.hEventNotify = m_hEvents[];
pNotify->SetNotificationPositions( , &dspNotify);
pNotify->Release(); if( m_mpghandle == NULL )
{
return E_FAIL;
}
mpg123_seek( m_mpghandle, , SEEK_SET ); if( myReadMPGBuffer( , dsbd.dwBufferBytes ) )
{
m_ds_dwPos += dsbd.dwBufferBytes;
//if(m_ds_dwFileSize <= DSBSIZE_MAX), this m_ds_dwLeave will be 0
m_ds_dwLeave -= dsbd.dwBufferBytes; return S_OK;
}
else
return E_FAIL;
} bool myD3DSound::myReadBuffer( long lock_pos, long lock_size )
{
if( m_pBuffer8 == NULL || m_fp == NULL )
return ; LPVOID buf = NULL;
DWORD buf_len = ;
if( SUCCEEDED( m_pBuffer8->Lock(lock_pos, lock_size, &buf, &buf_len, NULL, NULL, ) ) )
{
fread( buf, , buf_len, m_fp );
m_pBuffer8->Unlock( buf, buf_len, NULL, );
}
return ;
} bool myD3DSound::myReadMPGBuffer( long lock_pos, long lock_size )
{
if( m_pBuffer8 == NULL || m_mpghandle == NULL )
return ; LPVOID buf = NULL;
DWORD buf_len = ;
unsigned char* _buffer;
size_t outsize;
_buffer = (unsigned char*)malloc( lock_size * sizeof(unsigned char) );
if( SUCCEEDED( m_pBuffer8->Lock(lock_pos, lock_size, &buf, &buf_len, NULL, NULL, ) ) )
{
mpg123_read( m_mpghandle, _buffer, lock_size, &outsize);
memcpy(buf, _buffer, outsize);
m_pBuffer8->Unlock( buf, buf_len, NULL, );
}
return ;
} void myD3DSound::myCleanBuffer()
{
if( m_pBuffer8 )
{
m_pBuffer8->Stop();
}
SetPlaying( FALSE );
m_ds_dwPos = ;
m_ds_dwLeave = ;
m_ds_dwBuffSize =;
m_dwPlayPos = ;
SAFE_RELEASE( m_pBuffer8 );
#ifdef __MAX_BUFFER__
m_ds_dwFileSize = ;
#endif
m_ds_dwFileTime = ;
m_ds_dwFilebps = ;
m_iScrollPos = ; } void myD3DSound::cleanup()
{
if( m_mpghandle != NULL )
{
mpg123_close(m_mpghandle);
mpg123_delete(m_mpghandle);
m_mpghandle = NULL;
mpg123_exit();
}
} ///////////////////////
//global function
/////////////////////// DWORD ThreadNotifyEvent( LPVOID thread_data )
{
DWORD res_msg = ;
DWORD dwBuffsize = ;
FILE* fp = NULL;
mpg123_handle* mpghandle = NULL;
if( g_pmySound == NULL )
return ; g_pmySound->GetBufferValue( &fp, &mpghandle, &dwBuffsize );
if( (!g_pmySound->IsMPG3() && fp == NULL) || dwBuffsize == || (g_pmySound->IsMPG3() && mpghandle == ) )
return ;
while( )
{
res_msg = WaitForSingleObject( g_pmySound->m_hEvents[], INFINITE ); if( res_msg == WAIT_OBJECT_0 )
{
//update buffer
if( g_pmySound->m_ds_dwLeave == )
{
g_pmySound->myStop();
ExitThread( );
}
if( g_pmySound->IsMPG3() )
{
mpg123_seek( mpghandle, g_pmySound->m_ds_dwPos, SEEK_SET );
if( g_pmySound->m_ds_dwLeave <= dwBuffsize )
{
g_pmySound->myReadMPGBuffer( , g_pmySound->m_ds_dwLeave );
g_pmySound->m_ds_dwLeave = ;
}
else
{
g_pmySound->myReadMPGBuffer( , dwBuffsize );
g_pmySound->m_ds_dwPos += dwBuffsize;
g_pmySound->m_ds_dwLeave -= dwBuffsize;
}
}
else
{
fseek(fp, g_pmySound->m_ds_dwPos, SEEK_SET);
if( g_pmySound->m_ds_dwLeave <= dwBuffsize )
{
g_pmySound->myReadBuffer( , g_pmySound->m_ds_dwLeave );
g_pmySound->m_ds_dwLeave = ;
}
else
{
g_pmySound->myReadBuffer( , dwBuffsize );
g_pmySound->m_ds_dwPos += dwBuffsize;
g_pmySound->m_ds_dwLeave -= dwBuffsize;
}
}
}
}
return ;
} DWORD ThreadNotifyEvent2( LPVOID thread_data )
{
DWORD d_FileSize = g_pmySound->m_ds_dwFileSize;
if( d_FileSize <= DSBSIZE_MAX )
{
DWORD d_PosFile;
int icut = ;
while( )
{
//update slider
if( g_pmySound->m_pBuffer8 == NULL )
{
SendMessage( g_pmySound->m_scrollbar, TBM_SETPOS, TRUE, (LPARAM) );
ExitThread();
}
if( FAILED( g_pmySound->m_pBuffer8->GetCurrentPosition( &d_PosFile, NULL ) ) )
{
SendMessage( g_pmySound->m_scrollbar, TBM_SETPOS, TRUE, (LPARAM) );
ExitThread();
}
if( d_PosFile >= (d_FileSize/)* icut )
{
SendMessage( g_pmySound->m_scrollbar, TBM_SETPOS, TRUE, (LPARAM)icut );
icut++;
}
if( icut >= )
{
ExitThread();
}
}
}
return ;
} DWORD ThreadNotifyEvent3( LPVOID thread_data )
{
DWORD d_FileTime = g_pmySound->m_ds_dwFileTime;
DWORD d_Filebps = g_pmySound->m_ds_dwFilebps; //每秒传输字节
char ctmpTime[];
WCHAR wtmpTime[];
RECT rect;
memset(ctmpTime,, );
memset(wtmpTime,, );
DWORD d_Nowtime = ;
sprintf_s( ctmpTime, "%02d:%02d / %02d:%02d", d_Nowtime/, d_Nowtime%, d_FileTime/, d_FileTime% );
ChartoWCHAR( ctmpTime, wtmpTime );
SetWindowText( g_pmySound->m_songtime, wtmpTime );
while()
{
DWORD d_PosFile;
SYSTEMTIME time;
memset(ctmpTime,, );
memset(wtmpTime,, );
if( g_pmySound->m_pBuffer8 == NULL )
{
SetWindowText( g_pmySound->m_songtime, TEXT("00:00 / 00:00") );
ExitThread();
}
if( FAILED( g_pmySound->m_pBuffer8->GetCurrentPosition( &d_PosFile, NULL ) ) )
{
SetWindowText( g_pmySound->m_songtime, TEXT("00:00 / 00:00") );
ExitThread();
}
if( d_PosFile >= d_Filebps *(d_Nowtime+) )
{
d_Nowtime++;
sprintf_s( ctmpTime, "%02d:%02d / %02d:%02d", d_Nowtime/, d_Nowtime%, d_FileTime/, d_FileTime% );
ChartoWCHAR( ctmpTime, wtmpTime );
SetWindowText( g_pmySound->m_songtime, wtmpTime );
GetLocalTime( &time );
g_pmySound->mySetTimer( time );
SetWindowText( g_pmySound->m_timeshow, g_pmySound->myGetTimer() );
GetClientRect( g_pmySound->m_father, &rect );
InvalidateRect(g_pmySound->m_father,&rect,TRUE);
}
if( d_Nowtime == d_FileTime )
{
ExitThread();
}
}
} void ChartoWCHAR( const char* dsc, WCHAR* dst)
{
int len_c;
len_c = MultiByteToWideChar( CP_ACP,,dsc,-,NULL, );
MultiByteToWideChar( CP_ACP,,dsc,-,dst,len_c );
}
void WCHARtoChar( const WCHAR* dsc, char* dst )
{
int len_w;
len_w = WideCharToMultiByte(CP_ACP,,dsc,-,NULL,,NULL,);
WideCharToMultiByte(CP_ACP,,dsc,-,dst,len_w,NULL,); }
D3DSound.cpp