* Based on DirectSound driver by KC/Milan
*/
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#ifndef WINVER
#define WINVER 0x0501
#endif
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <dsound.h>
* function arguments. It is #defined away by a macro in sal.h, but this
* breaks GCC headers, as they also use __null (but for a different
* purpose). This pre-processor block undefines __null which returns
* __null to acting like a GCC keyword.
* This does nothing for the unofficial DirectX SDK, which has __null
* manually removed. */
#if defined __MINGW32__ && defined __null
#undef __null
#endif
const IID GUID_NULL = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
static const IID _al_IID_IDirectSoundBuffer8 = { 0x6825a449, 0x7524, 0x4d82, { 0x92, 0x0f, 0x50, 0xe3, 0x6a, 0xb3, 0xab, 0x1e } };
static const IID _al_IID_IDirectSoundCaptureBuffer8 = { 0x00990df4, 0x0dbb, 0x4872, { 0x83, 0x3e, 0x6d, 0x30, 0x3e, 0x80, 0xae, 0xb6 } };
#include "allegro5/allegro.h"
extern "C" {
ALLEGRO_DEBUG_CHANNEL("audio-dsound")
#include "allegro5/internal/aintern_audio.h"
#include "allegro5/internal/aintern_system.h"
#include "allegro5/internal/aintern_wunicode.h"
#define MAKE_UNION(ptr, t) \
union { \
LPVOID *v; \
t p; \
} u; \
u.p = (ptr);
static IDirectSound8 *device;
static IDirectSoundCapture8 *capture_device;
static char ds_err_str[100];
static int buffer_size;
static _AL_LIST* output_device_list;
#define MIN_BUFFER_SIZE 1024
#define MIN_FILL 512
#define MAX_FILL 1024
static BOOL CALLBACK _ds_enum_callback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext);
static HWND get_window()
{
const char *val = al_get_config_value(al_get_system_config(),
"directsound", "window");
HWND ret;
if (val && strncmp(val, "foreground", 10) == 0) {
ret = GetForegroundWindow();
ALLEGRO_INFO("Using foreground window: %p\n", ret);
}
else {
ret = GetDesktopWindow();
ALLEGRO_INFO("Using desktop window: %p\n", ret);
}
return ret;
}
static void dsound_set_buffer_size(int bits_per_sample)
{
int buffer_size_in_samples = 8192;
const char *val = al_get_config_value(al_get_system_config(),
"directsound", "buffer_size");
if (val && val[0] != '\0') {
int n = atoi(val);
if (n < MIN_BUFFER_SIZE)
n = MIN_BUFFER_SIZE;
buffer_size_in_samples = n;
}
buffer_size = buffer_size_in_samples * (bits_per_sample/8);
}
static char *ds_get_error(HRESULT hr)
{
switch (hr) {
case DSERR_ALLOCATED:
strcpy(ds_err_str, "DSERR_ALLOCATED");
break;
case DSERR_BADFORMAT:
strcpy(ds_err_str, "DSERR_BADFORMAT");
break;
case DSERR_BUFFERTOOSMALL:
strcpy(ds_err_str, "DSERR_BUFFERTOOSMALL");
break;
case DSERR_CONTROLUNAVAIL:
strcpy(ds_err_str, "DSERR_CONTROLUNAVAIL");
break;
case DSERR_DS8_REQUIRED:
strcpy(ds_err_str, "DSERR_DS8_REQUIRED");
break;
case DSERR_INVALIDCALL:
strcpy(ds_err_str, "DSERR_INVALIDCALL");
break;
case DSERR_INVALIDPARAM:
strcpy(ds_err_str, "DSERR_INVALIDPARAM");
break;
case DSERR_NOAGGREGATION:
strcpy(ds_err_str, "DSERR_NOAGGREGATION");
break;
case DSERR_OUTOFMEMORY:
strcpy(ds_err_str, "DSERR_OUTOFMEMORY");
break;
case DSERR_UNINITIALIZED:
strcpy(ds_err_str, "DSERR_UNINITIALIZED");
break;
case DSERR_UNSUPPORTED:
strcpy(ds_err_str, "DSERR_UNSUPPORTED");
break;
}
return ds_err_str;
}
struct ALLEGRO_DS_DATA {
int bits_per_sample;
int channels;
DSBUFFERDESC desc;
WAVEFORMATEX wave_fmt;
LPDIRECTSOUNDBUFFER ds_buffer;
LPDIRECTSOUNDBUFFER8 ds8_buffer;
int stop_voice;
ALLEGRO_THREAD *thread;
};
static bool _dsound_voice_is_playing(const ALLEGRO_VOICE *voice);
wants more data for a stream */
static void* _dsound_update(ALLEGRO_THREAD *self, void *arg)
{
ALLEGRO_VOICE *voice = (ALLEGRO_VOICE *)arg;
ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA*)voice->extra;
const int bytes_per_sample = ex_data->bits_per_sample / 8;
DWORD play_cursor = 0;
DWORD write_cursor;
DWORD saved_play_cursor = 0;
unsigned int samples;
LPVOID ptr1, ptr2;
DWORD block1_bytes, block2_bytes;
unsigned char *data;
unsigned char *silence;
HRESULT hr;
(void)self;
silence = (unsigned char *)al_malloc(buffer_size);
samples = buffer_size / bytes_per_sample / ex_data->channels;
al_fill_silence(silence, samples, voice->depth, voice->chan_conf);
hr = ex_data->ds8_buffer->Lock(0, buffer_size,
&ptr1, &block1_bytes, &ptr2, &block2_bytes,
DSBLOCK_ENTIREBUFFER);
if (!FAILED(hr)) {
unsigned int sample_bytes;
samples = buffer_size / bytes_per_sample / ex_data->channels;
data = (unsigned char *) _al_voice_update(voice, voice->mutex, &samples);
sample_bytes = samples * bytes_per_sample * ex_data->channels;
if (sample_bytes < block1_bytes)
block1_bytes = sample_bytes;
memcpy(ptr1, data, block1_bytes);
sample_bytes -= block1_bytes;
if (sample_bytes < block2_bytes)
block2_bytes = sample_bytes;
memcpy(ptr2, data + block1_bytes, block2_bytes);
ex_data->ds8_buffer->Unlock(ptr1, block1_bytes, ptr2, block2_bytes);
}
else {
ALLEGRO_ERROR("Lock failed: %s\n", ds_get_error(hr));
}
ex_data->ds8_buffer->Play(0, 0, DSBPLAY_LOOPING);
do {
if (!_dsound_voice_is_playing(voice)) {
ex_data->ds8_buffer->Play(0, 0, DSBPLAY_LOOPING);
}
ex_data->ds8_buffer->GetCurrentPosition(&play_cursor, &write_cursor);
* play_cursor.
*/
int d = play_cursor - saved_play_cursor;
if (d < 0)
d += buffer_size;
* mixing the samples and locking/unlocking the buffer.
*/
if (d < MIN_FILL) {
al_rest(0.005);
continue;
}
* while we wait for _al_voice_update to complete.
*/
samples = d / bytes_per_sample / ex_data->channels;
if (samples > MAX_FILL) {
samples = MAX_FILL;
}
data = (unsigned char *) _al_voice_update(voice, voice->mutex, &samples);
if (data == NULL) {
data = silence;
}
hr = ex_data->ds8_buffer->Lock(saved_play_cursor,
samples * bytes_per_sample * ex_data->channels,
&ptr1, &block1_bytes, &ptr2, &block2_bytes, 0);
if (!FAILED(hr)) {
memcpy(ptr1, data, block1_bytes);
memcpy(ptr2, data + block1_bytes, block2_bytes);
hr = ex_data->ds8_buffer->Unlock(ptr1, block1_bytes,
ptr2, block2_bytes);
if (FAILED(hr)) {
ALLEGRO_ERROR("Unlock failed: %s\n", ds_get_error(hr));
}
}
else {
ALLEGRO_ERROR("Lock failed: %s\n", ds_get_error(hr));
}
saved_play_cursor += block1_bytes + block2_bytes;
saved_play_cursor %= buffer_size;
} while (!ex_data->stop_voice);
ex_data->ds8_buffer->Stop();
al_free(silence);
ex_data->stop_voice = 0;
al_broadcast_cond(voice->cond);
return NULL;
}
previously set parameters, or defaults. It shouldn't need to start sending
audio data to the device yet, however. */
static int _dsound_open()
{
HRESULT hr;
ALLEGRO_INFO("Starting DirectSound...\n");
output_device_list = _al_list_create();
DirectSoundEnumerate((LPDSENUMCALLBACK)_ds_enum_callback, NULL);
hr = DirectSoundCreate8(NULL, &device, NULL);
if (FAILED(hr)) {
ALLEGRO_ERROR("DirectSoundCreate8 failed: %s\n", ds_get_error(hr));
return 1;
}
ALLEGRO_DEBUG("DirectSoundCreate8 succeeded\n");
hr = device->SetCooperativeLevel(get_window(), DSSCL_PRIORITY);
if (FAILED(hr)) {
ALLEGRO_ERROR("SetCooperativeLevel failed: %s\n", ds_get_error(hr));
return 1;
}
return 0;
}
other processes to use the device */
static void _dsound_close()
{
ALLEGRO_DEBUG("Releasing device\n");
_al_list_destroy(output_device_list);
device->Release();
ALLEGRO_DEBUG("Released device\n");
ALLEGRO_INFO("DirectSound closed\n");
}
any data common to streaming and non-streaming sources. */
static int _dsound_allocate_voice(ALLEGRO_VOICE *voice)
{
ALLEGRO_DS_DATA *ex_data;
int bits_per_sample;
int channels;
ALLEGRO_DEBUG("Allocating voice\n");
switch (voice->depth)
{
case ALLEGRO_AUDIO_DEPTH_UINT8:
bits_per_sample = 8;
break;
case ALLEGRO_AUDIO_DEPTH_INT8:
ALLEGRO_ERROR("DirectSound requires 8-bit data to be unsigned\n");
return 1;
case ALLEGRO_AUDIO_DEPTH_UINT16:
ALLEGRO_ERROR("DirectSound requires 16-bit data to be signed\n");
return 1;
case ALLEGRO_AUDIO_DEPTH_INT16:
bits_per_sample = 16;
break;
case ALLEGRO_AUDIO_DEPTH_UINT24:
ALLEGRO_ERROR("DirectSound does not support 24-bit data\n");
return 1;
case ALLEGRO_AUDIO_DEPTH_INT24:
ALLEGRO_ERROR("DirectSound does not support 24-bit data\n");
return 1;
case ALLEGRO_AUDIO_DEPTH_FLOAT32:
ALLEGRO_ERROR("DirectSound does not support 32-bit floating data\n");
return 1;
default:
ALLEGRO_ERROR("Cannot allocate unknown voice depth\n");
return 1;
}
switch (voice->chan_conf) {
case ALLEGRO_CHANNEL_CONF_1:
channels = 1;
break;
case ALLEGRO_CHANNEL_CONF_2:
channels = 2;
break;
default:
ALLEGRO_ERROR("Unsupported number of channels\n");
return 1;
}
ex_data = (ALLEGRO_DS_DATA *)al_calloc(1, sizeof(*ex_data));
if (!ex_data) {
ALLEGRO_ERROR("Could not allocate voice data memory\n");
return 1;
}
ex_data->bits_per_sample = bits_per_sample;
ex_data->channels = channels;
ex_data->stop_voice = 1;
dsound_set_buffer_size(bits_per_sample);
voice->extra = ex_data;
ALLEGRO_DEBUG("Allocated voice\n");
return 0;
}
but still retain a hold on the device. The voice should be stopped and
unloaded by the time this is called */
static void _dsound_deallocate_voice(ALLEGRO_VOICE *voice)
{
ALLEGRO_DEBUG("Deallocating voice\n");
al_free(voice->extra);
voice->extra = NULL;
ALLEGRO_DEBUG("Deallocated voice\n");
}
'streaming' field will be set to false for these voices, and it's
'buffer_size' field will be the total length in bytes of the sample data.
The voice's attached sample's looping mode should be honored, and loading
must fail if it cannot be. */
static int _dsound_load_voice(ALLEGRO_VOICE *voice, const void *_data)
{
ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
HRESULT hr;
LPVOID ptr1, ptr2;
DWORD block1_bytes, block2_bytes;
MAKE_UNION(&ex_data->ds8_buffer, LPDIRECTSOUNDBUFFER8 *);
ALLEGRO_DEBUG("Loading voice\n");
ex_data->wave_fmt.wFormatTag = WAVE_FORMAT_PCM;
ex_data->wave_fmt.nChannels = ex_data->channels;
ex_data->wave_fmt.nSamplesPerSec = voice->frequency;
ex_data->wave_fmt.nBlockAlign = ex_data->channels * (ex_data->bits_per_sample/8);
ex_data->wave_fmt.nAvgBytesPerSec = ex_data->wave_fmt.nBlockAlign * voice->frequency;
ex_data->wave_fmt.wBitsPerSample = ex_data->bits_per_sample;
ex_data->wave_fmt.cbSize = 0;
ex_data->desc.dwSize = sizeof(DSBUFFERDESC);
ex_data->desc.dwFlags = DSBCAPS_LOCSOFTWARE | DSBCAPS_GLOBALFOCUS;
ex_data->desc.dwBufferBytes = voice->buffer_size;
ex_data->desc.dwReserved = 0;
ex_data->desc.lpwfxFormat = &ex_data->wave_fmt;
ex_data->desc.guid3DAlgorithm = DS3DALG_DEFAULT;
hr = device->CreateSoundBuffer(&ex_data->desc, &ex_data->ds_buffer, NULL);
if (FAILED(hr)) {
ALLEGRO_ERROR("CreateSoundBuffer failed: %s\n", ds_get_error(hr));
return 1;
}
ex_data->ds_buffer->QueryInterface(_al_IID_IDirectSoundBuffer8, u.v);
hr = ex_data->ds8_buffer->Lock(0, voice->buffer_size,
&ptr1, &block1_bytes, &ptr2, &block2_bytes, 0);
if (FAILED(hr)) {
ALLEGRO_ERROR("Locking buffer failed: %s\n", ds_get_error(hr));
return 1;
}
unsigned char *data = (unsigned char *) _data;
memcpy(ptr1, data, block1_bytes);
memcpy(ptr2, data + block1_bytes, block2_bytes);
ex_data->ds8_buffer->Unlock(ptr1, block1_bytes, ptr2, block2_bytes);
return 0;
}
This method should not be called on a streaming voice. */
static void _dsound_unload_voice(ALLEGRO_VOICE *voice)
{
ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
ALLEGRO_DEBUG("Unloading voice\n");
ex_data->ds8_buffer->Release();
ALLEGRO_DEBUG("Unloaded voice\n");
}
should start polling the device and call _al_voice_update for audio data.
For non-streaming voices, it should resume playing from the last set
position */
static int _dsound_start_voice(ALLEGRO_VOICE *voice)
{
ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
HRESULT hr;
MAKE_UNION(&ex_data->ds8_buffer, LPDIRECTSOUNDBUFFER8 *);
ALLEGRO_DEBUG("Starting voice\n");
if (!voice->is_streaming) {
hr = ex_data->ds8_buffer->Play(0, 0, 0);
if (FAILED(hr)) {
ALLEGRO_ERROR("Streaming voice failed to start: %s\n", ds_get_error(hr));
return 1;
}
ALLEGRO_INFO("Streaming voice started\n");
return 0;
}
if (ex_data->stop_voice != 0) {
ex_data->wave_fmt.wFormatTag = WAVE_FORMAT_PCM;
ex_data->wave_fmt.nChannels = ex_data->channels;
ex_data->wave_fmt.nSamplesPerSec = voice->frequency;
ex_data->wave_fmt.nBlockAlign = ex_data->channels * (ex_data->bits_per_sample/8);
ex_data->wave_fmt.nAvgBytesPerSec = ex_data->wave_fmt.nBlockAlign * voice->frequency;
ex_data->wave_fmt.wBitsPerSample = ex_data->bits_per_sample;
ex_data->wave_fmt.cbSize = 0;
ex_data->desc.dwSize = sizeof(DSBUFFERDESC);
ex_data->desc.dwFlags = DSBCAPS_LOCSOFTWARE | DSBCAPS_GLOBALFOCUS;
ex_data->desc.dwBufferBytes = buffer_size;
ex_data->desc.dwReserved = 0;
ex_data->desc.lpwfxFormat = &ex_data->wave_fmt;
ex_data->desc.guid3DAlgorithm = DS3DALG_DEFAULT;
ALLEGRO_DEBUG("CreateSoundBuffer\n");
hr = device->CreateSoundBuffer(&ex_data->desc, &ex_data->ds_buffer, NULL);
if (FAILED(hr)) {
ALLEGRO_ERROR("CreateSoundBuffer failed: %s\n", ds_get_error(hr));
return 1;
}
ALLEGRO_DEBUG("CreateSoundBuffer succeeded\n");
ex_data->ds_buffer->QueryInterface(_al_IID_IDirectSoundBuffer8, u.v);
ex_data->ds8_buffer->SetVolume(DSBVOLUME_MAX);
ALLEGRO_DEBUG("Starting _dsound_update thread\n");
ex_data->stop_voice = 0;
ex_data->thread = al_create_thread(_dsound_update, (void*) voice);
al_start_thread(ex_data->thread);
}
else {
ALLEGRO_WARN("stop_voice == 0\n");
}
ALLEGRO_INFO("Voice started\n");
return 0;
}
should leave the data loaded, and reset the voice position to 0. */
static int _dsound_stop_voice(ALLEGRO_VOICE* voice)
{
ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
ALLEGRO_DEBUG("Stopping voice\n");
if (!ex_data->ds8_buffer) {
ALLEGRO_ERROR("Trying to stop empty voice buffer\n");
return 1;
}
if (!voice->is_streaming) {
ALLEGRO_DEBUG("Stopping non-streaming voice\n");
ex_data->ds8_buffer->Stop();
ex_data->ds8_buffer->SetCurrentPosition(0);
ALLEGRO_INFO("Non-streaming voice stopped\n");
return 0;
}
if (ex_data->stop_voice == 0) {
ALLEGRO_DEBUG("Joining thread\n");
ex_data->stop_voice = 1;
while (ex_data->stop_voice == 1) {
al_wait_cond(voice->cond, voice->mutex);
}
al_join_thread(ex_data->thread, NULL);
ALLEGRO_DEBUG("Joined thread\n");
ALLEGRO_DEBUG("Destroying thread\n");
al_destroy_thread(ex_data->thread);
ALLEGRO_DEBUG("Thread destroyed\n");
* restarts.
*/
ex_data->stop_voice = 1;
}
ALLEGRO_DEBUG("Releasing buffer\n");
ex_data->ds8_buffer->Release();
ex_data->ds8_buffer = NULL;
ALLEGRO_INFO("Voice stopped\n");
return 0;
}
and should return true if the voice is playing */
static bool _dsound_voice_is_playing(const ALLEGRO_VOICE *voice)
{
ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
DWORD status;
if (!ex_data) {
ALLEGRO_WARN("ex_data is null\n");
return false;
}
ex_data->ds8_buffer->GetStatus(&status);
return (status & DSBSTATUS_PLAYING);
}
the voice (sample_pos = byte_pos / (depth/8) / channels). This should never
be called on a streaming voice. */
static unsigned int _dsound_get_voice_position(const ALLEGRO_VOICE *voice)
{
ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
DWORD play_pos;
HRESULT hr;
hr = ex_data->ds8_buffer->GetCurrentPosition(&play_pos, NULL);
if (FAILED(hr)) {
ALLEGRO_ERROR("GetCurrentPosition failed: %s\n", ds_get_error(hr));
return 0;
}
return play_pos / (ex_data->channels * (ex_data->bits_per_sample/8));
}
given the value in samples. This should never be called on a streaming
voice. */
static int _dsound_set_voice_position(ALLEGRO_VOICE *voice, unsigned int val)
{
ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
HRESULT hr;
val *= ex_data->channels * (ex_data->bits_per_sample/8);
hr = ex_data->ds8_buffer->SetCurrentPosition(val);
if (FAILED(hr)) {
ALLEGRO_ERROR("SetCurrentPosition failed: %s\n", ds_get_error(hr));
return 1;
}
return 0;
}
struct DSOUND_RECORD_DATA {
LPDIRECTSOUNDCAPTUREBUFFER buffer;
LPDIRECTSOUNDCAPTUREBUFFER8 buffer8;
DSCBUFFERDESC desc;
WAVEFORMATEX wave_fmt;
};
static void *_dsound_update_recorder(ALLEGRO_THREAD *t, void *data)
{
ALLEGRO_AUDIO_RECORDER *r = (ALLEGRO_AUDIO_RECORDER *) data;
DSOUND_RECORD_DATA *extra = (DSOUND_RECORD_DATA *) r->extra;
DWORD last_read_pos = 0;
ALLEGRO_EVENT user_event;
bool is_dsound_recording = false;
size_t fragment_i = 0;
size_t bytes_written = 0;
ALLEGRO_INFO("Starting recorder thread\n");
while (!al_get_thread_should_stop(t)) {
al_lock_mutex(r->mutex);
while (!r->is_recording) {
if (is_dsound_recording) {
extra->buffer8->Stop();
is_dsound_recording = false;
}
al_wait_cond(r->cond, r->mutex);
if (al_get_thread_should_stop(t))
goto stop_recording;
}
if (!is_dsound_recording) {
extra->buffer8->Start(DSCBSTART_LOOPING);
is_dsound_recording = true;
extra->buffer8->GetCurrentPosition(NULL, &last_read_pos);
}
void *buffer1, *buffer2;
DWORD buffer1_size, buffer2_size;
DWORD cap_pos, bytes_to_read;
extra->buffer8->GetCurrentPosition(NULL, &cap_pos);
if (last_read_pos <= cap_pos)
bytes_to_read = cap_pos - last_read_pos;
else
bytes_to_read = extra->desc.dwBufferBytes - last_read_pos;
if (bytes_to_read) {
uint8_t *buffer;
size_t buffer_size;
extra->buffer8->Lock(last_read_pos, bytes_to_read, &buffer1, &buffer1_size, &buffer2, &buffer2_size, 0);
ALLEGRO_ASSERT(buffer2 == NULL);
buffer = (uint8_t *)buffer1;
buffer_size = buffer1_size;
while (buffer_size > 0) {
if (bytes_written + buffer_size <= r->fragment_size) {
memcpy((uint8_t*) r->fragments[fragment_i] + bytes_written, buffer, buffer_size);
bytes_written += buffer_size;
buffer_size = 0;
}
else {
ALLEGRO_AUDIO_RECORDER_EVENT *e;
size_t bytes_to_write = r->fragment_size - bytes_written;
memcpy((uint8_t*) r->fragments[fragment_i] + bytes_written, buffer, bytes_to_write);
buffer_size -= bytes_to_write;
buffer += bytes_to_write;
user_event.user.type = ALLEGRO_EVENT_AUDIO_RECORDER_FRAGMENT;
e = al_get_audio_recorder_event(&user_event);
e->buffer = r->fragments[fragment_i];
e->samples = r->samples;
al_emit_user_event(&r->source, &user_event, NULL);
if (++fragment_i == r->fragment_count) {
fragment_i = 0;
}
bytes_written = 0;
}
}
extra->buffer8->Unlock(buffer1, buffer1_size, buffer2, buffer2_size);
last_read_pos += bytes_to_read;
if (last_read_pos >= extra->desc.dwBufferBytes)
last_read_pos -= extra->desc.dwBufferBytes;
}
al_unlock_mutex(r->mutex);
al_rest(0.10);
}
stop_recording:
if (is_dsound_recording) {
extra->buffer8->Stop();
}
ALLEGRO_INFO("Leaving recorder thread\n");
return NULL;
}
static int _dsound_open_recorder(ALLEGRO_AUDIO_RECORDER *r)
{
HRESULT hr;
if (capture_device != NULL) {
there is no enumeration of devices, it doesn't matter for now. */
ALLEGRO_ERROR("Already recording.\n");
return 1;
}
ALLEGRO_INFO("Creating default capture device.\n");
hr = DirectSoundCaptureCreate8(NULL, &capture_device, NULL);
if (FAILED(hr)) {
ALLEGRO_ERROR("DirectSoundCaptureCreate8 failed: %s\n", ds_get_error(hr));
return 1;
}
hr = device->SetCooperativeLevel(get_window(), DSSCL_PRIORITY);
if (FAILED(hr)) {
ALLEGRO_ERROR("SetCooperativeLevel failed: %s\n", ds_get_error(hr));
return 1;
}
DSOUND_RECORD_DATA *extra = (DSOUND_RECORD_DATA *) al_calloc(1, sizeof(*extra));
DSCCAPS dsccaps;
dsccaps.dwSize = sizeof(DSCCAPS);
hr = capture_device->GetCaps(&dsccaps);
if (FAILED(hr)) {
ALLEGRO_ERROR("DirectSoundCaptureCreate8::GetCaps failed: %s\n", ds_get_error(hr));
}
else {
ALLEGRO_INFO("caps: %lu %lu\n", dsccaps.dwFormats, dsccaps.dwFormats & WAVE_FORMAT_2M16);
}
memset(&extra->wave_fmt, 0, sizeof(extra->wave_fmt));
extra->wave_fmt.wFormatTag = WAVE_FORMAT_PCM;
extra->wave_fmt.nChannels = (WORD)al_get_channel_count(r->chan_conf);
extra->wave_fmt.nSamplesPerSec = r->frequency;
extra->wave_fmt.wBitsPerSample = (WORD)al_get_audio_depth_size(r->depth) * 8;
extra->wave_fmt.nBlockAlign = extra->wave_fmt.nChannels * extra->wave_fmt.wBitsPerSample / 8;
extra->wave_fmt.nAvgBytesPerSec = extra->wave_fmt.nSamplesPerSec * extra->wave_fmt.nBlockAlign;
memset(&extra->desc, 0, sizeof(extra->desc));
extra->desc.dwSize = sizeof(extra->desc);
extra->desc.lpwfxFormat = &extra->wave_fmt;
extra->desc.dwBufferBytes = extra->wave_fmt.nAvgBytesPerSec * 5;
hr = capture_device->CreateCaptureBuffer(&extra->desc, &extra->buffer, NULL);
if (FAILED(hr)) {
al_free(extra);
ALLEGRO_ERROR("Unable to create Capture Buffer\n");
return 1;
}
extra->buffer->QueryInterface(_al_IID_IDirectSoundCaptureBuffer8, (void **) &extra->buffer8);
r->extra = extra;
r->thread = al_create_thread(_dsound_update_recorder, r);
return 0;
}
void _dsound_close_recorder(ALLEGRO_AUDIO_RECORDER *r) {
ALLEGRO_ASSERT(capture_device);
(void) r;
capture_device->Release();
capture_device = NULL;
}
static void _output_device_list_dtor(void* value, void* userdata)
{
(void)userdata;
ALLEGRO_AUDIO_DEVICE* device = (ALLEGRO_AUDIO_DEVICE*)value;
al_free(device->name);
al_free(device->identifier);
al_free(device);
}
static BOOL CALLBACK _ds_enum_callback(
LPGUID lpGuid,
LPCTSTR lpcstrDescription,
LPCTSTR lpcstrModule,
LPVOID lpContext)
{
(void)lpcstrModule;
(void)lpContext;
if (lpGuid != NULL) {
ALLEGRO_AUDIO_DEVICE* device = (ALLEGRO_AUDIO_DEVICE*)al_malloc(sizeof(ALLEGRO_AUDIO_DEVICE));
device->identifier = (void*)al_malloc(sizeof(GUID));
device->name = _twin_tchar_to_utf8(lpcstrDescription);
memcpy(device->identifier, lpGuid, sizeof(GUID));
_al_list_push_back_ex(output_device_list, device, _output_device_list_dtor);
}
return TRUE;
}
static _AL_LIST* _dsound_get_output_devices(void)
{
return output_device_list;
}
ALLEGRO_AUDIO_DRIVER _al_kcm_dsound_driver = {
"DirectSound",
_dsound_open,
_dsound_close,
_dsound_allocate_voice,
_dsound_deallocate_voice,
_dsound_load_voice,
_dsound_unload_voice,
_dsound_start_voice,
_dsound_stop_voice,
_dsound_voice_is_playing,
_dsound_get_voice_position,
_dsound_set_voice_position,
_dsound_open_recorder,
_dsound_close_recorder,
_dsound_get_output_devices,
};
}