Addons updated to new doc format
[io.git] / addons / PortAudio / source / new / IoAudioMixer.c
blob98bc319b173aa850d459ffa56a5ac5cdd90d9149
1 //metadoc AudioMixer copyright Steve Dekorte, 2002
2 //metadoc AudioMixer license All rights reserved. See _BSDLicense.txt.
4 #include "IoAudioMixer.h"
5 #include "IoAudioDevice.h"
6 #include "IoObject_actor.h"
7 #include "base/List.h"
8 #include "base/UArray.h"
9 #include "IoState.h"
10 #include "IoNumber.h"
11 #include "IoSeq.h"
12 #include "IoNil.h"
13 #include "IoSound.h"
14 #include "IoList.h"
15 #include <math.h>
17 #include "SoundTouch_wrapper.h"
19 /* 1/N of a second worth of frames/samples */
20 #define FRAMES_PER_BUFFER (44100/32)
22 /*#define DEBUG*/
24 AudioEvent *AudioEvent_new(void)
26 AudioEvent *self = calloc(1, sizeof(AudioEvent));
27 return self;
30 AudioEvent *AudioEvent_newWithSound_onSample_of_type_(IoSound *playSound, long sample, IoSound *ioTriggerSound, char etype)
32 AudioEvent *self = AudioEvent_new();
33 self->ioPlaySound = playSound;
34 self->sample = sample - 1;
35 self->etype = etype;
36 if (self->sample < 0) self->sample = 0;
37 self->ioTriggerSound = ioTriggerSound;
38 return self;
41 void AudioEvent_free(AudioEvent *self)
43 free(self);
46 void AudioEvent_mark(AudioEvent *self)
48 if (self->ioTriggerSound) IoObject_makeGrayIfWhite(self->ioTriggerSound);
49 if (self->ioPlaySound) IoObject_makeGrayIfWhite(self->ioPlaySound);
52 inline long AudioEvent_samplesUntilTrigger(AudioEvent *self)
54 if (!self->ioTriggerSound) return 0;
55 return self->sample - Sound_position(IoSound_rawSound(self->ioTriggerSound));
58 long AudioEvent_compare(AudioEvent **self, AudioEvent **event)
60 int diff = AudioEvent_samplesUntilTrigger(*event) - AudioEvent_samplesUntilTrigger(*self);
61 if (diff) return diff;
62 return ((*self)->etype) == AUDIOEVENT_ADD ? 0 : 1;
65 void AudioEvent_show(AudioEvent *self)
67 char *type = self->etype == AUDIOEVENT_ADD ? "add" : "remove";
69 printf("AudioEvent %p %s sound %p at sample %i\n",
70 (void *)self, type, self->ioPlaySound, (int)self->sample);
72 printf("event %s sound %p\n", type, self->ioPlaySound);
76 /* ---------------------------------------------------------- */
78 #define DATA(self) ((IoAudioMixerData *)self->data)
80 IoTag *IoAudioMixer_newTag(void *state)
82 IoTag *tag = IoTag_newWithName_("AudioMixer");
83 IoTag_state_(tag, state);
84 IoTag_cloneFunc_(tag, (IoTagCloneFunc *)IoAudioMixer_rawClone);
85 IoTag_freeFunc_(tag, (IoTagFreeFunc *)IoAudioMixer_free);
86 IoTag_markFunc_(tag, (IoTagMarkFunc *)IoAudioMixer_mark);
87 return tag;
90 IoAudioMixer *IoAudioMixer_proto(void *state)
92 IoObject *self = IoObject_new(state);
93 IoObject_tag_(self, IoAudioMixer_newTag(state));
95 self->data = calloc(1, sizeof(IoAudioMixerData));
96 DATA(self)->sounds = List_new();
97 DATA(self)->soundsToRemove = List_new();
98 DATA(self)->events = List_new();
99 DATA(self)->activeEvents = List_new();
100 DATA(self)->samplesPerBuffer = FRAMES_PER_BUFFER;
101 DATA(self)->volume = 1.0;
103 DATA(self)->ioBuffer = IoSeq_new(IOSTATE);
104 DATA(self)->buffer = IoSeq_rawUArray(DATA(self)->ioBuffer);
105 DATA(self)->mixBuffer = UArray_new();
106 DATA(self)->writeMessage =
107 IoMessage_newWithName_label_(IOSTATE,
108 IOSYMBOL("write"),
109 IOSYMBOL("AudioMixer"));
110 IoMessage_setCachedArg_to_(DATA(self)->writeMessage, 0, DATA(self)->ioBuffer);
112 DATA(self)->nonBlockingWriteMessage =
113 IoMessage_newWithName_label_(IOSTATE,
114 IOSYMBOL("nonBlockingWrite"),
115 IOSYMBOL("AudioMixer"));
116 IoMessage_setCachedArg_to_(DATA(self)->nonBlockingWriteMessage, 0, DATA(self)->ioBuffer);
118 DATA(self)->soundTouch = SoundTouch_init();
119 SoundTouch_setSampleRate(DATA(self)->soundTouch, 44100);
120 SoundTouch_setChannels(DATA(self)->soundTouch, 2);
121 DATA(self)->tempo = 1.0;
123 IoState_registerProtoWithFunc_(state, self, IoAudioMixer_proto);
126 IoMethodTable methodTable[] = {
127 {"start", IoAudioMixer_start},
128 {"stop", IoAudioMixer_stop},
129 {"sounds", IoAudioMixer_sounds},
130 {"addSound", IoAudioMixer_addSound_},
131 {"addSoundOnSampleOfSound",
132 IoAudioMixer_addSound_onSample_ofSound_},
133 {"removeSound", IoAudioMixer_removeSound_},
134 {"removeAllSounds", IoAudioMixer_removeAllSounds},
135 {"removeSoundOnSampleOfSound", IoAudioMixer_removeSound_onSample_ofSound_},
136 {"setSamplesPerBuffer", IoAudioMixer_setSamplesPerBuffer},
137 {"setVolume", IoAudioMixer_setVolume},
138 {"setTempo", IoAudioMixer_setTempo},
139 {"setSampleRate", IoAudioMixer_setSampleRate},
140 {"setPitchSemiTones", IoAudioMixer_setPitchSemiTones},
141 {"setAudioDevice", IoAudioMixer_setAudioDevice},
142 {NULL, NULL},
144 IoObject_addMethodTable_(self, methodTable);
147 DATA(self)->ioAudioDevice = IoObject_rawGetSlot_(self, IOSYMBOL("AudioDevice"));
149 IoMessage *m = 0x0;
150 IOASSERT(DATA(self)->ioAudioDevice, "unable to find AudioDevice");
152 return self;
155 IoAudioMixer *IoAudioMixer_rawClone(IoAudioMixer *proto)
157 IoObject *self = IoObject_rawClonePrimitive(proto);
158 self->data = cpalloc(proto->data, sizeof(IoAudioMixerData));
160 DATA(self)->ioBuffer = IoSeq_new(IOSTATE);
161 DATA(self)->buffer = IoSeq_rawUArray(DATA(self)->ioBuffer);
162 DATA(proto)->mixBuffer = UArray_new();
163 DATA(self)->writeMessage =
164 IoMessage_newWithName_label_(IOSTATE,
165 IOSYMBOL("write"),
166 IOSYMBOL("AudioMixer"));
167 IoMessage_setCachedArg_to_(DATA(self)->writeMessage, 0, DATA(self)->ioBuffer);
169 DATA(self)->nonBlockingWriteMessage =
170 IoMessage_newWithName_label_(IOSTATE,
171 IOSYMBOL("nonBlockingWrite"),
172 IOSYMBOL("AudioMixer"));
173 IoMessage_setCachedArg_to_(DATA(self)->nonBlockingWriteMessage, 0, DATA(self)->ioBuffer);
175 DATA(self)->sounds = List_new();
176 DATA(self)->soundsToRemove = List_new();
177 DATA(self)->events = List_new();
178 DATA(self)->activeEvents = List_new();
179 DATA(self)->volume = DATA(self)->volume;
181 DATA(self)->soundTouch = SoundTouch_init();
182 SoundTouch_setSampleRate(DATA(self)->soundTouch, 44100);
183 SoundTouch_setChannels(DATA(self)->soundTouch, 2);
184 DATA(self)->tempo = 1.0;
185 IoState_addValue_(IOSTATE, self);
186 return self;
189 IoAudioMixer *IoAudioMixer_new(void *state)
191 IoObject *proto = IoState_protoWithInitFunction_(state, IoAudioMixer_proto);
192 return IOCLONE(proto);
195 /* ----------------------------------------------------------- */
197 void IoAudioMixer_free(IoAudioMixer *self)
199 List_free(DATA(self)->sounds);
200 List_free(DATA(self)->soundsToRemove);
201 List_free(DATA(self)->events);
202 List_free(DATA(self)->activeEvents);
203 UArray_free(DATA(self)->mixBuffer);
204 SoundTouch_free(DATA(self)->soundTouch);
205 free(self->data);
208 void IoAudioMixer_mark(IoAudioMixer *self)
210 /* buffer is a UArray */
211 List_do_(DATA(self)->sounds, (ListDoCallback *)IoObject_makeGrayIfWhite);
212 List_do_(DATA(self)->events, (ListDoCallback *)AudioEvent_mark);
213 List_do_(DATA(self)->activeEvents, (ListDoCallback *)AudioEvent_mark);
214 List_do_(DATA(self)->soundsToRemove, (ListDoCallback *)IoObject_makeGrayIfWhite);
215 IoObject_makeGrayIfWhite(DATA(self)->ioAudioDevice);
216 IoObject_makeGrayIfWhite(DATA(self)->writeMessage);
217 IoObject_makeGrayIfWhite(DATA(self)->nonBlockingWriteMessage);
218 IoObject_makeGrayIfWhite(DATA(self)->ioBuffer);
221 /* ----------------------------------------------------------- */
223 void IoAudioMixer_updateBufferSize(IoAudioMixer *self)
225 int numBytes = DATA(self)->samplesPerBuffer * sizeof(float) * 2;
226 UArray_setSize_(DATA(self)->mixBuffer, numBytes * DATA(self)->tempo);
227 UArray_setSize_(DATA(self)->buffer, 10000*8);
230 IoObject *IoAudioMixer_setAudioDevice(IoAudioMixer *self, IoObject *locals, IoMessage *m)
232 DATA(self)->ioAudioDevice = IOREF(IoMessage_locals_valueArgAt_(m, locals, 0));
233 return self;
236 IoObject *IoAudioMixer_setSamplesPerBuffer(IoAudioMixer *self, IoObject *locals, IoMessage *m)
238 int s = IoMessage_locals_intArgAt_(m, locals, 0);
239 DATA(self)->samplesPerBuffer = s;
240 #ifdef DEBUG
241 printf("IoAudioMixer_setSamplesPerBuffer(%i)\n", s);
242 #endif
243 IoAudioMixer_updateBufferSize(self);
244 return self;
247 void IoAudioMixer_updateScale(IoAudioMixer *self)
249 DATA(self)->scale = DATA(self)->volume * (float)(0.9 / sqrt((double)List_size(DATA(self)->sounds)));
252 IoObject *IoAudioMixer_setVolume(IoAudioMixer *self, IoObject *locals, IoMessage *m)
254 DATA(self)->volume = IoMessage_locals_doubleArgAt_(m, locals, 0);
255 IoAudioMixer_updateScale(self);
256 return self;
259 IoObject *IoAudioMixer_sounds(IoAudioMixer *self, IoObject *locals, IoMessage *m)
261 IoList *ioList = IoList_new(IOSTATE);
262 List_copy_(IoList_rawList(ioList), DATA(self)->sounds);
263 return ioList;
266 void IoAudioMixer_showEvents(IoAudioMixer *self)
268 List *events = DATA(self)->events;
269 int i;
270 for (i=0; i<List_size(events); i++)
272 printf("%i: ", i);
273 AudioEvent_show(List_at_(events, i));
275 /*List_do_(events, (ListDoCallback *)AudioEvent_show);*/
276 printf("\n");
279 void IoAudioMixer_addEvent_(IoAudioMixer *self, AudioEvent *event)
281 List *events = DATA(self)->events;
282 List_append_(events, event);
283 List_qsort(events, (void *)AudioEvent_compare);
284 #ifdef DEBUG
285 IoAudioMixer_showEvents(self);
286 #endif
289 IoObject *IoAudioMixer_addSound_(IoAudioMixer *self, IoObject *locals, IoMessage *m)
291 IoSound *ioSound = IoMessage_locals_soundArgAt_(m, locals, 0);
292 AudioEvent *event = AudioEvent_newWithSound_onSample_of_type_(ioSound, 0, 0x0, AUDIOEVENT_ADD);
293 IoAudioMixer_addEvent_(self, event);
294 return self;
297 IoObject *IoAudioMixer_addSound_onSample_ofSound_(IoAudioMixer *self, IoObject *locals, IoMessage *m)
299 IoSound *ioSound = IoMessage_locals_soundArgAt_(m, locals, 0);
300 long sample = IoMessage_locals_longArgAt_(m, locals, 1);
301 IoSound *ioTriggerSound = IoMessage_locals_soundArgAt_(m, locals, 2);
302 AudioEvent *event = AudioEvent_newWithSound_onSample_of_type_(
303 ioSound, sample, ioTriggerSound, AUDIOEVENT_ADD);
304 IoAudioMixer_addEvent_(self, event);
305 return self;
308 void IoAudioMixer_justAddSound_(IoAudioMixer *self, IoSound *ioSound)
310 /*printf("add sound %p\n", (void *)ioSound);*/
311 List_append_(DATA(self)->sounds, ioSound);
312 Sound_setIsPlaying_(IoSound_rawSound(ioSound), 1);
315 void IoAudioMixer_justRemoveSound_(IoAudioMixer *self, IoSound *ioSound)
317 /*printf("remove sound %p\n", (void *)ioSound);*/
318 List_remove_(DATA(self)->sounds, ioSound);
319 Sound_setIsPlaying_(IoSound_rawSound(ioSound), 0);
322 IoObject *IoAudioMixer_removeSound_(IoAudioMixer *self, IoObject *locals, IoMessage *m)
324 IoSound *ioSound = IoMessage_locals_soundArgAt_(m, locals, 0);
325 /*List_append_(DATA(self)->soundsToRemove, ioSound);*/
326 AudioEvent *event = AudioEvent_newWithSound_onSample_of_type_(ioSound, 0, 0x0, AUDIOEVENT_REMOVE);
327 IoAudioMixer_addEvent_(self, event);
328 /*IoAudioMixer_justRemoveSound_(self, ioSound);*/
329 return self;
332 IoObject *IoAudioMixer_removeAllSounds(IoAudioMixer *self, IoObject *locals, IoMessage *m)
334 List *sounds = DATA(self)->sounds;
335 while (List_size(sounds))
337 IoAudioMixer_justRemoveSound_(self, List_at_(sounds, 0));
339 return self;
343 IoObject *IoAudioMixer_removeSound_onSample_ofSound_(IoAudioMixer *self, IoObject *locals, IoMessage *m)
345 IoSound *ioSound = IoMessage_locals_soundArgAt_(m, locals, 0);
346 long sample = IoMessage_locals_longArgAt_(m, locals, 1);
347 IoSound *ioTriggerSound = IoMessage_locals_soundArgAt_(m, locals, 2);
348 AudioEvent *event =
349 AudioEvent_newWithSound_onSample_of_type_(ioSound, sample, ioTriggerSound, AUDIOEVENT_REMOVE);
350 IoAudioMixer_addEvent_(self, event);
351 return self;
354 /* ----------------------------------------------------------- */
356 int IoAudioMixer_isActive(IoAudioMixer *self)
358 return List_size(DATA(self)->sounds) ||
359 List_size(DATA(self)->soundsToRemove) ||
360 List_size(DATA(self)->events) ||
361 List_size(DATA(self)->activeEvents);
364 IoObject *IoAudioMixer_start(IoAudioMixer *self, IoObject *locals, IoMessage *m)
366 IoAudioMixer_updateBufferSize(self);
368 DATA(self)->isRunning = 1;
369 while (DATA(self)->isRunning)
371 if (IoAudioMixer_isActive(self))
373 int outSamples = IoAudioMixer_mixOneChunk(self, locals, m);
374 if (outSamples < 5000)
376 #ifdef DEBUG
377 printf("< 5000 out - mix another chunk\n");
378 #endif
379 IoAudioMixer_mixOneChunk(self, locals, m);
382 //IoState_yield(IOSTATE);
384 return self;
387 IoObject *IoAudioMixer_stop(IoAudioMixer *self, IoObject *locals, IoMessage *m)
389 DATA(self)->isRunning = 0;
390 /*IoAudioDevice_clearBuffers(DATA(self)->ioAudioDevice);*/
391 return self;
394 void IoAudioMixer_processSoundRemovals(IoAudioMixer *self)
396 IoSound *s;
397 List *soundsToRemove = DATA(self)->soundsToRemove;
398 List *sounds = DATA(self)->sounds;
399 int removeCount = List_size(soundsToRemove);
401 if (removeCount)
403 printf("removeCount = %i\n", removeCount);
404 printf("soundsCount = %i\n", List_size(sounds));
407 while ( (s = List_pop(soundsToRemove)) )
408 { IoAudioMixer_justRemoveSound_(self, s); }
410 if (removeCount)
412 printf("remaining sounds = %i\n", List_size(sounds));
413 printf("remaining removeCount = %i\n\n", List_size(soundsToRemove));
416 IoAudioMixer_updateScale(self);
419 void IoAudioMixer_processActiveEvents(IoAudioMixer *self)
421 AudioEvent *e;
422 while ( (e = List_pop(DATA(self)->activeEvents)) )
424 #ifdef DEBUG
425 printf("processing: ");
426 AudioEvent_show(e);
427 #endif
428 switch (e->etype)
430 case AUDIOEVENT_ADD:
431 IoAudioMixer_justAddSound_(self, e->ioPlaySound);
432 break;
433 case AUDIOEVENT_REMOVE:
434 IoAudioMixer_justRemoveSound_(self, e->ioPlaySound);
435 break;
438 IoAudioMixer_updateScale(self);
441 int IoAudioMixer_mixOneChunk(IoAudioMixer *self, IoObject *locals, IoMessage *m)
443 UArray *mixBuffer = DATA(self)->mixBuffer;
444 List *sounds = DATA(self)->sounds;
445 List *soundsToRemove = DATA(self)->soundsToRemove;
446 AudioEvent *e = List_top(DATA(self)->events);
447 List *activeEvents = DATA(self)->activeEvents;
448 int frame;
449 /*int samplesPerBuffer = DATA(self)->samplesPerBuffer;*/
450 int samplesPerBuffer = UArray_size(DATA(self)->mixBuffer) / (sizeof(float) * 2);
452 UArray_setAllBytesTo_(mixBuffer, 0);
454 while ( e && (!e->ioTriggerSound))
456 List_append_(DATA(self)->activeEvents, List_pop(DATA(self)->events));
457 e = List_top(DATA(self)->events);
460 if (List_size(activeEvents)) IoAudioMixer_processActiveEvents(self);
461 if (List_size(soundsToRemove)) IoAudioMixer_processSoundRemovals(self);
463 for (frame = 0; frame < samplesPerBuffer; frame ++)
465 int index = frame * 2;
466 int i;
467 float *ol = UArray_floatPointerAt_(mixBuffer, index);
468 float *or = UArray_floatPointerAt_(mixBuffer, index+1);
470 for (i = 0; i < List_size(sounds); i++)
472 IoSound *ioSound = List_at_(sounds, i);
473 Sound *sound = IoSound_rawSound(ioSound);
474 float left, right;
475 char done = Sound_nextFloat(sound, &left, &right);
477 if (done && !Sound_isLooping(sound))
479 List_append_(soundsToRemove, ioSound);
480 continue;
483 (*ol) += left;
484 (*or) += right;
486 while (e &&
487 ((!e->ioTriggerSound) ||
488 ((e->ioTriggerSound == ioSound) &&
489 (e->sample == Sound_position(sound))))
492 List_append_(DATA(self)->activeEvents, List_pop(DATA(self)->events));
493 e = List_top(DATA(self)->events);
497 (*ol) *= DATA(self)->scale;
498 (*or) *= DATA(self)->scale;
500 if (List_size(activeEvents)) IoAudioMixer_processActiveEvents(self);
501 if (List_size(soundsToRemove)) IoAudioMixer_processSoundRemovals(self);
504 /* adjust pitch and tempo */
506 //double t1 = ((double)clock())/((double)CLOCKS_PER_SEC);
507 //double t2;
508 int receivedSamples = 1;
510 SoundTouch_putSamples(DATA(self)->soundTouch, (float *)UArray_bytes(mixBuffer), samplesPerBuffer);
511 //printf("put %i\n", samplesPerBuffer);
512 //while (receivedSamples)
514 UArray_setSize_(DATA(self)->buffer, 10000*8);
515 receivedSamples = SoundTouch_receiveSamples(DATA(self)->soundTouch,
516 (float *)UArray_bytes(DATA(self)->buffer),
517 UArray_size(DATA(self)->buffer) / (sizeof(float) * 2));
518 UArray_setSize_(DATA(self)->buffer, receivedSamples * (sizeof(float) * 2));
520 //printf("received %i\n", receivedSamples);
521 if (receivedSamples)
523 if (receivedSamples < 5000)
525 #ifdef DEBUG
526 printf("non-blocking write\n");
527 #endif
529 IoMessage_locals_performOn_(DATA(self)->nonBlockingWriteMessage,
530 self, DATA(self)->ioAudioDevice);
532 else
534 IoMessage_locals_performOn_(DATA(self)->writeMessage,
535 self, DATA(self)->ioAudioDevice);
538 #ifdef DEBUG
539 t2 = ((double)clock())/((double)CLOCKS_PER_SEC);
540 printf("tempo: %1.1f %i -> %i in %0.2f sec\n",
541 DATA(self)->tempo,
542 samplesPerBuffer,
543 receivedSamples,
544 (float)(t2 - t1));
545 #endif
548 //printf("\n");
549 return receivedSamples;
551 /* need to change this to be dynamic, so we can easily record the output */
552 /*IoAudioDevice_justWrite(DATA(self)->ioAudioDevice, locals, m, buffer);*/
553 return UArray_size(DATA(self)->buffer) / 8;
556 IoObject *IoAudioMixer_setTempo(IoAudioMixer *self, IoObject *locals, IoMessage *m)
558 DATA(self)->tempo = IoMessage_locals_floatArgAt_(m, locals, 0);
559 SoundTouch_setTempo(DATA(self)->soundTouch, DATA(self)->tempo);
560 printf("IoAudioMixer_setTempo(%f)\n", DATA(self)->tempo);
561 IoAudioMixer_updateBufferSize(self);
562 return self;
565 IoObject *IoAudioMixer_setSampleRate(IoAudioMixer *self, IoObject *locals, IoMessage *m)
567 DATA(self)->sampleRate = IoMessage_locals_intArgAt_(m, locals, 0);
568 SoundTouch_setSampleRate(DATA(self)->soundTouch, (unsigned int)DATA(self)->sampleRate);
569 printf("IoAudioMixer_setSampleRate(%i)\n", DATA(self)->sampleRate);
570 IoAudioMixer_updateBufferSize(self);
571 return self;
574 IoObject *IoAudioMixer_setPitchSemiTones(IoAudioMixer *self, IoObject *locals, IoMessage *m)
576 DATA(self)->pitch = IoMessage_locals_floatArgAt_(m, locals, 0);
577 SoundTouch_setPitchSemiTones(DATA(self)->soundTouch, DATA(self)->pitch);
578 printf("IoAudioMixer_setPitchSemiTones(%f)\n", DATA(self)->pitch);
579 IoAudioMixer_updateBufferSize(self);
580 return self;