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