Note Functions


#----------------------------------------------------------------------
#expFade (exponential Fade)
#sound   - The sound to fade
#factor  - The factor that the sound should be reduced by.
#returns - A new sound that is gradually reduced in volume
#          by the desired factor
#----------------------------------------------------------------------
def expFade(sound, factor):
  ns = getNumSamples(sound)
  newSound = duplicateSound(sound)
  for i in range(ns):
    val = getSampleValueAt(sound, i)
    newVal = val * pow(exp(1), -(float(i)/ns * log(factor)))
    setSampleValueAt(newSound, i, newVal)
  return newSound

#----------------------------------------------------------------------
#returnTone
#freq - frequency in hertz
#dur - duration in milliseconds
#vol - volume from 0-127
#sampleRate - the sampling rate of the returned sound
#returns - a sine wave sound of the desired frequency.
#----------------------------------------------------------------------
def returnTone(freq, dur, vol, sampleRate = 22050):
  numSamples = int(dur/1000.0 * sampleRate)
  sound = makeEmptySound(numSamples, sampleRate)
  volFactor = vol / 127.0 * 32767
  for i in range(0, numSamples):
    val = sin(2 * pi * freq * i * 1.0/sampleRate)
    setSampleValueAt(sound, i, val * volFactor) 
  return sound

#----------------------------------------------------------------------
#returnSquareTone
#freq - frequency in hertz
#dur - duration in milliseconds
#vol - volume from 0-127
#sampleRate - the sampling rate of the returned sound
#returns - a square wave sound of the desired frequency
#----------------------------------------------------------------------
def returnSquareTone(freq, dur, vol, sampleRate = 22050):
  numSamples = int(dur/1000.0 * sampleRate)
  sound = makeEmptySound(numSamples, sampleRate)
  period = 1.0/freq * sampleRate
  volFactor = vol / 127.0 * 32767
  for i in range(0, numSamples):
    if (i % period) < (.5 * period):
      val = -1 * volFactor
    else:
      val = 1 * volFactor
    setSampleValueAt(sound, i, val) 
  return sound


#----------------------------------------------------------------------
#returnNote
#note - Note in MIDI encoding
#dur - duration in milliseconds
#vol - volume from 0-127
#returns - The desired note as a sine wave.
#----------------------------------------------------------------------
def returnNote(note, dur, vol, sampleRate = 22050):
  f = 8.1758 * 2**(note / 12.0)  # midi to frequency conversion
  return expFade(returnTone(f, dur, vol, sampleRate), 10)

#----------------------------------------------------------------------
#returnSquareNote
#note - Note in MIDI encoding
#dur - duration in milliseconds
#vol - volume from 0-127
#returns - The desired note as a square wave.
#----------------------------------------------------------------------
def returnSquareNote(note, dur, vol):
  f = 8.1758 * 2**(note / 12.0)  # midi to frequency conversion
  return expFade(returnSquareTone(f, dur, vol), 10)

#----------------------------------------------------------------------
#returnNotes - Return a series of musical notes at a given volume. 
#notes - an array of integers: [note#1, dur#1, note#2, dur#2...] 
#volume - a volume from 1-127
#returns - a single sound containing all of the requested notes
#----------------------------------------------------------------------
def returnNotes(notes, vol):
  newSound = makeEmptySound(1)
  for noteNum in range(0, len(notes), 2):
    n = returnNote(notes[noteNum],notes[noteNum+1], vol)
    newSound = appendSound(newSound, n)
  return newSound     

#----------------------------------------------------------------------
#returnSquareNotes - Return a series of musical notes at a given volume. 
#notes - An array of integers: [note#1, dur#1, note#2, dur#2...] 
#volume - A volume from 1-127
#returns - A single sound containing all of the requested notes
#----------------------------------------------------------------------
def returnSquareNotes(notes, vol):
  newSound = makeEmptySound(1)
  for noteNum in range(0, len(notes), 2):
    n = returnSquareNote(notes[noteNum],notes[noteNum+1], vol)
    newSound = appendSound(newSound, n)
  return newSound     

#----------------------------------------------------------------------
#appendSound
#s1, s2 - sounds
#returns - A new sound: s2 appended to the end of s1. 
#----------------------------------------------------------------------
def appendSound(s1, s2):
  s1NumSamples = getNumSamples(s1)
  s2NumSamples = getNumSamples(s2)
  newSound = makeEmptySound(s1NumSamples+s2NumSamples, int(getSamplingRate(s1)))
  s1.copySoundInto(newSound, 0)
  s2.copySoundInto(newSound, s1NumSamples)
  return newSound


#----------------------------------------------------------------------
#appendSounds
#sounds - a list of sounds
#returns - A single sound created by appending all 
#          of the individual sounds in order.
#----------------------------------------------------------------------
def appendSounds(sounds):
  newSound = makeEmptySound(1, int(getSamplingRate(sounds[0])))
  for s in sounds:
    newSound = appendSound(newSound, s)
  return newSound