1 | /* -*- mode: C; tab-width:8; c-basic-offset:8 -*- |
---|
2 | * vi:set ts=8: |
---|
3 | * |
---|
4 | * waveout.c |
---|
5 | * |
---|
6 | * WAVE file output. Context writes, we save and sleep. |
---|
7 | * |
---|
8 | */ |
---|
9 | #include "al_siteconfig.h" |
---|
10 | |
---|
11 | #include <AL/al.h> |
---|
12 | #include <fcntl.h> |
---|
13 | #include <stdio.h> |
---|
14 | #include <stdlib.h> |
---|
15 | #include <string.h> |
---|
16 | #include <sys/stat.h> |
---|
17 | #include <sys/time.h> |
---|
18 | #include <sys/types.h> |
---|
19 | #include <unistd.h> |
---|
20 | |
---|
21 | #include "al_main.h" |
---|
22 | #include "al_debug.h" |
---|
23 | #include "backends/alc_backend.h" |
---|
24 | #include "audioconvert/ac_endian.h" |
---|
25 | |
---|
26 | #define WAVEOUT_NAMELEN 16 |
---|
27 | #define HEADERSIZE 28 |
---|
28 | #define DATAADJUSTMENT 44 |
---|
29 | |
---|
30 | |
---|
31 | #ifdef WORDS_BIGENDIAN |
---|
32 | #define RIFFMAGIC 0x52494646 |
---|
33 | #define WAVEMAGIC 0x57415645 |
---|
34 | #define FMTMAGIC 0x666D7420 |
---|
35 | #define DATAMAGIC 0x64617461 |
---|
36 | #else |
---|
37 | #define RIFFMAGIC 0x46464952 |
---|
38 | #define WAVEMAGIC 0x45564157 |
---|
39 | #define FMTMAGIC 0x20746D66 |
---|
40 | #define DATAMAGIC 0x61746164 |
---|
41 | #endif /* WORDS_BIGENDIAN */ |
---|
42 | |
---|
43 | /* better wart needed? JIV FIXME */ |
---|
44 | #ifdef MAXNAMELEN |
---|
45 | #undef MAXNAMELEN |
---|
46 | #endif /* MAXNAMELEN */ |
---|
47 | |
---|
48 | #define MAXNAMELEN 1024 |
---|
49 | |
---|
50 | typedef struct waveout_s { |
---|
51 | FILE *fh; |
---|
52 | |
---|
53 | ALuint format; |
---|
54 | ALuint speed; |
---|
55 | ALuint channels; |
---|
56 | |
---|
57 | ALuint length; |
---|
58 | |
---|
59 | ALushort bitspersample; |
---|
60 | |
---|
61 | char name[WAVEOUT_NAMELEN]; |
---|
62 | } waveout_t; |
---|
63 | |
---|
64 | static ALuint sleep_usec(ALuint speed, ALuint chunk); |
---|
65 | static void apply_header(waveout_t *wave); |
---|
66 | static const char *waveout_unique_name(char *template); |
---|
67 | |
---|
68 | /* |
---|
69 | * convert_to_little_endian( ALuint bps, void *data, int nbytes ) |
---|
70 | * |
---|
71 | * Convert data in place to little endian format. NOP on little endian |
---|
72 | * machines. |
---|
73 | */ |
---|
74 | static void convert_to_little_endian( ALuint bps, void *data, int nbytes ); |
---|
75 | |
---|
76 | static void *grab_write_waveout(void) { |
---|
77 | FILE *fh; |
---|
78 | waveout_t *retval = NULL; |
---|
79 | char template[MAXNAMELEN] = "openal-"; |
---|
80 | |
---|
81 | if(waveout_unique_name(template) == NULL) { |
---|
82 | perror("tmpnam"); |
---|
83 | } |
---|
84 | |
---|
85 | fh = fopen(template, "w+b"); |
---|
86 | if(fh == NULL) { |
---|
87 | fprintf(stderr, |
---|
88 | "waveout grab audio %s failed\n", template); |
---|
89 | return NULL; |
---|
90 | } |
---|
91 | |
---|
92 | retval = malloc(sizeof *retval); |
---|
93 | if(retval == NULL) { |
---|
94 | fclose(fh); |
---|
95 | return NULL; |
---|
96 | } |
---|
97 | |
---|
98 | memset(retval, 0, sizeof *retval); |
---|
99 | |
---|
100 | /* set to return params */ |
---|
101 | retval->fh = fh; |
---|
102 | strncpy(retval->name, template, WAVEOUT_NAMELEN); |
---|
103 | |
---|
104 | retval->length = 0; |
---|
105 | |
---|
106 | fprintf(stderr, "waveout grab audio %s\n", template); |
---|
107 | |
---|
108 | _alDebug(ALD_CONTEXT, __FILE__, __LINE__, |
---|
109 | "waveout grab audio ok"); |
---|
110 | |
---|
111 | fseek(retval->fh, SEEK_SET, HEADERSIZE); /* leave room for header */ |
---|
112 | return retval; |
---|
113 | } |
---|
114 | |
---|
115 | static void *grab_read_waveout(void) { |
---|
116 | return NULL; |
---|
117 | } |
---|
118 | |
---|
119 | void * |
---|
120 | alcBackendOpenWAVE_( ALC_OpenMode mode ) |
---|
121 | { |
---|
122 | return mode == ALC_OPEN_INPUT_ ? grab_read_waveout() : grab_write_waveout(); |
---|
123 | } |
---|
124 | |
---|
125 | void waveout_blitbuffer(void *handle, void *dataptr, int bytes_to_write) { |
---|
126 | waveout_t *whandle = NULL; |
---|
127 | |
---|
128 | if(handle == NULL) { |
---|
129 | return; |
---|
130 | } |
---|
131 | |
---|
132 | whandle = handle; |
---|
133 | |
---|
134 | if(whandle->fh == NULL) { |
---|
135 | return; |
---|
136 | } |
---|
137 | |
---|
138 | whandle->length += bytes_to_write; |
---|
139 | |
---|
140 | /* |
---|
141 | * WAV files expect their PCM data in LE format. If we are on |
---|
142 | * big endian host, we need to convert the data in place. |
---|
143 | * |
---|
144 | */ |
---|
145 | convert_to_little_endian( whandle->bitspersample, |
---|
146 | dataptr, |
---|
147 | bytes_to_write ); |
---|
148 | |
---|
149 | fwrite(dataptr, 1, bytes_to_write, whandle->fh); |
---|
150 | |
---|
151 | _alMicroSleep(sleep_usec(whandle->speed, bytes_to_write)); |
---|
152 | |
---|
153 | return; |
---|
154 | } |
---|
155 | |
---|
156 | /* |
---|
157 | * close file, free data |
---|
158 | */ |
---|
159 | void release_waveout(void *handle) { |
---|
160 | waveout_t *closer; |
---|
161 | |
---|
162 | if(handle == NULL) { |
---|
163 | return; |
---|
164 | } |
---|
165 | |
---|
166 | closer = handle; |
---|
167 | |
---|
168 | fprintf(stderr, "releasing waveout file %s\n", |
---|
169 | closer->name); |
---|
170 | |
---|
171 | fflush( closer->fh ); |
---|
172 | apply_header( closer ); |
---|
173 | |
---|
174 | fclose( closer->fh ); |
---|
175 | free( closer ); |
---|
176 | |
---|
177 | return; |
---|
178 | } |
---|
179 | |
---|
180 | static ALuint sleep_usec(ALuint speed, ALuint chunk) { |
---|
181 | ALuint retval; |
---|
182 | |
---|
183 | retval = 1000000.0 * chunk / speed; |
---|
184 | |
---|
185 | #if 0 |
---|
186 | fprintf(stderr, |
---|
187 | "(speed %d chunk %d retval = %d)\n", |
---|
188 | speed, |
---|
189 | chunk, |
---|
190 | retval); |
---|
191 | #endif |
---|
192 | |
---|
193 | return retval; |
---|
194 | } |
---|
195 | |
---|
196 | /* |
---|
197 | * FIXME: make endian correct |
---|
198 | */ |
---|
199 | static void apply_header(waveout_t *wave) { |
---|
200 | ALushort writer16; |
---|
201 | ALuint writer32; |
---|
202 | |
---|
203 | /* go to beginning */ |
---|
204 | if(fseek(wave->fh, SEEK_SET, 0) != 0) { |
---|
205 | fprintf(stderr, |
---|
206 | "Couldn't reset %s\n", wave->name); |
---|
207 | } |
---|
208 | |
---|
209 | /* 'RIFF' */ |
---|
210 | writer32 = RIFFMAGIC; |
---|
211 | fwrite(&writer32, 1, sizeof writer32, wave->fh); |
---|
212 | |
---|
213 | /* total length */ |
---|
214 | writer32 = swap32le(wave->length); |
---|
215 | fwrite(&writer32, 1, sizeof writer32, wave->fh); |
---|
216 | |
---|
217 | /* 'WAVE' */ |
---|
218 | writer32 = WAVEMAGIC; |
---|
219 | fwrite(&writer32, 1, sizeof writer32, wave->fh); |
---|
220 | |
---|
221 | /* 'fmt ' */ |
---|
222 | writer32 = FMTMAGIC; |
---|
223 | fwrite(&writer32, 1, sizeof writer32, wave->fh); |
---|
224 | |
---|
225 | /* fmt chunk length */ |
---|
226 | writer32 = swap32le(16); |
---|
227 | fwrite(&writer32, 1, sizeof writer32, wave->fh); |
---|
228 | |
---|
229 | /* ALushort encoding */ |
---|
230 | writer16 = swap16le(1); |
---|
231 | fwrite(&writer16, 1, sizeof writer16, wave->fh); |
---|
232 | |
---|
233 | /* Alushort channels */ |
---|
234 | writer16 = swap16le(wave->channels); |
---|
235 | fwrite(&writer16, 1, sizeof writer16, wave->fh); |
---|
236 | |
---|
237 | /* ALuint frequency */ |
---|
238 | writer32 = swap32le(wave->speed); |
---|
239 | fwrite(&writer32, 1, sizeof writer32, wave->fh); |
---|
240 | |
---|
241 | /* ALuint byterate */ |
---|
242 | writer32 = swap32le(wave->speed / sizeof (ALshort)); /* FIXME */ |
---|
243 | fwrite(&writer32, 1, sizeof writer32, wave->fh); |
---|
244 | |
---|
245 | /* ALushort blockalign */ |
---|
246 | writer16 = 0; |
---|
247 | fwrite(&writer16, 1, sizeof writer16, wave->fh); |
---|
248 | |
---|
249 | /* ALushort bitspersample */ |
---|
250 | writer16 = swap16le(wave->bitspersample); |
---|
251 | fwrite(&writer16, 1, sizeof writer16, wave->fh); |
---|
252 | |
---|
253 | /* 'data' */ |
---|
254 | writer32 = DATAMAGIC; |
---|
255 | fwrite(&writer32, 1, sizeof writer32, wave->fh); |
---|
256 | |
---|
257 | /* data length */ |
---|
258 | writer32 = swap32le(wave->length - DATAADJUSTMENT); /* samples */ |
---|
259 | fwrite(&writer32, 1, sizeof writer32, wave->fh); |
---|
260 | |
---|
261 | fprintf(stderr, "waveout length %d\n", wave->length); |
---|
262 | |
---|
263 | return; |
---|
264 | } |
---|
265 | |
---|
266 | static const char *waveout_unique_name(char *template) { |
---|
267 | static char retval[MAXNAMELEN]; |
---|
268 | int template_offset; |
---|
269 | static int sequence = 0; |
---|
270 | struct stat buf; |
---|
271 | |
---|
272 | strncpy(retval, template, MAXNAMELEN - 2); |
---|
273 | retval[MAXNAMELEN - 1] = '\0'; |
---|
274 | |
---|
275 | template_offset = strlen(retval); |
---|
276 | |
---|
277 | if(template_offset >= MAXNAMELEN - 28) { /* kludgey */ |
---|
278 | /* template too big */ |
---|
279 | return NULL; |
---|
280 | } |
---|
281 | |
---|
282 | do { |
---|
283 | /* repeat until we have a unique name */ |
---|
284 | snprintf(&retval[template_offset], sizeof(retval) - template_offset, "%d.wav", sequence++); |
---|
285 | strncpy(template, retval, MAXNAMELEN); |
---|
286 | } while(stat(retval, &buf) == 0); |
---|
287 | |
---|
288 | return retval; |
---|
289 | } |
---|
290 | |
---|
291 | static ALboolean set_write_waveout(void *handle, |
---|
292 | UNUSED(ALuint *bufsiz), |
---|
293 | ALenum *fmt, |
---|
294 | ALuint *speed) { |
---|
295 | waveout_t *whandle; |
---|
296 | ALuint chans = _alGetChannelsFromFormat( *fmt ); |
---|
297 | |
---|
298 | if(handle == NULL) { |
---|
299 | return AL_FALSE; |
---|
300 | } |
---|
301 | |
---|
302 | whandle = handle; |
---|
303 | |
---|
304 | whandle->speed = *speed; |
---|
305 | whandle->format = *fmt; |
---|
306 | whandle->channels = chans; |
---|
307 | whandle->bitspersample = _alGetBitsFromFormat(*fmt); |
---|
308 | |
---|
309 | return AL_TRUE; |
---|
310 | } |
---|
311 | |
---|
312 | static ALboolean set_read_waveout(UNUSED(void *handle), |
---|
313 | UNUSED(ALuint *bufsiz), |
---|
314 | UNUSED(ALenum *fmt), |
---|
315 | UNUSED(ALuint *speed)) { |
---|
316 | |
---|
317 | return AL_FALSE; |
---|
318 | } |
---|
319 | |
---|
320 | ALboolean |
---|
321 | alcBackendSetAttributesWAVE_(ALC_OpenMode mode, void *handle, ALuint *bufsiz, ALenum *fmt, ALuint *speed) |
---|
322 | { |
---|
323 | return mode == ALC_OPEN_INPUT_ ? |
---|
324 | set_read_waveout(handle, bufsiz, fmt, speed) : |
---|
325 | set_write_waveout(handle, bufsiz, fmt, speed); |
---|
326 | } |
---|
327 | |
---|
328 | /* |
---|
329 | * convert_to_little_endian( ALuint bps, void *data, int nbytes ) |
---|
330 | * |
---|
331 | * Convert data in place to little endian format. bps is the bits per |
---|
332 | * sample in data, nbytes is the length of data in bytes. If bps is 8, |
---|
333 | * or the machine is little endian, this is a nop. |
---|
334 | * |
---|
335 | * FIXME: |
---|
336 | * We only handle 16-bit signed formats for now. Should fix this. |
---|
337 | */ |
---|
338 | static void convert_to_little_endian( ALuint bps, void *data, int nbytes ) |
---|
339 | { |
---|
340 | assert( data ); |
---|
341 | assert( nbytes > 0 ); |
---|
342 | |
---|
343 | if( bps == 8 ) { |
---|
344 | /* 8-bit samples don't need to be converted */ |
---|
345 | return; |
---|
346 | } |
---|
347 | |
---|
348 | assert( bps == 16 ); |
---|
349 | |
---|
350 | #ifdef WORDS_BIGENDIAN |
---|
351 | /* do the conversion */ |
---|
352 | { |
---|
353 | ALshort *outp = data; |
---|
354 | ALuint i; |
---|
355 | for( i = 0; i < nbytes/sizeof(ALshort); i++ ) { |
---|
356 | outp[i] = swap16le( outp[i] ); |
---|
357 | } |
---|
358 | } |
---|
359 | #else |
---|
360 | (void)data; (void)nbytes; |
---|
361 | #endif /* WORDS_BIG_ENDIAN */ |
---|
362 | } |
---|
363 | |
---|
364 | void |
---|
365 | pause_waveout( UNUSED(void *handle) ) |
---|
366 | { |
---|
367 | } |
---|
368 | |
---|
369 | void |
---|
370 | resume_waveout( UNUSED(void *handle) ) |
---|
371 | { |
---|
372 | } |
---|
373 | |
---|
374 | ALsizei |
---|
375 | capture_waveout( UNUSED(void *handle), UNUSED(void *capture_buffer), UNUSED(int bufsiz) ) |
---|
376 | { |
---|
377 | return 0; |
---|
378 | } |
---|
379 | |
---|
380 | ALfloat |
---|
381 | get_waveoutchannel( UNUSED(void *handle), UNUSED(ALuint channel) ) |
---|
382 | { |
---|
383 | return 0.0; |
---|
384 | } |
---|
385 | |
---|
386 | int |
---|
387 | set_waveoutchannel( UNUSED(void *handle), UNUSED(ALuint channel), UNUSED(ALfloat volume) ) |
---|
388 | { |
---|
389 | return 0; |
---|
390 | } |
---|