Simplify setting a fontsound link
[openal-soft.git] / Alc / backends / wave.c
blob421ca5d7e14a3f749f3a20a0939f74544f0a1900
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <memory.h>
26 #include <errno.h>
27 #ifdef HAVE_WINDOWS_H
28 #include <windows.h>
29 #endif
31 #include "alMain.h"
32 #include "alu.h"
33 #include "threads.h"
34 #include "compat.h"
37 typedef struct {
38 FILE *f;
39 long DataStart;
41 ALvoid *buffer;
42 ALuint size;
44 volatile int killNow;
45 althrd_t thread;
46 } wave_data;
49 static const ALCchar waveDevice[] = "Wave File Writer";
51 static const ALubyte SUBTYPE_PCM[] = {
52 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
53 0x00, 0x38, 0x9b, 0x71
55 static const ALubyte SUBTYPE_FLOAT[] = {
56 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
57 0x00, 0x38, 0x9b, 0x71
60 static const ALuint channel_masks[] = {
61 0, /* invalid */
62 0x4, /* Mono */
63 0x1 | 0x2, /* Stereo */
64 0, /* 3 channel */
65 0x1 | 0x2 | 0x10 | 0x20, /* Quad */
66 0, /* 5 channel */
67 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */
68 0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */
69 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */
73 static void fwrite16le(ALushort val, FILE *f)
75 fputc(val&0xff, f);
76 fputc((val>>8)&0xff, f);
79 static void fwrite32le(ALuint val, FILE *f)
81 fputc(val&0xff, f);
82 fputc((val>>8)&0xff, f);
83 fputc((val>>16)&0xff, f);
84 fputc((val>>24)&0xff, f);
88 static int WaveProc(void *ptr)
90 ALCdevice *device = (ALCdevice*)ptr;
91 wave_data *data = (wave_data*)device->ExtraData;
92 struct timespec now, start;
93 ALint64 avail, done;
94 ALuint frameSize;
95 size_t fs;
96 const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
97 device->Frequency / 2);
99 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
101 frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
103 done = 0;
104 if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
106 ERR("Failed to get starting time\n");
107 return 1;
109 while(!data->killNow && device->Connected)
111 if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
113 ERR("Failed to get current time\n");
114 return 1;
117 avail = (now.tv_sec - start.tv_sec) * device->Frequency;
118 avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
119 if(avail < done)
121 /* Oops, time skipped backwards. Reset the number of samples done
122 * with one update available since we (likely) just came back from
123 * sleeping. */
124 done = avail - device->UpdateSize;
127 if(avail-done < device->UpdateSize)
128 al_nssleep(0, restTime);
129 else while(avail-done >= device->UpdateSize)
131 aluMixData(device, data->buffer, device->UpdateSize);
132 done += device->UpdateSize;
134 if(!IS_LITTLE_ENDIAN)
136 ALuint bytesize = BytesFromDevFmt(device->FmtType);
137 ALubyte *bytes = data->buffer;
138 ALuint i;
140 if(bytesize == 1)
142 for(i = 0;i < data->size;i++)
143 fputc(bytes[i], data->f);
145 else if(bytesize == 2)
147 for(i = 0;i < data->size;i++)
148 fputc(bytes[i^1], data->f);
150 else if(bytesize == 4)
152 for(i = 0;i < data->size;i++)
153 fputc(bytes[i^3], data->f);
156 else
158 fs = fwrite(data->buffer, frameSize, device->UpdateSize,
159 data->f);
160 (void)fs;
162 if(ferror(data->f))
164 ERR("Error writing to file\n");
165 ALCdevice_Lock(device);
166 aluHandleDisconnect(device);
167 ALCdevice_Unlock(device);
168 break;
173 return 0;
176 static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
178 wave_data *data;
179 const char *fname;
181 fname = GetConfigValue("wave", "file", "");
182 if(!fname[0])
183 return ALC_INVALID_VALUE;
185 if(!deviceName)
186 deviceName = waveDevice;
187 else if(strcmp(deviceName, waveDevice) != 0)
188 return ALC_INVALID_VALUE;
190 data = (wave_data*)calloc(1, sizeof(wave_data));
192 data->f = al_fopen(fname, "wb");
193 if(!data->f)
195 free(data);
196 ERR("Could not open file '%s': %s\n", fname, strerror(errno));
197 return ALC_INVALID_VALUE;
200 al_string_copy_cstr(&device->DeviceName, deviceName);
201 device->ExtraData = data;
202 return ALC_NO_ERROR;
205 static void wave_close_playback(ALCdevice *device)
207 wave_data *data = (wave_data*)device->ExtraData;
209 fclose(data->f);
210 free(data);
211 device->ExtraData = NULL;
214 static ALCboolean wave_reset_playback(ALCdevice *device)
216 wave_data *data = (wave_data*)device->ExtraData;
217 ALuint channels=0, bits=0;
218 size_t val;
220 fseek(data->f, 0, SEEK_SET);
221 clearerr(data->f);
223 switch(device->FmtType)
225 case DevFmtByte:
226 device->FmtType = DevFmtUByte;
227 break;
228 case DevFmtUShort:
229 device->FmtType = DevFmtShort;
230 break;
231 case DevFmtUInt:
232 device->FmtType = DevFmtInt;
233 break;
234 case DevFmtUByte:
235 case DevFmtShort:
236 case DevFmtInt:
237 case DevFmtFloat:
238 break;
240 bits = BytesFromDevFmt(device->FmtType) * 8;
241 channels = ChannelsFromDevFmt(device->FmtChans);
243 fprintf(data->f, "RIFF");
244 fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close
246 fprintf(data->f, "WAVE");
248 fprintf(data->f, "fmt ");
249 fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE
251 // 16-bit val, format type id (extensible: 0xFFFE)
252 fwrite16le(0xFFFE, data->f);
253 // 16-bit val, channel count
254 fwrite16le(channels, data->f);
255 // 32-bit val, frequency
256 fwrite32le(device->Frequency, data->f);
257 // 32-bit val, bytes per second
258 fwrite32le(device->Frequency * channels * bits / 8, data->f);
259 // 16-bit val, frame size
260 fwrite16le(channels * bits / 8, data->f);
261 // 16-bit val, bits per sample
262 fwrite16le(bits, data->f);
263 // 16-bit val, extra byte count
264 fwrite16le(22, data->f);
265 // 16-bit val, valid bits per sample
266 fwrite16le(bits, data->f);
267 // 32-bit val, channel mask
268 fwrite32le(channel_masks[channels], data->f);
269 // 16 byte GUID, sub-type format
270 val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f);
271 (void)val;
273 fprintf(data->f, "data");
274 fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close
276 if(ferror(data->f))
278 ERR("Error writing header: %s\n", strerror(errno));
279 return ALC_FALSE;
281 data->DataStart = ftell(data->f);
283 SetDefaultWFXChannelOrder(device);
285 return ALC_TRUE;
288 static ALCboolean wave_start_playback(ALCdevice *device)
290 wave_data *data = (wave_data*)device->ExtraData;
292 data->size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
293 data->buffer = malloc(data->size);
294 if(!data->buffer)
296 ERR("Buffer malloc failed\n");
297 return ALC_FALSE;
300 data->killNow = 0;
301 if(althrd_create(&data->thread, WaveProc, device) != althrd_success)
303 free(data->buffer);
304 data->buffer = NULL;
305 return ALC_FALSE;
308 return ALC_TRUE;
311 static void wave_stop_playback(ALCdevice *device)
313 wave_data *data = (wave_data*)device->ExtraData;
314 ALuint dataLen;
315 long size;
316 int res;
318 if(data->killNow)
319 return;
321 data->killNow = 1;
322 althrd_join(data->thread, &res);
324 free(data->buffer);
325 data->buffer = NULL;
327 size = ftell(data->f);
328 if(size > 0)
330 dataLen = size - data->DataStart;
331 if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0)
332 fwrite32le(dataLen, data->f); // 'data' header len
333 if(fseek(data->f, 4, SEEK_SET) == 0)
334 fwrite32le(size-8, data->f); // 'WAVE' header len
339 static const BackendFuncs wave_funcs = {
340 wave_open_playback,
341 wave_close_playback,
342 wave_reset_playback,
343 wave_start_playback,
344 wave_stop_playback,
345 NULL,
346 NULL,
347 NULL,
348 NULL,
349 NULL,
350 NULL,
351 ALCdevice_GetLatencyDefault
354 ALCboolean alc_wave_init(BackendFuncs *func_list)
356 *func_list = wave_funcs;
357 return ALC_TRUE;
360 void alc_wave_deinit(void)
364 void alc_wave_probe(enum DevProbe type)
366 if(!ConfigValueExists("wave", "file"))
367 return;
369 switch(type)
371 case ALL_DEVICE_PROBE:
372 AppendAllDevicesList(waveDevice);
373 break;
374 case CAPTURE_DEVICE_PROBE:
375 break;