blob: d3eefe2d09a9570bf21d23f0f59bf54d53440e66 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/*******************************************************
2
3 bdimad_dev.c
4
5 Author: bdSound Development Team (techsupport@bdsound.com)
6
7 Date: 30/10/2012
8 Version 1.0.206
9
10 Copyright (c) 2012 bdSound s.r.l. (www.bdsound.com)
11 All Rights Reserved.
12
13 *******************************************************/
14
15#include <pjmedia-audiodev/audiodev_imp.h>
16#include <pj/assert.h>
17#include <pj/log.h>
18#include <pj/os.h>
19#include <pj/string.h>
20#include <pj/unicode.h>
21#include <pjmedia/errno.h>
22
23#if defined(PJMEDIA_AUDIO_DEV_HAS_BDIMAD) && PJMEDIA_AUDIO_DEV_HAS_BDIMAD != 0
24
25#include <math.h>
26#include <wchar.h>
27
28/**************************
29* bdIMAD
30***************************/
31#include "../../third_party/bdsound/include/bdimad.h"
32
33/* Maximum supported audio devices */
34#define BD_IMAD_MAX_DEV_COUNT 100
35/* Maximum supported device name */
36#define BD_IMAD_MAX_DEV_LENGTH_NAME 256
37/* Only mono mode */
38#define BD_IMAD_MAX_CHANNELS 1
39/* Frequency default value (admitted 8000 Hz, 16000 Hz, 32000 Hz and 48000Hz) */
40#define BD_IMAD_DEFAULT_FREQ 48000
41/* Default milliseconds per buffer */
42#define BD_IMAD_MSECOND_PER_BUFFER 16
43/* Only for supported systems */
44#define BD_IMAD_STARTING_INPUT_VOLUME 50
45/* Only for supported systems */
46#define BD_IMAD_STARTING_OUTPUT_VOLUME 100
47/* Diagnostic Enable/Disable */
48#define BD_IMAD_DIAGNOSTIC BD_IMAD_DIAGNOSTIC_DISABLE
49
50/* Diagnostic folder path */
51wchar_t * bdImadPjDiagnosticFolderPath = L"";
52
53#define THIS_FILE "bdimad_dev.c"
54
55/* BD device info */
56struct bddev_info
57{
58 pjmedia_aud_dev_info info;
59 unsigned deviceId;
60};
61
62/* BD factory */
63struct bd_factory
64{
65 pjmedia_aud_dev_factory base;
66 pj_pool_t *base_pool;
67 pj_pool_t *pool;
68 pj_pool_factory *pf;
69 unsigned dev_count;
70 struct bddev_info *dev_info;
71};
72
73/* Sound stream. */
74struct bd_stream
75{
76 /** Base stream. */
77 pjmedia_aud_stream base;
78 /** Settings. */
79 pjmedia_aud_param param;
80 /** Memory pool. */
81 pj_pool_t *pool;
82
83 /** Capture callback. */
84 pjmedia_aud_rec_cb rec_cb;
85 /** Playback callback. */
86 pjmedia_aud_play_cb play_cb;
87 /** Application data. */
88 void *user_data;
89
90 /** Frame format */
91 pjmedia_format_id fmt_id;
92 /** Silence pattern */
93 pj_uint8_t silence_char;
94 /** Bytes per frame */
95 unsigned bytes_per_frame;
96 /** Samples per frame */
97 unsigned samples_per_frame;
98 /** Channel count */
99 int channel_count;
100
101 /** Extended frame buffer */
102 pjmedia_frame_ext *xfrm;
103 /** Total ext frm size */
104 unsigned xfrm_size;
105
106 /** Check running variable */
107 int go;
108
109 /** Timestamp iterator for capture */
110 pj_timestamp timestampCapture;
111 /** Timestamp iterator for playback */
112 pj_timestamp timestampPlayback;
113
114 /** bdIMAD current session instance */
115 bdIMADpj bdIMADpjInstance;
116 /** bdIMAD current session settings */
117 bdIMADpj_Setting_t *bdIMADpjSettingsPtr;
118 /** bdIMAD current session warnings */
119 bdIMADpj_Warnings_t *bdIMADpjWarningPtr;
120
121 pj_bool_t quit_flag;
122
123 pj_bool_t rec_thread_exited;
124 pj_bool_t rec_thread_initialized;
125 pj_thread_desc rec_thread_desc;
126 pj_thread_t *rec_thread;
127
128 pj_bool_t play_thread_exited;
129 pj_bool_t play_thread_initialized;
130 pj_thread_desc play_thread_desc;
131 pj_thread_t *play_thread;
132
133 /* Sometime the record callback does not return framesize as configured
134 * (e.g: in OSS), while this module must guarantee returning framesize
135 * as configured in the creation settings. In this case, we need a buffer
136 * for the recorded samples.
137 */
138 pj_int16_t *rec_buf;
139 unsigned rec_buf_count;
140
141 /* Sometime the player callback does not request framesize as configured
142 * (e.g: in Linux OSS) while sound device will always get samples from
143 * the other component as many as configured samples_per_frame.
144 */
145 pj_int16_t *play_buf;
146 unsigned play_buf_count;
147};
148
149/* Prototypes */
150
151// pjmedia_aud_dev_factory_op
152static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
153static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
154static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
155static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
156 unsigned index,
157 pjmedia_aud_dev_info *info);
158static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
159 unsigned index,
160 pjmedia_aud_param *param);
161static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
162 const pjmedia_aud_param *param,
163 pjmedia_aud_rec_cb rec_cb,
164 pjmedia_aud_play_cb play_cb,
165 void *user_data,
166 pjmedia_aud_stream **p_aud_strm);
167static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
168
169// pjmedia_aud_stream_op
170static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
171 pjmedia_aud_param *param);
172static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
173 pjmedia_aud_dev_cap cap,
174 void *value);
175static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
176 pjmedia_aud_dev_cap cap,
177 const void *value);
178static pj_status_t stream_start(pjmedia_aud_stream *strm);
179static pj_status_t stream_stop(pjmedia_aud_stream *strm);
180static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
181
182/* End Prototypes */
183
184/* Operations */
185static pjmedia_aud_dev_factory_op factory_op =
186{
187 &factory_init,
188 &factory_destroy,
189 &factory_get_dev_count,
190 &factory_get_dev_info,
191 &factory_default_param,
192 &factory_create_stream,
193 &factory_refresh
194};
195
196static pjmedia_aud_stream_op stream_op =
197{
198 &stream_get_param,
199 &stream_get_cap,
200 &stream_set_cap,
201 &stream_start,
202 &stream_stop,
203 &stream_destroy
204};
205
206/* End Operations */
207
208/* Utility functions */
209
210char* BD_IMAD_PJ_WCHARtoCHAR(wchar_t *orig)
211{
212 size_t origsize = wcslen(orig)+1;
213 const size_t newsize = origsize*sizeof(wchar_t);
214 char *nstring = (char*)calloc(newsize, sizeof(char));
215 wcstombs(nstring, orig, newsize);
216 return nstring;
217}
218
219#ifdef __cplusplus
220extern "C" {
221#endif
222void manage_code(const unsigned char * pCode, unsigned char *pMsg1,
223 unsigned char * pMsg2);
224#ifdef __cplusplus
225}
226#endif
227
228/* End Utility functions */
229
230/****************************************************************************
231* Factory operations
232*/
233
234/* Init BDIMAD audio driver */
235pjmedia_aud_dev_factory* pjmedia_bdimad_factory(pj_pool_factory *pf)
236{
237 struct bd_factory *f;
238 pj_pool_t *pool;
239
240 pool = pj_pool_create(pf, "BDIMAD_DRIVER", 1000, 1000, NULL);
241 f = PJ_POOL_ZALLOC_T(pool, struct bd_factory);
242 f->pf = pf;
243 f->base_pool = pool;
244 f->base.op = &factory_op;
245 f->pool = NULL;
246 f->dev_count = 0;
247 f->dev_info = NULL;
248
249 return &f->base;
250}
251
252/* API: init factory */
253static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
254{
255 pj_status_t ret = factory_refresh(f);
256 if (ret != PJ_SUCCESS) return ret;
257
258 PJ_LOG(4, (THIS_FILE, "BDIMAD initialized"));
259
260 return PJ_SUCCESS;
261}
262
263/* API: refresh the device list */
264static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
265{
266 struct bd_factory *wf = (struct bd_factory*)f;
267 unsigned int i = 0;
268 wchar_t *deviceNamep=NULL;
269 wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
270 unsigned int captureDeviceCount = 0;
271 wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
272 unsigned int playbackDeviceCount = 0;
273
274
275 if(wf->pool != NULL) {
276 pj_pool_release(wf->pool);
277 wf->pool = NULL;
278 }
279
280 // Enumerate capture sound devices
281 while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) !=
282 BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
283 {
284 wcscpy(captureDevName[captureDeviceCount], deviceNamep);
285 captureDeviceCount++;
286 }
287
288 // Enumerate playback sound devices
289 while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) !=
290 BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
291 {
292 wcscpy(playbackDevName[playbackDeviceCount], deviceNamep);
293 playbackDeviceCount++;
294 }
295
296 // Set devices info
297 wf->dev_count = captureDeviceCount + playbackDeviceCount;
298 wf->pool = pj_pool_create(wf->pf, "BD_IMAD_DEVICES", 1000, 1000, NULL);
299 wf->dev_info = (struct bddev_info*)pj_pool_calloc(wf->pool, wf->dev_count,
300 sizeof(struct bddev_info));
301
302 // Capture device properties
303 for(i=0;i<captureDeviceCount;i++) {
304 wf->dev_info[i].deviceId = i;
305 wf->dev_info[i].info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
306 PJMEDIA_AUD_DEV_CAP_EC;
307 wf->dev_info[i].info.default_samples_per_sec = BD_IMAD_DEFAULT_FREQ;
308 strcpy(wf->dev_info[i].info.driver, "BD_IMAD");
309 wf->dev_info[i].info.ext_fmt_cnt = 0;
310 wf->dev_info[i].info.input_count = BD_IMAD_MAX_CHANNELS;
311 wf->dev_info[i].info.output_count = 0;
312 strcpy(wf->dev_info[i].info.name,
313 BD_IMAD_PJ_WCHARtoCHAR(captureDevName[i]));
314 wf->dev_info[i].info.routes = 0;
315 }
316
317 // Playback device properties
318 for(i=0;i<playbackDeviceCount;i++) {
319 wf->dev_info[captureDeviceCount+i].deviceId = captureDeviceCount+i;
320 wf->dev_info[captureDeviceCount+i].info.caps =
321 PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
322 wf->dev_info[captureDeviceCount+i].info.default_samples_per_sec =
323 BD_IMAD_DEFAULT_FREQ;
324 strcpy(wf->dev_info[captureDeviceCount+i].info.driver, "BD_IMAD");
325 wf->dev_info[captureDeviceCount+i].info.ext_fmt_cnt = 0;
326 wf->dev_info[captureDeviceCount+i].info.input_count = 0;
327 wf->dev_info[captureDeviceCount+i].info.output_count =
328 BD_IMAD_MAX_CHANNELS;
329 strcpy(wf->dev_info[captureDeviceCount+i].info.name,
330 BD_IMAD_PJ_WCHARtoCHAR(playbackDevName[i]));
331 wf->dev_info[captureDeviceCount+i].info.routes = 0;
332 }
333
334 PJ_LOG(4, (THIS_FILE, "BDIMAD found %d devices:", wf->dev_count));
335 for(i=0; i<wf->dev_count; i++) {
336 PJ_LOG(4,
337 (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",
338 i,
339 wf->dev_info[i].info.name,
340 wf->dev_info[i].info.input_count,
341 wf->dev_info[i].info.output_count));
342 }
343 return PJ_SUCCESS;
344}
345
346/* API: destroy factory */
347static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
348{
349 struct bd_factory *wf = (struct bd_factory*)f;
350 pj_pool_t *pool = wf->base_pool;
351
352 pj_pool_release(wf->pool);
353 wf->base_pool = NULL;
354 pj_pool_release(pool);
355
356 return PJ_SUCCESS;
357}
358
359/* API: get number of devices */
360static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
361{
362 struct bd_factory *wf = (struct bd_factory*)f;
363 return wf->dev_count;
364}
365
366/* API: get device info */
367static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
368 unsigned index,
369 pjmedia_aud_dev_info *info)
370{
371 struct bd_factory *wf = (struct bd_factory*)f;
372
373 PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);
374
375 pj_memcpy(info, &wf->dev_info[index].info, sizeof(*info));
376
377 return PJ_SUCCESS;
378}
379
380/* API: create default device parameter */
381static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
382 unsigned index,
383 pjmedia_aud_param *param)
384{
385 struct bd_factory *wf = (struct bd_factory*)f;
386 struct bddev_info *di = &wf->dev_info[index];
387
388 pj_bzero(param, sizeof(*param));
389 if (di->info.input_count && di->info.output_count) {
390 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
391 param->rec_id = index;
392 param->play_id = index;
393 param->channel_count = di->info.output_count;
394 } else if(di->info.input_count) {
395 param->dir = PJMEDIA_DIR_CAPTURE;
396 param->rec_id = index;
397 param->play_id = PJMEDIA_AUD_INVALID_DEV;
398 param->channel_count = di->info.input_count;
399 } else if(di->info.output_count) {
400 param->dir = PJMEDIA_DIR_PLAYBACK;
401 param->play_id = index;
402 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
403 param->channel_count = di->info.output_count;
404 } else {
405 return PJMEDIA_EAUD_INVDEV;
406 }
407
408 param->bits_per_sample = BD_IMAD_BITS_X_SAMPLE;
409 param->clock_rate = di->info.default_samples_per_sec;
410 param->flags = di->info.caps;
411 param->samples_per_frame = di->info.default_samples_per_sec *
412 param->channel_count *
413 BD_IMAD_MSECOND_PER_BUFFER /
414 1000;
415
416 if(di->info.caps & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
417 param->input_vol = BD_IMAD_STARTING_INPUT_VOLUME;
418 }
419
420 if(di->info.caps & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
421 param->output_vol = BD_IMAD_STARTING_OUTPUT_VOLUME;
422 }
423
424 if(di->info.caps & PJMEDIA_AUD_DEV_CAP_EC) {
425 param->ec_enabled = PJ_TRUE;
426 }
427
428 return PJ_SUCCESS;
429}
430
431/* callbacks to set data */
432void bdimad_CaptureCallback(void *buffer, int samples, void *user_data)
433{
434 pj_status_t status = PJ_SUCCESS;
435 pjmedia_frame frame;
436 unsigned nsamples;
437
438 struct bd_stream *strm = (struct bd_stream*)user_data;
439
440 if(!strm->go)
441 goto on_break;
442
443 /* Known cases of callback's thread:
444 * - The thread may be changed in the middle of a session, e.g: in MacOS
445 * it happens when plugging/unplugging headphone.
446 * - The same thread may be reused in consecutive sessions. The first
447 * session will leave TLS set, but release the TLS data address,
448 * so the second session must re-register the callback's thread.
449 */
450 if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
451 {
452 pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
453 status = pj_thread_register("bd_CaptureCallback",
454 strm->rec_thread_desc,
455 &strm->rec_thread);
456 if (status != PJ_SUCCESS)
457 goto on_break;
458
459 strm->rec_thread_initialized = 1;
460 PJ_LOG(5,(THIS_FILE, "Recorder thread started"));
461 }
462
463 /* Calculate number of samples we've got */
464 nsamples = samples * strm->channel_count + strm->rec_buf_count;
465
466 /*
467 RECORD
468 */
469 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
470 if (nsamples >= strm->samples_per_frame) {
471 /* If buffer is not empty, combine the buffer with the just incoming
472 * samples, then call put_frame.
473 */
474 if (strm->rec_buf_count) {
475 unsigned chunk_count = 0;
476
477 chunk_count = strm->samples_per_frame - strm->rec_buf_count;
478 pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
479 (pj_int16_t*)buffer, chunk_count);
480
481 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
482 frame.buf = (void*) strm->rec_buf;
483 frame.size = strm->bytes_per_frame;
484 frame.timestamp.u64 = strm->timestampCapture.u64;
485 frame.bit_info = 0;
486
487 status = (*strm->rec_cb)(strm->user_data, &frame);
488
489 buffer = (pj_int16_t*) buffer + chunk_count;
490 nsamples -= strm->samples_per_frame;
491 strm->rec_buf_count = 0;
492 strm->timestampCapture.u64 += strm->samples_per_frame /
493 strm->channel_count;
494 }
495
496 /* Give all frames we have */
497 while (nsamples >= strm->samples_per_frame && status == 0) {
498 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
499 frame.buf = (void*) buffer;
500 frame.size = strm->bytes_per_frame;
501 frame.timestamp.u64 = strm->timestampCapture.u64;
502 frame.bit_info = 0;
503
504 status = (*strm->rec_cb)(strm->user_data, &frame);
505
506 buffer = (pj_int16_t*) buffer + strm->samples_per_frame;
507 nsamples -= strm->samples_per_frame;
508 strm->timestampCapture.u64 += strm->samples_per_frame /
509 strm->channel_count;
510 }
511
512 /* Store the remaining samples into the buffer */
513 if (nsamples && status == 0) {
514 strm->rec_buf_count = nsamples;
515 pjmedia_copy_samples(strm->rec_buf, (pj_int16_t*)buffer,
516 nsamples);
517 }
518
519 } else {
520 /* Not enough samples, let's just store them in the buffer */
521 pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
522 (pj_int16_t*)buffer,
523 samples * strm->channel_count);
524 strm->rec_buf_count += samples * strm->channel_count;
525 }
526 } else {
527 pj_assert(!"Frame type not supported");
528 }
529
530 strm->timestampCapture.u64 += strm->param.samples_per_frame /
531 strm->param.channel_count;
532
533 if (status==0)
534 return;
535
536on_break:
537 strm->rec_thread_exited = 1;
538}
539
540/* callbacks to get data */
541int bdimad_PlaybackCallback(void *buffer, int samples, void *user_data)
542{
543 pj_status_t status = PJ_SUCCESS;
544 pjmedia_frame frame;
545 struct bd_stream *strm = (struct bd_stream*)user_data;
546 unsigned nsamples_req = samples * strm->channel_count;
547
548 if(!strm->go)
549 goto on_break;
550
551 /* Known cases of callback's thread:
552 * - The thread may be changed in the middle of a session, e.g: in MacOS
553 * it happens when plugging/unplugging headphone.
554 * - The same thread may be reused in consecutive sessions. The first
555 * session will leave TLS set, but release the TLS data address,
556 * so the second session must re-register the callback's thread.
557 */
558 if (strm->play_thread_initialized == 0 || !pj_thread_is_registered())
559 {
560 pj_bzero(strm->play_thread_desc, sizeof(pj_thread_desc));
561 status = pj_thread_register("bd_PlaybackCallback",
562 strm->play_thread_desc,
563 &strm->play_thread);
564 if (status != PJ_SUCCESS)
565 goto on_break;
566
567 strm->play_thread_initialized = 1;
568 PJ_LOG(5,(THIS_FILE, "Player thread started"));
569 }
570
571 /*
572 PLAY
573 */
574 if(strm->fmt_id == PJMEDIA_FORMAT_L16) {
575 /* Check if any buffered samples */
576 if (strm->play_buf_count) {
577 /* samples buffered >= requested by sound device */
578 if (strm->play_buf_count >= nsamples_req) {
579 pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf,
580 nsamples_req);
581 strm->play_buf_count -= nsamples_req;
582 pjmedia_move_samples(strm->play_buf,
583 strm->play_buf + nsamples_req,
584 strm->play_buf_count);
585
586 return nsamples_req;
587 }
588
589 /* samples buffered < requested by sound device */
590 pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf,
591 strm->play_buf_count);
592 nsamples_req -= strm->play_buf_count;
593 buffer = (pj_int16_t*)buffer + strm->play_buf_count;
594 strm->play_buf_count = 0;
595 }
596
597 /* Fill output buffer as requested */
598 while (nsamples_req && status == 0) {
599 if (nsamples_req >= strm->samples_per_frame) {
600 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
601 frame.buf = buffer;
602 frame.size = strm->bytes_per_frame;
603 frame.timestamp.u64 = strm->timestampPlayback.u64;
604 frame.bit_info = 0;
605
606 status = (*strm->play_cb)(strm->user_data, &frame);
607 if (status != PJ_SUCCESS)
608 return 0;
609
610 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
611 pj_bzero(frame.buf, frame.size);
612
613 nsamples_req -= strm->samples_per_frame;
614 buffer = (pj_int16_t*)buffer + strm->samples_per_frame;
615 } else {
616 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
617 frame.buf = strm->play_buf;
618 frame.size = strm->bytes_per_frame;
619 frame.timestamp.u64 = strm->timestampPlayback.u64;
620 frame.bit_info = 0;
621
622 status = (*strm->play_cb)(strm->user_data, &frame);
623 if (status != PJ_SUCCESS)
624 return 0;
625
626 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
627 pj_bzero(frame.buf, frame.size);
628
629 pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf,
630 nsamples_req);
631 strm->play_buf_count = strm->samples_per_frame -
632 nsamples_req;
633 pjmedia_move_samples(strm->play_buf,
634 strm->play_buf+nsamples_req,
635 strm->play_buf_count);
636 nsamples_req = 0;
637 }
638
639 strm->timestampPlayback.u64 += strm->samples_per_frame /
640 strm->channel_count;
641 }
642 } else {
643 pj_assert(!"Frame type not supported");
644 }
645
646 if(status != PJ_SUCCESS) {
647 return 0;
648 }
649
650 strm->timestampPlayback.u64 += strm->param.samples_per_frame /
651 strm->param.channel_count;
652
653 if (status == 0)
654 return samples;
655
656on_break:
657 strm->play_thread_exited = 1;
658 return 0;
659}
660
661/* Internal: Get format name */
662static const char *get_fmt_name(pj_uint32_t id)
663{
664 static char name[8];
665
666 if (id == PJMEDIA_FORMAT_L16)
667 return "PCM";
668 pj_memcpy(name, &id, 4);
669 name[4] = '\0';
670 return name;
671}
672
673/* Internal: create BD device. */
674static pj_status_t init_streams(struct bd_factory *wf,
675 struct bd_stream *strm,
676 const pjmedia_aud_param *prm)
677{
678 unsigned ptime;
679 enum bdIMADpj_Status errorInitAEC;
680 wchar_t *deviceNamep=NULL;
681 wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
682 int captureDeviceCount = 0;
683 wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
684 int playbackDeviceCount = 0;
685
686 PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);
687 PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);
688
689 ptime = prm->samples_per_frame *
690 1000 /
691 (prm->clock_rate * prm->channel_count);
692 strm->bytes_per_frame = (prm->clock_rate *
693 ((prm->channel_count * prm->bits_per_sample) / 8)) *
694 ptime /
695 1000;
696 strm->timestampCapture.u64 = 0;
697 strm->timestampPlayback.u64 = 0;
698
699 //BD_IMAD_PJ
700 bdIMADpj_CreateStructures(&strm->bdIMADpjSettingsPtr,
701 &strm->bdIMADpjWarningPtr);
702
703 strm->bdIMADpjSettingsPtr->FrameSize_ms = ptime;
704 strm->bdIMADpjSettingsPtr->DiagnosticEnable = BD_IMAD_DIAGNOSTIC;
705 strm->bdIMADpjSettingsPtr->DiagnosticFolderPath =
706 bdImadPjDiagnosticFolderPath;
707 strm->bdIMADpjSettingsPtr->validate = (void *)manage_code;
708
709 if(prm->clock_rate != 8000 && prm->clock_rate != 16000
710 && prm->clock_rate != 32000 && prm->clock_rate != 48000) {
711 PJ_LOG(4, (THIS_FILE,
712 "BDIMAD support 8000 Hz, 16000 Hz, 32000 Hz and 48000 Hz "
713 "frequency."));
714 }
715 strm->bdIMADpjSettingsPtr->SamplingFrequency = prm->clock_rate;
716
717 if(prm->channel_count > BD_IMAD_MAX_CHANNELS) {
718 PJ_LOG(4, (THIS_FILE,
719 "BDIMAD doesn't support a number of channels upper than %d.",
720 BD_IMAD_MAX_CHANNELS));
721 }
722
723 // Enumerate capture sound devices
724 while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) !=
725 BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
726 {
727 wcscpy(captureDevName[captureDeviceCount], deviceNamep);
728 captureDeviceCount++;
729 }
730 strm->bdIMADpjSettingsPtr->CaptureDevice = captureDevName[(int)prm->rec_id];
731
732 // Enumerate playback sound devices
733 while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) !=
734 BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
735 {
736 wcscpy(playbackDevName[playbackDeviceCount], deviceNamep);
737 playbackDeviceCount++;
738 }
739 strm->bdIMADpjSettingsPtr->PlayDevice =
740 playbackDevName[(int)(prm->play_id-captureDeviceCount)];
741
742 strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer = &bdimad_CaptureCallback;
743 strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer_user_data = (void*)strm;
744 strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer = &bdimad_PlaybackCallback;
745 strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer_user_data = (void*)strm;
746
747 if(strm->bdIMADpjInstance != NULL)
748 bdIMADpj_FreeAEC(&strm->bdIMADpjInstance);
749 strm->bdIMADpjInstance = NULL;
750
751 errorInitAEC = bdIMADpj_InitAEC(&strm->bdIMADpjInstance,
752 &strm->bdIMADpjSettingsPtr,
753 &strm->bdIMADpjWarningPtr);
754
755 {
756 int auxInt = (prm->ec_enabled == PJ_TRUE ? 1 : 0);
757 bdIMADpj_setParameter(strm->bdIMADpjInstance,
758 BD_PARAM_IMAD_PJ_AEC_ENABLE,
759 &auxInt);
760 auxInt = 1;
761 //Mic control On by default
762 bdIMADpj_setParameter(strm->bdIMADpjInstance,
763 BD_PARAM_IMAD_PJ_MIC_CONTROL_ENABLE,
764 &auxInt);
765 }
766
767 if(errorInitAEC != BD_PJ_OK &&
768 errorInitAEC != BD_PJ_WARN_BDIMAD_WARNING_ASSERTED)
769 {
770 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(errorInitAEC);
771 }
772
773 return PJ_SUCCESS;
774}
775
776/****************************************
777 API: create stream
778*****************************************/
779static pj_status_t stream_stopBDIMAD(pjmedia_aud_stream *s)
780{
781 struct bd_stream *strm = (struct bd_stream*)s;
782 pj_status_t status = PJ_SUCCESS;
783 int i, err = 0;
784
785 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
786
787 strm->go = 0;
788
789 for (i=0; !strm->rec_thread_exited && i<100; ++i)
790 pj_thread_sleep(10);
791 for (i=0; !strm->play_thread_exited && i<100; ++i)
792 pj_thread_sleep(10);
793
794 pj_thread_sleep(1);
795
796 PJ_LOG(5,(THIS_FILE, "Stopping stream.."));
797
798 strm->play_thread_initialized = 0;
799 strm->rec_thread_initialized = 0;
800
801 PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));
802
803 return status;
804}
805
806static pj_status_t stream_stop(pjmedia_aud_stream *s)
807{
808 struct bd_stream *strm = (struct bd_stream*)s;
809
810 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
811
812 if(strm->bdIMADpjInstance != NULL) {
813 return stream_stopBDIMAD(s);
814 } else {
815 return PJMEDIA_EAUD_ERR;
816 }
817}
818
819static pj_status_t stream_destroyBDIMAD(pjmedia_aud_stream *s)
820{
821 struct bd_stream *strm = (struct bd_stream*)s;
822 int i = 0;
823
824 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
825
826 stream_stopBDIMAD(s);
827
828 // DeInit BDIMAD
829 bdIMADpj_FreeAEC(&strm->bdIMADpjInstance);
830 PJ_LOG(4, (THIS_FILE, "Free AEC"));
831
832 bdIMADpj_FreeStructures(&strm->bdIMADpjSettingsPtr,
833 &strm->bdIMADpjWarningPtr);
834 PJ_LOG(4, (THIS_FILE, "Free AEC Structure"));
835
836 strm->bdIMADpjInstance = NULL;
837 strm->bdIMADpjSettingsPtr = NULL;
838 strm->bdIMADpjWarningPtr = NULL;
839
840 strm->quit_flag = 1;
841 for (i=0; !strm->rec_thread_exited && i<100; ++i) {
842 pj_thread_sleep(1);
843 }
844 for (i=0; !strm->play_thread_exited && i<100; ++i) {
845 pj_thread_sleep(1);
846 }
847
848 PJ_LOG(5,(THIS_FILE, "Destroying stream.."));
849
850 pj_pool_release(strm->pool);
851 return PJ_SUCCESS;
852}
853
854/* API: Destroy stream. */
855static pj_status_t stream_destroy(pjmedia_aud_stream *s)
856{
857 struct bd_stream *strm = (struct bd_stream*)s;
858
859 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
860
861 if(strm->bdIMADpjInstance != NULL) {
862 return stream_destroyBDIMAD(s);
863 } else {
864 return PJMEDIA_EAUD_ERR;
865 }
866}
867
868/* API: set capability */
869static pj_status_t stream_set_capBDIMAD(pjmedia_aud_stream *s,
870 pjmedia_aud_dev_cap cap,
871 const void *pval)
872{
873 struct bd_stream *strm = (struct bd_stream*)s;
874 bdIMADpj_Status res = BD_PJ_OK;
875 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
876
877 if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
878 /* Output volume setting */
879 float vol = (float)*(unsigned*)pval;
880
881 if(vol > 100.0f) vol = 100.0f;
882 if(vol < 0.0f) vol = 0.0f;
883
884 vol = vol / 100.0f;
885 res = bdIMADpj_setParameter(strm->bdIMADpjInstance,
886 BD_PARAM_IMAD_PJ_SPK_VOLUME, &vol);
887
888
889 if(res == BD_PJ_OK) {
890 strm->param.output_vol = *(unsigned*)pval;
891 return PJ_SUCCESS;
892 } else {
893 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
894 }
895 }
896
897 if(cap == PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
898 /* Input volume setting */
899 float vol = (float)*(unsigned*)pval;
900
901 if(vol > 100.0f) vol = 100.0f;
902 if(vol < 0.0f) vol = 0.0f;
903
904 vol = vol / 100.0f;
905 res = bdIMADpj_setParameter(strm->bdIMADpjInstance,
906 BD_PARAM_IMAD_PJ_MIC_VOLUME, &vol);
907 if(res == BD_PJ_OK) {
908 strm->param.input_vol = *(unsigned*)pval;
909 return PJ_SUCCESS;
910 } else {
911 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
912 }
913 }
914
915 if(cap == PJMEDIA_AUD_DEV_CAP_EC) {
916 int aecOnOff = (*(pj_bool_t*)pval == PJ_TRUE ? 1 : 0);
917
918 /* AEC setting */
919 res = bdIMADpj_setParameter(strm->bdIMADpjInstance,
920 BD_PARAM_IMAD_PJ_AEC_ENABLE,
921 &aecOnOff);
922 if(res == BD_PJ_OK) {
923 strm->param.ec_enabled = (aecOnOff == 1 ? PJ_TRUE : PJ_FALSE);
924 return PJ_SUCCESS;
925 } else {
926 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
927 }
928 }
929
930 return PJMEDIA_EAUD_INVCAP;
931}
932
933static pj_status_t factory_create_streamBDIMAD(pjmedia_aud_dev_factory *f,
934 const pjmedia_aud_param *param,
935 pjmedia_aud_rec_cb rec_cb,
936 pjmedia_aud_play_cb play_cb,
937 void *user_data,
938 pjmedia_aud_stream **p_aud_strm)
939{
940 struct bd_factory *wf = (struct bd_factory*)f;
941 pj_pool_t *pool;
942 struct bd_stream *strm;
943 pj_uint8_t silence_char;
944 pj_status_t status;
945
946 switch (param->ext_fmt.id) {
947 case PJMEDIA_FORMAT_L16:
948 silence_char = '\0';
949 break;
950 default:
951 return PJMEDIA_EAUD_BADFORMAT;
952 }
953
954 /* Create and Initialize stream descriptor */
955 pool = pj_pool_create(wf->pf, "BDIMAD_STREAM", 1000, 1000, NULL);
956 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
957
958 strm = PJ_POOL_ZALLOC_T(pool, struct bd_stream);
959 pj_memcpy(&strm->param, param, sizeof(*param));
960 strm->pool = pool;
961 strm->rec_cb = rec_cb;
962 strm->play_cb = play_cb;
963 strm->user_data = user_data;
964 strm->fmt_id = (pjmedia_format_id)param->ext_fmt.id;
965 strm->silence_char = silence_char;
966 strm->channel_count = param->channel_count;
967 strm->samples_per_frame = param->samples_per_frame;
968
969 if (param->dir & PJMEDIA_DIR_CAPTURE_PLAYBACK) {
970 status = init_streams(wf, strm, param);
971
972 if (status != PJ_SUCCESS) {
973 stream_destroyBDIMAD(&strm->base);
974 return status;
975 }
976 } else {
977 stream_destroyBDIMAD(&strm->base);
978 return PJMEDIA_EAUD_ERR;
979 }
980
981 strm->rec_buf = (pj_int16_t*)pj_pool_alloc(pool,
982 strm->bytes_per_frame);
983 if (!strm->rec_buf) {
984 pj_pool_release(pool);
985 return PJ_ENOMEM;
986 }
987 strm->rec_buf_count = 0;
988
989 strm->play_buf = (pj_int16_t*)pj_pool_alloc(pool,
990 strm->bytes_per_frame);
991 if (!strm->play_buf) {
992 pj_pool_release(pool);
993 return PJ_ENOMEM;
994 }
995 strm->play_buf_count = 0;
996
997 /* Apply the remaining settings */
998 if(param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
999 stream_set_capBDIMAD(&strm->base,
1000 PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1001 &param->output_vol);
1002 }
1003 if(param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
1004 stream_set_capBDIMAD(&strm->base,
1005 PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
1006 &param->input_vol);
1007 }
1008 if(param->flags & PJMEDIA_AUD_DEV_CAP_EC) {
1009 stream_set_capBDIMAD(&strm->base,
1010 PJMEDIA_AUD_DEV_CAP_EC,
1011 &param->ec_enabled);
1012 }
1013
1014 strm->base.op = &stream_op;
1015 *p_aud_strm = &strm->base;
1016
1017 return PJ_SUCCESS;
1018}
1019
1020static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
1021 const pjmedia_aud_param *param,
1022 pjmedia_aud_rec_cb rec_cb,
1023 pjmedia_aud_play_cb play_cb,
1024 void *user_data,
1025 pjmedia_aud_stream **p_aud_strm)
1026{
1027 return factory_create_streamBDIMAD(f, param, rec_cb,
1028 play_cb, user_data, p_aud_strm);
1029}
1030// ----------------------------------------------------------------------
1031// ----------------------------------------------------------------------
1032// ----------------------------------------------------------------------
1033
1034/* API: Get stream info. */
1035static pj_status_t stream_get_param(pjmedia_aud_stream *s,
1036 pjmedia_aud_param *pi)
1037{
1038 struct bd_stream *strm = (struct bd_stream*)s;
1039
1040 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1041
1042 pj_memcpy(pi, &strm->param, sizeof(*pi));
1043
1044 // Get the output volume setting
1045 if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1046 &pi->output_vol) == PJ_SUCCESS)
1047 {
1048 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1049 }
1050
1051 // Get the input volume setting
1052 if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
1053 &pi->input_vol) == PJ_SUCCESS)
1054 {
1055 pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING;
1056 }
1057
1058 // Get the AEC setting
1059 if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC, &pi->ec_enabled) == PJ_SUCCESS)
1060 {
1061 pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;
1062 }
1063
1064 return PJ_SUCCESS;
1065}
1066
1067static pj_status_t stream_get_capBDIMAD(pjmedia_aud_stream *s,
1068 pjmedia_aud_dev_cap cap,
1069 void *pval)
1070{
1071 struct bd_stream *strm = (struct bd_stream*)s;
1072 bdIMADpj_Status res = BD_PJ_OK;
1073
1074 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1075
1076 if(cap == PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING)
1077 {
1078 /* Input volume setting */
1079 float vol;
1080 res = bdIMADpj_getParameter(strm->bdIMADpjInstance,
1081 BD_PARAM_IMAD_PJ_MIC_VOLUME, &vol);
1082 if(res == BD_PJ_OK) {
1083 vol = vol * 100;
1084 if(vol > 100.0f) vol = 100.0f;
1085 if(vol < 0.0f) vol = 0.0f;
1086 *(unsigned int *)pval = (unsigned int)vol;
1087 return PJ_SUCCESS;
1088 } else{
1089 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
1090 }
1091 } else if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1092 /* Output volume setting */
1093 float vol;
1094 res = bdIMADpj_getParameter(strm->bdIMADpjInstance,
1095 BD_PARAM_IMAD_PJ_SPK_VOLUME, &vol);
1096 if(res == BD_PJ_OK) {
1097 vol = vol * 100;
1098 if(vol > 100.0f) vol = 100.0f;
1099 if(vol < 0.0f) vol = 0.0f;
1100 *(unsigned int *)pval = (unsigned int)vol;
1101 return PJ_SUCCESS;
1102 } else {
1103 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
1104 }
1105 }
1106 else if(cap == PJMEDIA_AUD_DEV_CAP_EC) {
1107 int aecIsOn;
1108 res = bdIMADpj_getParameter(strm->bdIMADpjInstance,
1109 BD_PARAM_IMAD_PJ_AEC_ENABLE, &aecIsOn);
1110 if(res == BD_PJ_OK) {
1111 *(pj_bool_t*)pval = (aecIsOn == 1 ? PJ_TRUE : PJ_FALSE);
1112 return PJ_SUCCESS;
1113 } else {
1114 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
1115 }
1116 } else {
1117 return PJMEDIA_EAUD_INVCAP;
1118 }
1119}
1120
1121static pj_status_t stream_startBDIMAD(pjmedia_aud_stream *s)
1122{
1123 struct bd_stream *strm = (struct bd_stream*)s;
1124
1125 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
1126
1127 strm->go = 1;
1128
1129 return PJ_SUCCESS;
1130}
1131
1132/* API: get capability */
1133static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1134 pjmedia_aud_dev_cap cap,
1135 void *pval)
1136{
1137 struct bd_stream *strm = (struct bd_stream*)s;
1138
1139 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
1140
1141 if(strm->bdIMADpjInstance != NULL) {
1142 return stream_get_capBDIMAD(s, cap, pval);
1143 } else {
1144 return PJMEDIA_EAUD_ERR;
1145 }
1146}
1147
1148/* API: set capability */
1149static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1150 pjmedia_aud_dev_cap cap,
1151 const void *pval)
1152{
1153 struct bd_stream *strm = (struct bd_stream*)s;
1154
1155 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
1156
1157 if(strm->bdIMADpjInstance != NULL) {
1158 return stream_set_capBDIMAD(s, cap, pval);
1159 } else {
1160 return PJMEDIA_EAUD_ERR;
1161 }
1162}
1163
1164/* API: Start stream. */
1165static pj_status_t stream_start(pjmedia_aud_stream *s)
1166{
1167 struct bd_stream *strm = (struct bd_stream*)s;
1168
1169 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
1170
1171 if(strm->bdIMADpjInstance != NULL) {
1172 return stream_startBDIMAD(s);
1173 } else {
1174 return PJMEDIA_EAUD_ERR;
1175 }
1176}
1177
1178#if defined (_MSC_VER)
1179#pragma comment ( lib, "bdClientValidation.lib" )
1180#pragma comment ( lib, "bdIMADpj.lib" )
1181#endif
1182
1183
1184#endif /* PJMEDIA_AUDIO_DEV_HAS_BDIMAD */
1185