Mercurial > pmdwin
diff 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 diff
new file mode 100644 --- /dev/null +++ b/wave_out.c @@ -0,0 +1,129 @@ +#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); +} +