如何编程读取Wave文件数据并指挥声卡播放Wave文件?

山顶洞人-平 2003-02-12 11:10:37
不用控件,而根据Wave文件的格式播放
问几个问题:
1。取得文件头信息后,后面是PCM数据,请问怎么让声卡播放PCM数据?能否给例子?
2。如果有资料,请贴上看看,谢谢大家
...全文
275 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
akiy 2003-02-12
  • 打赏
  • 举报
回复
// Wave.h

#ifndef __WAVE_H__
#define __WAVE_H__

#include <mmsystem.h>
#import <winmm.lib>
class CWave
{

public:
CWave();
CWave( const char * );
CWave( const char *, HINSTANCE );
CWave( int, HINSTANCE );
~CWave();

int DeviceCount( void );

BOOL Load( const char * );
BOOL Load( const char *, HINSTANCE );
BOOL Load( int, HINSTANCE );

BOOL Play( BOOL bLoop = FALSE );
BOOL PlayFromDisk( const char * );
BOOL PlayFromRes( const char *, HINSTANCE );
BOOL PlayFromRes( int, HINSTANCE );
BOOL Stop( void );
BOOL Close( void );

BOOL IsLoaded( void );

protected:
void InitVars( void );

int m_nDevices;
BOOL m_bLoaded;
char *m_lpSoundData;
HANDLE m_hResHandle;

};

#endif
// Wave.cpp

#include "stdafx.h"
#include "Wave.h"
#include"Winuser.h"

CWave::CWave()
{

InitVars();

}

CWave::CWave( const char *lpszFilename )
{

InitVars();
Load( lpszFilename );

}

CWave::CWave( const char *lpszResID, HINSTANCE hInstance )
{

InitVars();
Load( lpszResID, hInstance );

}

CWave::CWave( int nResID, HINSTANCE hInstance )
{

InitVars();
Load( nResID, hInstance );

}

void CWave::InitVars( void )
{

m_bLoaded = FALSE;
m_lpSoundData = NULL;
m_hResHandle = NULL;
m_nDevices = waveOutGetNumDevs();

}

CWave::~CWave()
{

Close();

}

int CWave::DeviceCount( void )
{

return( m_nDevices );

}

BOOL CWave::IsLoaded( void )
{

return( m_bLoaded );

}

BOOL CWave::Load( const char *lpszFilename )
{

Close();

CFile File;
if( !File.Open( lpszFilename, CFile::modeRead ) )
return( FALSE );

DWORD dwFileLength = File.GetLength();
m_lpSoundData = new char [dwFileLength];
if( m_lpSoundData == NULL )
return( FALSE );

if( File.Read( m_lpSoundData, dwFileLength )
!= dwFileLength )
return( FALSE );

m_bLoaded = TRUE;

return( TRUE );

}

BOOL CWave::Load( const char *lpszResID, HINSTANCE hInstance )
{

Close();

HANDLE hResInfo;
hResInfo = FindResource ( hInstance, lpszResID, "WAVE" );
if( hResInfo == NULL )
return( FALSE );

m_hResHandle = LoadResource( hInstance, (HRSRC) hResInfo );
if( m_hResHandle == NULL )
return( FALSE );

m_lpSoundData = (char *) LockResource( m_hResHandle );
if( m_lpSoundData == NULL )
return( FALSE );

m_bLoaded = TRUE;

return( TRUE );

}

BOOL CWave::Load( int nResID, HINSTANCE hInstance )
{

Close();

HANDLE hResInfo;
hResInfo = FindResource ( hInstance,
MAKEINTRESOURCE( nResID ), "WAVE" );
if( hResInfo == NULL )
return( FALSE );

m_hResHandle = LoadResource( hInstance, (HRSRC) hResInfo );
if( m_hResHandle == NULL )
return( FALSE );

m_lpSoundData = (char *) LockResource( m_hResHandle );
if( m_lpSoundData == NULL )
return( FALSE );

m_bLoaded = TRUE;

return( TRUE );

}

BOOL CWave::Play( BOOL bLoop )
{

if( !m_bLoaded )
return( FALSE );

Stop();

DWORD dwFlags = SND_MEMORY | SND_ASYNC | SND_NODEFAULT;
if( bLoop )
dwFlags |= SND_LOOP;

return( PlaySound( m_lpSoundData, NULL, dwFlags ) );

}

BOOL CWave::PlayFromDisk( const char *lpszFilename )
{

Stop();

return( PlaySound( lpszFilename, NULL,
SND_FILENAME | SND_SYNC | SND_NODEFAULT ) );

}

BOOL CWave::PlayFromRes( const char *lpszResID,
HINSTANCE hInstance )
{

return( PlaySound( lpszResID, hInstance,
SND_RESOURCE | SND_SYNC | SND_NODEFAULT ) );

}

BOOL CWave::PlayFromRes( int nResID, HINSTANCE hInstance )
{

return( PlaySound( MAKEINTRESOURCE( nResID ),
hInstance, SND_RESOURCE | SND_SYNC | SND_NODEFAULT ) );

}

BOOL CWave::Stop( void )
{

return( PlaySound( NULL, NULL, NULL ) );

}

BOOL CWave::Close( void )
{

Stop();

if( m_hResHandle != NULL ){
UnlockResource( m_hResHandle );
FreeResource( m_hResHandle );
}
else if( m_lpSoundData != NULL )
delete [] m_lpSoundData;

m_hResHandle = NULL;
m_lpSoundData = NULL;

return( TRUE );

}

用这个把,很好用的
CWave m_wave;
m_wave.Load(IDR_WAVE1,AfxGetInstanceHandle());
m_wave.Play();

Probug 2003-02-12
  • 打赏
  • 举报
回复
先用waveOutOpen打开设备,再读几块数据到缓冲waveOutWrite,在MM_WOM_DONE事件中循环读数据到缓冲就行.
duwenyong 2003-02-12
  • 打赏
  • 举报
回复
Windows低层波形音频数据块结构 WAVEHDR,其声明如下:

type struct{
LPSTR lpData; //指向锁定的数据缓冲区的指针
DWORD dwBufferLength; //数据缓冲区的大小
DWORD dwByteRecorded; //录音时指明缓冲区中的数据量
DWORD dwUser; //用户数据
DWORD dwFlag; //提供缓冲区信息的标志
DWORD dwLoops; //循环播放的次数
struct wavehdr_tag *lpNext; //保留
DWORD reserved; //保留
} WAVEHDR;


常用mmio函数及实现过程简介:
mmioOpen( ) 打开一个RIFF文件
mmioDescend ( ) 进入块
mmioRead( ); 该取RIFF文件
mmioAscend ( ); 跳出块
mmioClose( ); 关闭PIFF文件
对于块来说,进入块和跳出块是配对的。
读取WAV文件的读取过程:
mmioOpen( ) 打开文件

mmioDescend ("WAVE") 进入"fmt"块

mmioRead( ) 读取WAVE文件格式信息

mmioAscend ( ) 跳出"fmt"块

mmioDescend ("data") 进入"data"块

mmioRead( ) 读取WAVE数据信息

mmioClose( ) 关闭文件。

输出WAV文件的过程:
WaveOutOpen () 打开一个输出设备

WaveOutPrepareHeader() 准备WAVE数据头。

WaveOutWrite() 将数据写入设备并开始播放

WaveOutReset() 停止播放并重置管理器

WaveOutClose() 并闭播放设备

WaveOutUnpareHeader() 清理用WaveOutPrepareHeader准备的Wave。


在使用低级音频函数播放音频时,应用程序必须不断地向设备驱动程序提供数据块,直到播放结束。
WINDOWS提供两种方法管理波形重放:一是使用窗口消息管理,二是使用低级回调函数管理。
另外,通过使用waveOutPause、waveOutRestart和waveOutReset来进行暂停、重新启动和停止播放

录音开始后,每当有采样数据填满数据块后,设备驱动程序就会发消息MM_WIM_DATA给用户窗口,
相应的消息回调函数OnMmWimData(...)对数据块中的采样数据进行处理,然后就可以发送给输出
设备进行回放,每当一个音频数据块播放完毕,设备驱动程序又会发出消息MM_WOM_DONE,相应
的消息回调函数 OnMmWomDone(...)记录音频数据并经必要准备后重新发送给输入设备,以准备
接收后续的采样数据。这样,最初为输入设备准备的音频数据块就在消息的控制下,在输入、输出
设备间循环使用,无需人为控制实现了实时采集、处理和播放。当结束通话时要关闭音频输入设备,
这时音频设备驱动程序会发送MM_WIM_CLOSE消息,可在相应的消息函数OnMmWimClose(..)中清除赋
给输入、输出设备的音频数据块。

*/
duwenyong 2003-02-12
  • 打赏
  • 举报
回复
int CWaveMix::Continue()
{
CWaveMix wav;
if(!wav.Open("2.wav"))
return FALSE;
memcpy(lpData,wav.lpData,DataSize);
// WaveHead.lpData=(LPSTR)wav.lpData;
// WaveHead.dwBufferLength=wav.DataSize;
if(waveOutPrepareHeader(hWaveOut,&WaveHead, sizeof(WAVEHDR)))
return Close();
if(waveOutWrite(hWaveOut,&WaveHead,sizeof(WAVEHDR)))
return Close();
return TRUE;
}

//#define min(a, b) (((a) < (b)) ? (a) : (b))
//把多个WAVE文件混音播放
int CWaveMix::Add(char* name)
{
register int x;
if(!OpenFlage)
return Open(name);

CWaveMix wav;
if(!wav.Open(name))
return FALSE;

MMTIME time;
//获得WAV文件当前播放位置
time.wType=TIME_BYTES;
if(waveOutGetPosition(hWaveOut,&time,sizeof(MMTIME)))
time.u.cb=0;
DWORD start=((time.u.cb>>1)<<1);
DWORD end=min(DataSize_start,wav.DataSize);
// DWORD end=wav.DataSize;
register WAVDATA* lpd=lpData+start;
for(register DWORD i=0;i<end;i++)
{
//将两组WAV文件数据相加,并检测数据大小是否合法,如果
//数据大小越界,则分别取最大值和最小值
x=(((*(lpd+i))+(*(wav.lpData+i))))-128;
if(x<0)
x=0;
if(x>255)
x=255;
*(lpd+i)=(BYTE)(x);
}
return TRUE;
}

int CWaveMix::Stop()
{
return !waveOutReset(hWaveOut);
}

int CWaveMix::Close()
{
if(hWaveOut)
{
waveOutReset(hWaveOut);
waveOutUnprepareHeader(hWaveOut,&WaveHead,sizeof(WAVEHDR));
waveOutClose(hWaveOut);
}


if(lpData)LocalUnlock(hData);
if(hData)GlobalFree(hData);
memset(this,0,sizeof(CWaveMix));
return 0;
}


void CWaveMix::MixData(PBYTE lpWavSrc[], PBYTE lpWavDst, int iChannels, WORD wLen)
{
int i,iSum;
for(int j=0;j<wLen;j++)
{
iSum=128;//静音时数值为128
for(i=0;i<iChannels;i++)
iSum=iSum+*(lpWavSrc[i]+j)-128;
if(iSum<0) iSum=0;
if(iSum>255) iSum=255;
*lpWavDst++=iSum;
}
}
csdnworldboy 2003-02-12
  • 打赏
  • 举报
回复
// WaveMix.cpp: implementation of the CWaveMix class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WavPlay.h"
#include "WaveMix.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CWaveMix::CWaveMix()
{
memset(this,0,sizeof(CWaveMix));
}

CWaveMix::~CWaveMix()
{
Close();
}

int CWaveMix::Open(char* name)
{
HMMIO hMmio;
MMCKINFO pinfo;
MMCKINFO cinfo;

if(hMmio)
Close();

//打开WAV文件,返回一个HMMIO句柄
hMmio=mmioOpen(name,NULL,MMIO_READ);
if(!hMmio)
return FALSE;
OpenFlage=1;

//查找父块"wave";
pinfo.fccType=mmioFOURCC('W','A','V','E');
if(mmioDescend(hMmio,&pinfo,NULL,MMIO_FINDRIFF))
goto FALSE_END;

//查找子块"fmt" parent"riff";
cinfo.ckid=mmioFOURCC('f','m','t',' ');
if(mmioDescend(hMmio,&cinfo,&pinfo,MMIO_FINDCHUNK))
goto FALSE_END;

mmioRead(hMmio,(LPSTR)&pFormat,sizeof(PCMWAVEFORMAT));//cinfo.cksize);
if(pFormat.wf.wFormatTag!=WAVE_FORMAT_PCM)
goto FALSE_END;

//跳入块"FMT"
mmioAscend(hMmio,&cinfo,0);

//查找数据块
cinfo.ckid=mmioFOURCC('d','a','t','a');
if(mmioDescend(hMmio,&cinfo,&pinfo,MMIO_FINDCHUNK))
goto FALSE_END;
DataSize=cinfo.cksize;
//读取数据
hData=GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE,DataSize);
lpData=(WAVDATA*)GlobalLock(hData);
if( !hData || !lpData ) goto FALSE_END;
if(mmioRead(hMmio,(HPSTR)lpData,DataSize)!=(LRESULT)DataSize)
goto FALSE_END;

//close and return
mmioClose(hMmio,MMIO_FHOPEN);
return TRUE;

FALSE_END:
if(hMmio)mmioClose(hMmio,MMIO_FHOPEN);
if(lpData)LocalUnlock(hData);
if(hData)GlobalFree(hData);
memset(this,0,sizeof(CWaveMix));
return 0;
}

int CWaveMix::Play(HWND hP)
{
if(!OpenFlage)
return FALSE;

//检测系统播放功能
if(waveOutOpen(NULL,WAVE_MAPPER,(WAVEFORMATEX*)&pFormat,NULL,NULL,WAVE_FORMAT_QUERY))
return Close();

if(waveOutOpen(&hWaveOut,WAVE_MAPPER,(WAVEFORMATEX*)&pFormat,(DWORD)hP,0,CALLBACK_WINDOW))
return Close();

WaveHead.lpData=(LPSTR)lpData;
WaveHead.dwBufferLength=DataSize;
WaveHead.dwFlags=0L;
WaveHead.dwLoops=0L;
DataSize_start=DataSize;

//往WAV设备中添加数据
if(waveOutPrepareHeader(hWaveOut,&WaveHead, sizeof(WAVEHDR)))
return Close();

if(waveOutWrite(hWaveOut,&WaveHead,sizeof(WAVEHDR)))
return Close();

return TRUE;
}
csdnworldboy 2003-02-12
  • 打赏
  • 举报
回复
// WaveMix.h: interface for the CWaveMix class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_WAVEMIX_H__A44E17C0_5869_11D6_9B5E_00055DA0ABC3__INCLUDED_)
#define AFX_WAVEMIX_H__A44E17C0_5869_11D6_9B5E_00055DA0ABC3__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "mmsystem.h"
#ifdef WIN32
#define WAVDATA BYTE
#else
#define WAVDATA BYTE _huge
#define WAVEFORMATEX PCMWAVEFORMAT
#endif

#ifdef WIN32
#define WAV16DATA WORD
#else
#define WAV16DATA WORD _huge
#endif


class CWaveMix
{
public:
void MixData(PBYTE lpWaveSrc[],PBYTE lpWavDst,int iChannels,WORD wLen);
CWaveMix();
virtual ~CWaveMix();
int Open(char*); //打开一个WAV文件
int Play(HWND); //播放一个WAV文件
int Add(char*); //往正在播放的WAV设备中添加WAV 文件
int Stop(); //停止播放
int Close(); //关闭设备
int Continue();

DWORD DataSize;
WAVDATA* lpData;
private:
BOOL OpenFlage;
HGLOBAL hData;
DWORD DataSize_start;

PCMWAVEFORMAT pFormat;
WAVEHDR WaveHead;
HWAVEOUT hWaveOut;

int m_Channels;
};

#endif // !defined(AFX_WAVEMIX_H__A44E17C0_5869_11D6_9B5E_00055DA0ABC3__INCLUDED_)
csdnworldboy 2003-02-12
  • 打赏
  • 举报
回复
用WaveOut系列函数
录音用WaveIn系列函数
akiy 2003-02-12
  • 打赏
  • 举报
回复
《windows 程序设计》上面说的挺详细的,你有没有看过?
山顶洞人-平 2003-02-12
  • 打赏
  • 举报
回复
请解释一下,如果我不用API PlaySound函数,如何播放?我想从底层做起,学学而已,请大家帮忙!

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧