ao 0.0.3

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.
@@ -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
+ }