Updating built in Io code to use += instead of x = x + y
[io/quag.git] / addons / PortAudio / source / new / IoAudioMixer.c
blob9696dce80d64957c2a7688a2830eac3394c3a9d5
1 /* copyright: Steve Dekorte, 2002
2 * All rights reserved. See _BSDLicense.txt.
3 */
5 #include "IoAudioMixer.h"
6 #include "IoAudioDevice.h"
7 #include "IoObject_actor.h"
8 #include "base/List.h"
9 #include "base/UArray.h"
10 #include "IoState.h"
11 #include "IoNumber.h"
12 #include "IoSeq.h"
13 #include "IoNil.h"
14 #include "IoSound.h"
15 #include "IoList.h"
16 #include <math.h>
18 #include "SoundTouch_wrapper.h"
20 /* 1/N of a second worth of frames/samples */
21 #define FRAMES_PER_BUFFER (44100/32)
23 /*#define DEBUG*/
25 AudioEvent *AudioEvent_new(void)
27 AudioEvent *self = calloc(1, sizeof(AudioEvent));
28 return self;
31 AudioEvent *AudioEvent_newWithSound_onSample_of_type_(IoSound *playSound, long sample, IoSound *ioTriggerSound, char etype)
33 AudioEvent *self = AudioEvent_new();
34 self->ioPlaySound = playSound;
35 self->sample = sample - 1;
36 self->etype = etype;
37 if (self->sample < 0) self->sample = 0;
38 self->ioTriggerSound = ioTriggerSound;
39 return self;
42 void AudioEvent_free(AudioEvent *self)
44 free(self);
47 void AudioEvent_mark(AudioEvent *self)
49 if (self->ioTriggerSound) IoObject_makeGrayIfWhite(self->ioTriggerSound);
50 if (self->ioPlaySound) IoObject_makeGrayIfWhite(self->ioPlaySound);
53 inline long AudioEvent_samplesUntilTrigger(AudioEvent *self)
55 if (!self->ioTriggerSound) return 0;
56 return self->sample - Sound_position(IoSound_rawSound(self->ioTriggerSound));
59 long AudioEvent_compare(AudioEvent **self, AudioEvent **event)
61 int diff = AudioEvent_samplesUntilTrigger(*event) - AudioEvent_samplesUntilTrigger(*self);
62 if (diff) return diff;
63 return ((*self)->etype) == AUDIOEVENT_ADD ? 0 : 1;
66 void AudioEvent_show(AudioEvent *self)
68 char *type = self->etype == AUDIOEVENT_ADD ? "add" : "remove";
70 printf("AudioEvent %p %s sound %p at sample %i\n",
71 (void *)self, type, self->ioPlaySound, (int)self->sample);
73 printf("event %s sound %p\n", type, self->ioPlaySound);
77 /* ---------------------------------------------------------- */
79 #define DATA(self) ((IoAudioMixerData *)self->data)
81 IoTag *IoAudioMixer_newTag(void *state)
83 IoTag *tag = IoTag_newWithName_("AudioMixer");
84 IoTag_state_(tag, state);
85 IoTag_cloneFunc_(tag, (IoTagCloneFunc *)IoAudioMixer_rawClone);
86 IoTag_freeFunc_(tag, (IoTagFreeFunc *)IoAudioMixer_free);
87 IoTag_markFunc_(tag, (IoTagMarkFunc *)IoAudioMixer_mark);
88 return tag;
91 IoAudioMixer *IoAudioMixer_proto(void *state)
93 IoObject *self = IoObject_new(state);
94 IoObject_tag_(self, IoAudioMixer_newTag(state));
96 self->data = calloc(1, sizeof(IoAudioMixerData));
97 DATA(self)->sounds = List_new();
98 DATA(self)->soundsToRemove = List_new();
99 DATA(self)->events = List_new();
100 DATA(self)->activeEvents = List_new();
101 DATA(self)->samplesPerBuffer = FRAMES_PER_BUFFER;
102 DATA(self)->volume = 1.0;
104 DATA(self)->ioBuffer = IoSeq_new(IOSTATE);
105 DATA(self)->buffer = IoSeq_rawUArray(DATA(self)->ioBuffer);
106 DATA(self)->mixBuffer = UArray_new();
107 DATA(self)->writeMessage =
108 IoMessage_newWithName_label_(IOSTATE,
109 IOSYMBOL("write"),
110 IOSYMBOL("AudioMixer"));
111 IoMessage_setCachedArg_to_(DATA(self)->writeMessage, 0, DATA(self)->ioBuffer);
113 DATA(self)->nonBlockingWriteMessage =
114 IoMessage_newWithName_label_(IOSTATE,
115 IOSYMBOL("nonBlockingWrite"),
116 IOSYMBOL("AudioMixer"));
117 IoMessage_setCachedArg_to_(DATA(self)->nonBlockingWriteMessage, 0, DATA(self)->ioBuffer);
119 DATA(self)->soundTouch = SoundTouch_init();
120 SoundTouch_setSampleRate(DATA(self)->soundTouch, 44100);
121 SoundTouch_setChannels(DATA(self)->soundTouch, 2);
122 DATA(self)->tempo = 1.0;
124 IoState_registerProtoWithFunc_(state, self, IoAudioMixer_proto);
127 IoMethodTable methodTable[] = {
128 {"start", IoAudioMixer_start},
129 {"stop", IoAudioMixer_stop},
130 {"sounds", IoAudioMixer_sounds},
131 {"addSound", IoAudioMixer_addSound_},
132 {"addSoundOnSampleOfSound",
133 IoAudioMixer_addSound_onSample_ofSound_},
134 {"removeSound", IoAudioMixer_removeSound_},
135 {"removeAllSounds", IoAudioMixer_removeAllSounds},
136 {"removeSoundOnSampleOfSound", IoAudioMixer_removeSound_onSample_ofSound_},
137 {"setSamplesPerBuffer", IoAudioMixer_setSamplesPerBuffer},
138 {"setVolume", IoAudioMixer_setVolume},
139 {"setTempo", IoAudioMixer_setTempo},
140 {"setSampleRate", IoAudioMixer_setSampleRate},
141 {"setPitchSemiTones", IoAudioMixer_setPitchSemiTones},
142 {"setAudioDevice", IoAudioMixer_setAudioDevice},
143 {NULL, NULL},
145 IoObject_addMethodTable_(self, methodTable);
148 DATA(self)->ioAudioDevice = IoObject_rawGetSlot_(self, IOSYMBOL("AudioDevice"));
150 IoMessage *m = 0x0;
151 IOASSERT(DATA(self)->ioAudioDevice, "unable to find AudioDevice");
153 return self;
156 IoAudioMixer *IoAudioMixer_rawClone(IoAudioMixer *proto)
158 IoObject *self = IoObject_rawClonePrimitive(proto);
159 self->data = cpalloc(proto->data, sizeof(IoAudioMixerData));
161 DATA(self)->ioBuffer = IoSeq_new(IOSTATE);
162 DATA(self)->buffer = IoSeq_rawUArray(DATA(self)->ioBuffer);
163 DATA(proto)->mixBuffer = UArray_new();
164 DATA(self)->writeMessage =
165 IoMessage_newWithName_label_(IOSTATE,
166 IOSYMBOL("write"),
167 IOSYMBOL("AudioMixer"));
168 IoMessage_setCachedArg_to_(DATA(self)->writeMessage, 0, DATA(self)->ioBuffer);
170 DATA(self)->nonBlockingWriteMessage =
171 IoMessage_newWithName_label_(IOSTATE,
172 IOSYMBOL("nonBlockingWrite"),
173 IOSYMBOL("AudioMixer"));
174 IoMessage_setCachedArg_to_(DATA(self)->nonBlockingWriteMessage, 0, DATA(self)->ioBuffer);
176 DATA(self)->sounds = List_new();
177 DATA(self)->soundsToRemove = List_new();
178 DATA(self)->events = List_new();
179 DATA(self)->activeEvents = List_new();
180 DATA(self)->volume = DATA(self)->volume;
182 DATA(self)->soundTouch = SoundTouch_init();
183 SoundTouch_setSampleRate(DATA(self)->soundTouch, 44100);
184 SoundTouch_setChannels(DATA(self)->soundTouch, 2);
185 DATA(self)->tempo = 1.0;
186 IoState_addValue_(IOSTATE, self);
187 return self;
190 IoAudioMixer *IoAudioMixer_new(void *state)
192 IoObject *proto = IoState_protoWithInitFunction_(state, IoAudioMixer_proto);
193 return IOCLONE(proto);
196 /* ----------------------------------------------------------- */
198 void IoAudioMixer_free(IoAudioMixer *self)
200 List_free(DATA(self)->sounds);
201 List_free(DATA(self)->soundsToRemove);
202 List_free(DATA(self)->events);
203 List_free(DATA(self)->activeEvents);
204 UArray_free(DATA(self)->mixBuffer);
205 SoundTouch_free(DATA(self)->soundTouch);
206 free(self->data);
209 void IoAudioMixer_mark(IoAudioMixer *self)
211 /* buffer is a UArray */
212 List_do_(DATA(self)->sounds, (ListDoCallback *)IoObject_makeGrayIfWhite);
213 List_do_(DATA(self)->events, (ListDoCallback *)AudioEvent_mark);
214 List_do_(DATA(self)->activeEvents, (ListDoCallback *)AudioEvent_mark);
215 List_do_(DATA(self)->soundsToRemove, (ListDoCallback *)IoObject_makeGrayIfWhite);
216 IoObject_makeGrayIfWhite(DATA(self)->ioAudioDevice);
217 IoObject_makeGrayIfWhite(DATA(self)->writeMessage);
218 IoObject_makeGrayIfWhite(DATA(self)->nonBlockingWriteMessage);
219 IoObject_makeGrayIfWhite(DATA(self)->ioBuffer);
222 /* ----------------------------------------------------------- */
224 void IoAudioMixer_updateBufferSize(IoAudioMixer *self)
226 int numBytes = DATA(self)->samplesPerBuffer * sizeof(float) * 2;
227 UArray_setSize_(DATA(self)->mixBuffer, numBytes * DATA(self)->tempo);
228 UArray_setSize_(DATA(self)->buffer, 10000*8);
231 IoObject *IoAudioMixer_setAudioDevice(IoAudioMixer *self, IoObject *locals, IoMessage *m)
233 DATA(self)->ioAudioDevice = IOREF(IoMessage_locals_valueArgAt_(m, locals, 0));
234 return self;
237 IoObject *IoAudioMixer_setSamplesPerBuffer(IoAudioMixer *self, IoObject *locals, IoMessage *m)
239 int s = IoMessage_locals_intArgAt_(m, locals, 0);
240 DATA(self)->samplesPerBuffer = s;
241 #ifdef DEBUG
242 printf("IoAudioMixer_setSamplesPerBuffer(%i)\n", s);
243 #endif
244 IoAudioMixer_updateBufferSize(self);
245 return self;
248 void IoAudioMixer_updateScale(IoAudioMixer *self)
250 DATA(self)->scale = DATA(self)->volume * (float)(0.9 / sqrt((double)List_size(DATA(self)->sounds)));
253 IoObject *IoAudioMixer_setVolume(IoAudioMixer *self, IoObject *locals, IoMessage *m)
255 DATA(self)->volume = IoMessage_locals_doubleArgAt_(m, locals, 0);
256 IoAudioMixer_updateScale(self);
257 return self;
260 IoObject *IoAudioMixer_sounds(IoAudioMixer *self, IoObject *locals, IoMessage *m)
262 IoList *ioList = IoList_new(IOSTATE);
263 List_copy_(IoList_rawList(ioList), DATA(self)->sounds);
264 return ioList;
267 void IoAudioMixer_showEvents(IoAudioMixer *self)
269 List *events = DATA(self)->events;
270 int i;
271 for (i=0; i<List_size(events); i++)
273 printf("%i: ", i);
274 AudioEvent_show(List_at_(events, i));
276 /*List_do_(events, (ListDoCallback *)AudioEvent_show);*/
277 printf("\n");
280 void IoAudioMixer_addEvent_(IoAudioMixer *self, AudioEvent *event)
282 List *events = DATA(self)->events;
283 List_append_(events, event);
284 List_qsort(events, (void *)AudioEvent_compare);
285 #ifdef DEBUG
286 IoAudioMixer_showEvents(self);
287 #endif
290 IoObject *IoAudioMixer_addSound_(IoAudioMixer *self, IoObject *locals, IoMessage *m)
292 IoSound *ioSound = IoMessage_locals_soundArgAt_(m, locals, 0);
293 AudioEvent *event = AudioEvent_newWithSound_onSample_of_type_(ioSound, 0, 0x0, AUDIOEVENT_ADD);
294 IoAudioMixer_addEvent_(self, event);
295 return self;
298 IoObject *IoAudioMixer_addSound_onSample_ofSound_(IoAudioMixer *self, IoObject *locals, IoMessage *m)
300 IoSound *ioSound = IoMessage_locals_soundArgAt_(m, locals, 0);
301 long sample = IoMessage_locals_longArgAt_(m, locals, 1);
302 IoSound *ioTriggerSound = IoMessage_locals_soundArgAt_(m, locals, 2);
303 AudioEvent *event = AudioEvent_newWithSound_onSample_of_type_(
304 ioSound, sample, ioTriggerSound, AUDIOEVENT_ADD);
305 IoAudioMixer_addEvent_(self, event);
306 return self;
309 void IoAudioMixer_justAddSound_(IoAudioMixer *self, IoSound *ioSound)
311 /*printf("add sound %p\n", (void *)ioSound);*/
312 List_append_(DATA(self)->sounds, ioSound);
313 Sound_setIsPlaying_(IoSound_rawSound(ioSound), 1);
316 void IoAudioMixer_justRemoveSound_(IoAudioMixer *self, IoSound *ioSound)
318 /*printf("remove sound %p\n", (void *)ioSound);*/
319 List_remove_(DATA(self)->sounds, ioSound);
320 Sound_setIsPlaying_(IoSound_rawSound(ioSound), 0);
323 IoObject *IoAudioMixer_removeSound_(IoAudioMixer *self, IoObject *locals, IoMessage *m)
325 IoSound *ioSound = IoMessage_locals_soundArgAt_(m, locals, 0);
326 /*List_append_(DATA(self)->soundsToRemove, ioSound);*/
327 AudioEvent *event = AudioEvent_newWithSound_onSample_of_type_(ioSound, 0, 0x0, AUDIOEVENT_REMOVE);
328 IoAudioMixer_addEvent_(self, event);
329 /*IoAudioMixer_justRemoveSound_(self, ioSound);*/
330 return self;
333 IoObject *IoAudioMixer_removeAllSounds(IoAudioMixer *self, IoObject *locals, IoMessage *m)
335 List *sounds = DATA(self)->sounds;
336 while (List_size(sounds))
338 IoAudioMixer_justRemoveSound_(self, List_at_(sounds, 0));
340 return self;
344 IoObject *IoAudioMixer_removeSound_onSample_ofSound_(IoAudioMixer *self, IoObject *locals, IoMessage *m)
346 IoSound *ioSound = IoMessage_locals_soundArgAt_(m, locals, 0);
347 long sample = IoMessage_locals_longArgAt_(m, locals, 1);
348 IoSound *ioTriggerSound = IoMessage_locals_soundArgAt_(m, locals, 2);
349 AudioEvent *event =
350 AudioEvent_newWithSound_onSample_of_type_(ioSound, sample, ioTriggerSound, AUDIOEVENT_REMOVE);
351 IoAudioMixer_addEvent_(self, event);
352 return self;
355 /* ----------------------------------------------------------- */
357 int IoAudioMixer_isActive(IoAudioMixer *self)
359 return List_size(DATA(self)->sounds) ||
360 List_size(DATA(self)->soundsToRemove) ||
361 List_size(DATA(self)->events) ||
362 List_size(DATA(self)->activeEvents);
365 IoObject *IoAudioMixer_start(IoAudioMixer *self, IoObject *locals, IoMessage *m)
367 IoAudioMixer_updateBufferSize(self);
369 DATA(self)->isRunning = 1;
370 while (DATA(self)->isRunning)
372 if (IoAudioMixer_isActive(self))
374 int outSamples = IoAudioMixer_mixOneChunk(self, locals, m);
375 if (outSamples < 5000)
377 #ifdef DEBUG
378 printf("< 5000 out - mix another chunk\n");
379 #endif
380 IoAudioMixer_mixOneChunk(self, locals, m);
383 //IoState_yield(IOSTATE);
385 return self;
388 IoObject *IoAudioMixer_stop(IoAudioMixer *self, IoObject *locals, IoMessage *m)
390 DATA(self)->isRunning = 0;
391 /*IoAudioDevice_clearBuffers(DATA(self)->ioAudioDevice);*/
392 return self;
395 void IoAudioMixer_processSoundRemovals(IoAudioMixer *self)
397 IoSound *s;
398 List *soundsToRemove = DATA(self)->soundsToRemove;
399 List *sounds = DATA(self)->sounds;
400 int removeCount = List_size(soundsToRemove);
402 if (removeCount)
404 printf("removeCount = %i\n", removeCount);
405 printf("soundsCount = %i\n", List_size(sounds));
408 while ( (s = List_pop(soundsToRemove)) )
409 { IoAudioMixer_justRemoveSound_(self, s); }
411 if (removeCount)
413 printf("remaining sounds = %i\n", List_size(sounds));
414 printf("remaining removeCount = %i\n\n", List_size(soundsToRemove));
417 IoAudioMixer_updateScale(self);
420 void IoAudioMixer_processActiveEvents(IoAudioMixer *self)
422 AudioEvent *e;
423 while ( (e = List_pop(DATA(self)->activeEvents)) )
425 #ifdef DEBUG
426 printf("processing: ");
427 AudioEvent_show(e);
428 #endif
429 switch (e->etype)
431 case AUDIOEVENT_ADD:
432 IoAudioMixer_justAddSound_(self, e->ioPlaySound);
433 break;
434 case AUDIOEVENT_REMOVE:
435 IoAudioMixer_justRemoveSound_(self, e->ioPlaySound);
436 break;
439 IoAudioMixer_updateScale(self);
442 int IoAudioMixer_mixOneChunk(IoAudioMixer *self, IoObject *locals, IoMessage *m)
444 UArray *mixBuffer = DATA(self)->mixBuffer;
445 List *sounds = DATA(self)->sounds;
446 List *soundsToRemove = DATA(self)->soundsToRemove;
447 AudioEvent *e = List_top(DATA(self)->events);
448 List *activeEvents = DATA(self)->activeEvents;
449 int frame;
450 /*int samplesPerBuffer = DATA(self)->samplesPerBuffer;*/
451 int samplesPerBuffer = UArray_size(DATA(self)->mixBuffer) / (sizeof(float) * 2);
453 UArray_setAllBytesTo_(mixBuffer, 0);
455 while ( e && (!e->ioTriggerSound))
457 List_append_(DATA(self)->activeEvents, List_pop(DATA(self)->events));
458 e = List_top(DATA(self)->events);
461 if (List_size(activeEvents)) IoAudioMixer_processActiveEvents(self);
462 if (List_size(soundsToRemove)) IoAudioMixer_processSoundRemovals(self);
464 for (frame = 0; frame < samplesPerBuffer; frame ++)
466 int index = frame * 2;
467 int i;
468 float *ol = UArray_floatPointerAt_(mixBuffer, index);
469 float *or = UArray_floatPointerAt_(mixBuffer, index+1);
471 for (i = 0; i < List_size(sounds); i++)
473 IoSound *ioSound = List_at_(sounds, i);
474 Sound *sound = IoSound_rawSound(ioSound);
475 float left, right;
476 char done = Sound_nextFloat(sound, &left, &right);
478 if (done && !Sound_isLooping(sound))
480 List_append_(soundsToRemove, ioSound);
481 continue;
484 (*ol) += left;
485 (*or) += right;
487 while (e &&
488 ((!e->ioTriggerSound) ||
489 ((e->ioTriggerSound == ioSound) &&
490 (e->sample == Sound_position(sound))))
493 List_append_(DATA(self)->activeEvents, List_pop(DATA(self)->events));
494 e = List_top(DATA(self)->events);
498 (*ol) *= DATA(self)->scale;
499 (*or) *= DATA(self)->scale;
501 if (List_size(activeEvents)) IoAudioMixer_processActiveEvents(self);
502 if (List_size(soundsToRemove)) IoAudioMixer_processSoundRemovals(self);
505 /* adjust pitch and tempo */
507 //double t1 = ((double)clock())/((double)CLOCKS_PER_SEC);
508 //double t2;
509 int receivedSamples = 1;
511 SoundTouch_putSamples(DATA(self)->soundTouch, (float *)UArray_bytes(mixBuffer), samplesPerBuffer);
512 //printf("put %i\n", samplesPerBuffer);
513 //while (receivedSamples)
515 UArray_setSize_(DATA(self)->buffer, 10000*8);
516 receivedSamples = SoundTouch_receiveSamples(DATA(self)->soundTouch,
517 (float *)UArray_bytes(DATA(self)->buffer),
518 UArray_size(DATA(self)->buffer) / (sizeof(float) * 2));
519 UArray_setSize_(DATA(self)->buffer, receivedSamples * (sizeof(float) * 2));
521 //printf("received %i\n", receivedSamples);
522 if (receivedSamples)
524 if (receivedSamples < 5000)
526 #ifdef DEBUG
527 printf("non-blocking write\n");
528 #endif
530 IoMessage_locals_performOn_(DATA(self)->nonBlockingWriteMessage,
531 self, DATA(self)->ioAudioDevice);
533 else
535 IoMessage_locals_performOn_(DATA(self)->writeMessage,
536 self, DATA(self)->ioAudioDevice);
539 #ifdef DEBUG
540 t2 = ((double)clock())/((double)CLOCKS_PER_SEC);
541 printf("tempo: %1.1f %i -> %i in %0.2f sec\n",
542 DATA(self)->tempo,
543 samplesPerBuffer,
544 receivedSamples,
545 (float)(t2 - t1));
546 #endif
549 //printf("\n");
550 return receivedSamples;
552 /* need to change this to be dynamic, so we can easily record the output */
553 /*IoAudioDevice_justWrite(DATA(self)->ioAudioDevice, locals, m, buffer);*/
554 return UArray_size(DATA(self)->buffer) / 8;
557 IoObject *IoAudioMixer_setTempo(IoAudioMixer *self, IoObject *locals, IoMessage *m)
559 DATA(self)->tempo = IoMessage_locals_floatArgAt_(m, locals, 0);
560 SoundTouch_setTempo(DATA(self)->soundTouch, DATA(self)->tempo);
561 printf("IoAudioMixer_setTempo(%f)\n", DATA(self)->tempo);
562 IoAudioMixer_updateBufferSize(self);
563 return self;
566 IoObject *IoAudioMixer_setSampleRate(IoAudioMixer *self, IoObject *locals, IoMessage *m)
568 DATA(self)->sampleRate = IoMessage_locals_intArgAt_(m, locals, 0);
569 SoundTouch_setSampleRate(DATA(self)->soundTouch, (unsigned int)DATA(self)->sampleRate);
570 printf("IoAudioMixer_setSampleRate(%i)\n", DATA(self)->sampleRate);
571 IoAudioMixer_updateBufferSize(self);
572 return self;
575 IoObject *IoAudioMixer_setPitchSemiTones(IoAudioMixer *self, IoObject *locals, IoMessage *m)
577 DATA(self)->pitch = IoMessage_locals_floatArgAt_(m, locals, 0);
578 SoundTouch_setPitchSemiTones(DATA(self)->soundTouch, DATA(self)->pitch);
579 printf("IoAudioMixer_setPitchSemiTones(%f)\n", DATA(self)->pitch);
580 IoAudioMixer_updateBufferSize(self);
581 return self;