Skip to content

Instantly share code, notes, and snippets.

@Jetroid
Last active February 3, 2020 05:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jetroid/e1f8c3234d9831b90fd19d8aadb609a1 to your computer and use it in GitHub Desktop.
Save Jetroid/e1f8c3234d9831b90fd19d8aadb609a1 to your computer and use it in GitHub Desktop.
import wave
import math
import collections
import sys
# Program written October 2018 for Frequency Central by Jet Holt
# call like this:
# python thisprogram.py /path/to/sound.wav r s
# r = resolution, in bits. ie how much data per sample
# s = steps, in bits. ie how many samples. Actual samples is 2^s
# eg:
# python thisprogram.py ./bassdrum.wav 10 12
# to output 4096 samples (4096 = 2^12) that have 10 bit resolution.
# Outputting every sample (ignoring s) is not supported by this program.
#Function to fix signedness
#Also converts stereo data to mono by summing and halfing
def getData(file):
if file.getnchannels() is 2:
my_bytes = file.readframes(1)
left = int.from_bytes(my_bytes[:file.getsampwidth()], byteorder='little', signed=True)
right = int.from_bytes(my_bytes[file.getsampwidth():], byteorder='little', signed=True)
return (left/2) + (right/2)
else:
return int.from_bytes(file.readframes(1), byteorder='little', signed=True)
# Open Wav file
filename = sys.argv[1]
if filename.endswith('.wav'):
filename = filename[:-4]
wav = wave.open(filename + ".wav",'rb')
# Resolution determined by cmd arg and wav sample width
resolution = min(int(sys.argv[2]), wav.getsampwidth()*8)
# Number of steps
steps = int(sys.argv[3])
number_bytes = math.ceil(resolution/8)
missing_bits = (number_bytes*8)-resolution
# Determine the minimum and maximum values of the wav file.
# (Ie what is the highest and lowest sample value?)
originalMax = 0
originalMin = 0
for i in range(wav.getnframes()):
data = getData(wav)
originalMin = min(data,originalMin)
originalMax = max(data,originalMax)
wav.rewind()
if (originalMax > -originalMin):
originalMin = -originalMax
else:
originalMax = -originalMin
originalRange = originalMax - originalMin
# Todo: Assumes the 'rest' state is 0.
# We use this
centerline = 0
# The next two parts, 'find the start' and 'find the end' are used to ignore silence
# (because we don't want to have to edit our wav files manually)
# We want to go through our wav file and start taking note of the data once the data
# changes by 1% of the range above or below the centerline.
# We will record the frames where this change occurs, then backtrack by 3 frames
# (we backtrack to account for the 1% of data that we dropped)
percent = originalRange * 0.01
#Find the start
dist = 10
d = collections.deque(maxlen=dist)
startFrame = 0
#If value stays withing 1% of centerline for a duration, it hasn't started yet
for i in range(wav.getnframes()):
data = getData(wav)
d.append(data)
average = sum(d)/dist
if (data > average and data > average + percent) or (data < average and data < average - percent):
startFrame = max(0, i - 3)
break
#Find the end
dist=150
d = collections.deque(maxlen=dist)
endFrame = wav.getnframes()
for i in range(wav.getnframes()-1,0,-1):
wav.setpos(i)
data = getData(wav)
d.append(data)
average = sum(d)/dist
if (data > average and data > average + percent) or (data < average and data < average - percent):
endFrame = min(wav.getnframes(), i + 3)
break
validFramesCount = wav.getnframes() - (startFrame + (wav.getnframes() - endFrame))
frame_interval = validFramesCount/(2**steps)
print(filename + " " + str(frame_interval))
limitMax = (2**resolution) - 1
limitMin = 0
# Get our data and manipulate it so that the highest values and lowest values
# from the sample match with the highest and lowest possible with our resolution
data = []
count = 0
for i in range(2**steps):
wav.setpos(math.floor((i*frame_interval) + startFrame))
value = getData(wav)
rescale = math.floor((value - originalMin) / (originalMax - originalMin) * limitMax)
data.append(rescale)
count = count + 1
# Now write our data to a file
# I break after ever 16 samples to make it easier to read
table = open(filename + '.txt','w')
count2 = 0
while count2 < count:
tablestr = ""
for i in range(16):
tablestr += str(data[count2])+", "
count2 = count2 + 1
table.write(tablestr+"\n")
table.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment