## Given a .wav audio file, downsamples it to 8000 Hz and writes it out ## as a ADPCM file suitable for use with AVRs. from struct import unpack import wave import os import sys def unpackMono(waveFile): w = wave.Wave_read(waveFile) data = [] for i in range(w.getnframes()): data.append(unpack("h", w.readframes(1))[0]) return(data) def scaleData(data): scale = max(max(data), abs(min(data))) * 1.0 return([x/scale for x in data]) def getDifferences(data): differences = [] for i in range(len(data)-1): differences.append(data[i+1]-data[i]) return(differences) def quantize(data, thresholds): quantized = [] n = len(thresholds) thresholdRange = range(n) for d in data: categorized = False for i in thresholdRange: if d <= thresholds[i]: quantized.append(i) categorized = True break if not categorized: quantized.append(n) return(quantized) def pack4(data): # for 2-bit data packedData = [] for i in range(len(data) / 4): thisByte = 0 thisByte += 2**6 * data[4*i] thisByte += 2**4 * data[4*i+1] thisByte += 2**2 * data[4*i+2] thisByte += data[4*i+3] packedData.append(thisByte) return(packedData) def pack2(data): # for 1-bit data packedData = [] for i in range(len(data) / 8): thisByte = 0 thisByte += 2**7 * data[8*i] thisByte += 2**6 * data[8*i+1] thisByte += 2**5 * data[8*i+2] thisByte += 2**4 * data[8*i+3] thisByte += 2**3 * data[8*i+4] thisByte += 2**2 * data[8*i+5] thisByte += 2**1 * data[8*i+6] thisByte += data[8*i+7] packedData.append(thisByte) return(packedData) def packOneBitDPCM(filename): data = unpackMono(filename) data = scaleData(data) differences = getDifferences(data) quantized = quantize(differences, [0]) packed = pack2(quantized) return(packed) def packTwoBitDPCM(filename): data = unpackMono(filename) data = scaleData(data) differences = getDifferences(data) quantized = quantize(differences, TWO_BIT_THRESHOLDS) packed = pack4(quantized) return(packed) def createHeader(filename, packedData): baseFilename = filename[:-4] outfile = open("DPCM_" + baseFilename + ".h", "w") outfile.write('uint8_t DPCM_{}[] PROGMEM = {{\n'.format(baseFilename)) for byte in packedData: outfile.write(' {:d},\n'.format(byte)) outfile.write('};\n') outfile.close() def testWaveFile(filename): w = wave.Wave_read(filename) bitrate = w.getframerate() channels = w.getnchannels() bits = w.getsampwidth()*8 if not bitrate==8000 or not channels==1 or not bits==16: newFilename = filename[:-4] + "_8000.wav" returnValue = os.system(SOXCOMMAND.format(filename, newFilename)) if returnValue: raise(SOX_Exception("Something went wrong calling sox: SOXCOMMAND.format(filename, newFilename")) filename = newFilename return(filename) class SOX_Exception(Exception): pass if __name__ == "__main__": ## Default is to convert all wav files in the current directory. TWO_BIT_THRESHOLDS = [-0.05, 0, 0.05] SOXCOMMAND = "sox {} -r 8000 -c 1 -b 16 {}" # for converting wave file ## install sox, or use itunes or audacity to convert ## wavefile to 8kHz, 16-bit, one-channel wavefiles = [x for x in os.walk(".").next()[2] if x.endswith(".wav")] for filename in wavefiles: filename = testWaveFile(filename) packedData = packTwoBitDPCM(filename) createHeader(filename, packedData) ## And create a digits set: digits = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "zero", "point", "volts"] allDigits = open("allDigits.h", "w") for digit in digits: filename = "DPCM_" + digit + "_8000.h" print filename allDigits.write(open(filename).read()) allDigits.close()