/******************************************************************************* * Copyright (C) 2018 Cadence Design Systems, Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to use this Software with Cadence processor cores only and * not with any other processors and platforms, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ /******************************************************************************* * xa-class-mixer.c * * Generic mixer component class * ******************************************************************************/ #define MODULE_TAG MIXER /******************************************************************************* * Includes ******************************************************************************/ #include "xf.h" #include "xa-class-base.h" #include "audio/xa-mixer-api.h" /******************************************************************************* * Tracing tags ******************************************************************************/ TRACE_TAG(INIT, 1); TRACE_TAG(WARNING, 1); TRACE_TAG(INFO, 1); TRACE_TAG(INPUT, 1); TRACE_TAG(OUTPUT, 1); /******************************************************************************* * Data structures ******************************************************************************/ /* ...mixed source - input data */ typedef struct XATrack { /* ...input port data */ xf_input_port_t input; /* ...current presentation timestamp (in samples; local to a mixer state) */ u32 pts; /* ...total amount of decoded frames since last synchronization point */ u32 decoded; /* ...total amount of rendered frames (consumed) since last synchronization point */ u32 rendered; } XATrack; /******************************************************************************* * Helpers ******************************************************************************/ static inline u32 xa_track_test_flags(XATrack *track, u32 flags) { return (track->input.flags & flags); } static inline u32 xa_track_set_flags(XATrack *track, u32 flags) { return (track->input.flags |= flags); } static inline u32 xa_track_clear_flags(XATrack *track, u32 flags) { return (track->input.flags &= ~flags); } static inline u32 xa_track_toggle_flags(XATrack *track, u32 flags) { return (track->input.flags ^= flags); } /******************************************************************************* * Mixer data definitions ******************************************************************************/ /* ...mixer data */ typedef struct XAMixer { /*************************************************************************** * Control data **************************************************************************/ /* ...generic audio codec data */ XACodecBase base; /* ...input tracks */ XATrack track[XA_MIXER_MAX_TRACK_NUMBER]; /* ...output port */ xf_output_port_t output; /*************************************************************************** * Run-time configuration parameters **************************************************************************/ /* ...audio frame size in samples */ u32 frame_size; /* ...audio frame duration */ u32 frame_duration; /* ...presentation timestamp (in samples; local mixer scope) */ u32 pts; } XAMixer; /******************************************************************************* * Mixer flags ******************************************************************************/ /* ...output port setup completed */ #define XA_MIXER_FLAG_OUTPUT_SETUP __XA_BASE_FLAG(1 << 0) /******************************************************************************* * Track state flags ******************************************************************************/ /* ...track is idle (will autostart as soon as input data received) */ #define XA_TRACK_FLAG_IDLE __XF_INPUT_FLAG(1 << 0) /* ...track is rendered */ #define XA_TRACK_FLAG_ACTIVE __XF_INPUT_FLAG(1 << 1) /* ...track is paused */ #define XA_TRACK_FLAG_PAUSED __XF_INPUT_FLAG(1 << 2) /* ...track input port is setup */ #define XA_TRACK_FLAG_INPUT_SETUP __XF_INPUT_FLAG(1 << 3) /* ...track has received data */ #define XA_TRACK_FLAG_RECVD_DATA __XF_INPUT_FLAG(1 << 4) /******************************************************************************* * Helper functions ******************************************************************************/ /* ...Count the tracks that have received data or are active*/ static inline UWORD32 xa_mixer_check_active(XAMixer *mixer) { XATrack *track; UWORD32 i; UWORD32 cnt = 0; for (track = &mixer->track[i = 0]; i < XA_MIXER_MAX_TRACK_NUMBER; i++, track++) { if (xa_track_test_flags(track, XA_TRACK_FLAG_RECVD_DATA | XA_TRACK_FLAG_ACTIVE)) cnt++; } return cnt; } /* ...prepare mixer for steady operation */ static inline XA_ERRORCODE xa_mixer_prepare_runtime(XAMixer *mixer) { XACodecBase *base = (XACodecBase *) mixer; xf_message_t *m = xf_msg_dequeue(&mixer->output.queue); xf_start_msg_t *msg = m->buffer; u32 frame_size; u32 factor; /* ...query mixer parameters */ XA_API(base, XA_API_CMD_GET_CONFIG_PARAM, XA_MIXER_CONFIG_PARAM_SAMPLE_RATE, &msg->sample_rate); XA_API(base, XA_API_CMD_GET_CONFIG_PARAM, XA_MIXER_CONFIG_PARAM_CHANNELS, &msg->channels); XA_API(base, XA_API_CMD_GET_CONFIG_PARAM, XA_MIXER_CONFIG_PARAM_PCM_WIDTH, &msg->pcm_width); XA_API(base, XA_API_CMD_GET_MEM_INFO_SIZE, 0, &msg->input_length); XA_API(base, XA_API_CMD_GET_MEM_INFO_SIZE, XA_MIXER_MAX_TRACK_NUMBER, &msg->output_length); XA_API(base, XA_API_CMD_GET_CONFIG_PARAM, XA_MIXER_CONFIG_PARAM_FRAME_SIZE, &frame_size); /* ...calculate mixer frame duration; get upsample factor */ XF_CHK_ERR(factor = xf_timebase_factor(msg->sample_rate), XA_MIXER_CONFIG_FATAL_RANGE); /* ...set mixer frame duration */ mixer->frame_duration = frame_size * factor; /* ...pass response to caller */ xf_response_data(m, sizeof(*msg)); return XA_NO_ERROR; } /******************************************************************************* * Commands handlers ******************************************************************************/ /* ...EMPTY-THIS-BUFFER command processing */ static XA_ERRORCODE xa_mixer_empty_this_buffer(XACodecBase *base, xf_message_t *m) { XAMixer *mixer = (XAMixer *) base; u32 i = XF_MSG_DST_PORT(m->id); XATrack *track = &mixer->track[i]; /* ...make sure the port is valid */ XF_CHK_ERR(i < XA_MIXER_MAX_TRACK_NUMBER, XA_API_FATAL_INVALID_CMD_TYPE); /* ...command is allowed only in "postinit" state */ XF_CHK_ERR(base->state & XA_BASE_FLAG_POSTINIT, XA_API_FATAL_INVALID_CMD); TRACE(INPUT, _b("track-%u: received buffer [%p]:%u"), i, m->buffer, m->length); /* ...update received data for the track */ if (m->length) xa_track_set_flags(track, XA_TRACK_FLAG_RECVD_DATA); else xa_track_clear_flags(track, XA_TRACK_FLAG_RECVD_DATA); /* ...place received message into track input port */ if (xf_input_port_put(&track->input, m)) { /* ...process track autostart if needed */ if (xa_track_test_flags(track, XA_TRACK_FLAG_IDLE)) { /* ...put track into active state */ xa_track_toggle_flags(track, XA_TRACK_FLAG_IDLE | XA_TRACK_FLAG_ACTIVE); /* ...save track presentation timestamp */ track->pts = mixer->pts; TRACE(INFO, _b("track-%u started (pts=%x)"), i, track->pts); } /* ...schedule data processing if there is output port available */ if (xf_output_port_ready(&mixer->output)) { /* ...force data processing */ xa_base_schedule(base, 0); } } return XA_NO_ERROR; } /* ...FILL-THIS-BUFFER command processing */ static XA_ERRORCODE xa_mixer_fill_this_buffer(XACodecBase *base, xf_message_t *m) { XAMixer *mixer = (XAMixer *) base; u32 i = XF_MSG_DST_PORT(m->id); /* ...make sure the port is valid */ XF_CHK_ERR(i == XA_MIXER_MAX_TRACK_NUMBER, XA_API_FATAL_INVALID_CMD_TYPE); /* ...command is allowed only in "postinit" state */ XF_CHK_ERR(base->state & XA_BASE_FLAG_POSTINIT, XA_API_FATAL_INVALID_CMD); /* ...process runtime initialization explicitly */ if (base->state & XA_BASE_FLAG_RUNTIME_INIT) { /* ...message must be zero-length */ XF_CHK_ERR(m->length == 0, XA_MIXER_EXEC_FATAL_STATE); } else if (m->length != 0) /* ...EOS response */ { /* ...message must have exactly expected size (there is no ordered abortion) */ XF_CHK_ERR(m->length == mixer->output.length, XA_MIXER_EXEC_FATAL_STATE); } TRACE(OUTPUT, _b("received output buffer [%p]:%u"), m->buffer, m->length); /* ...put message into output port */ if (xf_output_port_put(&mixer->output, m)) { /* ...force data processing */ xa_base_schedule(base, 0); } return XA_NO_ERROR; } /* ...output port routing */ static XA_ERRORCODE xa_mixer_port_route(XACodecBase *base, xf_message_t *m) { XAMixer *mixer = (XAMixer *) base; xf_route_port_msg_t *cmd = m->buffer; xf_output_port_t *port = &mixer->output; u32 src = XF_MSG_DST(m->id); u32 dst = cmd->dst; /* ...command is allowed only in "postinit" state */ XF_CHK_ERR(base->state & XA_BASE_FLAG_POSTINIT, XA_API_FATAL_INVALID_CMD); /* ...make sure output port is addressed */ XF_CHK_ERR(XF_MSG_DST_PORT(m->id) == XA_MIXER_MAX_TRACK_NUMBER, XA_API_FATAL_INVALID_CMD_TYPE); /* ...make sure port is not routed yet */ XF_CHK_ERR(!xf_output_port_routed(port), XA_API_FATAL_INVALID_CMD_TYPE); /* ...route output port - allocate queue */ 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); /* ...schedule processing instantly - tbd - check if we have anything pending on input */ xa_base_schedule(base, 0); /* ...pass success result to caller */ xf_response_ok(m); return XA_NO_ERROR; } /* ...port unroute command */ static XA_ERRORCODE xa_mixer_port_unroute(XACodecBase *base, xf_message_t *m) { XAMixer *mixer = (XAMixer *) base; xf_output_port_t *port = &mixer->output; /* ...command is allowed only in "postinit" state */ XF_CHK_ERR(base->state & XA_BASE_FLAG_POSTINIT, XA_API_FATAL_INVALID_CMD); /* ...make sure output port is addressed */ XF_CHK_ERR(XF_MSG_DST_PORT(m->id) == XA_MIXER_MAX_TRACK_NUMBER, XA_API_FATAL_INVALID_CMD_TYPE); /* ...cancel any pending processing */ xa_base_cancel(base); /* ...clear output-port-setup condition */ base->state &= ~XA_MIXER_FLAG_OUTPUT_SETUP; /* ...pass flush command down the graph */ if (xf_output_port_flush(port, XF_FLUSH)) { TRACE(INFO, _b("port is idle; instantly unroute")); /* ...flushing sequence is not needed; command may be satisfied instantly */ xf_output_port_unroute(port); /* ...pass response to the proxy */ xf_response_ok(m); } else { TRACE(INFO, _b("port is busy; propagate unroute command")); /* ...flushing sequence is started; save flow-control message */ xf_output_port_unroute_start(port, m); } return XA_NO_ERROR; } /* ...PAUSE message processing */ static XA_ERRORCODE xa_mixer_pause(XACodecBase *base, xf_message_t *m) { XAMixer *mixer = (XAMixer *) base; u32 i = XF_MSG_DST_PORT(m->id); XATrack *track = &mixer->track[i]; /* ...make sure the buffer is empty */ XF_CHK_ERR(m->length == 0, XA_API_FATAL_INVALID_CMD_TYPE); /* ...check destination port is valid */ XF_CHK_ERR(i < XA_MIXER_MAX_TRACK_NUMBER, XA_API_FATAL_INVALID_CMD_TYPE); /* ...check for actual track flags */ if (xa_track_test_flags(track, XA_TRACK_FLAG_ACTIVE)) { /* ...switch to paused state */ xa_track_toggle_flags(track, XA_TRACK_FLAG_ACTIVE | XA_TRACK_FLAG_PAUSED); /* ...other tracks may be waiting for this one, so force data processing */ if (xf_output_port_ready(&mixer->output)) { xa_base_schedule(base, 0); } TRACE(INFO, _b("mixer[%p]::track[%u] paused"), mixer, i); } else { /* ...track is in idle state and pausing here means suspending */ TRACE(INFO, _b("mixer[%p]::track[%u] is not active"), mixer, i); } /* ...complete message immediately */ xf_response(m); return XA_NO_ERROR; } /* ...RESUME command processing */ static XA_ERRORCODE xa_mixer_resume(XACodecBase *base, xf_message_t *m) { XAMixer *mixer = (XAMixer *) base; u32 i = XF_MSG_DST_PORT(m->id); XATrack *track = &mixer->track[i]; /* ...make sure the buffer is empty */ XF_CHK_ERR(m->length == 0, XA_API_FATAL_INVALID_CMD_TYPE); /* ...check destination port is valid */ XF_CHK_ERR(i < XA_MIXER_MAX_TRACK_NUMBER, XA_API_FATAL_INVALID_CMD_TYPE); /* ...check for actual track state */ if (xa_track_test_flags(track, XA_TRACK_FLAG_PAUSED)) { /* ...switch track to active state */ xa_track_toggle_flags(track, XA_TRACK_FLAG_ACTIVE | XA_TRACK_FLAG_PAUSED); /* ...reset track presentation timestamp - tbd */ track->pts = mixer->pts; /* ...force data processing if there is an output buffer */ if (xf_output_port_ready(&mixer->output)) { xa_base_schedule(base, 0); } TRACE(INFO, _b("mixer[%p]::track[%u] resumed"), mixer, i); } else { /* ...track is in idle state; do nothing */ TRACE(INFO, _b("mixer[%p]::track[%u] is not paused"), mixer, i); } /* ...complete message */ xf_response(m); return XA_NO_ERROR; } /* ...FLUSH command processing */ static XA_ERRORCODE xa_mixer_flush(XACodecBase *base, xf_message_t *m) { XAMixer *mixer = (XAMixer *) base; u32 i = XF_MSG_DST_PORT(m->id); XATrack *track = &mixer->track[i]; /* ...make sure the buffer is empty */ XF_CHK_ERR(m->length == 0, XA_API_FATAL_INVALID_CMD_TYPE); /* ...check destination port index */ if (i == XA_MIXER_MAX_TRACK_NUMBER) { /* ...flushing response received; that is a port unrouting sequence */ XF_CHK_ERR(xf_output_port_unrouting(&mixer->output), XA_API_FATAL_INVALID_CMD_TYPE); /* ...complete unroute sequence */ xf_output_port_unroute_done(&mixer->output); TRACE(INFO, _b("port is unrouted")); return XA_NO_ERROR; } /* ...check destination port index is valid */ XF_CHK_ERR(i < XA_MIXER_MAX_TRACK_NUMBER, XA_API_FATAL_INVALID_CMD_TYPE); /* ...input port flushing; check the track state is valid */ if (xa_track_test_flags(track, XA_TRACK_FLAG_ACTIVE | XA_TRACK_FLAG_PAUSED)) { /* ...purge input port */ xf_input_port_purge(&track->input); /* ...force clearing of ACTIVE and INPUT_SETUP condition */ xa_track_clear_flags(track, XA_TRACK_FLAG_ACTIVE | XA_TRACK_FLAG_PAUSED | XA_TRACK_FLAG_INPUT_SETUP); /* ...and enter into idle state */ xa_track_set_flags(track, XA_TRACK_FLAG_IDLE); /* ...other tracks may be waiting for this track, so force data processing */ if (xf_output_port_ready(&mixer->output)) { xa_base_schedule(base, 0); } TRACE(INFO, _b("mixer[%p]::track[%u] flushed"), mixer, i); } /* ...complete message instantly (no propagation to output port) */ xf_response(m); return XA_NO_ERROR; } /******************************************************************************* * Codec API implementation ******************************************************************************/ /* ...buffers handling */ static XA_ERRORCODE xa_mixer_memtab(XACodecBase *base, WORD32 idx, WORD32 type, WORD32 size, WORD32 align, u32 core) { XAMixer *mixer = (XAMixer *)base; if (type == XA_MEMTYPE_INPUT) { XATrack *track = &mixer->track[idx]; /* ...input buffer allocation; check track number is valid */ XF_CHK_ERR(idx < XA_MIXER_MAX_TRACK_NUMBER, XA_API_FATAL_INVALID_CMD_TYPE); /* ...create input port for a track */ XF_CHK_ERR(xf_input_port_init(&track->input, size, align, core) == 0, XA_API_FATAL_MEM_ALLOC); /* ...set input port buffer */ XA_API(base, XA_API_CMD_SET_MEM_PTR, idx, track->input.buffer); /* ...put track into idle state (will start as soon as we receive data) */ xa_track_set_flags(track, XA_TRACK_FLAG_IDLE); TRACE(INIT, _b("mixer[%p]::track[%u] input port created - size=%u"), mixer, idx, size); } else { /* ...output buffer allocation */ XF_CHK_ERR(type == XA_MEMTYPE_OUTPUT, XA_API_FATAL_INVALID_CMD_TYPE); /* ...check port number is what we expect */ XF_CHK_ERR(idx == XA_MIXER_MAX_TRACK_NUMBER, XA_API_FATAL_INVALID_CMD_TYPE); /* ...set mixer frame-size (in samples - for timestamp tracking) */ XA_API(base, XA_API_CMD_GET_CONFIG_PARAM, XA_MIXER_CONFIG_PARAM_FRAME_SIZE, &mixer->frame_size); /* ...create output port for a track */ XF_CHK_ERR(xf_output_port_init(&mixer->output, size) == 0, XA_API_FATAL_MEM_ALLOC); TRACE(INIT, _b("mixer[%p] output port created; size=%u"), mixer, size); } return XA_NO_ERROR; } /* ...preprocessing function */ static XA_ERRORCODE xa_mixer_preprocess(XACodecBase *base) { XAMixer *mixer = (XAMixer *) base; XATrack *track; u8 i; XA_ERRORCODE e = XA_MIXER_EXEC_NONFATAL_NO_DATA; /* ...prepare output buffer */ if (!(base->state & XA_MIXER_FLAG_OUTPUT_SETUP)) { void *output; /* ...set output buffer pointer */ if (base->state & XA_BASE_FLAG_RUNTIME_INIT) { /* ...no actual data processing during initialization */ return XA_NO_ERROR; } else if ((output = xf_output_port_data(&mixer->output)) == NULL) { /* ...no output buffer available */ return e; } /* ...set output buffer pointer */ XA_API(base, XA_API_CMD_SET_MEM_PTR, XA_MIXER_MAX_TRACK_NUMBER, output); /* ...mark output port is setup */ base->state ^= XA_MIXER_FLAG_OUTPUT_SETUP; } /* ...check EOS */ if (!xa_mixer_check_active(mixer)) { /* ...push EOS to output port */ xf_output_port_produce(&mixer->output, 0); TRACE(INFO, _b("mixer[%p]::EOS generated"), mixer); } /* ...setup input buffer pointers and length */ for (track = &mixer->track[i = 0]; i < XA_MIXER_MAX_TRACK_NUMBER; i++, track++) { /* ...skip tracks that are not played */ if (!xa_track_test_flags(track, XA_TRACK_FLAG_ACTIVE)) continue; /* ...set temporary mixing request */ e = XA_NO_ERROR; /* ...skip the tracks that has been setup already */ if (xa_track_test_flags(track, XA_TRACK_FLAG_INPUT_SETUP)) continue; /* ...found active track that hasn't been setup yet */ TRACE(INPUT, _b("track-%u: ts=%x vs mts=%x"), i, track->pts, mixer->pts); /* ...if track presentation timestamp is in the future, do nothing yet really */ if (!xf_time_after(track->pts, mixer->pts)) { u32 filled; /* ...take actual data from input port (mixer is always using internal buffer) */ if (!xf_input_port_fill(&track->input)) { /* ...failed to prefill input buffer - no sufficient data yet */ return XA_MIXER_EXEC_NONFATAL_NO_DATA; } else { /* ...retrieve number of bytes available */ filled = xf_input_port_level(&track->input); } /* ...set total number of bytes we have in buffer */ XA_API(base, XA_API_CMD_SET_INPUT_BYTES, i, &filled); /* ...actual data is to be played */ TRACE(INPUT, _b("track-%u: filled %u bytes"), i, filled); } /* ...mark the track input is setup (emit silence or actual data) */ xa_track_set_flags(track, XA_TRACK_FLAG_INPUT_SETUP); } /* ...do mixing operation only when all active tracks are setup */ return e; } /* ...postprocessing function */ static XA_ERRORCODE xa_mixer_postprocess(XACodecBase *base, int done) { XAMixer *mixer = (XAMixer *) base; XATrack *track; u32 produced; u32 consumed; u8 i; /* ...process execution stage transitions */ if (done) { if (base->state & XA_BASE_FLAG_RUNTIME_INIT) { /* ...failed to initialize runtime (can't be? - tbd)*/ BUG(1, _x("breakpoint")); } else if (base->state & XA_BASE_FLAG_EXECUTION) { /* ...enter into execution state; initialize runtime */ return XA_CHK(xa_mixer_prepare_runtime(mixer)); } else { /* ...mixer operation is over (can't be? - tbd) */ BUG(1, _x("breakpoint")); } } /* ...input ports maintenance; process all tracks */ for (track = &mixer->track[i = 0]; i < XA_MIXER_MAX_TRACK_NUMBER; i++, track++) { /* ...skip the tracks that are not runing */ if (!xa_track_test_flags(track, XA_TRACK_FLAG_ACTIVE)) continue; /* ...clear input setup flag */ xa_track_clear_flags(track, XA_TRACK_FLAG_INPUT_SETUP); /* ...advance track presentation timestamp */ track->pts += mixer->frame_size; /* ...get total amount of consumed bytes */ XA_API(base, XA_API_CMD_GET_CURIDX_INPUT_BUF, i, &consumed); TRACE(INPUT, _b("track-%u::postprocess(c=%u, ts=%x)"), i, consumed, track->pts); /* ...consume that amount from input port (may be zero) */ xf_input_port_consume(&track->input, consumed); /* ...check if input port is done */ if (xf_input_port_done(&track->input)) { /* ...input stream is over; return zero-length input back to caller */ xf_input_port_purge(&track->input); /* ...switch to idle state */ xa_track_toggle_flags(track, XA_TRACK_FLAG_ACTIVE | XA_TRACK_FLAG_IDLE); TRACE(INFO, _b("mixer[%p]::track[%u] completed"), mixer, i); } } /* ...check if we have produced anything */ XA_API(base, XA_API_CMD_GET_OUTPUT_BYTES, XA_MIXER_MAX_TRACK_NUMBER, &produced); TRACE(OUTPUT, _b("mixer[%p]::postprocess(p=%u, ts=%x, done=%u)"), mixer, produced, mixer->pts, done); /* ...output port maintenance */ if (produced) { /* ...make sure we have produced exactly single frame */ BUG(produced != mixer->output.length, _x("Invalid length: %u != %u"), produced, mixer->output.length); /* ...steady mixing process; advance mixer presentation timestamp */ mixer->pts += mixer->frame_size; /* ...push data from output port */ xf_output_port_produce(&mixer->output, produced); /* ...clear output-setup condition */ base->state &= ~XA_MIXER_FLAG_OUTPUT_SETUP; } /* ...reschedule data processing if there is a pending output message */ if (xf_output_port_ready(&mixer->output)) { /* ...schedule execution with respect to urgency */ xa_base_schedule(base, (produced ? mixer->frame_duration : 0)); } return XA_NO_ERROR; } /******************************************************************************* * Command-processing function ******************************************************************************/ /* ...command hooks */ static XA_ERRORCODE (* const xa_mixer_cmd[])(XACodecBase *, xf_message_t *) = { /* ...set-parameter - actually, same as in generic case */ [XF_OPCODE_TYPE(XF_SET_PARAM)] = xa_base_set_param, [XF_OPCODE_TYPE(XF_GET_PARAM)] = xa_base_get_param, /* ...output port routing/unrouting */ [XF_OPCODE_TYPE(XF_ROUTE)] = xa_mixer_port_route, [XF_OPCODE_TYPE(XF_UNROUTE)] = xa_mixer_port_unroute, /* ...input/output buffers processing */ [XF_OPCODE_TYPE(XF_EMPTY_THIS_BUFFER)] = xa_mixer_empty_this_buffer, [XF_OPCODE_TYPE(XF_FILL_THIS_BUFFER)] = xa_mixer_fill_this_buffer, [XF_OPCODE_TYPE(XF_FLUSH)] = xa_mixer_flush, /* ...track control */ [XF_OPCODE_TYPE(XF_PAUSE)] = xa_mixer_pause, [XF_OPCODE_TYPE(XF_RESUME)] = xa_mixer_resume, }; /* ...total number of commands supported */ #define XA_MIXER_CMD_NUM (sizeof(xa_mixer_cmd) / sizeof(xa_mixer_cmd[0])) /******************************************************************************* * Entry points ******************************************************************************/ /* ...mixer termination-state command processor */ static int xa_mixer_terminate(xf_component_t *component, xf_message_t *m) { XAMixer *mixer = (XAMixer *) component; u32 opcode = m->opcode; if (m == xf_output_port_control_msg(&mixer->output)) { /* ...output port flushing complete; mark port is idle and terminate */ xf_output_port_flush_done(&mixer->output); return -1; } else if (opcode == XF_FILL_THIS_BUFFER && xf_output_port_routed(&mixer->output)) { /* ...output buffer returned by the sink component; ignore and keep waiting */ TRACE(OUTPUT, _b("collect output buffer")); return 0; } else if (opcode == XF_UNREGISTER) { /* ...ignore subsequent unregister command/response */ return 0; } else { /* ...everything else is responded with generic failure */ xf_response_err(m); return 0; } } /* ...mixer class destructor */ static int xa_mixer_destroy(xf_component_t *component, xf_message_t *m) { XAMixer *mixer = (XAMixer *) component; u32 core = xf_component_core(component); u32 i; /* ...destroy all inputs */ for (i = 0; i < XA_MIXER_MAX_TRACK_NUMBER; i++) { xf_input_port_destroy(&mixer->track[i].input, core); } /* ...destroy output port */ xf_output_port_destroy(&mixer->output, core); /* ...destroy base object */ xa_base_destroy(&mixer->base, XF_MM(sizeof(*mixer)), core); TRACE(INIT, _b("mixer[%p] destroyed"), mixer); return 0; } /* ...mixer class first-stage destructor */ static int xa_mixer_cleanup(xf_component_t *component, xf_message_t *m) { XAMixer *mixer = (XAMixer *) component; u32 i; /* ...complete message with error result code */ xf_response_err(m); /* ...cancel internal scheduling message if needed */ xa_base_cancel(&mixer->base); /* ...purge all input ports (specify "unregister"? - don't know yet - tbd) */ for (i = 0; i < XA_MIXER_MAX_TRACK_NUMBER; i++) { xf_input_port_purge(&mixer->track[i].input); } /* ...flush output port */ if (xf_output_port_flush(&mixer->output, XF_FLUSH)) { /* ...flushing sequence is not needed; destroy mixer */ return xa_mixer_destroy(component, NULL); } else { /* ...wait until output port is cleaned; adjust component hooks */ component->entry = xa_mixer_terminate; component->exit = xa_mixer_destroy; TRACE(INIT, _b("mixer[%p] cleanup sequence started"), mixer); /* ...indicate that second stage is required */ return 1; } } /* ...mixer class factory */ xf_component_t * xa_mixer_factory(u32 core, xa_codec_func_t process) { XAMixer *mixer; /* ...construct generic audio component */ XF_CHK_ERR(mixer = (XAMixer *)xa_base_factory(core, XF_MM(sizeof(*mixer)), process), NULL); /* ...set generic codec API */ mixer->base.memtab = xa_mixer_memtab; mixer->base.preprocess = xa_mixer_preprocess; mixer->base.postprocess = xa_mixer_postprocess; /* ...set message-processing table */ mixer->base.command = xa_mixer_cmd; mixer->base.command_num = XA_MIXER_CMD_NUM; /* ...set component destructor hook */ mixer->base.component.exit = xa_mixer_cleanup; TRACE(INIT, _b("Mixer[%p] created"), mixer); /* ...return handle to component */ return (xf_component_t *) mixer; }