示例#1
0
void CsrSubwooferPluginConnect(Sink audio_sink, Task codec_task, Task app_task, subwooferPluginConnectParams * params)
{
    FILE_INDEX index;
    
    /* Update the current DSP status */
    SetCurrentDspStatus(DSP_LOADING);
    
    /* Give Kalimba the plugin task so it knows where to send messages */
    (void) MessageCancelAll( (TaskData*)&csr_subwoofer_plugin, MESSAGE_FROM_KALIMBA);
    MessageKalimbaTask( (TaskData*)&csr_subwoofer_plugin );
    
    /* Load the Subwofoer DSP application - Panic if it could not be loaded */
    index = PanicFalse( FileFind(FILE_ROOT, kal, sizeof(kal) - 1) );
    PanicFalse( KalimbaLoad(index) );
    
    /* update current DSP status */
    SetCurrentDspStatus(DSP_LOADED_IDLE);
    
    /* Allocate memory to store the plugin data */
    plugin_data = (subwooferPluginData*)PanicUnlessMalloc(sizeof(subwooferPluginData));
    
    /* Initialise the plugin data based on parameters supplied by the application */
    plugin_data->audio_source           = StreamSourceFromSink(audio_sink);
    plugin_data->swat_system_volume_db  = params->swat_system_volume_db;
    plugin_data->swat_trim_gain_db      = params->swat_trim_gain_db;
    plugin_data->adc_volume_index       = params->adc_volume;
    plugin_data->input                  = params->input;
    plugin_data->output                 = params->output;
    plugin_data->sample_rate            = params->sample_rate;
    plugin_data->adc_sample_rate        = params->adc_sample_rate;
    plugin_data->codec_task             = codec_task;
    plugin_data->app_task               = app_task;
    plugin_data->dsp_set_sample_rate    = 0; /* set later in response to AUDIO_PLUGIN_SET_MODE_MSG */
    
    /* Zero the codecs output gain */
    CodecSetOutputGainNow(plugin_data->codec_task, 0, left_and_right_ch);
    CodecSetInputGainNow(plugin_data->codec_task, 0, left_and_right_ch);
    
    
    /* If using the ADC, set the ADC source */
    if (plugin_data->input == SUBWOOFER_INPUT_ADC)
    {
        /* Get the ADC source */
        plugin_data->audio_source = StreamAudioSource(AUDIO_HARDWARE_CODEC, AUDIO_INSTANCE_0, AUDIO_CHANNEL_A);
    }
    
    
    /* Disconnect the source in case it's currently being disposed */
    StreamDisconnect(StreamSourceFromSink(audio_sink), 0);
    
    PRINT(("[SW_PLUGIN] : CsrSubwooferPluginConnect - complete\n"));
}
/****************************************************************************
DESCRIPTION: CsrSetVolumeI2SDevice 

    This function sets the I2S device volume via the I2C

PARAMETERS:
    
    int16 volume - volume level required scaled as 0 to +1024 corresponding to 0 to -120dB

RETURNS:
    none
*/    
void CsrSetVolumeI2SDevice_SSM2518(int16 left_vol, int16 right_vol, bool volume_in_dB)
{
    int32 volume=0;
    uint8 i2c_data[2];

    PRINT(("VP: SSM2518 SetVol L[%x] R[%x] dB[%x]\n",left_vol, right_vol,volume_in_dB));

    /* set the left volume level */
    i2c_data[0] = LEFT_VOLUME_CONTROL;
    if(volume_in_dB)
    {
        /* left_vol is scaled 0 to 8192 to simplify the maths and reduce stack usage,
           0 = 0dB, 1024 = -1024dB, to convert this to the 0 to 255 required by the SSM2518
           simply divide by 4 */               
        volume = (left_vol/4);
        /* convert from dB to i2s amp range of 0 to 255 where 0 = max and 255 = min */
        i2c_data[1] = volume;             
    }
    else
        i2c_data[1] = (0xff - (((left_vol & 0xf)*0xff)/CODEC_STEPS));

    PRINT(("VP: SSM2518 SetVol data [%x]\n", i2c_data[1]));

    PanicFalse(I2cTransfer(ADDR_WRITE, i2c_data, 2, NULL, 0));
    
    /* set the right volume level */
    i2c_data[0] = RIGHT_VOLUME_CONTROL;
    if(volume_in_dB)
    {
        /* right_vol is scaled 0 to 8192 to simplify the maths and reduce stack usage,
           0 = 0dB, 1024 = -120dB, to convert this to the 0 to 255 required by the SSM2518
           simply divide by 4 */               
        volume = (right_vol/4);
        /* convert from dB to i2s amp range of 0 to 255 where 0 = max and 255 = min */
        i2c_data[1] = volume;             
    }
    else
        i2c_data[1] = (0xff - (((right_vol & 0xf)*0xff)/CODEC_STEPS));
    PanicFalse(I2cTransfer(ADDR_WRITE, i2c_data, 2, NULL, 0));
    
    /* set the master mute control */
    i2c_data[0] = VOLUME_MUTE_CONTROL;
    /* if volumes are zero then set master mute */
    if((left_vol == 0xff) && (right_vol == 0xff))
        i2c_data[1] = 0x01;
    /* otherwise clear the master mute */    
    else
        i2c_data[1] = 0x00;
    PanicFalse(I2cTransfer(ADDR_WRITE, i2c_data, 2, NULL, 0));
}
示例#3
0
/****************************************************************************
NAME    
    audioControlLowPowerCodecs
    
DESCRIPTION
    Enables / disables the low power codecs. 
    will only use low power codecs is the flag UseLowPowerCodecs is set and 
    enable == TRUE
    
    This fn is called whenever an audioConnect occurs in order to select the 
    right codec ouptut stage power .
    
RETURNS
    none
*/
void audioControlLowPowerCodecs(  bool enable  )
{
    audio_channel channel  =  AUDIO_CHANNEL_A ;
    Sink sink   = NULL ;
    
    if ( theSink.features.stereo )
    {
        channel = AUDIO_CHANNEL_A_AND_B;
    }

    /* obtain sink to audio hardware */
    sink = StreamAudioSink(AUDIO_HARDWARE_CODEC, AUDIO_INSTANCE_0, channel);

    /* ensure sink is valid */
    if(sink)
    {
        /* if low power codec use is disabled in features ps or enable is false */
        if ((!theSink.features.UseLowPowerAudioCodecs)||(!enable))
        {
            /*disable low power codecs as feature disabled or enable override is set */
            AUD_DEBUG(("AUD:LowPowerCodecDisable CODEC NORMAL, feature [%c] enable [%c]\n", theSink.features.UseLowPowerAudioCodecs? 'T':'F', enable? 'L':'N')) ;
        
#ifdef BC5_MULTIMEDIA
            /* set to normal mode */
            PanicFalse(SinkConfigure(sink, STREAM_CODEC_LOW_POWER_OUTPUT_STAGE_ENABLE, FALSE ));
#else        
            /* set codec to high quality mode */
            PanicFalse(SinkConfigure(sink, STREAM_CODEC_QUALITY_MODE, CODEC_HIGH_MODE )) ;  
#endif        
        }
        /* if the low power audio codec feature is enabled and the enable is true */
        else
        {
#ifdef BC5_MULTIMEDIA
            /* enable the low power mode */        
            PanicFalse(SinkConfigure(sink, STREAM_CODEC_LOW_POWER_OUTPUT_STAGE_ENABLE, TRUE)) ;
#else        
            /* set the codec type to low power telephony mode */
            PanicFalse(SinkConfigure(sink, STREAM_CODEC_QUALITY_MODE, CODEC_TELEPHONY_MODE )) ;
#endif
            AUD_DEBUG(("AUD:LowPowerCodecEnable CODEC LOW POWER, feature [%c] enable [%c]\n", theSink.features.UseLowPowerAudioCodecs? 'T':'F', enable? 'L':'N')) ;
        }

        /* Close the Source/Sink */
        SinkClose(sink);
    }
}
/****************************************************************************
DESCRIPTION: CsrInitialiseI2SDevice :

    This function configures the I2S device 

PARAMETERS:
    
    uint32 sample_rate - sample rate of data coming from dsp

RETURNS:
    none
*/    
void CsrInitialiseI2SDevice_SSM2518(uint32 sample_rate)
{
    uint8 i2c_data[2];
    /*uint16 ack;*/
   
    
    /* set to normal operation with external MCLK, configure for NO_BLCK, MCLK is 64 * sample rate */
    i2c_data[0] = RESET_POWER_CONTROL;
    i2c_data[1] = (S_RST|SPWDN); 
    PanicFalse(I2cTransfer(ADDR_WRITE, i2c_data, 2, NULL, 0));     

    /* operate at 256 * fs to allow operation at 8KHz */
    i2c_data[0] = RESET_POWER_CONTROL;
    /* determine sample rate */
    if(sample_rate <= 16000)
        i2c_data[1] = 0x00;         
    else if(sample_rate <= 32000)       
        i2c_data[1] = 0x02;         
    else if(sample_rate <= 48000)       
        i2c_data[1] = 0x04;         
    else 
        i2c_data[1] = 0x08;         
    PanicFalse(I2cTransfer(ADDR_WRITE, i2c_data, 2, NULL, 0));     

    /* set to automatic sample rate setting */
    i2c_data[0] = EDGE_CLOCK_CONTROL;
    i2c_data[1] = ASR;         
    PanicFalse(I2cTransfer(ADDR_WRITE, i2c_data, 2, NULL, 0));     

    /* set sample rate and to use i2s with default settings */
    i2c_data[0] = SERIAL_INTERFACE_SAMPLE_RATE_CONTROL;
    /* determine sample rate */
    if(sample_rate <= 12000)
        i2c_data[1] = 0x00;         
    else if(sample_rate <= 24000)       
        i2c_data[1] = 0x01;         
    else if(sample_rate <= 48000)       
        i2c_data[1] = 0x02;         
    else 
        i2c_data[1] = 0x03;         
    PanicFalse(I2cTransfer(ADDR_WRITE, i2c_data, 2, NULL, 0));     
    
    /* set to ext b_clk, rising edge, msb first */
    i2c_data[0] = SERIAL_INTERFACE_CONTROL;
    i2c_data[1] = 0x00;  
    PanicFalse(I2cTransfer(ADDR_WRITE, i2c_data, 2, NULL, 0));     

    /* set the master mute control to on */
    i2c_data[0] = VOLUME_MUTE_CONTROL;
    i2c_data[1] = M_MUTE;
    PanicFalse(I2cTransfer(ADDR_WRITE, i2c_data, 2, NULL, 0));     
}
/****************************************************************************
NAME
    disconnectAudio

DESCRIPTION
    Disconnects the specified SCO/eSCO channel from the DSP.

*/
static void disconnectAudio (devInstanceTaskData *inst)
{
    DEBUG_AGHFP(("    disconnectAudio(0x%X)\n", (uint16)inst->audio_sink));
    if (inst->audio_sink)
    {
		SendEvent(EVT_AUDIO_DISCONNECT_IND,0);
		
        if (the_app->active_encoder == EncoderSco)
        {
			ledSetProfile(LedTypeSCO,FALSE);
            AudioDisconnect();    
#ifdef KAL_MSGxx
            /* Switch the DSP to IDLE mode */ 
            PanicFalse(KalimbaSendMessage(KALIMBA_ENCODER_SELECT, EncoderNone, 0, 0, 0));
#endif
            the_app->active_encoder = EncoderNone;
        }
        
        inst->audio_sink = 0;
        
    }
}
/****************************************************************************
NAME
    connectAudio

DESCRIPTION
    Connects the specified SCO/eSCO channel to the DSP.

*/
static void connectAudio(Sink audio_sink)
{
    DEBUG_AGHFP(("    connectAudio(0x%X)\n", (uint16)audio_sink));
        
    if (audio_sink)
    {
        audio_codec_type codec = audio_codec_none;

		SendEvent(EVT_AUDIO_CONNECT_CFM,0);
    
        if(!the_app->bidirect_faststream)
        {
              /* Decide the plugin */
            the_app->aghfp_audio_plugin = initScoPlugin();

			ledSetProfile(LedTypeSCO,TRUE);
            /* Connect the audio plugin */
            AudioConnect(the_app->aghfp_audio_plugin, 
                         audio_sink, 
                         the_app->sink_type,
                         the_app->codecTask,
                         0x0a, 
                         0,  
                         TRUE, 
                         AUDIO_MODE_CONNECTED, 
                         (void*)codec );
        
#ifdef KAL_MSG
            /* Switch the DSP to SCO mode */
            PanicFalse(KalimbaSendMessage(KALIMBA_ENCODER_SELECT, EncoderSco, 0, 0, 0));
#endif
			AudioSetVolume(the_app->vgs,the_app->codecTask);
			CodecSetInputGainNow(the_app->codecTask,the_app->vgm,left_and_right_ch);
            the_app->active_encoder = EncoderSco;
        }
    }
}
示例#7
0
void A2dpStartKalimbaStreaming(const A2DP *a2dp, uint16 media_sink)
{
    FILE_INDEX index = FILE_NONE;

#ifdef BUILD_FOR_23FW
    Transform t;    
#else    
    Transform t, l_t, r_t;
    
    Source l_src = StreamAudioSource(AUDIO_HARDWARE_CODEC, AUDIO_INSTANCE_0, AUDIO_CHANNEL_A);
    Source r_src = StreamAudioSource(AUDIO_HARDWARE_CODEC, AUDIO_INSTANCE_1, AUDIO_CHANNEL_B);
    Sink   l_snk = StreamAudioSink(AUDIO_HARDWARE_CODEC, AUDIO_INSTANCE_0, AUDIO_CHANNEL_A);
    Sink   r_snk = StreamAudioSink(AUDIO_HARDWARE_CODEC, AUDIO_INSTANCE_1, AUDIO_CHANNEL_B);
#endif
    
    /* load SBC codec */
    index = FileFind(FILE_ROOT, sbc_encoder, sizeof(sbc_encoder)-1);
    if (!KalimbaLoad(index))
        /* codec load failure, Panic as the test isn't going to work now */
        Panic();

    /* Init and configure RTP */
    t = TransformRtpSbcEncode(StreamKalimbaSource(0), (Sink)media_sink);
    TransformConfigure(t, VM_TRANSFORM_RTP_SBC_ENCODE_PACKET_SIZE, 668);
    TransformConfigure(t, VM_TRANSFORM_RTP_SBC_ENCODE_MANAGE_TIMING, FALSE);
    (void)TransformStart(t);

    /* Configure SBC encoding format */
    (void)PanicFalse(KalimbaSendMessage(KALIMBA_MSG_SBCENC_SET_PARAMS, 0x00bd, 0, 0, 0));
    (void)PanicFalse(KalimbaSendMessage(KALIMBA_MSG_SBCENC_SET_BITPOOL, 0x0030, 0, 0, 0));

#ifdef BUILD_FOR_23FW
    StreamDisconnect(StreamPcmSource(0), StreamPcmSink(0));
    StreamDisconnect(StreamPcmSource(1), StreamPcmSink(1));

    /* set up ADCs */
    (void)PcmClearAllRouting();
    (void)PanicFalse(PcmRateAndRoute(0, PCM_NO_SYNC, 44100, 44100, VM_PCM_INTERNAL_A));
    (void)PanicFalse(PcmRateAndRoute(1, 0, 44100, 44100, VM_PCM_INTERNAL_B));
    (void)PanicFalse(StreamConnect(StreamPcmSource(0),StreamKalimbaSink(0)));
    (void)PanicFalse(StreamConnect(StreamPcmSource(1),StreamKalimbaSink(1)));

#else
    SourceClose(l_src);
    SourceClose(r_src);
    SinkClose(l_snk);
    SinkClose(r_snk);
    
    (void) SourceConfigure(l_src, STREAM_CODEC_INPUT_RATE, 44100);
    (void) SourceConfigure(r_src, STREAM_CODEC_INPUT_RATE, 44100);
    (void) SourceSynchronise(l_src, r_src);
    
    /* set up ADCs */
    l_t = StreamConnect(l_src, StreamKalimbaSink(0));
    r_t = StreamConnect(r_src, StreamKalimbaSink(1));
    (void)TransformStart(l_t);
    (void)TransformStart(r_t);
#endif
    
    /* Start decode */
    (void) PanicFalse(KalimbaSendMessage(KALIMBA_MSG_GO,0,0,0,0));
}
示例#8
0
void handleInternalPluginMessage(Task task, MessageId id, Message message)
{
    switch(id)
    {
        case MESSAGE_FROM_KALIMBA:
        {
            const DSP_REGISTER_T *msg = (const DSP_REGISTER_T *)message;
            
            /* Handle the message sent from Kalimba */
            switch(msg->id)
            {
                case MUSIC_READY_MSG:
                {
                    PRINT(("[SW_PLUGIN] : MUSIC_READY_MSG a[%x] b[%x] c[%x] d[%x]\n", msg->a, msg->b, msg->c, msg->d));
                    if (plugin_data)
                    {
                        /* Send parameters PSKEY to DSP */
                        KalimbaSendMessage(MUSIC_LOADPARAMS_MSG, MUSIC_PS_BASE, 0, 0, 0);
                    }
                    else
                    {
                        SetCurrentDspStatus(DSP_ERROR);
                    }
                }
                break;
                case MUSIC_PARAMS_LOADED_MSG:
                {
                    /* Send a message to the app to inform the number of subwoofer volume gains (for ADC mode) */
                    AUDIO_PLUGIN_DSP_IND_T * message = PanicUnlessNew(AUDIO_PLUGIN_DSP_IND_T);
                    message->id = SUB_PLUGIN_NUM_VOL_GAINS;
                    message->size_value = 1;
                    message->value[0] = msg->a;
                    MessageSend(plugin_data->app_task, AUDIO_PLUGIN_DSP_IND, message);
                    
                    PRINT(("[SW_PLUGIN] : MUSIC_PARAMS_LOADED_MSG a[%x] b[%x] c[%x] d[%x]\n", msg->a, msg->b, msg->c, msg->d));
                    
                    /* Connect input and output streams */
                    connectAudioStreams();
                    
                    /* Send Volume to DSP */
                    KalimbaSendMessage(MUSIC_VOLUME_MSG, plugin_data->swat_system_volume_db, plugin_data->swat_trim_gain_db, 0, plugin_data->adc_volume_index);
                    
                    /* Set the DSP status */
                    SetCurrentDspStatus(DSP_RUNNING);
                    
                    /* Send GO to DSP */
                    PanicFalse(KalimbaSendMessage(KALIMBA_MSG_GO, 0, 0, 0, 0));
                    
                    /* Send a message to the app to inform that the plugin is ready to recieve audio data */
                    MessageSend(plugin_data->app_task, AUDIO_PLUGIN_DSP_READY_FOR_DATA, 0);
                }
                break;
                case MUSIC_CODEC_MSG:
                {
                    PRINT(("[SW_PLUGIN] : MUSIC_CODEC_MSG a[%x] b[%x] c[%x] d[%x]\n", msg->a, msg->b, msg->c, msg->d));
                    
                    /* Parameters for this message :                */
                    /* msg->a BITS[0-3] = input gain                */
                    /* msg->a BIT[15]   = enable/disable mic preamp */
                    /* msg->b = output gain                         */
                    
                    /* Only set the input gain if using the ADC input (ESCO/L2CAP inputs don't use the ADC) */
                    if (plugin_data->input == SUBWOOFER_INPUT_ADC)
                    {
                        PRINT(("[SW PLUGIN] : Set ADC input GAIN[%x] Pre-amp enable[%x]\n", (msg->a & 0x1F), (msg->a>>15) & 0x1));
                        AudioPluginSetMicGain(plugin_data->audio_source, FALSE, (msg->a & 0x1F), (msg->a>>15) & 0x1);
                    }
                    
                    PRINT(("[SW PLUGIN] : Set DAC GAIN[%x]\n", msg->b));

                    /* Set the codec output gain according to what the DSP sent */
                    if (plugin_data->output == SUBWOOFER_OUTPUT_I2S)
                    {
                        CsrI2SAudioOutputSetVolume(FALSE, msg->b, msg->b, FALSE);
                    }
                    else
                    {
                        CodecSetOutputGainNow(plugin_data->codec_task, msg->b, left_and_right_ch);
                    }
                }
                break;
                case MUSIC_CUR_EQ_BANK:
                {
                    PRINT(("[SW_PLUGIN] : MUSIC_CUR_EQ_BANK a[%x] b[%x] c[%x] d[%x]\n", msg->a, msg->b, msg->c, msg->d));
                    
                    /* TODO */
                }
                break;
                case MUSIC_SIGNAL_DETECT_STATUS:
                {
                    /* msg->a = 1 if playing audio, 0 if audio is silent */
                    PRINT(("[SW_PLUGIN] : MUSIC_SIGNAL_DETECT_STATUS a[%x]\n", msg->a));
                    
                    if (msg->a)
                    {
                        /* Audio on wired input has been detected - Inform client task */
                        AUDIO_PLUGIN_DSP_IND_T * message = PanicUnlessNew(AUDIO_PLUGIN_DSP_IND_T);
                        message->id = SUB_PLUGIN_ADC_SIGNAL_ACTIVE;
                        message->size_value = 1;
                        message->value[0] = 0;
                        MessageSend(plugin_data->app_task, AUDIO_PLUGIN_DSP_IND, message);
                    }
                    else
                    {
                        /* Audio on wired input is now silent - Inform client task */
                        AUDIO_PLUGIN_DSP_IND_T * message = PanicUnlessNew(AUDIO_PLUGIN_DSP_IND_T);
                        message->id = SUB_PLUGIN_ADC_SIGNAL_IDLE;
                        message->size_value = 1;
                        message->value[0] = 0;
                        MessageSend(plugin_data->app_task, AUDIO_PLUGIN_DSP_IND, message);
                    }
                 }
                break;
                default:
                {
                    PRINT(("[SW_PLUGIN] : Unhandled message from Kalimba ID[%x]\n", msg->id));
                }
                break;
            }
        }
        break;
        default:
        {
            PRINT(("[SW_PLUGIN] : Unhandled internal message ID[%x]\n", id));
        }
        break;
    }
示例#9
0
/****************************************************************************
NAME
    connectAudioStreams

DESCRIPTION
    Helper function to connect the audio streams
*/
static void connectAudioStreams(void)
{
    /* Connect the appropriate input stream to Kalimba */
    if (plugin_data->input == SUBWOOFER_INPUT_ADC)
    {
        PRINT(("[SW_PLUGIN] : Plugging ADC to Kalimba [%u]\n", plugin_data->adc_sample_rate));
        
        /* Configure the ADC */
        PanicFalse( SourceConfigure(plugin_data->audio_source, STREAM_CODEC_INPUT_RATE, plugin_data->adc_sample_rate) );
        
        /* Plug the ADC sink into the Kalimba */
        PanicFalse( StreamConnect(plugin_data->audio_source, StreamKalimbaSink(DSP_INPUT_PORT_ADC)) );
    }
    else if (plugin_data->input == SUBWOOFER_INPUT_ESCO)
    {
        /* Enable meta-data on the audio input */
        if ( SourceConfigure(plugin_data->audio_source, VM_SOURCE_SCO_METADATA_ENABLE, 1))
        {
            /* Plug the eSCO sink into the Kalimba */
            if ( !StreamConnect(plugin_data->audio_source, StreamKalimbaSink(DSP_INPUT_PORT_ESCO)) )
            {
                PRINT(("[SW_PLUGIN] : Plugging ESCO to Kalimba [FAILED]\n"));
                sendErrorToClient();
                /* Nothing more can be done here */
                return;
            }
            else
            {
                PRINT(("[SW_PLUGIN] : ESCO connected to Kalimba\n"));
            }
        }
        else
        {
            PRINT(("[SW_PLUGIN] : Plugging ESCO to Kalimba [FAILED]\n"));
            sendErrorToClient();
            /* Nothing more can be done here */
            return;
        }
    }
    else if (plugin_data->input == SUBWOOFER_INPUT_L2CAP)
    {
        /* Plug the L2CAP sink into the Kalimba */
        if ( !StreamConnect(plugin_data->audio_source, StreamKalimbaSink(DSP_INPUT_PORT_L2CAP)) )
        {
            PRINT(("[SW_PLUGIN] : Plugging L2CAP to Kalimba [FAILED]\n"));
            sendErrorToClient();
            /* Nothing more can be done here */
            return;
        }
        else
        {
            PRINT(("[SW_PLUGIN] : L2CAP connected to Kalimba\n"));
        }
    }
    else
    {
        PRINT(("[SW_PLUGIN] : Plugin parameter INPUT[%x] is invalid\n", plugin_data->input));
        Panic();
    }
    
    /* Connect Kalimba to the appropriate output stream */
    if (plugin_data->output == SUBWOOFER_OUTPUT_DAC)
    {
        
        
        /* Get the Codec DAC sink & Configure output rate */
        plugin_data->codec_sink = StreamAudioSink(AUDIO_HARDWARE_CODEC, AUDIO_INSTANCE_0, AUDIO_CHANNEL_A_AND_B);
        if ( !SinkConfigure(plugin_data->codec_sink, STREAM_CODEC_OUTPUT_RATE, plugin_data->sample_rate) )
        {
            PRINT(("[SW_PLUGIN] : Could not configure DAC [%u]\n", plugin_data->sample_rate));
            sendErrorToClient();
            /* Nothing more can be done here */
            return;
        }
        
        /* Plug the audio route from Kalimba to the DAC */
        if ( !StreamConnect(StreamKalimbaSource(DSP_OUTPUT_PORT_DAC), plugin_data->codec_sink) )
        {
            PRINT(("[SW_PLUGIN] : Could not connect DAC to Kalimba[%u]\n", plugin_data->sample_rate));
            sendErrorToClient();
            /* Nothing more can be done here */
            return;
        }
        
        PRINT(("[SW_PLUGIN] : Kalimba connected to DAC\n"));
    }
    else if (plugin_data->output == SUBWOOFER_OUTPUT_I2S)
    {
        PRINT(("[SW_PLUGIN] : Plugging Kalimba to I2S [%u]\n", plugin_data->sample_rate));
        
        /* Is a specified output frequency required? use resampling */
        if(CsrI2SMusicResamplingFrequency())
        {
            CsrI2SAudioOutputConnect(CsrI2SMusicResamplingFrequency(), FALSE, StreamKalimbaSource(DSP_OUTPUT_PORT_I2S), NULL);
        }
        else
        {
            CsrI2SAudioOutputConnect(plugin_data->sample_rate, FALSE, StreamKalimbaSource(DSP_OUTPUT_PORT_I2S), NULL);
        }
    }
    else
    {
        PRINT(("[SW_PLUGIN] : Plugin parameter OUTPUT[%x] is invalid\n", plugin_data->output));
        Panic();
    }
    
    /* set dsp operating flags */
    SetCurrentDspStatus(DSP_RUNNING);
    SetAudioInUse(TRUE);
}
/****************************************************************************
DESCRIPTION
	This function connects a synchronous audio stream to the pcm subsystem
*/ 
void CsrSbcEncoderPluginConnect( Sink audio_sink , Task codec_task , uint16 volume , uint32 rate , bool stereo , AUDIO_MODE_T mode , const void * params ) 
{
	/* DSP Application loading and Transform starting is handled by a call to A2dpAudioCodecEnable
		in the application, so should not be done here. */

	typedef struct
	{
		unsigned source_type:4;
		unsigned reserved:4;
		uint8 content_protection;
		uint32 voice_rate;
		unsigned bitpool:8;
		unsigned format:8;
		uint16 packet_size;
		Sink media_sink_b;
	} sbc_codec_data_type;

	sbc_codec_data_type *sbc_codecData = (sbc_codec_data_type *) params;

	if (!sbc_codecData)
		Panic();

    SBC = (SBC_t*)PanicUnlessMalloc (sizeof (SBC_t) ) ;
    
    SBC->media_sink[0] = audio_sink ;
    SBC->codec_task = codec_task ;
	SBC->media_sink[1] = sbc_codecData->media_sink_b;
	SBC->packet_size = sbc_codecData->packet_size;
    
    StreamDisconnect(StreamKalimbaSource(2), 0);

	/* Initialise the RTP SBC encoder */
	SBC->t[0] = TransformRtpSbcEncode(StreamKalimbaSource(2), audio_sink);

	/* Configure the RTP transform to generate the selected packet size */
	TransformConfigure(SBC->t[0], VM_TRANSFORM_RTP_SBC_ENCODE_PACKET_SIZE, sbc_codecData->packet_size);

	/* Transform should not manage timings. */
	TransformConfigure(SBC->t[0], VM_TRANSFORM_RTP_SBC_ENCODE_MANAGE_TIMING, FALSE);

	/* Start the transform */
	(void) TransformStart(SBC->t[0]);
    
    PRINT(("Audio Plugin: TransformStart sink:0x%x\n",(uint16)audio_sink));
	
	if (SBC->media_sink[1])
	{
		/* There is a 2nd audio stream so initialise this transform */
        StreamDisconnect(StreamKalimbaSource(3), 0);
		
		/* Initialise the RTP SBC encoder */
		SBC->t[1] = TransformRtpSbcEncode(StreamKalimbaSource(3), SBC->media_sink[1]);

		/* Configure the RTP transform to generate the selected packet size */
		TransformConfigure(SBC->t[1], VM_TRANSFORM_RTP_SBC_ENCODE_PACKET_SIZE, sbc_codecData->packet_size);

		/* Transform should not manage timings. */
		TransformConfigure(SBC->t[1], VM_TRANSFORM_RTP_SBC_ENCODE_MANAGE_TIMING, FALSE);

		/* Start the transform */
		(void) TransformStart(SBC->t[1]);
        
        PRINT(("Audio Plugin: TransformStart sink:0x%x\n",(uint16)SBC->media_sink[1]));
	}

	/* Configure SBC encoding format */
	if (!KalimbaSendMessage(KALIMBA_MSG_SBCENC_SET_PARAMS, sbc_codecData->format, 0, 0, 0))
		/* If message fails to get through, abort */
		Panic();

	/* Pass bit pool value to DSP */
	if (!KalimbaSendMessage(KALIMBA_MSG_SBCENC_SET_BITPOOL, sbc_codecData->bitpool, 0, 0, 0))
		/* If message fails to get through, abort */
		Panic();

	/* disard any data sent by the SNK */
    StreamConnectDispose(StreamSourceFromSink(audio_sink));
	
	if (SBC->media_sink[1])
	{
		/* disard any data sent by the 2nd SNK */
	    StreamConnectDispose(StreamSourceFromSink(SBC->media_sink[1]));
	}
	
	/* select the source type */
	if(sbc_codecData->source_type == SourceUsb)
	{
		PRINT(("Audio Plugin: SourceUsb\n"));
		/* Select the source type */
		PanicFalse(KalimbaSendMessage(KALIMBA_ENCODER_SELECT, 0x0001, 0, 0, 0));
	}
	else
	{
		PRINT(("Audio Plugin: SourceAnalog\n"));
		/* For analogue input source */
		StreamDisconnect(StreamPcmSource(0), StreamPcmSink(0));
		StreamDisconnect(StreamPcmSource(1), StreamPcmSink(1));
        
		(void)PcmClearAllRouting();

		if (CodecGetCodecType(codec_task) == codec_wm8731)
		{
			PRINT(("Audio Plugin: codec_wm8731\n"));
			/* configure slot 0 and 1 to be left and right channel
			and synchronise the offsets for stereo playback */
			(void)PcmRateAndRoute(0, PCM_NO_SYNC, (uint32) rate, (uint32) rate, VM_PCM_EXTERNAL_I2S);
			(void)PcmRateAndRoute(1, 0, (uint32) rate, (uint32) rate, VM_PCM_EXTERNAL_I2S);
		}
		else
		{
			PRINT(("Audio Plugin: codec_internal\n"));
			/* configure slot 0 and 1 to be left and right channel
			and synchronise the offsets for stereo playback */
			(void)PcmRateAndRoute(0, PCM_NO_SYNC, (uint32) rate, (uint32) rate, VM_PCM_INTERNAL_A);
			(void)PcmRateAndRoute(1, 0, (uint32) rate, (uint32) rate, VM_PCM_INTERNAL_B);
		}
		        
		/* plug Left ADC into port 0 */
		(void)StreamConnect(StreamPcmSource(0),StreamKalimbaSink(0)); 

		/* plug Right ADC into port 1 */
		(void)StreamConnect(StreamPcmSource(1),StreamKalimbaSink(1)); 
		
		/* Select the source type */
		PanicFalse(KalimbaSendMessage(KALIMBA_ENCODER_SELECT, 0x0002, 0, 0, 0)); 
		
		if(!KalimbaSendMessage(KALIMBA_MSG_GO,0,0,0,0))
		{
			PRINT(("SBC: Message KALIMBA_MSG_GO failed!\n"));
			Panic();
		}
	}

	/* Select the source type */
	/*PanicFalse(KalimbaSendMessage(KALIMBA_SOURCE_SELECT, SOURCE_USB, 0, 0, 0)); */
	
    /*CsrSbcEncoderUsbPluginSetVolume(volume) ;*/
	
	/*(void) StreamConnect(PanicNull(StreamUsbEndPointSource(end_point_iso_in)), StreamKalimbaSink(0));
    */
	/* Start decode */
   /* if(!KalimbaSendMessage(KALIMBA_MSG_GO,0,0,0,0))
	{
		PRINT(("SBC: Message KALIMBA_MSG_GO failed!\n"));
		Panic();
	}
	*/
}
static void CsrVoicePresencesPluginPlayDigit(void) 
{
    Source lSource ;
    voice_prompt prompt;
    
    /* Get the prompt data*/
    lSource = csrVoicePresencesGetPrompt(&prompt, koovox_phrase_data);
    if(!lSource) Panic();
    
    SetAudioBusy((TaskData*) &csr_voice_presences_plugin);

    /* Stash the source */
    koovox_phrase_data->source = lSource;
    koovox_phrase_data->decompression = prompt.decompression;
    koovox_phrase_data->stereo = prompt.stereo;
    koovox_phrase_data->playback_rate =  (prompt.playback_rate ? prompt.playback_rate : 8000);
    koovox_phrase_data->mixing = FALSE;    /* overridden later if this prompt is mixed */
    
    SetVpPlaying(TRUE);

	PRINT(("==decompression:%d===\n", prompt.decompression));

    /* Connect the stream to the DAC */
    switch(prompt.decompression)
    {
        case voice_prompts_codec_ima_adpcm:
        case voice_prompts_codec_none:     
        case voice_prompts_codec_tone:  
        {
            Sink lSink = NULL;
            Task taskdata = NULL;
            
            /* if DSP is already running, the voice prompt can be mixed with the dsp
               audio via the kalimba mixing port (3), either the CVC plugin or
               the music plugin will have already determined the output source and connected the 
               appropriate ports to the hardware, the volume will already have been set, it is not
               necessary to do it again */
            if(GetCurrentDspStatus())
            {            
                /* Configure tone or prompt playback */    
                if(prompt.decompression == voice_prompts_codec_tone)
                {
                    PRINT(("PRESENT: play tone\n"));
                    KalimbaSendMessage(MESSAGE_SET_TONE_RATE_MESSAGE_ID, koovox_phrase_data->playback_rate , 0/*Mono Bit 0 =0, TONE BIT 1 = 0*/, 0, 0); 
                }                    
                else
                {
                    PRINT(("PRESENT: play adpcm\n"));
                    KalimbaSendMessage(MESSAGE_SET_TONE_RATE_MESSAGE_ID, koovox_phrase_data->playback_rate , /*PROMPT_MONO*/PROMPT_ISPROMPT, 0, 0);                                  
                }
                /* stream voice prompt data to the DSP tone mixing port */                
                koovox_phrase_data->mixing = TRUE;
                lSink = StreamKalimbaSink(PRESENT_DSP_PORT);
                PRINT(("PRESENT: play dsp mix lSink = %x lSource = %x\n",(uint16)lSink,(uint16)lSource));
                SinkConfigure(lSink, STREAM_CODEC_OUTPUT_RATE, koovox_phrase_data->playback_rate);
                
                /* Get messages when source has finished */
                taskdata = MessageSinkTask( lSink , (TaskData*) &csr_voice_presences_plugin);

                /* connect the kalimba port to the audio_prompt */
                if(prompt.decompression == voice_prompts_codec_ima_adpcm)
                {   
                    PanicFalse(TransformStart(TransformAdpcmDecode(lSource, lSink)));
                }
                else
                {                      
                    PanicFalse(StreamConnect(lSource, lSink)); 
                }   
                
            }
			else
			{
				CsrVoicePresencesPluginStopPhrase();
			}
        }
        break;
        
        case voice_prompts_codec_pcm:        
        {    
            Sink lSink = NULL;
            Task taskdata = NULL;
            
            /* determine whether the prompt is being played as a mixed with audio via the dsp, if the dsp is being
               used for audio mixing there is no need to set the volume as this will already have been performed 
               plugin used to load the dsp for audio processing */            
            if(GetCurrentDspStatus())
            {
                /* store that this PCM prompt is mixing */
                koovox_phrase_data->mixing = TRUE;
                
                /* stream voice prompt data to the DSP tone mixing port */                
                lSink = StreamKalimbaSink(PRESENT_DSP_PORT);
                PRINT(("PRESENT: play dsp mix lSink = %x lSource = %x\n",(uint16)lSink,(uint16)lSource));
                SinkConfigure(lSink, STREAM_CODEC_OUTPUT_RATE, koovox_phrase_data->playback_rate);                               
                
                /* Get messages when source has finished */
                taskdata = MessageSinkTask( lSink , (TaskData*) &csr_voice_presences_plugin);

				PRINT(("PRESENT: sink task now %x was %x.\n",(uint16)&csr_voice_presences_plugin,(uint16)taskdata));
                /* Configure PCM prompt playback */    
                KalimbaSendMessage(MESSAGE_SET_TONE_RATE_MESSAGE_ID, koovox_phrase_data->playback_rate , (koovox_phrase_data->stereo?PROMPT_STEREO:0)|PROMPT_ISPROMPT, 0, 0);        
                
                /* Connect source to PCM */
                PanicFalse(StreamConnect(lSource, lSink));
            }
        }
        break;

        default:
            PRINT(("PRESENT: Codec Invalid\n"));
            Panic();
        break;
    }

}
/****************************************************************************
DESCRIPTION
	This function connects a synchronous audio stream to the pcm subsystem
*/
void CsrA2dpDecoderPluginConnect( A2dpPluginTaskdata *task, Sink audio_sink , Task codec_task , uint16 volume , uint32 rate , bool stereo , AUDIO_MODE_T mode , const void * params , Task app_task )
{
	FILE_INDEX index = FILE_NONE;
	char* kap_file = NULL ;

	/* Only need to read the PS Key value once */
    if (!pskey_read)
    {
        if (PsFullRetrieve(PSKEY_MAX_CLOCK_MISMATCH, &val_pskey_max_mismatch, sizeof(uint16)) == 0)
            val_pskey_max_mismatch = 0;
        pskey_read = TRUE;
    }

    switch ((A2DP_DECODER_PLUGIN_TYPE_T)task->a2dp_plugin_variant)
	{
	case SBC_DECODER:
		kap_file = "sbc_decoder/sbc_decoder.kap";
      break;
	case MP3_DECODER:
		kap_file = "mp3_decoder/mp3_decoder.kap";
		break;
	case AAC_DECODER:
		kap_file = "aac_decoder/aac_decoder.kap";
		break;
	case FASTSTREAM_SINK:
		kap_file = "faststream_sink/faststream_sink.kap";
		break;
	default:
		Panic();
		break;
	}


   /*ensure that the messages received are from the correct kap file*/
   (void) MessageCancelAll( (TaskData*) task, MESSAGE_FROM_KALIMBA);
   MessageKalimbaTask( (TaskData*) task );


	index = FileFind(FILE_ROOT,(const char *) kap_file ,strlen(kap_file));

	if (index == FILE_NONE)
		Panic();
	if (!KalimbaLoad(index))
		Panic();

    DECODER = (DECODER_t*)PanicUnlessMalloc (sizeof (DECODER_t) ) ;

    DECODER->media_sink = audio_sink ;
    DECODER->codec_task = codec_task ;
    DECODER->volume     = volume;
    DECODER->mode       = mode;
    DECODER->stereo     = stereo;
    DECODER->params     = (uint16) params;
    DECODER->rate       = rate;
    DECODER->app_task	= app_task;

	if ((A2DP_DECODER_PLUGIN_TYPE_T)task->a2dp_plugin_variant == AAC_DECODER)
	{
		/* Workaround for AAC+ sources that negotiate sampling frequency at half the actual value */
		if (rate < 32000)
			DECODER->rate = rate * 2;
	}

   CodecSetOutputGainNow(DECODER->codec_task, 0, left_and_right_ch);

   StreamDisconnect(StreamPcmSource(0), StreamPcmSink(0));
   StreamDisconnect(StreamPcmSource(1), StreamPcmSink(1));

   PanicFalse(PcmClearRouting(0));
   PanicFalse(PcmClearRouting(1));

   /* For sinks disconnect the source in case its currently being disposed. */
   StreamDisconnect(StreamSourceFromSink(audio_sink), 0);

	PRINT(("DECODER: CsrA2dpDecoderPluginConnect completed\n"));
}
/****************************************************************************
DESCRIPTION
	Connect the encoded audio input and pcm audio output streams
*/
static void MusicConnectAudio (A2dpPluginTaskdata *task, bool stereo )
{
   plugin_codec_data_type *codecData = (plugin_codec_data_type *) DECODER->params;
   uint32 voice_rate = 0;
   
   val_clock_mismatch = codecData->clock_mismatch;
   
   if ((A2DP_DECODER_PLUGIN_TYPE_T)task->a2dp_plugin_variant == FASTSTREAM_SINK)
	{
      /*
         FastStream does not use RTP.
         L2CAP frames enter/leave via port 2
      */
      
    	/*
         Initialise PCM.
         Output stereo at 44k1Hz or 48kHz, input from left ADC at 16kHz.
      */
      
      /* If no voice rate is set just make the ADC rate equal to 16kHz */
      if (!codecData->voice_rate)
         voice_rate = 16000;
      else
         voice_rate = codecData->voice_rate;
      
      PRINT(("FASTSTREAM: rate=0x%lx voice_rate=0x%lx\n format=0x%x bitpool=0x%x",DECODER->rate,codecData->voice_rate,codecData->format,codecData->bitpool));
      PanicFalse(PcmRateAndRoute(0, PCM_NO_SYNC, (uint32)DECODER->rate, (uint32) voice_rate, VM_PCM_INTERNAL_A));     
      
      /* is it mono playback? */
      if ( !stereo )
      {
         PRINT(("DECODER: Mono\n"));
         /* Connect Kalimba to PCM */
         if (DECODER->rate)
         {
            StreamDisconnect(StreamSourceFromSink(DECODER->media_sink), 0);
            PanicFalse(StreamConnect(StreamKalimbaSource(0),StreamPcmSink(0)));
            PanicFalse(StreamConnect(StreamSourceFromSink(DECODER->media_sink),StreamKalimbaSink(2)));
         }
      }
      else
      {
         PRINT(("DECODER: Stereo\n"));
         PanicFalse(PcmRateAndRoute(1, 0, (uint32)DECODER->rate, (uint32) voice_rate, VM_PCM_INTERNAL_B));
         /* Connect Kalimba to PCM */
         if (DECODER->rate)
         {
            StreamDisconnect(StreamSourceFromSink(DECODER->media_sink), 0);
            PanicFalse(StreamConnect(StreamKalimbaSource(0),StreamPcmSink(0)));
            PanicFalse(StreamConnect(StreamKalimbaSource(1),StreamPcmSink(1)));
            PanicFalse(StreamConnect(StreamSourceFromSink(DECODER->media_sink),StreamKalimbaSink(2)));
         }
      }

      if (codecData->voice_rate)
      {
         StreamDisconnect(0, DECODER->media_sink);
         /* configure parameters */
         PanicFalse(KalimbaSendMessage(KALIMBA_MSG_SBCENC_SET_PARAMS, codecData->format, 0, 0, 0));
         PanicFalse(KalimbaSendMessage(KALIMBA_MSG_SBCENC_SET_BITPOOL, codecData->bitpool, 0, 0, 0));
         PanicFalse(StreamConnect(StreamPcmSource(0), StreamKalimbaSink(0)));
         PanicFalse(StreamConnect(StreamKalimbaSource(2),DECODER->media_sink));
      }
     
   }
   
   else /* Not FastStream CODEC */
	{
		uint8 content_protection = codecData->content_protection;
		uint16 scms_enabled = 0;
		Transform rtp_transform = 0;

		switch ((A2DP_DECODER_PLUGIN_TYPE_T)task->a2dp_plugin_variant)
		{
		case SBC_DECODER:
			rtp_transform = TransformRtpSbcDecode(StreamSourceFromSink(DECODER->media_sink) , StreamKalimbaSink(0));
			break;
		case MP3_DECODER:
			rtp_transform = TransformRtpMp3Decode(StreamSourceFromSink(DECODER->media_sink) , StreamKalimbaSink(0));
			break;
		case AAC_DECODER:
			rtp_transform = TransformRtpAacDecode(StreamSourceFromSink(DECODER->media_sink) , StreamKalimbaSink(0));
			break;
		default:
			break;
		}

		/* Configure the content protection */
		if (content_protection)
			scms_enabled = 1;

		TransformConfigure(rtp_transform, VM_TRANSFORM_RTP_SCMS_ENABLE, scms_enabled);

		/*start the transform decode*/
    	(void)TransformStart( rtp_transform ) ;   

    	/* is it mono playback? */
		if ( !stereo )
		{
			PcmRateAndRoute(0, PCM_NO_SYNC, DECODER->rate, (uint32) 8000, VM_PCM_INTERNAL_A) ;
			PRINT(("DECODER: Mono\n"));
			/* plug port 0 into both DACs */
    		(void) PanicFalse(StreamConnect(StreamKalimbaSource(0),StreamPcmSink(0)));
		}
		else
		{
	    	PRINT(("DECODER: Stereo\n"));

	    	PanicFalse(PcmRateAndRoute(0, PCM_NO_SYNC, DECODER->rate, (uint32) 8000, VM_PCM_INTERNAL_A));
			PanicFalse(PcmRateAndRoute(1, 0, DECODER->rate, (uint32) 8000, VM_PCM_INTERNAL_B));

		    /* plug port 0 into Left DAC */
    		PanicFalse(StreamConnect(StreamKalimbaSource(0),StreamPcmSink(0)));
        	/* plug port 1 into Right DAC */
			PanicFalse(StreamConnect(StreamKalimbaSource(1),StreamPcmSink(1)));
		}
   }
         
    	CsrA2dpDecoderPluginSetVolume(task,DECODER->volume) ;

		/* The DSP must know the sample rate for tone mixing */
		KalimbaSendMessage(MESSAGE_SET_SAMPLE_RATE, DECODER->rate, val_pskey_max_mismatch, val_clock_mismatch, 0);
      PRINT(("DECODER: Send Go message to DSP now\n"));
		if(!KalimbaSendMessage(KALIMBA_MSG_GO,0,0,0,0))
		{
			PRINT(("DECODER: Message KALIMBA_MSG_GO failed!\n"));
			Panic();
		}
      
   }
/****************************************************************************
DESCRIPTION
    This function connects APTX low delay audio 
****************************************************************************/
void MusicConnectAptxLowLatency(A2dpPluginConnectParams *codecData, uint8 content_protection)
{
    Sink speaker_snk_a = NULL;
    Transform rtp_transform = 0;
    DECODER_t * DECODER = CsrA2dpDecoderGetDecoderData();
    
    /* when configured for using back channel low latency apps */
    if(isCodecLowLatencyBackChannel())
    {
        /* if the back channel is required and the mic/line input is configured */
        if (codecData->voice_rate)
        {
            /* Bidirectional channel uses faststream mono with the following settings */
            /*
               Configure the SBC format for the microphone data
               16kHz, Mono, Blocks 16, Sub-bands 8, Loudness, Bitpool = 32
               (data rate = 72kbps, packet size = 3*72 + 4 = 220 <= DM5).
            */
           codecData->format     = 0x31;
           codecData->bitpool    = 32;
           PRINT(("DECODER: apt-X Low Latency Bidirectional rate=0x%lx voice_rate=0x%lx\n format=0x%x\n",DECODER->rate,codecData->voice_rate,codecData->format));
        }
    }

    /* ensure the sample rate is valid */
    if (DECODER->rate)
    {
        /* disconnect media sink as it might be disposed */
        StreamDisconnect(StreamSourceFromSink(DECODER->media_sink), 0);

                /* determine the output hardware type */
        switch(DECODER->features.audio_output_type)
        {
            /* using the inbuilt dacs */
            case OUTPUT_INTERFACE_TYPE_NONE:
            case OUTPUT_INTERFACE_TYPE_DAC:
            {
                /* configure built-in audio hardware channel A */
                speaker_snk_a = StreamAudioSink(AUDIO_HARDWARE_CODEC, AUDIO_INSTANCE_0, AUDIO_CHANNEL_A);
                PanicFalse(SinkConfigure(speaker_snk_a, STREAM_CODEC_OUTPUT_RATE, DECODER->rate));
    
                /* if STEREO mode configured then connect the output channel B */
                if(DECODER->features.stereo)
                {
                    Sink speaker_snk_b = NULL;
        
                    PRINT(("DECODER: Stereo\n"));
        
                    /* connect channels B */
                    speaker_snk_b = StreamAudioSink(AUDIO_HARDWARE_CODEC, AUDIO_INSTANCE_0, AUDIO_CHANNEL_B);
                    /* configure channel to required rate */
                    PanicFalse(SinkConfigure(speaker_snk_b, STREAM_CODEC_OUTPUT_RATE, DECODER->rate));
                    /* synchronise both sinks for channels A & B */
                    PanicFalse(SinkSynchronise(speaker_snk_a, speaker_snk_b));
                    /* plug port 1 into Right DAC */
                    PanicFalse(StreamConnect(StreamKalimbaSource(AUDIO_OUT_FROM_DSP_LEFT),speaker_snk_a));
                    PanicFalse(StreamConnect(StreamKalimbaSource(AUDIO_OUT_FROM_DSP_RIGHT),speaker_snk_b));
                }
                /* mono operation, only connect left port */
                else
                {
                    /* plug port 0 into Left DAC */
                    PanicFalse(StreamConnect(StreamKalimbaSource(AUDIO_OUT_FROM_DSP_LEFT),speaker_snk_a));
                }
            }
            break;

            /* using the spdif digital output hardware */
            case OUTPUT_INTERFACE_TYPE_SPDIF:
            {
                Sink speaker_snk_b = NULL;
                /* configure spdif audio hardware channel 0 */
                speaker_snk_a = StreamAudioSink(AUDIO_HARDWARE_SPDIF, AUDIO_INSTANCE_0, SPDIF_CHANNEL_A);
                speaker_snk_b = StreamAudioSink(AUDIO_HARDWARE_SPDIF, AUDIO_INSTANCE_0, SPDIF_CHANNEL_B);
                /* configure channel to required rate */
                PanicFalse(SinkConfigure(speaker_snk_a,  STREAM_SPDIF_OUTPUT_RATE, DECODER->rate));
                PanicFalse(SinkConfigure(speaker_snk_b,  STREAM_SPDIF_OUTPUT_RATE, DECODER->rate));
                /* connect channels B */
                /* synchronise both sinks for channels A & B */
                PanicFalse(SinkSynchronise(speaker_snk_a, speaker_snk_b));
                /* plug port 1 into Right DAC */
                PanicFalse(StreamConnect(StreamKalimbaSource(AUDIO_OUT_FROM_DSP_LEFT),speaker_snk_a));
                PanicFalse(StreamConnect(StreamKalimbaSource(AUDIO_OUT_FROM_DSP_RIGHT),speaker_snk_b));   
      
                PRINT(("DECODER: Stereo\n"));
            }
            break;
            
            /* using the i2s digital output hardware */
            case OUTPUT_INTERFACE_TYPE_I2S:
            {
                /* is a specified output frequency required? use resampling*/
                if(CsrI2SMusicResamplingFrequency())
                    CsrI2SAudioOutputConnect(CsrI2SMusicResamplingFrequency(), DECODER->features.stereo, StreamKalimbaSource(AUDIO_OUT_FROM_DSP_LEFT), StreamKalimbaSource(AUDIO_OUT_FROM_DSP_RIGHT));                                 
                /* use the negotiated sample rate of the input, no resampling required */                
                else
                    CsrI2SAudioOutputConnect(DECODER->rate, DECODER->features.stereo, StreamKalimbaSource(AUDIO_OUT_FROM_DSP_LEFT), StreamKalimbaSource(AUDIO_OUT_FROM_DSP_RIGHT));                                                                
            }            
            break;
        }
    
        /* if content protection is required feed the media sink through the rtp decoder transform */
        if (content_protection)
        {
            rtp_transform = TransformRtpDecode(StreamSourceFromSink(DECODER->media_sink) , StreamKalimbaSink(LOW_LATENCY_CODEC_TO_DSP_PORT));
            TransformConfigure(rtp_transform, VM_TRANSFORM_RTP_SCMS_ENABLE, content_protection);
            /*start the transform decode*/
            PRINT(("aptX: RTP Transform \n"));
            (void)TransformStart( rtp_transform ) ;
        }
        /* connect the media sink to the dsp input port */
        else
        {
            PanicFalse(StreamConnect(StreamSourceFromSink(DECODER->media_sink),StreamKalimbaSink(LOW_LATENCY_CODEC_TO_DSP_PORT)));
        }
    }
    
    /* Send parameters that configure the SRA and buffer settings */
    PRINT(("aptX LL params: initial level=%d target level=%d sra max rate=%d/10000 sra avg time=%d good working buffer level=%d \n",
           codecData->aptx_sprint_params.target_codec_level,codecData->aptx_sprint_params.initial_codec_level,
           codecData->aptx_sprint_params.sra_max_rate,codecData->aptx_sprint_params.sra_avg_time,
           codecData->aptx_sprint_params.good_working_level));
    KalimbaSendMessage(MESSAGE_SET_APTX_LL_PARAMS1, codecData->aptx_sprint_params.target_codec_level,
                       codecData->aptx_sprint_params.initial_codec_level,
                       codecData->aptx_sprint_params.sra_max_rate,   /* Third field is scaled by 10000 */
                       codecData->aptx_sprint_params.sra_avg_time);
    KalimbaSendMessage(MESSAGE_SET_APTX_LL_PARAMS2, codecData->aptx_sprint_params.good_working_level,
                       0, 0, 0);

    /* update the current audio state */
    SetAudioInUse(TRUE);
}