做了一个跨平台解码器把MP3数据转为PCM原始波形数据,而要在不同平台上要想播放这些PCM数据,将数据送到AD转换芯片却有很大的不同。网上资料有限,很多都是互相抄袭,大部分都没进过验证。
因此花了很久时间终于把多个平台下播放音频数据的音频接口做了出来,想做播放器及底层音频程序的可以参考一下,代码不涉及任何播放器,完全依赖系统API。
总结如下:
1.Windows
#include <windows.h> #include <mmreg.h> #pragma comment(lib, "winmm.lib") /* * some good values for block size and count */ #define BLOCK_SIZE 8192 #define BLOCK_COUNT 20 /* * module level variables */ static CRITICAL_SECTION waveCriticalSection; static WAVEHDR* waveBlocks; static volatile int waveFreeBlockCount; static int waveCurrentBlock; static void CALLBACK waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) { /* * pointer to free block counter */ int* freeBlockCounter = (int*)dwInstance; /* * ignore calls that occur due to openining and closing the * device. */ if(uMsg != WOM_DONE) return; EnterCriticalSection(&waveCriticalSection); (*freeBlockCounter)++; LeaveCriticalSection(&waveCriticalSection); } WAVEHDR* allocateBlocks(int size, int count) { unsigned char* buffer; int i; WAVEHDR* blocks; DWORD totalBufferSize = (size + sizeof(WAVEHDR)) * count; /* * allocate memory for the entire set in one go */ if((buffer = (unsigned char*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, totalBufferSize )) == NULL) { fprintf(stderr, "Memory allocation error/n"); ExitProcess(1); } /* * and set up the pointers to each bit */ blocks = (WAVEHDR*)buffer; buffer += sizeof(WAVEHDR) * count; for(i = 0; i < count; i++) { blocks[i].dwBufferLength = size; blocks[i].lpData = (LPSTR)buffer; buffer += size; } return blocks; } void freeBlocks(WAVEHDR* blockArray) { /* * and this is why allocateBlocks works the way it does */ HeapFree(GetProcessHeap(), 0, blockArray); } void writeAudio(HWAVEOUT hWaveOut, LPSTR data, int size) { WAVEHDR* current; int remain; current = &waveBlocks[waveCurrentBlock]; while(size > 0) { /* * first make sure the header we're going to use is unprepared */ 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); /* * wait for a block to become free */ while(!waveFreeBlockCount) Sleep(10); /* * point to the next block */ waveCurrentBlock++; waveCurrentBlock %= BLOCK_COUNT; current = &waveBlocks[waveCurrentBlock]; current->dwUser = 0; } } HWAVEOUT hWaveOut; /* device handle */ WAVEFORMATEX wfx; /* look this up in your documentation */ void Dev_open(long br) { /* * initialise the module variables */ waveBlocks = allocateBlocks(BLOCK_SIZE, BLOCK_COUNT); waveFreeBlockCount = BLOCK_COUNT; waveCurrentBlock= 0; InitializeCriticalSection(&waveCriticalSection); /* * set up the WAVEFORMATEX structure. */ wfx.nSamplesPerSec = br; /* sample rate */ wfx.wBitsPerSample = 16; /* sample size */ wfx.nChannels= 2; /* channels*/ wfx.cbSize = 0; /* size of _extra_ info */ wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nBlockAlign = (wfx.wBitsPerSample * wfx.nChannels) >> 3; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; /* try to open the default wave device. WAVE_MAPPER is * a constant defined in mmsystem.h, it always points to the * default wave device on the system (some people have 2 or * more sound cards). */ if(waveOutOpen( &hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)waveOutProc, (DWORD_PTR)&waveFreeBlockCount, CALLBACK_FUNCTION ) != MMSYSERR_NOERROR) { printf( "unable to open wave mapper device/n"); ExitProcess(1); } printf("open wave mapper device success!/n"); }
2、linux ALSA接口 可用于LINUX 2.6
需要ALSA开发库的支持
#define ALSA_PCM_NEW_HW_PARAMS_API #include <alsa/asoundlib.h> snd_pcm_uframes_t frames; snd_pcm_t *handle; int init_dev(int freq) { snd_pcm_hw_params_t *params; int rc; int dir; unsigned int val; int size; /* Open PCM device for playback. */ rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); if (rc < 0) { fprintf(stderr, "unable to open pcm device: %s/n", snd_strerror(rc)); exit(1); } /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca(¶ms); /* Fill it in with default values. */ snd_pcm_hw_params_any(handle, params); /* Set the desired hardware parameters. */ /* Interleaved mode */ snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); /* Signed 16-bit little-endian format */ snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); /* Two channels (stereo) */ snd_pcm_hw_params_set_channels(handle, params, 2); /* 44100 bits/second sampling rate (CD quality) */ val = freq; snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); /* Set period size to 32 frames. */ frames = 32; snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); /* Write the parameters to the driver */ rc = snd_pcm_hw_params(handle, params); if (rc < 0) { fprintf(stderr, "unable to set hw parameters: %s/n", snd_strerror(rc)); exit(1); } /* Use a buffer large enough to hold one period */ snd_pcm_hw_params_get_period_size(params, &frames, &dir); size = frames * 4; /* 2 bytes/sample, 2 channels */ //buffer = (char *) malloc(size); /* We want to loop for 5 seconds */ snd_pcm_hw_params_get_period_time(params, &val, &dir); /* 5 seconds in microseconds divided by * period time */ printf("period time:%d/nbuffersize:%d/n",val,size); } void snd_write(rt_uint16_t*buffer, size_t size) { static int rc; rc=snd_pcm_writei(handle, buffer, size/4); if (rc == -EPIPE) { /* EPIPE means underrun */ fprintf(stderr, "underrun occurred/n"); snd_pcm_prepare(handle); } else if (rc < 0) { fprintf(stderr, "error from writei: %s/n", snd_strerror(rc)); } else if (rc != (int)size/4) { fprintf(stderr, "short write, write %d frames/n", rc); } }
3.LINUX oss接口,在做ARM嵌入式板子上就用的这个接口
int fd; /* sound device file descriptor */ void snd_write(rt_uint16_t*buffer, size_t size) { write(fd, buffer, size); } int init_dev(int freq) { int arg,status; arg = 2; /* mono or stereo */ /* open sound device */ fd = open("/dev/dsp", 1); if (fd < 0) { perror("open of /dev/dsp failed"); exit(1); } /* set sampling parameters */ arg = 16; /* sample size */ status = ioctl(fd, SOUND_PCM_WRITE_BITS, &arg); if (status == -1) perror("SOUND_PCM_WRITE_BITS ioctl failed"); arg = 2; /* mono or stereo */ status = ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &arg); if (status == -1) perror("SOUND_PCM_WRITE_CHANNELS ioctl failed"); arg = freq; /* sampling rate */ status = ioctl(fd, SOUND_PCM_WRITE_RATE, &arg); if (status == -1) perror("SOUND_PCM_WRITE_WRITE ioctl failed"); mix_fd = open("/dev/mixer", O_RDONLY); if (mix_fd == -1) { perror("unable to open /dev/mixer"); exit(1); } return 0; }