coreaudio 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "bundler", "~> 1.0.0"
|
10
|
+
gem "jeweler", "~> 1.6.4"
|
11
|
+
end
|
data/Gemfile.lock
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (C) 2011 Tomoyuki Chikanaga. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions
|
5
|
+
are met:
|
6
|
+
1. Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
2. Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
13
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
14
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
15
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
16
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
17
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
18
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
19
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
20
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
21
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
22
|
+
SUCH DAMAGE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
= coreaudio
|
2
|
+
|
3
|
+
CoreAudio (Audio Framework of Mac OS X) wrapper library for ruby 1.9
|
4
|
+
|
5
|
+
== Features
|
6
|
+
* HAL (Hardware Abstraction Layer) wrapper
|
7
|
+
* Detect audio devices
|
8
|
+
* Get/Set device properties (numbers of channels, sample rate, frame size...)
|
9
|
+
* Output audio data (Loop buffer, Stream buffer)
|
10
|
+
* Input audio data (Stream buffer)
|
11
|
+
* Save audio data to WAV/M4A file (wrapper for ExtAudioFile)
|
12
|
+
|
13
|
+
== ToDo
|
14
|
+
* Switch input/output audio sample type (signed 16bit integer, Float)
|
15
|
+
* Read audio file (wrapper for ExtAudioFile)
|
16
|
+
* Wrapper for AudioUnit/AudioGraph
|
17
|
+
|
18
|
+
== Contributing to coreaudio
|
19
|
+
|
20
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
21
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
22
|
+
* Fork the project
|
23
|
+
* Start a feature/bugfix branch
|
24
|
+
* Commit and push until you are happy with your contribution
|
25
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
26
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
27
|
+
|
28
|
+
== Copyright
|
29
|
+
|
30
|
+
Copyright (c) 2011 CHIKANAGA Tomoyuki. See LICENSE.txt for
|
31
|
+
further details.
|
32
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "coreaudio"
|
18
|
+
gem.homepage = "http://github.com/nagachika/coreaudio"
|
19
|
+
gem.license = "BSDL"
|
20
|
+
gem.summary = %Q{Mac OS X CoreAudio wrapper library}
|
21
|
+
gem.description = %Q{Mac OS X CoreAudio wrapper library}
|
22
|
+
gem.email = "nagachika00@gmail.com"
|
23
|
+
gem.authors = ["CHIKANAGA Tomoyuki"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
require 'rdoc/task'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "coreaudio #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
rdoc.rdoc_files.include('ext/**/*.m')
|
46
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "coreaudio"
|
2
|
+
|
3
|
+
dev = CoreAudio.default_output_device
|
4
|
+
buf = dev.output_buffer(1024)
|
5
|
+
|
6
|
+
phase = Math::PI * 2.0 * 440.0 / dev.nominal_rate
|
7
|
+
th = Thread.start do
|
8
|
+
i = 0
|
9
|
+
loop do
|
10
|
+
wav = Array.new(1024){|j| (0.4 * Math.sin(phase*(i+j)) * 0x7FFF).round }
|
11
|
+
i += 1024
|
12
|
+
buf << wav
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
buf.start
|
17
|
+
sleep 2
|
18
|
+
buf.stop
|
19
|
+
|
20
|
+
puts "#{buf.dropped_frame} frame dropped."
|
21
|
+
|
22
|
+
th.kill
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "coreaudio"
|
2
|
+
|
3
|
+
dev = CoreAudio.default_output_device
|
4
|
+
buf = dev.output_loop(44000)
|
5
|
+
|
6
|
+
phase = Math::PI * 2.0 * 440.0 / 44000.0
|
7
|
+
44000.times do |i|
|
8
|
+
buf[i] = ((0.4 * Math.sin(phase*i)) * 0x7FFF).round
|
9
|
+
end
|
10
|
+
|
11
|
+
buf.start
|
12
|
+
sleep 2
|
13
|
+
buf.stop
|
14
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
require "coreaudio"
|
3
|
+
|
4
|
+
dev = CoreAudio.default_input_device
|
5
|
+
buf = dev.input_buffer(1024)
|
6
|
+
|
7
|
+
wav = CoreAudio::AudioFile.new("sample.wav", :write, :format => :wav,
|
8
|
+
:rate => dev.nominal_rate,
|
9
|
+
:channel => dev.input_stream.channels)
|
10
|
+
|
11
|
+
samples = 0
|
12
|
+
th = Thread.start do
|
13
|
+
loop do
|
14
|
+
w = buf.read(4096)
|
15
|
+
samples += w.size / dev.input_stream.channels
|
16
|
+
wav.write(w)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
buf.start;
|
21
|
+
$stdout.print "RECORDING..."
|
22
|
+
$stdout.flush
|
23
|
+
sleep 5;
|
24
|
+
buf.stop
|
25
|
+
$stdout.puts "done."
|
26
|
+
th.kill.join
|
27
|
+
|
28
|
+
wav.close
|
29
|
+
|
30
|
+
puts "#{samples} samples read."
|
31
|
+
puts "#{buf.dropped_frame} frame dropped."
|
data/ext/audiofile.m
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <unistd.h>
|
3
|
+
|
4
|
+
#include <Foundation/Foundation.h>
|
5
|
+
#include <CoreAudio/CoreAudio.h>
|
6
|
+
|
7
|
+
#include <ruby.h>
|
8
|
+
|
9
|
+
#include <AudioToolbox/AudioToolbox.h>
|
10
|
+
|
11
|
+
#include "coreaudio.h"
|
12
|
+
|
13
|
+
|
14
|
+
VALUE rb_cAudioFile;
|
15
|
+
|
16
|
+
static VALUE sym_read, sym_write, sym_format;
|
17
|
+
static VALUE sym_rate, sym_file_rate, sym_channel, sym_file_channel;
|
18
|
+
static VALUE sym_wav, sym_m4a;
|
19
|
+
|
20
|
+
static void
|
21
|
+
setASBD(AudioStreamBasicDescription *asbd,
|
22
|
+
Float64 rate,
|
23
|
+
UInt32 format,
|
24
|
+
UInt32 flags,
|
25
|
+
UInt32 channel,
|
26
|
+
UInt32 bitsPerChannel,
|
27
|
+
UInt32 framePerPacket)
|
28
|
+
{
|
29
|
+
asbd->mSampleRate = rate;
|
30
|
+
asbd->mFormatID = format;
|
31
|
+
asbd->mFormatFlags = flags;
|
32
|
+
asbd->mBitsPerChannel = bitsPerChannel;
|
33
|
+
asbd->mChannelsPerFrame = channel;
|
34
|
+
asbd->mFramesPerPacket = framePerPacket;
|
35
|
+
asbd->mBytesPerFrame = bitsPerChannel/8*channel;
|
36
|
+
asbd->mBytesPerPacket = bitsPerChannel/8*channel*framePerPacket;
|
37
|
+
}
|
38
|
+
|
39
|
+
typedef struct {
|
40
|
+
AudioStreamBasicDescription file_desc;
|
41
|
+
AudioStreamBasicDescription inner_desc;
|
42
|
+
Boolean for_write;
|
43
|
+
ExtAudioFileRef file;
|
44
|
+
} ca_audio_file_t;
|
45
|
+
|
46
|
+
static void
|
47
|
+
ca_audio_file_free(void *ptr)
|
48
|
+
{
|
49
|
+
ca_audio_file_t *data = ptr;
|
50
|
+
|
51
|
+
if ( data->file ) {
|
52
|
+
CFRelease(data->file);
|
53
|
+
data->file = NULL;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
static size_t
|
58
|
+
ca_audio_file_memsize(const void *ptr)
|
59
|
+
{
|
60
|
+
(void)ptr;
|
61
|
+
return sizeof(ca_audio_file_t);
|
62
|
+
}
|
63
|
+
|
64
|
+
static const rb_data_type_t ca_audio_file_type = {
|
65
|
+
"ca_audio_file",
|
66
|
+
{NULL, ca_audio_file_free, ca_audio_file_memsize},
|
67
|
+
};
|
68
|
+
|
69
|
+
static VALUE
|
70
|
+
ca_audio_file_alloc(VALUE klass)
|
71
|
+
{
|
72
|
+
VALUE obj;
|
73
|
+
ca_audio_file_t *ptr;
|
74
|
+
|
75
|
+
obj = TypedData_Make_Struct(klass, ca_audio_file_t, &ca_audio_file_type, ptr);
|
76
|
+
|
77
|
+
return obj;
|
78
|
+
}
|
79
|
+
|
80
|
+
/*
|
81
|
+
* call-seq:
|
82
|
+
* AudioFile.new(filepath, mode, opt)
|
83
|
+
*
|
84
|
+
* open audio file at +filepath+. +mode+ should be :read or :write
|
85
|
+
* corresponding to file mode argument of Kernel#open.
|
86
|
+
* +opt+ must contain :format key.
|
87
|
+
*
|
88
|
+
* 'client data' means audio data pass to AudioFile#write or from
|
89
|
+
* AudioFile#read method.
|
90
|
+
*
|
91
|
+
* :format :: audio file format. currently audio file format and
|
92
|
+
* codec type is hardcoded. (:wav, :m4a)
|
93
|
+
* :rate :: sample rate of data pass from AudioFile#read or to AudioFile#write
|
94
|
+
* If not specified, :file_rate value is used. (Float)
|
95
|
+
* :channel :: number of channels
|
96
|
+
* :file_rate :: file data sample rate. only work when open for output. (Float)
|
97
|
+
* :file_channel :: file data number of channels. only work when open for
|
98
|
+
* output.
|
99
|
+
*/
|
100
|
+
static VALUE
|
101
|
+
ca_audio_file_initialize(int argc, VALUE *argv, VALUE self)
|
102
|
+
{
|
103
|
+
ca_audio_file_t *data;
|
104
|
+
VALUE path, mode, opt, format;
|
105
|
+
Float64 rate, file_rate;
|
106
|
+
UInt32 channel, file_channel;
|
107
|
+
CFURLRef outUrl = NULL;
|
108
|
+
AudioFileTypeID filetype;
|
109
|
+
ExtAudioFileRef audioFileRef = NULL;
|
110
|
+
UInt32 flags = kAudioFileFlags_EraseFile;
|
111
|
+
OSStatus err = noErr;
|
112
|
+
|
113
|
+
TypedData_Get_Struct(self, ca_audio_file_t, &ca_audio_file_type, data);
|
114
|
+
|
115
|
+
rb_scan_args(argc, argv, "11:", &path, &mode, &opt);
|
116
|
+
|
117
|
+
/* check mode */
|
118
|
+
if (NIL_P(mode) || mode == sym_read)
|
119
|
+
data->for_write = FALSE;
|
120
|
+
else if (mode == sym_write)
|
121
|
+
data->for_write = TRUE;
|
122
|
+
else
|
123
|
+
rb_raise(rb_eArgError, "coreaudio: mode should be :read or :write");
|
124
|
+
|
125
|
+
/* parse options */
|
126
|
+
if (NIL_P(rb_hash_aref(opt, sym_rate))) {
|
127
|
+
rate = 44100.0;
|
128
|
+
} else {
|
129
|
+
rate = NUM2DBL(rb_hash_aref(opt, sym_rate));
|
130
|
+
}
|
131
|
+
if (NIL_P(rb_hash_aref(opt, sym_file_rate))) {
|
132
|
+
file_rate = rate;
|
133
|
+
} else {
|
134
|
+
file_rate = NUM2DBL(rb_hash_aref(opt, sym_file_rate));
|
135
|
+
}
|
136
|
+
if (NIL_P(rb_hash_aref(opt, sym_channel))) {
|
137
|
+
channel = 2;
|
138
|
+
} else {
|
139
|
+
channel = NUM2UINT(rb_hash_aref(opt, sym_channel));
|
140
|
+
}
|
141
|
+
if (NIL_P(rb_hash_aref(opt, sym_file_channel))) {
|
142
|
+
file_channel = channel;
|
143
|
+
} else {
|
144
|
+
file_channel = NUM2UINT(rb_hash_aref(opt, sym_file_channel));
|
145
|
+
}
|
146
|
+
|
147
|
+
format = rb_hash_aref(opt, sym_format);
|
148
|
+
if (NIL_P(format))
|
149
|
+
rb_raise(rb_eArgError, "coreaudio: :format option must be specified");
|
150
|
+
|
151
|
+
if (format == sym_wav) {
|
152
|
+
filetype = kAudioFileWAVEType;
|
153
|
+
setASBD(&data->file_desc, file_rate, kAudioFormatLinearPCM,
|
154
|
+
kLinearPCMFormatFlagIsSignedInteger |
|
155
|
+
kAudioFormatFlagIsPacked,
|
156
|
+
file_channel, 16, 1);
|
157
|
+
} else if (format == sym_m4a) {
|
158
|
+
filetype = kAudioFileM4AType;
|
159
|
+
setASBD(&data->file_desc, file_rate, kAudioFormatMPEG4AAC,
|
160
|
+
0, file_channel, 0, 0);
|
161
|
+
} else {
|
162
|
+
volatile VALUE str = rb_inspect(format);
|
163
|
+
RB_GC_GUARD(str);
|
164
|
+
rb_raise(rb_eArgError, "coreaudio: unsupported format (%s)",
|
165
|
+
RSTRING_PTR(str));
|
166
|
+
}
|
167
|
+
|
168
|
+
/* create URL represent the target filepath */
|
169
|
+
outUrl = CFURLCreateFromFileSystemRepresentation(
|
170
|
+
NULL, StringValueCStr(path), (CFIndex)RSTRING_LEN(path), FALSE);
|
171
|
+
|
172
|
+
/* open ExtAudioFile */
|
173
|
+
err = ExtAudioFileCreateWithURL(outUrl, filetype, &data->file_desc,
|
174
|
+
NULL, flags, &data->file);
|
175
|
+
CFRelease(outUrl);
|
176
|
+
outUrl = NULL;
|
177
|
+
if (err != noErr) {
|
178
|
+
rb_raise(rb_eArgError,
|
179
|
+
"coreaudio: file to open ExtAudioFile: %d", (int)err);
|
180
|
+
}
|
181
|
+
|
182
|
+
setASBD(&data->inner_desc, rate, kAudioFormatLinearPCM,
|
183
|
+
kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked,
|
184
|
+
channel, 16, 1);
|
185
|
+
|
186
|
+
err = ExtAudioFileSetProperty(
|
187
|
+
data->file, kExtAudioFileProperty_ClientDataFormat,
|
188
|
+
sizeof(data->inner_desc), &data->inner_desc);
|
189
|
+
if (err != noErr) {
|
190
|
+
ExtAudioFileDispose(data->file);
|
191
|
+
data->file = NULL;
|
192
|
+
rb_raise(rb_eArgError, "coreaudio: fail to set client data format: %d",
|
193
|
+
(int)err);
|
194
|
+
}
|
195
|
+
|
196
|
+
return self;
|
197
|
+
}
|
198
|
+
|
199
|
+
static VALUE
|
200
|
+
ca_audio_file_close(VALUE self)
|
201
|
+
{
|
202
|
+
ca_audio_file_t *data;
|
203
|
+
|
204
|
+
TypedData_Get_Struct(self, ca_audio_file_t, &ca_audio_file_type, data);
|
205
|
+
if (data->file) {
|
206
|
+
ExtAudioFileDispose(data->file);
|
207
|
+
data->file = NULL;
|
208
|
+
}
|
209
|
+
|
210
|
+
return self;
|
211
|
+
}
|
212
|
+
|
213
|
+
static VALUE
|
214
|
+
ca_audio_file_write(VALUE self, VALUE data)
|
215
|
+
{
|
216
|
+
ca_audio_file_t *file;
|
217
|
+
short *buf;
|
218
|
+
AudioBufferList buf_list;
|
219
|
+
UInt32 frames;
|
220
|
+
size_t alloc_size;
|
221
|
+
OSStatus err = noErr;
|
222
|
+
int i;
|
223
|
+
|
224
|
+
if (!RB_TYPE_P(data, T_ARRAY))
|
225
|
+
rb_raise(rb_eArgError, "coreaudio: audio buffer must be an array");
|
226
|
+
|
227
|
+
TypedData_Get_Struct(self, ca_audio_file_t, &ca_audio_file_type, file);
|
228
|
+
|
229
|
+
frames = RARRAY_LEN(data) / file->inner_desc.mChannelsPerFrame;
|
230
|
+
alloc_size = (file->inner_desc.mBitsPerChannel/8) * RARRAY_LEN(data);
|
231
|
+
|
232
|
+
/* prepare interleaved audio buffer */
|
233
|
+
buf_list.mNumberBuffers = 1;
|
234
|
+
buf_list.mBuffers[0].mNumberChannels = file->inner_desc.mChannelsPerFrame;
|
235
|
+
buf_list.mBuffers[0].mDataByteSize = alloc_size;
|
236
|
+
buf_list.mBuffers[0].mData = xmalloc(alloc_size);
|
237
|
+
buf = buf_list.mBuffers[0].mData;
|
238
|
+
|
239
|
+
for (i = 0; i < RARRAY_LEN(data); i++) {
|
240
|
+
buf[i] = (short)NUM2INT(RARRAY_PTR(data)[i]);
|
241
|
+
}
|
242
|
+
|
243
|
+
err = ExtAudioFileWrite(file->file, frames, &buf_list);
|
244
|
+
|
245
|
+
xfree(buf);
|
246
|
+
|
247
|
+
if (err != noErr) {
|
248
|
+
rb_raise(rb_eRuntimeError,
|
249
|
+
"coreaudio: ExtAudioFileWrite() fails: %d", (int)err);
|
250
|
+
}
|
251
|
+
|
252
|
+
return self;
|
253
|
+
}
|
254
|
+
|
255
|
+
void
|
256
|
+
Init_coreaudio_audiofile(void)
|
257
|
+
{
|
258
|
+
sym_read = ID2SYM(rb_intern("read"));
|
259
|
+
sym_write = ID2SYM(rb_intern("write"));
|
260
|
+
sym_format = ID2SYM(rb_intern("format"));
|
261
|
+
sym_rate = ID2SYM(rb_intern("rate"));
|
262
|
+
sym_file_rate = ID2SYM(rb_intern("file_rate"));
|
263
|
+
sym_channel = ID2SYM(rb_intern("channel"));
|
264
|
+
sym_file_channel = ID2SYM(rb_intern("file_channel"));
|
265
|
+
sym_wav = ID2SYM(rb_intern("wav"));
|
266
|
+
sym_m4a = ID2SYM(rb_intern("m4a"));
|
267
|
+
|
268
|
+
rb_cAudioFile = rb_define_class_under(rb_mCoreAudio, "AudioFile",
|
269
|
+
rb_cObject);
|
270
|
+
rb_global_variable(&rb_cAudioFile);
|
271
|
+
|
272
|
+
rb_define_alloc_func(rb_cAudioFile, ca_audio_file_alloc);
|
273
|
+
rb_define_method(rb_cAudioFile, "initialize", ca_audio_file_initialize, -1);
|
274
|
+
rb_define_method(rb_cAudioFile, "close", ca_audio_file_close, 0);
|
275
|
+
rb_define_method(rb_cAudioFile, "write", ca_audio_file_write, 1);
|
276
|
+
}
|