make it possible to compile without audio/c-flod
[rofl0r-openDOW.git] / audio.c
blobb73ea595c4269cf44cbb2f2c33b156421b218531
1 #include "audio.h"
3 #if AUDIO_BACKEND == AUDIO_BACKEND_AO
4 //RcB: SKIPON "AUDIO_BACKEND=AUDIO_BACKEND_SDL"
5 //RcB: SKIPON "AUDIO_BACKEND=AUDIO_BACKEND_NONE"
6 #include "../c-flod/backends/aowriter.h"
7 //RcB: SKIPOFF "AUDIO_BACKEND=AUDIO_BACKEND_NONE"
8 //RcB: SKIPOFF "AUDIO_BACKEND=AUDIO_BACKEND_SDL"
9 #define BACKEND_STRUCT AoWriter
10 #define BACKEND_INIT AoWriter_init
11 #define BACKEND_CLOSE AoWriter_close
12 #define BACKEND_WRITE AoWriter_write
13 #elif AUDIO_BACKEND == AUDIO_BACKEND_SDL
14 //RcB: SKIPON "AUDIO_BACKEND=AUDIO_BACKEND_AO"
15 //RcB: SKIPON "AUDIO_BACKEND=AUDIO_BACKEND_NONE"
16 #include "../c-flod/backends/sdlwriter.h"
17 //RcB: SKIPOFF "AUDIO_BACKEND=AUDIO_BACKEND_NONE"
18 //RcB: SKIPOFF "AUDIO_BACKEND=AUDIO_BACKEND_AO"
19 #define BACKEND_STRUCT SdlWriter
20 #define BACKEND_INIT SdlWriter_init
21 #define BACKEND_CLOSE SdlWriter_close
22 #define BACKEND_WRITE SdlWriter_write
24 #endif
26 #if AUDIO_BACKEND == AUDIO_BACKEND_NONE
27 void audio_init(void) {}
28 int audio_open_music_resource(const unsigned char* data, size_t data_size, int track) { return 0; }
29 int audio_open_music(const char* filename, int track) { return 0; }
30 void audio_play_wave_resource(const WAVE_HEADER_COMPLETE* wave) {}
31 int audio_process(void) { return -1; }
32 #else
33 //RcB: SKIPON "AUDIO_BACKEND=AUDIO_BACKEND_NONE"
34 #include "../c-flod/backends/wave_format.h"
35 #include "../c-flod/neoart/flod/core/CorePlayer.h"
36 #include "../c-flod/neoart/flod/whittaker/DWPlayer.h"
37 #include "../c-flod/flashlib/ByteArray.h"
38 #include "../c-flod/neoart/flod/core/Amiga.h"
39 //RcB: SKIPOFF "AUDIO_BACKEND=AUDIO_BACKEND_NONE"
40 #include <assert.h>
41 #include <pthread.h>
42 #include <errno.h>
43 #include "../lib/include/timelib.h"
44 //RcB: LINK "-lpthread"
46 enum thread_status {
47 TS_WAITING,
48 TS_PLAYING,
49 TS_DONE,
50 TS_STOPPING,
53 struct AudioPlayer {
54 union {
55 struct CorePlayer core;
56 struct DWPlayer dw;
57 } player;
58 union {
59 struct CoreMixer core;
60 struct Amiga amiga;
61 } hardware;
62 union {
63 struct Backend backend;
64 struct BACKEND_STRUCT ao;
65 } writer;
66 struct ByteArray music_stream;
67 struct ByteArray wave_streams[2];
68 WAVE_HEADER_COMPLETE wavhdr;
69 struct ByteArray out_wave;
70 char wave_buffer[COREMIXER_MAX_BUFFER * 2 * sizeof(float)];
71 pthread_attr_t attr;
72 pthread_t thread;
73 pthread_mutex_t music_mutex;
74 pthread_mutex_t sound_mutex;
75 enum thread_status thread_music_status;
76 int free_waveslot;
77 int play_waveslot;
78 int empty_track_active;
81 static struct AudioPlayer playa;
83 #define mlock() pthread_mutex_lock(&playa.music_mutex)
84 #define munlock() pthread_mutex_unlock(&playa.music_mutex)
85 #define slock() pthread_mutex_lock(&playa.sound_mutex)
86 #define sunlock() pthread_mutex_unlock(&playa.sound_mutex)
88 static int handle_overflow(int *sample) {
89 if(*sample > 32767) {
90 *sample = 32767;
91 return 1;
92 } else if (*sample < -32768) {
93 *sample = -32768;
94 return -1;
96 return 0;
99 #define MUSIC_FINISHED() (!playa.empty_track_active && CoreMixer_get_complete(&playa.hardware.core))
100 #define GEN_MUSIC() do{ \
101 if(!playa.empty_track_active) playa.hardware.core.accurate(&playa.hardware.core); \
102 else { assert(sizeof(playa.wave_buffer) >= 1024*3); \
103 memset(playa.wave_buffer, 0, 1024*3); playa.out_wave.pos = 1024*3;} \
104 } while (0)
105 #define MUSIC_AVAIL() (playa.out_wave.pos)
106 #define GET_MUSIC_WORD() (playa.empty_track_active ? 0 : ByteArray_readShort(out))
107 #define MUSIC_REWIND_WORD() do { if(!playa.empty_track_active) ByteArray_set_position_rel(out, -2); } while (0)
109 static void *thread_func(void* data) {
110 (void) data;
111 while(1) {
112 mlock();
113 if(playa.thread_music_status == TS_STOPPING) {
114 playa.thread_music_status = TS_WAITING;
115 munlock();
116 msleep(1);
117 continue;
118 } else if(playa.thread_music_status == TS_DONE || playa.thread_music_status == TS_WAITING) {
119 munlock();
120 msleep(4);
121 continue;
123 munlock();
124 if(MUSIC_FINISHED()) {
125 mlock();
126 playa.thread_music_status = TS_DONE;
127 munlock();
128 continue;
130 GEN_MUSIC();
131 if(MUSIC_AVAIL()) {
132 //dprintf(2, "writing %zu bytes...\n", (size_t) playa.out_wave.pos);
133 slock();
134 if(playa.play_waveslot != -1) {
135 struct ByteArray* mine = &playa.wave_streams[playa.play_waveslot];
136 if(!mine->bytesAvailable(mine)) {
137 playa.play_waveslot = -1;
138 goto mixin_done;
140 struct ByteArray* out = &playa.out_wave;
141 off_t savepos = out->pos;
142 size_t avail = mine->bytesAvailable(mine);
143 size_t upsample_factor = 44100 / playa.wavhdr.wave_hdr.samplerate;
144 size_t processed_m = 0, processed_w = 0;
145 size_t readbytes = playa.wavhdr.wave_hdr.bitwidth == 8 ? 1 : 2;
146 int chan[2] = { 0, 0 };
147 int next[2];
148 ByteArray_set_position(out, 0);
149 while(processed_m < (size_t)savepos && processed_w < avail) {
150 size_t c, u;
151 for(c = 0; c < 2; c++) {
152 if(c < playa.wavhdr.wave_hdr.channels) {
153 if(readbytes == 1) next[c] = ((uint8_t) ByteArray_readByte(mine) - 128) * 256;
154 else next[c] = ByteArray_readShort(mine);
155 handle_overflow(&next[c]);
156 } else
157 next[c] = next[c - 1];
158 processed_w += readbytes;
160 for(u = 0; u < upsample_factor; u++) {
161 for(c = 0; c < 2; c++) {
162 int interpolated = u == 0 ? chan[c] :
163 chan[c] + ((next[c]-chan[c]) * ((float)u/(float)upsample_factor));
164 interpolated = (float) interpolated * 0.3; // decrease volume to avoid overflow
165 int music = GET_MUSIC_WORD();
166 int sample = music + interpolated;
167 if(handle_overflow(&sample)) dprintf(2, "overflow\n");
168 MUSIC_REWIND_WORD();
169 ByteArray_writeShort(out, sample);
170 processed_m += 2;
173 for (c=0; c<2; c++) chan[c] = next[c];
175 ByteArray_set_position(out, savepos);
177 mixin_done:
178 sunlock();
179 BACKEND_WRITE(&playa.writer.ao, playa.wave_buffer, playa.out_wave.pos);
180 //dprintf(2, "done\n");
181 playa.out_wave.pos = 0;
184 return 0;
187 void audio_init(void) {
188 Amiga_ctor(&playa.hardware.amiga);
189 DWPlayer_ctor(&playa.player.dw, &playa.hardware.amiga);
190 BACKEND_INIT(&playa.writer.ao, 0);
191 ByteArray_ctor(&playa.music_stream);
192 ByteArray_ctor(&playa.wave_streams[0]);
193 ByteArray_ctor(&playa.wave_streams[1]);
194 ByteArray_ctor(&playa.out_wave);
195 playa.out_wave.endian = BAE_LITTLE;
196 ByteArray_open_mem(&playa.out_wave, playa.wave_buffer, sizeof(playa.wave_buffer));
197 playa.hardware.core.wave = &playa.out_wave;
199 playa.thread_music_status = TS_WAITING;
200 errno = pthread_mutex_init(&playa.music_mutex, 0);
201 if(errno) perror("1");
202 errno = pthread_mutex_init(&playa.sound_mutex, 0);
203 if(errno) perror("1.5");
204 errno = pthread_attr_init(&playa.attr);
205 if(errno) perror("2");
206 errno = pthread_attr_setstacksize(&playa.attr, 128*1024);
207 if(errno) perror("3");
208 errno = pthread_create(&playa.thread, &playa.attr, thread_func, 0);
209 if(errno) perror("4");
210 playa.free_waveslot = 0;
211 playa.play_waveslot = -1;
214 int audio_open_music_resource(const unsigned char* data, size_t data_size, int track) {
215 mlock();
216 if(playa.thread_music_status != TS_WAITING) {
217 playa.thread_music_status = TS_STOPPING;
218 munlock();
219 int done = 0;
220 do {
221 mlock();
222 if(playa.thread_music_status == TS_WAITING) done = 1;
223 munlock();
224 if(!done) msleep(1);
225 } while(!done);
226 mlock();
228 playa.empty_track_active = 0;
229 munlock();
230 if(track == -1) { /* "empty" track */
231 playa.empty_track_active = 1;
232 memset(playa.wave_buffer, 0, sizeof(playa.wave_buffer));
233 return 0;
235 ByteArray_open_mem(&playa.music_stream, (void*) data, data_size);
236 CorePlayer_load(&playa.player.core, &playa.music_stream);
237 assert(playa.player.core.version);
238 if(track > playa.player.core.lastSong) return -1;
239 playa.player.core.playSong = track;
240 playa.player.core.initialize(&playa.player.core);
241 return 0;
244 #if 0
245 //FIXME: does not close file handle
246 int audio_open_music(const char* filename, int track) {
247 mlock();
248 if(playa.thread_music_status != TS_WAITING) {
249 playa.thread_music_status = TS_STOPPING;
250 munlock();
251 int done = 0;
252 do {
253 mlock();
254 if(playa.thread_music_status == TS_WAITING) done = 1;
255 munlock();
256 if(!done) msleep(1);
257 } while(!done);
258 mlock();
260 munlock();
261 ByteArray_open_file(&playa.music_stream, filename);
262 CorePlayer_load(&playa.player.core, &playa.music_stream);
263 assert(playa.player.core.version);
264 playa.player.core.initialize(&playa.player.core);
265 if(track > playa.player.core.lastSong) return -1;
266 playa.player.core.playSong = track;
267 return 0;
269 #endif
272 void audio_play_wave_resource(const WAVE_HEADER_COMPLETE* wave) {
273 if(!wave) return;
274 slock();
275 if(playa.free_waveslot >= (int) ARRAY_SIZE(playa.wave_streams)) {
276 playa.free_waveslot = 0;
278 struct ByteArray *mine = &playa.wave_streams[playa.free_waveslot];
279 if(!ByteArray_open_mem(mine, waveheader_get_data(wave), waveheader_get_datasize(wave))) {
280 abort();
282 ByteArray_set_endian(mine, BAE_LITTLE);
283 playa.wavhdr = *wave;
285 playa.play_waveslot = playa.free_waveslot;
286 playa.free_waveslot++;
287 sunlock();
290 #if 0
291 static void close_all_but_playing_slot() {
292 size_t i;
293 for(i = 0; i < ARRAY_SIZE(playa.wave_streams); i++) {
294 /*if(i != playa.play_waveslot) */
295 ByteArray_close_file(&playa.wave_streams[i]);
298 void audio_play_wav(const char* filename) {
299 slock();
300 if(playa.free_waveslot >= (int) ARRAY_SIZE(playa.wave_streams)) {
301 playa.free_waveslot = 0;
302 close_all_but_playing_slot();
304 struct ByteArray *mine = &playa.wave_streams[playa.free_waveslot];
305 if(!ByteArray_open_file(mine, filename)) {
306 perror("open");
307 abort();
309 ByteArray_set_endian(mine, BAE_LITTLE);
310 /* assuming 16bit, 44khz stereo wav for the beginning. */
311 ByteArray_readMultiByte(mine, (void*) &playa.wavhdr, sizeof(WAVE_HEADER_COMPLETE));
312 //ByteArray_set_position(mine, sizeof(WAVE_HEADER_COMPLETE));
314 playa.play_waveslot = playa.free_waveslot;
315 playa.free_waveslot++;
316 sunlock();
318 #endif
320 // return -1: when track is finished, 0 if something was played, 1 if nothing was played.
321 int audio_process(void) {
322 mlock();
323 if(playa.thread_music_status == TS_DONE) {
324 munlock();
325 return -1;
326 } else if (playa.thread_music_status == TS_WAITING) {
327 playa.thread_music_status = TS_PLAYING;
329 munlock();
330 return 0;
333 #endif /* AUDIO_BACKEND != AUDIO_BACKEND_NONE */