// Copyright 2013 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, 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.
// 
// See http://creativecommons.org/licenses/MIT/ for more information.
//
// -----------------------------------------------------------------------------
//
// Multi.

#include "yarns/multi.h"

#include <algorithm>

#include "stmlib/algorithms/voice_allocator.h"

#include "yarns/just_intonation_processor.h"
#include "yarns/midi_handler.h"
#include "yarns/settings.h"

namespace yarns {
  
using namespace std;
using namespace stmlib;

const uint8_t clock_divisions[] = {
  96, 48, 32, 24, 16, 12, 8, 6, 4, 3, 2, 1
};

void Multi::Init() {
  just_intonation_processor.Init();
  
  fill(
      &settings_.custom_pitch_table[0],
      &settings_.custom_pitch_table[12],
      0);
  
  for (uint8_t i = 0; i < kNumParts; ++i) {
    part_[i].Init();
    part_[i].set_custom_pitch_table(settings_.custom_pitch_table);
  }
  for (uint8_t i = 0; i < kNumVoices; ++i) {
    voice_[i].Init();
  }
  running_ = false;
  latched_ = false;
  recording_ = false;
  
  // Put the multi in a usable state. Even if these settings will later be
  // overriden with some data retrieved from Flash (presets).
  settings_.clock_tempo = 120;
  settings_.clock_swing = 0;
  settings_.clock_input_division = 1;
  settings_.clock_output_division = 7;
  settings_.clock_bar_duration = 4;
  settings_.clock_override = 0;
  
  MidiSettings* midi = part_[0].mutable_midi_settings();
  midi->channel = 0;
  midi->min_note = 0;
  midi->max_note = 127;
  midi->min_velocity = 0;
  midi->max_velocity = 127;
  midi->out_mode = MIDI_OUT_MODE_GENERATED_EVENTS;
  
  VoicingSettings* voicing = part_[0].mutable_voicing_settings();
  voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST;
  voicing->allocation_mode = VOICE_ALLOCATION_MODE_MONO;
  voicing->legato_mode = false;
  voicing->portamento = 0;
  voicing->pitch_bend_range = 2;
  voicing->vibrato_range = 1;
  voicing->modulation_rate = 50;
  voicing->trigger_duration = 2;
  voicing->aux_cv = 0;
  voicing->tuning_transpose = 0;
  voicing->tuning_fine = 0;
  voicing->tuning_root = 0;
  voicing->tuning_system = 0;
  voicing->audio_mode = 0;

  SequencerSettings* seq = part_[0].mutable_sequencer_settings();
  seq->clock_division = 7;
  seq->gate_length = 3;
  seq->arp_range = 0;
  seq->arp_direction = 0;
  seq->arp_pattern = 0;
  
  fill(
      &seq->step[0],
      &seq->step[kNumSteps],
      SequencerStep(SEQUENCER_STEP_REST, 0));
  seq->num_steps = 0;
  // A test sequence...
  // seq->num_steps = 4;
  // seq->step[0].data[0] = 48;
  // seq->step[0].data[1] = 0x7f;
  // seq->step[1].data[0] = 72;
  // seq->step[1].data[1] = 0x7f;
  // seq->step[2].data[0] = 60;
  // seq->step[2].data[1] = 0x7f;
  // seq->step[3].data[0] = 72;
  // seq->step[3].data[1] = 0x7f;
  // voicing->audio_mode = 1;  // To hear it...

  num_active_parts_ = 1;
  part_[0].AllocateVoices(&voice_[0], 1, false);
  settings_.layout = LAYOUT_MONO;
}

void Multi::Clock() {
  if (!running_) {
    return;
  }
  
  uint16_t output_division = clock_divisions[settings_.clock_output_division];
  uint16_t input_division = settings_.clock_input_division;
  
  // Logic equation for computing a clock output with a 50% duty cycle.
  if (output_division > 1) {
    if (clock_output_prescaler_ == 0 && clock_input_prescaler_ == 0) {
      clock_pulse_duration_ = 0xffff;
    }
    if (clock_output_prescaler_ == (output_division >> 1) &&
        clock_input_prescaler_ == (input_division >> 1)) {
      clock_pulse_duration_ = 0;
    }
  } else {
    if (input_division > 1) {
      clock_pulse_duration_ = \
          clock_input_prescaler_ <= (input_division - 1) >> 1 ? 0xffff : 0;
    } else {
      // Because no division is used, neither on the output nor on the input,
      // we don't have a sufficient fast time base to derive a 50% duty cycle
      // output. Instead, we output 5ms pulses.
      clock_pulse_duration_ = 40;
    }
  }
  
  if (!clock_input_prescaler_) {
    midi_handler.OnClock();
    
    if (song_pointer_) {
      ClockSong();
    } else {
      for (uint8_t i = 0; i < num_active_parts_; ++i) {
        part_[i].Clock();
      }
    }

    reset_flag_ = bar_position_ <= 0;
    ++bar_position_;
    if (bar_position_ >= settings_.clock_bar_duration * 24) {
      bar_position_ = 0;
    }
    if (settings_.clock_bar_duration > kMaxBarDuration) {
      // Send only one click on start.
      bar_position_ = 1;
    }
    
    ++clock_output_prescaler_;
    if (clock_output_prescaler_ >= output_division) {
      clock_output_prescaler_ = 0;
    }
  }

  ++clock_input_prescaler_;
  if (clock_input_prescaler_ >= settings_.clock_input_division) {
    clock_input_prescaler_ = 0;
  }
  
  if (stop_count_down_) {
    --stop_count_down_;
    if (!stop_count_down_ && started_by_keyboard_ && internal_clock()) {
      Stop();
    }
  }
}

void Multi::Start(bool started_by_keyboard) {
  if (running_ || recording_) {
    return;
  }
  if (internal_clock()) {
    internal_clock_ticks_ = 0;
    internal_clock_.Start(settings_.clock_tempo, settings_.clock_swing);
  }
  midi_handler.OnStart();

  started_by_keyboard_ = started_by_keyboard;
  running_ = true;
  latched_ = false;
  reset_flag_ = false;
  clock_input_prescaler_ = 0;
  clock_output_prescaler_ = 0;
  stop_count_down_ = 0;
  bar_position_ = 0;
  for (uint8_t i = 0; i < num_active_parts_; ++i) {
    part_[i].Start(started_by_keyboard);
  }
  song_pointer_ = NULL;
}

void Multi::Stop() {
  if (!running()) {
    return;
  }
  for (uint8_t i = 0; i < num_active_parts_; ++i) {
    part_[i].Stop();
  }
  midi_handler.OnStop();
  clock_pulse_duration_ = 0;
  stop_count_down_ = 0;
  running_ = false;
  latched_ = false;
  started_by_keyboard_ = false;
  song_pointer_ = NULL;
}

void Multi::Refresh() {
  if (clock_pulse_duration_) {
    --clock_pulse_duration_;
  }
  
  for (uint8_t i = 0; i < kNumVoices; ++i) {
    voice_[i].Refresh();
  }
}

void Multi::Set(uint8_t address, uint8_t value) {
  uint8_t* bytes;
  bytes = static_cast<uint8_t*>(static_cast<void*>(&settings_));
  uint8_t previous_value = bytes[address];
  bytes[address] = value;
  if (value != previous_value) {
    if (address == MULTI_LAYOUT) {
      ChangeLayout(
          static_cast<Layout>(previous_value),
          static_cast<Layout>(value));
    } else if (address == MULTI_CLOCK_TEMPO) {
      internal_clock_.set_tempo(settings_.clock_tempo);
    } else if (address == MULTI_CLOCK_SWING) {
      internal_clock_.set_swing(settings_.clock_swing);
    }
  }
}

void Multi::GetCvGate(uint16_t* cv, bool* gate) {
  switch (settings_.layout) {
    case LAYOUT_MONO:
    case LAYOUT_DUAL_POLYCHAINED:
      {
        cv[0] = voice_[0].note_dac_code();
        cv[1] = voice_[0].velocity_dac_code();
        cv[2] = voice_[0].modulation_dac_code();
        cv[3] = voice_[0].aux_cv_dac_code();
        gate[0] = voice_[0].gate();
        gate[1] = voice_[0].trigger();
        gate[2] = clock();
        gate[3] = reset_or_playing_flag();
      }
      break;
      
    case LAYOUT_DUAL_MONO:
      // {
      //   cv[0] = voice_[0].note_dac_code();
      //   cv[1] = voice_[1].note_dac_code();
      //   cv[2] = voice_[0].modulation_dac_code();
      //   cv[3] = voice_[1].modulation_dac_code();
      //   gate[0] = voice_[0].gate();
      //   gate[1] = voice_[1].gate();
      //   gate[2] = clock();
      //   gate[3] = reset_or_playing_flag();
      // }
      // break;
    
    case LAYOUT_DUAL_POLY:
    case LAYOUT_QUAD_POLYCHAINED:
      {
        cv[0] = voice_[0].note_dac_code();
        cv[1] = voice_[1].note_dac_code();
        cv[2] = (voice_[0].modulation_dac_code() >> 1) + \
            (voice_[1].modulation_dac_code() >> 1);
        cv[3] = (voice_[0].aux_cv_dac_code() >> 1) +
            (voice_[1].aux_cv_dac_code() >> 1);
        gate[0] = voice_[0].gate();
        gate[1] = voice_[1].gate();
        gate[2] = clock();
        gate[3] = reset_or_playing_flag();
      }
      break;
    
    case LAYOUT_QUAD_MONO:
    case LAYOUT_QUAD_POLY:
    case LAYOUT_OCTAL_POLYCHAINED:
    case LAYOUT_THREE_ONE:
      {
        cv[0] = voice_[0].note_dac_code();
        cv[1] = voice_[1].note_dac_code();
        cv[2] = voice_[2].note_dac_code();
        cv[3] = voice_[3].note_dac_code();
        gate[0] = voice_[0].gate();
        gate[1] = voice_[1].gate();
        if (settings_.clock_override) {
          gate[2] = clock();
          gate[3] = reset_or_playing_flag();
        } else {
          gate[2] = voice_[2].gate();
          gate[3] = voice_[3].gate();
        }
      }
      break;
    
    case LAYOUT_QUAD_TRIGGERS:
      {
        cv[0] = voice_[0].trigger_dac_code();
        cv[1] = voice_[1].trigger_dac_code();
        cv[2] = voice_[2].trigger_dac_code();
        cv[3] = voice_[3].trigger_dac_code();
        gate[0] = voice_[0].trigger() && ~voice_[1].gate();
        gate[1] = voice_[0].trigger() && voice_[1].gate();
        gate[2] = clock();
        gate[3] = reset_or_playing_flag();
      }
      break;
  }
}

bool Multi::GetAudioSource(uint8_t* audio_source) {
  bool has_audio_source = false;
  switch (settings_.layout) {
    case LAYOUT_MONO:
    case LAYOUT_DUAL_POLYCHAINED:
      {
        audio_source[0] = 0xff;
        audio_source[1] = 0xff;
        audio_source[2] = 0xff;
        audio_source[3] = voice_[0].audio_mode() ? 0 : 0xff;
        has_audio_source = voice_[0].audio_mode();
      }
      break;
      
    case LAYOUT_DUAL_MONO:
    case LAYOUT_DUAL_POLY:
    case LAYOUT_QUAD_POLYCHAINED:
      {
        audio_source[0] = 0xff;
        audio_source[1] = 0xff;
        audio_source[2] = voice_[0].audio_mode() ? 0 : 0xff;
        audio_source[3] = voice_[1].audio_mode() ? 1 : 0xff;
        has_audio_source = voice_[0].audio_mode() || voice_[1].audio_mode();
      }
      break;
      
    case LAYOUT_QUAD_MONO:
    case LAYOUT_QUAD_POLY:
    case LAYOUT_OCTAL_POLYCHAINED:
    case LAYOUT_THREE_ONE:
      {
        audio_source[0] = voice_[0].audio_mode() ? 0 : 0xff;
        audio_source[1] = voice_[1].audio_mode() ? 1 : 0xff;
        audio_source[2] = voice_[2].audio_mode() ? 2 : 0xff;
        audio_source[3] = voice_[3].audio_mode() ? 3 : 0xff;
        has_audio_source = voice_[0].audio_mode() || voice_[1].audio_mode() || \
            voice_[2].audio_mode() || voice_[3].audio_mode();
      }
      break;
    
    case LAYOUT_QUAD_TRIGGERS:
      {
        audio_source[0] = 0xff;
        audio_source[1] = 0xff;
        audio_source[2] = 0xff;
        audio_source[3] = 0xff;
        has_audio_source = false;
      }
      break;
  }
  return has_audio_source;
}

void Multi::GetLedsBrightness(uint8_t* brightness) {
  if (layout_configurator_.learning()) {
    fill(&brightness[0], &brightness[kNumVoices], 0);
    for (uint8_t i = 0; i < layout_configurator_.num_notes(); ++i) {
      brightness[i] = 255;
    }
    return;
  }
  
  switch (settings_.layout) {
    case LAYOUT_MONO:
    case LAYOUT_DUAL_POLYCHAINED:
      {
        brightness[0] = voice_[0].gate() ? 255 : 0;
        brightness[1] = voice_[0].velocity() << 1;
        brightness[2] = voice_[0].modulation() << 1;
        brightness[3] = voice_[0].aux_cv();
      }
      break;
      
    case LAYOUT_DUAL_MONO:
      // {
      //   brightness[0] = voice_[0].gate() ? 255 : 0;
      //   brightness[1] = voice_[1].gate() ? 255 : 0;
      //   brightness[2] = voice_[0].modulation();
      //   brightness[3] = voice_[1].modulation();
      // }
      // break;
      // 
    case LAYOUT_DUAL_POLY:
    case LAYOUT_QUAD_POLYCHAINED:
      {
        brightness[0] = voice_[0].gate() ? 255 : 0;
        brightness[1] = voice_[1].gate() ? 255 : 0;
        brightness[2] = voice_[0].modulation() + voice_[1].modulation();
        brightness[3] = (voice_[0].aux_cv() >> 1) + (voice_[1].aux_cv() >> 1);
      }
      break;
      
    case LAYOUT_QUAD_MONO:
    case LAYOUT_QUAD_POLY:
    case LAYOUT_OCTAL_POLYCHAINED:
    case LAYOUT_QUAD_TRIGGERS:
    case LAYOUT_THREE_ONE:
      {
        brightness[0] = voice_[0].gate() ? (voice_[0].velocity() << 1) : 0;
        brightness[1] = voice_[1].gate() ? (voice_[1].velocity() << 1) : 0;
        brightness[2] = voice_[2].gate() ? (voice_[2].velocity() << 1) : 0;
        brightness[3] = voice_[3].gate() ? (voice_[3].velocity() << 1) : 0;
      }
      break;
  }
}

void Multi::UpdateLayout() {
  // Reset and close all parts and voices.
  for (uint8_t i = 0; i < kNumParts; ++i) {
    part_[i].Reset();
  }
  for (uint8_t i = 0; i < kNumVoices; ++i) {
    voice_[i].NoteOff();
  }
  
  switch (settings_.layout) {
    case LAYOUT_MONO:
    case LAYOUT_DUAL_MONO:
    case LAYOUT_QUAD_MONO:
      {
        uint8_t num_parts = settings_.layout == LAYOUT_MONO ? 1 : \
            (settings_.layout == LAYOUT_DUAL_MONO ? 2 : 4);
        for (uint8_t i = 0; i < num_parts; ++i) {
          part_[i].AllocateVoices(&voice_[i], 1, false);
        }
        num_active_parts_ = num_parts;
      }
      break;
    
    case LAYOUT_DUAL_POLY:
    case LAYOUT_QUAD_POLY:
    case LAYOUT_DUAL_POLYCHAINED:
    case LAYOUT_QUAD_POLYCHAINED:
    case LAYOUT_OCTAL_POLYCHAINED:
      {
        uint8_t num_voices = settings_.layout == LAYOUT_DUAL_POLY || \
            settings_.layout == LAYOUT_QUAD_POLYCHAINED ? 2 : \
            (settings_.layout == LAYOUT_DUAL_POLYCHAINED ? 1 : 4);
        part_[0].AllocateVoices(
            &voice_[0],
            num_voices,
            settings_.layout >= LAYOUT_DUAL_POLYCHAINED);
        num_active_parts_ = 1;
      }
      break;
      
    case LAYOUT_QUAD_TRIGGERS:
      {
        for (uint8_t i = 0; i < 4; ++i) {
          part_[i].AllocateVoices(&voice_[i], 1, false);
        }
        num_active_parts_ = 4;
      }
      break;
      
    case LAYOUT_THREE_ONE:
      {
        part_[0].AllocateVoices(&voice_[0], 3, false);
        part_[1].AllocateVoices(&voice_[3], 1, false);
        num_active_parts_ = 2;
      }
      break;
    
    default:
      break;
  }
}

void Multi::ChangeLayout(Layout old_layout, Layout new_layout) {
  // Reset and close all parts and voices.
  for (uint8_t i = 0; i < kNumParts; ++i) {
    part_[i].Reset();
  }
  for (uint8_t i = 0; i < kNumVoices; ++i) {
    voice_[i].NoteOff();
  }
  
  switch (new_layout) {
    case LAYOUT_MONO:
    case LAYOUT_DUAL_MONO:
    case LAYOUT_QUAD_MONO:
      {
        uint8_t num_parts = new_layout == LAYOUT_MONO ? 1 : \
            (new_layout == LAYOUT_DUAL_MONO ? 2 : 4);

        for (uint8_t i = 0; i < num_parts; ++i) {
          MidiSettings* midi = part_[i].mutable_midi_settings();
          if (old_layout == LAYOUT_QUAD_TRIGGERS) {
            midi->min_note = 0;
            midi->max_note = 127;
          }
          midi->min_velocity = 0;
          midi->max_velocity = 127;

          VoicingSettings* voicing = part_[i].mutable_voicing_settings();
          voicing->allocation_mode = VOICE_ALLOCATION_MODE_MONO;
          voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST;
        }
        
        // Duplicate uninitialized voices.
        for (uint8_t i = 1; i < num_parts; ++i) {
          uint8_t destination = i;
          uint8_t source = i % num_active_parts_;
          if (destination != source) {
            memcpy(
                part_[destination].mutable_midi_settings(),
                part_[source].mutable_midi_settings(),
                sizeof(MidiSettings));
            memcpy(
                part_[destination].mutable_voicing_settings(),
                part_[source].mutable_voicing_settings(),
                sizeof(VoicingSettings));
            memcpy(
                part_[destination].mutable_sequencer_settings(),
                part_[source].mutable_sequencer_settings(),
                sizeof(SequencerSettings));
          }
        }
        
        for (uint8_t i = 0; i < num_parts; ++i) {
          part_[i].AllocateVoices(&voice_[i], 1, false);
        }
        num_active_parts_ = num_parts;
      }
      break;
    
    case LAYOUT_DUAL_POLY:
    case LAYOUT_QUAD_POLY:
    case LAYOUT_DUAL_POLYCHAINED:
    case LAYOUT_QUAD_POLYCHAINED:
    case LAYOUT_OCTAL_POLYCHAINED:
      {
        uint8_t num_voices = settings_.layout == LAYOUT_DUAL_POLY || \
            settings_.layout == LAYOUT_QUAD_POLYCHAINED ? 2 : \
            (settings_.layout == LAYOUT_DUAL_POLYCHAINED ? 1 : 4);
        
        MidiSettings* midi = part_[0].mutable_midi_settings();
        if (old_layout == LAYOUT_QUAD_TRIGGERS) {
          midi->min_note = 0;
          midi->max_note = 127;
        }
        midi->min_velocity = 0;
        midi->max_velocity = 127;

        VoicingSettings* voicing = part_[0].mutable_voicing_settings();
        voicing->allocation_mode = VOICE_ALLOCATION_MODE_POLY;
        voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST;
        voicing->portamento = 0;
        voicing->legato_mode = 0;
        
        part_[0].AllocateVoices(
            &voice_[0],
            num_voices,
            new_layout >= LAYOUT_DUAL_POLYCHAINED);
        num_active_parts_ = 1;
      }
      break;
      
    case LAYOUT_QUAD_TRIGGERS:
      {
        for (uint8_t i = 0; i < 4; ++i) {
          MidiSettings* midi = part_[i].mutable_midi_settings();
          if (old_layout != LAYOUT_QUAD_TRIGGERS) {
            midi->min_note = 36 + i * 2;
            midi->max_note = 36 + i * 2;
          }
          midi->min_velocity = 0;
          midi->max_velocity = 127;
          midi->channel = part_[0].mutable_midi_settings()->channel;
          midi->out_mode = part_[0].mutable_midi_settings()->out_mode;
          
          VoicingSettings* voicing = part_[i].mutable_voicing_settings();
          voicing->allocation_mode = VOICE_ALLOCATION_MODE_MONO;
          voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST;
          voicing->portamento = 0;
          voicing->legato_mode = 0;
        }
        
        // Duplicate sequencer settings.
        for (uint8_t i = 1; i < 4; ++i) {
          uint8_t destination = i;
          uint8_t source = i % num_active_parts_;
          if (destination != source) {
            memcpy(
                part_[destination].mutable_sequencer_settings(),
                part_[source].mutable_sequencer_settings(),
                sizeof(SequencerSettings));
          }
        }
        
        for (uint8_t i = 0; i < 4; ++i) {
          part_[i].AllocateVoices(&voice_[i], 1, false);
        }
        num_active_parts_ = 4;
      }
      break;
    
    case LAYOUT_THREE_ONE:
      {
        MidiSettings* midi = part_[0].mutable_midi_settings();
        if (old_layout == LAYOUT_QUAD_TRIGGERS) {
          midi->min_note = 0;
          midi->max_note = 127;
        }
        midi->min_velocity = 0;
        midi->max_velocity = 127;

        VoicingSettings* voicing = part_[0].mutable_voicing_settings();
        voicing->allocation_mode = VOICE_ALLOCATION_MODE_POLY;
        voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST;
        voicing->portamento = 0;
        voicing->legato_mode = 0;
        part_[0].AllocateVoices(&voice_[0], 3, false);

        midi = part_[1].mutable_midi_settings();
        if (old_layout == LAYOUT_QUAD_TRIGGERS) {
          midi->min_note = 0;
          midi->max_note = 127;
        }
        midi->min_velocity = 0;
        midi->max_velocity = 127;

        voicing = part_[1].mutable_voicing_settings();
        voicing->allocation_mode = VOICE_ALLOCATION_MODE_MONO;
        voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST;
        voicing->portamento = 0;
        voicing->legato_mode = 0;
        part_[1].AllocateVoices(&voice_[3], 1, false);

        num_active_parts_ = 2;
      }
      break;
    
    default:
      break;
  }
  
  for (uint8_t i = 0; i < num_active_parts_; ++i) {
    part_[i].set_siblings(num_active_parts_ > 1);
  }
}

void Multi::Touch() {
  Stop();

  internal_clock_.set_tempo(settings_.clock_tempo);
  UpdateLayout();
  
  for (uint8_t i = 0; i < kNumParts; ++i) {
    part_[i].Touch();
  }
}


const uint8_t song[] = {
  #include "song/song.h"
  255,
};

void Multi::StartSong() {
  Set(MULTI_LAYOUT, LAYOUT_QUAD_MONO);
  part_[0].mutable_voicing_settings()->audio_mode = 0x83;
  part_[1].mutable_voicing_settings()->audio_mode = 0x83;
  part_[2].mutable_voicing_settings()->audio_mode = 0x84;
  part_[3].mutable_voicing_settings()->audio_mode = 0x86;
  UpdateLayout();
  settings_.clock_tempo = 140;
  Stop();
  Start(false);
  
  song_pointer_ = &song[0];
  song_clock_ = 0;
  song_delta_ = 0;
}

void Multi::ClockSong() {
  while (song_clock_ >= song_delta_) {
    if (*song_pointer_ == 255) {
      song_pointer_ = &song[0];
    }
    if (*song_pointer_ == 254) {
      song_delta_ += 6;
    } else {
      uint8_t part = *song_pointer_ >> 6;
      uint8_t note = *song_pointer_ & 0x3f;
      if (note == 0) {
        part_[part].AllNotesOff(0);
      } else {
        part_[part].NoteOn(0, note + 24, 100);
      }
      song_clock_ = 0;
      song_delta_ = 0;
    }
    ++song_pointer_;
  }
  ++song_clock_;
}

bool Multi::ControlChange(uint8_t channel, uint8_t controller, uint8_t value) {
  bool thru = true;
  
  if (channel + 1 == settings_.remote_control_channel) {
    yarns::settings.SetFromCC(0xff, controller, value);
  }
  
  for (uint8_t i = 0; i < num_active_parts_; ++i) {
    if (part_[i].accepts(channel) && \
        channel + 1 != settings_.remote_control_channel) {
      thru = part_[i].ControlChange(channel, controller, value) && thru;
      yarns::settings.SetFromCC(i, controller, value);
    }
  }
  return thru;
}


/* extern */
Multi multi;

}  // namespace yarns
