/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{  
  /*!< At this stage the microcontroller clock setting is already configured, 
       this is done through SystemInit() function which is called from startup
       files (startup_stm32f40_41xxx.s/startup_stm32f427_437xx.s/
       startup_stm32f429_439xx.s/startup_stm32f401xx.s) before to branch to 
       application main. To reconfigure the default setting of SystemInit() 
       function, refer to system_stm32f4xx.c file
     */     
  
  /* Initialize LEDs on STM324x9I-EVAL board */
  STM_EVAL_LEDInit(LED1);
  STM_EVAL_LEDInit(LED2);    
  
  /* Configure the FMC interface : SDRAM */
  FMC_Config();
    
  /* Fill the buffer to write */
  Fill_Buffer(aTxBuffer, BUFFER_SIZE, 0x250F);   
  
  /* Write data to the SDRAM memory */
  for (uwIndex = 0; uwIndex < BUFFER_SIZE; uwIndex++)
  {
    *(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*uwIndex) = aTxBuffer[uwIndex];
  }    
  
  /* Read back data from the SDRAM memory */
  for (uwIndex = 0; uwIndex < BUFFER_SIZE; uwIndex++)
  {
    aRxBuffer[uwIndex] = *(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*uwIndex);
  } 
   
  /* Check the SDRAM memory content correctness */   
  for (uwIndex = 0; (uwIndex < BUFFER_SIZE) && (uwWriteReadStatus == 0); uwIndex++)
  {
    if (aRxBuffer[uwIndex] != aTxBuffer[uwIndex])
    {
      uwWriteReadStatus++;
    }
  }	

  if (uwWriteReadStatus)
  {
    /* KO */
    /* Turn on LD2 */
    STM_EVAL_LEDOn(LED2);     
  }
  else
  { 
    /* OK */
    /* Turn on LD1 */
    STM_EVAL_LEDOn(LED1);
  }

  while (1)
  {
  } 
  
}
int main(void)
{
    MMSample *sampleFileDataStart = WaveTable;
    size_t i;

    /* Enable signalling routines for errors */
    error_sig_init();

    /* Enable external SRAM */
    FMC_Config();

    codecDmaTxPtr = NULL;
    codecDmaRxPtr = NULL;

    /* The bus the signal chain is reading */
    MMBus *inBus = MMBus_new(BUS_BLOCK_SIZE,BUS_NUM_CHANS);

    /* The bus the signal chain is writing */
    MMBus *outBus = MMBus_new(BUS_BLOCK_SIZE,BUS_NUM_CHANS);

    /* a signal chain to put the signal processors into */
    MMSigChain sigChain;
    MMSigChain_init(&sigChain);

    /* A constant that zeros the bus each iteration */
    MMSigConst sigConst;
    MMSigConst_init(&sigConst,outBus,0,MMSigConst_doSum_FALSE);

    /* put sig constant at the top of the sig chain */
    MMSigProc_insertAfter(&sigChain.sigProcs,&sigConst);

    /* initialize wavetables */
    WaveTable_init();

    /* Give access to samples of sound as wave table */
    MMArray_set_data(&samples, WaveTable);
    MMArray_set_length(&samples, WAVTABLE_LENGTH_SAMPLES); 
    /* Set with this samplerate so it plays at normal speed when midi note 69
     * received */
    samples.samplerate = 440 * WAVTABLE_LENGTH_SAMPLES;//CODEC_SAMPLE_RATE;

    /* Allow MMWavTabRecorder to record into samples */
    MMWavTabRecorder_init(&wtr);
    wtr.buffer = &samples;
    wtr.inputBus = inBus;
    wtr.currentIndex = 0;
    wtr.state = MMWavTabRecorderState_RECORDING;

    /* Put MMWavTabRecorder at the top of the signal chain */
    MMSigProc_insertAfter(&sigChain.sigProcs,&wtr);

    /* Make poly voice manager */
    pvm = MMPolyManager_new(MIDI_NUM_NOTES);

    /* Enable MIDI hardware */
    MIDI_low_level_setup();

    /* Initialize MIDI Message builder */
    MIDIMsgBuilder_init(&midiMsgBuilder);

    /* set up the MIDI router to trigger samples */
    MIDI_Router_Standard_init(&midiRouter);
    for (i = 0; i < MIDI_NUM_NOTES; i++) {
        /* Initialize sample player */
        MMTrapEnvedSamplePlayer_init(&spsps[i], outBus, BUS_BLOCK_SIZE, 
                1. / (MMSample)CODEC_SAMPLE_RATE);
        /* Make new poly voice and add it to the poly voice manager */
        MMPolyManager_addVoice(pvm, i, (MMPolyVoice*)MMPvtesp_new(&spsps[i])); 
        /* insert in signal chain after sig const*/
        MMSigProc_insertAfter(&sigConst, &spsps[i]);
    }
    MIDI_Router_addCB(&midiRouter.router, MIDIMSG_NOTE_ON, 1, MIDI_note_on_do, spsps);
    MIDI_Router_addCB(&midiRouter.router, MIDIMSG_NOTE_OFF, 1, MIDI_note_off_do, spsps);
    MIDI_CC_CB_Router_addCB(&midiRouter.cbRouters[0],2,MIDI_cc_do,&wtr);
    MIDI_CC_CB_Router_addCB(&midiRouter.cbRouters[0],3,MIDI_cc_rate_control,&playbackRate);
    MIDI_CC_CB_Router_addCB(&midiRouter.cbRouters[0],4,MIDI_cc_period_control,&eventPeriod);
    MIDI_CC_CB_Router_addCB(&midiRouter.cbRouters[0],5,MIDI_cc_length_control,&eventLength);

    /* set up note scheduler */
    MMSeq *sequence = MMSeq_new();
    MMSeq_init(sequence, 0);

    /* Enable codec */
    i2s_dma_full_duplex_setup(CODEC_SAMPLE_RATE);

    while (1) {
        while (!(codecDmaTxPtr && codecDmaRxPtr));
        if ((MMSeq_getCurrentTime(sequence) % eventPeriod) == 0) {
            /* Make event */
            NoteOnEvent *noe = NoteOnEvent_new();
            MMPvtespParams *params = MMPvtespParams_new();
            params->paramType = MMPvtespParamType_NOTEON;
            params->note = get_next_free_voice_number();
            params->amplitude = 1.0;
            params->interpolation = MMInterpMethod_CUBIC;
            params->index = 0;
            params->attackTime = ATTACK_TIME;
            params->releaseTime = RELEASE_TIME; 
            params->samples = &samples;
            params->loop = 1;
            params->rate = playbackRate;
            params->rateSource = MMPvtespRateSource_RATE;
            NoteOnEvent_init(noe,pvm,params,sequence,eventLength);
            /* Schedule event to happen now */
            MMSeq_scheduleEvent(sequence,(MMEvent*)noe,MMSeq_getCurrentTime(sequence));
        }
        /* Do scheduled events and tick */
        MMSeq_doAllCurrentEvents(sequence);
        MMSeq_tick(sequence);
        MIDI_process_buffer(); /* process MIDI at most every audio block */
        MMSigProc_tick(&sigChain);
        size_t i;
        for (i = 0; i < CODEC_DMA_BUF_LEN; i += 2) {
            /* write out data */
            codecDmaTxPtr[i] = FLOAT_TO_INT16(outBus->data[i/2] * 0.1);
            codecDmaTxPtr[i+1] = FLOAT_TO_INT16(outBus->data[i/2] * 0.1);
            /* read in data */
            inBus->data[i/2] = INT16_TO_FLOAT(codecDmaRxPtr[i]);
        }
        codecDmaTxPtr = NULL;
        codecDmaRxPtr = NULL;
    }
}