137 lines
4.0 KiB
Python
137 lines
4.0 KiB
Python
## 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()
|
|
|