在之前有写过一篇 使用C来调用音频输入设备录音:https://mp.csdn.net/console/editor/html/105217385

本次将在调用输入设备的基础上再调用输出设备将录音内容播放出来:

主要用到的函数:

waveOutGetNumDevs:返回系统中的输出设备数量(可以不用)

waveOutGetDevCaps:检查指定输出设备特性(可以不用)

waveOutOpen:打开默认的wave设备

waveOutPrepareHeader:准备一个波形数据块用于播放

waveOutWrite:播放WAVEHDR中指定的音频数据

waveOutUnprepareHeader:清除由waveOutPrepareHeader完成的准备

waveOutClose:关闭设备

以下是调用输出设备进行播放的代码:

#include <windows.h>
#include <mmsystem.h>
#include <stdio.h>
#include "PlayPcm.h"
#define BLOCK_SIZE 8192
#define BLOCK_COUNT 20
static void CALLBACK waveOutProc(HWAVEOUT, UINT, DWORD, DWORD, DWORD);
static WAVEHDR* allocateBlocks(int size, int count);
static void freeBlocks(WAVEHDR* blockArray);
static void writeAudio(HWAVEOUT hWaveOut, LPSTR data, int size);
static void WaveInitFormat(LPWAVEFORMATEX m_WaveFormat, WORD nCh, DWORD nSampleRate, WORD BitsPerSample);
static CRITICAL_SECTION waveCriticalSection;
static WAVEHDR* waveBlocks;
static volatile int waveFreeBlockCount;
static int waveCurrentBlock;
static BOOL waveOutFlag = FALSE;
static HWAVEOUT hWaveOut; /* device handle */
//int PlayPcm(int argc, char* argv[])
int PlayPcmInit()
{//返回系统中的输出设备数量int count = waveOutGetNumDevs();printf("\n音频输出数量:%d\n", count);if (count == 0){return 0;}//检查指定输出设备特性 参数:输出设备标识/句柄;结构体指针;结构体大小WAVEOUTCAPS waveOutcaps;MMRESULT mmResult = waveOutGetDevCaps(0, &waveOutcaps, sizeof(WAVEOUTCAPS));//WAVEOUTCAPS 结构体参数:wMid驱动程序标识、wPid输出设备产品标识、vDriverVersion驱动程序版本号、szPname[MAXPNAMELEN]制造商名称、dwFormats支持的格式、wChannels支持的声道数printf("\n音频输出设备:%s\n", waveOutcaps.szPname);if (MMSYSERR_NOERROR == mmResult){WAVEFORMATEX wfx; char buffer[1024];int i;/** 初始化模块变量*/waveBlocks = allocateBlocks(BLOCK_SIZE, BLOCK_COUNT);waveFreeBlockCount = BLOCK_COUNT;waveCurrentBlock = 0;InitializeCriticalSection(&waveCriticalSection);/**设置WAVEFORMATEX结构。*/WaveInitFormat(&wfx, 1, 16000, 16);/** 尝试打开默认的wave设备,WAVE_MAPPER是mmsystem.h中定义的常量,它总是指向系统上默认的wave设备*/if (waveOutOpen(&hWaveOut,WAVE_MAPPER,&wfx,(DWORD_PTR)waveOutProc,(DWORD_PTR)&waveFreeBlockCount,CALLBACK_FUNCTION) != MMSYSERR_NOERROR){return 0;ExitProcess(1);}else{waveOutFlag = TRUE;}return 1;}else{return 0;}}int PlayPcm(LPSTR buffer,int len)
{if (waveOutFlag) {if (buffer == NULL)return 0;if(1< sizeof(buffer)){writeAudio(hWaveOut, buffer, len);}return 1;}elsereturn 0;
}int PlayPcmEnd()
{int i;for (i = 0; i < waveFreeBlockCount; i++)if (waveBlocks[i].dwFlags & WHDR_PREPARED){waveOutUnprepareHeader(hWaveOut, &waveBlocks[i], sizeof(WAVEHDR));}     DeleteCriticalSection(&waveCriticalSection);freeBlocks(waveBlocks);waveOutClose(hWaveOut);return 1;
}void writeAudio(HWAVEOUT hWaveOut, LPSTR data, int size)
{WAVEHDR* current;int remain;current = &waveBlocks[waveCurrentBlock];while (size > 0) {/** 首先确保我们要使用的头是准备好的*/if (current->dwFlags & WHDR_PREPARED)waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR));if (size < (int)(BLOCK_SIZE - current->dwUser)) {memcpy(current->lpData + current->dwUser, data, size);current->dwUser += size;break;}remain = BLOCK_SIZE - current->dwUser;memcpy(current->lpData + current->dwUser, data, remain);size -= remain;data += remain;current->dwBufferLength = BLOCK_SIZE;waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR));waveOutWrite(hWaveOut, current, sizeof(WAVEHDR));EnterCriticalSection(&waveCriticalSection);waveFreeBlockCount--;LeaveCriticalSection(&waveCriticalSection);/** 等待一个块释放*/while (!waveFreeBlockCount)Sleep(10);/** 指向下一个区*/waveCurrentBlock++;waveCurrentBlock %= BLOCK_COUNT;current = &waveBlocks[waveCurrentBlock];current->dwUser = 0;}
}WAVEHDR* allocateBlocks(int size, int count)
{unsigned char* buffer;int i;WAVEHDR* blocks;DWORD totalBufferSize = (size + sizeof(WAVEHDR)) * count;/** 一次性为整个内存集分配内存*/if ((buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,totalBufferSize)) == NULL){fprintf(stderr, "Memory allocation error\n");ExitProcess(1);}/** 并设置指向每一位的指针*/blocks = (WAVEHDR*)buffer;buffer += sizeof(WAVEHDR) * count;for (i = 0; i < count; i++) {blocks[i].dwBufferLength = size;blocks[i].lpData = buffer;buffer += size;}return blocks;
}
void freeBlocks(WAVEHDR* blockArray)
{HeapFree(GetProcessHeap(), 0, blockArray);
}static void CALLBACK waveOutProc(HWAVEOUT hWaveOut,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2
)
{/** 指向空闲块计数器的指针*/int* freeBlockCounter = (int*)dwInstance;/** 忽略由于打开和关闭设备而发生的调用*/if (uMsg != WOM_DONE)return;EnterCriticalSection(&waveCriticalSection);(*freeBlockCounter)++;LeaveCriticalSection(&waveCriticalSection);
}void WaveInitFormat(LPWAVEFORMATEX m_WaveFormat, WORD nCh, DWORD nSampleRate, WORD BitsPerSample)
{m_WaveFormat->wFormatTag = WAVE_FORMAT_PCM;m_WaveFormat->nChannels = nCh;//声道数,单声道一个,立体声两个m_WaveFormat->nSamplesPerSec = nSampleRate;//采样率 8000、11025、22050、44100Hzm_WaveFormat->nAvgBytesPerSec = nSampleRate * nCh * BitsPerSample / 8;//每秒平均数据传输速率 m_WaveFormat->nBlockAlign = m_WaveFormat->nChannels * BitsPerSample / 8;//单帧数据量m_WaveFormat->wBitsPerSample = BitsPerSample;//采样精度m_WaveFormat->cbSize = 0;//保留
}

完整可执行录音+播放 代码:https://download.csdn.net/download/Hilaph/12535362

本文参考了:https://blog.csdn.net/zhangxizhicn/article/details/6843589

windows C 调用音频输出设备 实现播放相关推荐

  1. 使用命令设置Windows音量和音频输出设备

    前言 Windows似乎并没有音量设置的命令,也没有输出设备的设置命令.如果你知道,请告诉我一下~ 因此,这里使用了一个神级小工具:nircmd 官网下载地址: 32位:http://www.nirs ...

  2. windows C 调用音频输入设备 实现录音

    所用库: #include "mmsystem.h" 采集输入设备音频 void RecordWave() {//返回系统中的输入设备数量int count = waveInGet ...

  3. Windows 下音频数据采集和播放

    音频操作所需头文件和链接库 #include<mmsystem.h> #include<mmreg.h> #pragma  comment(lib, "winmm.l ...

  4. 【Android 高性能音频】OboeTester 音频性能测试应用 ( Oboe 输出测试参数 | API 选择 | 音频输出设备选择 | 采样率 | 通道 | 采样格式 | 播放偏好 )

    文章目录 一.Oboe 输出测试参数面板 二.Oboe 输出测试参数 API 及 设备选择 三.Oboe 输出测试参数 音频参数 四.Oboe 输出测试参数 播放偏好 五.Oboe 输出测试参数 ( ...

  5. windows代码设置默认音频输出设备

    1.windows音频输入设备与输出设备 1)windows中音频输入设备是指麦克风,如下图: 2)windows中音频输出设备是指扬声器,如下图: 2.有些时候,windows音频播放设备可能有多个 ...

  6. WINDOWS下对音频的处理过程(转)

    WINDOWS下对音频的处理过程 WINDOWS下对音频的处理,大致可分为两部分,即音频的输入.输出,和ACM压缩处理. 一般情况下在WINDOWS下可以调用诸如sndPlaySound等API(MC ...

  7. 音频输出设备是如何决定的

    1. 既然是分析音频输出设备,我们首先需要知道当前手机支持的音频输出设备有哪些 adb shell dumpsys media.audio_policy > /home/jon/audio_po ...

  8. android音频系统(7):通话过程中的音频输出设备切换

    前言:由于通话比较特殊,Android对于通话过程中音频输出设备的切换做了特殊处理,它在上层也是通过切换音频播放状态来完成切换操作的,android用CallAudioState来封装通话过程中的音频 ...

  9. 实时音频采集与播放技术的研究

    实时音频采集与播放技术的研究 荣治国 陈松乔 (中南大学信息工程学院 湖南 长沙 410083) 介绍了音频采集.播放的三种技术,分别给出实现模型,并对三种技术作出对比分析,以此提出了声音实时传输的依 ...

最新文章

  1. ASP.NET超凡的代码控制
  2. C - Group HDU - 4638[离线+树状数组]
  3. https搭建(openssl)
  4. linux线程同步(4)-自旋锁
  5. mybatis学习(22):查询排序
  6. 最小公倍数的求解原理和证明
  7. filezilla 共享多个目录_filezilla设置中文,3步搞定filezilla中文设置
  8. Android中ActionBar中不显示overflow(就是三个点的那个按钮)解决办法
  9. 熊族部落---要邀请码
  10. linux flash文件系统,需要了解Linux flash文件系统
  11. 安卓原生系统_安卓原生系统为什么比MIUI和EMUI等国产系统更流畅
  12. Java导出Word文档的实现
  13. 青岛大学计算机科学学院,青岛大学信息工程学院
  14. Activiti学习——生成历史流程跟踪图
  15. 2017计算机二级ms office高级应用成绩查询
  16. JS实现动画特效2(缓动函数封装、导航栏筋斗云效果)
  17. IDEA Intellij小技巧和插件
  18. mysql 8 commen table expression 树查询
  19. 预测师的随想系列二:记一次囚徒博弈游戏
  20. 生命与自然的相互感应

热门文章

  1. Centos7安装pt-query-digest慢查询工具与实践
  2. 第15周项目二—洗牌(1)
  3. 揭秘 typedef四用途与两陷阱
  4. java中flush函数作用_Java语言中flush()函数作用及使用方法详解
  5. 7-33 删除句子中的单词 (8 分)输入n(n<=10)个由若干个单词组成的英文句子(句子的长度不超过100),每个句子以英文的标点“.”结束,每个句子中单词之间由1个空格进行分隔,如果最后一个单
  6. 今日头条校招2017.7.21编程3,PM、idea、程序员
  7. java发邮件要收费吗,2年以上经验必看
  8. eclipse安装nodejs插件nodeclipse
  9. 如何打开sql server配置管理器
  10. 3D可视化色彩设计大揭秘