ao 0.0.4 → 0.1.0
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.
- checksums.yaml +5 -5
- data/Rakefile +14 -0
- data/ext/audio/cao.h +94 -0
- data/ext/{constant.c → audio/constant.c} +0 -0
- data/ext/audio/device.c +202 -0
- data/ext/{exception.c → audio/exception.c} +0 -0
- data/ext/{extconf.rb → audio/extconf.rb} +4 -0
- data/ext/audio/file.c +127 -0
- data/ext/{info.c → audio/info.c} +0 -0
- data/ext/audio/live.c +114 -0
- data/ext/audio/mixer.c +128 -0
- data/ext/{option.c → audio/option.c} +2 -2
- data/ext/audio/output.c +113 -0
- data/ext/audio/queue.c +52 -0
- data/ext/audio/queue.h +6 -0
- data/ext/audio/thread.c +88 -0
- data/ext/audio/thread.h +5 -0
- data/lib/ao.rb +3 -0
- data/lib/audio/output.rb +2 -2
- metadata +32 -20
- data/ext/cao.h +0 -61
- data/ext/device.c +0 -133
- data/ext/output.c +0 -309
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ce2aaa85c50a86ac3467984323d71563ecb1eeab53892a211fee59b67752fd28
|
4
|
+
data.tar.gz: 44126c6129f860e6c5de4076442e5d28300326127d6dd86e39ef28c0903c970a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4228179a5b5c685a30438e530350422a581784abf2311a10d9eeb48046afa27628c7dbdf4159fac8f97f9bdbd8082b53890a2b096c0620ed8fea76c1c772a29
|
7
|
+
data.tar.gz: fe1d53eb6782afa35c1cedf0829fe1f9038f8d12095bac1c20c20221b2116d2074f6233ad17b0dab03759f8980494a73fe4bf17b96e5b73ca9cbb8bec414f1c5
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#-*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rake/extensiontask'
|
4
|
+
|
5
|
+
Rake::ExtensionTask.new('audio') do |ext|
|
6
|
+
ext.name = 'outputc'
|
7
|
+
ext.lib_dir ='lib/audio'
|
8
|
+
ext.cross_compile = true
|
9
|
+
ext.cross_platform = ENV['CROSS_COMPILE'] || 'armv6j-linux-gnueabi'
|
10
|
+
ext.cross_compiling do |gem_spec|
|
11
|
+
gem_spec.post_install_message = "You installed the binary version of this gem!"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
data/ext/audio/cao.h
ADDED
@@ -0,0 +1,94 @@
|
|
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
|
+
#include <pthread.h>
|
9
|
+
|
10
|
+
extern VALUE cAudio;
|
11
|
+
extern VALUE cAO_Live;
|
12
|
+
extern VALUE cAO_File;
|
13
|
+
extern VALUE cAO_Info;
|
14
|
+
extern VALUE cAO_Mixer;
|
15
|
+
extern VALUE cAO_DeviceData;
|
16
|
+
extern VALUE cAO_eAOError;
|
17
|
+
extern VALUE cAO_eDeviceError;
|
18
|
+
extern VALUE cAO_eUnknownError;
|
19
|
+
|
20
|
+
extern VALUE cAO_eNoDriver;
|
21
|
+
extern VALUE cAO_eNotFile;
|
22
|
+
extern VALUE cAO_eNotLive;
|
23
|
+
extern VALUE cAO_eBadOption;
|
24
|
+
extern VALUE cAO_eDriverError;
|
25
|
+
|
26
|
+
extern VALUE cAO_eFileError;
|
27
|
+
extern VALUE cAO_eFileExists;
|
28
|
+
extern VALUE cAO_eBadFormat;
|
29
|
+
|
30
|
+
#define cAO_STATE_EXIT -1
|
31
|
+
#define cAO_STATE_STOP 0
|
32
|
+
#define cAO_STATE_ACTIVE 1
|
33
|
+
#define cAO_STATE_PLAYING 2
|
34
|
+
|
35
|
+
typedef struct queue {
|
36
|
+
void *data;
|
37
|
+
struct queue *next;
|
38
|
+
} queue;
|
39
|
+
|
40
|
+
typedef struct sample_t {
|
41
|
+
uint_32 bytes;
|
42
|
+
char *buffer;
|
43
|
+
} sample_t;
|
44
|
+
|
45
|
+
typedef struct ao_struct {
|
46
|
+
ao_device *device;
|
47
|
+
ao_sample_format *format;
|
48
|
+
ao_option *option;
|
49
|
+
queue *queue;
|
50
|
+
int qsize;
|
51
|
+
pthread_t thread;
|
52
|
+
pthread_mutex_t mutex;
|
53
|
+
pthread_cond_t cond;
|
54
|
+
int status;
|
55
|
+
} ao_struct;
|
56
|
+
|
57
|
+
|
58
|
+
void init_info(void);
|
59
|
+
void init_option(void);
|
60
|
+
void init_mixer(void);
|
61
|
+
ao_struct *init_aos(ao_device *dev,
|
62
|
+
ao_sample_format *format,
|
63
|
+
ao_option *option);
|
64
|
+
void close_device(ao_struct *aos);
|
65
|
+
void remove_device(ao_struct *aos);
|
66
|
+
|
67
|
+
void init_exception(void);
|
68
|
+
void init_constant(void);
|
69
|
+
ao_sample_format *
|
70
|
+
set_format(VALUE bits, VALUE rate, VALUE channels,
|
71
|
+
VALUE byte_format, VALUE matrix);
|
72
|
+
void free_format(ao_sample_format *format);
|
73
|
+
ao_option * set_option(VALUE a_options);
|
74
|
+
|
75
|
+
VALUE
|
76
|
+
raodev_play(VALUE obj, VALUE output_samples);
|
77
|
+
|
78
|
+
VALUE raodev_close(VALUE obj);
|
79
|
+
VALUE raodev_closed(VALUE obj);
|
80
|
+
VALUE raodev_playing(VALUE obj);
|
81
|
+
VALUE raodev_waiting(VALUE obj);
|
82
|
+
|
83
|
+
VALUE
|
84
|
+
rao_open_live(VALUE obj, VALUE driver_id,
|
85
|
+
VALUE bits, VALUE rate, VALUE channels,
|
86
|
+
VALUE byte_format, VALUE matrix,
|
87
|
+
VALUE a_options, VALUE thread);
|
88
|
+
VALUE
|
89
|
+
rao_open_file(VALUE obj, VALUE driver_id,
|
90
|
+
VALUE filename, VALUE overwrite,
|
91
|
+
VALUE bits, VALUE rate, VALUE channels,
|
92
|
+
VALUE byte_format, VALUE matrix,
|
93
|
+
VALUE a_options, VALUE thread);
|
94
|
+
#endif
|
File without changes
|
data/ext/audio/device.c
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <string.h>
|
4
|
+
#include <unistd.h>
|
5
|
+
#include <assert.h>
|
6
|
+
#include <errno.h>
|
7
|
+
#include "cao.h"
|
8
|
+
#include "queue.h"
|
9
|
+
|
10
|
+
typedef struct aos_gvl {
|
11
|
+
ao_struct *aos;
|
12
|
+
sample_t sample;
|
13
|
+
int res;
|
14
|
+
} aos_gvl;
|
15
|
+
|
16
|
+
void *nogvl_ao_play(void *dat){
|
17
|
+
aos_gvl *aosg = (aos_gvl *)dat;
|
18
|
+
|
19
|
+
aosg->res =
|
20
|
+
ao_play(aosg->aos->device,
|
21
|
+
aosg->sample.buffer,
|
22
|
+
aosg->sample.bytes);
|
23
|
+
return NULL;
|
24
|
+
}
|
25
|
+
|
26
|
+
ao_struct *
|
27
|
+
init_aos(ao_device *dev, ao_sample_format *format,
|
28
|
+
ao_option *option){
|
29
|
+
ao_struct *aos;
|
30
|
+
|
31
|
+
aos = ALLOC(ao_struct);
|
32
|
+
aos->device = dev;
|
33
|
+
aos->format = format;
|
34
|
+
aos->option = option;
|
35
|
+
aos->queue = NULL;
|
36
|
+
aos->status = 1;
|
37
|
+
aos->qsize = 0;
|
38
|
+
aos->thread = 0;
|
39
|
+
return aos;
|
40
|
+
}
|
41
|
+
|
42
|
+
/*
|
43
|
+
* call-seq: ao.play(output_samples)
|
44
|
+
*
|
45
|
+
* 受け取ったサンプルを再生する。
|
46
|
+
* threadが有効の場合、受け取ったサンプルを
|
47
|
+
* 再生キューに追加する。
|
48
|
+
* (デバイスがファイル出力の場合はファイルに書き出す)
|
49
|
+
* 一度に渡せる量はunsigned int(32bit)の範囲まで。
|
50
|
+
*
|
51
|
+
* [arg1] buffer(String)
|
52
|
+
* [return] Fixnum
|
53
|
+
*/
|
54
|
+
VALUE
|
55
|
+
raodev_play(VALUE obj, VALUE output_samples)
|
56
|
+
{
|
57
|
+
sample_t *sample;
|
58
|
+
ao_struct *aos;
|
59
|
+
aos_gvl aosg;
|
60
|
+
|
61
|
+
Check_Type(output_samples, T_STRING);
|
62
|
+
if (rb_ivar_get(obj, rb_intern("@device")) == Qnil){
|
63
|
+
rb_raise(cAO_eAOError, "Device already closed.");
|
64
|
+
}
|
65
|
+
Data_Get_Struct(rb_ivar_get(obj, rb_intern("@device")),
|
66
|
+
ao_struct, aos);
|
67
|
+
if ((sample = malloc(sizeof(sample_t))) == NULL){
|
68
|
+
rb_raise(cAO_eAOError, "Memory allocation failure.");
|
69
|
+
}
|
70
|
+
sample->bytes = RSTRING_LENINT(output_samples);
|
71
|
+
if ((sample->buffer = malloc(sample->bytes)) == NULL){
|
72
|
+
rb_raise(cAO_eAOError, "Memory allocation failure.");
|
73
|
+
}
|
74
|
+
memcpy(sample->buffer, StringValuePtr(output_samples), sample->bytes);
|
75
|
+
|
76
|
+
if (aos->thread == 1){
|
77
|
+
enqueue(aos, sample);
|
78
|
+
} else {
|
79
|
+
aosg.aos = aos;
|
80
|
+
aosg.sample = *sample;
|
81
|
+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
82
|
+
rb_thread_call_without_gvl2(nogvl_ao_play, &aosg, NULL, NULL);
|
83
|
+
#else
|
84
|
+
nogvl_ao_play(&aosg);
|
85
|
+
#endif
|
86
|
+
free(sample->buffer);
|
87
|
+
}
|
88
|
+
return obj;
|
89
|
+
}
|
90
|
+
|
91
|
+
/*
|
92
|
+
* ao_struct構造体の持つデバイスを全て閉じ、
|
93
|
+
* またオプションとフォーマットの情報も削除する。
|
94
|
+
* メンバ変数にはNULLを設定する。
|
95
|
+
* ao_struct構造体自体は開放しない。
|
96
|
+
* (rubyのGCによって実行されるremove_device関数に任せるため)
|
97
|
+
*/
|
98
|
+
void
|
99
|
+
close_device(ao_struct *aos){
|
100
|
+
int i;
|
101
|
+
sample_t *sample;
|
102
|
+
|
103
|
+
if (aos->thread == 1){
|
104
|
+
assert(pthread_mutex_lock(&aos->mutex) == 0);
|
105
|
+
if (aos->status > 0){
|
106
|
+
aos->status = 0;
|
107
|
+
}
|
108
|
+
assert(pthread_cond_signal(&aos->cond) == 0);
|
109
|
+
assert(pthread_mutex_unlock(&aos->mutex) == 0);
|
110
|
+
|
111
|
+
while ((sample = dequeue(aos)) != NULL){
|
112
|
+
free(sample->buffer);
|
113
|
+
free(sample);
|
114
|
+
}
|
115
|
+
pthread_join(aos->thread, NULL);
|
116
|
+
} else {
|
117
|
+
if (aos->device != NULL){
|
118
|
+
ao_close(aos->device);
|
119
|
+
}
|
120
|
+
if (aos->option != NULL){
|
121
|
+
ao_free_options(aos->option);
|
122
|
+
}
|
123
|
+
if (aos->format != NULL){
|
124
|
+
free_format(aos->format);
|
125
|
+
}
|
126
|
+
aos->device = NULL;
|
127
|
+
aos->option = NULL;
|
128
|
+
aos->format = NULL;
|
129
|
+
aos->queue = NULL;
|
130
|
+
}
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
|
134
|
+
/*
|
135
|
+
* GCが実行された時にデバイスを閉じ、構造体を開放する。
|
136
|
+
* この関数はopen_live()とopen_file()にてao_struct構造体をWrapする時に
|
137
|
+
* 構造体free用関数として渡す(rubyのGCにて呼ばれるよう設定する)
|
138
|
+
* Data_Wrap_Struct(cAO_DeviceData, 0, remove_device, aos);
|
139
|
+
*/
|
140
|
+
void
|
141
|
+
remove_device(ao_struct *aos)
|
142
|
+
{
|
143
|
+
close_device(aos);
|
144
|
+
free(aos);
|
145
|
+
return;
|
146
|
+
}
|
147
|
+
|
148
|
+
/*
|
149
|
+
* call-seq: ao.closed?
|
150
|
+
*
|
151
|
+
* デバイスが既に閉じられているか確認する。
|
152
|
+
* [return] true or false
|
153
|
+
*/
|
154
|
+
VALUE
|
155
|
+
raodev_closed(VALUE obj)
|
156
|
+
{
|
157
|
+
ao_struct *aos;
|
158
|
+
Data_Get_Struct(rb_ivar_get(obj, rb_intern("@device")),
|
159
|
+
ao_struct, aos);
|
160
|
+
if (aos->device == NULL){
|
161
|
+
return Qtrue;
|
162
|
+
}
|
163
|
+
return Qfalse;
|
164
|
+
}
|
165
|
+
|
166
|
+
/*
|
167
|
+
* call-seq: ao.playing?
|
168
|
+
*
|
169
|
+
* デバイスがオーディオを出力中か確認する。
|
170
|
+
* (threadを有効にしていない場合、常にfalse)
|
171
|
+
* [return] true or false
|
172
|
+
*/
|
173
|
+
VALUE
|
174
|
+
raodev_playing(VALUE obj)
|
175
|
+
{
|
176
|
+
ao_struct *aos;
|
177
|
+
Data_Get_Struct(rb_ivar_get(obj, rb_intern("@device")),
|
178
|
+
ao_struct, aos);
|
179
|
+
if (aos->thread == 1 && aos->status == 2){
|
180
|
+
return Qtrue;
|
181
|
+
}
|
182
|
+
return Qfalse;
|
183
|
+
}
|
184
|
+
|
185
|
+
/*
|
186
|
+
* call-seq: ao.waiting
|
187
|
+
*
|
188
|
+
* 再生キューの待ち数を返す。
|
189
|
+
* (threadを有効にしていない場合、常に0)
|
190
|
+
* [return] Integer
|
191
|
+
*/
|
192
|
+
VALUE
|
193
|
+
raodev_waiting(VALUE obj)
|
194
|
+
{
|
195
|
+
ao_struct *aos;
|
196
|
+
Data_Get_Struct(rb_ivar_get(obj, rb_intern("@device")),
|
197
|
+
ao_struct, aos);
|
198
|
+
if (aos->thread == 1){
|
199
|
+
return INT2FIX(aos->qsize);
|
200
|
+
}
|
201
|
+
return 0;
|
202
|
+
}
|
File without changes
|
@@ -3,10 +3,14 @@
|
|
3
3
|
|
4
4
|
require 'mkmf'
|
5
5
|
|
6
|
+
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
|
7
|
+
|
6
8
|
dir_config('audio')
|
9
|
+
have_header('pthread.h')
|
7
10
|
have_header('ao/ao.h')
|
8
11
|
have_header('ao/os_types.h')
|
9
12
|
have_header('ao/plugin.h')
|
10
13
|
have_library('ao')
|
14
|
+
have_library('pthread')
|
11
15
|
have_func('rb_thread_call_without_gvl')
|
12
16
|
create_makefile('audio/outputc')
|
data/ext/audio/file.c
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <string.h>
|
4
|
+
#include <errno.h>
|
5
|
+
#include "cao.h"
|
6
|
+
#include "thread.h"
|
7
|
+
|
8
|
+
ao_device *
|
9
|
+
rao_open_file_dev(int driver_id, char *filename,
|
10
|
+
int overwrite, ao_sample_format *format,
|
11
|
+
ao_option *option){
|
12
|
+
ao_device *dev;
|
13
|
+
|
14
|
+
dev = ao_open_file(driver_id, filename,
|
15
|
+
overwrite, format, option);
|
16
|
+
if (dev == NULL){
|
17
|
+
free_format(format);
|
18
|
+
ao_free_options(option);
|
19
|
+
switch(errno){
|
20
|
+
case AO_ENODRIVER:
|
21
|
+
rb_raise(cAO_eNoDriver,
|
22
|
+
"No driver corresponds - %s",
|
23
|
+
strerror(errno));
|
24
|
+
break;
|
25
|
+
case AO_ENOTFILE:
|
26
|
+
rb_raise(cAO_eNotFile,
|
27
|
+
"This driver is not a file output device - %s",
|
28
|
+
strerror(errno));
|
29
|
+
break;
|
30
|
+
case AO_EBADOPTION:
|
31
|
+
rb_raise(cAO_eBadOption,
|
32
|
+
"A valid option key has an invalid value - %s",
|
33
|
+
strerror(errno));
|
34
|
+
break;
|
35
|
+
case AO_EOPENFILE:
|
36
|
+
rb_raise(cAO_eFileError,
|
37
|
+
"Cannot open the device - %s",
|
38
|
+
strerror(errno));
|
39
|
+
break;
|
40
|
+
case AO_EFILEEXISTS:
|
41
|
+
rb_raise(cAO_eFileExists,
|
42
|
+
"The file already exists - %s",
|
43
|
+
strerror(errno));
|
44
|
+
case AO_EFAIL:
|
45
|
+
rb_raise(cAO_eUnknownError,
|
46
|
+
"Any other cause of failure - %s",
|
47
|
+
strerror(errno));
|
48
|
+
break;
|
49
|
+
default:
|
50
|
+
rb_raise(cAO_eUnknownError,
|
51
|
+
"Unknown error - %s",
|
52
|
+
strerror(errno));
|
53
|
+
}
|
54
|
+
}
|
55
|
+
return dev;
|
56
|
+
}
|
57
|
+
|
58
|
+
/*
|
59
|
+
* call-seq: Audio::FileOutput.new(driver_id, filepath, overwrite, bits, rate, channels, byte_format, matrix, options)
|
60
|
+
*
|
61
|
+
* ファイルを開く。引数に指定するmatrixとoptionについては以下を参照。
|
62
|
+
* [matrix] http://xiph.org/ao/doc/ao_sample_format.html
|
63
|
+
* [option] http://xiph.org/ao/doc/drivers.html
|
64
|
+
* optionはKey-Valueを要素に含む2次元配列を設定する。
|
65
|
+
* 特に設定が必要なければnilで構わない。
|
66
|
+
* ex) RAWドライバの出力エンディアンをビッグエンディアンに設定する場合は [["byteirder", "big"]] を渡す。
|
67
|
+
*
|
68
|
+
* [arg1] DriverID(Fixnum)
|
69
|
+
* [arg2] filepath(String)
|
70
|
+
* [arg3] overwrite?(true or false)
|
71
|
+
* [arg4] bits(Fixnum)
|
72
|
+
* [arg5] rate(Fixnum)
|
73
|
+
* [arg6] channels(fixnum)
|
74
|
+
* [arg7] byte_format(fixnum)
|
75
|
+
* [arg8] matrix(String or nil)
|
76
|
+
* [arg9] options(Array or nil)
|
77
|
+
* [arg10] thread(true or false)
|
78
|
+
* [return] self
|
79
|
+
*/
|
80
|
+
VALUE
|
81
|
+
rao_open_file(VALUE obj, VALUE driver_id,
|
82
|
+
VALUE filename, VALUE overwrite,
|
83
|
+
VALUE bits, VALUE rate, VALUE channels,
|
84
|
+
VALUE byte_format, VALUE matrix,
|
85
|
+
VALUE a_options, VALUE thread)
|
86
|
+
{
|
87
|
+
ao_struct *aos;
|
88
|
+
ao_device *dev;
|
89
|
+
ao_sample_format *format;
|
90
|
+
ao_option *option;
|
91
|
+
int overwrite_int = 0;
|
92
|
+
VALUE rdev;
|
93
|
+
|
94
|
+
if (rb_iv_get(obj, "@device") != Qnil){
|
95
|
+
Data_Get_Struct(rb_iv_get(obj, "@device"),
|
96
|
+
ao_struct, aos);
|
97
|
+
rb_raise(cAO_eDeviceError,
|
98
|
+
"Device is already open.\n");
|
99
|
+
}
|
100
|
+
|
101
|
+
Check_Type(filename, T_STRING);
|
102
|
+
Check_Type(driver_id, T_FIXNUM);
|
103
|
+
if (TYPE(overwrite) == T_TRUE){
|
104
|
+
overwrite_int = 1;
|
105
|
+
}
|
106
|
+
format = set_format(bits, rate, channels, byte_format, matrix);
|
107
|
+
option = set_option(a_options);
|
108
|
+
dev = rao_open_file_dev(FIX2INT(driver_id), StringValuePtr(filename), overwrite_int, format, option);
|
109
|
+
if ((aos = init_aos(dev, format, option)) == NULL){
|
110
|
+
rb_raise(cAO_eUnknownError,
|
111
|
+
"memory allocation failure - %s",
|
112
|
+
strerror(errno));
|
113
|
+
}
|
114
|
+
if (TYPE(thread) == T_TRUE){
|
115
|
+
if ((aos = create_thread(aos)) == NULL){
|
116
|
+
rb_raise(cAO_eUnknownError,
|
117
|
+
"memory allocation failure - %s",
|
118
|
+
strerror(errno));
|
119
|
+
}
|
120
|
+
}
|
121
|
+
rdev = Data_Wrap_Struct(cAO_DeviceData, 0,
|
122
|
+
remove_device, aos);
|
123
|
+
rb_iv_set(obj, "@device", rdev);
|
124
|
+
rb_ary_push(rb_cv_get(cAO_File, "@@devices"), rdev);
|
125
|
+
|
126
|
+
return obj;
|
127
|
+
}
|