diff --git a/Alc/ALc.c b/Alc/ALc.c index d33390f7..0ec42533 100644 --- a/Alc/ALc.c +++ b/Alc/ALc.c @@ -61,6 +61,8 @@ struct { { "winmm", alcWinMMInit, EmptyFuncs }, #endif + { "wave", alc_wave_init, EmptyFuncs }, + { NULL, NULL, EmptyFuncs } }; #undef EmptyFuncs diff --git a/Alc/wave.c b/Alc/wave.c new file mode 100644 index 00000000..3de7ea80 --- /dev/null +++ b/Alc/wave.c @@ -0,0 +1,335 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include +#include +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + FILE *f; + long DataStart; + + ALvoid *buffer; + ALuint size; + + int killNow; + ALvoid *thread; +} wave_data; + + +static ALCchar *waveDevice; + + +static ALuint WaveProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + wave_data *data = (wave_data*)pDevice->ExtraData; + ALuint frameSize; + ALuint now, last; + size_t WriteCnt; + ALuint avail; + union { + short s; + char b[sizeof(short)]; + } uSB; + + uSB.s = 1; + frameSize = aluBytesFromFormat(pDevice->Format) * + aluChannelsFromFormat(pDevice->Format); + + last = timeGetTime(); + while(!data->killNow) + { + now = timeGetTime(); + + avail = (now-last) * pDevice->Frequency / 1000; + if(avail < pDevice->UpdateFreq/4) + { + Sleep(1); + continue; + } + + while(avail > 0) + { + SuspendContext(NULL); + WriteCnt = min(data->size, avail); + aluMixData(pDevice->Context, data->buffer, WriteCnt * frameSize, + pDevice->Format); + ProcessContext(NULL); + + if(uSB.b[0] != 1 && aluBytesFromFormat(pDevice->Format) > 1) + { + ALubyte *bytes = data->buffer; + ALuint i; + + for(i = 0;i < WriteCnt*frameSize;i++) + fputc(bytes[i^1], data->f); + } + else + fwrite(data->buffer, frameSize, WriteCnt, data->f); + if(ferror(data->f)) + { + AL_PRINT("Error writing to file\n"); + data->killNow = 1; + break; + } + + avail -= WriteCnt; + } + last = now; + } + + return 0; +} + +static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + wave_data *data; + ALuint channels; + ALuint bits; + char fname[64]; + int i; + + strncpy(fname, GetConfigValue("wave", "file", ""), sizeof(fname)-1); + fname[sizeof(fname)-1] = 0; + if(!fname[0]) + return ALC_FALSE; + + if(deviceName) + { + if(strcmp(deviceName, waveDevice) != 0) + return ALC_FALSE; + device->szDeviceName = waveDevice; + } + else + device->szDeviceName = waveDevice; + + data = (wave_data*)calloc(1, sizeof(wave_data)); + + data->f = fopen(fname, "wb"); + if(!data->f) + { + free(data); + AL_PRINT("Could not open file '%s': %s\n", fname, strerror(errno)); + return ALC_FALSE; + } + + bits = aluBytesFromFormat(device->Format) * 8; + channels = aluChannelsFromFormat(device->Format); + switch(bits) + { + case 8: + case 16: + if(channels == 0) + { + AL_PRINT("Unknown format?! %x\n", device->Format); + fclose(data->f); + free(data); + return ALC_FALSE; + } + break; + + default: + AL_PRINT("Unknown format?! %x\n", device->Format); + fclose(data->f); + free(data); + return ALC_FALSE; + } + + fprintf(data->f, "RIFF"); + fputc(0, data->f); // 'RIFF' header len; filled in at close + fputc(0, data->f); + fputc(0, data->f); + fputc(0, data->f); + + fprintf(data->f, "WAVE"); + + fprintf(data->f, "fmt "); + fputc(16, data->f); // 'fmt ' header len; 16 bytes for PCM + fputc(0, data->f); + fputc(0, data->f); + fputc(0, data->f); + // 16-bit val, format type id (PCM: 1) + fputc(1, data->f); + fputc(0, data->f); + // 16-bit val, channel count + fputc(channels&0xff, data->f); + fputc((channels>>8)&0xff, data->f); + // 32-bit val, frequency + fputc(device->Frequency&0xff, data->f); + fputc((device->Frequency>>8)&0xff, data->f); + fputc((device->Frequency>>16)&0xff, data->f); + fputc((device->Frequency>>24)&0xff, data->f); + // 32-bit val, bytes per second + i = device->Frequency * channels * bits / 8; + fputc(i&0xff, data->f); + fputc((i>>8)&0xff, data->f); + fputc((i>>16)&0xff, data->f); + fputc((i>>24)&0xff, data->f); + // 16-bit val, frame size + i = channels * bits / 8; + fputc(i&0xff, data->f); + fputc((i>>8)&0xff, data->f); + // 16-bit val, bits per sample + fputc(bits&0xff, data->f); + fputc((bits>>8)&0xff, data->f); + + fprintf(data->f, "data"); + fputc(0, data->f); // 'data' header len; filled in at close + fputc(0, data->f); + fputc(0, data->f); + fputc(0, data->f); + + data->DataStart = ftell(data->f); + if(data->DataStart == -1 || ferror(data->f)) + { + AL_PRINT("Error writing header: %s\n", strerror(errno)); + fclose(data->f); + free(data); + return ALC_FALSE; + } + + device->MaxNoOfSources = 256; + device->UpdateFreq = max(device->UpdateFreq, 2048); + + data->size = device->UpdateFreq; + data->buffer = malloc(data->size * channels * bits / 8); + if(!data->buffer) + { + AL_PRINT("buffer malloc failed\n"); + fclose(data->f); + free(data); + return ALC_FALSE; + } + + device->ExtraData = data; + data->thread = StartThread(WaveProc, device); + if(data->thread == NULL) + { + device->ExtraData = NULL; + fclose(data->f); + free(data->buffer); + free(data); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void wave_close_playback(ALCdevice *device) +{ + wave_data *data = (wave_data*)device->ExtraData; + ALuint dataLen; + long size; + + data->killNow = 1; + StopThread(data->thread); + + size = ftell(data->f); + if(size > 0) + { + dataLen = size - data->DataStart; + if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0) + { + fputc(dataLen&0xff, data->f); // 'data' header len + fputc((dataLen>>8)&0xff, data->f); + fputc((dataLen>>16)&0xff, data->f); + fputc((dataLen>>24)&0xff, data->f); + } + if(fseek(data->f, 4, SEEK_SET) == 0) + { + size -= 8; + fputc(size&0xff, data->f); // 'WAVE' header len + fputc((size>>8)&0xff, data->f); + fputc((size>>16)&0xff, data->f); + fputc((size>>24)&0xff, data->f); + } + } + + fclose(data->f); + free(data->buffer); + free(data); + device->ExtraData = NULL; +} + + +static ALCboolean wave_open_capture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) +{ + (void)pDevice; + (void)deviceName; + (void)frequency; + (void)format; + (void)SampleSize; + return ALC_FALSE; +} + +static void wave_close_capture(ALCdevice *pDevice) +{ + (void)pDevice; +} + +static void wave_start_capture(ALCdevice *pDevice) +{ + (void)pDevice; +} + +static void wave_stop_capture(ALCdevice *pDevice) +{ + (void)pDevice; +} + +static void wave_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + (void)pDevice; + (void)pBuffer; + (void)lSamples; +} + +static ALCuint wave_available_samples(ALCdevice *pDevice) +{ + (void)pDevice; + return 0; +} + + +BackendFuncs wave_funcs = { + wave_open_playback, + wave_close_playback, + wave_open_capture, + wave_close_capture, + wave_start_capture, + wave_stop_capture, + wave_capture_samples, + wave_available_samples +}; + +void alc_wave_init(BackendFuncs *func_list) +{ + *func_list = wave_funcs; + + waveDevice = AppendDeviceList("Wave File Writer"); + AppendAllDeviceList(waveDevice); +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 4955d250..ee2cc94e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,6 +190,7 @@ SET(ALC_OBJS Alc/ALc.c Alc/alcRing.c Alc/alcThread.c Alc/bs2b.c + Alc/wave.c ) SET(BACKENDS "") @@ -263,6 +264,9 @@ IF("${HAVE_WINDOWS_H}") ENDIF() ENDIF() +# This is always available +SET(BACKENDS ${BACKENDS} WaveFile) + # End configuration CONFIGURE_FILE( "${OpenAL_SOURCE_DIR}/config.h.in" diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index c238a3a9..840c6993 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -149,6 +149,7 @@ void alc_alsa_init(BackendFuncs *func_list); void alc_oss_init(BackendFuncs *func_list); void alcDSoundInit(BackendFuncs *func_list); void alcWinMMInit(BackendFuncs *FuncList); +void alc_wave_init(BackendFuncs *func_list); struct ALCdevice_struct diff --git a/openalrc.sample b/openalrc.sample index 0e378a84..37325d14 100644 --- a/openalrc.sample +++ b/openalrc.sample @@ -44,7 +44,7 @@ drivers = # Sets the backend driver list order, comma-seperated. Unknown # backends and duplicated names are ignored, and unlisted backends # won't be considered for use. An empty list means the default. # Default is: - # alsa,oss,dsound,winmm + # alsa,oss,dsound,winmm,wave [alsa] # ALSA backend stuff device = default # Sets the device name for the default playback device. @@ -67,3 +67,9 @@ capture = /dev/dsp # Sets the device name for OSS capture. Default is /dev/dsp [winmm] # Windows Multimedia backend stuff # Nothing yet... + +[wave] # Wave File Writer stuff +file = # Sets the filename of the wave file to write to. An empty name + # prevents the backend from opening, even when explicitly requested. + # THIS WILL OVERWRITE EXISTING FILES WITHOUT QUESTION! + # Default is empty