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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章