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.
- data/.document +5 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +16 -0
- data/LICENSE.txt +22 -0
- data/README.rdoc +32 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/examples/outbuffer_sine.rb +22 -0
- data/examples/outloop_sine.rb +14 -0
- data/examples/record_to_wave.rb +31 -0
- data/ext/audiofile.m +276 -0
- data/ext/coreaudio.h +14 -0
- data/ext/coreaudio.m +1054 -0
- data/ext/extconf.rb +11 -0
- data/lib/coreaudio.rb +2 -0
- metadata +88 -0
data/ext/coreaudio.h
ADDED
@@ -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
|
data/ext/coreaudio.m
ADDED
@@ -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
|
+
}
|