make mission end screen more accurate
[rofl0r-openDOW.git] / audio.c
blobc62650bef91e275607cfa9be145c70f5e47b1cbb
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;
71 static struct AudioPlayer playa;
73 #define mlock() pthread_mutex_lock(&playa.music_mutex)
74 #define munlock() pthread_mutex_unlock(&playa.music_mutex)
75 #define slock() pthread_mutex_lock(&playa.sound_mutex)
76 #define sunlock() pthread_mutex_unlock(&playa.sound_mutex)
78 static int handle_overflow(int *sample) {
79 if(*sample > 32767) {
80 *sample = 32767;
81 return 1;
82 } else if (*sample < -32768) {
83 *sample = -32768;
84 return -1;
86 return 0;
89 static void *thread_func(void* data) {
90 (void) data;
91 while(1) {
92 mlock();
93 if(playa.thread_music_status == TS_STOPPING) {
94 playa.thread_music_status = TS_WAITING;
95 munlock();
96 msleep(1);
97 continue;
98 } else if(playa.thread_music_status == TS_DONE || playa.thread_music_status == TS_WAITING) {
99 munlock();
100 msleep(4);
101 continue;
103 munlock();
104 if(CoreMixer_get_complete(&playa.hardware.core)) {
105 mlock();
106 playa.thread_music_status = TS_DONE;
107 munlock();
108 continue;
110 playa.hardware.core.accurate(&playa.hardware.core);
111 if(playa.out_wave.pos) {
112 //dprintf(2, "writing %zu bytes...\n", (size_t) playa.out_wave.pos);
113 slock();
114 if(playa.play_waveslot != -1) {
115 struct ByteArray* mine = &playa.wave_streams[playa.play_waveslot];
116 if(!mine->bytesAvailable(mine)) {
117 playa.play_waveslot = -1;
118 goto mixin_done;
120 struct ByteArray* out = &playa.out_wave;
121 off_t savepos = out->pos;
122 size_t avail = mine->bytesAvailable(mine);
123 size_t upsample_factor = 44100 / playa.wavhdr.wave_hdr.samplerate;
124 size_t processed_m = 0, processed_w = 0;
125 size_t readbytes = playa.wavhdr.wave_hdr.bitwidth == 8 ? 1 : 2;
126 int chan[2] = { 0, 0 };
127 int next[2];
128 ByteArray_set_position(out, 0);
129 while(processed_m < (size_t)savepos && processed_w < avail) {
130 size_t c, u;
131 for(c = 0; c < 2; c++) {
132 if(c < playa.wavhdr.wave_hdr.channels) {
133 if(readbytes == 1) next[c] = ((uint8_t) ByteArray_readByte(mine) - 128) * 256;
134 else next[c] = ByteArray_readShort(mine);
135 handle_overflow(&next[c]);
136 } else
137 next[c] = next[c - 1];
138 processed_w += readbytes;
140 for(u = 0; u < upsample_factor; u++) {
141 for(c = 0; c < 2; c++) {
142 int interpolated = u == 0 ? chan[c] :
143 chan[c] + ((next[c]-chan[c]) * ((float)u/(float)upsample_factor));
144 interpolated = (float) interpolated * 0.3; // decrease volume to avoid overflow
145 int music = ByteArray_readShort(out);
146 int sample = music + interpolated;
147 if(handle_overflow(&sample)) dprintf(2, "overflow\n");
148 ByteArray_set_position_rel(out, -2);
149 ByteArray_writeShort(out, sample);
150 processed_m += 2;
153 for (c=0; c<2; c++) chan[c] = next[c];
155 ByteArray_set_position(out, savepos);
157 mixin_done:
158 sunlock();
159 BACKEND_WRITE(&playa.writer.ao, playa.wave_buffer, playa.out_wave.pos);
160 //dprintf(2, "done\n");
161 playa.out_wave.pos = 0;
164 return 0;
167 void audio_init(void) {
168 Amiga_ctor(&playa.hardware.amiga);
169 DWPlayer_ctor(&playa.player.dw, &playa.hardware.amiga);
170 BACKEND_INIT(&playa.writer.ao, 0);
171 ByteArray_ctor(&playa.music_stream);
172 ByteArray_ctor(&playa.wave_streams[0]);
173 ByteArray_ctor(&playa.wave_streams[1]);
174 ByteArray_ctor(&playa.out_wave);
175 playa.out_wave.endian = BAE_LITTLE;
176 ByteArray_open_mem(&playa.out_wave, playa.wave_buffer, sizeof(playa.wave_buffer));
177 playa.hardware.core.wave = &playa.out_wave;
179 playa.thread_music_status = TS_WAITING;
180 errno = pthread_mutex_init(&playa.music_mutex, 0);
181 if(errno) perror("1");
182 errno = pthread_mutex_init(&playa.sound_mutex, 0);
183 if(errno) perror("1.5");
184 errno = pthread_attr_init(&playa.attr);
185 if(errno) perror("2");
186 errno = pthread_attr_setstacksize(&playa.attr, 128*1024);
187 if(errno) perror("3");
188 errno = pthread_create(&playa.thread, &playa.attr, thread_func, 0);
189 if(errno) perror("4");
190 playa.free_waveslot = 0;
191 playa.play_waveslot = -1;
194 int audio_open_music_resource(const unsigned char* data, size_t data_size, int track) {
195 mlock();
196 if(playa.thread_music_status != TS_WAITING) {
197 playa.thread_music_status = TS_STOPPING;
198 munlock();
199 int done = 0;
200 do {
201 mlock();
202 if(playa.thread_music_status == TS_WAITING) done = 1;
203 munlock();
204 if(!done) msleep(1);
205 } while(!done);
206 mlock();
208 munlock();
209 ByteArray_open_mem(&playa.music_stream, (void*) data, data_size);
210 CorePlayer_load(&playa.player.core, &playa.music_stream);
211 assert(playa.player.core.version);
212 if(track > playa.player.core.lastSong) return -1;
213 playa.player.core.playSong = track;
214 playa.player.core.initialize(&playa.player.core);
215 return 0;
218 #if 0
219 //FIXME: does not close file handle
220 int audio_open_music(const char* filename, int track) {
221 mlock();
222 if(playa.thread_music_status != TS_WAITING) {
223 playa.thread_music_status = TS_STOPPING;
224 munlock();
225 int done = 0;
226 do {
227 mlock();
228 if(playa.thread_music_status == TS_WAITING) done = 1;
229 munlock();
230 if(!done) msleep(1);
231 } while(!done);
232 mlock();
234 munlock();
235 ByteArray_open_file(&playa.music_stream, filename);
236 CorePlayer_load(&playa.player.core, &playa.music_stream);
237 assert(playa.player.core.version);
238 playa.player.core.initialize(&playa.player.core);
239 if(track > playa.player.core.lastSong) return -1;
240 playa.player.core.playSong = track;
241 return 0;
243 #endif
246 void audio_play_wave_resource(const WAVE_HEADER_COMPLETE* wave) {
247 if(!wave) return;
248 slock();
249 if(playa.free_waveslot >= (int) ARRAY_SIZE(playa.wave_streams)) {
250 playa.free_waveslot = 0;
252 struct ByteArray *mine = &playa.wave_streams[playa.free_waveslot];
253 if(!ByteArray_open_mem(mine, waveheader_get_data(wave), waveheader_get_datasize(wave))) {
254 abort();
256 ByteArray_set_endian(mine, BAE_LITTLE);
257 playa.wavhdr = *wave;
259 playa.play_waveslot = playa.free_waveslot;
260 playa.free_waveslot++;
261 sunlock();
264 #if 0
265 static void close_all_but_playing_slot() {
266 size_t i;
267 for(i = 0; i < ARRAY_SIZE(playa.wave_streams); i++) {
268 /*if(i != playa.play_waveslot) */
269 ByteArray_close_file(&playa.wave_streams[i]);
272 void audio_play_wav(const char* filename) {
273 slock();
274 if(playa.free_waveslot >= (int) ARRAY_SIZE(playa.wave_streams)) {
275 playa.free_waveslot = 0;
276 close_all_but_playing_slot();
278 struct ByteArray *mine = &playa.wave_streams[playa.free_waveslot];
279 if(!ByteArray_open_file(mine, filename)) {
280 perror("open");
281 abort();
283 ByteArray_set_endian(mine, BAE_LITTLE);
284 /* assuming 16bit, 44khz stereo wav for the beginning. */
285 ByteArray_readMultiByte(mine, (void*) &playa.wavhdr, sizeof(WAVE_HEADER_COMPLETE));
286 //ByteArray_set_position(mine, sizeof(WAVE_HEADER_COMPLETE));
288 playa.play_waveslot = playa.free_waveslot;
289 playa.free_waveslot++;
290 sunlock();
292 #endif
294 // return -1: when track is finished, 0 if something was played, 1 if nothing was played.
295 int audio_process(void) {
296 mlock();
297 if(playa.thread_music_status == TS_DONE) {
298 munlock();
299 return -1;
300 } else if (playa.thread_music_status == TS_WAITING) {
301 playa.thread_music_status = TS_PLAYING;
303 munlock();
304 return 0;