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