Tristan Matthews | 0a329cc | 2013-07-17 13:20:14 -0400 | [diff] [blame] | 1 | /*******************************************************
|
| 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 */
|
| 51 | wchar_t * bdImadPjDiagnosticFolderPath = L"";
|
| 52 |
|
| 53 | #define THIS_FILE "bdimad_dev.c"
|
| 54 |
|
| 55 | /* BD device info */
|
| 56 | struct bddev_info
|
| 57 | {
|
| 58 | pjmedia_aud_dev_info info;
|
| 59 | unsigned deviceId;
|
| 60 | };
|
| 61 |
|
| 62 | /* BD factory */
|
| 63 | struct 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. */
|
| 74 | struct 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
|
| 152 | static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
|
| 153 | static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
|
| 154 | static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
|
| 155 | static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
|
| 156 | unsigned index,
|
| 157 | pjmedia_aud_dev_info *info);
|
| 158 | static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
|
| 159 | unsigned index,
|
| 160 | pjmedia_aud_param *param);
|
| 161 | static 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);
|
| 167 | static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
|
| 168 |
|
| 169 | // pjmedia_aud_stream_op
|
| 170 | static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
|
| 171 | pjmedia_aud_param *param);
|
| 172 | static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
|
| 173 | pjmedia_aud_dev_cap cap,
|
| 174 | void *value);
|
| 175 | static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
|
| 176 | pjmedia_aud_dev_cap cap,
|
| 177 | const void *value);
|
| 178 | static pj_status_t stream_start(pjmedia_aud_stream *strm);
|
| 179 | static pj_status_t stream_stop(pjmedia_aud_stream *strm);
|
| 180 | static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
|
| 181 |
|
| 182 | /* End Prototypes */
|
| 183 |
|
| 184 | /* Operations */
|
| 185 | static 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 |
|
| 196 | static 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 |
|
| 210 | char* 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
|
| 220 | extern "C" {
|
| 221 | #endif
|
| 222 | void 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 */
|
| 235 | pjmedia_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 */
|
| 253 | static 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 */
|
| 264 | static 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 */
|
| 347 | static 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 */
|
| 360 | static 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 */
|
| 367 | static 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 */
|
| 381 | static 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 */
|
| 432 | void 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 |
|
| 536 | on_break:
|
| 537 | strm->rec_thread_exited = 1;
|
| 538 | }
|
| 539 |
|
| 540 | /* callbacks to get data */
|
| 541 | int 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 |
|
| 656 | on_break:
|
| 657 | strm->play_thread_exited = 1;
|
| 658 | return 0;
|
| 659 | }
|
| 660 |
|
| 661 | /* Internal: Get format name */
|
| 662 | static 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. */
|
| 674 | static 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 | *****************************************/
|
| 779 | static 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 |
|
| 806 | static 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 |
|
| 819 | static 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. */
|
| 855 | static 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 */
|
| 869 | static 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 |
|
| 933 | static 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 | ¶m->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 | ¶m->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 | ¶m->ec_enabled);
|
| 1012 | }
|
| 1013 |
|
| 1014 | strm->base.op = &stream_op;
|
| 1015 | *p_aud_strm = &strm->base;
|
| 1016 |
|
| 1017 | return PJ_SUCCESS;
|
| 1018 | }
|
| 1019 |
|
| 1020 | static 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. */
|
| 1035 | static 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 |
|
| 1067 | static 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 |
|
| 1121 | static 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 */
|
| 1133 | static 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 */
|
| 1149 | static 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. */
|
| 1165 | static 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 |
|