PageRenderTime 45ms CodeModel.GetById 23ms app.highlight 17ms RepoModel.GetById 2ms app.codeStats 0ms

/src/echonest/modify.py

http://echo-nest-remix.googlecode.com/
Python | 109 lines | 88 code | 11 blank | 10 comment | 50 complexity | d9e865ed7e3a1c4ae0ece1ddceb215ea MD5 | raw file
  1#!/usr/bin/env python
  2# encoding: utf-8
  3"""
  4modify.py
  5
  6Created by Ben Lacker on 2009-06-12.
  7"""
  8from echonest.audio import *
  9import numpy
 10import soundtouch
 11
 12class Modify(soundtouch.SoundTouch):
 13    def __init__(self, sampleRate=44100, numChannels=1, blockSize = 10000):
 14        self.setSampleRate(sampleRate)
 15        self.setChannels(numChannels)
 16        self.sampleRate = sampleRate
 17        self.numChannels = numChannels
 18        self.blockSize = blockSize
 19
 20    def doInBlocks(self, f, in_data, arg):
 21        # For now, make everything mono. We'll deal with channels later
 22        if in_data.ndim > 1:
 23            in_data = in_data[:, 0]
 24        collect = []
 25        if len(in_data) > self.blockSize:
 26            for x in range(len(in_data)/self.blockSize):
 27                start = x * self.blockSize
 28                data = in_data[start:start + self.blockSize -1]
 29                collect.append(self.processAudio(f, data, arg))
 30            data = in_data[-1*(len(in_data) % self.blockSize):]
 31            collect.append(self.processAudio(f, data, arg))
 32        else:
 33            collect.append(self.processAudio(f, in_data, arg))
 34        return assemble(collect, numChannels=self.numChannels, sampleRate=self.sampleRate)
 35
 36    def processAudio(self, f, data, arg):
 37        f(arg)
 38        self.putSamples(data)
 39        out_data = numpy.array(numpy.zeros((len(data)*2,), dtype=numpy.float32))
 40        out_samples = self.receiveSamples(out_data)
 41        new_ad = AudioData(ndarray=out_data[:out_samples], shape=(out_samples, ), 
 42                    sampleRate=self.sampleRate, numChannels=self.numChannels)
 43        return new_ad
 44
 45    def shiftRate(self, audio_data, ratio=1):
 46        if not isinstance(audio_data, AudioData):
 47            raise TypeError('First argument must be an AudioData object.')
 48        if not (isinstance(ratio, int) or isinstance(ratio, float)):
 49            raise ValueError('Ratio must be an int or float.')
 50        if (ratio < 0) or (ratio > 10):
 51            raise ValueError('Ratio must be between 0 and 10.')
 52        return self.doInBlocks(self.setRate, audio_data.data, ratio)
 53
 54    def shiftTempo(self, audio_data, ratio):
 55        if not isinstance(audio_data, AudioData):
 56            raise TypeError('First argument must be an AudioData object.')
 57        if not (isinstance(ratio, int) or isinstance(ratio, float)):
 58            raise ValueError('Ratio must be an int or float.')
 59        if (ratio < 0) or (ratio > 10):
 60            raise ValueError('Ratio must be between 0 and 10.')
 61        return self.doInBlocks(self.setTempo, audio_data.data, ratio)
 62
 63    def shiftRateChange(self, audio_data, percent):
 64        if not isinstance(audio_data, AudioData):
 65            raise TypeError('First argument must be an AudioData object.')
 66        if not (isinstance(percent, int) or isinstance(percent, float)):
 67            raise ValueError('Percent must be an int or float.')
 68        if (percent < -50) or (percent > 100):
 69            raise ValueError('Percent must be between -50 and 100.')
 70        return self.doInBlocks(self.setRateChange, audio_data.data, percent)
 71
 72    def shiftTempoChange(self, audio_data, percent):
 73        if not isinstance(audio_data, AudioData):
 74            raise TypeError('First argument must be an AudioData object.')
 75        if not (isinstance(percent, int) or isinstance(percent, float)):
 76            raise ValueError('Percent must be an int or float.')
 77        if (percent < -50) or (percent > 100):
 78            raise ValueError('Percent must be between -50 and 100.')
 79        return self.doInBlocks(self.setTempoChange, audio_data.data, percent)
 80
 81    def shiftPitchSemiTones(self, audio_data, semitones=0):
 82        if not isinstance(audio_data, AudioData):
 83            raise TypeError('First argument must be an AudioData object.')
 84        if not isinstance(semitones, int):
 85            raise TypeError('Second argument must be an integer.')
 86        # I think this is right, but maybe it has to be between -12 and 12?
 87        if abs(semitones) > 60:
 88            raise ValueError('Semitones argument must be an int between -60 and 60.')
 89        return self.doInBlocks(self.setPitchSemiTones, audio_data.data, semitones)
 90
 91    def shiftPitchOctaves(self, audio_data, octaves=0):
 92        if not isinstance(audio_data, AudioData):
 93            raise TypeError('First argument must be an AudioData object.')
 94        if not (isinstance(octaves, int) or isinstance(octaves, float)):
 95            raise ValueError('Octaves must be an int or float.')
 96        if abs(octaves) > 5:
 97            raise ValueError('Octaves argument must be between -5 and 5.')
 98        # what are the limits? Nothing in soundtouch documentation...
 99        return self.doInBlocks(self.setPitchOctaves, audio_data.data, octaves)
100    
101    def shiftPitch(self, audio_data, ratio=1):
102        if not isinstance(audio_data, AudioData):
103            raise TypeError('First argument must be an AudioData object.')
104        if not (isinstance(ratio, int) or isinstance(ratio, float)):
105            raise ValueError('Ratio must be an int or float.')
106        if (ratio < 0) or (ratio > 10):
107            raise ValueError('Ratio must be between 0 and 10.')
108        return self.doInBlocks(self.setPitch, audio_data.data, ratio)
109