ao 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1,114 @@
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_live_dev(int driver_id,
10
+ ao_sample_format *format,
11
+ ao_option *option){
12
+ ao_device *dev;
13
+
14
+ dev = ao_open_live(driver_id, format, option);
15
+ if (dev == NULL){
16
+ free_format(format);
17
+ ao_free_options(option);
18
+ switch(errno){
19
+ case AO_ENODRIVER:
20
+ rb_raise(cAO_eNoDriver,
21
+ "No driver corresponds - %s",
22
+ strerror(errno));
23
+ break;
24
+ case AO_ENOTLIVE:
25
+ rb_raise(cAO_eNotLive,
26
+ "This driver is not a live output device - %s",
27
+ strerror(errno));
28
+ break;
29
+ case AO_EBADOPTION:
30
+ rb_raise(cAO_eBadOption,
31
+ "A valid option key has an invalid value - %s",
32
+ strerror(errno));
33
+ break;
34
+ case AO_EOPENDEVICE:
35
+ rb_raise(cAO_eDeviceError,
36
+ "Cannot open the device - %s",
37
+ strerror(errno));
38
+ break;
39
+ case AO_EFAIL:
40
+ rb_raise(cAO_eUnknownError,
41
+ "Any other cause of failure - %s",
42
+ strerror(errno));
43
+ break;
44
+ default:
45
+ rb_raise(cAO_eUnknownError,
46
+ "Unknown error - %s",
47
+ strerror(errno));
48
+ }
49
+ }
50
+ return dev;
51
+ }
52
+
53
+ /*
54
+ * call-seq: Audio::LiveOutput.new(driver_id, bits, rate, channels, byte_format, matrix, options)
55
+ *
56
+ * オーディオ出力デバイスを開く。
57
+ * 引数に指定するmatrixとoptionについては以下を参照。
58
+ * [matrix] http://xiph.org/ao/doc/ao_sample_format.html
59
+ * [option] http://xiph.org/ao/doc/drivers.html
60
+ * optionはKey-Valueを要素に含む2次元配列を設定する。
61
+ * 特に設定が必要なければnilで構わない。
62
+ * ex) ALSAドライバのデバイスをhw:1に設定する場合は [["dev", "hw:1"]] を渡す。
63
+ *
64
+ * [arg1] DriverID
65
+ * [arg2] bits(Fixnum)
66
+ * [arg3] rate(Fixnum)
67
+ * [arg4] channels(fixnum)
68
+ * [arg5] byte_format(fixnum)
69
+ * [arg6] matrix(String or nil)
70
+ * [arg7] options(Array or nil)
71
+ * [arg8] thread(true or false)
72
+ * [return] self
73
+ */
74
+ VALUE
75
+ rao_open_live(VALUE obj, VALUE driver_id,
76
+ VALUE bits, VALUE rate, VALUE channels,
77
+ VALUE byte_format, VALUE matrix,
78
+ VALUE a_options, VALUE thread)
79
+ {
80
+ ao_struct *aos;
81
+ ao_device *dev;
82
+ ao_sample_format *format;
83
+ ao_option *option;
84
+ VALUE rdev;
85
+
86
+ if (rb_iv_get(obj, "@device") != Qnil){
87
+ Data_Get_Struct(rb_iv_get(obj, "@device"),
88
+ ao_struct, aos);
89
+ rb_raise(cAO_eDeviceError,
90
+ "Device is already open.\n");
91
+ }
92
+ Check_Type(driver_id, T_FIXNUM);
93
+ format = set_format(bits, rate, channels, byte_format, matrix);
94
+ option = set_option(a_options);
95
+
96
+ dev = rao_open_live_dev(FIX2INT(driver_id), format, option);
97
+ if ((aos = init_aos(dev, format, option)) == NULL){
98
+ rb_raise(cAO_eUnknownError,
99
+ "memory allocation failure - %s",
100
+ strerror(errno));
101
+ }
102
+ if (TYPE(thread) == T_TRUE){
103
+ if ((aos = create_thread(aos)) == 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, aos);
111
+ rb_iv_set(obj, "@device", rdev);
112
+ rb_ary_push(rb_cv_get(cAO_Live, "@@devices"), rdev);
113
+ return obj;
114
+ }
@@ -0,0 +1,128 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <stdint.h>
5
+ #include <errno.h>
6
+ #include "cao.h"
7
+
8
+ int
9
+ mix_sample_8(sample_t **samples, sample_t *ret, int chs){
10
+ int32_t sum;
11
+ int ch;
12
+ int pos;
13
+ int8_t *buffer;
14
+ int8_t *src;
15
+
16
+ buffer = (int8_t *)ret->buffer;
17
+ for (pos=0; pos<ret->bytes; pos++){
18
+ sum = 0;
19
+ for (ch=0; ch<chs; ch++){
20
+ if (pos < samples[ch]->bytes){
21
+ src = (int8_t *)samples[ch]->buffer;
22
+ sum += src[pos];
23
+ }
24
+ }
25
+ buffer[pos] = (int8_t)(sum / chs);
26
+ }
27
+ return 1;
28
+ }
29
+
30
+ int
31
+ mix_sample_16(sample_t **samples, sample_t *ret, int chs){
32
+ int32_t sum;
33
+ int ch;
34
+ int pos;
35
+ int16_t *buffer;
36
+ int16_t *src;
37
+
38
+ buffer = (int16_t *)ret->buffer;
39
+ for (pos=0; pos< ret->bytes / 2; pos++){
40
+ sum = 0;
41
+ for (ch=0; ch<chs; ch++){
42
+ if (pos < samples[ch]->bytes / 2){
43
+ src = (int16_t *)samples[ch]->buffer;
44
+ sum += src[pos];
45
+ }
46
+ }
47
+ buffer[pos] = (int16_t)(sum / chs);
48
+ }
49
+
50
+ return 1;
51
+ }
52
+
53
+ int
54
+ mix_sample(int bits, sample_t **samples,
55
+ sample_t *ret, int chs){
56
+ if (bits == 8){
57
+ return mix_sample_8(samples, ret, chs);
58
+ } else if(bits == 16){
59
+ return mix_sample_16(samples, ret, chs);
60
+ } else {
61
+ return -1;
62
+ }
63
+ }
64
+
65
+ /*
66
+ * call-seq: Audio::Mixer.mix(samples)
67
+ *
68
+ * 複数のオーディオサンプルをMixする。
69
+ * samplesには、Mix対象のデータ(String)の配列を入れる。
70
+ * 8bitまたは16bitのみ対応
71
+ *
72
+ * [arg1] bits(8 or 16)
73
+ * [arg2] samples([sample1, sample2, ...])
74
+ * [return] Mixed string
75
+ */
76
+ VALUE
77
+ rao_mix_samples(VALUE obj, VALUE bits, VALUE rsamples){
78
+ VALUE rsample;
79
+ sample_t **samples;
80
+ sample_t ret;
81
+ int stat;
82
+ int chs = 0;
83
+ int maxlen = 0;
84
+
85
+ Check_Type(bits, T_FIXNUM);
86
+ Check_Type(rsamples, T_ARRAY);
87
+ samples = malloc(sizeof(sample_t *) * 1);
88
+ while(1){
89
+ rsample = rb_ary_entry(rsamples, chs);
90
+ if (TYPE(rsample) == T_NIL){
91
+ break;
92
+ }
93
+ Check_Type(rsample, T_STRING);
94
+ samples[chs] = malloc(sizeof(sample_t));
95
+ samples[chs]->buffer = StringValuePtr(rsample);
96
+ samples[chs]->bytes = RSTRING_LEN(rsample);
97
+ if (samples[chs]->bytes > maxlen){
98
+ maxlen = samples[chs]->bytes;
99
+ }
100
+ chs++;
101
+ samples = realloc(samples, sizeof(sample_t *) * chs);
102
+ }
103
+
104
+ ret.bytes = sizeof(char) * maxlen;
105
+ ret.buffer = malloc(ret.bytes);
106
+ stat = mix_sample(FIX2INT(bits), samples, &ret, chs);
107
+ free(samples);
108
+ if (stat == -1){
109
+ rb_raise(cAO_eAOError, "Unsupported format.");
110
+ return Qnil;
111
+ }
112
+ rsample = rb_str_new(ret.buffer, ret.bytes);
113
+ free(ret.buffer);
114
+ return rsample;
115
+ }
116
+
117
+ void
118
+ init_mixer(void)
119
+ {
120
+ /*
121
+ * Document-class: Audio::Mixer
122
+ *
123
+ * オーディオデータをシンプルにミックスするモジュール。
124
+ */
125
+ cAO_Mixer = rb_define_module_under(cAudio, "Mixer");
126
+
127
+ rb_define_module_function(cAO_Mixer, "mix", rao_mix_samples, 2);
128
+ }
@@ -18,7 +18,7 @@ set_format(VALUE bits, VALUE rate, VALUE channels,
18
18
  {
19
19
  ao_sample_format *format;
20
20
  size_t len;
21
-
21
+
22
22
  if ((format = malloc(sizeof(ao_sample_format))) == NULL){
23
23
  rb_raise(cAO_eUnknownError, "memory allocation failure.");
24
24
  }
@@ -70,7 +70,7 @@ set_option(VALUE a_options)
70
70
  ao_option *option = NULL;
71
71
  int pos, result;
72
72
  VALUE element, key, value;
73
-
73
+
74
74
  switch(TYPE(a_options)){
75
75
  case T_ARRAY:
76
76
  for (pos=0; pos<RARRAY_LEN(a_options); pos++){
@@ -0,0 +1,113 @@
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_Info;
11
+ VALUE cAO_Mixer;
12
+ VALUE cAO_DeviceData;
13
+ VALUE cAO_eAOError;
14
+ VALUE cAO_eDeviceError;
15
+ VALUE cAO_eUnknownError;
16
+
17
+ VALUE cAO_eNoDriver;
18
+ VALUE cAO_eNotFile;
19
+ VALUE cAO_eNotLive;
20
+ VALUE cAO_eBadOption;
21
+ VALUE cAO_eDriverError;
22
+
23
+ VALUE
24
+ rao_close(VALUE obj)
25
+ {
26
+ ao_struct *aos;
27
+
28
+ if (rb_iv_get(obj, "@device") == Qnil){
29
+ return Qfalse;
30
+ }
31
+
32
+ Data_Get_Struct(rb_iv_get(obj, "@device"),
33
+ ao_struct, aos);
34
+ close_device(aos);
35
+ rb_ary_delete(rb_cv_get(cAO_Live, "@@devices"), rb_intern("@device"));
36
+ /*rb_iv_set(obj, "@device", Qnil);*/
37
+ return Qtrue;
38
+ }
39
+
40
+ /*
41
+ * libaoを終了する。shutdownの前に開いている全てのデバイスを
42
+ * closeしておかなければならない。
43
+ *
44
+ * [return] nil
45
+ */
46
+ void
47
+ rao_shutdown(VALUE obj){
48
+ VALUE rdev;
49
+ ao_struct *aos;
50
+
51
+ rb_gc_start();
52
+ while ((rdev = rb_ary_pop(rb_cv_get(cAO_Live, "@@devices"))) != Qnil){
53
+ Data_Get_Struct(rdev, ao_struct, aos);
54
+ close_device(aos);
55
+ }
56
+ ao_shutdown();
57
+ return;
58
+ }
59
+
60
+ void
61
+ Init_outputc(void)
62
+ {
63
+ ao_initialize();
64
+ /*
65
+ * Document-class: Audio
66
+ *
67
+ * Ruby-AOの基礎となるクラス。
68
+ */
69
+ cAudio = rb_define_class("Audio", rb_cObject);
70
+
71
+ /*
72
+ * Document-class: Audio::LiveOutput
73
+ *
74
+ * オーディオデバイス出力機能をサポートするクラス。
75
+ */
76
+ cAO_Live = rb_define_class_under(cAudio, "LiveOutputC", rb_cObject);
77
+ rb_cv_set(cAO_Live, "@@devices", rb_ary_new());
78
+
79
+ /*
80
+ * Document-class: Audio::BasicOutput::DeviceData
81
+ *
82
+ * 開いたデバイスに関する基本的な情報を保持するクラス。
83
+ * ruby側から操作はしない。
84
+ */
85
+ cAO_DeviceData = rb_define_class_under(cAO_Live, "DeviceData", rb_cData);
86
+
87
+ /* library initialize & shutdown */
88
+ rb_define_private_method(cAO_Live, "initialize", rao_open_live, 7);
89
+
90
+ /* device setup */
91
+ rb_define_method(cAO_Live, "close", rao_close, 0);
92
+ rb_define_method(cAO_Live, "play", raodev_play, 1);
93
+ rb_define_method(cAO_Live, "closed?", raodev_closed, 0);
94
+ rb_define_method(cAO_Live, "playing?", raodev_playing, 0);
95
+ rb_define_method(cAO_Live, "waiting", raodev_waiting, 0);
96
+
97
+ /*
98
+ * Document-class: Audio::FileOutput
99
+ *
100
+ * オーディオファイル出力機能をサポートするクラス。
101
+ */
102
+ cAO_File = rb_define_class_under(cAudio, "FileOutputC", cAO_Live);
103
+ rb_define_private_method(cAO_File, "initialize", rao_open_file, 9);
104
+
105
+ init_exception();
106
+ init_info();
107
+ init_mixer();
108
+ init_constant();
109
+ init_option();
110
+
111
+ /* Shutdown */
112
+ rb_set_end_proc(rao_shutdown, (VALUE)NULL);
113
+ }
@@ -0,0 +1,52 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <pthread.h>
5
+ #include <assert.h>
6
+ #include "cao.h"
7
+
8
+ int
9
+ enqueue(ao_struct *aos, void *value){
10
+ queue *last;
11
+
12
+ assert(pthread_mutex_lock(&aos->mutex) == 0);
13
+ if (aos->queue == NULL){
14
+ aos->queue = malloc(sizeof(queue));
15
+ aos->queue->data = value;
16
+ aos->queue->next = NULL;
17
+ aos->qsize++;
18
+ assert(pthread_cond_signal(&aos->cond) == 0);
19
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
20
+ return aos->qsize;
21
+ }
22
+
23
+ last = aos->queue;
24
+ while (last->next != NULL){
25
+ last = last->next;
26
+ }
27
+ last->next = malloc(sizeof(queue));
28
+ last->next->data = value;
29
+ last->next->next = NULL;
30
+ aos->qsize++;
31
+ assert(pthread_cond_signal(&aos->cond) == 0);
32
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
33
+ return aos->qsize;
34
+ }
35
+
36
+ void *
37
+ dequeue(ao_struct *aos){
38
+ void *res;
39
+ queue *trash;
40
+
41
+ if (aos->queue == NULL){
42
+ return NULL;
43
+ }
44
+ assert(pthread_mutex_lock(&aos->mutex) == 0);
45
+ trash = aos->queue;
46
+ res = aos->queue->data;
47
+ aos->queue = aos->queue->next;
48
+ aos->qsize--;
49
+ free(trash);
50
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
51
+ return res;
52
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef QUEUE_H
2
+ #define QUEUE_H
3
+
4
+ int enqueue(ao_struct *aos, void *value);
5
+ void *dequeue(ao_struct *aos);
6
+ #endif
@@ -0,0 +1,88 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <errno.h>
5
+ #include <assert.h>
6
+ #include <pthread.h>
7
+ #include <time.h>
8
+ #include <signal.h>
9
+ #include "cao.h"
10
+ #include "queue.h"
11
+ #include "thread.h"
12
+
13
+ void *
14
+ thread_player(void *val){
15
+ queue res;
16
+ ao_struct *aos = val;
17
+ sample_t *sample;
18
+ struct timespec tout = {0, 1000000};
19
+
20
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
21
+ /*assert(pthread_detach(pthread_self()) == 0);*/
22
+
23
+ while (1){
24
+ assert(pthread_mutex_lock(&aos->mutex) == 0);
25
+ while (aos->status == 1 && aos->queue == NULL){
26
+ pthread_cond_timedwait(&aos->cond, &aos->mutex, &tout);
27
+ }
28
+ if (aos->status != 1){
29
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
30
+ break;
31
+ }
32
+ aos->status = 2;
33
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
34
+
35
+ while ((sample = dequeue(aos)) != NULL){
36
+ ao_play(aos->device, sample->buffer,
37
+ sample->bytes);
38
+ free(sample->buffer);
39
+ free(sample);
40
+ assert(pthread_mutex_lock(&aos->mutex) == 0);
41
+ if (aos->status == 1){
42
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
43
+ break;
44
+ }
45
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
46
+ }
47
+
48
+ assert(pthread_mutex_lock(&aos->mutex) == 0);
49
+ if (aos->status == 2){
50
+ aos->status = 1;
51
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
52
+ break;
53
+ }
54
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
55
+ }
56
+
57
+ while ((sample = dequeue(aos)) != NULL){
58
+ free(sample->buffer);
59
+ free(sample);
60
+ }
61
+ if (aos->device != NULL){
62
+ ao_close(aos->device);
63
+ }
64
+ if (aos->option != NULL){
65
+ ao_free_options(aos->option);
66
+ }
67
+ if (aos->format != NULL){
68
+ free_format(aos->format);
69
+ }
70
+ aos->device = NULL;
71
+ aos->option = NULL;
72
+ aos->format = NULL;
73
+ aos->queue = NULL;
74
+ assert(pthread_mutex_lock(&aos->mutex) == 0);
75
+ aos->status = -1;
76
+ assert(pthread_mutex_unlock(&aos->mutex) == 0);
77
+ pthread_exit(NULL);
78
+ }
79
+
80
+ ao_struct *
81
+ create_thread(ao_struct *aos){
82
+ aos->thread = 1;
83
+ assert(pthread_mutex_init(&aos->mutex, NULL) == 0);
84
+ assert(pthread_cond_init(&aos->cond, NULL) == 0);
85
+ assert(pthread_create(&aos->thread, NULL,
86
+ thread_player, aos) == 0);
87
+ return aos;
88
+ }