diff options
author | Chris Robinson <[email protected]> | 2008-01-11 09:32:22 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2008-01-11 09:32:22 -0800 |
commit | f10408739e22cea6b3c52b8a9f6e36792c33d855 (patch) | |
tree | 547df88fe3dcbbbd51c72c56067e5cf173b3f9ae /Alc/wave.c | |
parent | e1d0ad749bd5772d968aa8b5ed600dee905310a4 (diff) |
Add a wave file writing backend
Diffstat (limited to 'Alc/wave.c')
-rw-r--r-- | Alc/wave.c | 335 |
1 files changed, 335 insertions, 0 deletions
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 <stdlib.h> +#include <stdio.h> +#include <memory.h> +#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); +} |