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