/examples/cowbell/cowbell.py

http://echo-nest-remix.googlecode.com/ · Python · 113 lines · 89 code · 14 blank · 10 comment · 18 complexity · 01907b65806cc84b5a2361c1fdb6a786 MD5 · raw file

  1. # By Rob Ochshorn and Adam Baratz.
  2. # Slightly refactored by Joshua Lifton.
  3. import numpy
  4. import os
  5. import random
  6. import time
  7. import echonest.audio as audio
  8. usage = """
  9. Usage:
  10. python cowbell.py <inputFilename> <outputFilename> <cowbellIntensity> <walkenIntensity>
  11. Example:
  12. python cowbell.py YouCanCallMeAl.mp3 YouCanCallMeCow.mp3 0.2 0.5
  13. Reference:
  14. http://www.youtube.com/watch?v=ZhSkRHXTKlw
  15. """
  16. # constants
  17. COWBELL_THRESHOLD = 0.85
  18. COWBELL_OFFSET = -0.005
  19. # samples
  20. soundsPath = "sounds/"
  21. cowbellSounds = map(lambda x: audio.AudioData(os.path.join(soundsPath, "cowbell%s.wav" % x), sampleRate=44100, numChannels=2), range(5))
  22. walkenSounds = map(lambda x: audio.AudioData(os.path.join(soundsPath, "walken%s.wav" % x), sampleRate=44100, numChannels=2), range(16))
  23. trill = audio.AudioData(os.path.join(soundsPath, "trill.wav"), sampleRate=44100, numChannels=2)
  24. def linear(input, in1, in2, out1, out2):
  25. return ((input-in1) / (in2-in1)) * (out2-out1) + out1
  26. def exp(input, in1, in2, out1, out2, coeff):
  27. if (input <= in1):
  28. return out1
  29. if (input >= in2):
  30. return out2
  31. return pow( ((input-in1) / (in2-in1)) , coeff ) * (out2-out1) + out1
  32. class Cowbell:
  33. def __init__(self, input_file):
  34. self.audiofile = audio.LocalAudioFile(input_file)
  35. self.audiofile.data *= linear(self.audiofile.analysis.loudness, -2, -12, 0.5, 1.5) * 0.75
  36. def run(self, cowbell_intensity, walken_intensity, out):
  37. if cowbell_intensity != -1:
  38. self.cowbell_intensity = cowbell_intensity
  39. self.walken_intensity = walken_intensity
  40. t1 = time.time()
  41. sequence = self.sequence(cowbellSounds)
  42. print "Sequence and mixed in %g seconds" % (time.time() - t1)
  43. self.audiofile.encode(out)
  44. def sequence(self, chops):
  45. # add cowbells on the beats
  46. for beat in self.audiofile.analysis.beats:
  47. volume = linear(self.cowbell_intensity, 0, 1, 0.1, 0.3)
  48. # mix in cowbell on beat
  49. if self.cowbell_intensity == 1:
  50. self.mix(beat.start+COWBELL_OFFSET, seg=cowbellSounds[random.randint(0,1)], volume=volume)
  51. else:
  52. self.mix(beat.start+COWBELL_OFFSET, seg=cowbellSounds[random.randint(2,4)], volume=volume)
  53. # divide beat into quarters
  54. quarters = (numpy.arange(1,4) * beat.duration) / 4. + beat.start
  55. # mix in cowbell on quarters
  56. for quarter in quarters:
  57. volume = exp(random.random(), 0.5, 0.1, 0, self.cowbell_intensity, 0.8) * 0.3
  58. pan = linear(random.random(), 0, 1, -self.cowbell_intensity, self.cowbell_intensity)
  59. if self.cowbell_intensity < COWBELL_THRESHOLD:
  60. self.mix(quarter+COWBELL_OFFSET, seg=cowbellSounds[2], volume=volume)
  61. else:
  62. randomCowbell = linear(random.random(), 0, 1, COWBELL_THRESHOLD, 1)
  63. if randomCowbell < self.cowbell_intensity:
  64. self.mix(start=quarter+COWBELL_OFFSET, seg=cowbellSounds[random.randint(0,1)], volume=volume)
  65. else:
  66. self.mix(start=quarter+COWBELL_OFFSET, seg=cowbellSounds[random.randint(2,4)], volume=volume)
  67. # add trills / walken on section changes
  68. for section in self.audiofile.analysis.sections[1:]:
  69. if random.random() > self.walken_intensity:
  70. sample = trill
  71. volume = 0.3
  72. else:
  73. sample = walkenSounds[random.randint(0, len(walkenSounds)-1)]
  74. volume = 1.5
  75. self.mix(start=section.start+COWBELL_OFFSET, seg=sample, volume=volume)
  76. def mix(self, start=None, seg=None, volume=0.3, pan=0.):
  77. # this assumes that the audios have the same frequency/numchannels
  78. startsample = int(start * self.audiofile.sampleRate)
  79. seg = seg[0:]
  80. seg.data *= (volume-(pan*volume), volume+(pan*volume)) # pan + volume
  81. if self.audiofile.data.shape[0] - startsample > seg.data.shape[0]:
  82. self.audiofile.data[startsample:startsample+len(seg.data)] += seg.data[0:]
  83. def main(inputFilename, outputFilename, cowbellIntensity, walkenIntensity ) :
  84. c = Cowbell(inputFilename)
  85. print 'cowbelling...'
  86. c.run(cowbellIntensity, walkenIntensity, outputFilename)
  87. if __name__ == '__main__':
  88. import sys
  89. try :
  90. inputFilename = sys.argv[1]
  91. outputFilename = sys.argv[2]
  92. cowbellIntensity = float(sys.argv[3])
  93. walkenIntensity = float(sys.argv[4])
  94. except :
  95. print usage
  96. sys.exit(-1)
  97. main(inputFilename, outputFilename, cowbellIntensity, walkenIntensity)