coreaudio 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ #ifndef COREAUDIO_H
2
+ #define COREAUDIO_H 1
3
+
4
+ extern VALUE rb_mCoreAudio;
5
+ extern VALUE rb_mAudioFile;
6
+
7
+ extern void Init_coreaudio_audiofile(void);
8
+
9
+ /*-- Utility Macros --*/
10
+ #define CROPF(F) ((F) > 1.0 ? 1.0 : (((F) < -1.0) ? -1.0 : (F)))
11
+ #define FLOAT2SHORT(F) ((short)(CROPF(F)*0x7FFF))
12
+ #define SHORT2FLOAT(S) ((double)(S) / 32767.0)
13
+
14
+ #endif
@@ -0,0 +1,1054 @@
1
+ #include <stdio.h>
2
+ #include <unistd.h>
3
+
4
+ #include <pthread.h>
5
+
6
+ #include <Foundation/Foundation.h>
7
+ #include <CoreAudio/CoreAudio.h>
8
+
9
+ #include <ruby.h>
10
+
11
+ #include "coreaudio.h"
12
+
13
+ VALUE rb_mCoreAudio;
14
+
15
+ static VALUE rb_cAudioDevice;
16
+ static VALUE rb_cAudioStream;
17
+ static VALUE rb_cOutLoop;
18
+ static VALUE rb_cAudioBuffer;
19
+ static VALUE rb_cOutputBuffer;
20
+ static VALUE rb_cInputBuffer;
21
+ static ID sym_iv_devid;
22
+ static ID sym_iv_name;
23
+ static ID sym_iv_available_sample_rate;
24
+ static ID sym_iv_nominal_rate;
25
+ static ID sym_iv_input_stream;
26
+ static ID sym_iv_output_stream;
27
+ static ID sym_iv_channels;
28
+ static ID sym_iv_buffer_frame_size;
29
+
30
+ /* utility macro */
31
+ #define PropertyAddress { \
32
+ .mScope = kAudioObjectPropertyScopeGlobal, \
33
+ .mElement = kAudioObjectPropertyElementMaster \
34
+ }
35
+
36
+ /*--- CoreAudio::AudioStream ---*/
37
+ static VALUE
38
+ ca_get_stream_channel_num(AudioDeviceID devID,
39
+ AudioObjectPropertyScope scope)
40
+ {
41
+ AudioObjectPropertyAddress address = PropertyAddress;
42
+ UInt32 size;
43
+ AudioChannelLayout *layout;
44
+ OSStatus status;
45
+ UInt32 ch_num;
46
+
47
+ address.mSelector = kAudioDevicePropertyPreferredChannelLayout;
48
+ address.mScope = scope;
49
+ if (!AudioObjectHasProperty(devID, &address))
50
+ return INT2NUM(0);
51
+
52
+ status = AudioObjectGetPropertyDataSize(devID, &address, 0, NULL, &size);
53
+
54
+ if (status != noErr) {
55
+ rb_raise(rb_eArgError,
56
+ "coreaudio: get preferred channel layout size failed: %d", status);
57
+ }
58
+
59
+ layout = alloca(size);
60
+
61
+ status = AudioObjectGetPropertyData(devID, &address, 0, NULL, &size, layout);
62
+ if (status != noErr) {
63
+ rb_raise(rb_eArgError,
64
+ "coreaudio: get preferred channel layout failed: %d", status);
65
+ }
66
+
67
+ if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
68
+ ch_num = layout->mNumberChannelDescriptions;
69
+ } else if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
70
+ UInt32 i;
71
+ ch_num = 0;
72
+ for ( i = 0; i < sizeof(layout->mChannelBitmap)*8; i++ ) {
73
+ if ( (layout->mChannelBitmap >> i) & 0x01 )
74
+ ch_num++;
75
+ }
76
+ } else {
77
+ ch_num = AudioChannelLayoutTag_GetNumberOfChannels(layout->mChannelLayoutTag);
78
+ }
79
+ return UINT2NUM(ch_num);
80
+ }
81
+
82
+ static VALUE
83
+ ca_get_stream_buffer_frame(AudioDeviceID devID, AudioObjectPropertyScope scope)
84
+ {
85
+ AudioObjectPropertyAddress address = PropertyAddress;
86
+ UInt32 size, framesize;
87
+ OSStatus status;
88
+
89
+ address.mSelector = kAudioDevicePropertyBufferFrameSize;
90
+ address.mScope = scope;
91
+
92
+ if (!AudioObjectHasProperty(devID, &address))
93
+ return INT2NUM(0);
94
+
95
+ size = sizeof(framesize);
96
+ status = AudioObjectGetPropertyData(devID, &address, 0, NULL, &size, &framesize);
97
+ if (status != noErr) {
98
+ rb_raise(rb_eArgError,
99
+ "coreaudio: get buffer frame size failed: %d", status);
100
+ }
101
+ return UINT2NUM(framesize);
102
+ }
103
+
104
+ static VALUE
105
+ ca_stream_initialize(VALUE self, VALUE devid_val, VALUE is_input)
106
+ {
107
+ AudioDeviceID devID = (AudioDeviceID)NUM2UINT(devid_val);
108
+ AudioObjectPropertyScope scope;
109
+
110
+ if (RTEST(is_input))
111
+ scope = kAudioDevicePropertyScopeInput;
112
+ else
113
+ scope = kAudioDevicePropertyScopeOutput;
114
+ rb_ivar_set(self, sym_iv_channels, ca_get_stream_channel_num(devID, scope));
115
+ rb_ivar_set(self, sym_iv_buffer_frame_size, ca_get_stream_buffer_frame(devID, scope));
116
+
117
+ return self;
118
+ }
119
+
120
+ static VALUE
121
+ ca_stream_new(VALUE devid, VALUE is_input)
122
+ {
123
+ VALUE stream;
124
+ stream = rb_obj_alloc(rb_cAudioStream);
125
+ ca_stream_initialize(stream, devid, is_input);
126
+ return stream;
127
+ }
128
+
129
+ /*--- CoreAudio::AudioDevice ---*/
130
+ static VALUE
131
+ ca_get_device_name(AudioDeviceID devID)
132
+ {
133
+ AudioObjectPropertyAddress address = PropertyAddress;
134
+ UInt32 size;
135
+ OSStatus status;
136
+ CFStringRef deviceName = NULL;
137
+ VALUE str;
138
+
139
+ address.mSelector = kAudioObjectPropertyName;
140
+ size = sizeof(deviceName);
141
+
142
+ status = AudioObjectGetPropertyData(devID, &address, 0, NULL, &size, &deviceName);
143
+ if ( status != noErr ) {
144
+ rb_raise(rb_eArgError, "coreaudio: get device name failed: %d", status);
145
+ }
146
+ str = rb_str_new2(CFStringGetCStringPtr(deviceName, kCFStringEncodingASCII));
147
+ CFRelease(deviceName);
148
+
149
+ return str;
150
+ }
151
+
152
+ static VALUE
153
+ ca_get_device_available_sample_rate(AudioDeviceID devID)
154
+ {
155
+ AudioObjectPropertyAddress address = PropertyAddress;
156
+ UInt32 size;
157
+ UInt32 n_rates;
158
+ AudioValueRange *sample_rates;
159
+ OSStatus status;
160
+ VALUE ary;
161
+ UInt32 i;
162
+
163
+ address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
164
+ status = AudioObjectGetPropertyDataSize(devID, &address, 0, NULL, &size);
165
+
166
+ if (status != noErr) {
167
+ rb_raise(rb_eArgError,
168
+ "coreaudio: get available sample rates size failed: %d", status);
169
+ }
170
+
171
+ n_rates = size / (UInt32)sizeof(AudioValueRange);
172
+ sample_rates = ALLOCA_N(AudioValueRange, n_rates);
173
+
174
+ status = AudioObjectGetPropertyData(devID, &address, 0, NULL, &size, sample_rates);
175
+ if (status != noErr) {
176
+ rb_raise(rb_eArgError,
177
+ "coreaudio: get available sample rates failed: %d", status);
178
+ }
179
+
180
+ ary = rb_ary_new();
181
+ for ( i = 0; i < n_rates; i++ ) {
182
+ rb_ary_push(ary,
183
+ rb_ary_new3(2,
184
+ DBL2NUM((double)sample_rates[i].mMinimum),
185
+ DBL2NUM((double)sample_rates[i].mMaximum)));
186
+ }
187
+
188
+ return ary;
189
+ }
190
+
191
+ static VALUE
192
+ ca_get_device_nominal_sample_rate(AudioDeviceID devID)
193
+ {
194
+ AudioObjectPropertyAddress address = PropertyAddress;
195
+ UInt32 size;
196
+ Float64 rate;
197
+ OSStatus status;
198
+
199
+ address.mSelector = kAudioDevicePropertyNominalSampleRate;
200
+ status = AudioObjectGetPropertyDataSize(devID, &address, 0, NULL, &size);
201
+
202
+ if (status != noErr) {
203
+ rb_raise(rb_eArgError,
204
+ "coreaudio: get nominal sample rates size failed: %d", status);
205
+ }
206
+
207
+ status = AudioObjectGetPropertyData(devID, &address, 0, NULL, &size, &rate);
208
+ if (status != noErr) {
209
+ rb_raise(rb_eArgError,
210
+ "coreaudio: get nominal sample rates failed: %d", status);
211
+ }
212
+ return DBL2NUM((double)rate);
213
+ }
214
+
215
+ static VALUE
216
+ ca_get_device_actual_sample_rate(VALUE self)
217
+ {
218
+ AudioDeviceID devID = NUM2UINT(rb_ivar_get(self, sym_iv_devid));
219
+ AudioObjectPropertyAddress address = PropertyAddress;
220
+ UInt32 size;
221
+ Float64 rate;
222
+ OSStatus status;
223
+
224
+ address.mSelector = kAudioDevicePropertyActualSampleRate;
225
+ status = AudioObjectGetPropertyDataSize(devID, &address, 0, NULL, &size);
226
+
227
+ size = sizeof(rate);
228
+ status = AudioObjectGetPropertyData(devID, &address, 0, NULL, &size, &rate);
229
+ if (status != noErr) {
230
+ rb_raise(rb_eArgError,
231
+ "coreaudio: get actual sample rates failed: %d", status);
232
+ }
233
+ return DBL2NUM((double)rate);
234
+ }
235
+
236
+ static VALUE
237
+ ca_device_initialize(VALUE self, VALUE devIdVal)
238
+ {
239
+ AudioDeviceID devID = (AudioDeviceID)NUM2LONG(devIdVal);
240
+ VALUE device_name;
241
+ VALUE available_sample_rate;
242
+ VALUE nominal_rate;
243
+ VALUE input_stream, output_stream;
244
+
245
+ device_name = ca_get_device_name(devID);
246
+ available_sample_rate = ca_get_device_available_sample_rate(devID);
247
+ rb_obj_freeze(available_sample_rate);
248
+ nominal_rate = ca_get_device_nominal_sample_rate(devID);
249
+ input_stream = ca_stream_new(devIdVal, Qtrue);
250
+ output_stream = ca_stream_new(devIdVal, Qfalse);
251
+
252
+ rb_ivar_set(self, sym_iv_devid, devIdVal);
253
+ rb_ivar_set(self, sym_iv_name, device_name);
254
+ rb_ivar_set(self, sym_iv_available_sample_rate, available_sample_rate);
255
+ rb_ivar_set(self, sym_iv_nominal_rate, nominal_rate);
256
+ rb_ivar_set(self, sym_iv_input_stream, input_stream);
257
+ rb_ivar_set(self, sym_iv_output_stream, output_stream);
258
+
259
+ return self;
260
+ }
261
+
262
+ static VALUE
263
+ ca_device_new(AudioDeviceID devid)
264
+ {
265
+ VALUE devIdVal = UINT2NUM(devid);
266
+ VALUE device;
267
+
268
+ device = rb_obj_alloc(rb_cAudioDevice);
269
+ ca_device_initialize(device, devIdVal);
270
+
271
+ return device;
272
+ }
273
+
274
+ /*
275
+ * Document-method: CoreAudio.devices
276
+ * call-seq:
277
+ * CoreAudio.devices
278
+ *
279
+ * Get available all audio devices (CoreAudio::AudioDevice object).
280
+ */
281
+ static VALUE
282
+ ca_devices(VALUE mod)
283
+ {
284
+ AudioObjectPropertyAddress address = PropertyAddress;
285
+ AudioDeviceID *devIDs = NULL;
286
+ UInt32 size = 0, devnum = 0;
287
+ OSStatus status = noErr;
288
+ VALUE ary;
289
+ UInt32 i;
290
+
291
+ address.mSelector = kAudioHardwarePropertyDevices;
292
+
293
+ status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
294
+ &address, 0, NULL, &size);
295
+ if (status != noErr)
296
+ rb_raise(rb_eRuntimeError, "coreaudio: get devices size failed: %d", status);
297
+
298
+ devnum = size / (UInt32)sizeof(AudioDeviceID);
299
+ devIDs = ALLOCA_N(AudioDeviceID, devnum);
300
+
301
+ status = AudioObjectGetPropertyData(kAudioObjectSystemObject,
302
+ &address, 0, NULL, &size, devIDs);
303
+ if (status != noErr)
304
+ rb_raise(rb_eRuntimeError, "coreaudio: get devices failed: %d", status);
305
+
306
+ ary = rb_ary_new();
307
+ for (i = 0; i < devnum; i++) {
308
+ rb_ary_push(ary, ca_device_new(devIDs[i]));
309
+ }
310
+ return ary;
311
+ }
312
+
313
+ /*
314
+ * Document-method: CoreAudio.default_input_device
315
+ * call-seq:
316
+ * CoreAudio.default_input_device
317
+ *
318
+ * Get system default audio input device as CoreAudio::AudioDevice object.
319
+ */
320
+ static VALUE
321
+ ca_default_input_device(VALUE mod)
322
+ {
323
+ AudioDeviceID devID;
324
+ AudioObjectPropertyAddress address = PropertyAddress;
325
+ UInt32 size;
326
+ OSStatus status;
327
+
328
+ address.mSelector = kAudioHardwarePropertyDefaultInputDevice;
329
+ size = sizeof(devID);
330
+
331
+ status = AudioObjectGetPropertyData(kAudioObjectSystemObject,
332
+ &address, 0, NULL, &size, &devID);
333
+
334
+ if (status != noErr)
335
+ rb_raise(rb_eArgError, "coreaudio: get default input device failed: %d", status);
336
+
337
+ return ca_device_new(devID);
338
+ }
339
+
340
+
341
+ /*
342
+ * Document-method: CoreAudio.default_output_device
343
+ * call-seq:
344
+ * CoreAudio.default_output_device
345
+ *
346
+ * Get system default audio output device as CoreAudio::AudioDevice object.
347
+ */
348
+ static VALUE
349
+ ca_default_output_device(VALUE mod)
350
+ {
351
+ AudioDeviceID devID;
352
+ AudioObjectPropertyAddress address = PropertyAddress;
353
+ UInt32 size;
354
+ OSStatus status;
355
+
356
+ address.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
357
+ size = sizeof(devID);
358
+
359
+ status = AudioObjectGetPropertyData(kAudioObjectSystemObject,
360
+ &address, 0, NULL, &size, &devID);
361
+
362
+ if (status != noErr)
363
+ rb_raise(rb_eArgError, "coreaudio: get default output device failed: %d", status);
364
+
365
+ return ca_device_new(devID);
366
+ }
367
+
368
+ /*
369
+ * Document-class: CoreAudio::OutLoop
370
+ *
371
+ * CoreAudio::OutLoop is an class for loop waveform to output audio stream.
372
+ */
373
+ typedef struct {
374
+ AudioDeviceID devID;
375
+ AudioDeviceIOProcID procID;
376
+ UInt32 frame;
377
+ UInt32 channel;
378
+ short *buf;
379
+ } ca_out_loop_data;
380
+
381
+ static void
382
+ ca_out_loop_data_free(void *ptr)
383
+ {
384
+ if (ptr) {
385
+ ca_out_loop_data *data = ptr;
386
+ if (data->procID)
387
+ AudioDeviceDestroyIOProcID(data->devID, data->procID);
388
+ if (data->buf)
389
+ free(data->buf);
390
+ free(ptr);
391
+ }
392
+ }
393
+
394
+ static size_t
395
+ ca_out_loop_data_memsize(const void *ptr)
396
+ {
397
+ const ca_out_loop_data *data = ptr;
398
+ return sizeof(ca_out_loop_data) + data->channel * data->frame * sizeof(short);
399
+ }
400
+
401
+ static const rb_data_type_t ca_out_loop_data_type = {
402
+ "ca_out_loop_data",
403
+ {NULL, ca_out_loop_data_free, ca_out_loop_data_memsize},
404
+ };
405
+
406
+ static OSStatus
407
+ ca_out_loop_proc(
408
+ AudioDeviceID inDevice,
409
+ const AudioTimeStamp* inNow,
410
+ const AudioBufferList* inInputData,
411
+ const AudioTimeStamp* inInputTime,
412
+ AudioBufferList* outOutputData,
413
+ const AudioTimeStamp* inOutputTime,
414
+ void* inClientData)
415
+ {
416
+ NSUInteger i;
417
+ UInt32 buffers = outOutputData->mNumberBuffers;
418
+ ca_out_loop_data *loop_data = inClientData;
419
+ UInt32 channel = loop_data->channel;
420
+
421
+ for (i = 0; i < buffers; i++) {
422
+ float *ptr = outOutputData->mBuffers[i].mData;
423
+ UInt32 size = outOutputData->mBuffers[i].mDataByteSize / (UInt32)sizeof(float) / channel;
424
+ UInt32 offset = (UInt32)inOutputTime->mSampleTime % loop_data->frame;
425
+ UInt32 copied = 0;
426
+
427
+ if (outOutputData->mBuffers[i].mNumberChannels != channel) {
428
+ memset(ptr, 0, size * channel * sizeof(float));
429
+ continue;
430
+ }
431
+
432
+ while ( copied < size ) {
433
+ UInt32 len = loop_data->frame - offset;
434
+ UInt32 j;
435
+ if ( len > size - copied )
436
+ len = size - copied;
437
+ for (j = 0; j < len*channel; j++) {
438
+ ptr[copied*channel+j] = SHORT2FLOAT(loop_data->buf[offset*channel+j]);
439
+ }
440
+ offset = (offset + len) % loop_data->frame;
441
+ copied += len;
442
+ }
443
+ }
444
+
445
+ return 0;
446
+ }
447
+
448
+ static VALUE
449
+ ca_out_loop_data_alloc(VALUE klass)
450
+ {
451
+ VALUE obj;
452
+ ca_out_loop_data *ptr;
453
+
454
+ obj = TypedData_Make_Struct(klass, ca_out_loop_data, &ca_out_loop_data_type, ptr);
455
+ return obj;
456
+ }
457
+
458
+ static VALUE
459
+ ca_out_loop_data_initialize(VALUE self, VALUE devID, VALUE frame, VALUE channel)
460
+ {
461
+ ca_out_loop_data *data;
462
+ OSStatus status;
463
+
464
+ TypedData_Get_Struct(self, ca_out_loop_data, &ca_out_loop_data_type, data);
465
+ data->devID = NUM2UINT(devID);
466
+ status = AudioDeviceCreateIOProcID(data->devID, ca_out_loop_proc, data, &data->procID);
467
+ if ( status != noErr )
468
+ {
469
+ rb_raise(rb_eRuntimeError, "coreaudio: create proc ID fail: %d", status);
470
+ }
471
+ data->frame = NUM2UINT(frame);
472
+ data->channel = NUM2UINT(channel);
473
+ data->buf = malloc(sizeof(short)*data->frame*data->channel);
474
+ if (data->buf == NULL)
475
+ rb_raise(rb_eNoMemError, "coreaudio: fail to alloc out loop data buffer");
476
+ return self;
477
+ }
478
+
479
+ /*
480
+ * Document-method: CoreAudio::AudioDevice#out_loop
481
+ * call-seq:
482
+ * device.out_loop(frame)
483
+ *
484
+ * Create output audio loop buffer.
485
+ *
486
+ * == Parameters
487
+ * * +frame+ is an integer value indicate loop buffer size in number of
488
+ * sample/frame. The number of channel is considered automatically.
489
+ */
490
+ static VALUE
491
+ ca_device_create_out_loop_proc(VALUE self, VALUE frame)
492
+ {
493
+ VALUE proc;
494
+ VALUE out_stream = rb_ivar_get(self, sym_iv_output_stream);
495
+
496
+ proc = ca_out_loop_data_alloc(rb_cOutLoop);
497
+ ca_out_loop_data_initialize(proc, rb_ivar_get(self, sym_iv_devid), frame,
498
+ rb_ivar_get(out_stream, sym_iv_channels));
499
+ return proc;
500
+ }
501
+
502
+ /*
503
+ * call-seq:
504
+ * outloop.start
505
+ *
506
+ * Start play of output audio loop.
507
+ */
508
+ static VALUE
509
+ ca_out_loop_data_start(VALUE self)
510
+ {
511
+ ca_out_loop_data *data;
512
+ OSStatus status;
513
+
514
+ TypedData_Get_Struct(self, ca_out_loop_data, &ca_out_loop_data_type, data);
515
+
516
+ status = AudioDeviceStart(data->devID, data->procID);
517
+ if ( status != noErr )
518
+ {
519
+ rb_raise(rb_eRuntimeError, "coreaudio: audio device start fail: %d", status);
520
+ }
521
+ return self;
522
+ }
523
+
524
+ /*
525
+ * call-seq:
526
+ * outloop.stop
527
+ *
528
+ * Stop play of output audio loop.
529
+ */
530
+ static VALUE
531
+ ca_out_loop_data_stop(VALUE self)
532
+ {
533
+ ca_out_loop_data *data;
534
+ OSStatus status;
535
+
536
+ TypedData_Get_Struct(self, ca_out_loop_data, &ca_out_loop_data_type, data);
537
+
538
+ status = AudioDeviceStop(data->devID, data->procID);
539
+ if ( status != noErr )
540
+ {
541
+ rb_raise(rb_eRuntimeError, "coreaudio: audio device stop fail: %d", status);
542
+ }
543
+ return self;
544
+ }
545
+
546
+ /*
547
+ * call-seq:
548
+ * outloop[frame] = sample
549
+ * outloop[frame] = [sample1, sample2]
550
+ *
551
+ * Assign audio loop buffer frame value.
552
+ * If assigned value is an Fixnum (-32767..32767, signed 16bit),
553
+ * the value is stored to all channel.
554
+ * The +sample+ should be normalize to -32767 <= sample <= 32767 range.
555
+ * If assigned value is an Array of Fixnum, each value is stored to each
556
+ * correponding channel. If size of array is not equal to the AudioDevice's
557
+ * output stream channel number, raise ArgumentError.
558
+ */
559
+ static VALUE
560
+ ca_out_loop_data_assign(VALUE self, VALUE index, VALUE val)
561
+ {
562
+ ca_out_loop_data *data;
563
+ size_t idx;
564
+ UInt32 i;
565
+
566
+ TypedData_Get_Struct(self, ca_out_loop_data, &ca_out_loop_data_type, data);
567
+
568
+ idx = NUM2UINT(index) % data->frame;
569
+ if (TYPE(val) == T_ARRAY) {
570
+ if (RARRAY_LEN(val) != data->channel) {
571
+ rb_raise(rb_eArgError, "size of array and channel size mismatch");
572
+ }
573
+ for (i = 0; i < data->channel; i++) {
574
+ data->buf[idx*data->channel+i] = (short)NUM2INT(RARRAY_PTR(val)[i]);
575
+ }
576
+ } else {
577
+ for (i = 0; i < data->channel; i++) {
578
+ data->buf[idx*data->channel+i] = (short)NUM2INT(val);
579
+ }
580
+ }
581
+ return val;
582
+ }
583
+
584
+ typedef struct {
585
+ AudioDeviceID devID;
586
+ AudioDeviceIOProcID procID;
587
+ UInt32 frame;
588
+ UInt32 channel;
589
+ short *buf;
590
+ UInt32 start;
591
+ UInt32 end;
592
+ long dropped_frame;
593
+ pthread_mutex_t mutex;
594
+ pthread_cond_t cond;
595
+ } ca_buffer_data;
596
+
597
+ static void
598
+ ca_buffer_data_free(void *ptr)
599
+ {
600
+ if (ptr) {
601
+ ca_buffer_data *data = ptr;
602
+ if (data->procID)
603
+ AudioDeviceDestroyIOProcID(data->devID, data->procID);
604
+ pthread_cond_destroy(&data->cond);
605
+ pthread_mutex_destroy(&data->mutex);
606
+ if (data->buf)
607
+ free(data->buf);
608
+ free(ptr);
609
+ }
610
+ }
611
+
612
+ static size_t
613
+ ca_buffer_data_memsize(const void *ptr)
614
+ {
615
+ const ca_buffer_data *data = ptr;
616
+ return sizeof(ca_buffer_data) + data->channel * data->frame * sizeof(short);
617
+ }
618
+
619
+ static const rb_data_type_t ca_buffer_data_type = {
620
+ "ca_buffer_data",
621
+ {NULL, ca_buffer_data_free, ca_buffer_data_memsize},
622
+ };
623
+
624
+ static VALUE
625
+ ca_buffer_data_alloc(VALUE klass)
626
+ {
627
+ VALUE obj;
628
+ ca_buffer_data *ptr;
629
+
630
+ obj = TypedData_Make_Struct(klass, ca_buffer_data, &ca_buffer_data_type, ptr);
631
+ pthread_mutex_init(&ptr->mutex, NULL);
632
+ pthread_cond_init(&ptr->cond, NULL);
633
+ return obj;
634
+ }
635
+
636
+ static VALUE
637
+ ca_buffer_data_start(VALUE self)
638
+ {
639
+ ca_buffer_data *data;
640
+ OSStatus status;
641
+
642
+ TypedData_Get_Struct(self, ca_buffer_data, &ca_buffer_data_type, data);
643
+
644
+ data->dropped_frame = 0;
645
+ status = AudioDeviceStart(data->devID, data->procID);
646
+ if ( status != noErr )
647
+ {
648
+ rb_raise(rb_eRuntimeError, "coreaudio: audio device start fail: %d", status);
649
+ }
650
+ return self;
651
+ }
652
+
653
+ static VALUE
654
+ ca_buffer_data_stop(VALUE self)
655
+ {
656
+ ca_buffer_data *data;
657
+ OSStatus status;
658
+
659
+ TypedData_Get_Struct(self, ca_buffer_data, &ca_buffer_data_type, data);
660
+
661
+ status = AudioDeviceStop(data->devID, data->procID);
662
+ if ( status != noErr )
663
+ {
664
+ rb_raise(rb_eRuntimeError, "coreaudio: audio device stop fail: %d", status);
665
+ }
666
+ return self;
667
+ }
668
+
669
+ static VALUE
670
+ ca_buffer_wait(void *ptr)
671
+ {
672
+ ca_buffer_data *data = ptr;
673
+ int ret;
674
+
675
+ ret = pthread_cond_wait(&data->cond, &data->mutex);
676
+
677
+ return (VALUE)ret;
678
+ }
679
+
680
+ #if 0
681
+ /* use pthread_mutex_lock in unblocking function cause deadlock.
682
+ * because calling thread have GVL lock and interrupted thread could
683
+ * be waiting mutex lock for GVL.
684
+ * So use RUBY_UBF_IO for unblocking function.
685
+ * Although pthread_cond_wait() shouldn't return EINTR acoording to POSIX,
686
+ * on Mac OS X pthread_cond_wait() actually returns when received signals. */
687
+ static void
688
+ ca_buffer_unblocking_func(void *ptr)
689
+ {
690
+ ca_buffer_data *data = ptr;
691
+
692
+ pthread_mutex_lock(&data->mutex);
693
+ pthread_cond_broadcast(&data->cond);
694
+ pthread_mutex_unlock(&data->mutex);
695
+ }
696
+ #endif
697
+
698
+ static VALUE
699
+ ca_buffer_wait_blocking(VALUE value)
700
+ {
701
+ void *ptr = (void *)value;
702
+ #if 0
703
+ return rb_thread_blocking_region(ca_buffer_wait, ptr,
704
+ ca_buffer_unblocking_func, ptr);
705
+ #endif
706
+ return rb_thread_blocking_region(ca_buffer_wait, ptr,
707
+ RUBY_UBF_IO, NULL);
708
+ }
709
+
710
+ static VALUE
711
+ ca_buffer_dropped_frame(VALUE self)
712
+ {
713
+ ca_buffer_data *data;
714
+ long dropped;
715
+
716
+ TypedData_Get_Struct(self, ca_buffer_data, &ca_buffer_data_type, data);
717
+
718
+ pthread_mutex_lock(&data->mutex);
719
+ dropped = data->dropped_frame;
720
+ pthread_mutex_unlock(&data->mutex);
721
+ return LONG2NUM(dropped);
722
+ }
723
+
724
+ static VALUE
725
+ ca_buffer_reset_dropped_frame(VALUE self)
726
+ {
727
+ ca_buffer_data *data;
728
+ long dropped;
729
+
730
+ TypedData_Get_Struct(self, ca_buffer_data, &ca_buffer_data_type, data);
731
+
732
+ pthread_mutex_lock(&data->mutex);
733
+ dropped = data->dropped_frame;
734
+ data->dropped_frame = 0;
735
+ pthread_mutex_unlock(&data->mutex);
736
+ return LONG2NUM(dropped);
737
+ }
738
+
739
+ /*
740
+ * Document-class: CoreAudio::OutputBuffer
741
+ *
742
+ * CoreAudio::OutputBuffer is a class for stream waveform to output audio stream.
743
+ */
744
+ static OSStatus
745
+ ca_out_buffer_proc(
746
+ AudioDeviceID inDevice,
747
+ const AudioTimeStamp* inNow,
748
+ const AudioBufferList* inInputData,
749
+ const AudioTimeStamp* inInputTime,
750
+ AudioBufferList* outOutputData,
751
+ const AudioTimeStamp* inOutputTime,
752
+ void* inClientData)
753
+ {
754
+ NSUInteger n_buf;
755
+ UInt32 buffers = outOutputData->mNumberBuffers;
756
+ ca_buffer_data *buffer_data = inClientData;
757
+ UInt32 channel = buffer_data->channel;
758
+
759
+ for (n_buf = 0; n_buf < buffers; n_buf++) {
760
+ float *ptr = outOutputData->mBuffers[n_buf].mData;
761
+ UInt32 size = outOutputData->mBuffers[n_buf].mDataByteSize / (UInt32)sizeof(float) / channel;
762
+ UInt32 copied = 0;
763
+ UInt32 i;
764
+
765
+ if (outOutputData->mBuffers[n_buf].mNumberChannels != channel) {
766
+ memset(ptr, 0, size * channel * sizeof(float));
767
+ continue;
768
+ }
769
+
770
+ pthread_mutex_lock(&buffer_data->mutex);
771
+ for ( copied = 0, i = buffer_data->start; copied < size && i != buffer_data->end; copied++, i = (i+1) % buffer_data->frame ) {
772
+ UInt32 ch;
773
+ for (ch = 0; ch < channel; ch++) {
774
+ ptr[copied*channel+ch] = SHORT2FLOAT(buffer_data->buf[i*channel+ch]);
775
+ }
776
+ }
777
+ buffer_data->start = i;
778
+ pthread_cond_broadcast(&buffer_data->cond);
779
+ pthread_mutex_unlock(&buffer_data->mutex);
780
+ if ( copied < size ) {
781
+ memset(ptr+(copied*channel), 0, sizeof(float)*channel*(size-copied));
782
+ buffer_data->dropped_frame += size - copied;
783
+ }
784
+ }
785
+
786
+ return 0;
787
+ }
788
+
789
+ static VALUE
790
+ ca_out_buffer_data_initialize(VALUE self, VALUE devID, VALUE frame, VALUE channel)
791
+ {
792
+ ca_buffer_data *data;
793
+ OSStatus status;
794
+
795
+ TypedData_Get_Struct(self, ca_buffer_data, &ca_buffer_data_type, data);
796
+ data->devID = NUM2UINT(devID);
797
+ status = AudioDeviceCreateIOProcID(data->devID, ca_out_buffer_proc, data, &data->procID);
798
+ if ( status != noErr )
799
+ {
800
+ rb_raise(rb_eRuntimeError, "coreaudio: create proc ID fail: %d", status);
801
+ }
802
+ data->frame = NUM2UINT(frame);
803
+ data->channel = NUM2UINT(channel);
804
+ data->buf = malloc(sizeof(short)*data->frame*data->channel);
805
+ if (data->buf == NULL)
806
+ rb_raise(rb_eNoMemError, "coreaudio: fail to alloc out buffer data buffer");
807
+ return self;
808
+ }
809
+
810
+ static VALUE
811
+ ca_device_create_out_buffer_proc(VALUE self, VALUE frame)
812
+ {
813
+ VALUE proc;
814
+ VALUE out_stream = rb_ivar_get(self, sym_iv_output_stream);
815
+
816
+ proc = ca_buffer_data_alloc(rb_cOutputBuffer);
817
+ ca_out_buffer_data_initialize(proc, rb_ivar_get(self, sym_iv_devid), frame,
818
+ rb_ivar_get(out_stream, sym_iv_channels));
819
+ return proc;
820
+ }
821
+
822
+ static VALUE
823
+ ca_out_buffer_data_append(VALUE self, VALUE ary)
824
+ {
825
+ ca_buffer_data *data;
826
+ UInt32 idx;
827
+ VALUE val;
828
+ long i;
829
+ UInt32 j;
830
+
831
+ TypedData_Get_Struct(self, ca_buffer_data, &ca_buffer_data_type, data);
832
+
833
+ pthread_mutex_lock(&data->mutex);
834
+ idx = data->end;
835
+ for ( i = 0; i < RARRAY_LEN(ary); i++, idx = (idx+1)%data->frame) {
836
+ while ( (idx+1) % data->frame == data->start ) {
837
+ int ret, state;
838
+ data->end = idx;
839
+ ret = (int)rb_protect(ca_buffer_wait_blocking, (VALUE)data, &state);
840
+ if (state) {
841
+ pthread_mutex_unlock(&data->mutex);
842
+ rb_jump_tag(state);
843
+ }
844
+ switch(ret) {
845
+ case 0:
846
+ case EINTR:
847
+ case EAGAIN:
848
+ break;
849
+ default:
850
+ pthread_mutex_unlock(&data->mutex);
851
+ rb_sys_fail("pthread_cond_wait");
852
+ break;
853
+ }
854
+ }
855
+ val = RARRAY_PTR(ary)[i];
856
+
857
+ if (TYPE(val) == T_ARRAY) {
858
+ if (RARRAY_LEN(val) != data->channel) {
859
+ pthread_mutex_unlock(&data->mutex);
860
+ rb_raise(rb_eArgError, "size of array and channel size mismatch");
861
+ }
862
+ for (j = 0; j < data->channel; j++) {
863
+ data->buf[idx*data->channel+j] = (short)NUM2INT(RARRAY_PTR(val)[j]);
864
+ }
865
+ } else {
866
+ for (j = 0; j < data->channel; j++) {
867
+ data->buf[idx*data->channel+j] = (short)NUM2INT(val);
868
+ }
869
+ }
870
+ data->end = idx;
871
+ }
872
+ pthread_mutex_unlock(&data->mutex);
873
+ return self;
874
+ }
875
+
876
+ /*
877
+ * Document-class: CoreAudio::InputBuffer
878
+ *
879
+ * CoreAudio::InputBuffer is a class for stream waveform to input audio stream.
880
+ */
881
+ static OSStatus
882
+ ca_in_buffer_proc(
883
+ AudioDeviceID inDevice,
884
+ const AudioTimeStamp* inNow,
885
+ const AudioBufferList* inInputData,
886
+ const AudioTimeStamp* inInputTime,
887
+ AudioBufferList* outOutputData,
888
+ const AudioTimeStamp* inOutputTime,
889
+ void* inClientData)
890
+ {
891
+ NSUInteger n_buf;
892
+ UInt32 buffers = inInputData->mNumberBuffers;
893
+ ca_buffer_data *buffer_data = inClientData;
894
+ UInt32 channel = buffer_data->channel;
895
+
896
+ for (n_buf = 0; n_buf < buffers; n_buf++) {
897
+ float *ptr = inInputData->mBuffers[n_buf].mData;
898
+ UInt32 size = inInputData->mBuffers[n_buf].mDataByteSize / (UInt32)sizeof(float) / channel;
899
+ UInt32 copied, idx;
900
+
901
+ if (inInputData->mBuffers[n_buf].mNumberChannels != channel) {
902
+ continue;
903
+ }
904
+
905
+ pthread_mutex_lock(&buffer_data->mutex);
906
+
907
+ copied = 0;
908
+ for (idx = buffer_data->end;
909
+ copied < size && (idx+1) % buffer_data->frame != buffer_data->start;
910
+ copied++, idx = (idx+1) % buffer_data->frame) {
911
+ UInt32 ch;
912
+ for (ch = 0; ch < channel; ch++) {
913
+ buffer_data->buf[idx*channel+ch] = FLOAT2SHORT(ptr[copied*channel+ch]);
914
+ }
915
+ }
916
+ buffer_data->end = idx;
917
+ buffer_data->dropped_frame += size - copied;
918
+
919
+ pthread_cond_broadcast(&buffer_data->cond);
920
+ pthread_mutex_unlock(&buffer_data->mutex);
921
+ }
922
+
923
+ return 0;
924
+ }
925
+
926
+ static VALUE
927
+ ca_in_buffer_data_initialize(VALUE self, VALUE devID, VALUE frame, VALUE channel)
928
+ {
929
+ ca_buffer_data *data;
930
+ OSStatus status;
931
+
932
+ TypedData_Get_Struct(self, ca_buffer_data, &ca_buffer_data_type, data);
933
+ data->devID = NUM2UINT(devID);
934
+ status = AudioDeviceCreateIOProcID(data->devID, ca_in_buffer_proc, data, &data->procID);
935
+ if ( status != noErr )
936
+ {
937
+ rb_raise(rb_eRuntimeError, "coreaudio: create proc ID fail: %d", status);
938
+ }
939
+ data->frame = NUM2UINT(frame);
940
+ data->channel = NUM2UINT(channel);
941
+ data->buf = malloc(sizeof(short)*data->frame*data->channel);
942
+ if (data->buf == NULL)
943
+ rb_raise(rb_eNoMemError, "coreaudio: fail to alloc input buffer data buffer");
944
+ return self;
945
+ }
946
+
947
+ static VALUE
948
+ ca_device_create_in_buffer_proc(VALUE self, VALUE frame)
949
+ {
950
+ VALUE proc;
951
+ VALUE in_stream = rb_ivar_get(self, sym_iv_input_stream);
952
+
953
+ proc = ca_buffer_data_alloc(rb_cInputBuffer);
954
+ ca_in_buffer_data_initialize(proc, rb_ivar_get(self, sym_iv_devid), frame,
955
+ rb_ivar_get(in_stream, sym_iv_channels));
956
+ return proc;
957
+ }
958
+
959
+ static VALUE
960
+ ca_in_buffer_data_read(VALUE self, VALUE num)
961
+ {
962
+ ca_buffer_data *data;
963
+ UInt32 frame = NUM2UINT(num);
964
+ VALUE val;
965
+ UInt32 i;
966
+ UInt32 j;
967
+
968
+ TypedData_Get_Struct(self, ca_buffer_data, &ca_buffer_data_type, data);
969
+
970
+ val = rb_ary_new2(frame * data->channel);
971
+
972
+ pthread_mutex_lock(&data->mutex);
973
+ for ( i = 0; i < frame; i++, data->start = (data->start+1)%data->frame) {
974
+ while (data->start == data->end) {
975
+ int ret, state;
976
+ ret = (int)rb_protect(ca_buffer_wait_blocking, (VALUE)data, &state);
977
+ if (state) {
978
+ pthread_mutex_unlock(&data->mutex);
979
+ rb_jump_tag(state);
980
+ }
981
+ switch(ret) {
982
+ case 0:
983
+ case EINTR:
984
+ case EAGAIN:
985
+ break;
986
+ default:
987
+ pthread_mutex_unlock(&data->mutex);
988
+ rb_sys_fail("pthread_cond_wait");
989
+ break;
990
+ }
991
+ }
992
+ for ( j = 0; j < data->channel; j++ ) {
993
+ rb_ary_push(val, INT2NUM((double)data->buf[data->start*data->channel+j]));
994
+ }
995
+ }
996
+ pthread_mutex_unlock(&data->mutex);
997
+ return val;
998
+ }
999
+
1000
+ void
1001
+ Init_coreaudio_ext(void)
1002
+ {
1003
+ sym_iv_devid = rb_intern("@devid");
1004
+ sym_iv_name = rb_intern("@name");
1005
+ sym_iv_available_sample_rate = rb_intern("@available_sample_rate");
1006
+ sym_iv_nominal_rate = rb_intern("@nominal_rate");
1007
+ sym_iv_input_stream = rb_intern("@input_stream");
1008
+ sym_iv_output_stream = rb_intern("@output_stream");
1009
+ sym_iv_channels = rb_intern("@channels");
1010
+ sym_iv_buffer_frame_size = rb_intern("@buffer_frame_size");
1011
+
1012
+ rb_mCoreAudio = rb_define_module("CoreAudio");
1013
+ rb_cAudioDevice = rb_define_class_under(rb_mCoreAudio, "AudioDevice", rb_cObject);
1014
+ rb_cAudioStream = rb_define_class_under(rb_mCoreAudio, "AudioStream", rb_cObject);
1015
+ rb_cOutLoop = rb_define_class_under(rb_mCoreAudio, "OutLoop", rb_cObject);
1016
+ rb_cAudioBuffer = rb_define_class_under(rb_mCoreAudio, "AudioBuffer", rb_cObject);
1017
+ rb_cOutputBuffer = rb_define_class_under(rb_mCoreAudio, "OutputBuffer", rb_cAudioBuffer);
1018
+ rb_cInputBuffer = rb_define_class_under(rb_mCoreAudio, "InputBuffer", rb_cAudioBuffer);
1019
+
1020
+ rb_define_method(rb_cAudioDevice, "initialize", ca_device_initialize, 1);
1021
+ rb_define_method(rb_cAudioDevice, "actual_rate", ca_get_device_actual_sample_rate, 0);
1022
+ rb_define_method(rb_cAudioDevice, "output_loop", ca_device_create_out_loop_proc, 1);
1023
+ rb_define_method(rb_cAudioDevice, "output_buffer", ca_device_create_out_buffer_proc, 1);
1024
+ rb_define_method(rb_cAudioDevice, "input_buffer", ca_device_create_in_buffer_proc, 1);
1025
+ rb_define_attr(rb_cAudioDevice, "devid", 1, 0);
1026
+ rb_define_attr(rb_cAudioDevice, "name", 1, 0);
1027
+ rb_define_attr(rb_cAudioDevice, "available_sample_rate", 1, 0);
1028
+ rb_define_attr(rb_cAudioDevice, "nominal_rate", 1, 0);
1029
+ rb_define_attr(rb_cAudioDevice, "input_stream", 1, 0);
1030
+ rb_define_attr(rb_cAudioDevice, "output_stream", 1, 0);
1031
+
1032
+ rb_define_method(rb_cAudioStream, "initialize", ca_stream_initialize, 2);
1033
+ rb_define_attr(rb_cAudioStream, "channels", 1, 0);
1034
+ rb_define_attr(rb_cAudioStream, "buffer_frame_size", 1, 0);
1035
+
1036
+ rb_define_singleton_method(rb_mCoreAudio, "devices", ca_devices, 0);
1037
+ rb_define_singleton_method(rb_mCoreAudio, "default_input_device", ca_default_input_device, 0);
1038
+ rb_define_singleton_method(rb_mCoreAudio, "default_output_device", ca_default_output_device, 0);
1039
+
1040
+ rb_define_method(rb_cOutLoop, "[]=", ca_out_loop_data_assign, 2);
1041
+ rb_define_method(rb_cOutLoop, "start", ca_out_loop_data_start, 0);
1042
+ rb_define_method(rb_cOutLoop, "stop", ca_out_loop_data_stop, 0);
1043
+
1044
+ rb_define_method(rb_cAudioBuffer, "start", ca_buffer_data_start, 0);
1045
+ rb_define_method(rb_cAudioBuffer, "stop", ca_buffer_data_stop, 0);
1046
+ rb_define_method(rb_cAudioBuffer, "dropped_frame", ca_buffer_dropped_frame, 0);
1047
+ rb_define_method(rb_cAudioBuffer, "reset_dropped_frame", ca_buffer_reset_dropped_frame, 0);
1048
+
1049
+ rb_define_method(rb_cOutputBuffer, "<<", ca_out_buffer_data_append, 1);
1050
+
1051
+ rb_define_method(rb_cInputBuffer, "read", ca_in_buffer_data_read, 1);
1052
+
1053
+ Init_coreaudio_audiofile();
1054
+ }