-
-
Save Jetroid/e1f8c3234d9831b90fd19d8aadb609a1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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