musix 0.3.5

Music player library for esoteric audio formats (music from C64,Amiga etc)
Documentation
#include "AudioWinmm.h"
#include <mmsystem.h>
#include "tedmem.h"
#include "cpu.h"

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

static CRITICAL_SECTION waveCriticalSection;
static WAVEHDR* waveBlocks;
static volatile int waveFreeBlockCount;
static int waveCurrentBlock;

HWAVEOUT hWaveOut; /* device handle */
HANDLE hFile;/* file handle */
WAVEFORMATEX wfx; /* look this up in your documentation */
char buffer[1024]; /* intermediate buffer for reading */

//struct WaveProcData{
	TED *ted;
	HANDLE semaph;
//};
//static WaveProcData wpd;

static 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;
}

static void freeBlocks(WAVEHDR* blockArray)
{
	// and this is why allocateBlocks works the way it does
	HeapFree(GetProcessHeap(), 0, blockArray);
}

void CALLBACK AudioWinmm::sndCallbackFunc(HANDLE wout, UINT msg,
								  DWORD user, DWORD dw1, DWORD dw2)
{
	//pointer to free block counter
	DWORD *freeBlockCounter = (DWORD *) user;

	if (msg == WOM_DONE) {
		EnterCriticalSection(&waveCriticalSection);
		(*freeBlockCounter)++;
		LeaveCriticalSection(&waveCriticalSection);
		if (ted) {
			//ted->ted_process((short*) stream, len / 2);
		}
	}
}

AudioWinmm::AudioWinmm(void *userData, unsigned int sampleFrq_) : Audio(sampleFrq_)
{
	MMRESULT mRes;
	unsigned int i;
	ted = reinterpret_cast<TED *>(userData);

	// Find a usable waveOut device and open it 
	for(i = 0; i < waveOutGetNumDevs(); i++) { 
		if(i == waveOutGetNumDevs()) {// Error (very probably no free devices found) 
//			MessageBox(NULL,L"No audio device found!",L"ERROR",MB_OK);
			return;
		}
	}

	// initialise the module variables
	waveBlocks = allocateBlocks(BLOCK_SIZE, BLOCK_COUNT);
	waveFreeBlockCount = BLOCK_COUNT;
	waveCurrentBlock = 0;
	InitializeCriticalSection(&waveCriticalSection);

	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.nChannels = 1;
	wfx.nSamplesPerSec = sampleFrq_;
	wfx.wBitsPerSample = 16;
	wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; 
	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
	wfx.cbSize = 0;
	if ( (mRes = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx,
			(DWORD_PTR) sndCallbackFunc, (DWORD_PTR)&waveFreeBlockCount, CALLBACK_FUNCTION))
			!= MMSYSERR_NOERROR) {
		TCHAR errMsg[100];
		//wsprintf(errMsg, TEXT("Cannot open waveout device.\rError code: %0X."),mRes);
//		MessageBox(NULL,errMsg,L"ERROR",MB_OK);
		return;
	}
	play();
}

void AudioWinmm::reset()
{
	if (hWaveOut)
		waveOutReset(hWaveOut);
}

void AudioWinmm::pause()
{
	paused = true;
	if (hWaveOut) {
		waveOutPause(hWaveOut);
	}
}

void AudioWinmm::play()
{
	paused = false;
	if (hWaveOut) {
		waveOutRestart(hWaveOut);
	}
}

void AudioWinmm::stop()
{
	paused = true;
	if (hWaveOut) {
		waveOutReset(hWaveOut);
		waveOutPause(hWaveOut);
	}
}

void AudioWinmm::write(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(0);
		// point to the next block
		waveCurrentBlock++;
		waveCurrentBlock %= BLOCK_COUNT;
		current = &waveBlocks[waveCurrentBlock];
		current->dwUser = 0;
	}
}

AudioWinmm::~AudioWinmm()
{
	// unprepare any blocks that are still prepared
	for(int i = 0; i < waveFreeBlockCount; i++) 
		if(waveBlocks[i].dwFlags & WHDR_PREPARED)
			waveOutUnprepareHeader(hWaveOut, &waveBlocks[i], sizeof(WAVEHDR));

	DeleteCriticalSection(&waveCriticalSection);
	if (hWaveOut) {
		waveOutReset(hWaveOut);
		waveOutClose(hWaveOut);
		hWaveOut = NULL;
	}
	freeBlocks(waveBlocks);
}