mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-13 09:35:23 -04:00
210 lines
6.5 KiB
C
210 lines
6.5 KiB
C
#include "Core.h"
|
|
|
|
#if CC_AUD_BACKEND == CC_AUD_BACKEND_WINMM
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define NOSERVICE
|
|
#define NOMCX
|
|
#define NOIME
|
|
#ifndef UNICODE
|
|
#define UNICODE
|
|
#define _UNICODE
|
|
#endif
|
|
#include <windows.h>
|
|
/*
|
|
#include <winmm.h>
|
|
*/
|
|
/* Compatibility versions so compiling works on older Windows SDKs */
|
|
#include "../misc/windows/min-winmm.h"
|
|
|
|
#include "Audio.h"
|
|
struct AudioContext {
|
|
HWAVEOUT handle;
|
|
WAVEHDR headers[AUDIO_MAX_BUFFERS];
|
|
int count, channels, sampleRate, volume;
|
|
cc_uint32 _tmpSize[AUDIO_MAX_BUFFERS];
|
|
void* _tmpData[AUDIO_MAX_BUFFERS];
|
|
};
|
|
|
|
#define AUDIO_COMMON_VOLUME
|
|
#include "_AudioBase.h"
|
|
|
|
cc_bool AudioBackend_Init(void) { return true; }
|
|
void AudioBackend_Tick(void) { }
|
|
void AudioBackend_Free(void) { }
|
|
|
|
cc_result Audio_Init(struct AudioContext* ctx, int buffers) {
|
|
int i;
|
|
for (i = 0; i < buffers; i++) {
|
|
ctx->headers[i].dwFlags = WHDR_DONE;
|
|
}
|
|
ctx->count = buffers;
|
|
ctx->volume = 100;
|
|
return 0;
|
|
}
|
|
|
|
static void Audio_Stop(struct AudioContext* ctx) {
|
|
waveOutReset(ctx->handle);
|
|
}
|
|
|
|
static cc_result Audio_Reset(struct AudioContext* ctx) {
|
|
cc_result res;
|
|
if (!ctx->handle) return 0;
|
|
|
|
res = waveOutClose(ctx->handle);
|
|
ctx->handle = NULL;
|
|
return res;
|
|
}
|
|
|
|
void Audio_Close(struct AudioContext* ctx) {
|
|
int inUse;
|
|
if (ctx->handle) {
|
|
Audio_Stop(ctx);
|
|
Audio_Poll(ctx, &inUse); /* unprepare buffers */
|
|
Audio_Reset(ctx);
|
|
}
|
|
AudioBase_Clear(ctx);
|
|
}
|
|
|
|
cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) {
|
|
WAVEFORMATEX fmt;
|
|
cc_result res;
|
|
int sampleSize;
|
|
|
|
sampleRate = Audio_AdjustSampleRate(sampleRate, playbackRate);
|
|
if (ctx->channels == channels && ctx->sampleRate == sampleRate) return 0;
|
|
ctx->channels = channels;
|
|
ctx->sampleRate = sampleRate;
|
|
|
|
sampleSize = channels * 2; /* 16 bits per sample / 8 */
|
|
if ((res = Audio_Reset(ctx))) return res;
|
|
|
|
fmt.wFormatTag = WAVE_FORMAT_PCM;
|
|
fmt.nChannels = channels;
|
|
fmt.nSamplesPerSec = sampleRate;
|
|
fmt.nAvgBytesPerSec = sampleRate * sampleSize;
|
|
fmt.nBlockAlign = sampleSize;
|
|
fmt.wBitsPerSample = 16;
|
|
fmt.cbSize = 0;
|
|
|
|
res = waveOutOpen(&ctx->handle, WAVE_MAPPER, &fmt, 0, 0, CALLBACK_NULL);
|
|
/* Show a better error message when no audio output devices connected than */
|
|
/* "A device ID has been used that is out of range for your system" */
|
|
if (res == MMSYSERR_BADDEVICEID && waveOutGetNumDevs() == 0)
|
|
return ERR_NO_AUDIO_OUTPUT;
|
|
return res;
|
|
}
|
|
|
|
void Audio_SetVolume(struct AudioContext* ctx, int volume) { ctx->volume = volume; }
|
|
|
|
cc_result Audio_QueueChunk(struct AudioContext* ctx, struct AudioChunk* chunk) {
|
|
cc_result res;
|
|
WAVEHDR* hdr;
|
|
cc_bool ok;
|
|
int i;
|
|
struct AudioChunk tmp = *chunk;
|
|
|
|
for (i = 0; i < ctx->count; i++) {
|
|
hdr = &ctx->headers[i];
|
|
if (!(hdr->dwFlags & WHDR_DONE)) continue;
|
|
|
|
ok = AudioBase_AdjustSound(ctx, i, &tmp);
|
|
if (!ok) return ERR_OUT_OF_MEMORY;
|
|
|
|
Mem_Set(hdr, 0, sizeof(WAVEHDR));
|
|
hdr->lpData = (LPSTR)tmp.data;
|
|
hdr->dwBufferLength = tmp.size;
|
|
hdr->dwLoops = 1;
|
|
|
|
if ((res = waveOutPrepareHeader(ctx->handle, hdr, sizeof(WAVEHDR)))) return res;
|
|
if ((res = waveOutWrite(ctx->handle, hdr, sizeof(WAVEHDR)))) return res;
|
|
return 0;
|
|
}
|
|
/* tried to queue data without polling for free buffers first */
|
|
return ERR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) {
|
|
cc_result res = 0;
|
|
WAVEHDR* hdr;
|
|
int i, count = 0;
|
|
|
|
for (i = 0; i < ctx->count; i++) {
|
|
hdr = &ctx->headers[i];
|
|
if (!(hdr->dwFlags & WHDR_DONE)) { count++; continue; }
|
|
|
|
if (!(hdr->dwFlags & WHDR_PREPARED)) continue;
|
|
/* unprepare this WAVEHDR so it can be reused */
|
|
res = waveOutUnprepareHeader(ctx->handle, hdr, sizeof(WAVEHDR));
|
|
if (res) break;
|
|
}
|
|
|
|
*inUse = count; return res;
|
|
}
|
|
|
|
|
|
/*########################################################################################################################*
|
|
*------------------------------------------------------Stream context-----------------------------------------------------*
|
|
*#########################################################################################################################*/
|
|
cc_result StreamContext_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) {
|
|
return Audio_SetFormat(ctx, channels, sampleRate, playbackRate);
|
|
}
|
|
|
|
cc_result StreamContext_Enqueue(struct AudioContext* ctx, struct AudioChunk* chunk) {
|
|
return Audio_QueueChunk(ctx, chunk);
|
|
}
|
|
|
|
cc_result StreamContext_Play(struct AudioContext* ctx) {
|
|
return 0;
|
|
}
|
|
|
|
cc_result StreamContext_Pause(struct AudioContext* ctx) {
|
|
return ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
cc_result StreamContext_Update(struct AudioContext* ctx, int* inUse) {
|
|
return Audio_Poll(ctx, inUse);
|
|
}
|
|
|
|
|
|
/*########################################################################################################################*
|
|
*------------------------------------------------------Sound context------------------------------------------------------*
|
|
*#########################################################################################################################*/
|
|
cc_bool SoundContext_FastPlay(struct AudioContext* ctx, struct AudioData* data) {
|
|
int channels = data->channels;
|
|
int sampleRate = Audio_AdjustSampleRate(data->sampleRate, data->rate);
|
|
return !ctx->channels || (ctx->channels == channels && ctx->sampleRate == sampleRate);
|
|
}
|
|
|
|
cc_result SoundContext_PlayData(struct AudioContext* ctx, struct AudioData* data) {
|
|
cc_result res;
|
|
|
|
if ((res = Audio_SetFormat(ctx, data->channels, data->sampleRate, data->rate))) return res;
|
|
if ((res = Audio_QueueChunk(ctx, &data->chunk))) return res;
|
|
|
|
return 0;
|
|
}
|
|
|
|
cc_result SoundContext_PollBusy(struct AudioContext* ctx, cc_bool* isBusy) {
|
|
int inUse = 1;
|
|
cc_result res;
|
|
if ((res = Audio_Poll(ctx, &inUse))) return res;
|
|
|
|
*isBusy = inUse > 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*########################################################################################################################*
|
|
*--------------------------------------------------------Audio misc-------------------------------------------------------*
|
|
*#########################################################################################################################*/
|
|
cc_bool Audio_DescribeError(cc_result res, cc_string* dst) {
|
|
char buffer[NATIVE_STR_LEN] = { 0 };
|
|
waveOutGetErrorTextA(res, buffer, NATIVE_STR_LEN);
|
|
|
|
if (!buffer[0]) return false;
|
|
String_AppendConst(dst, buffer);
|
|
return true;
|
|
}
|
|
#endif
|
|
|