ao 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ #-*- coding: utf-8 -*-
3
+
4
+ #
5
+ # RAW to WAV converter
6
+ # (bit: 16bit, rate: 44100Hz, channel: 2ch,
7
+ # endian: native, matrix: default, option: default)
8
+ #
9
+
10
+ require 'audio/output'
11
+
12
+ unless ARGV[1]
13
+ puts('ex) ./raw2wav.rb input.raw output.wav')
14
+ exit(1)
15
+ end
16
+
17
+ input = ARGV[0]
18
+ output = ARGV[1]
19
+ ao =
20
+ raise unless File.file?(input)
21
+ Audio::FileOutput.new(driver_id: Audio::Info.driver_id('wav'),
22
+ filename: output){|ao|
23
+ File.open(input){|f|
24
+ while buffer = f.read(4096)
25
+ ao.play(buffer)
26
+ end
27
+ }
28
+ }
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ #-*- coding: utf-8 -*-
3
+
4
+ #
5
+ # Simple RAW Audio Player
6
+ # (bits: 16bit, rate: 44100Hz, channels: 2ch,
7
+ # byte_format: Little endian,
8
+ # matrix: default, option: default)
9
+ #
10
+
11
+ require 'audio/output'
12
+
13
+ ARGV.each{|file|
14
+ if File.file?(file)
15
+ Audio::LiveOutput.new(bits: 16, rate: 44100, channels: 2,
16
+ byte_format: Audio::Info::FMT_LITTLE){|ao|
17
+ File.open(file){|f|
18
+ while buffer = f.read(4096)
19
+ ao.play(buffer)
20
+ end
21
+ }
22
+ }
23
+ end
24
+ }
@@ -0,0 +1,61 @@
1
+ #ifndef CAO_H
2
+ #define CAO_H
3
+
4
+ #include <ruby.h>
5
+ #include <ruby/thread.h>
6
+ #include <ao/ao.h>
7
+ #include <ao/os_types.h>
8
+
9
+ extern VALUE cAudio;
10
+ extern VALUE cAO_Live;
11
+ extern VALUE cAO_File;
12
+ extern VALUE cAO_Info;
13
+ extern VALUE cAO_DeviceData;
14
+ extern VALUE cAO_eAOError;
15
+ extern VALUE cAO_eDeviceError;
16
+ extern VALUE cAO_eUnknownError;
17
+
18
+ extern VALUE cAO_eNoDriver;
19
+ extern VALUE cAO_eNotFile;
20
+ extern VALUE cAO_eNotLive;
21
+ extern VALUE cAO_eBadOption;
22
+ extern VALUE cAO_eDriverError;
23
+
24
+ extern VALUE cAO_eFileError;
25
+ extern VALUE cAO_eFileExists;
26
+ extern VALUE cAO_eBadFormat;
27
+
28
+ typedef struct sample_buffer {
29
+ int bytes;
30
+ char *samples;
31
+ struct sample_buffer *next;
32
+ } sample_buffer;
33
+
34
+ typedef struct dev_data {
35
+ ao_device *device;
36
+ ao_sample_format *format;
37
+ ao_option *option;
38
+ sample_buffer *buffer;
39
+ int playing;
40
+ } dev_data;
41
+
42
+ dev_data * append_device(ao_device *dev,
43
+ ao_sample_format *format,
44
+ ao_option *option);
45
+ void close_device(dev_data *devdat);
46
+ void remove_device(dev_data *devdat);
47
+
48
+ void init_exception(void);
49
+ void init_constant(void);
50
+ ao_sample_format *
51
+ set_format(VALUE bits, VALUE rate, VALUE channels,
52
+ VALUE byte_format, VALUE matrix);
53
+ void free_format(ao_sample_format *format);
54
+ ao_option * set_option(VALUE a_options);
55
+
56
+ VALUE
57
+ raodev_play(VALUE obj, VALUE output_samples);
58
+
59
+ VALUE raodev_close(VALUE obj);
60
+ VALUE raodev_closed(VALUE obj);
61
+ #endif
@@ -0,0 +1,25 @@
1
+ #include "cao.h"
2
+
3
+ VALUE cAudio;
4
+ VALUE cAO_Info;
5
+
6
+ /* constants */
7
+ void
8
+ init_constant(void)
9
+ {
10
+ /* ドライバがLive出力用であることを示す。 */
11
+ rb_define_const(cAO_Info, "TYPE_LIVE", INT2FIX(AO_TYPE_LIVE));
12
+
13
+ /* ドライバがファイル出力用であることを示す。 */
14
+ rb_define_const(cAO_Info, "TYPE_FILE", INT2FIX(AO_TYPE_FILE));
15
+
16
+ /* データのエンディアンがリトルエンディアンであることを示す。 */
17
+ rb_define_const(cAO_Info, "FMT_LITTLE", INT2FIX(AO_FMT_LITTLE));
18
+
19
+ /* データのエンディアンがビッグエンディアンであることを示す。 */
20
+ rb_define_const(cAO_Info, "FMT_BIG", INT2FIX(AO_FMT_BIG));
21
+
22
+ /* データのエンディアンがホストのネイティブ形式であることを示す。 */
23
+ rb_define_const(cAO_Info, "FMT_NATIVE", INT2FIX(AO_FMT_NATIVE));
24
+ return;
25
+ }
@@ -0,0 +1,133 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <unistd.h>
5
+ #include "cao.h"
6
+
7
+ typedef struct play_data {
8
+ ao_device *device;
9
+ char *samples;
10
+ uint_32 bytes;
11
+ int result;
12
+ } play_data;
13
+
14
+ void *nogvl_ao_play(void *playdata){
15
+ play_data *pd = playdata;
16
+ pd->result =
17
+ ao_play(pd->device, pd->samples, pd->bytes);
18
+ return NULL;
19
+ }
20
+
21
+ /*
22
+ * デバイス一覧にデバイスを追加する。
23
+ * 追加した構造体へのポインタを返す。
24
+ * 割当に失敗した場合は-1を返す。
25
+ */
26
+ dev_data *
27
+ append_device(ao_device *dev, ao_sample_format *format,
28
+ ao_option *option)
29
+ {
30
+ dev_data *newdev;
31
+ newdev = ALLOC(dev_data);
32
+ newdev->device = dev;
33
+ newdev->format = format;
34
+ newdev->option = option;
35
+ newdev->playing = 0;
36
+ newdev->buffer = NULL;
37
+ return newdev;
38
+ }
39
+
40
+ /*
41
+ * devdata構造体の持つデバイスを全て閉じ、
42
+ * またオプションとフォーマットの情報も削除する。
43
+ * メンバ変数にはNULLを設定する。
44
+ * devdata構造体自体は開放しない。
45
+ * (rubyのGCによって実行されるremove_device関数に任せるため)
46
+ */
47
+ void
48
+ close_device(dev_data *devdat){
49
+ if (devdat->device != NULL){
50
+ ao_close(devdat->device);
51
+ }
52
+ if (devdat->option != NULL){
53
+ ao_free_options(devdat->option);
54
+ }
55
+ if (devdat->format != NULL){
56
+ free_format(devdat->format);
57
+ }
58
+ devdat->device = NULL;
59
+ devdat->option = NULL;
60
+ devdat->format = NULL;
61
+ devdat->buffer = NULL;
62
+ return;
63
+ }
64
+
65
+ /*
66
+ * GCが実行された時にデバイスを閉じ、構造体を開放する。
67
+ * この関数はopen_live()とopen_file()にてdev_data構造体をWrapする時に
68
+ * 構造体free用関数として渡す(rubyのGCにて呼ばれるよう設定する)
69
+ * Data_Wrap_Struct(cAO_DeviceData, 0, remove_device, devdat);
70
+ */
71
+ void
72
+ remove_device(dev_data *devdat)
73
+ {
74
+ close_device(devdat);
75
+ free(devdat);
76
+ return;
77
+ }
78
+
79
+ /*
80
+ * call-seq: ao.play(output_samples)
81
+ *
82
+ * 受け取ったサンプルを再生する。
83
+ * (デバイスがファイル出力の場合はファイルに書き出す)
84
+ * 一度に渡せる量はunsigned int(32bit)の範囲まで。
85
+ *
86
+ * [arg1] buffer(String)
87
+ * [return] Fixnum
88
+ */
89
+ VALUE
90
+ raodev_play(VALUE obj, VALUE output_samples)
91
+ {
92
+ dev_data *devdata;
93
+ play_data playdata;
94
+
95
+ Check_Type(output_samples, T_STRING);
96
+ Data_Get_Struct(rb_ivar_get(obj, rb_intern("@device")),
97
+ dev_data, devdata);
98
+ playdata.device = devdata->device;
99
+ playdata.bytes = RSTRING_LENINT(output_samples);
100
+ if ((playdata.samples = malloc(playdata.bytes)) == NULL){
101
+ rb_raise(cAO_eAOError, "Memory allocation failure.");
102
+ }
103
+ memcpy(playdata.samples, StringValuePtr(output_samples), playdata.bytes);
104
+ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
105
+ rb_thread_call_without_gvl2(nogvl_ao_play, &playdata, NULL, NULL);
106
+ /*rb_thread_call_without_gvl2((rb_blocking_function_t *)nogvl_ao_play, &playdata, NULL, NULL);*/
107
+ #else
108
+ nogvl_ao_play(&playdata);
109
+ #endif
110
+ free(playdata.samples);
111
+ if (playdata.result == 0){
112
+ rb_raise(cAO_eDeviceError, "Device should be closed.");
113
+ }
114
+ return INT2FIX(playdata.result);
115
+ }
116
+
117
+ /*
118
+ * call-seq: ao.closed?
119
+ *
120
+ * デバイスが既に閉じられているか確認する。
121
+ * [return] true or false
122
+ */
123
+ VALUE
124
+ raodev_closed(VALUE obj)
125
+ {
126
+ dev_data *devdata;
127
+ Data_Get_Struct(rb_ivar_get(obj, rb_intern("@device")),
128
+ dev_data, devdata);
129
+ if (devdata->device == NULL){
130
+ return Qtrue;
131
+ }
132
+ return Qfalse;
133
+ }
@@ -0,0 +1,110 @@
1
+ #include "cao.h"
2
+
3
+ VALUE cAudio;
4
+ VALUE cAO_Live;
5
+ VALUE cAO_File;
6
+ VALUE cAO_eAOError;
7
+ VALUE cAO_eNoDriver;
8
+ VALUE cAO_eNotFile;
9
+ VALUE cAO_eNotLive;
10
+ VALUE cAO_eBadOption;
11
+ VALUE cAO_eDeviceError;
12
+ VALUE cAO_eDriverError;
13
+ VALUE cAO_eFileError;
14
+ VALUE cAO_eFileExists;
15
+ VALUE cAO_eBadFormat;
16
+ VALUE cAO_eUnknownError;
17
+
18
+ void init_exception(void)
19
+ {
20
+ /*
21
+ * Document-class: Audio::LiveOutput::Error
22
+ *
23
+ * Audio::BasicOutputに関するエラーは
24
+ * Audio::BasicOutput::Errorを継承する。
25
+ */
26
+ cAO_eAOError =
27
+ rb_define_class_under(cAO_Live, "Error", rb_eStandardError);
28
+
29
+ /*
30
+ * Document-class: Audio::LiveOutput::NoDriver
31
+ *
32
+ * ドライバが見つからない時に発生する。
33
+ */
34
+ cAO_eNoDriver =
35
+ rb_define_class_under(cAO_Live, "NoDriver", cAO_eAOError);
36
+
37
+ /*
38
+ * Document-class: Audio::LiveOutput::NotFile
39
+ *
40
+ * ドライバがファイル出力用のものではない時に発生する。
41
+ */
42
+ cAO_eNotFile =
43
+ rb_define_class_under(cAO_Live, "NotFile", cAO_eAOError);
44
+
45
+ /*
46
+ * Document-class: Audio::LiveOutput::NotLive
47
+ *
48
+ * ドライバがデバイス出力用のものではない時に発生する。
49
+ */
50
+ cAO_eNotLive =
51
+ rb_define_class_under(cAO_Live, "NotLive", cAO_eAOError);
52
+
53
+ /*
54
+ * Document-class: Audio::LiveOutput::BadOption
55
+ *
56
+ * ドライバに設定したオプションの値が不正である時に発生する。
57
+ */
58
+ cAO_eBadOption =
59
+ rb_define_class_under(cAO_Live, "BadOption", cAO_eAOError);
60
+
61
+ /*
62
+ * Document-class: Audio::LiveOutput::DriverError
63
+ *
64
+ * ドライバに関するエラー。
65
+ */
66
+ cAO_eDriverError =
67
+ rb_define_class_under(cAO_Live, "DriverError", cAO_eAOError);
68
+
69
+ /*
70
+ * Document-class: Audio::LiveOutput::DeviceError
71
+ *
72
+ * デバイスに関するエラー。
73
+ */
74
+ cAO_eDeviceError =
75
+ rb_define_class_under(cAO_Live, "DeviceError", cAO_eAOError);
76
+
77
+ /*
78
+ * Document-class: Audio::FileOutput::FileError
79
+ *
80
+ * ファイル出力に関するエラー。
81
+ */
82
+ cAO_eFileError =
83
+ rb_define_class_under(cAO_File, "FileError", cAO_eAOError);
84
+
85
+ /*
86
+ * Document-class: Audio::FileOutput::FileExists
87
+ *
88
+ * ファイル出力時の上書き禁止が設定されている時に
89
+ * 既にファイルが存在する場合に発生する。
90
+ */
91
+ cAO_eFileExists =
92
+ rb_define_class_under(cAO_File, "FileExists", cAO_eAOError);
93
+
94
+ /*
95
+ * Document-class: Audio::FileOutput::Badformat
96
+ *
97
+ * オーディオ出力フォーマットが不正である場合発生する。
98
+ */
99
+ cAO_eBadFormat =
100
+ rb_define_class_under(cAO_File, "BadFormat", cAO_eAOError);
101
+
102
+ /*
103
+ * Document-class: Audio::LiveOutput::UnknownError
104
+ *
105
+ * 不明なエラー。
106
+ */
107
+ cAO_eUnknownError =
108
+ rb_define_class_under(cAO_Live, "UnknownError", cAO_eAOError);
109
+ return;
110
+ }
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ #-*- coding: utf-8 -*-
3
+
4
+ require 'mkmf'
5
+
6
+ dir_config('audio')
7
+ have_header('ao/ao.h')
8
+ have_header('ao/os_types.h')
9
+ have_header('ao/plugin.h')
10
+ have_library('ao')
11
+ have_func('rb_thread_call_without_gvl')
12
+ create_makefile('audio/outputc')
@@ -0,0 +1,187 @@
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_Info;
10
+ VALUE cAO_eDriverError;
11
+ VALUE cAO_eBadFormat;
12
+
13
+ /*
14
+ ao_info構造体をRubyのHashに変換し返す。
15
+ */
16
+ static VALUE
17
+ ao_info2hash(ao_info *info)
18
+ {
19
+ int opt_count;
20
+ VALUE ihash, optary;
21
+
22
+ ihash = rb_hash_new();
23
+ optary = rb_ary_new();
24
+ rb_hash_aset(ihash, ID2SYM(rb_intern("type")), INT2FIX(info->type));
25
+ rb_hash_aset(ihash, ID2SYM(rb_intern("name")), rb_str_new2(info->name));
26
+ rb_hash_aset(ihash, ID2SYM(rb_intern("short_name")), rb_str_new2(info->short_name));
27
+ rb_hash_aset(ihash, ID2SYM(rb_intern("author")), rb_str_new2(info->author));
28
+ rb_hash_aset(ihash, ID2SYM(rb_intern("comment")), rb_str_new2(info->comment));
29
+ rb_hash_aset(ihash, ID2SYM(rb_intern("byte_format")), INT2FIX(info->preferred_byte_format));
30
+ rb_hash_aset(ihash, ID2SYM(rb_intern("priority")), INT2FIX(info->priority));
31
+ rb_hash_aset(ihash, ID2SYM(rb_intern("opt_count")), INT2FIX(info->option_count));
32
+ for(opt_count=0; opt_count<info->option_count; opt_count++){
33
+ rb_ary_push(optary, rb_str_new2(info->options[opt_count]));
34
+ }
35
+ rb_hash_aset(ihash, ID2SYM(rb_intern("options")), optary);
36
+ return ihash;
37
+ }
38
+
39
+ /* driver information */
40
+ /*
41
+ * call-seq: Audio::Info.driver_id(short_name)
42
+ *
43
+ * short_nameを元にDriverIDを検索する。
44
+ * 見つからなかった場合はnilを返す。
45
+ *
46
+ * [arg1] short_name(String)
47
+ * [return] DriverID or nil
48
+ */
49
+ static VALUE
50
+ rao_driver_id(VALUE obj, VALUE short_name)
51
+ {
52
+ int driver_id;
53
+
54
+ Check_Type(short_name, T_STRING);
55
+ driver_id = ao_driver_id(StringValuePtr(short_name));
56
+ if (driver_id < 0){
57
+ return Qnil;
58
+ }
59
+ return INT2FIX(driver_id);
60
+ }
61
+
62
+ /*
63
+ * call-seq: Audio::Info.default_driver_id
64
+ *
65
+ * デフォルトのDriverIDを返す。
66
+ * [return] DriverID
67
+ */
68
+ static VALUE
69
+ rao_default_driver_id(VALUE obj)
70
+ {
71
+ int id;
72
+
73
+ id = ao_default_driver_id();
74
+ if (id < 0){
75
+ rb_raise(cAO_eNoDriver,
76
+ "Failure to find a usable audio output device.");
77
+ }
78
+ return INT2FIX(id);
79
+ }
80
+
81
+ /*
82
+ * call-seq: Audio::Info.driver_info(driver_id)
83
+ *
84
+ * ドライバの情報を確認する。
85
+ * ドライバ情報の要素順は以下の通り。
86
+ * [name(String), short_name(String), author(String),
87
+ * comment(String), preferred_byte_format(Fixnum),
88
+ * priority(Fixnum), option_count(Fixnum), options(Array)]
89
+ *
90
+ * [arg1] DriverID
91
+ * [return] Driver Information(Array)
92
+ */
93
+ static VALUE
94
+ rao_driver_info(VALUE obj, VALUE driver_id)
95
+ {
96
+ ao_info *info;
97
+
98
+ Check_Type(driver_id, T_FIXNUM);
99
+ info = ao_driver_info(FIX2INT(driver_id));
100
+ if (info == NULL){
101
+ rb_raise(cAO_eNoDriver,
102
+ "Does not correspond to an actual driver.");
103
+ }
104
+ return ao_info2hash(info);;
105
+ }
106
+
107
+ /*
108
+ * call-seq: Audio::Info.driver_info_list
109
+ *
110
+ * システムのlibaoがサポートしているドライバ一覧を配列で返す。
111
+ * [return] ドライバ一覧の配列
112
+ */
113
+ static VALUE
114
+ rao_driver_info_list(VALUE obj)
115
+ {
116
+ ao_info **info;
117
+ int info_count, driver_count;
118
+ VALUE iary;
119
+
120
+ iary = rb_ary_new();
121
+ info = ao_driver_info_list(&driver_count);
122
+ for (info_count=0; info_count<driver_count;
123
+ info_count++){
124
+ rb_ary_push(iary, ao_info2hash(info[info_count]));
125
+ }
126
+ return iary;
127
+ }
128
+
129
+ /*
130
+ * call-seq: Audio::Info.file_extension(driver_id)
131
+ *
132
+ * ファイル出力時の拡張子の標準を確認する。
133
+ *
134
+ * [arg1] DriverID
135
+ * [return] ext name(String)
136
+ */
137
+ static VALUE
138
+ rao_file_extension(VALUE obj, VALUE driver_id)
139
+ {
140
+ char *ext;
141
+
142
+ Check_Type(driver_id, T_FIXNUM);
143
+ ext = (char *)ao_file_extension(FIX2INT(driver_id));
144
+ if (ext == NULL){
145
+ rb_raise(cAO_eDriverError,
146
+ "This driver has no file extension associated with it or if this driver does not exist.");
147
+ }
148
+ return rb_str_new2(ext);
149
+ }
150
+
151
+ /* miscellaneous */
152
+ /*
153
+ * call-seq: Audio::Info.bigendian?
154
+ *
155
+ * ホストの環境がビッグエンディアンであるか否かを調査する。
156
+ * [return] ビッグエンディアンであればtrue、リトルエンディアンであればfalse
157
+ */
158
+ static VALUE
159
+ rao_is_big_endian(VALUE obj)
160
+ {
161
+ if (ao_is_big_endian() == 1){
162
+ return Qtrue;
163
+ } else {
164
+ return Qfalse;
165
+ }
166
+ }
167
+
168
+ void
169
+ init_info(void)
170
+ {
171
+ /*
172
+ * Document-class: Audio::Info
173
+ *
174
+ * オーディオ出力デバイスに関する情報を取り扱うモジュール。
175
+ */
176
+ cAO_Info = rb_define_module_under(cAudio, "Info");
177
+
178
+ /* driver information */
179
+ rb_define_module_function(cAO_Info, "driver_id", rao_driver_id, 1);
180
+ rb_define_module_function(cAO_Info, "default_driver_id", rao_default_driver_id, 0);
181
+ rb_define_module_function(cAO_Info, "driver_info", rao_driver_info, 1);
182
+ rb_define_module_function(cAO_Info, "driver_info_list", rao_driver_info_list, 0);
183
+ rb_define_module_function(cAO_Info, "file_extension", rao_file_extension, 1);
184
+
185
+ /* miscellaneous */
186
+ rb_define_module_function(cAO_Info, "bigendian?", rao_is_big_endian, 0);
187
+ }