ao 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,133 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <errno.h>
5
+ #include "cao.h"
6
+
7
+ VALUE cAudio;
8
+ VALUE cAO_Info;
9
+ VALUE cAO_eUnknownError;
10
+
11
+ /*
12
+ 引数に設定されたサンプルフォーマットをao_sample_format構造体に設定する。
13
+ 成功したらao_sample_format構造体へのポインタを返す。
14
+ */
15
+ ao_sample_format *
16
+ set_format(VALUE bits, VALUE rate, VALUE channels,
17
+ VALUE byte_format, VALUE matrix)
18
+ {
19
+ ao_sample_format *format;
20
+ size_t len;
21
+
22
+ if ((format = malloc(sizeof(ao_sample_format))) == NULL){
23
+ rb_raise(cAO_eUnknownError, "memory allocation failure.");
24
+ }
25
+ Check_Type(bits, T_FIXNUM);
26
+ Check_Type(rate, T_FIXNUM);
27
+ Check_Type(channels, T_FIXNUM);
28
+ Check_Type(byte_format, T_FIXNUM);
29
+ format->bits = FIX2INT(bits);
30
+ format->rate = FIX2INT(rate);
31
+ format->channels = FIX2INT(channels);
32
+ format->byte_format = FIX2INT(byte_format);
33
+ switch(TYPE(matrix)){
34
+ case T_STRING:
35
+ if ((format->matrix = strndup(StringValuePtr(matrix),
36
+ RSTRING_LENINT(matrix))) == NULL){
37
+ rb_raise(cAO_eAOError, "Memory allocation failure.");
38
+ }
39
+ break;
40
+ case T_NIL:
41
+ format->matrix = NULL;
42
+ break;
43
+ default:
44
+ Check_Type(matrix, T_STRING);
45
+ break;
46
+ }
47
+ return format;
48
+ }
49
+
50
+ /*
51
+ */
52
+ void
53
+ free_format(ao_sample_format *format)
54
+ {
55
+ if (format == NULL){ return; }
56
+ if (format->matrix != NULL){
57
+ free(format->matrix);
58
+ }
59
+ free(format);
60
+ return;
61
+ }
62
+
63
+ /*
64
+ Rubyの配列からデバイスオプションを読み込む。
65
+ 配列ではなくnilであった場合はNULLを返す。
66
+ */
67
+ ao_option *
68
+ set_option(VALUE a_options)
69
+ {
70
+ ao_option *option = NULL;
71
+ int pos, result;
72
+ VALUE element, key, value;
73
+
74
+ switch(TYPE(a_options)){
75
+ case T_ARRAY:
76
+ for (pos=0; pos<RARRAY_LEN(a_options); pos++){
77
+ element = rb_ary_entry(a_options, pos);
78
+ Check_Type(element, T_ARRAY);
79
+ key = rb_ary_entry(element, 0);
80
+ value = rb_ary_entry(element, 1);
81
+ Check_Type(key, T_STRING);
82
+ Check_Type(value, T_STRING);
83
+ result = ao_append_option(&option,
84
+ StringValuePtr(key),
85
+ StringValuePtr(value));
86
+ if (result == 0){
87
+ rb_raise(cAO_eUnknownError, "memory allocation failure.");
88
+ }
89
+ }
90
+ break;
91
+ case T_NIL:
92
+ break;
93
+ default:
94
+ rb_raise(cAO_eBadOption, "Unsupported option type.");
95
+ break;
96
+ }
97
+ return option;
98
+ }
99
+
100
+
101
+ /*
102
+ * call-seq: ao.append_global_option(key, value)
103
+ *
104
+ * libao全体で参照されるオプションを設定する。
105
+ * オプションはKey-Value形式で設定する。
106
+ * 各ドライバ毎のオプションについては下記を参照。
107
+ * http://xiph.org/ao/doc/drivers.html
108
+ *
109
+ * [arg1] key(String)
110
+ * [arg2] value(String)
111
+ * [return] true
112
+ */
113
+ static VALUE
114
+ rao_append_global_option(VALUE obj,
115
+ VALUE key, VALUE value)
116
+ {
117
+ int result;
118
+
119
+ Check_Type(key, T_STRING);
120
+ Check_Type(value, T_STRING);
121
+ result = ao_append_global_option(StringValuePtr(key),
122
+ StringValuePtr(value));
123
+ if (result == 0){
124
+ rb_raise(cAO_eUnknownError, "memory allocation failure.");
125
+ }
126
+ return Qtrue;
127
+ }
128
+
129
+ void
130
+ init_option(void)
131
+ {
132
+ rb_define_method(cAO_Info, "append_global_option", rao_append_global_option, 2);
133
+ }
@@ -0,0 +1,309 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <errno.h>
5
+ #include "cao.h"
6
+
7
+ VALUE cAudio;
8
+ VALUE cAO_Live;
9
+ VALUE cAO_File;
10
+ VALUE cAO_DeviceData;
11
+ VALUE cAO_eAOError;
12
+ VALUE cAO_eDeviceError;
13
+ VALUE cAO_eUnknownError;
14
+
15
+ VALUE cAO_eNoDriver;
16
+ VALUE cAO_eNotFile;
17
+ VALUE cAO_eNotLive;
18
+ VALUE cAO_eBadOption;
19
+ VALUE cAO_eDriverError;
20
+
21
+ VALUE cAO_eFileError;
22
+ VALUE cAO_eFileExists;
23
+ VALUE cAO_eBadFormat;
24
+
25
+ /*
26
+ * call-seq: Audio::LiveOutput.new(driver_id, bits, rate, channels, byte_format, matrix, options)
27
+ *
28
+ * オーディオ出力デバイスを開く。
29
+ * 引数に指定するmatrixとoptionについては以下を参照。
30
+ * [matrix] http://xiph.org/ao/doc/ao_sample_format.html
31
+ * [option] http://xiph.org/ao/doc/drivers.html
32
+ * optionはKey-Valueを要素に含む2次元配列を設定する。
33
+ * 特に設定が必要なければnilで構わない。
34
+ * ex) ALSAドライバのデバイスをhw:1に設定する場合は [["dev", "hw:1"]] を渡す。
35
+ *
36
+ * [arg1] DriverID
37
+ * [arg2] bits(Fixnum)
38
+ * [arg3] rate(Fixnum)
39
+ * [arg4] channels(fixnum)
40
+ * [arg5] byte_format(fixnum)
41
+ * [arg6] matrix(String or nil)
42
+ * [arg7] options(Array or nil)
43
+ * [return] self
44
+ */
45
+ static VALUE
46
+ rao_open_live(VALUE obj, VALUE driver_id,
47
+ VALUE bits, VALUE rate, VALUE channels,
48
+ VALUE byte_format, VALUE matrix,
49
+ VALUE a_options)
50
+ {
51
+ ao_device *dev;
52
+ ao_sample_format *format;
53
+ ao_option *option;
54
+ dev_data *devdat;
55
+ VALUE rdev;
56
+
57
+ if (rb_iv_get(obj, "@device") != Qnil){
58
+ Data_Get_Struct(rb_iv_get(obj, "@device"),
59
+ dev_data, devdat);
60
+ rb_raise(cAO_eDeviceError,
61
+ "Device is already open.\n");
62
+ }
63
+
64
+ Check_Type(driver_id, T_FIXNUM);
65
+ format = set_format(bits, rate, channels, byte_format, matrix);
66
+ option = set_option(a_options);
67
+ dev = ao_open_live(FIX2INT(driver_id), format, option);
68
+ if (dev == NULL){
69
+ free_format(format);
70
+ ao_free_options(option);
71
+ switch(errno){
72
+ case AO_ENODRIVER:
73
+ rb_raise(cAO_eNoDriver,
74
+ "No driver corresponds - %s",
75
+ strerror(errno));
76
+ break;
77
+ case AO_ENOTLIVE:
78
+ rb_raise(cAO_eNotLive,
79
+ "This driver is not a live output device - %s",
80
+ strerror(errno));
81
+ break;
82
+ case AO_EBADOPTION:
83
+ rb_raise(cAO_eBadOption,
84
+ "A valid option key has an invalid value - %s",
85
+ strerror(errno));
86
+ break;
87
+ case AO_EOPENDEVICE:
88
+ rb_raise(cAO_eDeviceError,
89
+ "Cannot open the device - %s",
90
+ strerror(errno));
91
+ break;
92
+ case AO_EFAIL:
93
+ rb_raise(cAO_eUnknownError,
94
+ "Any other cause of failure - %s",
95
+ strerror(errno));
96
+ break;
97
+ default:
98
+ rb_raise(cAO_eUnknownError,
99
+ "Unknown error - %s",
100
+ strerror(errno));
101
+ }
102
+ }
103
+ if ((devdat = append_device(dev, format, option)) == NULL){
104
+ rb_raise(cAO_eUnknownError,
105
+ "memory allocation failure - %s",
106
+ strerror(errno));
107
+ }
108
+
109
+ rdev = Data_Wrap_Struct(cAO_DeviceData, 0,
110
+ remove_device, devdat);
111
+ rb_iv_set(obj, "@device", rdev);
112
+ rb_ary_push(rb_cv_get(cAO_Live, "@@devices"), rdev);
113
+
114
+ return obj;
115
+ }
116
+
117
+ /*
118
+ * call-seq: Audio::FileOutput.new(driver_id, filepath, overwrite, bits, rate, channels, byte_format, matrix, options)
119
+ *
120
+ * ファイルを開く。引数に指定するmatrixとoptionについては以下を参照。
121
+ * [matrix] http://xiph.org/ao/doc/ao_sample_format.html
122
+ * [option] http://xiph.org/ao/doc/drivers.html
123
+ * optionはKey-Valueを要素に含む2次元配列を設定する。
124
+ * 特に設定が必要なければnilで構わない。
125
+ * ex) RAWドライバの出力エンディアンをビッグエンディアンに設定する場合は [["byteirder", "big"]] を渡す。
126
+ *
127
+ * [arg1] DriverID(Fixnum)
128
+ * [arg2] filepath(String)
129
+ * [arg3] overwrite?(true or false)
130
+ * [arg4] bits(Fixnum)
131
+ * [arg5] rate(Fixnum)
132
+ * [arg6] channels(fixnum)
133
+ * [arg7] byte_format(fixnum)
134
+ * [arg8] matrix(String or nil)
135
+ * [arg9] options(Array or nil)
136
+ * [return] self
137
+ */
138
+ static VALUE
139
+ rao_open_file(VALUE obj, VALUE driver_id,
140
+ VALUE filename, VALUE overwrite,
141
+ VALUE bits, VALUE rate, VALUE channels,
142
+ VALUE byte_format, VALUE matrix,
143
+ VALUE a_options)
144
+ {
145
+ dev_data *devdat;
146
+ ao_device *dev;
147
+ ao_sample_format *format;
148
+ ao_option *option;
149
+ int overwrite_int = 0;
150
+ VALUE rdev;
151
+
152
+
153
+ if (rb_iv_get(obj, "@device") != Qnil){
154
+ Data_Get_Struct(rb_iv_get(obj, "@device"),
155
+ dev_data, devdat);
156
+ rb_raise(cAO_eDeviceError,
157
+ "Device is already open.\n");
158
+ }
159
+
160
+ Check_Type(filename, T_STRING);
161
+ Check_Type(driver_id, T_FIXNUM);
162
+ if (TYPE(overwrite) == T_TRUE){
163
+ overwrite_int = 1;
164
+ }
165
+ format = set_format(bits, rate, channels, byte_format, matrix);
166
+ option = set_option(a_options);
167
+ dev = ao_open_file(FIX2INT(driver_id), StringValuePtr(filename),
168
+ overwrite_int, format, option);
169
+ if (dev == NULL){
170
+ free_format(format);
171
+ ao_free_options(option);
172
+ switch(errno){
173
+ case AO_ENODRIVER:
174
+ rb_raise(cAO_eNoDriver,
175
+ "No driver corresponds - %s",
176
+ strerror(errno));
177
+ break;
178
+ case AO_ENOTFILE:
179
+ rb_raise(cAO_eNotFile,
180
+ "This driver is not a file output device - %s",
181
+ strerror(errno));
182
+ break;
183
+ case AO_EBADOPTION:
184
+ rb_raise(cAO_eBadOption,
185
+ "A valid option key has an invalid value - %s",
186
+ strerror(errno));
187
+ break;
188
+ case AO_EOPENFILE:
189
+ rb_raise(cAO_eFileError,
190
+ "Cannot open the device - %s",
191
+ strerror(errno));
192
+ break;
193
+ case AO_EFILEEXISTS:
194
+ rb_raise(cAO_eFileExists,
195
+ "The file already exists - %s",
196
+ strerror(errno));
197
+ case AO_EFAIL:
198
+ rb_raise(cAO_eUnknownError,
199
+ "Any other cause of failure - %s",
200
+ strerror(errno));
201
+ break;
202
+ default:
203
+ rb_raise(cAO_eUnknownError,
204
+ "Unknown error - %s",
205
+ strerror(errno));
206
+ }
207
+ }
208
+ if ((devdat = append_device(dev, format, option)) == NULL){
209
+ rb_raise(cAO_eUnknownError,
210
+ "memory allocation failure - %s",
211
+ strerror(errno));
212
+ }
213
+
214
+ rdev = Data_Wrap_Struct(cAO_DeviceData, 0,
215
+ remove_device, devdat);
216
+ rb_iv_set(obj, "@device", rdev);
217
+ rb_ary_push(rb_cv_get(cAO_File, "@@devices"), rdev);
218
+
219
+ return obj;
220
+ }
221
+
222
+ static VALUE
223
+ rao_close(VALUE obj)
224
+ {
225
+ dev_data *devdata;
226
+
227
+ if (rb_iv_get(obj, "@device") == Qnil){
228
+ return Qfalse;
229
+ }
230
+
231
+ Data_Get_Struct(rb_iv_get(obj, "@device"),
232
+ dev_data, devdata);
233
+ close_device(devdata);
234
+ rb_ary_delete(rb_cv_get(cAO_Live, "@@devices"), rb_intern("@device"));
235
+ rb_iv_set(obj, "@device", Qnil);
236
+ return Qtrue;
237
+ }
238
+
239
+ /*
240
+ * libaoを終了する。shutdownの前に開いている全てのデバイスを
241
+ * closeしておかなければならない。
242
+ *
243
+ * [return] nil
244
+ */
245
+ void
246
+ rao_shutdown(VALUE obj){
247
+ VALUE rdev;
248
+ dev_data *devdata;
249
+
250
+ rb_gc_start();
251
+ while ((rdev = rb_ary_pop(rb_cv_get(cAO_Live, "@@devices"))) != Qnil){
252
+ Data_Get_Struct(rdev, dev_data, devdata);
253
+ close_device(devdata);
254
+ }
255
+ ao_shutdown();
256
+ return;
257
+ }
258
+
259
+ void
260
+ Init_outputc(void)
261
+ {
262
+ ao_initialize();
263
+ /*
264
+ * Document-class: Audio
265
+ *
266
+ * Ruby-AOの基礎となるクラス。
267
+ */
268
+ cAudio = rb_define_class("Audio", rb_cObject);
269
+
270
+ /*
271
+ * Document-class: Audio::LiveOutput
272
+ *
273
+ * オーディオデバイス出力機能をサポートするクラス。
274
+ */
275
+ cAO_Live = rb_define_class_under(cAudio, "LiveOutputC", rb_cObject);
276
+ rb_cv_set(cAO_Live, "@@devices", rb_ary_new());
277
+
278
+ /*
279
+ * Document-class: Audio::BasicOutput::DeviceData
280
+ *
281
+ * 開いたデバイスに関する基本的な情報を保持するクラス。
282
+ * ruby側から操作はしない。
283
+ */
284
+ cAO_DeviceData = rb_define_class_under(cAO_Live, "DeviceData", rb_cData);
285
+
286
+ /* library initialize & shutdown */
287
+ rb_define_private_method(cAO_Live, "initialize", rao_open_live, 7);
288
+
289
+ /* device setup */
290
+ rb_define_method(cAO_Live, "close", rao_close, 0);
291
+ rb_define_method(cAO_Live, "play", raodev_play, 1);
292
+ rb_define_method(cAO_Live, "closed?", raodev_closed, 0);
293
+
294
+ /*
295
+ * Document-class: Audio::FileOutput
296
+ *
297
+ * オーディオファイル出力機能をサポートするクラス。
298
+ */
299
+ cAO_File = rb_define_class_under(cAudio, "FileOutputC", cAO_Live);
300
+ rb_define_private_method(cAO_File, "initialize", rao_open_file, 9);
301
+
302
+ init_exception();
303
+ init_info();
304
+ init_constant();
305
+ init_option();
306
+
307
+ /* Shutdown */
308
+ rb_set_end_proc(rao_shutdown, (VALUE)NULL);
309
+ }
@@ -0,0 +1,45 @@
1
+ #-*- coding: utf-8 -*-
2
+
3
+ require 'audio/outputc'
4
+
5
+ class Audio::LiveOutput < Audio::LiveOutputC
6
+ def initialize(
7
+ driver_id:
8
+ Audio::Info.default_driver_id,
9
+ bits: 16, rate: 44100, channels: 2,
10
+ byte_format:
11
+ Audio::Info::FMT_NATIVE,
12
+ matrix: nil, options: nil)
13
+ ao = super(driver_id, bits, rate, channels,
14
+ byte_format, matrix, options)
15
+ if block_given?
16
+ begin
17
+ yield ao
18
+ ensure
19
+ ao.close
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ class Audio::FileOutput < Audio::FileOutputC
26
+ def initialize(
27
+ driver_id:
28
+ Audio::Info.driver_id('wav'),
29
+ filename:, overwrite: false,
30
+ bits: 16, rate: 44100, channels: 2,
31
+ byte_format:
32
+ Audio::Info::FMT_NATIVE,
33
+ matrix: nil, options: nil)
34
+ ao = super(driver_id, filename, overwrite,
35
+ bits, rate, channels, byte_format,
36
+ matrix, options)
37
+ if block_given?
38
+ begin
39
+ yield ao
40
+ ensure
41
+ ao.close
42
+ end
43
+ end
44
+ end
45
+ end