diff options
Diffstat (limited to 'Alc/backends/wave.c')
-rw-r--r-- | Alc/backends/wave.c | 325 |
1 files changed, 195 insertions, 130 deletions
diff --git a/Alc/backends/wave.c b/Alc/backends/wave.c index 421ca5d7..6b47c611 100644 --- a/Alc/backends/wave.c +++ b/Alc/backends/wave.c @@ -13,8 +13,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ @@ -24,26 +24,13 @@ #include <stdio.h> #include <memory.h> #include <errno.h> -#ifdef HAVE_WINDOWS_H -#include <windows.h> -#endif #include "alMain.h" #include "alu.h" #include "threads.h" #include "compat.h" - -typedef struct { - FILE *f; - long DataStart; - - ALvoid *buffer; - ALuint size; - - volatile int killNow; - althrd_t thread; -} wave_data; +#include "backends/base.h" static const ALCchar waveDevice[] = "Wave File Writer"; @@ -57,18 +44,15 @@ static const ALubyte SUBTYPE_FLOAT[] = { 0x00, 0x38, 0x9b, 0x71 }; -static const ALuint channel_masks[] = { - 0, /* invalid */ - 0x4, /* Mono */ - 0x1 | 0x2, /* Stereo */ - 0, /* 3 channel */ - 0x1 | 0x2 | 0x10 | 0x20, /* Quad */ - 0, /* 5 channel */ - 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */ - 0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */ - 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */ +static const ALubyte SUBTYPE_BFORMAT_PCM[] = { + 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, + 0xca, 0x00, 0x00, 0x00 }; +static const ALubyte SUBTYPE_BFORMAT_FLOAT[] = { + 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, + 0xca, 0x00, 0x00, 0x00 +}; static void fwrite16le(ALushort val, FILE *f) { @@ -85,10 +69,57 @@ static void fwrite32le(ALuint val, FILE *f) } -static int WaveProc(void *ptr) +typedef struct ALCwaveBackend { + DERIVE_FROM_TYPE(ALCbackend); + + FILE *mFile; + long mDataStart; + + ALvoid *mBuffer; + ALuint mSize; + + volatile int killNow; + althrd_t thread; +} ALCwaveBackend; + +static int ALCwaveBackend_mixerProc(void *ptr); + +static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device); +static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, Destruct) +static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name); +static void ALCwaveBackend_close(ALCwaveBackend *self); +static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self); +static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self); +static void ALCwaveBackend_stop(ALCwaveBackend *self); +static DECLARE_FORWARD2(ALCwaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend) + +DEFINE_ALCBACKEND_VTABLE(ALCwaveBackend); + + +static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCwaveBackend, ALCbackend, self); + + self->mFile = NULL; + self->mDataStart = -1; + + self->mBuffer = NULL; + self->mSize = 0; + + self->killNow = 1; +} + + +static int ALCwaveBackend_mixerProc(void *ptr) { - ALCdevice *device = (ALCdevice*)ptr; - wave_data *data = (wave_data*)device->ExtraData; + ALCwaveBackend *self = (ALCwaveBackend*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; struct timespec now, start; ALint64 avail, done; ALuint frameSize; @@ -106,7 +137,7 @@ static int WaveProc(void *ptr) ERR("Failed to get starting time\n"); return 1; } - while(!data->killNow && device->Connected) + while(!self->killNow && device->Connected) { if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) { @@ -125,41 +156,41 @@ static int WaveProc(void *ptr) } if(avail-done < device->UpdateSize) - al_nssleep(0, restTime); + al_nssleep(restTime); else while(avail-done >= device->UpdateSize) { - aluMixData(device, data->buffer, device->UpdateSize); + aluMixData(device, self->mBuffer, device->UpdateSize); done += device->UpdateSize; if(!IS_LITTLE_ENDIAN) { ALuint bytesize = BytesFromDevFmt(device->FmtType); - ALubyte *bytes = data->buffer; + ALubyte *bytes = self->mBuffer; ALuint i; if(bytesize == 1) { - for(i = 0;i < data->size;i++) - fputc(bytes[i], data->f); + for(i = 0;i < self->mSize;i++) + fputc(bytes[i], self->mFile); } else if(bytesize == 2) { - for(i = 0;i < data->size;i++) - fputc(bytes[i^1], data->f); + for(i = 0;i < self->mSize;i++) + fputc(bytes[i^1], self->mFile); } else if(bytesize == 4) { - for(i = 0;i < data->size;i++) - fputc(bytes[i^3], data->f); + for(i = 0;i < self->mSize;i++) + fputc(bytes[i^3], self->mFile); } } else { - fs = fwrite(data->buffer, frameSize, device->UpdateSize, - data->f); + fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, + self->mFile); (void)fs; } - if(ferror(data->f)) + if(ferror(self->mFile)) { ERR("Error writing to file\n"); ALCdevice_Lock(device); @@ -173,52 +204,52 @@ static int WaveProc(void *ptr) return 0; } -static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName) + +static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name) { - wave_data *data; + ALCdevice *device; const char *fname; - fname = GetConfigValue("wave", "file", ""); - if(!fname[0]) - return ALC_INVALID_VALUE; + fname = GetConfigValue(NULL, "wave", "file", ""); + if(!fname[0]) return ALC_INVALID_VALUE; - if(!deviceName) - deviceName = waveDevice; - else if(strcmp(deviceName, waveDevice) != 0) + if(!name) + name = waveDevice; + else if(strcmp(name, waveDevice) != 0) return ALC_INVALID_VALUE; - data = (wave_data*)calloc(1, sizeof(wave_data)); - - data->f = al_fopen(fname, "wb"); - if(!data->f) + self->mFile = al_fopen(fname, "wb"); + if(!self->mFile) { - free(data); ERR("Could not open file '%s': %s\n", fname, strerror(errno)); return ALC_INVALID_VALUE; } - al_string_copy_cstr(&device->DeviceName, deviceName); - device->ExtraData = data; + device = STATIC_CAST(ALCbackend, self)->mDevice; + al_string_copy_cstr(&device->DeviceName, name); + return ALC_NO_ERROR; } -static void wave_close_playback(ALCdevice *device) +static void ALCwaveBackend_close(ALCwaveBackend *self) { - wave_data *data = (wave_data*)device->ExtraData; - - fclose(data->f); - free(data); - device->ExtraData = NULL; + if(self->mFile) + fclose(self->mFile); + self->mFile = NULL; } -static ALCboolean wave_reset_playback(ALCdevice *device) +static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self) { - wave_data *data = (wave_data*)device->ExtraData; - ALuint channels=0, bits=0; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + ALuint channels=0, bits=0, chanmask=0; + int isbformat = 0; size_t val; - fseek(data->f, 0, SEEK_SET); - clearerr(data->f); + fseek(self->mFile, 0, SEEK_SET); + clearerr(self->mFile); + + if(GetConfigValueBool(NULL, "wave", "bformat", 0)) + device->FmtChans = DevFmtBFormat3D; switch(device->FmtType) { @@ -237,135 +268,156 @@ static ALCboolean wave_reset_playback(ALCdevice *device) case DevFmtFloat: break; } + switch(device->FmtChans) + { + case DevFmtMono: chanmask = 0x04; break; + case DevFmtStereo: chanmask = 0x01 | 0x02; break; + case DevFmtQuad: chanmask = 0x01 | 0x02 | 0x10 | 0x20; break; + case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break; + case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break; + case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break; + case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break; + case DevFmtBFormat3D: + isbformat = 1; + chanmask = 0; + break; + } bits = BytesFromDevFmt(device->FmtType) * 8; channels = ChannelsFromDevFmt(device->FmtChans); - fprintf(data->f, "RIFF"); - fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close + fprintf(self->mFile, "RIFF"); + fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close - fprintf(data->f, "WAVE"); + fprintf(self->mFile, "WAVE"); - fprintf(data->f, "fmt "); - fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE + fprintf(self->mFile, "fmt "); + fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE // 16-bit val, format type id (extensible: 0xFFFE) - fwrite16le(0xFFFE, data->f); + fwrite16le(0xFFFE, self->mFile); // 16-bit val, channel count - fwrite16le(channels, data->f); + fwrite16le(channels, self->mFile); // 32-bit val, frequency - fwrite32le(device->Frequency, data->f); + fwrite32le(device->Frequency, self->mFile); // 32-bit val, bytes per second - fwrite32le(device->Frequency * channels * bits / 8, data->f); + fwrite32le(device->Frequency * channels * bits / 8, self->mFile); // 16-bit val, frame size - fwrite16le(channels * bits / 8, data->f); + fwrite16le(channels * bits / 8, self->mFile); // 16-bit val, bits per sample - fwrite16le(bits, data->f); + fwrite16le(bits, self->mFile); // 16-bit val, extra byte count - fwrite16le(22, data->f); + fwrite16le(22, self->mFile); // 16-bit val, valid bits per sample - fwrite16le(bits, data->f); + fwrite16le(bits, self->mFile); // 32-bit val, channel mask - fwrite32le(channel_masks[channels], data->f); + fwrite32le(chanmask, self->mFile); // 16 byte GUID, sub-type format - val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f); + val = fwrite(((bits==32) ? (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) : + (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM)), 1, 16, self->mFile); (void)val; - fprintf(data->f, "data"); - fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close + fprintf(self->mFile, "data"); + fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close - if(ferror(data->f)) + if(ferror(self->mFile)) { ERR("Error writing header: %s\n", strerror(errno)); return ALC_FALSE; } - data->DataStart = ftell(data->f); + self->mDataStart = ftell(self->mFile); SetDefaultWFXChannelOrder(device); return ALC_TRUE; } -static ALCboolean wave_start_playback(ALCdevice *device) +static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self) { - wave_data *data = (wave_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - data->size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - data->buffer = malloc(data->size); - if(!data->buffer) + self->mSize = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + self->mBuffer = malloc(self->mSize); + if(!self->mBuffer) { ERR("Buffer malloc failed\n"); return ALC_FALSE; } - data->killNow = 0; - if(althrd_create(&data->thread, WaveProc, device) != althrd_success) + self->killNow = 0; + if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success) { - free(data->buffer); - data->buffer = NULL; + free(self->mBuffer); + self->mBuffer = NULL; + self->mSize = 0; return ALC_FALSE; } return ALC_TRUE; } -static void wave_stop_playback(ALCdevice *device) +static void ALCwaveBackend_stop(ALCwaveBackend *self) { - wave_data *data = (wave_data*)device->ExtraData; ALuint dataLen; long size; int res; - if(data->killNow) + if(self->killNow) return; - data->killNow = 1; - althrd_join(data->thread, &res); + self->killNow = 1; + althrd_join(self->thread, &res); - free(data->buffer); - data->buffer = NULL; + free(self->mBuffer); + self->mBuffer = NULL; - size = ftell(data->f); + size = ftell(self->mFile); if(size > 0) { - dataLen = size - data->DataStart; - if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0) - fwrite32le(dataLen, data->f); // 'data' header len - if(fseek(data->f, 4, SEEK_SET) == 0) - fwrite32le(size-8, data->f); // 'WAVE' header len + dataLen = size - self->mDataStart; + if(fseek(self->mFile, self->mDataStart-4, SEEK_SET) == 0) + fwrite32le(dataLen, self->mFile); // 'data' header len + if(fseek(self->mFile, 4, SEEK_SET) == 0) + fwrite32le(size-8, self->mFile); // 'WAVE' header len } } -static const BackendFuncs wave_funcs = { - wave_open_playback, - wave_close_playback, - wave_reset_playback, - wave_start_playback, - wave_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - ALCdevice_GetLatencyDefault -}; +typedef struct ALCwaveBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCwaveBackendFactory; +#define ALCWAVEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwaveBackendFactory, ALCbackendFactory) } } + +ALCbackendFactory *ALCwaveBackendFactory_getFactory(void); + +static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self); +static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit) +static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type); +static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory); + -ALCboolean alc_wave_init(BackendFuncs *func_list) +ALCbackendFactory *ALCwaveBackendFactory_getFactory(void) { - *func_list = wave_funcs; - return ALC_TRUE; + static ALCwaveBackendFactory factory = ALCWAVEBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); } -void alc_wave_deinit(void) + +static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory* UNUSED(self)) { + return ALC_TRUE; } -void alc_wave_probe(enum DevProbe type) +static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUSED(self), ALCbackend_Type type) { - if(!ConfigValueExists("wave", "file")) - return; + if(type == ALCbackend_Playback) + return !!ConfigValueExists(NULL, "wave", "file"); + return ALC_FALSE; +} +static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type) +{ switch(type) { case ALL_DEVICE_PROBE: @@ -375,3 +427,16 @@ void alc_wave_probe(enum DevProbe type) break; } } + +static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCwaveBackend *backend; + NEW_OBJ(backend, ALCwaveBackend)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} |