view wave_out.c @ 0:c55ea9478c80

Hello Gensokyo!
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 21 May 2013 10:29:21 +0200
parents
children
line wrap: on
line source

#include <windows.h>
#include <mmsystem.h>
#include <stdio.h>
#include <stdint.h>

/*
 * some good values for block size and count
 */
#define BLOCK_SIZE 8192
#define BLOCK_COUNT 16

/*
 * module level variables
 */
static WAVEHDR* waveBlocks;
volatile int waveFreeBlockCount;
static int waveCurrentBlock;

static int xaddl(int volatile *atomic, int add)
{
  int val; /* This works for the 486 and later */
  __asm__ __volatile__("lock; xaddl %0, %1"
                       : "=r" (val), "+m" (*atomic)
                       : "m" (*atomic), "0" (add));
  return val;
}

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;
	xaddl(freeBlockCounter, 1);
}

static WAVEHDR* allocateBlocks(unsigned int size, unsigned 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 = VirtualAlloc(NULL, totalBufferSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)) == NULL) {
	    return NULL;
	}
	/*
	 * 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 = (char*)buffer;
	   buffer += size;
	}
	return blocks;
}

void writeAudio(HWAVEOUT hWaveOut, short *data, unsigned 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));
	xaddl(&waveFreeBlockCount, -1);

	while(!waveFreeBlockCount)
	    Sleep(10);

	waveCurrentBlock++;
	waveCurrentBlock %= BLOCK_COUNT;
	current = &waveBlocks[waveCurrentBlock];
	current->dwUser = 0;
	}
}

unsigned int wave_out_open(HWAVEOUT *hWaveOut, unsigned int sfreq, unsigned char channels) {
	WAVEFORMATEX wfx;
	waveBlocks = allocateBlocks(BLOCK_SIZE, BLOCK_COUNT);
	waveFreeBlockCount = BLOCK_COUNT;
	waveCurrentBlock= 0;
    if(waveBlocks == NULL) {
        return MMSYSERR_NOMEM;
    }
	wfx.nSamplesPerSec = sfreq;
	wfx.wBitsPerSample = 16;
	wfx.nChannels = channels; 
	wfx.cbSize = 0;
	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.nBlockAlign = (wfx.wBitsPerSample * wfx.nChannels) >> 3;
	wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
	return waveOutOpen(hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)waveOutProc, (DWORD_PTR)&waveFreeBlockCount, CALLBACK_FUNCTION);
}

void wave_out_close(HWAVEOUT hWaveOut) {
	int i;
	while(waveFreeBlockCount < BLOCK_COUNT)
	    Sleep(10);

	for(i = 0; i < waveFreeBlockCount; i++) 
	    if(waveBlocks[i].dwFlags & WHDR_PREPARED)
	        waveOutUnprepareHeader(hWaveOut, &waveBlocks[i], sizeof(WAVEHDR));
	VirtualFree(waveBlocks, 0, MEM_RELEASE);
	waveOutClose(hWaveOut);
}