rlm@0: /** rlm@0: * OpenAL cross platform audio library rlm@0: * Copyright (C) 1999-2007 by authors. rlm@0: * This library is free software; you can redistribute it and/or rlm@0: * modify it under the terms of the GNU Library General Public rlm@0: * License as published by the Free Software Foundation; either rlm@0: * version 2 of the License, or (at your option) any later version. rlm@0: * rlm@0: * This library is distributed in the hope that it will be useful, rlm@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of rlm@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU rlm@0: * Library General Public License for more details. rlm@0: * rlm@0: * You should have received a copy of the GNU Library General Public rlm@0: * License along with this library; if not, write to the rlm@0: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, rlm@0: * Boston, MA 02111-1307, USA. rlm@0: * Or go to http://www.gnu.org/copyleft/lgpl.html rlm@0: */ rlm@0: rlm@0: #include "config.h" rlm@0: rlm@0: #include rlm@0: #include rlm@0: #include rlm@0: #include "alMain.h" rlm@0: #include "AL/al.h" rlm@0: #include "AL/alc.h" rlm@0: rlm@0: rlm@0: typedef struct { rlm@0: FILE *f; rlm@0: long DataStart; rlm@0: rlm@0: ALvoid *buffer; rlm@0: ALuint size; rlm@0: rlm@0: volatile int killNow; rlm@0: ALvoid *thread; rlm@0: } wave_data; rlm@0: rlm@0: rlm@0: static const ALCchar waveDevice[] = "Wave File Writer"; rlm@0: rlm@0: static const ALubyte SUBTYPE_PCM[] = { rlm@0: 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, rlm@0: 0x00, 0x38, 0x9b, 0x71 rlm@0: }; rlm@0: static const ALubyte SUBTYPE_FLOAT[] = { rlm@0: 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, rlm@0: 0x00, 0x38, 0x9b, 0x71 rlm@0: }; rlm@0: rlm@0: static const ALuint channel_masks[] = { rlm@0: 0, /* invalid */ rlm@0: 0x4, /* Mono */ rlm@0: 0x1 | 0x2, /* Stereo */ rlm@0: 0, /* 3 channel */ rlm@0: 0x1 | 0x2 | 0x10 | 0x20, /* Quad */ rlm@0: 0, /* 5 channel */ rlm@0: 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */ rlm@0: 0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */ rlm@0: 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */ rlm@0: }; rlm@0: rlm@0: rlm@0: static void fwrite16le(ALushort val, FILE *f) rlm@0: { rlm@0: fputc(val&0xff, f); rlm@0: fputc((val>>8)&0xff, f); rlm@0: } rlm@0: rlm@0: static void fwrite32le(ALuint val, FILE *f) rlm@0: { rlm@0: fputc(val&0xff, f); rlm@0: fputc((val>>8)&0xff, f); rlm@0: fputc((val>>16)&0xff, f); rlm@0: fputc((val>>24)&0xff, f); rlm@0: } rlm@0: rlm@0: rlm@0: static ALuint WaveProc(ALvoid *ptr) rlm@0: { rlm@0: ALCdevice *pDevice = (ALCdevice*)ptr; rlm@0: wave_data *data = (wave_data*)pDevice->ExtraData; rlm@0: ALuint frameSize; rlm@0: ALuint now, start; rlm@0: ALuint64 avail, done; rlm@0: size_t fs; rlm@0: union { rlm@0: short s; rlm@0: char b[sizeof(short)]; rlm@0: } uSB; rlm@0: const ALuint restTime = (ALuint64)pDevice->UpdateSize * 1000 / rlm@0: pDevice->Frequency / 2; rlm@0: rlm@0: uSB.s = 1; rlm@0: frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); rlm@0: rlm@0: done = 0; rlm@0: start = timeGetTime(); rlm@0: while(!data->killNow && pDevice->Connected) rlm@0: { rlm@0: now = timeGetTime(); rlm@0: rlm@0: avail = (ALuint64)(now-start) * pDevice->Frequency / 1000; rlm@0: if(avail < done) rlm@0: { rlm@0: /* Timer wrapped. Add the remainder of the cycle to the available rlm@0: * count and reset the number of samples done */ rlm@0: avail += (ALuint64)0xFFFFFFFFu*pDevice->Frequency/1000 - done; rlm@0: done = 0; rlm@0: } rlm@0: if(avail-done < pDevice->UpdateSize) rlm@0: { rlm@0: Sleep(restTime); rlm@0: continue; rlm@0: } rlm@0: rlm@0: while(avail-done >= pDevice->UpdateSize) rlm@0: { rlm@0: aluMixData(pDevice, data->buffer, pDevice->UpdateSize); rlm@0: done += pDevice->UpdateSize; rlm@0: rlm@0: if(uSB.b[0] != 1) rlm@0: { rlm@0: ALuint bytesize = BytesFromDevFmt(pDevice->FmtType); rlm@0: ALubyte *bytes = data->buffer; rlm@0: ALuint i; rlm@0: rlm@0: if(bytesize == 1) rlm@0: { rlm@0: for(i = 0;i < data->size;i++) rlm@0: fputc(bytes[i], data->f); rlm@0: } rlm@0: else if(bytesize == 2) rlm@0: { rlm@0: for(i = 0;i < data->size;i++) rlm@0: fputc(bytes[i^1], data->f); rlm@0: } rlm@0: else if(bytesize == 4) rlm@0: { rlm@0: for(i = 0;i < data->size;i++) rlm@0: fputc(bytes[i^3], data->f); rlm@0: } rlm@0: } rlm@0: else rlm@0: fs = fwrite(data->buffer, frameSize, pDevice->UpdateSize, rlm@0: data->f); rlm@0: if(ferror(data->f)) rlm@0: { rlm@0: ERR("Error writing to file\n"); rlm@0: aluHandleDisconnect(pDevice); rlm@0: break; rlm@0: } rlm@0: } rlm@0: } rlm@0: rlm@0: return 0; rlm@0: } rlm@0: rlm@0: static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceName) rlm@0: { rlm@0: wave_data *data; rlm@0: const char *fname; rlm@0: rlm@0: fname = GetConfigValue("wave", "file", ""); rlm@0: if(!fname[0]) rlm@0: return ALC_FALSE; rlm@0: rlm@0: if(!deviceName) rlm@0: deviceName = waveDevice; rlm@0: else if(strcmp(deviceName, waveDevice) != 0) rlm@0: return ALC_FALSE; rlm@0: rlm@0: data = (wave_data*)calloc(1, sizeof(wave_data)); rlm@0: rlm@0: data->f = fopen(fname, "wb"); rlm@0: if(!data->f) rlm@0: { rlm@0: free(data); rlm@0: ERR("Could not open file '%s': %s\n", fname, strerror(errno)); rlm@0: return ALC_FALSE; rlm@0: } rlm@0: rlm@0: device->szDeviceName = strdup(deviceName); rlm@0: device->ExtraData = data; rlm@0: return ALC_TRUE; rlm@0: } rlm@0: rlm@0: static void wave_close_playback(ALCdevice *device) rlm@0: { rlm@0: wave_data *data = (wave_data*)device->ExtraData; rlm@0: rlm@0: fclose(data->f); rlm@0: free(data); rlm@0: device->ExtraData = NULL; rlm@0: } rlm@0: rlm@0: static ALCboolean wave_reset_playback(ALCdevice *device) rlm@0: { rlm@0: wave_data *data = (wave_data*)device->ExtraData; rlm@0: ALuint channels=0, bits=0; rlm@0: size_t val; rlm@0: rlm@0: fseek(data->f, 0, SEEK_SET); rlm@0: clearerr(data->f); rlm@0: rlm@0: switch(device->FmtType) rlm@0: { rlm@0: case DevFmtByte: rlm@0: device->FmtType = DevFmtUByte; rlm@0: break; rlm@0: case DevFmtUShort: rlm@0: device->FmtType = DevFmtShort; rlm@0: break; rlm@0: case DevFmtUByte: rlm@0: case DevFmtShort: rlm@0: case DevFmtFloat: rlm@0: break; rlm@0: } rlm@0: bits = BytesFromDevFmt(device->FmtType) * 8; rlm@0: channels = ChannelsFromDevFmt(device->FmtChans); rlm@0: rlm@0: fprintf(data->f, "RIFF"); rlm@0: fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close rlm@0: rlm@0: fprintf(data->f, "WAVE"); rlm@0: rlm@0: fprintf(data->f, "fmt "); rlm@0: fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE rlm@0: rlm@0: // 16-bit val, format type id (extensible: 0xFFFE) rlm@0: fwrite16le(0xFFFE, data->f); rlm@0: // 16-bit val, channel count rlm@0: fwrite16le(channels, data->f); rlm@0: // 32-bit val, frequency rlm@0: fwrite32le(device->Frequency, data->f); rlm@0: // 32-bit val, bytes per second rlm@0: fwrite32le(device->Frequency * channels * bits / 8, data->f); rlm@0: // 16-bit val, frame size rlm@0: fwrite16le(channels * bits / 8, data->f); rlm@0: // 16-bit val, bits per sample rlm@0: fwrite16le(bits, data->f); rlm@0: // 16-bit val, extra byte count rlm@0: fwrite16le(22, data->f); rlm@0: // 16-bit val, valid bits per sample rlm@0: fwrite16le(bits, data->f); rlm@0: // 32-bit val, channel mask rlm@0: fwrite32le(channel_masks[channels], data->f); rlm@0: // 16 byte GUID, sub-type format rlm@0: val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f); rlm@0: rlm@0: fprintf(data->f, "data"); rlm@0: fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close rlm@0: rlm@0: if(ferror(data->f)) rlm@0: { rlm@0: ERR("Error writing header: %s\n", strerror(errno)); rlm@0: return ALC_FALSE; rlm@0: } rlm@0: rlm@0: data->DataStart = ftell(data->f); rlm@0: rlm@0: data->size = device->UpdateSize * channels * bits / 8; rlm@0: data->buffer = malloc(data->size); rlm@0: if(!data->buffer) rlm@0: { rlm@0: ERR("Buffer malloc failed\n"); rlm@0: return ALC_FALSE; rlm@0: } rlm@0: rlm@0: SetDefaultWFXChannelOrder(device); rlm@0: rlm@0: data->thread = StartThread(WaveProc, device); rlm@0: if(data->thread == NULL) rlm@0: { rlm@0: free(data->buffer); rlm@0: data->buffer = NULL; rlm@0: return ALC_FALSE; rlm@0: } rlm@0: rlm@0: return ALC_TRUE; rlm@0: } rlm@0: rlm@0: static void wave_stop_playback(ALCdevice *device) rlm@0: { rlm@0: wave_data *data = (wave_data*)device->ExtraData; rlm@0: ALuint dataLen; rlm@0: long size; rlm@0: rlm@0: if(!data->thread) rlm@0: return; rlm@0: rlm@0: data->killNow = 1; rlm@0: StopThread(data->thread); rlm@0: data->thread = NULL; rlm@0: rlm@0: data->killNow = 0; rlm@0: rlm@0: free(data->buffer); rlm@0: data->buffer = NULL; rlm@0: rlm@0: size = ftell(data->f); rlm@0: if(size > 0) rlm@0: { rlm@0: dataLen = size - data->DataStart; rlm@0: if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0) rlm@0: fwrite32le(dataLen, data->f); // 'data' header len rlm@0: if(fseek(data->f, 4, SEEK_SET) == 0) rlm@0: fwrite32le(size-8, data->f); // 'WAVE' header len rlm@0: } rlm@0: } rlm@0: rlm@0: rlm@0: static const BackendFuncs wave_funcs = { rlm@0: wave_open_playback, rlm@0: wave_close_playback, rlm@0: wave_reset_playback, rlm@0: wave_stop_playback, rlm@0: NULL, rlm@0: NULL, rlm@0: NULL, rlm@0: NULL, rlm@0: NULL, rlm@0: NULL rlm@0: }; rlm@0: rlm@0: ALCboolean alc_wave_init(BackendFuncs *func_list) rlm@0: { rlm@0: *func_list = wave_funcs; rlm@0: printf("WAVE: I'm init!!\n"); rlm@0: return ALC_TRUE; rlm@0: } rlm@0: rlm@0: void alc_wave_deinit(void) rlm@0: { rlm@0: } rlm@0: rlm@0: void alc_wave_probe(enum DevProbe type) rlm@0: { rlm@0: printf("WAVE: I'm being probed :)\n"); rlm@0: if(!ConfigValueExists("wave", "file")) rlm@0: return; rlm@0: rlm@0: switch(type) rlm@0: { rlm@0: case DEVICE_PROBE: rlm@0: AppendDeviceList(waveDevice); rlm@0: break; rlm@0: case ALL_DEVICE_PROBE: rlm@0: AppendAllDeviceList(waveDevice); rlm@0: break; rlm@0: case CAPTURE_DEVICE_PROBE: rlm@0: break; rlm@0: } rlm@0: }