作为一个多媒体技术方面的初学者,我从wav文件的播放开始了解媒体播放的流程。

于是从建立两个线程开始,线程1用来将文件中的数据读到Buffer中去,以后称为读线程,线程2用来将Buffer中的数据送到设备的缓存,以后成为写线程。在最开始的时候,本着从易到难的精神,只开了一个buffer,我的想法很简单,先将数据写进这个Buffer,然后再送入设备进行播放,结果是每个Buffer中间有个空隙,这件事情是明摆着的,只采用一个Buffer肯定会导致这样的结果。在我看来是因为,设备将缓存中的数据播放完以后回头去Buffer取数据时发现Buffer是空的,因而再去读线程将PCM数据写入Buffer,也就是说设备有一段时间是无事可做的,听起来当然是断断续续的了。

听了高手的意见建立了一个包含两个Buffer的队列。

在用缓冲队列播放wav文件的时候却始终遇到一个问题,在播放的时候会在每个buffer读取数据的空隙产生停顿,这不是还和一个Buffer一样?难道采用两个Buffer也不行,要更多的Buffer?在网上查阅一些资料时发现理解上的偏差,两个buffer的切换流程如下所示:
Buffer[0] read;
Buffer[1] read;
Buffer[0] write to device;
Buffer[0]read;
Buffer[1]write to device;
也就是说,在Buffer[0]写入设备缓存之后就立刻再去取数据,也就是要保证在每个时刻都至少有一个Buffer里面有数据。

而我没有预先将Buffer写满,结局自然是和一个Buffer一样,想到这里我信心满满,觉得问题就要解决了,实际上还有很多问题。最终我放弃了建立两个子线程的想法(实际也不需要)。

有关Buffer的切换是想明白了,具体实施起来有两种方法。两种方法本质上是相同的,只不过是在waveOutOpen的时候最后一个参数的设置不同。
方法一:使用CALLBACK_FUNCTION(回调函数)。详见wince help中关于WaveOutProc函数的说明,只需要一个线程,通过回调函数查看系统消息WOM_DONE,来确定Buffer中的数据有没有被播放完毕,如果播放完毕则首先清空设备缓存(waveOutUnprepare),然后写Buffer,将Buffer中的数据送入设备缓存。还要判断文件是否已经读完,如果读完则跳出CallBack函数。
方法二:使用CALLBACK_EVENT。另外建立一个子线程。微软方面提供的文档告诉我们,应该检查dwFlags&&WHDR_DONE 是否为WHDR_DONE,以确保Buffer中的数据已经被播放完毕。当子线程开始执行时要判断这两个参数为真时,才能清空设备缓存。

两者的不同是,方法一中,系统会自动将播放完毕的Buffer地址返回到程序中,当callback函数被调用时可以直接使用;使用方法二,则要不断地判断Buffer中的数据是否已经播放完毕。

ps:采用多少个只要每次填入的数据(播放时间)在1/50~1/70秒以上,就不会出现咔咔的声音,和具体和设备性能以及声卡驱动有关。

 1方法一:callback_function
 2void CALLBACK waveOutProc(HWAVEOUT hWaveOut,UINT uMsg,DWORD dwInstance,
 3           DWORD dwParam1,DWORD dwParam2)
 4{
 5    WAVEHDR* pHdr = (PWAVEHDR)dwParam1; // 该参数由系统自动返回
 6    DWORD dwBytesRead;
 7    if(uMsg != WOM_DONE )   //判断是否播放完
 8    {
 9        printf("Error Wom_Done ");
10        return;
11    }
12    if(g_dwBytesleft == 0)
13                   {    
14                        g_res0 = waveOutReset(g_hWaveOut);
15                        g_res0 = waveOutClose(g_hWaveOut);
16                        if(g_res0 != MMSYSERR_NOERROR)
17                            printf("Wave out close error  ");
18                        GetExitCodeProcess( hProcess, lpExitCode);
19                        CloseHandle(hProcess);
20                        return;
21                   } //如果文件读完,退出程序
22    
23        mr = waveOutUnprepareHeader(hWaveOut,pHdr,sizeof(WAVEHDR));//清空设备缓存
24        //判断是否成功
25        DWORD dwBytesRequire = g_dwBytesleft < BUFFER_LENGTH ?  g_dwBytesleft:BUFFER_LENGTH;  //考虑剩余数据不足Buffer长度的情况
26        
27        ReadFile(g_fh, pHdr->lpData, dwBytesRequire ,&dwBytesRead, NULL);
28        
29//判断是否成功
30        g_dwBytesleft -= dwBytesRead;
31
32        mr = waveOutPrepareHeader(hWaveOut,pHdr,sizeof(WAVEHDR));
33        //判断是否成功
34        g_res0= waveOutWrite(g_hWaveOut, pHdr, sizeof(WAVEHDR));
35        //判断是否成功
36}
37
38方法二:callback_event
39DWORD WaveThreadFunc(DATABUFFER* pBuffer)
40{
41    printf("start thread ");
42    DWORD dwBytesRead = NULL;
43    g_bEndThread = FALSE;
44    int i = 0;  
45    while(!g_bEndThread)
46    {
47  
48        DWORD dw = WaitForSingleObject(g_hWaveEvent,0); //等待事件信号
49        if(dw == WAIT_OBJECT_0)
50        {    
51            printf("start switch ");
52            
53            {
54                pBuffer = &g_WaveBuffers[i]; //依次取各个Buffer
55                if((pBuffer->Hdr.dwFlags && WHDR_DONE) == WHDR_DONE)
56//Buffer内数据已经播放完成
57                
58                    waveOutUnprepareHeader(g_hWaveOut,&pBuffer->Hdr,sizeof(WAVEHDR));
59                    DWORD dwBytesRequire = g_dwBytesleft < BUFFERSIZE ?  g_dwBytesleft:BUFFERSIZE;
60                    ReadFile(g_fh, pBuffer->Hdr.lpData, dwBytesRequire ,&dwBytesRead, NULL);
61                    g_dwBytesleft -= dwBytesRead;
62                    waveOutPrepareHeader(g_hWaveOut,&pBuffer->Hdr,sizeof(WAVEHDR));
63                    waveOutWrite(g_hWaveOut, &pBuffer->Hdr, sizeof(WAVEHDR));
64                    
65                }
66                
67            }
68            i++;
69            if(i == NBUFFERS)
70                i = i%NBUFFERS;
71            ResetEvent(g_hWaveEvent);
72        }
73    }
74    ExitThread (0);
75    return 0;
76}

Walzer评点:

文章给全了用CALLBACK FUNCTION和CALLBACK EVENT两种处理方式的SAMPLE CODE。但要特别注意,这只是个DEMO,在多线程的项目中,WAVEOUT CALLBACK处理中调用WaveOutUnprepare甚至WaveOutWrite是很危险的,具体见我那篇《WaveOutReset的N种死法》。

本文转自Walzer博客园博客,原文链接:http://www.cnblogs.com/walzer/archive/2008/02/20/1074400.html,如需转载请自行联系原作者

用API函数播放wav文件声音不连续的解决方法相关推荐

  1. 谷歌浏览器播放视频只有声音没有画面解决方法

    1.打开自己电脑的谷歌浏览器.,点击一下浏览器页面右上角的三点符号,如图所示: 2在菜单中点击-设置: 3.将网页一直拉到最底端,看到一个-高级选项,点击-高级 4.在下拉菜单中找到-使用硬件加速模式 ...

  2. python调用系统api_Python调用系统底层API播放wav文件的方法

    本文实例讲述了Python调用系统底层API播放wav文件的方法.分享给大家供大家参考,具体如下: 这里未使用其他库,只是使用 pywin32 调用系统底层 API 播放 wav 文件. 具体代码如下 ...

  3. 关闭用playsound函数的WAV文件

    播放声音文件 PlaySound函数应用 1.关闭用playsound函数的WAV文件 PlaySound(0,NULL,0);即可 // test2.cpp : Defines the entry ...

  4. java wav文件_java播放wav文件

    STM32播放WAV程序_计算机软件及应用_IT/计算机_专业资料.此程序配置的采样率为16k,PWM频率144k; 此代码可以实现16位单声道的WAV文件解码,通过PWM 或者DAC输出音频.... ...

  5. matlab读取、播放wav文件

    [x, Fs] = audioread('E:\语音信号处理\speech_signal\bluesky3.wav'); %读取wav文件 fprintf('默认抽样频率为:%d\n', Fs); % ...

  6. java 下载wav 文件怎么打开,java播放wav文件,该如何处理

    java播放wav文件 用AudioInputStream实现 用application打开 放不出声音,下面提示错误 java.io.IOException: mark/reset not supp ...

  7. QSoundEffect播放wav没有声音,提示 using null output device, none available.

    记:在银河麒麟下(ARM版)用QT播放音频文件时没有声音,但实际点击文件播放却有声音. 1. 网上方法 ①在网上搜了一下,大部分说是由于在程序运行目录下缺少audio文件夹,需要从Qt安装目录下拷贝过 ...

  8. python 播放 wav 文件

    python 播放 wav 文件 import pyaudio import wave def play_wav(wav_path):CHUNK = 1024# print("wav_pat ...

  9. php 内容写入文件内容_PHP使用内置函数file_put_contents写入文件及追加内容的方法...

    本文实例讲述了PHP使用内置函数file_put_contents写入文件及追加内容的方法.分享给大家供大家参考,具体如下: 以追加形式写入内容 当设置 flags 参数值为 FILE_APPEND ...

最新文章

  1. 2021 线性代数 第五章 习题课
  2. windows下 composer常见问题及处理
  3. postman怎么不登陆使用_最新百度云不限速,免安装、免登陆、不限速,打开网站就能使用...
  4. SpringCloud工作笔记061---springBoot maven 打包jar报错_serverEndpointExporter
  5. 数据--第35课 - 创建二叉树
  6. RDP报表工具:六大优势铸造核心竞争力
  7. Ant Design Vue下载本地文件(其他框架也适用)
  8. 树莓派3连接ps4无线手柄
  9. 微信公众号问题:{errcode:40125,errmsg:invalid appsecret, view more at http:\/\/t.cn\/LOEdzVq, hints: [ ...
  10. try catch 的使用
  11. 用C语言打印菱形图案
  12. pandas的一些理解
  13. 均值不等式中考_不等式(初三不等式100道带答案)
  14. OBS/Python、Lua
  15. 计算材料学中的四巨头之间的关系
  16. lua可以调用mysql数据库吗_Lua 数据库访问
  17. android build.version,android兼容性.使用Build.VERSION_CODES时我很困惑
  18. Android 开发文档doc下载 ,怎么找到它
  19. macOS “应用程序不能打开”
  20. 数据分析实例-平安银行股票分析

热门文章

  1. AndroidStudio设置不自动弹出 Documentation 窗口
  2. ios重签名iReSign
  3. https的双向认证
  4. View和ViewGroup的层次关系
  5. MacBook双开微信
  6. sql server版本特性简介、版本介绍简介
  7. 第二章 使用unittest模块扩展功能测试
  8. 这些老外的开源技术养活了很多国产软件
  9. Redis学习第三课:Redis Hash类型及操作
  10. flex4 s:Datagrid s:typicalItem