#!/usr/bin/python ###### # riffhax: convert and manipulate riffworks and wave files # version: $Rev: 13 $ # source: http://www.devbox.be, http://pub.devbox.be ### # # $Id: riffhax.py 13 2009-06-24 16:58:49Z jimmy $ # # Copyright (C) 2009 Jimmy Scott #jimmy#inet-solutions#be#. Belgium. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # 3. The names of the authors may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. # ###### import os import sys import wave import zipfile # Some globals I assume to be a constant psi_channels = 2 psi_sample_width = 2 psi_sample_freq = 44100 psi_comp_type = 'NONE' psi_comp_name = 'not compressed' def main(): try: command = sys.argv[1] except: do_help() if command == "help" or command == "-h": do_help() elif command == "wavinfo": do_wavinfo() elif command == "psiinfo": do_psiinfo() elif command == "psi2wav": do_psi2wav() elif command == "wav2psi": do_wav2psi() elif command == "psipad": do_psipad() elif command == "wavpad": do_wavpad() elif command == "psicpy": do_psicpy() elif command == "wavcpy": do_wavcpy() elif command == "frames": do_frames() elif command == "rwlist": do_rwlist() elif command == "rwlistall": do_rwlistall() else: sys.stderr.write("%s: invalid command\n" % sys.argv[0]) sys.exit(1) def do_help(): sys.stderr.write("usage:\n") sys.stderr.write("\n Display file info:\n") sys.stderr.write("\t%s wavinfo \n" % sys.argv[0]) sys.stderr.write("\t%s psiinfo \n" % sys.argv[0]) sys.stderr.write("\n Convert between psi and wav:\n") sys.stderr.write("\t%s wav2psi \n" % sys.argv[0]) sys.stderr.write("\t%s psi2wav \n" % sys.argv[0]) sys.stderr.write("\n Add empty padding frames:\n") sys.stderr.write("\t%s wavpad
 \n"
		% sys.argv[0])
	sys.stderr.write("\t%s psipad   
 \n"
		% sys.argv[0])
	sys.stderr.write("\n    Copy frames to new file:\n")
	sys.stderr.write("\t%s wavcpy    \n"
		% sys.argv[0])
	sys.stderr.write("\t%s psicpy    \n"
		% sys.argv[0])
	sys.stderr.write("\n    Calculate number of frames:\n")
	sys.stderr.write("\t%s frames   \n" % sys.argv[0])
	sys.stderr.write("\n    List rwr/rws file structure:\n")
	sys.stderr.write("\t%s rwlist \n" % sys.argv[0])
	sys.stderr.write("\t%s rwlistall \n" % sys.argv[0])
	sys.stderr.write("\n    Unpack rws/rwr file (external):\n")
	sys.stderr.write("\tunzip  -d \n")
	sys.stderr.write("\n    Pack rws/rwr file (external):\n")
	sys.stderr.write("\tcd  && zip -0r  *")
	sys.stderr.write(" -x '*.DS_Store'\n")
	sys.stderr.write("\n")
	sys.exit(1)

def do_frames():
	num_bars = int(sys.argv[2])
	b_per_bar = int(sys.argv[3])
	b_per_min = int(sys.argv[4])
	sample_freq = psi_sample_freq
	
	frames = ( float(sample_freq*60) / (float(b_per_min)/b_per_bar) ) \
		* num_bars
	
	print "%d Bars, %d Beats Per Bar, %d Beats Per Minute = %f Frames" % \
		(num_bars, b_per_bar, b_per_min, frames)

def do_psiinfo():
	psi_file = sys.argv[2]
	psi_file_size = os.stat(psi_file)[6]
	assumed_frames = int(psi_file_size / psi_channels / psi_sample_width)
	frames_check = (assumed_frames * psi_channels * psi_sample_width)
	print "File: %s:" % psi_file
	print "\tSize: %d" % psi_file_size
	print "\tAssumed Frames: %d (%d / %d / %d)" % \
		(assumed_frames, psi_file_size, psi_channels, psi_sample_width)
	if psi_file_size != frames_check:
		print"\tWARNING: ASSUMPTION IS LIKELY INCORRECT"

def do_wavinfo():
	wav_file = sys.argv[2]
	wav_fh = wave.open(wav_file, 'r')
        print "File: %s" % wav_file
        print "\tChannels: %d" % wav_fh.getnchannels()
        print "\tSample Width: %d" % wav_fh.getsampwidth()
        print "\tSampling Frequency: %d" % wav_fh.getframerate()
        print "\tNumber of Audio Frames: %d" % wav_fh.getnframes()
        print "\tCompression Type: %s" % wav_fh.getcomptype()
        print "\tCompression Name: %s" % wav_fh.getcompname()
	wav_fh.close()

def do_psi2wav():
	psi_file = sys.argv[2]
	wav_file = sys.argv[3]
	
	psi_fh = open(psi_file, 'r')
	psi_data = psi_fh.read()
	psi_fh.close()
	
	wav_fh = wave.open(wav_file, 'w')
	wav_fh.setnchannels(psi_channels)
	wav_fh.setsampwidth(psi_sample_width)
	wav_fh.setframerate(psi_sample_freq)
	wav_fh.setcomptype(psi_comp_type, psi_comp_name)
	wav_fh.writeframes(psi_data)
	wav_fh.close()
	
	print "Written to %s" % wav_file

def do_wav2psi():
	wav_file = sys.argv[2]
	psi_file = sys.argv[3]
	
	wav_fh = wave.open(wav_file, 'r')	
	
	if wav_fh.getnchannels() != psi_channels:
		print "WARNING: UNEXPECTED NUMBER OF CHANNELS"
	if wav_fh.getsampwidth() != psi_sample_width:
		print "WARNING: UNEXPECTED SAMPLE WIDTH"
	if wav_fh.getframerate() != psi_sample_freq:
		print "WARNING: UNEXPECTED SAMPLE FREQUENCY"
	if wav_fh.getcomptype() != psi_comp_type:
		print "WARNING: UNEXPECTED COMPRESSION TYPE"
	if wav_fh.getcompname() != psi_comp_name:
		print "WARNING: UNEXPECTED COMPRESSION NAME"
	
	wav_data = wav_fh.readframes(wav_fh.getnframes())
	wav_fh.close()
	
	psi_fh = open(psi_file, 'w')
	psi_fh.write(wav_data)
	psi_fh.close()
	
	print "Written to %s" % psi_file

def do_wavpad():
	wav_file = sys.argv[2]
	out_file = sys.argv[3]
	num_frames_pre = int(sys.argv[4])
	num_frames_post = int(sys.argv[5])
	
	wav_fh = wave.open(wav_file, 'r')
	
	wav_channels = wav_fh.getnchannels()
	wav_sample_width = wav_fh.getsampwidth()
	wav_sample_freq = wav_fh.getframerate()
	wav_comp_type = wav_fh.getcomptype()
	wav_comp_name = wav_fh.getcompname()
	wav_data = wav_fh.readframes(wav_fh.getnframes())
	wav_fh.close()
	
	if wav_comp_type != psi_comp_type:
		print "WARNING: UNEXPECTED COMPRESSION TYPE"
	if wav_comp_name != psi_comp_name:
		print "WARNING: UNEXPECTED COMPRESSION NAME"
	
	empty_frame = '\x00' * ( wav_channels * wav_sample_width )
	
	out_fh = wave.open(out_file, 'w')
	out_fh.setnchannels(wav_channels)
	out_fh.setsampwidth(wav_sample_width)
	out_fh.setframerate(wav_sample_freq)
	out_fh.setcomptype(wav_comp_type, wav_comp_name)
	out_fh.writeframes(empty_frame * num_frames_pre)
	out_fh.writeframes(wav_data)
	out_fh.writeframes(empty_frame * num_frames_post)
	out_fh.close()
	
	print "Written to %s" % out_file

def do_psipad():
	psi_file = sys.argv[2]
	out_file = sys.argv[3]
	num_frames_pre = int(sys.argv[4])
	num_frames_post = int(sys.argv[5])
	
	psi_fh = open(psi_file, 'r')
	psi_data = psi_fh.read()
	psi_fh.close()
	
	empty_frame = '\x00' * ( psi_channels * psi_sample_width )
	
	out_fh = open(out_file, 'w')
	out_fh.write(empty_frame * num_frames_pre)
	out_fh.write(psi_data)
	out_fh.write(empty_frame * num_frames_post)
	out_fh.close()
	
	print "Written to %s" % out_file

def do_rwlist():
	rw_file = sys.argv[2]
	ignored_files = []
	
	rw_fh = zipfile.ZipFile(rw_file)
	
	print "File: %s" % rw_file
	for file_name in rw_fh.namelist():
		if file_name.startswith('__MACOSX/') or \
		   file_name.endswith('/.DS_Store') or \
		   file_name.endswith('/desktop.ini') or \
		   file_name.endswith('/thumbs.db'):
			ignored_files.append(file_name)
			continue
		print "\t%s" % file_name
	
	if ignored_files:
		print "\nFiltered:"
		for file_name in ignored_files:
			print "\t%s" % file_name
	
	rw_fh.close()

def do_rwlistall():
	rw_file = sys.argv[2]
	
	rw_fh = zipfile.ZipFile(rw_file)
	print "File: %s\n" % rw_file
	rw_fh.printdir()
	rw_fh.close()

def do_wavcpy():
	wav_file = sys.argv[2]
	out_file = sys.argv[3]
	first_frame = int(sys.argv[4])
	last_frame = int(sys.argv[5])
	
	wav_fh = wave.open(wav_file, 'r')
	
	wav_channels = wav_fh.getnchannels()
	wav_sample_width = wav_fh.getsampwidth()
	wav_sample_freq = wav_fh.getframerate()
	wav_comp_type = wav_fh.getcomptype()
	wav_comp_name = wav_fh.getcompname()
	
	wav_fh.setpos(first_frame - 1)
	wav_data = wav_fh.readframes((last_frame - first_frame) + 1)
	wav_fh.close()
	
	if wav_comp_type != psi_comp_type:
		print "WARNING: UNEXPECTED COMPRESSION TYPE"
	if wav_comp_name != psi_comp_name:
		print "WARNING: UNEXPECTED COMPRESSION NAME"
	
	out_fh = wave.open(out_file, 'w')
	out_fh.setnchannels(wav_channels)
	out_fh.setsampwidth(wav_sample_width)
	out_fh.setframerate(wav_sample_freq)
	out_fh.setcomptype(wav_comp_type, wav_comp_name)
	out_fh.writeframes(wav_data)
	out_fh.close()
	
	print "Written to %s" % out_file

def do_psicpy():
	psi_file = sys.argv[2]
	out_file = sys.argv[3]
	first_frame = int(sys.argv[4])
	last_frame = int(sys.argv[5])
	
	psi_fh = open(psi_file, 'r')
	
	psi_fh.seek((first_frame - 1) * (psi_channels * psi_sample_width))
	psi_data = psi_fh.read(((last_frame - first_frame) + 1) * (psi_channels * psi_sample_width))
	psi_fh.close()
	
	out_fh = open(out_file, 'w')
	out_fh.write(psi_data)
	out_fh.close()
	
	print "Written to %s" % out_file

main()