coreaudio 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
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
@@ -0,0 +1,16 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.6.4)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rake (0.9.2.2)
10
+
11
+ PLATFORMS
12
+ ruby
13
+
14
+ DEPENDENCIES
15
+ bundler (~> 1.0.0)
16
+ jeweler (~> 1.6.4)
@@ -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.
@@ -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
+
@@ -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."
@@ -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
+ }