#
# lfo.py - Lfo tab conversion objects
#
# Copyright (c) 2006,2007 Matt Gerassimoff
#
# This file is part of g2ools.
#
# g2ools is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# g2ools is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Foobar; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
from nord.utils import setv, getv, isnm1osc
from nord.nm1.colors import nm1conncolors
from nord.convert import Convert
from nord.convert.convert import handleoscmasterslv, handlekbt, doslvcables
from nord.convert.table import modtable

def handleslv(conv, ratemodin, ratemodparam):
  nmm, g2m = conv.nmmodule, conv.g2module
  nmmp, g2mp = nmm.params, g2m.params

  slv, kbt = None, g2m.inputs.Rate

  if len(nmm.outputs.Slv.cables):
    oscmaster = conv.add_module('OscMaster')
    setv(g2mp.Rate, 64)
    setv(oscmaster.params.Kbt, 0) # Off
    setv(oscmaster.params.FreqCoarse, getv(nmmp.Rate))
    #setv(oscmaster.params.PitchMod, modtable[getv(nmmp.RateMod)][0])
    conv.connect(oscmaster.outputs.Out, g2m.inputs.Rate)
    ratemodin = oscmaster.inputs.PitchVar
    ratemodparam = oscmaster.params.PitchMod
    slv = g2m.inputs.Rate
    kbt = oscmaster.inputs.Pitch
    conv.kbt = oscmaster.params.Kbt

    if getv(nmmp.Range) == 1: # Lo
      slv = handleoscmasterslv(conv, oscmaster, 64, 40, 50, 103, 41, True)
    else:
      slv = handleoscmasterslv(conv, oscmaster, 76, 64, 52, 104, 35, False)

  # add fine tuning 
  if len(nmm.inputs.Rate.cables):
    mod = getv(nmmp.RateMod)
    if mod == 0 or mod == 127:
      setv(ratemodparam, mod)
    else:
      setv(ratemodparam, modtable[mod][0])
      adj = conv.add_module('Mix2-1B', name='PitchAdj')
      conv.connect(adj.outputs.Out, ratemodin)
      conv.connect(adj.inputs.Chain, adj.inputs.In1)
      conv.connect(adj.inputs.In1, adj.inputs.In2)
      setv(adj.params.Inv1, 1)
      setv(adj.params.Lev1, modtable[mod][1])
      setv(adj.params.Lev2, modtable[mod][2])
      ratemodin = adj.inputs.Chain

  return ratemodin, ratemodparam, slv, kbt

def postmst(conv, mstindex):
  nmm, g2m = conv.nmmodule, conv.g2module
  nmmp, g2mp = nmm.params, g2m.params

  mstin = nmm.inputs.Mst
  if not len(mstin.cables):
    return

  if not mstin.net.output:
    return

  mstconv = mstin.net.output.module.conv
  mst = mstconv.g2module
  if hasattr(mst.params, 'PolyMono'):
    setv(g2mp.PolyMono, getv(mst.params.PolyMono))
  if hasattr(mst.params, 'Kbt') and hasattr(g2mp, 'Kbt'):
    setv(g2mp.Kbt, getv(mst.params.Kbt))

  if mstin.net.output.rate != nm1conncolors.slave:
    oscc = conv.add_module('OscC', name='')
    setv(oscc.params.FreqCoarse, 0)
    setv(oscc.params.FmAmount, 79)
    setv(oscc.params.Kbt, 0)
    pout = conv.add_module('ZeroCnt', name='')
    conv.connect(oscc.outputs.Out, pout.inputs.In)
    conv.connect(pout.outputs.Out, g2m.inputs.Rate)
    setv(g2mp.Range, 2)
    conv.inputs[mstindex] = oscc.inputs.FmMod
    return

  if isnm1osc(mst):
    setv(g2mp.Range, 2)
  elif hasattr(mst.params, 'Range'):
    setv(g2mp.Range, getv(mst.params.Range))
  else:
    setv(g2mp.Range, 1)

class ConvLFOA(Convert):
  maing2module = 'LfoB'
  parammap = ['Rate', 'Range', 'Waveform', 'RateMod', ['PolyMono', 'Mono'],
              None, 'Phase', ['Active', 'Mute']]
  inputmap = ['Rate', 'Rst']
  outputmap = ['', 'Out'] # Slv

  def domodule(self):
    nmm, g2m = self.nmmodule, self.g2module
    nmmp, g2mp = nmm.params, g2m.params

    # handle special parameters
    waveform = getv(nmmp.Waveform)
    setv(g2mp.Waveform, [0, 1, 2, 2, 3][waveform])
    if waveform != 3:
      setv(g2mp.OutputType, 5) # BipInv
    else:
      # 180 phase
      setv(g2mp.Phase, (range(64, 128)+range(64))[getv(nmmp.Phase)])
    setv(g2mp.Active, 1-getv(nmmp.Mute))

    self.kbt = g2m.params.Kbt
    # update Rate input, Slv Output
    ratemodin, rateparam, slv, kbt = handleslv(self,
        g2m.inputs.RateVar, g2mp.RateMod)
    self.inputs[0], self.outputs[0], kbt = ratemodin, slv, kbt
    self.kbtout = handlekbt(self, kbt, 4, False)

  def precables(self):
    doslvcables(self)

class ConvLFOB(Convert):
  maing2module = 'LfoShpA'
  parammap = ['Rate', 'Range', 'Phase', 'RateMod', ['PolyMono', 'Mono'],
              None, ['PhaseMod', 'PwMod'], ['Shape', 'Pw']]
  inputmap = ['Rate', 'Rst', 'ShapeMod']
  outputmap = ['Out', ''] # Slv

  def domodule(self):
    nmm, g2m = self.nmmodule, self.g2module
    nmmp, g2mp = nmm.params, g2m.params

    setv(g2mp.Waveform, 5)
    setv(g2mp.OutputType, 5) # BipInv
    setv(g2mp.PhaseMod, getv(nmmp.PwMod))

    self.kbt = g2m.params.Kbt

    ratemodin, rateparam, slv, kbt = handleslv(self,
        g2m.inputs.RateVar, g2mp.RateMod)
    self.inputs[0], self.outputs[1], kbt = ratemodin, slv, kbt
    self.kbtout = handlekbt(self, kbt, 4, False)

  def precables(self):
    doslvcables(self)

class ConvLFOC(Convert):
  maing2module = 'LfoA'
  parammap = ['Rate', 'Range', 'Waveform', 'RateMod', ['PolyMono', 'Mono'],
              ['Active', 'Mute']]
  inputmap = ['RateVar']
  outputmap = ['Out', '']

  def domodule(self):
    nmm, g2m = self.nmmodule, self.g2module
    nmmp, g2mp = nmm.params, g2m.params

    waveform = getv(nmmp.Waveform)
    setv(g2mp.Waveform, [0, 1, 2, 2, 3][waveform])
    if waveform != 3:
      setv(g2mp.OutputType, 5) # BipInv
    setv(g2mp.Active, 1-getv(nmmp.Mute))

    self.kbt = g2m.params.Kbt
    ratemodin, rateparam, slv, kbt = handleslv(self,
        g2m.inputs.RateVar, g2mp.RateMod)
    self.inputs[0], self.outputs[1], kbt = ratemodin, slv, kbt

  def precables(self):
    doslvcables(self)

class ConvLFOSlvA(Convert):
  maing2module = 'LfoB'
  parammap = ['Rate', 'Phase', 'Waveform', ['PolyMono', 'Mono'],
      ['Active', 'Mute']]
  inputmap = ['Rate', 'Rst']
  outputmap = ['Out']

  def domodule(self):
    nmm, g2m = self.nmmodule, self.g2module
    nmmp, g2mp = nmm.params, g2m.params

    # handle special parameters
    waveform = getv(nmmp.Waveform)
    setv(g2mp.Waveform, [0, 1, 2, 2, 3][waveform])
    if waveform != 3:
      setv(g2mp.OutputType, 5) # BipInv
    else:
      # 180 phase
      setv(g2mp.Phase, (range(64, 128)+range(64))[getv(nmmp.Phase)])
    setv(g2mp.Active, 1-getv(nmmp.Mute))

    postmst(self, 0)

class ConvLFOSlvB(Convert):
  maing2module = 'LfoC'
  waveform = 2
  parammap = ['Rate']
  inputmap = ['Rate']
  outputmap = ['Out']

  def domodule(self):
    nmm, g2m = self.nmmodule, self.g2module
    nmmp, g2mp = nmm.params, g2m.params

    # handle special parameters
    g2m.modes.Waveform.value = self.waveform
    if self.waveform != 2:
      setv(g2mp.OutputType, 4) # Bip
    else:
      setv(g2mp.OutputType, 5) # BipInv

    postmst(self, 0)

class ConvLFOSlvC(ConvLFOSlvB):
  waveform = 0

  #3phase thinks we may need this.  I'm leaving it as a comment for now.
  #def domodule(self):
  #  ConvLFOSlvB.domodule(self)
  #  setv(self.g2module.params.OutputType, 5) # BipInv

class ConvLFOSlvD(ConvLFOSlvB):
  waveform = 3

class ConvLFOSlvE(ConvLFOSlvC):
  waveform = 1

class ConvClkGen(Convert):
  maing2module = 'ClkGen'
  parammap = ['Rate', ['Active', 'On/Off']]
  inputmap = ['Rst']
  outputmap = ['1/96', '1/16', '', 'Sync']

  def domodule(self):
    nmm, g2m = self.nmmodule, self.g2module
    nmmp, g2mp = nmm.params, g2m.params

    setv(g2mp.Active, getv(getattr(nmmp, 'On/Off')))
    setv(g2mp.Source, 0)  # Internal
    if len(nmm.outputs.Sync.cables) != 0:
      pulse = self.add_module('Pulse')
      setv(pulse.params.Time, 32)
      self.connect(g2m.outputs.ClkActive, pulse.inputs.In)
      self.outputs[3] = pulse.outputs.Out
    
    #handle Slv connections
    if len(nmm.outputs.Slv.cables):
      zerocnt = self.add_module('ZeroCnt', name='96th In')
      oscmaster = self.add_module('OscMaster', name='26-241 BPM')
      setv(oscmaster.params.FreqCoarse, 9) # -55 semi
      setv(oscmaster.params.Kbt, 0) # off
      self.connect(getattr(g2m.outputs, '1/96'), zerocnt.inputs.In)
      self.connect(zerocnt.outputs.Out, oscmaster.inputs.Pitch)
      self.outputs[2] = oscmaster.outputs.Out

class ConvClkRndGen(Convert):
  maing2module = 'RndClkA'
  parammap = [['PolyMono', 'Mono'], ['StepProb', 'Color']]
  inputmap = ['Clk']
  outputmap = ['Out']

  def domodule(self):
    nmm, g2m = self.nmmodule, self.g2module
    nmmp, g2mp = nmm.params, g2m.params

    if getv(nmmp.Color) == 1:
      setv(g2mp.StepProb, 43)
    else:
      setv(g2mp.StepProb, 127)

class ConvRndStepGen(ConvLFOSlvB):
  waveform = 4

class ConvRandomGen(ConvLFOSlvB):
  waveform = 5

class ConvRndPulseGen(Convert):
  maing2module = 'RndTrig'
  parammap = [['StepProb', 'Density']]
  outputmap = ['Out']

  def domodule(self):
    nmm, g2m = self.nmmodule, self.g2module
    nmmp, g2mp = nmm.params, g2m.params

    setv(g2mp.StepProb, 96)
    lfoc = self.add_module('LfoC', name='Clk')
    self.connect(lfoc.outputs.Out, g2m.inputs.Clk)
    setv(lfoc.params.Rate, getv(nmmp.Density))
    self.params[0] = lfoc.params.Rate

class ConvPatternGen(Convert):
  maing2module = 'RndPattern'
  parammap = [['PatternA', 'Pattern'], ['PatternB', 'Bank'],
              ['StepProb', 'LowDelta'], ['LoopCount', 'Step'],
              None]
  inputmap = ['Clk', 'Rst', 'A']
  outputmap = ['Out']

  def domodule(self):
    nmm, g2m = self.nmmodule, self.g2module
    nmmp, g2mp = nmm.params, g2m.params

    pattern = (getv(nmmp.Pattern) + 64) % 128
    setv(g2mp.PatternA, pattern)
    bank = (getv(nmmp.Bank) + 64) % 128
    setv(g2mp.PatternB, bank)

    # PatternA and PatternB receive same input
    if len(getattr(nmm.inputs, 'Pattern&Bank').cables):
      self.connect(g2m.inputs.A, g2m.inputs.B) 
    lowdelta = getv(nmmp.LowDelta)
    if lowdelta:
      notequant = self.add_module('NoteQuant')
      self.connect(g2m.outputs.Out, notequant.inputs.In)
      setv(notequant.params.Range, 77)
      setv(notequant.params.Notes, 1)
      self.outputs[0] = notequant.outputs.Out
      stepprob, add = 55, 75
      setv(g2mp.StepProb, 55)
    else:
      stepprob, add = 127, 74
    setv(g2mp.StepProb, stepprob)
    levadd = self.add_module('LevAdd')
    self.connect(self.outputs[0], levadd.inputs.In)
    setv(levadd.params.Level, add)
    self.outputs[0] = levadd.outputs.Out

