Line |
Branch |
Exec |
Source |
1 |
|
|
/* GATE PROJECT LICENSE: |
2 |
|
|
+----------------------------------------------------------------------------+ |
3 |
|
|
| Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> | |
4 |
|
|
| All rights reserved. | |
5 |
|
|
| | |
6 |
|
|
| Redistribution and use in source and binary forms, with or without | |
7 |
|
|
| modification, are permitted provided that the following conditions are met:| |
8 |
|
|
| | |
9 |
|
|
| 1. Redistributions of source code must retain the above copyright notice, | |
10 |
|
|
| this list of conditions and the following disclaimer. | |
11 |
|
|
| 2. Redistributions in binary form must reproduce the above copyright | |
12 |
|
|
| notice, this list of conditions and the following disclaimer in the | |
13 |
|
|
| documentation and/or other materials provided with the distribution. | |
14 |
|
|
| | |
15 |
|
|
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"| |
16 |
|
|
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
17 |
|
|
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
18 |
|
|
| ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
19 |
|
|
| LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
20 |
|
|
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
21 |
|
|
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
22 |
|
|
| INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
23 |
|
|
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
24 |
|
|
| ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
25 |
|
|
| THE POSSIBILITY OF SUCH DAMAGE. | |
26 |
|
|
+----------------------------------------------------------------------------+ |
27 |
|
|
*/ |
28 |
|
|
#include "gate/io/audiosources.h" |
29 |
|
|
#include "gate/results.h" |
30 |
|
|
#include "gate/queues.h" |
31 |
|
|
#include "gate/threading.h" |
32 |
|
|
#include "gate/synchronization.h" |
33 |
|
|
#include "gate/serializers.h" |
34 |
|
|
#include "gate/mathematics.h" |
35 |
|
|
#include "gate/libraries.h" |
36 |
|
|
#include "gate/debugging.h" |
37 |
|
|
|
38 |
|
|
#if defined(GATE_SYS_WIN16) |
39 |
|
|
# define GATE_IO_AUDIO_NO_IMPL 1 |
40 |
|
|
#elif defined(GATE_SYS_WIN) |
41 |
|
|
# if defined(GATE_SYS_WINSTORE) |
42 |
|
|
# define GATE_IO_AUDIO_NO_IMPL 1 |
43 |
|
|
# else |
44 |
|
|
# define GATE_IO_AUDIO_WINAPI 1 |
45 |
|
|
# endif |
46 |
|
|
#elif defined(GATE_SYS_LINUX) && !defined(GATE_SYS_ANDROID) |
47 |
|
|
# define GATE_IO_AUDIO_ALSA 1 |
48 |
|
|
#else |
49 |
|
|
# define GATE_IO_AUDIO_NO_IMPL 1 |
50 |
|
|
#endif |
51 |
|
|
|
52 |
|
|
#if !defined(GATE_IO_AUDIO_NO_IMPL) |
53 |
|
|
|
54 |
|
|
static void gate_audio_device_impl_release(void* device); |
55 |
|
|
static int gate_audio_device_impl_retain(void* device); |
56 |
|
|
static char const* gate_audio_device_impl_get_interface_name(void* device); |
57 |
|
|
|
58 |
|
|
static char const* gate_audio_device_impl_get_id(void* device); |
59 |
|
|
static char const* gate_audio_device_impl_get_name(void* device); |
60 |
|
|
static gate_intptr_t gate_audio_device_impl_get_handle(void* device); |
61 |
|
|
static gate_enumint_t gate_audio_device_impl_get_device_type(void* device); |
62 |
|
|
static gate_size_t gate_audio_device_impl_get_supported_formats(void* device, gate_audio_format_t* format_buffer, gate_size_t format_buffer_count); |
63 |
|
|
|
64 |
|
|
static gate_result_t gate_audio_device_impl_open(void* device, gate_audio_format_t const* format, gate_size_t sample_count); |
65 |
|
|
static gate_result_t gate_audio_device_impl_close(void* device); |
66 |
|
|
|
67 |
|
|
static gate_result_t gate_audio_device_impl_read(void* device, gate_audio_sample_t* sample); |
68 |
|
|
static gate_result_t gate_audio_device_impl_read_async(void* device, gate_delegate_t const* completion); |
69 |
|
|
static gate_result_t gate_audio_device_impl_write(void* device, gate_audio_sample_t const* sample); |
70 |
|
|
static gate_result_t gate_audio_device_impl_write_async(void* device, gate_audio_sample_t const* sample, gate_delegate_t const* completion); |
71 |
|
|
|
72 |
|
|
static GATE_INTERFACE_VTBL(gate_audio_device) gate_audio_device_vtbl_impl; |
73 |
|
✗ |
static void gate_init_audio_device_vtbl_impl() |
74 |
|
|
{ |
75 |
|
✗ |
if (!gate_audio_device_vtbl_impl.get_interface_name) |
76 |
|
|
{ |
77 |
|
|
GATE_INTERFACE_VTBL(gate_audio_device) const local_vtbl = |
78 |
|
|
{ |
79 |
|
|
&gate_audio_device_impl_get_interface_name, |
80 |
|
|
&gate_audio_device_impl_release, |
81 |
|
|
&gate_audio_device_impl_retain, |
82 |
|
|
|
83 |
|
|
&gate_audio_device_impl_get_id, |
84 |
|
|
&gate_audio_device_impl_get_name, |
85 |
|
|
&gate_audio_device_impl_get_handle, |
86 |
|
|
&gate_audio_device_impl_get_device_type, |
87 |
|
|
&gate_audio_device_impl_get_supported_formats, |
88 |
|
|
|
89 |
|
|
&gate_audio_device_impl_open, |
90 |
|
|
&gate_audio_device_impl_close, |
91 |
|
|
|
92 |
|
|
&gate_audio_device_impl_read, |
93 |
|
|
&gate_audio_device_impl_read_async, |
94 |
|
|
&gate_audio_device_impl_write, |
95 |
|
|
&gate_audio_device_impl_write_async |
96 |
|
|
}; |
97 |
|
✗ |
gate_audio_device_vtbl_impl = local_vtbl; |
98 |
|
|
} |
99 |
|
✗ |
} |
100 |
|
|
|
101 |
|
✗ |
static char const* gate_audio_device_impl_get_interface_name(void* device) |
102 |
|
|
{ |
103 |
|
|
GATE_UNUSED_ARG(device); |
104 |
|
✗ |
return GATE_INTERFACE_NAME_AUDIO_DEVICE; |
105 |
|
|
} |
106 |
|
|
|
107 |
|
|
#endif /* !defined(GATE_IO_AUDIO_NO_IMPL) */ |
108 |
|
|
|
109 |
|
|
|
110 |
|
✗ |
gate_audio_sample_t* gate_audio_sample_create( |
111 |
|
|
gate_audio_sample_t* new_sample, |
112 |
|
|
gate_audio_format_t const* format, |
113 |
|
|
void const* data, gate_size_t data_size, |
114 |
|
|
void const* meta_data, gate_size_t meta_data_size, |
115 |
|
|
gate_uint32_t sample_count, |
116 |
|
|
gate_int64_t record_time) |
117 |
|
|
{ |
118 |
|
✗ |
gate_audio_sample_t* ret = NULL; |
119 |
|
✗ |
if (new_sample != NULL) |
120 |
|
|
{ |
121 |
|
✗ |
gate_mem_clear(new_sample, sizeof(gate_audio_sample_t)); |
122 |
|
|
do |
123 |
|
|
{ |
124 |
|
✗ |
gate_mem_copy(&new_sample->format, format, sizeof(gate_audio_format_t)); |
125 |
|
|
|
126 |
|
✗ |
new_sample->sample_count = sample_count; |
127 |
|
✗ |
new_sample->record_time = record_time; |
128 |
|
|
|
129 |
|
✗ |
if (data_size != 0) |
130 |
|
|
{ |
131 |
|
✗ |
new_sample->data = gate_memoryblock_create(data_size); |
132 |
|
✗ |
if (new_sample->data == NULL) |
133 |
|
|
{ |
134 |
|
|
// failed |
135 |
|
✗ |
break; |
136 |
|
|
} |
137 |
|
✗ |
if (data != NULL) |
138 |
|
|
{ |
139 |
|
✗ |
gate_mem_copy(gate_memoryblock_get_content(new_sample->data), data, data_size); |
140 |
|
|
} |
141 |
|
|
} |
142 |
|
|
|
143 |
|
✗ |
if (meta_data_size != 0) |
144 |
|
|
{ |
145 |
|
✗ |
new_sample->meta_data = gate_memoryblock_create(meta_data_size); |
146 |
|
✗ |
if (new_sample->meta_data == NULL) |
147 |
|
|
{ |
148 |
|
|
// failed |
149 |
|
✗ |
break; |
150 |
|
|
} |
151 |
|
✗ |
if (meta_data != NULL) |
152 |
|
|
{ |
153 |
|
✗ |
gate_mem_copy(gate_memoryblock_get_content(new_sample->meta_data), meta_data, meta_data_size); |
154 |
|
|
} |
155 |
|
|
} |
156 |
|
|
|
157 |
|
✗ |
ret = new_sample; |
158 |
|
|
} while (0); |
159 |
|
|
|
160 |
|
✗ |
if (ret == NULL) |
161 |
|
|
{ |
162 |
|
|
// release resources, if something has failed |
163 |
|
✗ |
if (new_sample->data != NULL) |
164 |
|
|
{ |
165 |
|
✗ |
gate_object_release(new_sample->data); |
166 |
|
✗ |
new_sample->data = NULL; |
167 |
|
|
} |
168 |
|
|
|
169 |
|
✗ |
if (new_sample->meta_data != NULL) |
170 |
|
|
{ |
171 |
|
✗ |
gate_object_release(new_sample->meta_data); |
172 |
|
✗ |
new_sample->meta_data = NULL; |
173 |
|
|
} |
174 |
|
|
} |
175 |
|
|
} |
176 |
|
✗ |
return ret; |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
|
180 |
|
✗ |
void gate_audio_sample_release(gate_audio_sample_t* sample) |
181 |
|
|
{ |
182 |
|
✗ |
if (sample != NULL) |
183 |
|
|
{ |
184 |
|
✗ |
if (sample->data != NULL) |
185 |
|
|
{ |
186 |
|
✗ |
gate_object_release(sample->data); |
187 |
|
|
} |
188 |
|
✗ |
if (sample->meta_data != NULL) |
189 |
|
|
{ |
190 |
|
✗ |
gate_object_release(sample->meta_data); |
191 |
|
|
} |
192 |
|
|
|
193 |
|
✗ |
gate_mem_clear(sample, sizeof(gate_audio_sample_t)); |
194 |
|
|
} |
195 |
|
✗ |
} |
196 |
|
|
|
197 |
|
✗ |
int gate_compare_audio_format(void const* item1, void const* item2) |
198 |
|
|
{ |
199 |
|
✗ |
gate_audio_format_t const* format1 = (gate_audio_format_t const*)item1; |
200 |
|
✗ |
gate_audio_format_t const* format2 = (gate_audio_format_t const*)item2; |
201 |
|
✗ |
if (format1->bits_per_sample < format2->bits_per_sample) return -1; |
202 |
|
✗ |
if (format1->bits_per_sample > format2->bits_per_sample) return 1; |
203 |
|
✗ |
if (format1->channels < format2->channels) return -1; |
204 |
|
✗ |
if (format1->channels > format2->channels) return 1; |
205 |
|
✗ |
if (format1->bits_per_sample < format2->bits_per_sample) return -1; |
206 |
|
✗ |
if (format1->bits_per_sample > format2->bits_per_sample) return 1; |
207 |
|
✗ |
if (format1->encoding_id < format2->encoding_id) return -1; |
208 |
|
✗ |
if (format1->encoding_id > format2->encoding_id) return 1; |
209 |
|
✗ |
return 0; |
210 |
|
|
} |
211 |
|
|
|
212 |
|
✗ |
gate_audio_format_t const* gate_audio_format_choose(gate_audio_format_t const* formats, gate_size_t formats_count, |
213 |
|
|
gate_uint32_t const* desired_samples_per_second, |
214 |
|
|
gate_uint16_t const* desired_channels, |
215 |
|
|
gate_uint16_t const* desired_bits_per_sample) |
216 |
|
|
{ |
217 |
|
✗ |
gate_audio_format_t const* ret = NULL; |
218 |
|
|
gate_uint32_t scores[1024]; /* smaller is better */ |
219 |
|
|
gate_uint32_t qualities[1024]; /* greater is better */ |
220 |
|
|
gate_size_t index; |
221 |
|
|
gate_audio_format_t const* format; |
222 |
|
✗ |
gate_uint32_t best_score = 0xffffffff; |
223 |
|
✗ |
gate_uint32_t best_quality = 0; |
224 |
|
|
gate_int32_t diff; |
225 |
|
|
|
226 |
|
✗ |
if (formats_count > 1024) formats_count = 1024; |
227 |
|
✗ |
for (index = 0; index != formats_count; ++index) |
228 |
|
|
{ |
229 |
|
✗ |
format = &formats[index]; |
230 |
|
✗ |
scores[index] = 0; |
231 |
|
✗ |
if (desired_samples_per_second != NULL) |
232 |
|
|
{ |
233 |
|
✗ |
diff = (gate_int32_t)*desired_samples_per_second - (gate_int32_t)format->samples_per_second; |
234 |
|
✗ |
scores[index] += (gate_uint32_t)gate_math_abs_i32(diff); |
235 |
|
|
} |
236 |
|
✗ |
if (desired_channels != NULL) |
237 |
|
|
{ |
238 |
|
✗ |
diff = (gate_int32_t)*desired_channels - (gate_int32_t)format->channels; |
239 |
|
✗ |
scores[index] += (gate_uint32_t)gate_math_abs_i32(diff) * 100; |
240 |
|
|
} |
241 |
|
✗ |
if (desired_bits_per_sample != NULL) |
242 |
|
|
{ |
243 |
|
✗ |
diff = (gate_int32_t)*desired_bits_per_sample - (gate_int32_t)format->bits_per_sample; |
244 |
|
✗ |
scores[index] += (gate_uint32_t)gate_math_abs_i32(diff); |
245 |
|
|
} |
246 |
|
✗ |
qualities[index] = format->bits_per_sample * format->channels * format->samples_per_second; |
247 |
|
|
} |
248 |
|
✗ |
for (index = 0; index != formats_count; ++index) |
249 |
|
|
{ |
250 |
|
✗ |
if (scores[index] < best_score || |
251 |
|
✗ |
((scores[index] == best_score) && (qualities[index] > best_quality)) |
252 |
|
|
) |
253 |
|
|
{ |
254 |
|
✗ |
ret = &formats[index]; |
255 |
|
✗ |
best_score = scores[index]; |
256 |
|
✗ |
best_quality = qualities[index]; |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
} |
260 |
|
✗ |
if (ret == NULL) |
261 |
|
|
{ |
262 |
|
|
/* choose best quality */ |
263 |
|
✗ |
for (index = 0; index != formats_count; ++index) |
264 |
|
|
{ |
265 |
|
✗ |
if (qualities[index] > best_quality) |
266 |
|
|
{ |
267 |
|
✗ |
ret = &formats[index]; |
268 |
|
✗ |
best_quality = qualities[index]; |
269 |
|
|
} |
270 |
|
|
} |
271 |
|
|
} |
272 |
|
✗ |
return ret; |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
|
276 |
|
|
|
277 |
|
|
struct gate_audio_device_by_id_helper |
278 |
|
|
{ |
279 |
|
|
char const* id; |
280 |
|
|
gate_audio_device_t* device; |
281 |
|
|
}; |
282 |
|
|
|
283 |
|
✗ |
static gate_result_t gate_audio_device_by_id_callback(gate_audio_device_t* device, void* param) |
284 |
|
|
{ |
285 |
|
✗ |
struct gate_audio_device_by_id_helper* ptr = (struct gate_audio_device_by_id_helper*)param; |
286 |
|
|
|
287 |
|
✗ |
char const* id = gate_audio_device_get_id(device); |
288 |
|
✗ |
if ((0 == gate_str_comp(id, ptr->id)) && (ptr->device == NULL)) |
289 |
|
|
{ |
290 |
|
✗ |
ptr->device = device; |
291 |
|
✗ |
gate_object_retain(ptr->device); |
292 |
|
|
} |
293 |
|
✗ |
return GATE_RESULT_OK; |
294 |
|
|
} |
295 |
|
|
|
296 |
|
✗ |
gate_result_t gate_audio_device_by_id(gate_enumint_t dev_type, char const* id, gate_audio_device_t** ptr_device) |
297 |
|
|
{ |
298 |
|
|
gate_result_t result; |
299 |
|
|
struct gate_audio_device_by_id_helper param; |
300 |
|
|
gate_delegate_t callback; |
301 |
|
|
|
302 |
|
✗ |
param.id = id; |
303 |
|
✗ |
param.device = NULL; |
304 |
|
|
|
305 |
|
✗ |
GATE_DELEGATE_BIND_FUNC(gate_audio_devices_enum, &callback, gate_audio_device_by_id_callback); |
306 |
|
|
|
307 |
|
✗ |
result = gate_audio_devices_enum(dev_type, &callback, ¶m); |
308 |
|
|
|
309 |
|
✗ |
if (GATE_SUCCEEDED(result)) |
310 |
|
|
{ |
311 |
|
✗ |
if (param.device != NULL) |
312 |
|
|
{ |
313 |
|
✗ |
if (ptr_device != NULL) |
314 |
|
|
{ |
315 |
|
|
/* return device */ |
316 |
|
✗ |
*ptr_device = param.device; |
317 |
|
✗ |
param.device = NULL; |
318 |
|
|
} |
319 |
|
|
} |
320 |
|
|
else |
321 |
|
|
{ |
322 |
|
✗ |
result = GATE_RESULT_NOMATCH; |
323 |
|
|
} |
324 |
|
|
} |
325 |
|
|
|
326 |
|
✗ |
if (param.device != NULL) |
327 |
|
|
{ |
328 |
|
✗ |
gate_object_release(param.device); |
329 |
|
|
} |
330 |
|
|
|
331 |
|
✗ |
return result; |
332 |
|
|
} |
333 |
|
|
|
334 |
|
|
|
335 |
|
|
|
336 |
|
|
static void gate_audio_device_wave_stream_release(void* device); |
337 |
|
|
static int gate_audio_device_wave_stream_retain(void* device); |
338 |
|
|
static char const* gate_audio_device_wave_stream_get_interface_name(void* device); |
339 |
|
|
|
340 |
|
|
static char const* gate_audio_device_wave_stream_get_id(void* device); |
341 |
|
|
static char const* gate_audio_device_wave_stream_get_name(void* device); |
342 |
|
|
static gate_intptr_t gate_audio_device_wave_stream_get_handle(void* device); |
343 |
|
|
static gate_enumint_t gate_audio_device_wave_stream_get_device_type(void* device); |
344 |
|
|
static gate_size_t gate_audio_device_wave_stream_get_supported_formats(void* device, gate_audio_format_t* format_buffer, gate_size_t format_buffer_count); |
345 |
|
|
|
346 |
|
|
static gate_result_t gate_audio_device_wave_stream_open(void* device, gate_audio_format_t const* format, gate_size_t sample_count); |
347 |
|
|
static gate_result_t gate_audio_device_wave_stream_close(void* device); |
348 |
|
|
|
349 |
|
|
static gate_result_t gate_audio_device_wave_stream_read(void* device, gate_audio_sample_t* sample); |
350 |
|
|
static gate_result_t gate_audio_device_wave_stream_read_async(void* device, gate_delegate_t const* completion); |
351 |
|
|
static gate_result_t gate_audio_device_wave_stream_write(void* device, gate_audio_sample_t const* sample); |
352 |
|
|
static gate_result_t gate_audio_device_wave_stream_write_async(void* device, gate_audio_sample_t const* sample, gate_delegate_t const* completion); |
353 |
|
|
|
354 |
|
|
static GATE_INTERFACE_VTBL(gate_audio_device) gate_audio_device_vtbl_wave_stream; |
355 |
|
✗ |
static void gate_init_audio_device_vtbl_wave_stream() |
356 |
|
|
{ |
357 |
|
✗ |
if (!gate_audio_device_vtbl_wave_stream.get_interface_name) |
358 |
|
|
{ |
359 |
|
|
GATE_INTERFACE_VTBL(gate_audio_device) const local_vtbl = |
360 |
|
|
{ |
361 |
|
|
&gate_audio_device_wave_stream_get_interface_name, |
362 |
|
|
&gate_audio_device_wave_stream_release, |
363 |
|
|
&gate_audio_device_wave_stream_retain, |
364 |
|
|
|
365 |
|
|
&gate_audio_device_wave_stream_get_id, |
366 |
|
|
&gate_audio_device_wave_stream_get_name, |
367 |
|
|
&gate_audio_device_wave_stream_get_handle, |
368 |
|
|
&gate_audio_device_wave_stream_get_device_type, |
369 |
|
|
&gate_audio_device_wave_stream_get_supported_formats, |
370 |
|
|
|
371 |
|
|
&gate_audio_device_wave_stream_open, |
372 |
|
|
&gate_audio_device_wave_stream_close, |
373 |
|
|
|
374 |
|
|
&gate_audio_device_wave_stream_read, |
375 |
|
|
&gate_audio_device_wave_stream_read_async, |
376 |
|
|
&gate_audio_device_wave_stream_write, |
377 |
|
|
&gate_audio_device_wave_stream_write_async |
378 |
|
|
}; |
379 |
|
✗ |
gate_audio_device_vtbl_wave_stream = local_vtbl; |
380 |
|
|
} |
381 |
|
✗ |
} |
382 |
|
|
|
383 |
|
|
typedef struct gate_audio_wave_stream_impl |
384 |
|
|
{ |
385 |
|
|
GATE_INTERFACE_VTBL(gate_audio_device) const* vtbl; |
386 |
|
|
|
387 |
|
|
gate_atomic_int_t ref_counter; |
388 |
|
|
gate_stream_t* input; |
389 |
|
|
gate_controlstream_t* output; |
390 |
|
|
gate_bool_t opened; |
391 |
|
|
gate_audio_format_t format; |
392 |
|
|
gate_atomic_int_t format_defined; |
393 |
|
|
gate_uint32_t data_length; |
394 |
|
|
gate_int64_t write_start_position; |
395 |
|
|
} gate_audio_wave_stream_impl_t; |
396 |
|
|
|
397 |
|
✗ |
static void gate_audio_device_wave_stream_release(void* device) |
398 |
|
|
{ |
399 |
|
✗ |
gate_audio_wave_stream_impl_t* impl = (gate_audio_wave_stream_impl_t*)device; |
400 |
|
✗ |
if (gate_atomic_int_dec(&impl->ref_counter) == 0) |
401 |
|
|
{ |
402 |
|
✗ |
if (impl->input) |
403 |
|
|
{ |
404 |
|
✗ |
gate_object_release(impl->input); |
405 |
|
✗ |
impl->input = NULL; |
406 |
|
|
} |
407 |
|
✗ |
if (impl->output) |
408 |
|
|
{ |
409 |
|
✗ |
gate_object_release(impl->output); |
410 |
|
✗ |
impl->output = NULL; |
411 |
|
|
} |
412 |
|
|
} |
413 |
|
✗ |
gate_mem_dealloc(impl); |
414 |
|
✗ |
} |
415 |
|
✗ |
static int gate_audio_device_wave_stream_retain(void* device) |
416 |
|
|
{ |
417 |
|
✗ |
gate_audio_wave_stream_impl_t* impl = (gate_audio_wave_stream_impl_t*)device; |
418 |
|
✗ |
return gate_atomic_int_inc(&impl->ref_counter); |
419 |
|
|
} |
420 |
|
✗ |
static char const* gate_audio_device_wave_stream_get_interface_name(void* device) |
421 |
|
|
{ |
422 |
|
|
(void)device; |
423 |
|
✗ |
return GATE_INTERFACE_NAME_AUDIO_DEVICE; |
424 |
|
|
} |
425 |
|
|
|
426 |
|
✗ |
static char const* gate_audio_device_wave_stream_get_id(void* device) |
427 |
|
|
{ |
428 |
|
|
(void)device; |
429 |
|
✗ |
return "riff_stream"; |
430 |
|
|
} |
431 |
|
✗ |
static char const* gate_audio_device_wave_stream_get_name(void* device) |
432 |
|
|
{ |
433 |
|
|
(void)device; |
434 |
|
✗ |
return "RIFF/WAVE Stream"; |
435 |
|
|
} |
436 |
|
✗ |
static gate_intptr_t gate_audio_device_wave_stream_get_handle(void* device) |
437 |
|
|
{ |
438 |
|
|
(void)device; |
439 |
|
✗ |
return -1; |
440 |
|
|
} |
441 |
|
✗ |
static gate_enumint_t gate_audio_device_wave_stream_get_device_type(void* device) |
442 |
|
|
{ |
443 |
|
✗ |
gate_audio_wave_stream_impl_t* impl = (gate_audio_wave_stream_impl_t*)device; |
444 |
|
✗ |
if (impl->input != NULL) |
445 |
|
|
{ |
446 |
|
✗ |
return gate_audio_device_input; |
447 |
|
|
} |
448 |
|
✗ |
else if (impl->output != NULL) |
449 |
|
|
{ |
450 |
|
✗ |
return gate_audio_device_output; |
451 |
|
|
} |
452 |
|
|
else |
453 |
|
|
{ |
454 |
|
✗ |
return gate_audio_device_unknown; |
455 |
|
|
} |
456 |
|
|
} |
457 |
|
|
|
458 |
|
✗ |
static gate_bool_t gate_audio_riff_check_format(char const buffer[36], gate_audio_format_t* format, gate_uint32_t* ptr_datalen) |
459 |
|
|
{ |
460 |
|
✗ |
gate_bool_t ret = false; |
461 |
|
|
gate_uint32_t format_len, sample_rate, bytes_per_sec; |
462 |
|
|
gate_uint16_t format_tag, channels, block_align, bits_sample; |
463 |
|
|
|
464 |
|
✗ |
if ((0 == gate_str_comp_range(&buffer[0], "RIFF", 4)) |
465 |
|
✗ |
&& (0 == gate_str_comp_range(&buffer[8], "WAVE", 4)) |
466 |
|
✗ |
&& (0 == gate_str_comp_range(&buffer[12], "fmt ", 4)) |
467 |
|
|
) |
468 |
|
|
{ |
469 |
|
|
/* |
470 |
|
|
16 4 <fmt length> Length of remaining fmt header (16 Byte) |
471 |
|
|
20 2 <format tag> Datenformat der Abtastwerte (siehe separate Tabelle weiter unten) |
472 |
|
|
22 2 <channels> Count of channels: 1 = mono, 2 = stereo |
473 |
|
|
24 4 <sample rate> Abtastrate pro Sekunde (z.B. 44100) |
474 |
|
|
28 4 <bytes/second> Abtastrate * Block-Align |
475 |
|
|
32 2 <block align> <channels> * (<bits/sample> / 8) (Division ohne Rest) |
476 |
|
|
34 2 <bits/sample> 8, 16 oder 24 |
477 |
|
|
*/ |
478 |
|
✗ |
gate_deserialize_uint32_l(&buffer[4], 4, ptr_datalen); |
479 |
|
✗ |
gate_deserialize_uint32_l(&buffer[16], 4, &format_len); |
480 |
|
✗ |
gate_deserialize_uint16_l(&buffer[20], 2, &format_tag); |
481 |
|
✗ |
gate_deserialize_uint16_l(&buffer[22], 2, &channels); |
482 |
|
✗ |
gate_deserialize_uint32_l(&buffer[24], 4, &sample_rate); |
483 |
|
✗ |
gate_deserialize_uint32_l(&buffer[28], 4, &bytes_per_sec); |
484 |
|
✗ |
gate_deserialize_uint16_l(&buffer[32], 2, &block_align); |
485 |
|
✗ |
gate_deserialize_uint16_l(&buffer[34], 2, &bits_sample); |
486 |
|
|
|
487 |
|
✗ |
format->encoding_id = format_tag; |
488 |
|
✗ |
format->samples_per_second = sample_rate; |
489 |
|
✗ |
format->bits_per_sample = bits_sample; |
490 |
|
✗ |
format->channels = channels; |
491 |
|
|
|
492 |
|
✗ |
ret = true; |
493 |
|
|
} |
494 |
|
✗ |
return ret; |
495 |
|
|
} |
496 |
|
|
|
497 |
|
✗ |
static gate_bool_t gate_audio_riff_create_header(char buffer[36], gate_audio_format_t const* format) |
498 |
|
|
{ |
499 |
|
|
static gate_uint32_t const format_len = 16; |
500 |
|
|
static gate_uint32_t const datalen = 36 + 8; /* total file length = RIFF+WAVE+fmt+data headers */ |
501 |
|
|
|
502 |
|
✗ |
gate_uint32_t const bytes_per_second = format->samples_per_second * format->channels * format->bits_per_sample / 8; |
503 |
|
✗ |
gate_uint16_t const block_align = format->channels * format->bits_per_sample / 8; |
504 |
|
✗ |
gate_uint16_t encoding_id = 0; |
505 |
|
✗ |
gate_mem_copy(&buffer[0], "RIFF", 4); |
506 |
|
✗ |
gate_serialize_uint32_l(&buffer[4], 4, &datalen); |
507 |
|
✗ |
gate_mem_copy(&buffer[8], "WAVE", 4); |
508 |
|
✗ |
gate_mem_copy(&buffer[12], "fmt ", 4); |
509 |
|
✗ |
gate_serialize_uint32_l(&buffer[16], 4, &format_len); |
510 |
|
✗ |
encoding_id = (gate_uint16_t)format->encoding_id; |
511 |
|
✗ |
gate_serialize_uint16_l(&buffer[20], 2, &encoding_id); |
512 |
|
✗ |
gate_serialize_uint16_l(&buffer[22], 2, &format->channels); |
513 |
|
✗ |
gate_serialize_uint32_l(&buffer[24], 4, &format->samples_per_second); |
514 |
|
✗ |
gate_serialize_uint32_l(&buffer[28], 4, &bytes_per_second); |
515 |
|
✗ |
gate_serialize_uint16_l(&buffer[32], 2, &block_align); |
516 |
|
✗ |
gate_serialize_uint16_l(&buffer[34], 2, &format->bits_per_sample); |
517 |
|
✗ |
return true; |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
|
521 |
|
✗ |
static gate_size_t gate_audio_device_wave_stream_get_supported_formats(void* device, gate_audio_format_t* format_buffer, gate_size_t format_buffer_count) |
522 |
|
|
{ |
523 |
|
✗ |
gate_audio_wave_stream_impl_t* impl = (gate_audio_wave_stream_impl_t*)device; |
524 |
|
✗ |
gate_size_t ret = 0; |
525 |
|
|
gate_result_t result; |
526 |
|
|
char hdr_buffer[36]; |
527 |
|
✗ |
gate_size_t bytes_returned = 0; |
528 |
|
✗ |
gate_bool_t format_defined = false; |
529 |
|
|
|
530 |
|
✗ |
if (impl->output != NULL) |
531 |
|
|
{ |
532 |
|
|
/* output stream */ |
533 |
|
|
/* not supported/do nothing */ |
534 |
|
|
} |
535 |
|
|
|
536 |
|
✗ |
if (impl->input != NULL) |
537 |
|
|
{ |
538 |
|
|
/* input stream */ |
539 |
|
|
|
540 |
|
✗ |
format_defined = gate_atomic_int_set(&impl->format_defined, 1); |
541 |
|
✗ |
if (!format_defined) |
542 |
|
|
{ |
543 |
|
|
do |
544 |
|
|
{ |
545 |
|
✗ |
result = gate_stream_read_block(impl->input, hdr_buffer, sizeof(hdr_buffer), &bytes_returned); |
546 |
|
✗ |
GATE_BREAK_IF_FAILED(result); |
547 |
|
|
|
548 |
|
✗ |
if (bytes_returned < sizeof(hdr_buffer)) |
549 |
|
|
{ |
550 |
|
✗ |
gate_atomic_int_set(&impl->format_defined, 0); |
551 |
|
✗ |
break; |
552 |
|
|
} |
553 |
|
|
|
554 |
|
✗ |
if (!gate_audio_riff_check_format(hdr_buffer, &impl->format, &impl->data_length)) |
555 |
|
|
{ |
556 |
|
✗ |
gate_atomic_int_set(&impl->format_defined, 0); |
557 |
|
✗ |
break; |
558 |
|
|
} |
559 |
|
✗ |
format_defined = true; |
560 |
|
|
} while (0); |
561 |
|
|
} |
562 |
|
|
} |
563 |
|
|
|
564 |
|
✗ |
if (format_defined) |
565 |
|
|
{ |
566 |
|
✗ |
if ((format_buffer_count != 0) && (format_buffer != NULL)) |
567 |
|
|
{ |
568 |
|
✗ |
format_buffer[0] = impl->format; |
569 |
|
✗ |
ret = 1; |
570 |
|
|
} |
571 |
|
|
} |
572 |
|
✗ |
return ret; |
573 |
|
|
} |
574 |
|
|
|
575 |
|
✗ |
static gate_result_t gate_audio_device_wave_stream_open(void* device, gate_audio_format_t const* format, gate_size_t sample_count) |
576 |
|
|
{ |
577 |
|
✗ |
gate_audio_wave_stream_impl_t* const impl = (gate_audio_wave_stream_impl_t*)device; |
578 |
|
✗ |
gate_result_t ret = GATE_RESULT_FAILED; |
579 |
|
|
char riff_hdrs[36]; |
580 |
|
|
char data_hdrs[8]; |
581 |
|
|
gate_size_t bytes_written; |
582 |
|
|
|
583 |
|
|
GATE_UNUSED_ARG(sample_count); |
584 |
|
|
|
585 |
|
|
do |
586 |
|
|
{ |
587 |
|
✗ |
if (impl->opened) |
588 |
|
|
{ |
589 |
|
✗ |
ret = GATE_RESULT_INVALIDSTATE; |
590 |
|
✗ |
break; |
591 |
|
|
} |
592 |
|
|
|
593 |
|
✗ |
if (impl->input != NULL) |
594 |
|
|
{ |
595 |
|
✗ |
gate_audio_device_wave_stream_get_supported_formats(device, NULL, 0); |
596 |
|
✗ |
if (0 == gate_atomic_int_get(&impl->format_defined)) |
597 |
|
|
{ |
598 |
|
✗ |
ret = GATE_RESULT_INVALIDHEADER; |
599 |
|
✗ |
break; |
600 |
|
|
} |
601 |
|
✗ |
impl->opened = true; |
602 |
|
✗ |
ret = GATE_RESULT_OK; |
603 |
|
|
} |
604 |
|
|
|
605 |
|
✗ |
if (impl->output != NULL) |
606 |
|
|
{ |
607 |
|
✗ |
if (format == NULL) |
608 |
|
|
{ |
609 |
|
✗ |
ret = GATE_RESULT_INVALIDARG; |
610 |
|
✗ |
break; |
611 |
|
|
} |
612 |
|
✗ |
gate_mem_copy(&impl->format, format, sizeof(impl->format)); |
613 |
|
✗ |
ret = gate_stream_get_position(impl->output, &impl->write_start_position); |
614 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
615 |
|
|
|
616 |
|
✗ |
gate_audio_riff_create_header(riff_hdrs, &impl->format); |
617 |
|
✗ |
ret = gate_stream_write_block((gate_stream_t*)impl->output, riff_hdrs, sizeof(riff_hdrs), &bytes_written); |
618 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
619 |
|
|
|
620 |
|
✗ |
gate_mem_copy(&data_hdrs[0], "data", 4); |
621 |
|
✗ |
gate_mem_clear(&data_hdrs[4], 4); |
622 |
|
✗ |
impl->data_length = 0; |
623 |
|
✗ |
ret = gate_stream_write_block((gate_stream_t*)impl->output, data_hdrs, sizeof(riff_hdrs), &bytes_written); |
624 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
625 |
|
|
|
626 |
|
✗ |
impl->opened = true; |
627 |
|
|
} |
628 |
|
|
|
629 |
|
|
} while (0); |
630 |
|
|
|
631 |
|
✗ |
return ret; |
632 |
|
|
} |
633 |
|
✗ |
static gate_result_t gate_audio_device_wave_stream_close(void* device) |
634 |
|
|
{ |
635 |
|
✗ |
gate_result_t ret = GATE_RESULT_OK; |
636 |
|
✗ |
gate_audio_wave_stream_impl_t* const impl = (gate_audio_wave_stream_impl_t*)device; |
637 |
|
|
|
638 |
|
✗ |
gate_int64_t cur_pos = 0; |
639 |
|
✗ |
gate_uint32_t data_len = 0; |
640 |
|
|
char buffer[4]; |
641 |
|
✗ |
gate_size_t len_written = 0; |
642 |
|
|
|
643 |
|
|
do |
644 |
|
|
{ |
645 |
|
✗ |
if (!impl->opened) |
646 |
|
|
{ |
647 |
|
|
/* is already closed */ |
648 |
|
✗ |
break; |
649 |
|
|
} |
650 |
|
|
|
651 |
|
✗ |
if (impl->output != NULL) |
652 |
|
|
{ |
653 |
|
✗ |
ret = gate_stream_get_position(impl->output, &cur_pos); |
654 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
655 |
|
|
|
656 |
|
|
/* total file_len - 8 is stored at offset 4 */ |
657 |
|
✗ |
GATE_DEBUG_ASSERT(cur_pos >= (impl->write_start_position + 8)); |
658 |
|
✗ |
data_len = (gate_uint32_t)(cur_pos - impl->write_start_position - 8); |
659 |
|
✗ |
gate_serialize_uint32_l(buffer, sizeof(buffer), &data_len); |
660 |
|
|
|
661 |
|
✗ |
ret = gate_stream_seek(impl->output, impl->write_start_position + 4, GATE_STREAM_SEEK_BEGIN, NULL); |
662 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
663 |
|
|
|
664 |
|
✗ |
ret = gate_stream_write_block((gate_stream_t*)impl->output, buffer, 4, &len_written); |
665 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
666 |
|
|
|
667 |
|
|
/* samples_length is stored at offset 40 */ |
668 |
|
✗ |
GATE_DEBUG_ASSERT(cur_pos >= (impl->write_start_position + 44)); |
669 |
|
✗ |
data_len = (gate_uint32_t)(cur_pos - impl->write_start_position - 44); |
670 |
|
✗ |
gate_serialize_uint32_l(buffer, sizeof(buffer), &data_len); |
671 |
|
|
|
672 |
|
✗ |
ret = gate_stream_seek(impl->output, impl->write_start_position + 40, GATE_STREAM_SEEK_BEGIN, NULL); |
673 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
674 |
|
|
|
675 |
|
✗ |
ret = gate_stream_write_block((gate_stream_t*)impl->output, buffer, 4, &len_written); |
676 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
677 |
|
|
|
678 |
|
|
|
679 |
|
|
/* go back to file end: */ |
680 |
|
✗ |
ret = gate_stream_seek(impl->output, cur_pos, GATE_STREAM_SEEK_BEGIN, NULL); |
681 |
|
|
|
682 |
|
✗ |
ret = gate_stream_flush(impl->output); |
683 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
684 |
|
|
} |
685 |
|
|
} while (0); |
686 |
|
|
|
687 |
|
✗ |
return ret; |
688 |
|
|
} |
689 |
|
|
|
690 |
|
✗ |
static gate_result_t gate_audio_device_wave_stream_read(void* device, gate_audio_sample_t* sample) |
691 |
|
|
{ |
692 |
|
✗ |
gate_audio_wave_stream_impl_t* impl = (gate_audio_wave_stream_impl_t*)device; |
693 |
|
✗ |
gate_result_t result = GATE_RESULT_OK; |
694 |
|
|
char riff_hdr[8]; |
695 |
|
|
gate_size_t bytes_received; |
696 |
|
|
gate_uint32_t data_len; |
697 |
|
✗ |
gate_bool_t data_found = false; |
698 |
|
✗ |
char* sample_data = NULL; |
699 |
|
|
|
700 |
|
|
do |
701 |
|
|
{ |
702 |
|
✗ |
if (!impl->opened) |
703 |
|
|
{ |
704 |
|
✗ |
result = GATE_RESULT_INVALIDSTATE; |
705 |
|
✗ |
break; |
706 |
|
|
} |
707 |
|
|
|
708 |
|
✗ |
if (impl->input == NULL) |
709 |
|
|
{ |
710 |
|
✗ |
result = GATE_RESULT_NOTSUPPORTED; |
711 |
|
✗ |
break; |
712 |
|
|
} |
713 |
|
✗ |
gate_mem_clear(sample, sizeof(gate_audio_sample_t)); |
714 |
|
|
|
715 |
|
✗ |
while (!data_found) |
716 |
|
|
{ |
717 |
|
✗ |
result = gate_stream_read_block(impl->input, riff_hdr, sizeof(riff_hdr), &bytes_received); |
718 |
|
✗ |
GATE_BREAK_IF_FAILED(result); |
719 |
|
✗ |
if (bytes_received == 0) |
720 |
|
|
{ |
721 |
|
✗ |
result = GATE_RESULT_ENDOFSTREAM; |
722 |
|
✗ |
break; |
723 |
|
|
} |
724 |
|
✗ |
if (bytes_received != sizeof(riff_hdr)) |
725 |
|
|
{ |
726 |
|
✗ |
result = GATE_RESULT_INVALIDHEADER; |
727 |
|
✗ |
break; |
728 |
|
|
} |
729 |
|
|
|
730 |
|
✗ |
gate_deserialize_uint32_l(&riff_hdr[4], 4, &data_len); |
731 |
|
✗ |
if (0 == gate_str_comp_range(riff_hdr, "data", 4)) |
732 |
|
|
{ |
733 |
|
✗ |
sample->format = impl->format; |
734 |
|
✗ |
sample->record_time = 0; |
735 |
|
✗ |
sample->meta_data = NULL; |
736 |
|
✗ |
sample->data = gate_memoryblock_create(data_len); |
737 |
|
✗ |
if (sample->data == NULL) |
738 |
|
|
{ |
739 |
|
✗ |
result = GATE_RESULT_OUTOFMEMORY; |
740 |
|
✗ |
break; |
741 |
|
|
} |
742 |
|
✗ |
sample_data = gate_memoryblock_get_content(sample->data); |
743 |
|
✗ |
result = gate_stream_read_block(impl->input, sample_data, data_len, &bytes_received); |
744 |
|
✗ |
GATE_BREAK_IF_FAILED(result); |
745 |
|
|
|
746 |
|
✗ |
sample->sample_count = (gate_uint32_t)(bytes_received / ((gate_size_t)impl->format.channels * ((gate_size_t)impl->format.bits_per_sample / 8))); |
747 |
|
✗ |
data_found = true; |
748 |
|
|
} |
749 |
|
|
else |
750 |
|
|
{ |
751 |
|
✗ |
result = gate_stream_skip(impl->input, data_len, NULL); |
752 |
|
✗ |
GATE_BREAK_IF_FAILED(result); |
753 |
|
|
} |
754 |
|
|
} |
755 |
|
|
} while (0); |
756 |
|
✗ |
return result; |
757 |
|
|
} |
758 |
|
✗ |
static gate_result_t gate_audio_device_wave_stream_read_async(void* device, gate_delegate_t const* completion) |
759 |
|
|
{ |
760 |
|
|
(void)device; |
761 |
|
|
(void)completion; |
762 |
|
✗ |
return GATE_RESULT_NOTSUPPORTED; |
763 |
|
|
} |
764 |
|
✗ |
static gate_result_t gate_audio_device_wave_stream_write(void* device, gate_audio_sample_t const* sample) |
765 |
|
|
{ |
766 |
|
✗ |
gate_audio_wave_stream_impl_t* impl = (gate_audio_wave_stream_impl_t*)device; |
767 |
|
✗ |
gate_result_t result = GATE_RESULT_FAILED; |
768 |
|
|
//char hdr[8] = "data"; |
769 |
|
|
gate_uint32_t memsize; |
770 |
|
|
char const* memdata; |
771 |
|
|
gate_size_t data_written; |
772 |
|
|
do |
773 |
|
|
{ |
774 |
|
✗ |
if (!impl->opened) |
775 |
|
|
{ |
776 |
|
✗ |
result = GATE_RESULT_INVALIDSTATE; |
777 |
|
✗ |
break; |
778 |
|
|
} |
779 |
|
✗ |
if (impl->output == NULL) |
780 |
|
|
{ |
781 |
|
✗ |
result = GATE_RESULT_NOTSUPPORTED; |
782 |
|
✗ |
break; |
783 |
|
|
} |
784 |
|
✗ |
memsize = (gate_uint32_t)gate_memoryblock_get_size(sample->data); |
785 |
|
✗ |
memdata = (char*)gate_memoryblock_get_content(sample->data); |
786 |
|
|
|
787 |
|
|
/*gate_serialize_uint32_l(&hdr[4], 4, &memsize); |
788 |
|
|
result = gate_stream_write_block((gate_stream_t*)impl->output, hdr, sizeof(hdr), &data_written); |
789 |
|
|
GATE_BREAK_IF_FAILED(result); |
790 |
|
|
*/ |
791 |
|
|
|
792 |
|
✗ |
result = gate_stream_write_block((gate_stream_t*)impl->output, memdata, memsize, &data_written); |
793 |
|
|
} while (0); |
794 |
|
|
|
795 |
|
✗ |
return result; |
796 |
|
|
} |
797 |
|
✗ |
static gate_result_t gate_audio_device_wave_stream_write_async(void* device, gate_audio_sample_t const* sample, gate_delegate_t const* completion) |
798 |
|
|
{ |
799 |
|
|
(void)device; |
800 |
|
|
(void)sample; |
801 |
|
|
(void)completion; |
802 |
|
✗ |
return GATE_RESULT_NOTSUPPORTED; |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
|
806 |
|
✗ |
gate_result_t gate_audio_create_wave_reader(gate_stream_t* input, gate_audio_device_t** ptr_reader) |
807 |
|
|
{ |
808 |
|
|
gate_result_t ret; |
809 |
|
|
gate_audio_wave_stream_impl_t* impl; |
810 |
|
|
do |
811 |
|
|
{ |
812 |
|
✗ |
if ((input == NULL) || (ptr_reader == NULL)) |
813 |
|
|
{ |
814 |
|
✗ |
ret = GATE_RESULT_INVALIDARG; |
815 |
|
✗ |
break; |
816 |
|
|
} |
817 |
|
✗ |
impl = (gate_audio_wave_stream_impl_t*)gate_mem_alloc(sizeof(gate_audio_wave_stream_impl_t)); |
818 |
|
✗ |
if (impl == NULL) |
819 |
|
|
{ |
820 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
821 |
|
✗ |
break; |
822 |
|
|
} |
823 |
|
✗ |
gate_mem_clear(impl, sizeof(gate_audio_wave_stream_impl_t)); |
824 |
|
✗ |
gate_init_audio_device_vtbl_wave_stream(); |
825 |
|
✗ |
impl->vtbl = &gate_audio_device_vtbl_wave_stream; |
826 |
|
✗ |
impl->input = input; |
827 |
|
✗ |
gate_object_retain(impl->input); |
828 |
|
✗ |
gate_atomic_int_init(&impl->ref_counter, 1); |
829 |
|
✗ |
gate_atomic_int_set(&impl->format_defined, 0); |
830 |
|
✗ |
ret = GATE_RESULT_OK; |
831 |
|
✗ |
*ptr_reader = (gate_audio_device_t*)impl; |
832 |
|
|
} while (0); |
833 |
|
✗ |
return ret; |
834 |
|
|
} |
835 |
|
|
|
836 |
|
|
|
837 |
|
✗ |
gate_result_t gate_audio_create_wave_writer(gate_controlstream_t* output, gate_audio_device_t** ptr_writer) |
838 |
|
|
{ |
839 |
|
|
gate_result_t ret; |
840 |
|
|
gate_audio_wave_stream_impl_t* impl; |
841 |
|
|
do |
842 |
|
|
{ |
843 |
|
✗ |
if ((output == NULL) || (ptr_writer == NULL)) |
844 |
|
|
{ |
845 |
|
✗ |
ret = GATE_RESULT_INVALIDARG; |
846 |
|
✗ |
break; |
847 |
|
|
} |
848 |
|
✗ |
impl = (gate_audio_wave_stream_impl_t*)gate_mem_alloc(sizeof(gate_audio_wave_stream_impl_t)); |
849 |
|
✗ |
if (impl == NULL) |
850 |
|
|
{ |
851 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
852 |
|
✗ |
break; |
853 |
|
|
} |
854 |
|
✗ |
gate_mem_clear(impl, sizeof(gate_audio_wave_stream_impl_t)); |
855 |
|
✗ |
gate_init_audio_device_vtbl_wave_stream(); |
856 |
|
✗ |
impl->vtbl = &gate_audio_device_vtbl_wave_stream; |
857 |
|
✗ |
impl->output = output; |
858 |
|
✗ |
gate_object_retain(impl->output); |
859 |
|
✗ |
gate_atomic_int_init(&impl->ref_counter, 1); |
860 |
|
✗ |
gate_atomic_int_set(&impl->format_defined, 0); |
861 |
|
✗ |
ret = GATE_RESULT_OK; |
862 |
|
✗ |
*ptr_writer = (gate_audio_device_t*)impl; |
863 |
|
|
} while (0); |
864 |
|
✗ |
return ret; |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
|
868 |
|
|
|
869 |
|
|
|
870 |
|
|
#if defined(GATE_IO_AUDIO_WINAPI) |
871 |
|
|
|
872 |
|
|
# if defined(UNICODE) || defined(_UNICODE) |
873 |
|
|
# include <winsock2.h> |
874 |
|
|
# else |
875 |
|
|
# include <winsock.h> |
876 |
|
|
# endif |
877 |
|
|
# include <windows.h> |
878 |
|
|
# include "gate/platforms.h" |
879 |
|
|
|
880 |
|
|
struct gate_win32_format_mapping |
881 |
|
|
{ |
882 |
|
|
DWORD win_format; |
883 |
|
|
gate_audio_format_t gate_format; |
884 |
|
|
}; |
885 |
|
|
|
886 |
|
|
static struct gate_win32_format_mapping gate_win32_all_formats[] = |
887 |
|
|
{ |
888 |
|
|
#if defined(WAVE_FORMAT_48M08) |
889 |
|
|
{ WAVE_FORMAT_48M08, { 1, 48000, 1, 8 } }, |
890 |
|
|
#endif |
891 |
|
|
#if defined(WAVE_FORMAT_48M16) |
892 |
|
|
{ WAVE_FORMAT_48M16, { 1, 48000, 1, 16 } }, |
893 |
|
|
#endif |
894 |
|
|
#if defined(WAVE_FORMAT_48S08) |
895 |
|
|
{ WAVE_FORMAT_48S08, { 1, 48000, 2, 8 } }, |
896 |
|
|
#endif |
897 |
|
|
#if defined(WAVE_FORMAT_48S16) |
898 |
|
|
{ WAVE_FORMAT_48S16, { 1, 48000, 2, 16 } }, |
899 |
|
|
#endif |
900 |
|
|
#if defined(WAVE_FORMAT_96M08) |
901 |
|
|
{ WAVE_FORMAT_96M08, { 1, 96000, 1, 8 } }, |
902 |
|
|
#endif |
903 |
|
|
#if defined(WAVE_FORMAT_96M16) |
904 |
|
|
{ WAVE_FORMAT_96M16, { 1, 96000, 1, 16 } }, |
905 |
|
|
#endif |
906 |
|
|
#if defined(WAVE_FORMAT_96S08) |
907 |
|
|
{ WAVE_FORMAT_96S08, { 1, 96000, 2, 8 } }, |
908 |
|
|
#endif |
909 |
|
|
#if defined(WAVE_FORMAT_96S16) |
910 |
|
|
{ WAVE_FORMAT_96S16, { 1, 96000, 2, 16 } }, |
911 |
|
|
#endif |
912 |
|
|
{ WAVE_FORMAT_1M08, { 1, 11025, 1, 8 } }, |
913 |
|
|
{ WAVE_FORMAT_1M16, { 1, 11025, 1, 16 } }, |
914 |
|
|
{ WAVE_FORMAT_1S08, { 1, 11025, 2, 8 } }, |
915 |
|
|
{ WAVE_FORMAT_1S16, { 1, 11025, 2, 16 } }, |
916 |
|
|
{ WAVE_FORMAT_2M08, { 1, 22050, 1, 8 } }, |
917 |
|
|
{ WAVE_FORMAT_2M16, { 1, 22050, 1, 16 } }, |
918 |
|
|
{ WAVE_FORMAT_2S08, { 1, 22050, 2, 8 } }, |
919 |
|
|
{ WAVE_FORMAT_2S16, { 1, 22050, 2, 16 } }, |
920 |
|
|
{ WAVE_FORMAT_4M08, { 1, 44100, 1, 8 } }, |
921 |
|
|
{ WAVE_FORMAT_4M16, { 1, 44100, 1, 16 } }, |
922 |
|
|
{ WAVE_FORMAT_4S08, { 1, 44100, 2, 8 } }, |
923 |
|
|
{ WAVE_FORMAT_4S16, { 1, 44100, 2, 16 } }, |
924 |
|
|
}; |
925 |
|
|
|
926 |
|
|
typedef struct |
927 |
|
|
{ |
928 |
|
|
UINT(WINAPI* MMwaveInGetNumDevs) (VOID); |
929 |
|
|
MMRESULT(WINAPI* MMwaveInGetDevCaps) (UINT uDeviceID, LPWAVEINCAPS pwic, UINT cbwic); |
930 |
|
|
MMRESULT(WINAPI* MMwaveInPrepareHeader) (HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); |
931 |
|
|
MMRESULT(WINAPI* MMwaveInUnprepareHeader) (HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); |
932 |
|
|
MMRESULT(WINAPI* MMwaveInAddBuffer) (HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); |
933 |
|
|
MMRESULT(WINAPI* MMwaveInOpen) (LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); |
934 |
|
|
MMRESULT(WINAPI* MMwaveInStart) (HWAVEIN hwi); |
935 |
|
|
MMRESULT(WINAPI* MMwaveInReset) (HWAVEIN hwi); |
936 |
|
|
MMRESULT(WINAPI* MMwaveInStop) (HWAVEIN hwi); |
937 |
|
|
MMRESULT(WINAPI* MMwaveInClose) (HWAVEIN hwi); |
938 |
|
|
|
939 |
|
|
UINT(WINAPI* MMwaveOutGetNumDevs) (VOID); |
940 |
|
|
MMRESULT(WINAPI* MMwaveOutGetDevCaps) (UINT uDeviceID, LPWAVEOUTCAPS pwoc, UINT cbwoc); |
941 |
|
|
MMRESULT(WINAPI* MMwaveOutPrepareHeader) (HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); |
942 |
|
|
MMRESULT(WINAPI* MMwaveOutUnprepareHeader) (HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); |
943 |
|
|
MMRESULT(WINAPI* MMwaveOutOpen) (LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); |
944 |
|
|
MMRESULT(WINAPI* MMwaveOutWrite) (HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); |
945 |
|
|
MMRESULT(WINAPI* MMwaveOutReset) (HWAVEOUT hwo); |
946 |
|
|
MMRESULT(WINAPI* MMwaveOutClose) (HWAVEOUT hwo); |
947 |
|
|
} win32_audio_functions_t; |
948 |
|
|
|
949 |
|
|
static win32_audio_functions_t win32_audio = GATE_INIT_EMPTY; |
950 |
|
|
static volatile gate_bool_t win32_audio_functions_loaded = false; |
951 |
|
|
|
952 |
|
|
static gate_bool_t load_winmm_functions() |
953 |
|
|
{ |
954 |
|
|
static volatile HMODULE hmod_winmm = NULL; |
955 |
|
|
|
956 |
|
|
do |
957 |
|
|
{ |
958 |
|
|
if (win32_audio_functions_loaded) |
959 |
|
|
{ |
960 |
|
|
break; |
961 |
|
|
} |
962 |
|
|
|
963 |
|
|
if (hmod_winmm == NULL) |
964 |
|
|
{ |
965 |
|
|
hmod_winmm = LoadLibrary(_T("winmm.dll")); |
966 |
|
|
if (hmod_winmm == NULL) |
967 |
|
|
{ |
968 |
|
|
break; |
969 |
|
|
} |
970 |
|
|
} |
971 |
|
|
//gate_win32_get_proc_address(hmod_winmm, "", &win32_audio.); |
972 |
|
|
|
973 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveInGetNumDevs", &win32_audio.MMwaveInGetNumDevs)) break; |
974 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, GATE_WIN32_DUAL_NAME("waveInGetDevCaps"), &win32_audio.MMwaveInGetDevCaps)) break; |
975 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveInPrepareHeader", &win32_audio.MMwaveInPrepareHeader)) break; |
976 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveInUnprepareHeader", &win32_audio.MMwaveInUnprepareHeader)) break; |
977 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveInAddBuffer", &win32_audio.MMwaveInAddBuffer)) break; |
978 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveInOpen", &win32_audio.MMwaveInOpen)) break; |
979 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveInStart", &win32_audio.MMwaveInStart)) break; |
980 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveInReset", &win32_audio.MMwaveInReset)) break; |
981 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveInStop", &win32_audio.MMwaveInStop)) break; |
982 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveInClose", &win32_audio.MMwaveInClose)) break; |
983 |
|
|
|
984 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveOutGetNumDevs", &win32_audio.MMwaveOutGetNumDevs)) break; |
985 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, GATE_WIN32_DUAL_NAME("waveOutGetDevCaps"), &win32_audio.MMwaveOutGetDevCaps)) break; |
986 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveOutPrepareHeader", &win32_audio.MMwaveOutPrepareHeader)) break; |
987 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveOutUnprepareHeader", &win32_audio.MMwaveOutUnprepareHeader)) break; |
988 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveOutOpen", &win32_audio.MMwaveOutOpen)) break; |
989 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveOutWrite", &win32_audio.MMwaveOutWrite)) break; |
990 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveOutReset", &win32_audio.MMwaveOutReset)) break; |
991 |
|
|
if (!gate_win32_get_proc_address(hmod_winmm, "waveOutClose", &win32_audio.MMwaveOutClose)) break; |
992 |
|
|
|
993 |
|
|
win32_audio_functions_loaded = true; |
994 |
|
|
|
995 |
|
|
} while (0); |
996 |
|
|
|
997 |
|
|
return win32_audio_functions_loaded; |
998 |
|
|
} |
999 |
|
|
|
1000 |
|
|
static gate_size_t gate_win32_get_formats(DWORD win_formats, gate_audio_format_t* formats, gate_size_t format_count) |
1001 |
|
|
{ |
1002 |
|
|
static gate_size_t gate_win32_all_format_count = sizeof(gate_win32_all_formats) / sizeof(gate_win32_all_formats[0]); |
1003 |
|
|
gate_size_t ret = 0; |
1004 |
|
|
gate_size_t index; |
1005 |
|
|
|
1006 |
|
|
for (index = 0; index != gate_win32_all_format_count; ++index) |
1007 |
|
|
{ |
1008 |
|
|
if (win_formats & gate_win32_all_formats[index].win_format) |
1009 |
|
|
{ |
1010 |
|
|
*formats = gate_win32_all_formats[index].gate_format; |
1011 |
|
|
++ret; |
1012 |
|
|
++formats; |
1013 |
|
|
if (ret >= format_count) |
1014 |
|
|
{ |
1015 |
|
|
break; |
1016 |
|
|
} |
1017 |
|
|
} |
1018 |
|
|
} |
1019 |
|
|
return ret; |
1020 |
|
|
} |
1021 |
|
|
|
1022 |
|
|
typedef struct gate_audio_device_impl |
1023 |
|
|
{ |
1024 |
|
|
GATE_INTERFACE_VTBL(gate_audio_device) const* vtbl; |
1025 |
|
|
|
1026 |
|
|
gate_atomic_int_t ref_counter; |
1027 |
|
|
UINT device_id; |
1028 |
|
|
char device_id_text[32]; |
1029 |
|
|
char device_name[128]; |
1030 |
|
|
gate_enumint_t device_type; |
1031 |
|
|
HWAVEIN wave_in_handle; |
1032 |
|
|
HWAVEOUT wave_out_handle; |
1033 |
|
|
HANDLE event_handle; |
1034 |
|
|
gate_audio_format_t supported_formats[32]; |
1035 |
|
|
gate_size_t supported_formats_count; |
1036 |
|
|
gate_audio_format_t selected_format; |
1037 |
|
|
gate_size_t default_sample_count; |
1038 |
|
|
|
1039 |
|
|
gate_atomic_flag_t exequeue_running; |
1040 |
|
|
gate_exequeue_t exequeue; |
1041 |
|
|
|
1042 |
|
|
} gate_audio_device_impl_t; |
1043 |
|
|
|
1044 |
|
|
static void gate_audio_device_impl_release(void* device) |
1045 |
|
|
{ |
1046 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1047 |
|
|
if (gate_atomic_int_dec(&impl->ref_counter) == 0) |
1048 |
|
|
{ |
1049 |
|
|
gate_audio_device_impl_close(device); |
1050 |
|
|
|
1051 |
|
|
if (impl->event_handle != NULL) |
1052 |
|
|
{ |
1053 |
|
|
CloseHandle(impl->event_handle); |
1054 |
|
|
impl->event_handle = NULL; |
1055 |
|
|
} |
1056 |
|
|
|
1057 |
|
|
gate_mem_dealloc(impl); |
1058 |
|
|
} |
1059 |
|
|
} |
1060 |
|
|
static int gate_audio_device_impl_retain(void* device) |
1061 |
|
|
{ |
1062 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1063 |
|
|
return gate_atomic_int_inc(&impl->ref_counter); |
1064 |
|
|
} |
1065 |
|
|
|
1066 |
|
|
static char const* gate_audio_device_impl_get_id(void* device) |
1067 |
|
|
{ |
1068 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1069 |
|
|
return impl->device_id_text; |
1070 |
|
|
} |
1071 |
|
|
static char const* gate_audio_device_impl_get_name(void* device) |
1072 |
|
|
{ |
1073 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1074 |
|
|
return impl->device_name; |
1075 |
|
|
} |
1076 |
|
|
static gate_intptr_t gate_audio_device_impl_get_handle(void* device) |
1077 |
|
|
{ |
1078 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1079 |
|
|
return (gate_intptr_t)impl->device_id; |
1080 |
|
|
} |
1081 |
|
|
static gate_enumint_t gate_audio_device_impl_get_device_type(void* device) |
1082 |
|
|
{ |
1083 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1084 |
|
|
return impl->device_type; |
1085 |
|
|
} |
1086 |
|
|
static gate_size_t gate_audio_device_impl_get_supported_formats(void* device, gate_audio_format_t* format_buffer, gate_size_t format_buffer_count) |
1087 |
|
|
{ |
1088 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1089 |
|
|
gate_size_t count = format_buffer_count > impl->supported_formats_count ? impl->supported_formats_count : format_buffer_count; |
1090 |
|
|
gate_mem_copy(format_buffer, &impl->supported_formats[0], sizeof(gate_audio_format_t) * count); |
1091 |
|
|
return count; |
1092 |
|
|
} |
1093 |
|
|
|
1094 |
|
|
static gate_result_t gate_audio_device_impl_open(void* device, gate_audio_format_t const* format, gate_size_t sample_count) |
1095 |
|
|
{ |
1096 |
|
|
gate_result_t ret = GATE_RESULT_NOTSUPPORTED; |
1097 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1098 |
|
|
WAVEFORMATEX format_ex; |
1099 |
|
|
MMRESULT mm_result; |
1100 |
|
|
|
1101 |
|
|
do |
1102 |
|
|
{ |
1103 |
|
|
if (sample_count == 0) |
1104 |
|
|
{ |
1105 |
|
|
sample_count = format->samples_per_second; |
1106 |
|
|
} |
1107 |
|
|
|
1108 |
|
|
impl->selected_format = *format; |
1109 |
|
|
if (impl->event_handle == NULL) |
1110 |
|
|
{ |
1111 |
|
|
impl->event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); |
1112 |
|
|
if (NULL == impl->event_handle) |
1113 |
|
|
{ |
1114 |
|
|
ret = GATE_RESULT_OUTOFRESOURCES; |
1115 |
|
|
break; |
1116 |
|
|
} |
1117 |
|
|
} |
1118 |
|
|
impl->default_sample_count = sample_count; |
1119 |
|
|
|
1120 |
|
|
format_ex.wFormatTag = (WORD)format->encoding_id; |
1121 |
|
|
format_ex.nChannels = format->channels; |
1122 |
|
|
format_ex.nSamplesPerSec = format->samples_per_second; |
1123 |
|
|
format_ex.wBitsPerSample = format->bits_per_sample; |
1124 |
|
|
format_ex.nBlockAlign = (format_ex.nChannels * format_ex.wBitsPerSample) >> 3; |
1125 |
|
|
format_ex.nAvgBytesPerSec = (format_ex.nBlockAlign * format_ex.nSamplesPerSec); |
1126 |
|
|
format_ex.cbSize = 0; |
1127 |
|
|
|
1128 |
|
|
if (impl->device_type == gate_audio_device_input) |
1129 |
|
|
{ |
1130 |
|
|
if (impl->wave_in_handle != NULL) |
1131 |
|
|
{ |
1132 |
|
|
ret = GATE_RESULT_INVALIDSTATE; |
1133 |
|
|
break; |
1134 |
|
|
} |
1135 |
|
|
|
1136 |
|
|
mm_result = win32_audio.MMwaveInOpen(&impl->wave_in_handle, impl->device_id, &format_ex, |
1137 |
|
|
(DWORD_PTR)impl->event_handle, (DWORD_PTR)device, CALLBACK_EVENT); |
1138 |
|
|
if (MMSYSERR_NOERROR != mm_result) |
1139 |
|
|
{ |
1140 |
|
|
ret = GATE_RESULT_FAILED; |
1141 |
|
|
impl->wave_in_handle = NULL; |
1142 |
|
|
break; |
1143 |
|
|
} |
1144 |
|
|
|
1145 |
|
|
mm_result = win32_audio.MMwaveInStart(impl->wave_in_handle); |
1146 |
|
|
if (MMSYSERR_NOERROR != mm_result) |
1147 |
|
|
{ |
1148 |
|
|
ret = GATE_RESULT_FAILED; |
1149 |
|
|
win32_audio.MMwaveInClose(impl->wave_in_handle); |
1150 |
|
|
impl->wave_in_handle = NULL; |
1151 |
|
|
break; |
1152 |
|
|
} |
1153 |
|
|
|
1154 |
|
|
ResetEvent(impl->event_handle); |
1155 |
|
|
ret = GATE_RESULT_OK; |
1156 |
|
|
} |
1157 |
|
|
else if (impl->device_type == gate_audio_device_output) |
1158 |
|
|
{ |
1159 |
|
|
if (impl->wave_out_handle != NULL) |
1160 |
|
|
{ |
1161 |
|
|
ret = GATE_RESULT_INVALIDSTATE; |
1162 |
|
|
break; |
1163 |
|
|
} |
1164 |
|
|
|
1165 |
|
|
mm_result = win32_audio.MMwaveOutOpen(&impl->wave_out_handle, impl->device_id, &format_ex, |
1166 |
|
|
(DWORD_PTR)impl->event_handle, (DWORD_PTR)device, CALLBACK_EVENT); |
1167 |
|
|
if (MMSYSERR_NOERROR != mm_result) |
1168 |
|
|
{ |
1169 |
|
|
impl->wave_out_handle = NULL; |
1170 |
|
|
ret = GATE_RESULT_FAILED; |
1171 |
|
|
break; |
1172 |
|
|
} |
1173 |
|
|
|
1174 |
|
|
ResetEvent(impl->event_handle); |
1175 |
|
|
ret = GATE_RESULT_OK; |
1176 |
|
|
} |
1177 |
|
|
|
1178 |
|
|
} while (0); |
1179 |
|
|
return ret; |
1180 |
|
|
} |
1181 |
|
|
static gate_result_t gate_audio_device_impl_close(void* device) |
1182 |
|
|
{ |
1183 |
|
|
gate_result_t ret = GATE_RESULT_OK; |
1184 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1185 |
|
|
|
1186 |
|
|
if (impl->wave_in_handle != NULL) |
1187 |
|
|
{ |
1188 |
|
|
win32_audio.MMwaveInStop(impl->wave_in_handle); |
1189 |
|
|
win32_audio.MMwaveInReset(impl->wave_in_handle); |
1190 |
|
|
win32_audio.MMwaveInClose(impl->wave_in_handle); |
1191 |
|
|
impl->wave_in_handle = NULL; |
1192 |
|
|
} |
1193 |
|
|
|
1194 |
|
|
if (impl->wave_out_handle != NULL) |
1195 |
|
|
{ |
1196 |
|
|
win32_audio.MMwaveOutReset(impl->wave_out_handle); |
1197 |
|
|
win32_audio.MMwaveOutClose(impl->wave_out_handle); |
1198 |
|
|
impl->wave_out_handle = NULL; |
1199 |
|
|
} |
1200 |
|
|
|
1201 |
|
|
return ret; |
1202 |
|
|
} |
1203 |
|
|
|
1204 |
|
|
static gate_result_t gate_audio_device_impl_read(void* device, gate_audio_sample_t* sample) |
1205 |
|
|
{ |
1206 |
|
|
gate_result_t ret = GATE_RESULT_FAILED; |
1207 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1208 |
|
|
|
1209 |
|
|
WAVEHDR wavehdr; |
1210 |
|
|
char* ptr_buffer = NULL; |
1211 |
|
|
MMRESULT mm_result; |
1212 |
|
|
|
1213 |
|
|
gate_size_t buffersize; |
1214 |
|
|
|
1215 |
|
|
do |
1216 |
|
|
{ |
1217 |
|
|
if (impl->wave_in_handle == NULL) |
1218 |
|
|
{ |
1219 |
|
|
ret = GATE_RESULT_INVALIDSTATE; |
1220 |
|
|
break; |
1221 |
|
|
} |
1222 |
|
|
|
1223 |
|
|
gate_mem_clear(sample, sizeof(gate_audio_sample_t)); |
1224 |
|
|
|
1225 |
|
|
buffersize = impl->default_sample_count |
1226 |
|
|
* impl->selected_format.channels |
1227 |
|
|
* impl->selected_format.bits_per_sample |
1228 |
|
|
/ 8 |
1229 |
|
|
; |
1230 |
|
|
|
1231 |
|
|
if (buffersize == 0) |
1232 |
|
|
{ |
1233 |
|
|
ret = GATE_RESULT_INVALIDARG; |
1234 |
|
|
break; |
1235 |
|
|
} |
1236 |
|
|
|
1237 |
|
|
sample->sample_count = (gate_uint32_t)impl->default_sample_count; |
1238 |
|
|
sample->format = impl->selected_format; |
1239 |
|
|
sample->record_time = 0; |
1240 |
|
|
sample->data = gate_memoryblock_create(buffersize); |
1241 |
|
|
if (sample->data == NULL) |
1242 |
|
|
{ |
1243 |
|
|
ret = GATE_RESULT_OUTOFMEMORY; |
1244 |
|
|
break; |
1245 |
|
|
} |
1246 |
|
|
ptr_buffer = (char*)gate_memoryblock_get_content(sample->data); |
1247 |
|
|
|
1248 |
|
|
gate_mem_clear(&wavehdr, sizeof(wavehdr)); |
1249 |
|
|
wavehdr.dwBufferLength = (DWORD)buffersize; |
1250 |
|
|
wavehdr.lpData = ptr_buffer; |
1251 |
|
|
|
1252 |
|
|
mm_result = win32_audio.MMwaveInPrepareHeader(impl->wave_in_handle, &wavehdr, sizeof(wavehdr)); |
1253 |
|
|
if (MMSYSERR_NOERROR != mm_result) |
1254 |
|
|
{ |
1255 |
|
|
ret = GATE_RESULT_FAILED; |
1256 |
|
|
break; |
1257 |
|
|
} |
1258 |
|
|
|
1259 |
|
|
mm_result = win32_audio.MMwaveInAddBuffer(impl->wave_in_handle, &wavehdr, sizeof(wavehdr)); |
1260 |
|
|
if (MMSYSERR_NOERROR != mm_result) |
1261 |
|
|
{ |
1262 |
|
|
mm_result = win32_audio.MMwaveInUnprepareHeader(impl->wave_in_handle, &wavehdr, sizeof(wavehdr)); |
1263 |
|
|
ret = GATE_RESULT_FAILED; |
1264 |
|
|
break; |
1265 |
|
|
} |
1266 |
|
|
|
1267 |
|
|
WaitForSingleObject(impl->event_handle, INFINITE); |
1268 |
|
|
mm_result = win32_audio.MMwaveInUnprepareHeader(impl->wave_in_handle, &wavehdr, sizeof(wavehdr)); |
1269 |
|
|
sample->sample_count = wavehdr.dwBytesRecorded * 8 |
1270 |
|
|
/ impl->selected_format.bits_per_sample |
1271 |
|
|
/ impl->selected_format.channels; |
1272 |
|
|
sample->format = impl->selected_format; |
1273 |
|
|
ret = GATE_RESULT_OK; |
1274 |
|
|
|
1275 |
|
|
} while (0); |
1276 |
|
|
|
1277 |
|
|
if (GATE_FAILED(ret)) |
1278 |
|
|
{ |
1279 |
|
|
if (sample->data != NULL) |
1280 |
|
|
{ |
1281 |
|
|
gate_object_release(sample->data); |
1282 |
|
|
} |
1283 |
|
|
if (sample->meta_data != NULL) |
1284 |
|
|
{ |
1285 |
|
|
gate_object_release(sample->meta_data); |
1286 |
|
|
} |
1287 |
|
|
gate_mem_clear(sample, sizeof(gate_audio_sample_t)); |
1288 |
|
|
} |
1289 |
|
|
return ret; |
1290 |
|
|
} |
1291 |
|
|
static gate_result_t gate_audio_device_impl_write(void* device, gate_audio_sample_t const* sample) |
1292 |
|
|
{ |
1293 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1294 |
|
|
gate_result_t ret = GATE_RESULT_OK; |
1295 |
|
|
|
1296 |
|
|
WAVEHDR wavehdr; |
1297 |
|
|
MMRESULT mm_result; |
1298 |
|
|
|
1299 |
|
|
do |
1300 |
|
|
{ |
1301 |
|
|
if (impl->wave_out_handle == NULL) |
1302 |
|
|
{ |
1303 |
|
|
ret = GATE_RESULT_INVALIDSTATE; |
1304 |
|
|
break; |
1305 |
|
|
} |
1306 |
|
|
gate_mem_clear(&wavehdr, sizeof(wavehdr)); |
1307 |
|
|
|
1308 |
|
|
wavehdr.dwBufferLength = (DWORD)gate_memoryblock_get_size(sample->data); |
1309 |
|
|
wavehdr.lpData = (char*)gate_memoryblock_get_content(sample->data); |
1310 |
|
|
|
1311 |
|
|
if ((wavehdr.lpData == NULL) || (wavehdr.dwBufferLength == 0)) |
1312 |
|
|
{ |
1313 |
|
|
ret = GATE_RESULT_INVALIDARG; |
1314 |
|
|
break; |
1315 |
|
|
} |
1316 |
|
|
|
1317 |
|
|
mm_result = win32_audio.MMwaveOutPrepareHeader(impl->wave_out_handle, &wavehdr, (UINT)sizeof(wavehdr)); |
1318 |
|
|
if (mm_result != MMSYSERR_NOERROR) |
1319 |
|
|
{ |
1320 |
|
|
ret = GATE_RESULT_FAILED; |
1321 |
|
|
break; |
1322 |
|
|
} |
1323 |
|
|
mm_result = win32_audio.MMwaveOutWrite(impl->wave_out_handle, &wavehdr, (UINT)sizeof(wavehdr)); |
1324 |
|
|
if (mm_result != MMSYSERR_NOERROR) |
1325 |
|
|
{ |
1326 |
|
|
ret = GATE_RESULT_FAILED; |
1327 |
|
|
mm_result = win32_audio.MMwaveOutUnprepareHeader(impl->wave_out_handle, &wavehdr, (UINT)sizeof(wavehdr)); |
1328 |
|
|
break; |
1329 |
|
|
} |
1330 |
|
|
|
1331 |
|
|
WaitForSingleObject(impl->event_handle, INFINITE); |
1332 |
|
|
mm_result = win32_audio.MMwaveOutUnprepareHeader(impl->wave_out_handle, &wavehdr, (UINT)sizeof(wavehdr)); |
1333 |
|
|
ret = GATE_RESULT_OK; |
1334 |
|
|
} while (0); |
1335 |
|
|
|
1336 |
|
|
return ret; |
1337 |
|
|
} |
1338 |
|
|
|
1339 |
|
|
//static gate_result_t / |
1340 |
|
|
|
1341 |
|
|
static gate_result_t gate_audio_device_impl_read_async(void* device, gate_delegate_t const* completion) |
1342 |
|
|
{ |
1343 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1344 |
|
|
gate_result_t result = GATE_RESULT_FAILED; |
1345 |
|
|
|
1346 |
|
|
do |
1347 |
|
|
{ |
1348 |
|
|
if (gate_atomic_flag_set(&impl->exequeue_running) == false) |
1349 |
|
|
{ |
1350 |
|
|
result = gate_exequeue_create(&impl->exequeue, NULL, NULL, NULL); |
1351 |
|
|
GATE_BREAK_IF_FAILED(result); |
1352 |
|
|
} |
1353 |
|
|
/*TODO*/ |
1354 |
|
|
//gate_exequeue_push(impl->exequeue, ) |
1355 |
|
|
|
1356 |
|
|
} while (0); |
1357 |
|
|
|
1358 |
|
|
return result; |
1359 |
|
|
} |
1360 |
|
|
|
1361 |
|
|
static gate_result_t gate_audio_device_impl_write_async(void* device, gate_audio_sample_t const* sample, gate_delegate_t const* completion) |
1362 |
|
|
{ |
1363 |
|
|
gate_audio_device_impl_t* impl = (gate_audio_device_impl_t*)device; |
1364 |
|
|
gate_result_t result = GATE_RESULT_OK; |
1365 |
|
|
|
1366 |
|
|
/*TODO*/ |
1367 |
|
|
|
1368 |
|
|
return result; |
1369 |
|
|
} |
1370 |
|
|
|
1371 |
|
|
|
1372 |
|
|
gate_result_t gate_audio_devices_enum(gate_enumint_t type, gate_delegate_t const* callback, void* param) |
1373 |
|
|
{ |
1374 |
|
|
gate_result_t ret = GATE_RESULT_OK; |
1375 |
|
|
UINT num_devs; |
1376 |
|
|
UINT dev_index; |
1377 |
|
|
WAVEINCAPS incaps; |
1378 |
|
|
WAVEOUTCAPS outcaps; |
1379 |
|
|
MMRESULT mmresult; |
1380 |
|
|
|
1381 |
|
|
if (!load_winmm_functions()) |
1382 |
|
|
{ |
1383 |
|
|
return GATE_RESULT_NOTSUPPORTED; |
1384 |
|
|
} |
1385 |
|
|
|
1386 |
|
|
num_devs = win32_audio.MMwaveInGetNumDevs(); |
1387 |
|
|
|
1388 |
|
|
if ((type == gate_audio_device_unknown) || (type == gate_audio_device_input)) |
1389 |
|
|
{ |
1390 |
|
|
for (dev_index = 0; dev_index < num_devs; dev_index++) |
1391 |
|
|
{ |
1392 |
|
|
mmresult = win32_audio.MMwaveInGetDevCaps(dev_index, &incaps, sizeof(incaps)); |
1393 |
|
|
if (MMSYSERR_NOERROR == mmresult) |
1394 |
|
|
{ |
1395 |
|
|
gate_audio_device_impl_t* impl = gate_mem_alloc(sizeof(gate_audio_device_impl_t)); |
1396 |
|
|
if (impl != NULL) |
1397 |
|
|
{ |
1398 |
|
|
gate_mem_clear(impl, sizeof(gate_audio_device_impl_t)); |
1399 |
|
|
gate_init_audio_device_vtbl_impl(); |
1400 |
|
|
impl->vtbl = &gate_audio_device_vtbl_impl; |
1401 |
|
|
|
1402 |
|
|
gate_atomic_int_init(&impl->ref_counter, 1); |
1403 |
|
|
|
1404 |
|
|
impl->device_id = dev_index; |
1405 |
|
|
impl->device_type = gate_audio_device_input; |
1406 |
|
|
|
1407 |
|
|
gate_str_print_uint(&impl->device_id_text[1], sizeof(impl->device_id_text) - 1, dev_index, 0); |
1408 |
|
|
impl->device_id_text[0] = 'I'; |
1409 |
|
|
|
1410 |
|
|
gate_win32_winstr_2_utf8(incaps.szPname, sizeof(incaps.szPname) / sizeof(incaps.szPname[0]), |
1411 |
|
|
impl->device_name, sizeof(impl->device_name)); |
1412 |
|
|
|
1413 |
|
|
impl->wave_in_handle = NULL; |
1414 |
|
|
impl->wave_out_handle = NULL; |
1415 |
|
|
impl->event_handle = NULL; |
1416 |
|
|
|
1417 |
|
|
impl->supported_formats_count = gate_win32_get_formats( |
1418 |
|
|
incaps.dwFormats, impl->supported_formats, |
1419 |
|
|
sizeof(impl->supported_formats) / sizeof(impl->supported_formats[0])); |
1420 |
|
|
|
1421 |
|
|
ret = gate_delegate_invoke(callback, (gate_audio_device_t*)impl, param); |
1422 |
|
|
} |
1423 |
|
|
|
1424 |
|
|
if (impl != NULL) |
1425 |
|
|
{ |
1426 |
|
|
gate_object_release(impl); |
1427 |
|
|
} |
1428 |
|
|
|
1429 |
|
|
GATE_BREAK_IF_FAILED(ret); |
1430 |
|
|
} |
1431 |
|
|
} |
1432 |
|
|
|
1433 |
|
|
} |
1434 |
|
|
|
1435 |
|
|
if ((type == gate_audio_device_unknown) || (type == gate_audio_device_output)) |
1436 |
|
|
{ |
1437 |
|
|
if (GATE_SUCCEEDED(ret)) |
1438 |
|
|
{ |
1439 |
|
|
num_devs = win32_audio.MMwaveOutGetNumDevs(); |
1440 |
|
|
|
1441 |
|
|
for (dev_index = 0; dev_index < num_devs; dev_index++) |
1442 |
|
|
{ |
1443 |
|
|
mmresult = win32_audio.MMwaveOutGetDevCaps(dev_index, &outcaps, sizeof(outcaps)); |
1444 |
|
|
if (MMSYSERR_NOERROR == mmresult) |
1445 |
|
|
{ |
1446 |
|
|
gate_audio_device_impl_t* impl = gate_mem_alloc(sizeof(gate_audio_device_impl_t)); |
1447 |
|
|
if (impl != NULL) |
1448 |
|
|
{ |
1449 |
|
|
gate_mem_clear(impl, sizeof(gate_audio_device_impl_t)); |
1450 |
|
|
gate_init_audio_device_vtbl_impl(); |
1451 |
|
|
impl->vtbl = &gate_audio_device_vtbl_impl; |
1452 |
|
|
|
1453 |
|
|
gate_atomic_int_init(&impl->ref_counter, 1); |
1454 |
|
|
|
1455 |
|
|
impl->device_id = dev_index; |
1456 |
|
|
impl->device_type = gate_audio_device_output; |
1457 |
|
|
|
1458 |
|
|
gate_str_print_uint(&impl->device_id_text[1], sizeof(impl->device_id_text) - 1, dev_index, 0); |
1459 |
|
|
impl->device_id_text[0] = 'O'; |
1460 |
|
|
|
1461 |
|
|
gate_win32_winstr_2_utf8(outcaps.szPname, sizeof(outcaps.szPname) / sizeof(outcaps.szPname[0]), |
1462 |
|
|
impl->device_name, sizeof(impl->device_name)); |
1463 |
|
|
|
1464 |
|
|
impl->wave_in_handle = NULL; |
1465 |
|
|
impl->wave_out_handle = NULL; |
1466 |
|
|
impl->event_handle = NULL; |
1467 |
|
|
|
1468 |
|
|
impl->supported_formats_count = gate_win32_get_formats( |
1469 |
|
|
outcaps.dwFormats, impl->supported_formats, |
1470 |
|
|
sizeof(impl->supported_formats) / sizeof(impl->supported_formats[0])); |
1471 |
|
|
|
1472 |
|
|
ret = gate_delegate_invoke(callback, impl, param); |
1473 |
|
|
} |
1474 |
|
|
|
1475 |
|
|
if (impl != NULL) |
1476 |
|
|
{ |
1477 |
|
|
gate_object_release(impl); |
1478 |
|
|
} |
1479 |
|
|
|
1480 |
|
|
GATE_BREAK_IF_FAILED(ret); |
1481 |
|
|
} |
1482 |
|
|
} |
1483 |
|
|
} |
1484 |
|
|
} |
1485 |
|
|
return ret; |
1486 |
|
|
} |
1487 |
|
|
|
1488 |
|
|
#endif /* GATE_IO_AUDIO_WINAPI */ |
1489 |
|
|
|
1490 |
|
|
|
1491 |
|
|
|
1492 |
|
|
|
1493 |
|
|
|
1494 |
|
|
/* |
1495 |
|
|
class AudioReader : public IAsyncAudioInputStream, protected Lockable |
1496 |
|
|
{ |
1497 |
|
|
public: |
1498 |
|
|
friend class AudioStreamService; |
1499 |
|
|
|
1500 |
|
|
typedef IAsyncAudioInputStream::AsyncReadDelegate AsyncReadDelegate; |
1501 |
|
|
virtual result_t beginRead(intptr_t nLength, const AsyncReadDelegate & delRead, intptr_t nParam); |
1502 |
|
|
virtual result_t endRead(); |
1503 |
|
|
|
1504 |
|
|
typedef IAudioStreamService::DeviceRef DeviceRef; |
1505 |
|
|
|
1506 |
|
|
AudioReader(AudioStreamService& rService, IExecutionQueue* pQueue, DeviceRef refDevice, AudioFormat const & rFormat, intptr_t nHandle, dword_t dwDefaultSampleCount = 0); |
1507 |
|
|
|
1508 |
|
|
AudioFormat const & Format() const; |
1509 |
|
|
DeviceRef WaveDevice(); |
1510 |
|
|
intptr_t Handle(); |
1511 |
|
|
|
1512 |
|
|
void dispatchEvent(HWAVEIN hwi, UINT uMsg, LPWAVEHDR pHdr); |
1513 |
|
|
void processEvent(HWAVEIN hwi, UINT uMsg, LPWAVEHDR pHdr); |
1514 |
|
|
void processSample(AutoPtr<AudioSample> ptrSample); |
1515 |
|
|
|
1516 |
|
|
result_t close(); |
1517 |
|
|
|
1518 |
|
|
protected: |
1519 |
|
|
AudioStreamService* m_service; |
1520 |
|
|
IExecutionQueue* m_queue; |
1521 |
|
|
DeviceRef m_device; |
1522 |
|
|
AudioFormat m_format; |
1523 |
|
|
intptr_t m_handle; |
1524 |
|
|
dword_t m_dwDefaultSampleCount; |
1525 |
|
|
bool m_bStarted; |
1526 |
|
|
|
1527 |
|
|
//intptr_t BufferSize; |
1528 |
|
|
//LPWAVEHDR Buffer1; |
1529 |
|
|
//LPWAVEHDR Buffer2; |
1530 |
|
|
//AudioFormat RecordFormat; |
1531 |
|
|
bool Closed; |
1532 |
|
|
|
1533 |
|
|
struct ReadJob |
1534 |
|
|
{ |
1535 |
|
|
AsyncReadDelegate ReadCallback; |
1536 |
|
|
intptr_t Length; |
1537 |
|
|
intptr_t Param; |
1538 |
|
|
WAVEHDR Header; |
1539 |
|
|
MemStream Buffer; |
1540 |
|
|
}; |
1541 |
|
|
|
1542 |
|
|
typedef List<ReadJob> ReadJobList; |
1543 |
|
|
ReadJobList m_listReadJobs; |
1544 |
|
|
|
1545 |
|
|
private: |
1546 |
|
|
AudioReader(const AudioReader & r); |
1547 |
|
|
AudioReader& operator=(const AudioReader & r); |
1548 |
|
|
|
1549 |
|
|
}; |
1550 |
|
|
|
1551 |
|
|
|
1552 |
|
|
|
1553 |
|
|
class AudioWriter : public IAsyncAudioOutputStream, protected Lockable |
1554 |
|
|
{ |
1555 |
|
|
public: |
1556 |
|
|
friend class AudioStreamService; |
1557 |
|
|
|
1558 |
|
|
typedef IAsyncAudioOutputStream::AsyncWriteDelegate AsyncWriteDelegate; |
1559 |
|
|
virtual result_t beginWrite(AudioSample const * atData, intptr_t nLength, const AsyncWriteDelegate & delWrite, intptr_t nParam); |
1560 |
|
|
virtual result_t endWrite(); |
1561 |
|
|
|
1562 |
|
|
void dispatchEvent(HWAVEOUT hwo, UINT msg, LPWAVEHDR wavehdr); |
1563 |
|
|
void processEvent(HWAVEOUT hwo, UINT msg, LPWAVEHDR wavehdr); |
1564 |
|
|
|
1565 |
|
|
result_t close(); |
1566 |
|
|
|
1567 |
|
|
|
1568 |
|
|
typedef IAudioStreamService::DeviceRef DeviceRef; |
1569 |
|
|
|
1570 |
|
|
protected: |
1571 |
|
|
AudioWriter(AudioStreamService & rService, IExecutionQueue* pQueue, DeviceRef refDevice, AudioFormat const & rFormat, intptr_t nHandle); |
1572 |
|
|
|
1573 |
|
|
AudioStreamService* m_pService; |
1574 |
|
|
IExecutionQueue* m_queue; |
1575 |
|
|
DeviceRef m_refDevice; |
1576 |
|
|
AudioFormat m_format; |
1577 |
|
|
intptr_t m_handle; |
1578 |
|
|
bool m_started; |
1579 |
|
|
|
1580 |
|
|
struct WriteJob |
1581 |
|
|
{ |
1582 |
|
|
AsyncWriteDelegate WriteCallback; |
1583 |
|
|
intptr_t Length; |
1584 |
|
|
intptr_t Param; |
1585 |
|
|
WAVEHDR Header; |
1586 |
|
|
AudioSample Sample; |
1587 |
|
|
}; |
1588 |
|
|
|
1589 |
|
|
typedef List<WriteJob> WriteJobList; |
1590 |
|
|
|
1591 |
|
|
WriteJobList m_listWriteJobs; |
1592 |
|
|
|
1593 |
|
|
result_t processSample(WriteJob & sample); |
1594 |
|
|
|
1595 |
|
|
private: |
1596 |
|
|
AudioWriter(const AudioWriter & r); |
1597 |
|
|
AudioWriter& operator=(const AudioWriter & r); |
1598 |
|
|
}; |
1599 |
|
|
|
1600 |
|
|
|
1601 |
|
|
|
1602 |
|
|
|
1603 |
|
|
|
1604 |
|
|
struct WaveInMetaData |
1605 |
|
|
{ |
1606 |
|
|
AudioReader * Reader; |
1607 |
|
|
intptr_t BufferSize; |
1608 |
|
|
LPWAVEHDR Buffer1; |
1609 |
|
|
LPWAVEHDR Buffer2; |
1610 |
|
|
AudioFormat RecordFormat; |
1611 |
|
|
bool Closed; |
1612 |
|
|
|
1613 |
|
|
WaveInMetaData(AudioReader * poCtrl, intptr_t nBufferSize) |
1614 |
|
|
: Reader(poCtrl), BufferSize(nBufferSize), Buffer1(0), Buffer2(0), Closed(false) |
1615 |
|
|
{ |
1616 |
|
|
} |
1617 |
|
|
}; |
1618 |
|
|
|
1619 |
|
|
|
1620 |
|
|
|
1621 |
|
|
void CALLBACK g_audiostream_waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD) |
1622 |
|
|
{ |
1623 |
|
|
AudioReader * poReader = reinterpret_cast<AudioReader*>(DWORD_PTR(dwInstance)); |
1624 |
|
|
if (poReader) |
1625 |
|
|
{ |
1626 |
|
|
LPWAVEHDR pwh = reinterpret_cast<LPWAVEHDR>(DWORD_PTR(dwParam1)); |
1627 |
|
|
poReader->dispatchEvent(hwi, uMsg, pwh); |
1628 |
|
|
} |
1629 |
|
|
} |
1630 |
|
|
|
1631 |
|
|
void CALLBACK g_audiostream_waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD) |
1632 |
|
|
{ |
1633 |
|
|
AudioWriter * poWriter = reinterpret_cast<AudioWriter*>(DWORD_PTR(dwInstance)); |
1634 |
|
|
if (poWriter) |
1635 |
|
|
{ |
1636 |
|
|
LPWAVEHDR pwh = reinterpret_cast<LPWAVEHDR>(DWORD_PTR(dwParam1)); |
1637 |
|
|
poWriter->dispatchEvent(hwo, uMsg, pwh); |
1638 |
|
|
} |
1639 |
|
|
} |
1640 |
|
|
|
1641 |
|
|
|
1642 |
|
|
|
1643 |
|
|
*/ |
1644 |
|
|
|
1645 |
|
|
|
1646 |
|
|
|
1647 |
|
|
|
1648 |
|
|
|
1649 |
|
|
|
1650 |
|
|
|
1651 |
|
|
|
1652 |
|
|
|
1653 |
|
|
|
1654 |
|
|
|
1655 |
|
|
|
1656 |
|
|
|
1657 |
|
|
/////////////////////////////////////////////// |
1658 |
|
|
// // |
1659 |
|
|
// class AudioStreamService implementation // |
1660 |
|
|
// // |
1661 |
|
|
/////////////////////////////////////////////// |
1662 |
|
|
|
1663 |
|
|
|
1664 |
|
|
/* |
1665 |
|
|
|
1666 |
|
|
AudioStreamService::AudioStreamService() |
1667 |
|
|
{ |
1668 |
|
|
this->m_exequeue.create(); |
1669 |
|
|
this->m_queue = &*this->m_exequeue; |
1670 |
|
|
} |
1671 |
|
|
|
1672 |
|
|
AudioStreamService::AudioStreamService(IExecutionQueue* pQueue) |
1673 |
|
|
: m_queue(pQueue) |
1674 |
|
|
{ |
1675 |
|
|
} |
1676 |
|
|
|
1677 |
|
|
AudioStreamService::~AudioStreamService() throw() |
1678 |
|
|
{ |
1679 |
|
|
} |
1680 |
|
|
|
1681 |
|
|
void AudioStreamService::run() |
1682 |
|
|
{ |
1683 |
|
|
this->m_queue->run(); |
1684 |
|
|
} |
1685 |
|
|
|
1686 |
|
|
result_t AudioStreamService::quit() |
1687 |
|
|
{ |
1688 |
|
|
return this->m_queue->quit(); |
1689 |
|
|
} |
1690 |
|
|
|
1691 |
|
|
|
1692 |
|
|
result_t AudioStreamService::getDevices(Array<DeviceRef>& raDevs) |
1693 |
|
|
{ |
1694 |
|
|
result_t resReturn = AudioInputStream::getDevices(raDevs); |
1695 |
|
|
if (resReturn != results::Ok) return resReturn; |
1696 |
|
|
return AudioOutputStream::getDevices(raDevs); |
1697 |
|
|
} |
1698 |
|
|
|
1699 |
|
|
result_t AudioStreamService::open(DeviceRef refDev, AudioFormat const & rFormat, Ref<IAsyncAudioInputStream>& rrefReader) |
1700 |
|
|
{ |
1701 |
|
|
HWAVEIN hwi = 0; |
1702 |
|
|
|
1703 |
|
|
WAVEFORMATEX uFormatEx; |
1704 |
|
|
uFormatEx.wFormatTag = WAVE_FORMAT_PCM; |
1705 |
|
|
uFormatEx.nChannels = rFormat.Channels; |
1706 |
|
|
uFormatEx.nSamplesPerSec = rFormat.SamplesPerSecond; |
1707 |
|
|
uFormatEx.wBitsPerSample = rFormat.BitsPerSample; |
1708 |
|
|
uFormatEx.nBlockAlign = (uFormatEx.nChannels * uFormatEx.wBitsPerSample) >> 3; |
1709 |
|
|
uFormatEx.nAvgBytesPerSec = (uFormatEx.nBlockAlign * uFormatEx.nSamplesPerSec); |
1710 |
|
|
uFormatEx.cbSize = 0; |
1711 |
|
|
|
1712 |
|
|
Ref<AudioReader> refReader(new AudioReader(*this, this->m_queue, refDev, rFormat, 0)); |
1713 |
|
|
|
1714 |
|
|
MMRESULT iResult = ::waveInOpen(&hwi, UINT(refDev->Handle()), &uFormatEx, DWORD_PTR(&g_audiostream_waveInProc), DWORD_PTR(&*refReader), CALLBACK_FUNCTION); |
1715 |
|
|
if (iResult == MMSYSERR_NOERROR) |
1716 |
|
|
{ |
1717 |
|
|
Lock l(this); |
1718 |
|
|
intptr_t nHandle = intptr_t(hwi); |
1719 |
|
|
refReader->m_handle = nHandle; |
1720 |
|
|
this->m_listReader.add(refReader); |
1721 |
|
|
rrefReader = refReader; |
1722 |
|
|
//this->m_mapHandles.add(nHandle, nHandle); |
1723 |
|
|
//this->m_mapDevices.add(nHandle, roDev); |
1724 |
|
|
//this->m_mapFormats.add(nHandle, roFormat); |
1725 |
|
|
//this->m_mapSounds.add(nHandle, SoundList()); |
1726 |
|
|
//this->m_mapMeta.add(nHandle, poMeta); |
1727 |
|
|
return results::Ok; |
1728 |
|
|
} |
1729 |
|
|
else |
1730 |
|
|
{ |
1731 |
|
|
return result_t(iResult); |
1732 |
|
|
} |
1733 |
|
|
} |
1734 |
|
|
|
1735 |
|
|
result_t AudioStreamService::open(DeviceRef refDev, AudioFormat const & rFormat, Ref<IAsyncAudioOutputStream>& rrefWriter) |
1736 |
|
|
{ |
1737 |
|
|
HWAVEOUT hwo = 0; |
1738 |
|
|
|
1739 |
|
|
WAVEFORMATEX uFormatEx; |
1740 |
|
|
uFormatEx.wFormatTag = WAVE_FORMAT_PCM; |
1741 |
|
|
uFormatEx.nChannels = rFormat.Channels; |
1742 |
|
|
uFormatEx.nSamplesPerSec = rFormat.SamplesPerSecond; |
1743 |
|
|
uFormatEx.wBitsPerSample = rFormat.BitsPerSample; |
1744 |
|
|
uFormatEx.nBlockAlign = (uFormatEx.nChannels * uFormatEx.wBitsPerSample) >> 3; |
1745 |
|
|
uFormatEx.nAvgBytesPerSec = (uFormatEx.nBlockAlign * uFormatEx.nSamplesPerSec); |
1746 |
|
|
uFormatEx.cbSize = 0; |
1747 |
|
|
|
1748 |
|
|
Ref<AudioWriter> refWriter(new AudioWriter(*this, this->m_queue, refDev, rFormat, 0)); |
1749 |
|
|
|
1750 |
|
|
MMRESULT iResult = ::waveOutOpen(&hwo, UINT(refDev->Handle()), &uFormatEx, DWORD_PTR(g_audiostream_waveOutProc), DWORD_PTR(&*refWriter), CALLBACK_FUNCTION); //DWORD_PTR(this->m_pvHandle), 0, CALLBACK_WINDOW); |
1751 |
|
|
if (iResult == MMSYSERR_NOERROR) |
1752 |
|
|
{ |
1753 |
|
|
Lock l(this); |
1754 |
|
|
intptr_t nHandle = intptr_t(hwo); |
1755 |
|
|
//Ref<AudioWriter> refWriter = new AudioWriter(*this, &this->m_queue, rFormat, nHandle); |
1756 |
|
|
refWriter->m_handle = nHandle; |
1757 |
|
|
this->m_listWriter.add(refWriter); |
1758 |
|
|
rrefWriter = refWriter; |
1759 |
|
|
//this->m_mapHandles.add(nHandle, nHandle); |
1760 |
|
|
//this->m_mapDevices.add(nHandle, roDev); |
1761 |
|
|
//this->m_mapFormats.add(nHandle, roFormat); |
1762 |
|
|
//this->m_mapSounds.add(nHandle, SoundList()); |
1763 |
|
|
|
1764 |
|
|
//rnHandle = nHandle; |
1765 |
|
|
return results::Ok; |
1766 |
|
|
} |
1767 |
|
|
else |
1768 |
|
|
{ |
1769 |
|
|
return result_t(iResult); |
1770 |
|
|
} |
1771 |
|
|
} |
1772 |
|
|
|
1773 |
|
|
result_t AudioStreamService::close(Ref<IAsyncAudioInputStream> refReader) |
1774 |
|
|
{ |
1775 |
|
|
Ref<AudioReader> reader; |
1776 |
|
|
Lock l(this); |
1777 |
|
|
for (AudioInputList::Iterator iter(this->m_listReader); !!iter; ++iter) |
1778 |
|
|
{ |
1779 |
|
|
if (static_cast<IAsyncAudioInputStream*>(iter->get()) == refReader.get()) |
1780 |
|
|
{ |
1781 |
|
|
reader = *iter; |
1782 |
|
|
this->m_listReader.remove(iter); |
1783 |
|
|
break; |
1784 |
|
|
} |
1785 |
|
|
} |
1786 |
|
|
if (!reader) return results::NoMatch; |
1787 |
|
|
return reader->close(); |
1788 |
|
|
} |
1789 |
|
|
|
1790 |
|
|
result_t AudioStreamService::close(Ref<IAsyncAudioOutputStream> refWriter) |
1791 |
|
|
{ |
1792 |
|
|
Ref<AudioWriter> writer; |
1793 |
|
|
Lock l(this); |
1794 |
|
|
for (AudioOutputList::Iterator iter(this->m_listWriter); !!iter; ++iter) |
1795 |
|
|
{ |
1796 |
|
|
if (static_cast<IAsyncAudioOutputStream*>(iter->get()) == refWriter.get()) |
1797 |
|
|
{ |
1798 |
|
|
writer = *iter; |
1799 |
|
|
this->m_listWriter.remove(iter); |
1800 |
|
|
break; |
1801 |
|
|
} |
1802 |
|
|
} |
1803 |
|
|
if (!writer) return results::NoMatch; |
1804 |
|
|
return writer->close(); |
1805 |
|
|
} |
1806 |
|
|
|
1807 |
|
|
|
1808 |
|
|
*/ |
1809 |
|
|
|
1810 |
|
|
|
1811 |
|
|
|
1812 |
|
|
|
1813 |
|
|
|
1814 |
|
|
|
1815 |
|
|
|
1816 |
|
|
|
1817 |
|
|
|
1818 |
|
|
|
1819 |
|
|
|
1820 |
|
|
//////////////////////////////////////////////////////////// |
1821 |
|
|
// // |
1822 |
|
|
// class AudioStreamService::AudioWriter implementation // |
1823 |
|
|
// // |
1824 |
|
|
//////////////////////////////////////////////////////////// |
1825 |
|
|
|
1826 |
|
|
/* |
1827 |
|
|
AudioWriter::AudioWriter(AudioStreamService & rService, IExecutionQueue* pQueue, DeviceRef refDevice, AudioFormat const & rFormat, intptr_t nHandle) |
1828 |
|
|
: m_pService(&rService), m_queue(pQueue), m_refDevice(refDevice), |
1829 |
|
|
m_format(rFormat), m_handle(nHandle), m_started(false) |
1830 |
|
|
{ |
1831 |
|
|
} |
1832 |
|
|
|
1833 |
|
|
result_t AudioWriter::beginWrite(AudioSample const * atData, intptr_t nLength, const AsyncWriteDelegate & delWrite, intptr_t nParam) |
1834 |
|
|
{ |
1835 |
|
|
if (nLength <= 0) return results::InvalidParameter; |
1836 |
|
|
for (intptr_t n = 1; n < nLength; ++n) |
1837 |
|
|
{ |
1838 |
|
|
if (atData[n].Format != atData[0].Format) |
1839 |
|
|
{ |
1840 |
|
|
return results::InvalidContent; |
1841 |
|
|
} |
1842 |
|
|
} |
1843 |
|
|
|
1844 |
|
|
WriteJob * ptrJob = 0; |
1845 |
|
|
{ |
1846 |
|
|
Lock l(this); |
1847 |
|
|
WriteJobList::Iterator job = this->m_listWriteJobs.add(WriteJob()); |
1848 |
|
|
job->Sample.Format = atData[0].Format; |
1849 |
|
|
job->Sample.MetaData = atData[0].MetaData; |
1850 |
|
|
job->WriteCallback = delWrite; |
1851 |
|
|
job->Length = 1; |
1852 |
|
|
job->Param = nParam; |
1853 |
|
|
for (intptr_t n = 0; n < nLength; ++n) job->Sample.Data.write(atData[n].Data); |
1854 |
|
|
ptrJob = &*job; |
1855 |
|
|
} |
1856 |
|
|
return this->processSample(*ptrJob); |
1857 |
|
|
|
1858 |
|
|
if (!this->m_started) |
1859 |
|
|
{ |
1860 |
|
|
this->m_started = true; |
1861 |
|
|
//::waveoutst |
1862 |
|
|
} |
1863 |
|
|
return results::Ok; |
1864 |
|
|
} |
1865 |
|
|
|
1866 |
|
|
result_t AudioWriter::endWrite() |
1867 |
|
|
{ |
1868 |
|
|
return results::NotImplemented; |
1869 |
|
|
} |
1870 |
|
|
|
1871 |
|
|
result_t AudioWriter::processSample(WriteJob & job) |
1872 |
|
|
{ |
1873 |
|
|
HWAVEOUT hwo = reinterpret_cast<HWAVEOUT>(this->m_handle); |
1874 |
|
|
Mem::clear(&job.Header, sizeof(job.Header)); |
1875 |
|
|
job.Header.dwBufferLength = DWORD(job.Sample.Data.Length()); |
1876 |
|
|
job.Header.lpData = &job.Sample.Data[0]; |
1877 |
|
|
MMRESULT iResult = waveOutPrepareHeader(hwo, &job.Header, UINT(sizeof(job.Header))); |
1878 |
|
|
if (MMSYSERR_NOERROR == iResult) |
1879 |
|
|
{ |
1880 |
|
|
iResult = waveOutWrite(hwo, &job.Header, UINT(sizeof(job.Header))); |
1881 |
|
|
} |
1882 |
|
|
return result_t(iResult); |
1883 |
|
|
} |
1884 |
|
|
|
1885 |
|
|
|
1886 |
|
|
void AudioWriter::dispatchEvent(HWAVEOUT hwo, UINT msg, LPWAVEHDR wavehdr) |
1887 |
|
|
{ |
1888 |
|
|
GTry |
1889 |
|
|
{ |
1890 |
|
|
AutoPtr<IRunnable> ptrInvoke = new Executor3<void, HWAVEOUT, UINT, LPWAVEHDR>(&AudioWriter::processEvent, this, hwo, msg, wavehdr); |
1891 |
|
|
this->m_queue->invoke(ptrInvoke); |
1892 |
|
|
} |
1893 |
|
|
GCatchAll() |
1894 |
|
|
{ |
1895 |
|
|
} |
1896 |
|
|
} |
1897 |
|
|
|
1898 |
|
|
void AudioWriter::processEvent(HWAVEOUT hwo, UINT msg, LPWAVEHDR wavehdr) |
1899 |
|
|
{ |
1900 |
|
|
switch (msg) |
1901 |
|
|
{ |
1902 |
|
|
case WOM_CLOSE: |
1903 |
|
|
{ |
1904 |
|
|
break; |
1905 |
|
|
} |
1906 |
|
|
case WOM_DONE: |
1907 |
|
|
{ |
1908 |
|
|
::waveOutUnprepareHeader(hwo, wavehdr, sizeof(WAVEHDR)); |
1909 |
|
|
OptionalVar<WriteJob> job; |
1910 |
|
|
{ |
1911 |
|
|
Lock l(this); |
1912 |
|
|
for (WriteJobList::Iterator iter(this->m_listWriteJobs); !!iter; ++iter) |
1913 |
|
|
{ |
1914 |
|
|
if (&iter->Header == wavehdr) |
1915 |
|
|
{ |
1916 |
|
|
job.create(); |
1917 |
|
|
job->WriteCallback = iter->WriteCallback; |
1918 |
|
|
job->Header = iter->Header; |
1919 |
|
|
job->Sample.swap(iter->Sample); |
1920 |
|
|
job->Param = iter->Param; |
1921 |
|
|
job->Length = iter->Length; |
1922 |
|
|
this->m_listWriteJobs.remove(iter); |
1923 |
|
|
break; |
1924 |
|
|
} |
1925 |
|
|
//iter->WriteCallback(this, results::Ok, |
1926 |
|
|
} |
1927 |
|
|
} |
1928 |
|
|
if (!!job) |
1929 |
|
|
{ |
1930 |
|
|
job->WriteCallback(this, results::Ok, &job->Sample, job->Length, 1, job->Param); |
1931 |
|
|
} |
1932 |
|
|
break; |
1933 |
|
|
} |
1934 |
|
|
case WOM_OPEN: |
1935 |
|
|
{ |
1936 |
|
|
break; |
1937 |
|
|
} |
1938 |
|
|
} |
1939 |
|
|
} |
1940 |
|
|
|
1941 |
|
|
result_t AudioWriter::close() |
1942 |
|
|
{ |
1943 |
|
|
HWAVEOUT hwo = reinterpret_cast<HWAVEOUT>(this->m_handle); |
1944 |
|
|
waveOutReset(hwo); |
1945 |
|
|
waveOutClose(hwo); |
1946 |
|
|
return results::Ok; |
1947 |
|
|
} |
1948 |
|
|
|
1949 |
|
|
*/ |
1950 |
|
|
|
1951 |
|
|
|
1952 |
|
|
|
1953 |
|
|
|
1954 |
|
|
|
1955 |
|
|
|
1956 |
|
|
|
1957 |
|
|
|
1958 |
|
|
|
1959 |
|
|
//////////////////////////////////////////////////////////// |
1960 |
|
|
// // |
1961 |
|
|
// class AudioStreamService::AudioReader implementation // |
1962 |
|
|
// // |
1963 |
|
|
//////////////////////////////////////////////////////////// |
1964 |
|
|
|
1965 |
|
|
/* |
1966 |
|
|
AudioReader::AudioReader(AudioStreamService & rService, IExecutionQueue * pQueue, DeviceRef refDevice, AudioFormat const & rFormat, intptr_t nHandle, dword_t dwDefaultSampleCount) |
1967 |
|
|
: m_service(&rService), m_queue(pQueue), m_device(refDevice), |
1968 |
|
|
m_format(rFormat), m_handle(nHandle), m_dwDefaultSampleCount(dwDefaultSampleCount), m_bStarted(false) |
1969 |
|
|
{ |
1970 |
|
|
if (!m_dwDefaultSampleCount) m_dwDefaultSampleCount = rFormat.SamplesPerSecond; |
1971 |
|
|
} |
1972 |
|
|
|
1973 |
|
|
result_t AudioReader::beginRead(intptr_t nLength, const AsyncReadDelegate & delRead, intptr_t nParam) |
1974 |
|
|
{ |
1975 |
|
|
if (nLength <= 0) return results::InvalidParameter; |
1976 |
|
|
|
1977 |
|
|
MMRESULT iResult = MMSYSERR_NOERROR; |
1978 |
|
|
HWAVEIN hwi = reinterpret_cast<HWAVEIN>(this->m_handle); |
1979 |
|
|
intptr_t nBufferSize = this->m_dwDefaultSampleCount |
1980 |
|
|
* this->m_format.Channels |
1981 |
|
|
* this->m_format.BitsPerSample |
1982 |
|
|
* nLength |
1983 |
|
|
/ 8; |
1984 |
|
|
{ |
1985 |
|
|
Lock l(this); |
1986 |
|
|
ReadJobList::Iterator entry = this->m_listReadJobs.add(ReadJob()); |
1987 |
|
|
entry->ReadCallback = delRead; |
1988 |
|
|
entry->Length = nLength; |
1989 |
|
|
entry->Param = nParam; |
1990 |
|
|
char * ptrBuffer = entry->Buffer.createContent(nBufferSize); |
1991 |
|
|
LPWAVEHDR pwh = &entry->Header; |
1992 |
|
|
Mem::clear(pwh, sizeof(entry->Header)); |
1993 |
|
|
pwh->dwFlags = 0; |
1994 |
|
|
pwh->dwLoops = 0; |
1995 |
|
|
pwh->dwUser = 1; |
1996 |
|
|
pwh->lpNext = 0; |
1997 |
|
|
pwh->reserved = 0; |
1998 |
|
|
pwh->dwBufferLength = DWORD(nBufferSize); |
1999 |
|
|
pwh->dwBytesRecorded = 0; |
2000 |
|
|
pwh->lpData = ptrBuffer; |
2001 |
|
|
|
2002 |
|
|
iResult = ::waveInPrepareHeader(hwi, pwh, sizeof(entry->Header)); |
2003 |
|
|
if (MMSYSERR_NOERROR == iResult) |
2004 |
|
|
{ |
2005 |
|
|
iResult = ::waveInAddBuffer(hwi, pwh, sizeof(WAVEHDR)); |
2006 |
|
|
} |
2007 |
|
|
} |
2008 |
|
|
|
2009 |
|
|
if (iResult == MMSYSERR_NOERROR) |
2010 |
|
|
{ |
2011 |
|
|
if (!m_bStarted) |
2012 |
|
|
{ |
2013 |
|
|
m_bStarted = true; |
2014 |
|
|
iResult = ::waveInStart(hwi); |
2015 |
|
|
if (iResult != MMSYSERR_NOERROR) |
2016 |
|
|
{ |
2017 |
|
|
m_bStarted = false; |
2018 |
|
|
} |
2019 |
|
|
} |
2020 |
|
|
} |
2021 |
|
|
|
2022 |
|
|
return result_t(iResult); |
2023 |
|
|
} |
2024 |
|
|
|
2025 |
|
|
result_t AudioReader::endRead() |
2026 |
|
|
{ |
2027 |
|
|
HWAVEIN hwi = reinterpret_cast<HWAVEIN>(this->m_handle); |
2028 |
|
|
MMRESULT iResult = ::waveInStop(hwi); |
2029 |
|
|
if (iResult == MMSYSERR_NOERROR) iResult = ::waveInReset(hwi); |
2030 |
|
|
return result_t(iResult); |
2031 |
|
|
} |
2032 |
|
|
|
2033 |
|
|
void AudioReader::processEvent(HWAVEIN hwi, UINT uMsg, LPWAVEHDR pwh) |
2034 |
|
|
{ |
2035 |
|
|
switch (uMsg) |
2036 |
|
|
{ |
2037 |
|
|
case WIM_OPEN: |
2038 |
|
|
break; |
2039 |
|
|
case WIM_DATA: |
2040 |
|
|
{ |
2041 |
|
|
if (pwh) |
2042 |
|
|
{ |
2043 |
|
|
//MMRESULT iResult = |
2044 |
|
|
::waveInUnprepareHeader(hwi, pwh, sizeof(WAVEHDR)); |
2045 |
|
|
//if(MMSYSERR_NOERROR == iResult) |
2046 |
|
|
{ |
2047 |
|
|
AudioSample sample; |
2048 |
|
|
AsyncReadDelegate delCallback; |
2049 |
|
|
intptr_t nReqLength = 0; |
2050 |
|
|
intptr_t nParam = 0; |
2051 |
|
|
{ |
2052 |
|
|
Lock l(this); |
2053 |
|
|
for (ReadJobList::Iterator iter(this->m_listReadJobs); !!iter; ++iter) |
2054 |
|
|
{ |
2055 |
|
|
if (pwh == &iter->Header) |
2056 |
|
|
{ |
2057 |
|
|
nParam = iter->Param; |
2058 |
|
|
nReqLength = iter->Length; |
2059 |
|
|
delCallback = iter->ReadCallback; |
2060 |
|
|
sample.Format = this->m_format; |
2061 |
|
|
sample.SampleCount = pwh->dwBytesRecorded * 8 / this->m_format.Channels / this->m_format.BitsPerSample; |
2062 |
|
|
sample.Data.swap(iter->Buffer); |
2063 |
|
|
this->m_listReadJobs.remove(iter); |
2064 |
|
|
break; |
2065 |
|
|
} |
2066 |
|
|
} |
2067 |
|
|
} |
2068 |
|
|
if (!!delCallback) |
2069 |
|
|
{ |
2070 |
|
|
delCallback(this, results::Ok, &sample, nReqLength, 1, nParam); |
2071 |
|
|
} |
2072 |
|
|
|
2073 |
|
|
} |
2074 |
|
|
//else |
2075 |
|
|
{ |
2076 |
|
|
// GATE_BREAKPOINT; |
2077 |
|
|
} |
2078 |
|
|
} |
2079 |
|
|
break; |
2080 |
|
|
} |
2081 |
|
|
case WIM_CLOSE: |
2082 |
|
|
{ |
2083 |
|
|
this->Closed = true; |
2084 |
|
|
break; |
2085 |
|
|
} |
2086 |
|
|
} |
2087 |
|
|
} |
2088 |
|
|
|
2089 |
|
|
void AudioReader::processSample(AutoPtr<AudioSample> ptrSample) |
2090 |
|
|
{ |
2091 |
|
|
ReadJob job; |
2092 |
|
|
{ |
2093 |
|
|
Lock l(this); |
2094 |
|
|
ReadJobList::Iterator iter(this->m_listReadJobs); |
2095 |
|
|
if (iter) |
2096 |
|
|
{ |
2097 |
|
|
job = *iter; |
2098 |
|
|
this->m_listReadJobs.remove(iter); |
2099 |
|
|
} |
2100 |
|
|
} |
2101 |
|
|
if (!!job.ReadCallback) job.ReadCallback(this, results::Ok, ptrSample.get(), job.Length, 1, job.Param); |
2102 |
|
|
} |
2103 |
|
|
|
2104 |
|
|
void AudioReader::dispatchEvent(HWAVEIN hwi, UINT uMsg, LPWAVEHDR pHdr) |
2105 |
|
|
{ |
2106 |
|
|
GTry |
2107 |
|
|
{ |
2108 |
|
|
AutoPtr<IRunnable> ptr = new Executor3<void, HWAVEIN, UINT, LPWAVEHDR>(&AudioReader::processEvent, this, hwi, uMsg, pHdr); |
2109 |
|
|
this->m_queue->invoke(ptr); |
2110 |
|
|
} |
2111 |
|
|
GCatchAll() |
2112 |
|
|
{ |
2113 |
|
|
} |
2114 |
|
|
} |
2115 |
|
|
|
2116 |
|
|
result_t AudioReader::close() |
2117 |
|
|
{ |
2118 |
|
|
HWAVEIN hwi = reinterpret_cast<HWAVEIN>(this->m_handle); |
2119 |
|
|
waveInReset(hwi); |
2120 |
|
|
waveInClose(hwi); |
2121 |
|
|
return results::Ok; |
2122 |
|
|
} |
2123 |
|
|
|
2124 |
|
|
*/ |
2125 |
|
|
|
2126 |
|
|
|
2127 |
|
|
|
2128 |
|
|
|
2129 |
|
|
|
2130 |
|
|
|
2131 |
|
|
|
2132 |
|
|
|
2133 |
|
|
|
2134 |
|
|
|
2135 |
|
|
|
2136 |
|
|
|
2137 |
|
|
|
2138 |
|
|
#if defined(GATE_IO_AUDIO_ALSA) |
2139 |
|
|
|
2140 |
|
|
#include <alsa/asoundlib.h> /*libasound2-dev*/ |
2141 |
|
|
|
2142 |
|
|
struct gate_audio_alsa_snd_code |
2143 |
|
|
{ |
2144 |
|
|
int (*pcm_open) (snd_pcm_t** pcm, const char* name, snd_pcm_stream_t stream, int mode); |
2145 |
|
|
int (*pcm_close) (snd_pcm_t* pcm); |
2146 |
|
|
int (*pcm_hw_params_malloc) (snd_pcm_hw_params_t** ptr); |
2147 |
|
|
int (*pcm_hw_params_any) (snd_pcm_t* pcm, snd_pcm_hw_params_t* params); |
2148 |
|
|
int (*pcm_hw_params_set_access) (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_access_t access); |
2149 |
|
|
int (*pcm_hw_params_set_format) (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_format_t format); |
2150 |
|
|
int (*pcm_hw_params_set_rate_near) (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, unsigned int* val, int* dir); |
2151 |
|
|
int (*pcm_hw_params_set_channels) (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, unsigned int val); |
2152 |
|
|
void(*pcm_hw_params_free) (snd_pcm_hw_params_t* obj); |
2153 |
|
|
int (*pcm_hw_params) (snd_pcm_t* pcm, snd_pcm_hw_params_t* params); |
2154 |
|
|
int (*pcm_prepare) (snd_pcm_t* pcm); |
2155 |
|
|
snd_pcm_sframes_t(*pcm_readi) (snd_pcm_t* pcm, void* buffer, snd_pcm_uframes_t size); |
2156 |
|
|
snd_pcm_sframes_t(*pcm_writei) (snd_pcm_t* pcm, const void* buffer, snd_pcm_uframes_t size); |
2157 |
|
|
snd_pcm_sframes_t(*pcm_avail) (snd_pcm_t* pcm); |
2158 |
|
|
snd_pcm_state_t(*pcm_state) (snd_pcm_t* pcm); |
2159 |
|
|
}; |
2160 |
|
|
|
2161 |
|
|
static struct gate_audio_alsa_snd_code alsa_snd = GATE_INIT_EMPTY; |
2162 |
|
|
|
2163 |
|
✗ |
static gate_result_t gate_audio_alsa_init() |
2164 |
|
|
{ |
2165 |
|
|
static gate_string_t alsa_lib_name = GATE_STRING_INIT_STATIC("libasound.so"); |
2166 |
|
|
static gate_library_t alsa_library = NULL; |
2167 |
|
|
|
2168 |
|
✗ |
gate_library_t tmp_lib = NULL; |
2169 |
|
✗ |
gate_result_t result = GATE_RESULT_FAILED; |
2170 |
|
|
|
2171 |
|
✗ |
if (alsa_library == NULL) |
2172 |
|
|
{ |
2173 |
|
|
do |
2174 |
|
|
{ |
2175 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_open(&alsa_lib_name, &tmp_lib, GATE_LIBRARY_FLAG_DEFAULT)); |
2176 |
|
|
|
2177 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_open", &alsa_snd.pcm_open)); |
2178 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_close", &alsa_snd.pcm_close)); |
2179 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_hw_params_malloc", &alsa_snd.pcm_hw_params_malloc)); |
2180 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_hw_params_any", &alsa_snd.pcm_hw_params_any)); |
2181 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_hw_params_set_access", &alsa_snd.pcm_hw_params_set_access)); |
2182 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_hw_params_set_format", &alsa_snd.pcm_hw_params_set_format)); |
2183 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_hw_params_set_rate_near", &alsa_snd.pcm_hw_params_set_rate_near)); |
2184 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_hw_params_set_channels", &alsa_snd.pcm_hw_params_set_channels)); |
2185 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_hw_params_free", &alsa_snd.pcm_hw_params_free)); |
2186 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_hw_params", &alsa_snd.pcm_hw_params)); |
2187 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_prepare", &alsa_snd.pcm_prepare)); |
2188 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_readi", &alsa_snd.pcm_readi)); |
2189 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_open", &alsa_snd.pcm_open)); |
2190 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_writei", &alsa_snd.pcm_writei)); |
2191 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_avail", &alsa_snd.pcm_avail)); |
2192 |
|
✗ |
GATE_BREAK_IF_FAILED(result = gate_library_get_function_name(tmp_lib, "snd_pcm_state", &alsa_snd.pcm_state)); |
2193 |
|
|
|
2194 |
|
✗ |
alsa_library = tmp_lib; |
2195 |
|
✗ |
tmp_lib = NULL; |
2196 |
|
✗ |
result = GATE_RESULT_OK; |
2197 |
|
|
} while (0); |
2198 |
|
|
|
2199 |
|
✗ |
if (tmp_lib != NULL) |
2200 |
|
|
{ |
2201 |
|
✗ |
gate_library_close(tmp_lib); |
2202 |
|
|
} |
2203 |
|
|
} |
2204 |
|
|
else |
2205 |
|
|
{ |
2206 |
|
✗ |
result = GATE_RESULT_OK; |
2207 |
|
|
} |
2208 |
|
✗ |
return result; |
2209 |
|
|
} |
2210 |
|
|
|
2211 |
|
|
|
2212 |
|
|
typedef struct gate_audio_alsa_impl |
2213 |
|
|
{ |
2214 |
|
|
GATE_INTERFACE_VTBL(gate_audio_device) const* vtbl; |
2215 |
|
|
|
2216 |
|
|
gate_atomic_int_t ref_counter; |
2217 |
|
|
char id[256]; |
2218 |
|
|
char name[256]; |
2219 |
|
|
snd_pcm_t* handle; |
2220 |
|
|
gate_enumint_t type; |
2221 |
|
|
|
2222 |
|
|
gate_audio_format_t format; |
2223 |
|
|
gate_atomic_int_t format_defined; |
2224 |
|
|
gate_uint32_t sample_count; |
2225 |
|
|
|
2226 |
|
|
} gate_audio_alsa_impl_t; |
2227 |
|
|
|
2228 |
|
|
|
2229 |
|
✗ |
static void gate_audio_device_impl_release(void* device) |
2230 |
|
|
{ |
2231 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2232 |
|
✗ |
if (0 == gate_atomic_int_dec(&impl->ref_counter)) |
2233 |
|
|
{ |
2234 |
|
✗ |
gate_audio_device_impl_close(impl); |
2235 |
|
✗ |
gate_mem_dealloc(impl); |
2236 |
|
|
} |
2237 |
|
✗ |
} |
2238 |
|
✗ |
static int gate_audio_device_impl_retain(void* device) |
2239 |
|
|
{ |
2240 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2241 |
|
✗ |
return gate_atomic_int_inc(&impl->ref_counter); |
2242 |
|
|
} |
2243 |
|
|
|
2244 |
|
✗ |
static char const* gate_audio_device_impl_get_id(void* device) |
2245 |
|
|
{ |
2246 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2247 |
|
✗ |
return impl->id; |
2248 |
|
|
} |
2249 |
|
✗ |
static char const* gate_audio_device_impl_get_name(void* device) |
2250 |
|
|
{ |
2251 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2252 |
|
✗ |
return impl->name; |
2253 |
|
|
} |
2254 |
|
✗ |
static gate_intptr_t gate_audio_device_impl_get_handle(void* device) |
2255 |
|
|
{ |
2256 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2257 |
|
✗ |
return (gate_intptr_t)(void*)impl->handle; |
2258 |
|
|
} |
2259 |
|
✗ |
static gate_enumint_t gate_audio_device_impl_get_device_type(void* device) |
2260 |
|
|
{ |
2261 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2262 |
|
✗ |
return impl->type; |
2263 |
|
|
} |
2264 |
|
|
|
2265 |
|
|
static gate_audio_format_t const alsa_all_formats[] = |
2266 |
|
|
{ |
2267 |
|
|
{ 1, 44100, 2, 16 }, |
2268 |
|
|
{ 1, 44100, 1, 16 }, |
2269 |
|
|
{ 1, 44100, 2, 8 }, |
2270 |
|
|
{ 1, 44100, 1, 8 }, |
2271 |
|
|
|
2272 |
|
|
{ 1, 22050, 2, 16 }, |
2273 |
|
|
{ 1, 22050, 1, 16 }, |
2274 |
|
|
{ 1, 22050, 2, 8 }, |
2275 |
|
|
{ 1, 22050, 1, 8 }, |
2276 |
|
|
|
2277 |
|
|
{ 1, 11025, 2, 16 }, |
2278 |
|
|
{ 1, 11025, 1, 16 }, |
2279 |
|
|
{ 1, 11025, 2, 8 }, |
2280 |
|
|
{ 1, 11025, 1, 8 } |
2281 |
|
|
}; |
2282 |
|
|
|
2283 |
|
✗ |
static gate_size_t gate_audio_device_impl_get_supported_formats( |
2284 |
|
|
void* device, gate_audio_format_t* format_buffer, gate_size_t format_buffer_count) |
2285 |
|
|
{ |
2286 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2287 |
|
|
static gate_size_t const alsa_all_formats_count = sizeof(alsa_all_formats) / sizeof(alsa_all_formats[0]); |
2288 |
|
✗ |
gate_size_t ret = format_buffer_count < alsa_all_formats_count ? format_buffer_count : alsa_all_formats_count; |
2289 |
|
|
gate_size_t index; |
2290 |
|
|
|
2291 |
|
✗ |
for (index = 0; index != ret; ++index) |
2292 |
|
|
{ |
2293 |
|
✗ |
format_buffer[index] = alsa_all_formats[index]; |
2294 |
|
|
} |
2295 |
|
✗ |
return ret; |
2296 |
|
|
} |
2297 |
|
|
|
2298 |
|
✗ |
static gate_result_t gate_audio_device_impl_open(void* device, gate_audio_format_t const* format, gate_size_t sample_count) |
2299 |
|
|
{ |
2300 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2301 |
|
✗ |
gate_result_t result = GATE_RESULT_FAILED; |
2302 |
|
|
int pcm_code; |
2303 |
|
✗ |
snd_pcm_t* handle = NULL; |
2304 |
|
✗ |
snd_pcm_hw_params_t* hw_params = NULL; |
2305 |
|
|
int pcm_format; |
2306 |
|
✗ |
unsigned int pcm_rate = format->samples_per_second; |
2307 |
|
|
|
2308 |
|
|
do |
2309 |
|
|
{ |
2310 |
|
✗ |
result = gate_audio_alsa_init(); |
2311 |
|
✗ |
GATE_BREAK_IF_FAILED(result); |
2312 |
|
|
|
2313 |
|
✗ |
if (sample_count == 0) |
2314 |
|
|
{ |
2315 |
|
✗ |
impl->sample_count = format->samples_per_second; |
2316 |
|
|
} |
2317 |
|
|
else |
2318 |
|
|
{ |
2319 |
|
✗ |
impl->sample_count = sample_count; |
2320 |
|
|
} |
2321 |
|
|
|
2322 |
|
✗ |
if (format->bits_per_sample == 8) |
2323 |
|
|
{ |
2324 |
|
✗ |
pcm_format = SND_PCM_FORMAT_U8; |
2325 |
|
|
} |
2326 |
|
✗ |
else if (format->bits_per_sample == 16) |
2327 |
|
|
{ |
2328 |
|
✗ |
pcm_format = SND_PCM_FORMAT_S16_LE; |
2329 |
|
|
} |
2330 |
|
|
else |
2331 |
|
|
{ |
2332 |
|
✗ |
result = GATE_RESULT_INVALIDINPUT; |
2333 |
|
✗ |
break; |
2334 |
|
|
} |
2335 |
|
|
|
2336 |
|
|
|
2337 |
|
✗ |
switch (impl->type) |
2338 |
|
|
{ |
2339 |
|
✗ |
case gate_audio_device_input: |
2340 |
|
|
{ |
2341 |
|
✗ |
if (impl->handle != NULL) |
2342 |
|
|
{ |
2343 |
|
✗ |
result = GATE_RESULT_INVALIDSTATE; |
2344 |
|
✗ |
break; |
2345 |
|
|
} |
2346 |
|
|
|
2347 |
|
|
/* assume failure until all functions return success */ |
2348 |
|
✗ |
result = GATE_RESULT_FAILED; |
2349 |
|
|
|
2350 |
|
✗ |
pcm_code = alsa_snd.pcm_open(&handle, impl->id, SND_PCM_STREAM_CAPTURE, 0); |
2351 |
|
✗ |
if (pcm_code < 0) |
2352 |
|
|
{ |
2353 |
|
✗ |
GATE_DEBUG_TRACE("pcm_open() failed"); |
2354 |
|
✗ |
break; |
2355 |
|
|
} |
2356 |
|
|
|
2357 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_malloc(&hw_params); |
2358 |
|
✗ |
if (pcm_code < 0) |
2359 |
|
|
{ |
2360 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_malloc() failed"); |
2361 |
|
✗ |
break; |
2362 |
|
|
} |
2363 |
|
|
|
2364 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_any(handle, hw_params); |
2365 |
|
✗ |
if (pcm_code < 0) |
2366 |
|
|
{ |
2367 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_any() failed"); |
2368 |
|
✗ |
break; |
2369 |
|
|
} |
2370 |
|
|
|
2371 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); |
2372 |
|
✗ |
if (pcm_code < 0) |
2373 |
|
|
{ |
2374 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_set_access() failed"); |
2375 |
|
✗ |
break; |
2376 |
|
|
} |
2377 |
|
|
|
2378 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_set_format(handle, hw_params, pcm_format); |
2379 |
|
✗ |
if (pcm_code < 0) |
2380 |
|
|
{ |
2381 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_set_format() failed"); |
2382 |
|
✗ |
break; |
2383 |
|
|
} |
2384 |
|
|
|
2385 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_set_rate_near(handle, hw_params, &pcm_rate, NULL); |
2386 |
|
✗ |
if (pcm_code < 0) |
2387 |
|
|
{ |
2388 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_set_rate_near() failed"); |
2389 |
|
✗ |
break; |
2390 |
|
|
} |
2391 |
|
|
|
2392 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_set_channels(handle, hw_params, format->channels); |
2393 |
|
✗ |
if (pcm_code < 0) |
2394 |
|
|
{ |
2395 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_set_channels() failed"); |
2396 |
|
✗ |
break; |
2397 |
|
|
} |
2398 |
|
|
|
2399 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params(handle, hw_params); |
2400 |
|
✗ |
if (pcm_code < 0) |
2401 |
|
|
{ |
2402 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params() failed"); |
2403 |
|
✗ |
break; |
2404 |
|
|
} |
2405 |
|
|
|
2406 |
|
✗ |
impl->format = *format; |
2407 |
|
✗ |
gate_atomic_int_set(&impl->format_defined, 1); |
2408 |
|
✗ |
impl->handle = handle; |
2409 |
|
✗ |
handle = NULL; |
2410 |
|
✗ |
result = GATE_RESULT_OK; |
2411 |
|
✗ |
break; |
2412 |
|
|
} |
2413 |
|
✗ |
case gate_audio_device_output: |
2414 |
|
|
{ |
2415 |
|
✗ |
pcm_code = alsa_snd.pcm_open(&handle, impl->id, SND_PCM_STREAM_PLAYBACK, 0); |
2416 |
|
✗ |
if (pcm_code < 0) |
2417 |
|
|
{ |
2418 |
|
✗ |
GATE_DEBUG_TRACE("pcm_open() failed"); |
2419 |
|
✗ |
break; |
2420 |
|
|
} |
2421 |
|
|
|
2422 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_malloc(&hw_params); |
2423 |
|
✗ |
if (pcm_code < 0) |
2424 |
|
|
{ |
2425 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_malloc() failed"); |
2426 |
|
✗ |
break; |
2427 |
|
|
} |
2428 |
|
|
|
2429 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_any(handle, hw_params); |
2430 |
|
✗ |
if (pcm_code < 0) |
2431 |
|
|
{ |
2432 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_any() failed"); |
2433 |
|
✗ |
break; |
2434 |
|
|
} |
2435 |
|
|
|
2436 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); |
2437 |
|
✗ |
if (pcm_code < 0) |
2438 |
|
|
{ |
2439 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_set_access() failed"); |
2440 |
|
✗ |
break; |
2441 |
|
|
} |
2442 |
|
|
|
2443 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_set_format(handle, hw_params, pcm_format); |
2444 |
|
✗ |
if (pcm_code < 0) |
2445 |
|
|
{ |
2446 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_set_format() failed"); |
2447 |
|
✗ |
break; |
2448 |
|
|
} |
2449 |
|
|
|
2450 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_set_rate_near(handle, hw_params, &pcm_rate, NULL); |
2451 |
|
✗ |
if (pcm_code < 0) |
2452 |
|
|
{ |
2453 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_set_rate_near() failed"); |
2454 |
|
✗ |
break; |
2455 |
|
|
} |
2456 |
|
|
|
2457 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params_set_channels(handle, hw_params, format->channels); |
2458 |
|
✗ |
if (pcm_code < 0) |
2459 |
|
|
{ |
2460 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params_set_channels() failed"); |
2461 |
|
✗ |
break; |
2462 |
|
|
} |
2463 |
|
|
|
2464 |
|
✗ |
pcm_code = alsa_snd.pcm_hw_params(handle, hw_params); |
2465 |
|
✗ |
if (pcm_code < 0) |
2466 |
|
|
{ |
2467 |
|
✗ |
GATE_DEBUG_TRACE("pcm_hw_params() failed"); |
2468 |
|
✗ |
break; |
2469 |
|
|
} |
2470 |
|
|
|
2471 |
|
✗ |
pcm_code = alsa_snd.pcm_prepare(handle); |
2472 |
|
✗ |
if (pcm_code < 0) |
2473 |
|
|
{ |
2474 |
|
✗ |
GATE_DEBUG_TRACE("pcm_prepare() failed"); |
2475 |
|
✗ |
break; |
2476 |
|
|
} |
2477 |
|
|
|
2478 |
|
✗ |
impl->format = *format; |
2479 |
|
✗ |
gate_atomic_int_set(&impl->format_defined, 1); |
2480 |
|
✗ |
impl->handle = handle; |
2481 |
|
✗ |
handle = NULL; |
2482 |
|
✗ |
result = GATE_RESULT_OK; |
2483 |
|
✗ |
break; |
2484 |
|
|
} |
2485 |
|
✗ |
case gate_audio_device_unknown: |
2486 |
|
|
{ |
2487 |
|
✗ |
result = GATE_RESULT_NOTSUPPORTED; |
2488 |
|
✗ |
break; |
2489 |
|
|
} |
2490 |
|
|
} |
2491 |
|
✗ |
} while (0); |
2492 |
|
|
|
2493 |
|
✗ |
if (hw_params != NULL) |
2494 |
|
|
{ |
2495 |
|
✗ |
alsa_snd.pcm_hw_params_free(hw_params); |
2496 |
|
|
} |
2497 |
|
✗ |
if (handle != NULL) |
2498 |
|
|
{ |
2499 |
|
✗ |
alsa_snd.pcm_close(handle); |
2500 |
|
|
} |
2501 |
|
✗ |
return result; |
2502 |
|
|
} |
2503 |
|
✗ |
static gate_result_t gate_audio_device_impl_close(void* device) |
2504 |
|
|
{ |
2505 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2506 |
|
✗ |
gate_result_t result = GATE_RESULT_OK; |
2507 |
|
|
|
2508 |
|
✗ |
if (impl->handle != NULL) |
2509 |
|
|
{ |
2510 |
|
✗ |
alsa_snd.pcm_close(impl->handle); |
2511 |
|
✗ |
impl->handle = NULL; |
2512 |
|
|
} |
2513 |
|
✗ |
return result; |
2514 |
|
|
} |
2515 |
|
|
|
2516 |
|
✗ |
static gate_result_t gate_audio_device_impl_read(void* device, gate_audio_sample_t* sample) |
2517 |
|
|
{ |
2518 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2519 |
|
✗ |
gate_result_t result = GATE_RESULT_FAILED; |
2520 |
|
|
gate_size_t buffer_size; |
2521 |
|
|
int pcm_code; |
2522 |
|
|
char* buffer; |
2523 |
|
|
|
2524 |
|
|
do |
2525 |
|
|
{ |
2526 |
|
✗ |
buffer_size = impl->sample_count * impl->format.channels * impl->format.bits_per_sample / 8; |
2527 |
|
✗ |
sample->format = impl->format; |
2528 |
|
✗ |
sample->data = gate_memoryblock_create(buffer_size); |
2529 |
|
✗ |
sample->meta_data = NULL; |
2530 |
|
✗ |
sample->record_time = 0; |
2531 |
|
✗ |
buffer = gate_memoryblock_get_content(sample->data); |
2532 |
|
|
|
2533 |
|
✗ |
pcm_code = alsa_snd.pcm_readi(impl->handle, buffer, buffer_size * 8 / impl->format.bits_per_sample); |
2534 |
|
✗ |
if (pcm_code < 0) |
2535 |
|
|
{ |
2536 |
|
✗ |
result = GATE_RESULT_FAILED; |
2537 |
|
|
} |
2538 |
|
|
else |
2539 |
|
|
{ |
2540 |
|
✗ |
sample->sample_count = pcm_code / impl->format.channels; |
2541 |
|
✗ |
result = GATE_RESULT_OK; |
2542 |
|
|
} |
2543 |
|
|
} while (0); |
2544 |
|
|
|
2545 |
|
✗ |
return result; |
2546 |
|
|
} |
2547 |
|
✗ |
static gate_result_t gate_audio_device_impl_read_async(void* device, gate_delegate_t const* completion) |
2548 |
|
|
{ |
2549 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2550 |
|
✗ |
gate_result_t result = GATE_RESULT_NOTIMPLEMENTED; |
2551 |
|
|
|
2552 |
|
✗ |
return result; |
2553 |
|
|
} |
2554 |
|
✗ |
static gate_result_t gate_audio_device_impl_write(void* device, gate_audio_sample_t const* sample) |
2555 |
|
|
{ |
2556 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)device; |
2557 |
|
✗ |
gate_result_t result = GATE_RESULT_FAILED; |
2558 |
|
|
int pcm_code; |
2559 |
|
|
char* buffer; |
2560 |
|
|
gate_size_t bufferlen; |
2561 |
|
|
snd_pcm_sframes_t available_frames; |
2562 |
|
|
|
2563 |
|
|
do |
2564 |
|
|
{ |
2565 |
|
✗ |
if (0 != gate_compare_audio_format(&sample->format, &impl->format)) |
2566 |
|
|
{ |
2567 |
|
✗ |
result = GATE_RESULT_INVALIDINPUT; |
2568 |
|
✗ |
break; |
2569 |
|
|
} |
2570 |
|
|
|
2571 |
|
✗ |
buffer = gate_memoryblock_get_content(sample->data); |
2572 |
|
✗ |
bufferlen = gate_memoryblock_get_size(sample->data); |
2573 |
|
|
|
2574 |
|
✗ |
pcm_code = alsa_snd.pcm_writei(impl->handle, buffer, sample->sample_count * sample->format.channels); |
2575 |
|
✗ |
if (pcm_code < 0) |
2576 |
|
|
{ |
2577 |
|
✗ |
result = GATE_RESULT_FAILED; |
2578 |
|
✗ |
break; |
2579 |
|
|
} |
2580 |
|
|
|
2581 |
|
✗ |
available_frames = alsa_snd.pcm_avail(impl->handle); |
2582 |
|
✗ |
while (available_frames > 0) |
2583 |
|
|
{ |
2584 |
|
✗ |
gate_thread_sleep(1); |
2585 |
|
✗ |
available_frames = alsa_snd.pcm_avail(impl->handle); |
2586 |
|
|
} |
2587 |
|
|
|
2588 |
|
✗ |
result = GATE_RESULT_OK; |
2589 |
|
|
} while (0); |
2590 |
|
|
|
2591 |
|
✗ |
return result; |
2592 |
|
|
} |
2593 |
|
|
|
2594 |
|
✗ |
static gate_result_t gate_audio_device_impl_write_async( |
2595 |
|
|
void* device, gate_audio_sample_t const* sample, gate_delegate_t const* completion) |
2596 |
|
|
{ |
2597 |
|
✗ |
gate_audio_alsa_impl_t* impl = (gate_audio_alsa_impl_t*)impl; |
2598 |
|
✗ |
gate_result_t result = GATE_RESULT_NOTIMPLEMENTED; |
2599 |
|
|
|
2600 |
|
✗ |
return result; |
2601 |
|
|
} |
2602 |
|
|
|
2603 |
|
|
|
2604 |
|
✗ |
gate_result_t gate_audio_devices_enum(gate_enumint_t type, gate_delegate_t const* callback, void* param) |
2605 |
|
|
{ |
2606 |
|
✗ |
gate_result_t result = GATE_RESULT_OK; |
2607 |
|
|
gate_audio_alsa_impl_t* device; |
2608 |
|
|
|
2609 |
|
✗ |
if ((type == gate_audio_device_unknown) || (type == gate_audio_device_input)) |
2610 |
|
|
{ |
2611 |
|
✗ |
gate_audio_alsa_impl_t* impl = gate_mem_alloc(sizeof(gate_audio_alsa_impl_t)); |
2612 |
|
✗ |
gate_mem_clear(impl, sizeof(gate_audio_alsa_impl_t)); |
2613 |
|
✗ |
gate_init_audio_device_vtbl_impl(); |
2614 |
|
✗ |
impl->vtbl = &gate_audio_device_vtbl_impl; |
2615 |
|
|
|
2616 |
|
✗ |
gate_atomic_int_init(&impl->ref_counter, 1); |
2617 |
|
|
|
2618 |
|
✗ |
gate_str_print_text(impl->id, sizeof(impl->id), "default", 7); |
2619 |
|
✗ |
gate_str_print_text(impl->name, sizeof(impl->name), "Default Audio Input", 19); |
2620 |
|
✗ |
impl->type = gate_audio_device_input; |
2621 |
|
|
|
2622 |
|
✗ |
result = gate_delegate_invoke(callback, (gate_audio_device_t*)impl, param); |
2623 |
|
|
} |
2624 |
|
|
|
2625 |
|
✗ |
if ((type == gate_audio_device_unknown) || (type == gate_audio_device_output)) |
2626 |
|
|
{ |
2627 |
|
✗ |
gate_audio_alsa_impl_t* impl = gate_mem_alloc(sizeof(gate_audio_alsa_impl_t)); |
2628 |
|
✗ |
gate_mem_clear(impl, sizeof(gate_audio_alsa_impl_t)); |
2629 |
|
✗ |
gate_init_audio_device_vtbl_impl(); |
2630 |
|
✗ |
impl->vtbl = &gate_audio_device_vtbl_impl; |
2631 |
|
|
|
2632 |
|
✗ |
gate_atomic_int_init(&impl->ref_counter, 1); |
2633 |
|
|
|
2634 |
|
✗ |
gate_str_print_text(impl->id, sizeof(impl->id), "default", 7); |
2635 |
|
✗ |
gate_str_print_text(impl->name, sizeof(impl->name), "Default Audio Output", 20); |
2636 |
|
✗ |
impl->type = gate_audio_device_output; |
2637 |
|
|
|
2638 |
|
✗ |
result = gate_delegate_invoke(callback, (gate_audio_device_t*)impl, param); |
2639 |
|
|
} |
2640 |
|
|
|
2641 |
|
✗ |
return result; |
2642 |
|
|
} |
2643 |
|
|
|
2644 |
|
|
|
2645 |
|
|
|
2646 |
|
|
#endif /* GATE_IO_AUDIO_ALSA */ |
2647 |
|
|
|
2648 |
|
|
|
2649 |
|
|
|
2650 |
|
|
|
2651 |
|
|
#if defined(GATE_IO_AUDIO_NO_IMPL) |
2652 |
|
|
|
2653 |
|
|
gate_result_t gate_audio_devices_enum(gate_enumint_t type, gate_delegate_t const* callback, void* param) |
2654 |
|
|
{ |
2655 |
|
|
GATE_UNUSED_ARG(type); |
2656 |
|
|
GATE_UNUSED_ARG(callback); |
2657 |
|
|
GATE_UNUSED_ARG(param); |
2658 |
|
|
return GATE_RESULT_NOTIMPLEMENTED; |
2659 |
|
|
} |
2660 |
|
|
|
2661 |
|
|
#endif /* GATE_IO_AUDIO_NO_IMPL */ |
2662 |
|
|
|