#!/usr/bin/env python """Convert WAV files to a .H file suitable for use with the Rugged Circuits BeatVox audio synthesizer shield.""" import wave import sys import os.path import datetime import string import struct import glob import operator from optparse import OptionParser # Program version VERSION="1.1" PROGINFO=""" Convert WAV files into a C-language header file (.h file) for use with the BeatVox audio synthesizer shield for Arduino. See this URL for more details: http://ruggedcircuits.com/html/beatvox.html This code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GNU General Public License can be viewed at Rugged Circuits LLC http://ruggedcircuits.com """ invalidChars = r' !@#$%^&*()-+=[]{}/?,.<>' trans = string.maketrans(invalidChars, '_'*len(invalidChars)) def makeValidName(s, trans=trans): return string.translate(s, trans) def process8BitFile(fin, fout): lineCount = 0 (numchan, bytespersample, Fs, length, comp_type, comp_name) = fin.getparams() for ix in range(length): data = fin.readframes(1) val = int(round(float(ord(data[0]))/255*242)) print >> fout, "%3u, " % val, lineCount += 1 if (lineCount == 16): print >> fout lineCount = 0 if lineCount > 0: print >> fout def process16BitFile(fin, fout): lineCount = 0 (numchan, bytespersample, Fs, length, comp_type, comp_name) = fin.getparams() for ix in range(length): data = struct.unpack(r'> fout, "%3u, " % val, lineCount += 1 if (lineCount == 16): print >> fout lineCount = 0 if lineCount > 0: print >> fout if __name__=="__main__": usage = "Usage: %prog [Options] WAVFile1 [WAVFile2 ...]" parser = OptionParser(usage=usage, version="%prog "+VERSION+PROGINFO) parser.add_option("-o", "--output-file", dest="OutFileName", metavar="FILE", help="Output header file name (default: %default)", default="Sounds.h") (options, args) = parser.parse_args() if len(args) < 1: parser.error("Must specify at least 1 WAV file") try: fout = file(options.OutFileName, "wt") except Exception,detail: print "Failed to open output file '%s':\n %s" % (options.OutFileName, str(detail)) sys.exit(1) print >> fout, """#ifndef BEATVOX_SOUNDS_H #define BEATVOX_SOUNDS_H /* This file was automatically generated by %s version %s on %s. What is this? See this URL: http://ruggedcircuits.com/html/beatvox.html The program was invoked with parameters: """ % (os.path.split(sys.argv[0])[1], VERSION, datetime.datetime.isoformat(datetime.datetime.now())) if sys.platform=='win32': files = [glob.glob(f) for f in args] files = reduce(operator.add, files) else: files = args for arg in files: print >> fout, " ", arg print >> fout, "*/" FileList = [] for fname in files: print >> fout, "\n//////// File: %s" % fname just_the_file = os.path.split(fname)[1] just_the_base = os.path.splitext(just_the_file)[0] PCMArrayName = makeValidName("PCM_%s" % just_the_base) print >> fout, "unsigned char %s [] PROGMEM = {" % PCMArrayName try: fin = wave.open(fname, "rb") except Exception,detail: print "Unable to open input WAV file '%s':\n %s" % (fname, str(detail)) sys.exit(2) (numchan, bytespersample, Fs, length, comp_type, comp_name) = fin.getparams() if numchan != 1: print 'ERROR: %s is not monophonic' % fname sys.exit(3) if (bytespersample < 1) or (bytespersample > 2): print 'ERROR: Only 8-bit or 16-bit samples are supported' sys.exit(5) if abs(Fs-22039)/22039.0*100 > 0.1: print '*'*40 print 'WARNING: %s sampling rate is not approximately 22039 Hz' % fname print '*'*40 if comp_type != "NONE": print 'ERROR: %s is compressed with compression type %s' % (fname, comp_name) sys.exit(4) FileList.append((PCMArrayName, length)) if bytespersample==1: process8BitFile(fin, fout) else: process16BitFile(fin, fout) fin.close() print >> fout, "};" print >> fout, "const unsigned char NumSounds = %u;" % len(FileList) print >> fout, "const unsigned short SoundLengths[] = {", for ix in range(len(FileList)): print >> fout, "%u, " % FileList[ix][1], print >> fout, "};" print >> fout, "const unsigned char * const SoundPointers[] = {", for ix in range(len(FileList)): print >> fout, "%s, " % FileList[ix][0], print >> fout, "};" print >> fout, "#endif // BEATVOX_SOUNDS_H" # Total size used in FLASH is samples (obviously) plus size of tables above sizeUsed = 2*len(FileList) + 2*len(FileList) # 2 bytes for length, 2 bytes for a pointer for ix in range(len(FileList)): sizeUsed += FileList[ix][1] print "Output header file name: ", options.OutFileName print "Total FLASH usage: ", sizeUsed, "bytes" # vim: expandtab ts=2 sw=2 ai