| 1 | /*************************************************************************** |
|---|
| 2 | * Copyright (c) 2003-2009, Broadcom Corporation |
|---|
| 3 | * All Rights Reserved |
|---|
| 4 | * Confidential Property of Broadcom Corporation |
|---|
| 5 | * |
|---|
| 6 | * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE |
|---|
| 7 | * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR |
|---|
| 8 | * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. |
|---|
| 9 | * |
|---|
| 10 | * $brcm_Workfile: bsettop_pcm_playback.c $ |
|---|
| 11 | * $brcm_Revision: 8 $ |
|---|
| 12 | * $brcm_Date: 12/3/09 4:06p $ |
|---|
| 13 | * |
|---|
| 14 | * Module Description: |
|---|
| 15 | * |
|---|
| 16 | * Revision History: |
|---|
| 17 | * |
|---|
| 18 | * $brcm_Log: /BSEAV/api/src/nexus/bsettop_pcm_playback.c $ |
|---|
| 19 | * |
|---|
| 20 | * 8 12/3/09 4:06p jgarrett |
|---|
| 21 | * SW7405-3452: Setting default playback start threshold to 0 |
|---|
| 22 | * |
|---|
| 23 | * 7 11/19/09 8:02p jgarrett |
|---|
| 24 | * SW7405-3357: Adding audio output routing for decode and pcm playback |
|---|
| 25 | * prior to start |
|---|
| 26 | * |
|---|
| 27 | * 6 4/3/08 7:26p jgarrett |
|---|
| 28 | * PR 40017: Fixing race between decode and pcm |
|---|
| 29 | * |
|---|
| 30 | * 5 4/3/08 5:41p jgarrett |
|---|
| 31 | * PR 41312: Setting callback events |
|---|
| 32 | * |
|---|
| 33 | * 4 3/12/08 3:17p jgarrett |
|---|
| 34 | * PR 40017: Adding PCM support |
|---|
| 35 | * |
|---|
| 36 | * 3 12/20/07 10:28a erickson |
|---|
| 37 | * PR37590: fix warnings |
|---|
| 38 | * |
|---|
| 39 | * 2 10/16/07 12:35p erickson |
|---|
| 40 | * PR36068: brutus up over settop api/nexus |
|---|
| 41 | * |
|---|
| 42 | ***************************************************************************/ |
|---|
| 43 | #include "bsettop_impl.h" |
|---|
| 44 | #include "nexus_audio_playback.h" |
|---|
| 45 | #include "nexus_audio_input.h" |
|---|
| 46 | #include "nexus_audio_output.h" |
|---|
| 47 | |
|---|
| 48 | /* This is hardcoded in bconfig right now */ |
|---|
| 49 | #define B_N_PCM_PLAYS 2 |
|---|
| 50 | |
|---|
| 51 | BDBG_OBJECT_ID(bpcm_play); |
|---|
| 52 | |
|---|
| 53 | BDBG_MODULE(pcm); |
|---|
| 54 | |
|---|
| 55 | static bpcm_play g_pcmplays[B_N_PCM_PLAYS]; |
|---|
| 56 | static void bpcm_play_p_callback(void *pParam1, int param2); |
|---|
| 57 | static void bpcm_play_p_callback_handler(void *context); |
|---|
| 58 | |
|---|
| 59 | void bpcm_play_settings_init(bpcm_play_settings *settings, bpcm_play_t pcmplay) |
|---|
| 60 | { |
|---|
| 61 | BDBG_ASSERT(NULL != settings); |
|---|
| 62 | BSTD_UNUSED(pcmplay); |
|---|
| 63 | BKNI_Memset(settings, 0, sizeof(*settings)); |
|---|
| 64 | settings->pcm.bits_per_sample=16; |
|---|
| 65 | settings->pcm.channels = 2; |
|---|
| 66 | settings->pcm.sample_rate = 44100; /* This was the settop API default, brutus relies on that */ |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | bpcm_play_t bpcm_play_open(bobject_t id) |
|---|
| 70 | { |
|---|
| 71 | bpcm_play_t pcmplay; |
|---|
| 72 | unsigned index = B_ID_GET_INDEX(id); |
|---|
| 73 | |
|---|
| 74 | if ( index >= B_N_PCM_PLAYS ) |
|---|
| 75 | { |
|---|
| 76 | BSETTOP_ERROR(berr_invalid_parameter); |
|---|
| 77 | return NULL; |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | pcmplay = &g_pcmplays[index]; |
|---|
| 81 | BDBG_MSG(("Opening PCM play %d (first=%d)", index)); |
|---|
| 82 | |
|---|
| 83 | if ( pcmplay->opened ) |
|---|
| 84 | { |
|---|
| 85 | BDBG_ERR(("PCM Playback %d (%p) already open (opened=%d)", index, pcmplay, pcmplay->opened)); |
|---|
| 86 | return NULL; |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | BDBG_OBJECT_SET(pcmplay, bpcm_play); |
|---|
| 90 | |
|---|
| 91 | pcmplay->nPlayback = NEXUS_AudioPlayback_Open(index, NULL); |
|---|
| 92 | if ( NULL == pcmplay->nPlayback ) |
|---|
| 93 | { |
|---|
| 94 | BDBG_ERR(("Unable to open nexus audio playback channel")); |
|---|
| 95 | BDBG_OBJECT_UNSET(pcmplay, bpcm_play); |
|---|
| 96 | return NULL; |
|---|
| 97 | } |
|---|
| 98 | pcmplay->event = B_Event_Create(NULL); |
|---|
| 99 | if ( NULL == pcmplay->event ) |
|---|
| 100 | { |
|---|
| 101 | BSETTOP_ERROR(berr_external_error); |
|---|
| 102 | NEXUS_AudioPlayback_Close(pcmplay->nPlayback); |
|---|
| 103 | BDBG_OBJECT_UNSET(pcmplay, bpcm_play); |
|---|
| 104 | return NULL; |
|---|
| 105 | } |
|---|
| 106 | pcmplay->eventId = b_event_register(pcmplay->event, bpcm_play_p_callback_handler, pcmplay); |
|---|
| 107 | if ( NULL == pcmplay->eventId ) |
|---|
| 108 | { |
|---|
| 109 | BSETTOP_ERROR(berr_external_error); |
|---|
| 110 | B_Event_Destroy(pcmplay->event); |
|---|
| 111 | NEXUS_AudioPlayback_Close(pcmplay->nPlayback); |
|---|
| 112 | BDBG_OBJECT_UNSET(pcmplay, bpcm_play); |
|---|
| 113 | return NULL; |
|---|
| 114 | } |
|---|
| 115 | pcmplay->display = NULL; |
|---|
| 116 | pcmplay->opened = true; |
|---|
| 117 | pcmplay->disabled = false; |
|---|
| 118 | |
|---|
| 119 | return pcmplay; |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | void bpcm_play_close(bpcm_play_t pcmplay) |
|---|
| 123 | { |
|---|
| 124 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 125 | BDBG_ASSERT(pcmplay->opened); |
|---|
| 126 | |
|---|
| 127 | b_event_unregister(pcmplay->eventId); |
|---|
| 128 | B_Event_Destroy(pcmplay->event); |
|---|
| 129 | |
|---|
| 130 | if ( pcmplay->display ) |
|---|
| 131 | { |
|---|
| 132 | BDBG_WRN(("Forcing stop on pcm playback channel %p", pcmplay)); |
|---|
| 133 | bpcm_play_stop(pcmplay); |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | NEXUS_AudioPlayback_Close(pcmplay->nPlayback); |
|---|
| 137 | pcmplay->opened = false; |
|---|
| 138 | BDBG_OBJECT_UNSET(pcmplay, bpcm_play); |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | bresult bpcm_play_start(bpcm_play_t pcmplay, bdisplay_t display, const bpcm_play_settings *settings) |
|---|
| 142 | { |
|---|
| 143 | NEXUS_Error errCode; |
|---|
| 144 | |
|---|
| 145 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 146 | BDBG_ASSERT(pcmplay->opened); |
|---|
| 147 | BDBG_ASSERT(NULL != display); |
|---|
| 148 | BDBG_ASSERT(NULL != settings); |
|---|
| 149 | |
|---|
| 150 | BDBG_ASSERT(false == pcmplay->disabled); |
|---|
| 151 | |
|---|
| 152 | BDBG_MSG(("play start")); |
|---|
| 153 | |
|---|
| 154 | if ( pcmplay->output_settings.display ) |
|---|
| 155 | { |
|---|
| 156 | if ( display != pcmplay->output_settings.display ) |
|---|
| 157 | { |
|---|
| 158 | BDBG_ERR(("PCM Playback %p is bound to display %p. Cannot start on display %p", pcmplay, pcmplay->output_settings.display, display)); |
|---|
| 159 | return BSETTOP_ERROR(berr_invalid_parameter); |
|---|
| 160 | } |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | if ( pcmplay->display ) |
|---|
| 164 | { |
|---|
| 165 | BDBG_WRN(("Stopping existing playback")); |
|---|
| 166 | bpcm_play_stop(pcmplay); |
|---|
| 167 | } |
|---|
| 168 | |
|---|
| 169 | NEXUS_AudioPlayback_GetDefaultStartSettings(&pcmplay->nSettings); |
|---|
| 170 | pcmplay->nSettings.bitsPerSample = settings->pcm.bits_per_sample; |
|---|
| 171 | pcmplay->nSettings.sampleRate = settings->pcm.sample_rate; |
|---|
| 172 | pcmplay->nSettings.signedData = true; |
|---|
| 173 | pcmplay->nSettings.stereo = (settings->pcm.channels == 1)?false:true; |
|---|
| 174 | pcmplay->nSettings.startThreshold = 0; /* Start immediately, don't wait for data */ |
|---|
| 175 | pcmplay->callback = settings->callback; |
|---|
| 176 | pcmplay->context = settings->callback_context; |
|---|
| 177 | if ( pcmplay->callback ) |
|---|
| 178 | { |
|---|
| 179 | pcmplay->nSettings.dataCallback.callback = bpcm_play_p_callback; |
|---|
| 180 | pcmplay->nSettings.dataCallback.context = pcmplay; |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | if ( NULL == pcmplay->output_settings.display ) |
|---|
| 184 | { |
|---|
| 185 | /* Hook up to mixer */ |
|---|
| 186 | bdisplay_p_enable_audio(display, false); |
|---|
| 187 | errCode = NEXUS_AudioMixer_AddInput(display->nAudioMixer, |
|---|
| 188 | NEXUS_AudioPlayback_GetConnector(pcmplay->nPlayback)); |
|---|
| 189 | bdisplay_p_enable_audio(display, true); |
|---|
| 190 | if ( errCode ) |
|---|
| 191 | { |
|---|
| 192 | return BSETTOP_ERROR(berr_external_error); |
|---|
| 193 | } |
|---|
| 194 | } |
|---|
| 195 | |
|---|
| 196 | /* Start Playback */ |
|---|
| 197 | BDBG_MSG(("Starting Nexus Playback")); |
|---|
| 198 | errCode = NEXUS_AudioPlayback_Start(pcmplay->nPlayback, &pcmplay->nSettings); |
|---|
| 199 | if ( errCode ) |
|---|
| 200 | { |
|---|
| 201 | bdisplay_p_enable_audio(display, false); |
|---|
| 202 | NEXUS_AudioMixer_RemoveInput(display->nAudioMixer, |
|---|
| 203 | NEXUS_AudioPlayback_GetConnector(pcmplay->nPlayback)); |
|---|
| 204 | bdisplay_p_enable_audio(display, true); |
|---|
| 205 | return BSETTOP_ERROR(berr_external_error); |
|---|
| 206 | } |
|---|
| 207 | display->mixerInputsStarted++; |
|---|
| 208 | |
|---|
| 209 | /* Bind to display */ |
|---|
| 210 | pcmplay->display = display; |
|---|
| 211 | |
|---|
| 212 | /* Success */ |
|---|
| 213 | BDBG_MSG(("Playback Started")); |
|---|
| 214 | return b_ok; |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | bresult bpcm_play_get_status(bpcm_play_t pcmplay, bpcm_play_status *status) |
|---|
| 218 | { |
|---|
| 219 | NEXUS_AudioPlaybackStatus nStatus; |
|---|
| 220 | |
|---|
| 221 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 222 | BDBG_ASSERT(pcmplay->opened); |
|---|
| 223 | |
|---|
| 224 | BKNI_Memset(status, 0, sizeof(*status)); |
|---|
| 225 | NEXUS_AudioPlayback_GetStatus(pcmplay->nPlayback, &nStatus); |
|---|
| 226 | status->started = (NULL == pcmplay->display)?false:true; |
|---|
| 227 | status->fifo_size = nStatus.fifoSize; |
|---|
| 228 | if ( status->started ) |
|---|
| 229 | { |
|---|
| 230 | status->queued_bytes = nStatus.queuedBytes; |
|---|
| 231 | status->buffer_base = NULL; /* N/A */ |
|---|
| 232 | } |
|---|
| 233 | |
|---|
| 234 | return b_ok; |
|---|
| 235 | } |
|---|
| 236 | |
|---|
| 237 | bresult bpcm_play_get_buffer(bpcm_play_t pcmplay, void **data, size_t *length) |
|---|
| 238 | { |
|---|
| 239 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 240 | BDBG_ASSERT(pcmplay->opened); |
|---|
| 241 | BDBG_ASSERT(NULL != data); |
|---|
| 242 | BDBG_ASSERT(NULL != length); |
|---|
| 243 | |
|---|
| 244 | /*BDBG_MSG(("Get Buffer"));*/ |
|---|
| 245 | |
|---|
| 246 | if ( NULL == pcmplay->display ) |
|---|
| 247 | { |
|---|
| 248 | BDBG_WRN(("PCM Playback %p not started", pcmplay)); |
|---|
| 249 | return BSETTOP_ERROR(berr_invalid_parameter); |
|---|
| 250 | } |
|---|
| 251 | |
|---|
| 252 | if ( NEXUS_AudioPlayback_GetBuffer(pcmplay->nPlayback, data, length) ) |
|---|
| 253 | { |
|---|
| 254 | return BSETTOP_ERROR(berr_external_error); |
|---|
| 255 | } |
|---|
| 256 | |
|---|
| 257 | return b_ok; |
|---|
| 258 | } |
|---|
| 259 | |
|---|
| 260 | bresult bpcm_play_write_complete(bpcm_play_t pcmplay, size_t amount_written) |
|---|
| 261 | { |
|---|
| 262 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 263 | BDBG_ASSERT(pcmplay->opened); |
|---|
| 264 | |
|---|
| 265 | /*BDBG_MSG(("Write Complete"));*/ |
|---|
| 266 | |
|---|
| 267 | if ( NULL == pcmplay->display ) |
|---|
| 268 | { |
|---|
| 269 | BDBG_WRN(("PCM Playback %p not started", pcmplay)); |
|---|
| 270 | return BSETTOP_ERROR(berr_invalid_parameter); |
|---|
| 271 | } |
|---|
| 272 | |
|---|
| 273 | if ( NEXUS_AudioPlayback_ReadComplete(pcmplay->nPlayback, amount_written) ) |
|---|
| 274 | { |
|---|
| 275 | return BSETTOP_ERROR(berr_external_error); |
|---|
| 276 | } |
|---|
| 277 | |
|---|
| 278 | return b_ok; |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | bresult bpcm_play_stop(bpcm_play_t pcmplay) |
|---|
| 282 | { |
|---|
| 283 | bdisplay_t display; |
|---|
| 284 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 285 | BDBG_ASSERT(pcmplay->opened); |
|---|
| 286 | |
|---|
| 287 | BDBG_ASSERT(false == pcmplay->disabled); |
|---|
| 288 | |
|---|
| 289 | BDBG_MSG(("play stop")); |
|---|
| 290 | |
|---|
| 291 | display = pcmplay->display; |
|---|
| 292 | if ( NULL == display ) |
|---|
| 293 | { |
|---|
| 294 | BDBG_MSG(("Stopping an un-started playback")); |
|---|
| 295 | return b_ok; |
|---|
| 296 | } |
|---|
| 297 | |
|---|
| 298 | pcmplay->display = NULL; /* Must do this before disabling audio */ |
|---|
| 299 | NEXUS_AudioPlayback_Stop(pcmplay->nPlayback); |
|---|
| 300 | display->mixerInputsStarted--; |
|---|
| 301 | if ( NULL == pcmplay->output_settings.display ) |
|---|
| 302 | { |
|---|
| 303 | bdisplay_p_enable_audio(display, false); |
|---|
| 304 | NEXUS_AudioMixer_RemoveInput(display->nAudioMixer, |
|---|
| 305 | NEXUS_AudioPlayback_GetConnector(pcmplay->nPlayback)); |
|---|
| 306 | bdisplay_p_enable_audio(display, true); |
|---|
| 307 | } |
|---|
| 308 | |
|---|
| 309 | return b_ok; |
|---|
| 310 | } |
|---|
| 311 | |
|---|
| 312 | void bpcm_play_p_enable(bdisplay_t display, bool enabled) |
|---|
| 313 | { |
|---|
| 314 | int i; |
|---|
| 315 | |
|---|
| 316 | B_LOCK_ASSERT(); |
|---|
| 317 | BDBG_MSG(("%sabling pcm", enabled?"en":"dis")); |
|---|
| 318 | |
|---|
| 319 | for ( i = 0; i < B_N_PCM_PLAYS; i++ ) |
|---|
| 320 | { |
|---|
| 321 | bpcm_play_t pcmplay = &g_pcmplays[i]; |
|---|
| 322 | BDBG_MSG(("pcmplay %p open %d display %p check %p", pcmplay, pcmplay->opened, pcmplay->display, display)); |
|---|
| 323 | if ( pcmplay->opened && pcmplay->display == display ) |
|---|
| 324 | { |
|---|
| 325 | if ( enabled ) |
|---|
| 326 | { |
|---|
| 327 | pcmplay->disabled = false; |
|---|
| 328 | NEXUS_AudioPlayback_Start(pcmplay->nPlayback, &pcmplay->nSettings); |
|---|
| 329 | B_Event_Set(pcmplay->event); |
|---|
| 330 | } |
|---|
| 331 | else |
|---|
| 332 | { |
|---|
| 333 | pcmplay->disabled = true; |
|---|
| 334 | BDBG_MSG(("Forcing stop of PCM playback channel %p", pcmplay)); |
|---|
| 335 | NEXUS_AudioPlayback_Stop(pcmplay->nPlayback); |
|---|
| 336 | } |
|---|
| 337 | } |
|---|
| 338 | } |
|---|
| 339 | } |
|---|
| 340 | |
|---|
| 341 | static void bpcm_play_p_callback(void *pParam1, int param2) |
|---|
| 342 | { |
|---|
| 343 | bpcm_play_t pcmplay = pParam1; |
|---|
| 344 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 345 | BSTD_UNUSED(param2); |
|---|
| 346 | B_Event_Set(pcmplay->event); |
|---|
| 347 | } |
|---|
| 348 | |
|---|
| 349 | static void bpcm_play_p_callback_handler(void *context) |
|---|
| 350 | { |
|---|
| 351 | bpcm_play_t pcmplay = context; |
|---|
| 352 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 353 | |
|---|
| 354 | if ( pcmplay->callback ) |
|---|
| 355 | { |
|---|
| 356 | b_unlock(); |
|---|
| 357 | pcmplay->callback(pcmplay->context); |
|---|
| 358 | b_lock(); |
|---|
| 359 | } |
|---|
| 360 | } |
|---|
| 361 | |
|---|
| 362 | void bpcm_play_get_output_settings( |
|---|
| 363 | bpcm_play_t pcmplay, |
|---|
| 364 | bpcm_play_output_settings *settings /* [out] */ |
|---|
| 365 | ) |
|---|
| 366 | { |
|---|
| 367 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 368 | BDBG_ASSERT(NULL != settings); |
|---|
| 369 | *settings = pcmplay->output_settings; |
|---|
| 370 | } |
|---|
| 371 | |
|---|
| 372 | bresult bpcm_play_set_output_settings( |
|---|
| 373 | bpcm_play_t pcmplay, |
|---|
| 374 | const bpcm_play_output_settings *settings |
|---|
| 375 | ) |
|---|
| 376 | { |
|---|
| 377 | BDBG_OBJECT_ASSERT(pcmplay, bpcm_play); |
|---|
| 378 | BDBG_ASSERT(NULL != settings); |
|---|
| 379 | /* See if we're running */ |
|---|
| 380 | if ( pcmplay->display ) |
|---|
| 381 | { |
|---|
| 382 | return BSETTOP_ERROR(berr_busy); |
|---|
| 383 | } |
|---|
| 384 | /* If we're already attached to a display, make sure nothing else is using it. */ |
|---|
| 385 | if ( pcmplay->output_settings.display ) |
|---|
| 386 | { |
|---|
| 387 | if ( pcmplay->output_settings.display->mixerInputsStarted > 0 ) |
|---|
| 388 | { |
|---|
| 389 | return BSETTOP_ERROR(berr_busy); |
|---|
| 390 | } |
|---|
| 391 | /* Remove ourself from the current display */ |
|---|
| 392 | if ( NEXUS_AudioMixer_RemoveInput(pcmplay->output_settings.display->nAudioMixer, |
|---|
| 393 | NEXUS_AudioPlayback_GetConnector(pcmplay->nPlayback)) ) |
|---|
| 394 | { |
|---|
| 395 | return BSETTOP_ERROR(berr_external_error); |
|---|
| 396 | } |
|---|
| 397 | pcmplay->output_settings.display = NULL; |
|---|
| 398 | } |
|---|
| 399 | if ( settings->display ) |
|---|
| 400 | { |
|---|
| 401 | if ( settings->display->mixerInputsStarted > 0 ) |
|---|
| 402 | { |
|---|
| 403 | return BSETTOP_ERROR(berr_busy); |
|---|
| 404 | } |
|---|
| 405 | /* Add to the new display */ |
|---|
| 406 | if ( NEXUS_AudioMixer_AddInput(settings->display->nAudioMixer, |
|---|
| 407 | NEXUS_AudioPlayback_GetConnector(pcmplay->nPlayback)) ) |
|---|
| 408 | { |
|---|
| 409 | return BSETTOP_ERROR(berr_external_error); |
|---|
| 410 | } |
|---|
| 411 | } |
|---|
| 412 | /* Successful, save settings */ |
|---|
| 413 | pcmplay->output_settings = *settings; |
|---|
| 414 | return b_ok; |
|---|
| 415 | } |
|---|
| 416 | |
|---|