coreaudio 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }