Steve Muckle | 0a9c087 | 2022-02-16 05:58:07 +0000 | [diff] [blame] | 1 | /******************************************************************************* |
| 2 | * Copyright (C) 2018 Cadence Design Systems, Inc. |
| 3 | * |
| 4 | * Permission is hereby granted, free of charge, to any person obtaining |
| 5 | * a copy of this software and associated documentation files (the |
| 6 | * "Software"), to use this Software with Cadence processor cores only and |
| 7 | * not with any other processors and platforms, subject to |
| 8 | * the following conditions: |
| 9 | * |
| 10 | * The above copyright notice and this permission notice shall be included |
| 11 | * in all copies or substantial portions of the Software. |
| 12 | * |
| 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 14 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 15 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| 16 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| 17 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| 18 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| 19 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 20 | |
| 21 | ******************************************************************************/ |
| 22 | |
| 23 | /******************************************************************************* |
| 24 | * xa-class-audio-codec.c |
| 25 | * |
| 26 | * Generic audio codec task implementation |
| 27 | * |
| 28 | ******************************************************************************/ |
| 29 | |
| 30 | #define MODULE_TAG CODEC |
| 31 | |
| 32 | /******************************************************************************* |
| 33 | * Includes |
| 34 | ******************************************************************************/ |
| 35 | |
| 36 | #include "xf.h" |
| 37 | #include "xa-class-base.h" |
| 38 | #include "audio/xa-audio-decoder-api.h" |
| 39 | |
| 40 | /******************************************************************************* |
| 41 | * Tracing configuration |
| 42 | ******************************************************************************/ |
| 43 | |
| 44 | TRACE_TAG(INIT, 1); |
| 45 | TRACE_TAG(WARNING, 1); |
| 46 | TRACE_TAG(INFO, 1); |
| 47 | TRACE_TAG(INPUT, 1); |
| 48 | TRACE_TAG(OUTPUT, 1); |
| 49 | TRACE_TAG(DECODE, 1); |
| 50 | |
| 51 | /******************************************************************************* |
| 52 | * Internal functions definitions |
| 53 | ******************************************************************************/ |
| 54 | |
| 55 | typedef struct XAAudioCodec |
| 56 | { |
| 57 | /*************************************************************************** |
| 58 | * Control data |
| 59 | **************************************************************************/ |
| 60 | |
| 61 | /* ...generic audio codec data */ |
| 62 | XACodecBase base; |
| 63 | |
| 64 | /* ...input port data */ |
| 65 | xf_input_port_t input; |
| 66 | |
| 67 | /* ...output port data */ |
| 68 | xf_output_port_t output; |
| 69 | |
| 70 | /* ...input port index */ |
| 71 | WORD32 in_idx; |
| 72 | |
| 73 | /* ...output port index */ |
| 74 | WORD32 out_idx; |
| 75 | |
| 76 | /*************************************************************************** |
| 77 | * Run-time configuration parameters |
| 78 | **************************************************************************/ |
| 79 | |
| 80 | /* ...sample size in bytes */ |
| 81 | u32 sample_size; |
| 82 | |
| 83 | /* ...audio sample duration */ |
| 84 | u32 factor; |
| 85 | |
| 86 | /* ...total number of produced audio frames since last reset */ |
| 87 | u32 produced; |
| 88 | |
| 89 | } XAAudioCodec; |
| 90 | |
| 91 | /******************************************************************************* |
| 92 | * Auxiliary codec execution flags |
| 93 | ******************************************************************************/ |
| 94 | |
| 95 | /* ...input port setup condition */ |
| 96 | #define XA_CODEC_FLAG_INPUT_SETUP __XA_BASE_FLAG(1 << 0) |
| 97 | |
| 98 | /* ...output port setup condition */ |
| 99 | #define XA_CODEC_FLAG_OUTPUT_SETUP __XA_BASE_FLAG(1 << 1) |
| 100 | |
| 101 | /******************************************************************************* |
| 102 | * Data processing scheduling |
| 103 | ******************************************************************************/ |
| 104 | |
| 105 | /* ...prepare codec for steady operation (tbd - don't absolutely like it) */ |
| 106 | static inline XA_ERRORCODE xa_codec_prepare_runtime(XAAudioCodec *codec) |
| 107 | { |
| 108 | XACodecBase *base = (XACodecBase *)codec; |
| 109 | xf_message_t *m = xf_msg_queue_head(&codec->output.queue); |
| 110 | xf_start_msg_t *msg = m->buffer; |
| 111 | u32 frame_size; |
| 112 | u32 factor; |
| 113 | |
| 114 | /* ...fill-in buffer parameters */ |
| 115 | XA_API(base, XA_API_CMD_GET_CONFIG_PARAM, XA_CODEC_CONFIG_PARAM_SAMPLE_RATE, &msg->sample_rate); |
| 116 | XA_API(base, XA_API_CMD_GET_CONFIG_PARAM, XA_CODEC_CONFIG_PARAM_CHANNELS, &msg->channels); |
| 117 | XA_API(base, XA_API_CMD_GET_CONFIG_PARAM, XA_CODEC_CONFIG_PARAM_PCM_WIDTH, &msg->pcm_width); |
| 118 | XA_API(base, XA_API_CMD_GET_MEM_INFO_SIZE, codec->in_idx, &msg->input_length); |
| 119 | XA_API(base, XA_API_CMD_GET_MEM_INFO_SIZE, codec->out_idx, &msg->output_length); |
| 120 | |
| 121 | TRACE(INIT, _b("codec[%p]::runtime init: f=%u, c=%u, w=%u, i=%u, o=%u"), codec, msg->sample_rate, msg->channels, msg->pcm_width, msg->input_length, msg->output_length); |
| 122 | |
| 123 | /* ...reallocate input port buffer as needed - tbd */ |
| 124 | BUG(msg->input_length > codec->input.length, _x("Input buffer reallocation required: %u to %u"), codec->input.length, msg->input_length); |
| 125 | |
| 126 | /* ...save sample size in bytes */ |
| 127 | codec->sample_size = msg->channels * (msg->pcm_width == 16 ? 2 : 4); |
| 128 | |
| 129 | /* ...calculate frame duration; get number of samples in the frame (don't like division here - tbd) */ |
| 130 | frame_size = msg->output_length / codec->sample_size; |
| 131 | |
| 132 | /* ...it must be a multiple */ |
| 133 | XF_CHK_ERR(frame_size * codec->sample_size == msg->output_length, XA_API_FATAL_INVALID_CMD_TYPE); |
| 134 | |
| 135 | /* ...retrieve upsampling factor for given sample rate */ |
| 136 | XF_CHK_ERR(factor = xf_timebase_factor(msg->sample_rate), XA_API_FATAL_INVALID_CMD_TYPE); |
| 137 | |
| 138 | /* ...set frame duration factor (converts number of bytes into timebase units) */ |
| 139 | codec->factor = factor / codec->sample_size; |
| 140 | |
| 141 | TRACE(INIT, _b("ts-factor: %u (%u)"), codec->factor, factor); |
| 142 | |
| 143 | BUG(codec->factor * codec->sample_size != factor, _x("Freq mismatch: %u vs %u"), codec->factor * codec->sample_size, factor); |
| 144 | |
| 145 | /* ...pass response to caller (push out of output port) */ |
| 146 | xf_output_port_produce(&codec->output, sizeof(*msg)); |
| 147 | |
| 148 | /* ...codec runtime initialization is completed */ |
| 149 | TRACE(INIT, _b("codec[%p] runtime initialized: i=%u, o=%u"), codec, msg->input_length, msg->output_length); |
| 150 | |
| 151 | return XA_NO_ERROR; |
| 152 | } |
| 153 | |
| 154 | /******************************************************************************* |
| 155 | * Commands processing |
| 156 | ******************************************************************************/ |
| 157 | |
| 158 | /* ...EMPTY-THIS-BUFFER command processing */ |
| 159 | static XA_ERRORCODE xa_codec_empty_this_buffer(XACodecBase *base, xf_message_t *m) |
| 160 | { |
| 161 | XAAudioCodec *codec = (XAAudioCodec *) base; |
| 162 | |
| 163 | /* ...make sure the port is valid */ |
| 164 | XF_CHK_ERR(XF_MSG_DST_PORT(m->id) == 0, XA_API_FATAL_INVALID_CMD); |
| 165 | |
| 166 | /* ...command is allowed only in post-init state */ |
| 167 | XF_CHK_ERR(base->state & XA_BASE_FLAG_POSTINIT, XA_API_FATAL_INVALID_CMD); |
| 168 | |
| 169 | /* ...put message into input queue */ |
| 170 | if (xf_input_port_put(&codec->input, m)) |
| 171 | { |
| 172 | /* ...restart stream if it is in completed state */ |
| 173 | if (base->state & XA_BASE_FLAG_COMPLETED) |
| 174 | { |
| 175 | /* ...reset execution stage */ |
| 176 | base->state = XA_BASE_FLAG_POSTINIT | XA_BASE_FLAG_EXECUTION; |
| 177 | |
| 178 | /* ...reset execution runtime */ |
| 179 | XA_API(base, XA_API_CMD_EXECUTE, XA_CMD_TYPE_DO_RUNTIME_INIT, NULL); |
| 180 | |
| 181 | /* ...reset produced samples counter */ |
| 182 | codec->produced = 0; |
| 183 | } |
| 184 | |
| 185 | /* ...codec must be in one of these states */ |
| 186 | XF_CHK_ERR(base->state & (XA_BASE_FLAG_RUNTIME_INIT | XA_BASE_FLAG_EXECUTION), XA_API_FATAL_INVALID_CMD); |
| 187 | |
| 188 | /* ...schedule data processing if output is ready */ |
| 189 | if (xf_output_port_ready(&codec->output)) |
| 190 | { |
| 191 | xa_base_schedule(base, 0); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | TRACE(INPUT, _b("Received buffer [%p]:%u"), m->buffer, m->length); |
| 196 | |
| 197 | return XA_NO_ERROR; |
| 198 | } |
| 199 | |
| 200 | /* ...FILL-THIS-BUFFER command processing */ |
| 201 | static XA_ERRORCODE xa_codec_fill_this_buffer(XACodecBase *base, xf_message_t *m) |
| 202 | { |
| 203 | XAAudioCodec *codec = (XAAudioCodec *) base; |
| 204 | |
| 205 | /* ...make sure the port is valid */ |
| 206 | XF_CHK_ERR(XF_MSG_DST_PORT(m->id) == 1, XA_API_FATAL_INVALID_CMD); |
| 207 | |
| 208 | /* ...command is allowed only in postinit state */ |
| 209 | XF_CHK_ERR(base->state & XA_BASE_FLAG_POSTINIT, XA_API_FATAL_INVALID_CMD); |
| 210 | |
| 211 | /* ...special handling of zero-length buffer */ |
| 212 | if (base->state & XA_BASE_FLAG_RUNTIME_INIT) |
| 213 | { |
| 214 | /* ...message must be zero-length */ |
| 215 | BUG(m->length != 0, _x("Invalid message length: %u"), m->length); |
| 216 | } |
| 217 | else if (m == xf_output_port_control_msg(&codec->output)) |
| 218 | { |
| 219 | /* ...end-of-stream processing indication received; check the state */ |
| 220 | BUG((base->state & XA_BASE_FLAG_COMPLETED) == 0, _x("invalid state: %x"), base->state); |
| 221 | |
| 222 | /* ... mark flushing sequence is done */ |
| 223 | xf_output_port_flush_done(&codec->output); |
| 224 | |
| 225 | /* ...complete pending zero-length input buffer */ |
| 226 | xf_input_port_purge(&codec->input); |
| 227 | |
| 228 | TRACE(INFO, _b("codec[%p] playback completed"), codec); |
| 229 | |
| 230 | /* ...playback is over */ |
| 231 | return XA_NO_ERROR; |
| 232 | } |
| 233 | else if ((base->state & XA_BASE_FLAG_COMPLETED) && !xf_output_port_routed(&codec->output)) |
| 234 | { |
| 235 | /* ...return message arrived from application immediately */ |
| 236 | xf_response_ok(m); |
| 237 | |
| 238 | return XA_NO_ERROR; |
| 239 | } |
| 240 | else |
| 241 | { |
| 242 | TRACE(OUTPUT, _b("Received output buffer [%p]:%u"), m->buffer, m->length); |
| 243 | |
| 244 | /* ...adjust message length (may be shorter than original) */ |
| 245 | m->length = codec->output.length; |
| 246 | } |
| 247 | |
| 248 | /* ...place message into output port */ |
| 249 | if (xf_output_port_put(&codec->output, m) && xf_input_port_ready(&codec->input)) |
| 250 | { |
| 251 | /* ...schedule data processing instantly */ |
| 252 | if (base->state & (XA_BASE_FLAG_RUNTIME_INIT | XA_BASE_FLAG_EXECUTION)) |
| 253 | { |
| 254 | xa_base_schedule(base, 0); |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | return XA_NO_ERROR; |
| 259 | } |
| 260 | |
| 261 | /* ...output port routing */ |
| 262 | static XA_ERRORCODE xa_codec_port_route(XACodecBase *base, xf_message_t *m) |
| 263 | { |
| 264 | XAAudioCodec *codec = (XAAudioCodec *) base; |
| 265 | xf_route_port_msg_t *cmd = m->buffer; |
| 266 | xf_output_port_t *port = &codec->output; |
| 267 | u32 src = XF_MSG_DST(m->id); |
| 268 | u32 dst = cmd->dst; |
| 269 | |
| 270 | /* ...command is allowed only in "postinit" state */ |
| 271 | XF_CHK_ERR(base->state & XA_BASE_FLAG_POSTINIT, XA_API_FATAL_INVALID_CMD); |
| 272 | |
| 273 | /* ...make sure output port is addressed */ |
| 274 | XF_CHK_ERR(XF_MSG_DST_PORT(m->id) == 1, XA_API_FATAL_INVALID_CMD_TYPE); |
| 275 | |
| 276 | /* ...make sure port is not routed yet */ |
| 277 | XF_CHK_ERR(!xf_output_port_routed(port), XA_API_FATAL_INVALID_CMD_TYPE); |
| 278 | |
| 279 | /* ...route output port - allocate queue */ |
| 280 | XF_CHK_ERR(xf_output_port_route(port, __XF_MSG_ID(dst, src), cmd->alloc_number, cmd->alloc_size, cmd->alloc_align) == 0, XA_API_FATAL_MEM_ALLOC); |
| 281 | |
| 282 | /* ...schedule processing instantly */ |
| 283 | xa_base_schedule(base, 0); |
| 284 | |
| 285 | /* ...pass success result to caller */ |
| 286 | xf_response_ok(m); |
| 287 | |
| 288 | return XA_NO_ERROR; |
| 289 | } |
| 290 | |
| 291 | /* ...port unroute command */ |
| 292 | static XA_ERRORCODE xa_codec_port_unroute(XACodecBase *base, xf_message_t *m) |
| 293 | { |
| 294 | XAAudioCodec *codec = (XAAudioCodec *) base; |
| 295 | |
| 296 | /* ...command is allowed only in "postinit" state */ |
| 297 | XF_CHK_ERR(base->state & XA_BASE_FLAG_POSTINIT, XA_API_FATAL_INVALID_CMD); |
| 298 | |
| 299 | /* ...make sure output port is addressed */ |
| 300 | XF_CHK_ERR(XF_MSG_DST_PORT(m->id) == 1, XA_API_FATAL_INVALID_CMD_TYPE); |
| 301 | |
| 302 | /* ...cancel any pending processing */ |
| 303 | xa_base_cancel(base); |
| 304 | |
| 305 | /* ...clear output-port-setup condition */ |
| 306 | base->state &= ~XA_CODEC_FLAG_OUTPUT_SETUP; |
| 307 | |
| 308 | /* ...pass flush command down the graph */ |
| 309 | if (xf_output_port_flush(&codec->output, XF_FLUSH)) |
| 310 | { |
| 311 | TRACE(INFO, _b("port is idle; instantly unroute")); |
| 312 | |
| 313 | /* ...flushing sequence is not needed; command may be satisfied instantly */ |
| 314 | xf_output_port_unroute(&codec->output); |
| 315 | |
| 316 | /* ...pass response to the proxy */ |
| 317 | xf_response_ok(m); |
| 318 | } |
| 319 | else |
| 320 | { |
| 321 | TRACE(INFO, _b("port is busy; propagate unroute command")); |
| 322 | |
| 323 | /* ...flushing sequence is started; save flow-control message */ |
| 324 | xf_output_port_unroute_start(&codec->output, m); |
| 325 | } |
| 326 | |
| 327 | return XA_NO_ERROR; |
| 328 | } |
| 329 | |
| 330 | /* ...FLUSH command processing */ |
| 331 | static XA_ERRORCODE xa_codec_flush(XACodecBase *base, xf_message_t *m) |
| 332 | { |
| 333 | XAAudioCodec *codec = (XAAudioCodec *) base; |
| 334 | |
| 335 | /* ...command is allowed only in "postinit" state */ |
| 336 | XF_CHK_ERR(base->state & XA_BASE_FLAG_POSTINIT, XA_API_FATAL_INVALID_CMD); |
| 337 | |
| 338 | /* ...ensure input parameter length is zero */ |
| 339 | XF_CHK_ERR(m->length == 0, XA_API_FATAL_INVALID_CMD_TYPE); |
| 340 | |
| 341 | TRACE(1, _b("flush command received")); |
| 342 | |
| 343 | /* ...flush command must be addressed to input port */ |
| 344 | if (XF_MSG_DST_PORT(m->id) == 0) |
| 345 | { |
| 346 | /* ...cancel data processing message if needed */ |
| 347 | xa_base_cancel(base); |
| 348 | |
| 349 | /* ...input port flushing; purge content of input buffer */ |
| 350 | xf_input_port_purge(&codec->input); |
| 351 | |
| 352 | /* ...clear input-ready condition */ |
| 353 | base->state &= ~XA_CODEC_FLAG_INPUT_SETUP; |
| 354 | |
| 355 | /* ...reset execution runtime */ |
| 356 | XA_API(base, XA_API_CMD_EXECUTE, XA_CMD_TYPE_DO_RUNTIME_INIT, NULL); |
| 357 | |
| 358 | /* ...reset produced samples counter */ |
| 359 | codec->produced = 0; |
| 360 | |
| 361 | /* ...propagate flushing command to output port */ |
| 362 | if (xf_output_port_flush(&codec->output, XF_FLUSH)) |
| 363 | { |
| 364 | /* ...flushing sequence is not needed; satisfy command instantly */ |
| 365 | xf_response(m); |
| 366 | } |
| 367 | else |
| 368 | { |
| 369 | /* ...flushing sequence is started; save flow-control message at input port */ |
| 370 | xf_input_port_control_save(&codec->input, m); |
| 371 | } |
| 372 | } |
| 373 | else if (xf_output_port_unrouting(&codec->output)) |
| 374 | { |
| 375 | /* ...flushing during port unrouting; complete unroute sequence */ |
| 376 | xf_output_port_unroute_done(&codec->output); |
| 377 | |
| 378 | TRACE(INFO, _b("port is unrouted")); |
| 379 | } |
| 380 | else |
| 381 | { |
| 382 | /* ...output port flush command/response; check if the port is routed */ |
| 383 | if (!xf_output_port_routed(&codec->output)) |
| 384 | { |
| 385 | /* ...complete all queued messages */ |
| 386 | xf_output_port_flush(&codec->output, XF_FLUSH); |
| 387 | |
| 388 | /* ...and pass response to flushing command */ |
| 389 | xf_response(m); |
| 390 | } |
| 391 | else |
| 392 | { |
| 393 | /* ...response to flushing command received */ |
| 394 | BUG(m != xf_output_port_control_msg(&codec->output), _x("invalid message: %p"), m); |
| 395 | |
| 396 | /* ...mark flushing sequence is completed */ |
| 397 | xf_output_port_flush_done(&codec->output); |
| 398 | |
| 399 | /* ...complete original flow-control command */ |
| 400 | xf_input_port_purge_done(&codec->input); |
| 401 | } |
| 402 | |
| 403 | /* ...clear output-setup condition */ |
| 404 | base->state &= ~XA_CODEC_FLAG_OUTPUT_SETUP; |
| 405 | } |
| 406 | |
| 407 | return XA_NO_ERROR; |
| 408 | } |
| 409 | |
| 410 | /******************************************************************************* |
| 411 | * Generic codec API |
| 412 | ******************************************************************************/ |
| 413 | |
| 414 | /* ...memory buffer handling */ |
| 415 | static XA_ERRORCODE xa_codec_memtab(XACodecBase *base, WORD32 idx, WORD32 type, WORD32 size, WORD32 align, u32 core) |
| 416 | { |
| 417 | XAAudioCodec *codec = (XAAudioCodec *) base; |
| 418 | |
| 419 | if (type == XA_MEMTYPE_INPUT) |
| 420 | { |
| 421 | /* ...input port specification; allocate internal buffer */ |
| 422 | XF_CHK_ERR(xf_input_port_init(&codec->input, size, align, core) == 0, XA_API_FATAL_MEM_ALLOC); |
| 423 | |
| 424 | /* ...save input port index */ |
| 425 | codec->in_idx = idx; |
| 426 | |
| 427 | /* ...set input buffer pointer as needed */ |
| 428 | (size ? XA_API(base, XA_API_CMD_SET_MEM_PTR, idx, codec->input.buffer) : 0); |
| 429 | |
| 430 | (size ? TRACE(1, _x("set input ptr: %p"), codec->input.buffer) : 0); |
| 431 | } |
| 432 | else |
| 433 | { |
| 434 | /* ...output buffer specification */ |
| 435 | XF_CHK_ERR(type == XA_MEMTYPE_OUTPUT, XA_API_FATAL_INVALID_CMD_TYPE); |
| 436 | |
| 437 | /* ...initialize output port queue (no allocation here yet) */ |
| 438 | XF_CHK_ERR(xf_output_port_init(&codec->output, size) == 0, XA_API_FATAL_MEM_ALLOC); |
| 439 | |
| 440 | /* ...save output port index */ |
| 441 | codec->out_idx = idx; |
| 442 | } |
| 443 | |
| 444 | return XA_NO_ERROR; |
| 445 | } |
| 446 | |
| 447 | /* ...prepare input/output buffers */ |
| 448 | static XA_ERRORCODE xa_codec_preprocess(XACodecBase *base) |
| 449 | { |
| 450 | XAAudioCodec *codec = (XAAudioCodec *) base; |
| 451 | |
| 452 | /* ...prepare output buffer if needed */ |
| 453 | if (!(base->state & XA_CODEC_FLAG_OUTPUT_SETUP)) |
| 454 | { |
| 455 | void *output; |
| 456 | |
| 457 | /* ...get output buffer from port, if possible */ |
| 458 | if (base->state & XA_BASE_FLAG_RUNTIME_INIT) |
| 459 | { |
| 460 | /* ...run-time is not initialized yet; use scratch buffer */ |
| 461 | output = base->scratch; |
| 462 | } |
| 463 | else if ((output = xf_output_port_data(&codec->output)) == NULL) |
| 464 | { |
| 465 | /* ...no output buffer available */ |
| 466 | return XA_CODEC_EXEC_NO_DATA; |
| 467 | } |
| 468 | |
| 469 | /* ...set the output buffer pointer */ |
| 470 | XA_API(base, XA_API_CMD_SET_MEM_PTR, codec->out_idx, output); |
| 471 | |
| 472 | TRACE(1, _x("set output ptr: %p"), output); |
| 473 | |
| 474 | /* ...mark output port is setup */ |
| 475 | base->state ^= XA_CODEC_FLAG_OUTPUT_SETUP; |
| 476 | } |
| 477 | |
| 478 | /* ...prepare input data if needed */ |
| 479 | if (!(base->state & XA_CODEC_FLAG_INPUT_SETUP)) |
| 480 | { |
| 481 | void *input; |
| 482 | u32 filled; |
| 483 | |
| 484 | /* ...fill input buffer */ |
| 485 | if (xf_input_port_bypass(&codec->input)) |
| 486 | { |
| 487 | /* ...use input buffer directly; check if there is data available */ |
| 488 | if ((input = xf_input_port_data(&codec->input)) != NULL) |
| 489 | { |
| 490 | /* ...set input data buffer pointer */ |
| 491 | XA_API(base, XA_API_CMD_SET_MEM_PTR, codec->in_idx, input); |
| 492 | |
| 493 | /* ...retrieve number of input bytes */ |
| 494 | filled = xf_input_port_length(&codec->input); |
| 495 | } |
| 496 | else if (!xf_input_port_done(&codec->input)) |
| 497 | { |
| 498 | /* ...return non-fatal indication to prevent further processing */ |
| 499 | return XA_CODEC_EXEC_NO_DATA; |
| 500 | } |
| 501 | else |
| 502 | { |
| 503 | /* ...mark we have no data in current buffer */ |
| 504 | filled = 0; |
| 505 | } |
| 506 | } |
| 507 | else |
| 508 | { |
| 509 | /* ...port is in non-bypass mode; try to fill internal buffer */ |
| 510 | if (xf_input_port_done(&codec->input) || xf_input_port_fill(&codec->input)) |
| 511 | { |
| 512 | /* ...retrieve number of bytes in input buffer (not really - tbd) */ |
| 513 | filled = xf_input_port_level(&codec->input); |
| 514 | } |
| 515 | else |
| 516 | { |
| 517 | /* ...return non-fatal indication to prevent further processing */ |
| 518 | return XA_CODEC_EXEC_NO_DATA; |
| 519 | } |
| 520 | } |
| 521 | |
| 522 | /* ...check if input stream is over */ |
| 523 | if (xf_input_port_done(&codec->input)) |
| 524 | { |
| 525 | /* ...pass input-over command to the codec to indicate the final buffer */ |
| 526 | XA_API(base, XA_API_CMD_INPUT_OVER, codec->in_idx, NULL); |
| 527 | |
| 528 | TRACE(INFO, _b("codec[%p]: signal input-over (filled: %u)"), codec, filled); |
| 529 | } |
| 530 | |
| 531 | TRACE(INPUT, _b("input-buffer fill-level: %u bytes"), filled); |
| 532 | |
| 533 | /* ...specify number of bytes available in the input buffer */ |
| 534 | XA_API(base, XA_API_CMD_SET_INPUT_BYTES, codec->in_idx, &filled); |
| 535 | |
| 536 | /* ...mark input port is setup */ |
| 537 | base->state ^= XA_CODEC_FLAG_INPUT_SETUP; |
| 538 | } |
| 539 | |
| 540 | return XA_NO_ERROR; |
| 541 | } |
| 542 | |
| 543 | /* ...post-processing operation; input/output ports maintenance */ |
| 544 | static XA_ERRORCODE xa_codec_postprocess(XACodecBase *base, int done) |
| 545 | { |
| 546 | XAAudioCodec *codec = (XAAudioCodec *) base; |
| 547 | WORD32 consumed = 0; |
| 548 | WORD32 produced = 0; |
| 549 | |
| 550 | /* ...get number of consumed / produced bytes */ |
| 551 | XA_API(base, XA_API_CMD_GET_CURIDX_INPUT_BUF, codec->in_idx, &consumed); |
| 552 | |
| 553 | /* ...get number of produced bytes only if runtime is initialized (sample size is known) */ |
| 554 | (codec->sample_size ? XA_API(base, XA_API_CMD_GET_OUTPUT_BYTES, codec->out_idx, &produced) : 0); |
| 555 | |
| 556 | TRACE(DECODE, _b("codec[%p]::postprocess(c=%u, p=%u, d=%u)"), codec, consumed, produced, done); |
| 557 | |
| 558 | /* ...input buffer maintenance; check if we consumed anything */ |
| 559 | if (consumed) |
| 560 | { |
| 561 | /* ...consume specified number of bytes from input port */ |
| 562 | xf_input_port_consume(&codec->input, consumed); |
| 563 | |
| 564 | /* ...clear input-setup flag */ |
| 565 | base->state ^= XA_CODEC_FLAG_INPUT_SETUP; |
| 566 | } |
| 567 | |
| 568 | /* ...output buffer maintenance; check if we have produced anything */ |
| 569 | if (produced) |
| 570 | { |
| 571 | /* ...increment total number of produced samples (really don't like division here - tbd) */ |
| 572 | codec->produced += produced / codec->sample_size; |
| 573 | |
| 574 | /* ...immediately complete output buffer (don't wait until it gets filled) */ |
| 575 | xf_output_port_produce(&codec->output, produced); |
| 576 | |
| 577 | /* ...clear output port setup flag */ |
| 578 | base->state ^= XA_CODEC_FLAG_OUTPUT_SETUP; |
| 579 | } |
| 580 | |
| 581 | /* ...process execution stage transition */ |
| 582 | if (done) |
| 583 | { |
| 584 | if (base->state & XA_BASE_FLAG_RUNTIME_INIT) |
| 585 | { |
| 586 | /* ...stream is completed while codec is in runtime initialization stage */ |
| 587 | BUG(1, _x("breakpoint")); |
| 588 | } |
| 589 | else if (base->state & XA_BASE_FLAG_EXECUTION) |
| 590 | { |
| 591 | /* ...runtime initialization done */ |
| 592 | XA_CHK(xa_codec_prepare_runtime(codec)); |
| 593 | |
| 594 | /* ...clear output port setup flag as we were using scratch buffer; |
| 595 | * technically, no need to repeat setup of input buffer, but some codecs require |
| 596 | * it as well |
| 597 | */ |
| 598 | base->state &= ~(XA_CODEC_FLAG_INPUT_SETUP | XA_CODEC_FLAG_OUTPUT_SETUP); |
| 599 | } |
| 600 | else |
| 601 | { |
| 602 | /* ...output stream is over; propagate condition to sink port */ |
| 603 | if (xf_output_port_flush(&codec->output, XF_FILL_THIS_BUFFER)) |
| 604 | { |
| 605 | /* ...flushing sequence is not needed; complete pending zero-length input */ |
| 606 | xf_input_port_purge(&codec->input); |
| 607 | |
| 608 | /* ...no propagation to output port */ |
| 609 | TRACE(INFO, _b("codec[%p] playback completed"), codec); |
| 610 | } |
| 611 | else |
| 612 | { |
| 613 | /* ...flushing sequence is started; wait until flow-control message returns */ |
| 614 | TRACE(INFO, _b("propagate end-of-stream condition")); |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | /* ...return early to prevent task rescheduling */ |
| 619 | return XA_NO_ERROR; |
| 620 | } |
| 621 | |
| 622 | /* ...reschedule processing if needed */ |
| 623 | if (xf_input_port_ready(&codec->input) && xf_output_port_ready(&codec->output)) |
| 624 | { |
| 625 | /* ...schedule data processing with respect to its urgency */ |
| 626 | xa_base_schedule(base, produced * codec->factor); |
| 627 | } |
| 628 | |
| 629 | return XA_NO_ERROR; |
| 630 | } |
| 631 | |
| 632 | /* ...configuration parameter retrieval */ |
| 633 | static XA_ERRORCODE xa_codec_getparam(XACodecBase *base, WORD32 id, pVOID value) |
| 634 | { |
| 635 | XAAudioCodec *codec = (XAAudioCodec *) base; |
| 636 | |
| 637 | if (id == XA_CODEC_CONFIG_PARAM_PRODUCED) |
| 638 | { |
| 639 | /* ...retrieve number of produced samples since last reset */ |
| 640 | *(u32 *)value = codec->produced; |
| 641 | |
| 642 | return XA_NO_ERROR; |
| 643 | } |
| 644 | else |
| 645 | { |
| 646 | /* ...pass command to underlying codec plugin */ |
| 647 | return XA_API(base, XA_API_CMD_GET_CONFIG_PARAM, id, value); |
| 648 | } |
| 649 | } |
| 650 | |
| 651 | /******************************************************************************* |
| 652 | * Component entry point |
| 653 | ******************************************************************************/ |
| 654 | |
| 655 | /* ...command hooks */ |
| 656 | static XA_ERRORCODE (* const xa_codec_cmd[])(XACodecBase *, xf_message_t *) = |
| 657 | { |
| 658 | [XF_OPCODE_TYPE(XF_SET_PARAM)] = xa_base_set_param, |
| 659 | [XF_OPCODE_TYPE(XF_GET_PARAM)] = xa_base_get_param, |
| 660 | [XF_OPCODE_TYPE(XF_ROUTE)] = xa_codec_port_route, |
| 661 | [XF_OPCODE_TYPE(XF_UNROUTE)] = xa_codec_port_unroute, |
| 662 | [XF_OPCODE_TYPE(XF_EMPTY_THIS_BUFFER)] = xa_codec_empty_this_buffer, |
| 663 | [XF_OPCODE_TYPE(XF_FILL_THIS_BUFFER)] = xa_codec_fill_this_buffer, |
| 664 | [XF_OPCODE_TYPE(XF_FLUSH)] = xa_codec_flush, |
| 665 | [XF_OPCODE_TYPE(XF_SET_PARAM_EXT)] = xa_base_set_param_ext, |
| 666 | [XF_OPCODE_TYPE(XF_GET_PARAM_EXT)] = xa_base_get_param_ext, |
| 667 | }; |
| 668 | |
| 669 | /* ...total number of commands supported */ |
| 670 | #define XA_CODEC_CMD_NUM (sizeof(xa_codec_cmd) / sizeof(xa_codec_cmd[0])) |
| 671 | |
| 672 | /* ...command processor for termination state (only for routed port case) */ |
| 673 | static int xa_audio_codec_terminate(xf_component_t *component, xf_message_t *m) |
| 674 | { |
| 675 | XAAudioCodec *codec = (XAAudioCodec *) component; |
| 676 | u32 opcode = m->opcode; |
| 677 | |
| 678 | /* ...check if we received output port control message */ |
| 679 | if (m == xf_output_port_control_msg(&codec->output)) |
| 680 | { |
| 681 | /* ...output port flushing complete; mark port is idle and terminate */ |
| 682 | xf_output_port_flush_done(&codec->output); |
| 683 | return -1; |
| 684 | } |
| 685 | else if (opcode == XF_FILL_THIS_BUFFER) |
| 686 | { |
| 687 | /* ...output buffer returned by the sink component; ignore and keep waiting */ |
| 688 | TRACE(OUTPUT, _b("collect output buffer")); |
| 689 | return 0; |
| 690 | } |
| 691 | else if (opcode == XF_UNREGISTER) |
| 692 | { |
| 693 | /* ...ignore subsequent unregister command/response - tbd */ |
| 694 | return 0; |
| 695 | } |
| 696 | else |
| 697 | { |
| 698 | /* ...everything else is responded with generic failure */ |
| 699 | xf_response_err(m); |
| 700 | return 0; |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | /* ...audio codec destructor */ |
| 705 | static int xa_audio_codec_destroy(xf_component_t *component, xf_message_t *m) |
| 706 | { |
| 707 | XAAudioCodec *codec = (XAAudioCodec *) component; |
| 708 | u32 core = xf_component_core(component); |
| 709 | |
| 710 | /* ...destroy input port */ |
| 711 | xf_input_port_destroy(&codec->input, core); |
| 712 | |
| 713 | /* ...destroy output port */ |
| 714 | xf_output_port_destroy(&codec->output, core); |
| 715 | |
| 716 | /* ...deallocate all resources */ |
| 717 | xa_base_destroy(&codec->base, XF_MM(sizeof(*codec)), core); |
| 718 | |
| 719 | TRACE(INIT, _b("audio-codec[%p@%u] destroyed"), codec, core); |
| 720 | |
| 721 | /* ...indicate the client has been destroyed */ |
| 722 | return 0; |
| 723 | } |
| 724 | |
| 725 | /* ...audio codec destructor - first stage (ports unrouting) */ |
| 726 | static int xa_audio_codec_cleanup(xf_component_t *component, xf_message_t *m) |
| 727 | { |
| 728 | XAAudioCodec *codec = (XAAudioCodec *) component; |
| 729 | |
| 730 | /* ...complete message with error response */ |
| 731 | xf_response_err(m); |
| 732 | |
| 733 | /* ...cancel internal scheduling message if needed */ |
| 734 | xa_base_cancel(&codec->base); |
| 735 | |
| 736 | /* ...purge input port (returns OK? pretty strange at this point - tbd) */ |
| 737 | xf_input_port_purge(&codec->input); |
| 738 | |
| 739 | /* ...propagate unregister command to connected component */ |
| 740 | if (xf_output_port_flush(&codec->output, XF_FLUSH)) |
| 741 | { |
| 742 | /* ...flushing sequence is not needed; destroy audio codec */ |
| 743 | return xa_audio_codec_destroy(component, NULL); |
| 744 | } |
| 745 | else |
| 746 | { |
| 747 | /* ...wait until output port is cleaned; adjust component hooks */ |
| 748 | component->entry = xa_audio_codec_terminate; |
| 749 | component->exit = xa_audio_codec_destroy; |
| 750 | |
| 751 | TRACE(INIT, _b("codec[%p] cleanup sequence started"), codec); |
| 752 | |
| 753 | /* ...indicate that second stage is required */ |
| 754 | return 1; |
| 755 | } |
| 756 | } |
| 757 | |
| 758 | /******************************************************************************* |
| 759 | * Audio codec component factory |
| 760 | ******************************************************************************/ |
| 761 | |
| 762 | xf_component_t * xa_audio_codec_factory(u32 core, xa_codec_func_t process) |
| 763 | { |
| 764 | XAAudioCodec *codec; |
| 765 | |
| 766 | /* ...allocate local memory for codec structure */ |
| 767 | XF_CHK_ERR(codec = (XAAudioCodec *) xa_base_factory(core, XF_MM(sizeof(*codec)), process), NULL); |
| 768 | |
| 769 | /* ...set base codec API methods */ |
| 770 | codec->base.memtab = xa_codec_memtab; |
| 771 | codec->base.preprocess = xa_codec_preprocess; |
| 772 | codec->base.postprocess = xa_codec_postprocess; |
| 773 | codec->base.getparam = xa_codec_getparam; |
| 774 | |
| 775 | /* ...set message commands processing table */ |
| 776 | codec->base.command = xa_codec_cmd; |
| 777 | codec->base.command_num = XA_CODEC_CMD_NUM; |
| 778 | |
| 779 | /* ...set component destructor hook */ |
| 780 | codec->base.component.exit = xa_audio_codec_cleanup; |
| 781 | |
| 782 | TRACE(INIT, _b("Codec[%p] initialized"), codec); |
| 783 | |
| 784 | return (xf_component_t *) codec; |
| 785 | } |