windows C 调用音频输出设备 实现播放

在之前有写过一篇 使用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;
	}
	else
		return 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、44100Hz
	m_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

猜你喜欢

转载自blog.csdn.net/Hilaph/article/details/106855017