From aa71fe5b8819b0e3461ad29b96a859a17d22a810 Mon Sep 17 00:00:00 2001 From: wvengen Date: Sun, 14 Jun 2020 13:57:32 +0200 Subject: [PATCH 1/3] Initial conversion to Python 3 --- lib/audio_helpers.py | 26 +++++++++++++------------- lib/compare.py | 34 +++++++++++++++------------------- lib/deflac.py | 10 +++++----- lib/flacize.py | 18 +++++++++--------- lib/graph.py | 2 +- lib/group_velcurves.py | 12 ++++++------ lib/loop.py | 28 ++++++++++++++-------------- lib/midi_helpers.py | 4 ++-- lib/quantize.py | 32 ++++++++++++++++---------------- lib/record.py | 18 +++++++++--------- lib/send_notes.py | 22 +++++++++++----------- lib/sfzparser.py | 10 +++++----- lib/spectrogram.py | 2 +- lib/starts_with_click.py | 4 ++-- lib/truncate.py | 6 +++--- lib/utils.py | 2 +- lib/volume_leveler.py | 30 +++++++++++++++--------------- lib/wavio.py | 6 +++--- record.py | 2 +- 19 files changed, 132 insertions(+), 136 deletions(-) diff --git a/lib/audio_helpers.py b/lib/audio_helpers.py index a7f8247..04068b0 100644 --- a/lib/audio_helpers.py +++ b/lib/audio_helpers.py @@ -1,12 +1,12 @@ import time import numpy -from utils import note_name, percent_to_db -from record import record -from constants import CLIPPING_THRESHOLD, \ +from .utils import note_name, percent_to_db +from .record import record +from .constants import CLIPPING_THRESHOLD, \ CLIPPING_CHECK_NOTE, \ EXIT_ON_CLIPPING, \ SAMPLE_RATE -from midi_helpers import all_notes_off, CHANNEL_OFFSET +from .midi_helpers import all_notes_off, CHANNEL_OFFSET def generate_sample( @@ -46,7 +46,7 @@ def on_time_up(): def sample_threshold_from_noise_floor(bit_depth, audio_interface_name): time.sleep(1) - print "Sampling noise floor..." + print("Sampling noise floor...") sample_width, data, release_time = record( limit=2.0, after_start=None, @@ -60,9 +60,9 @@ def sample_threshold_from_noise_floor(bit_depth, audio_interface_name): numpy.amax(numpy.absolute(data)) / float(2 ** (bit_depth - 1)) ) - print "Noise floor has volume %8.8f dBFS" % percent_to_db(noise_floor) + print("Noise floor has volume %8.8f dBFS" % percent_to_db(noise_floor)) threshold = noise_floor * 1.1 - print "Setting threshold to %8.8f dBFS" % percent_to_db(threshold) + print("Setting threshold to %8.8f dBFS" % percent_to_db(threshold)) return threshold @@ -74,9 +74,9 @@ def check_for_clipping( audio_interface_name, ): time.sleep(1) - print "Checking for clipping and balance on note %s..." % ( + print("Checking for clipping and balance on note %s..." % ( note_name(CLIPPING_CHECK_NOTE) - ) + )) sample_width, data, release_time = generate_sample( limit=2.0, @@ -99,14 +99,14 @@ def check_for_clipping( ) # All notes off, but like, a lot, again - for _ in xrange(0, 2): + for _ in range(0, 2): all_notes_off(midiout, midi_channel) - print "Maximum volume is around %8.8f dBFS" % percent_to_db(max_volume) + print("Maximum volume is around %8.8f dBFS" % percent_to_db(max_volume)) if max_volume >= CLIPPING_THRESHOLD: - print "Clipping detected (%2.2f dBFS >= %2.2f dBFS) at max volume!" % ( + print("Clipping detected (%2.2f dBFS >= %2.2f dBFS) at max volume!" % ( percent_to_db(max_volume), percent_to_db(CLIPPING_THRESHOLD) - ) + )) if EXIT_ON_CLIPPING: raise ValueError("Clipping detected at max volume!") diff --git a/lib/compare.py b/lib/compare.py index 8e918ce..af1641d 100644 --- a/lib/compare.py +++ b/lib/compare.py @@ -7,9 +7,9 @@ from tqdm import tqdm from tabulate import tabulate -from utils import normalized, trim_mono_data -from audio_helpers import fundamental_frequency -from wavio import read_wave_file +from .utils import normalized, trim_mono_data +from .audio_helpers import fundamental_frequency +from .wavio import read_wave_file import matplotlib.pyplot as plt @@ -106,21 +106,18 @@ def process_all(aifs): ) results = sorted( results, - key=lambda (dl, dr, - pl, pr, - freqd, - fa, fb): dl + dr + abs(freqd - 1)) + key=lambda dl_dr_pl_pr_freqd_fa_fb: dl_dr_pl_pr_freqd_fa_fb[0] + dl_dr_pl_pr_freqd_fa_fb[1] + abs(dl_dr_pl_pr_freqd_fa_fb[4] - 1)) with open('results.csv', 'wb') as f: writer = csv.writer(f) writer.writerows([headers]) writer.writerows(results) - print "%d results" % len(results) - print tabulate( + print("%d results" % len(results)) + print(tabulate( results, headers=headers, floatfmt='.4f' - ) + )) def graph_ffts(): @@ -139,10 +136,9 @@ def graph_ffts(): idx = numpy.argmax(numpy.abs(w)) freq = freqs[idx] plt.plot(w) - print freq - print \ - fundamental_frequency(normalized(list)), \ - fundamental_frequency(normalized(left + right)) + print(freq) + print(fundamental_frequency(normalized(list)), \ + fundamental_frequency(normalized(left + right))) # plt.show() @@ -189,11 +185,11 @@ def freq_shift(): shifted_diffr = normalized_difference(*aligned_sublists(wavea[1], waveb_shifted[1])) - print files - print 'diffs\t\t', diffl, diffr - print 'shifted diffs\t', shifted_diffl, shifted_diffr - print 'freqs', freqd - print 'shifted freqs', shifted_freqd + print(files) + print('diffs\t\t', diffl, diffr) + print('shifted diffs\t', shifted_diffl, shifted_diffr) + print('freqs', freqd) + print('shifted freqs', shifted_freqd) if __name__ == "__main__": diff --git a/lib/deflac.py b/lib/deflac.py index 3de6b4b..1813092 100644 --- a/lib/deflac.py +++ b/lib/deflac.py @@ -5,11 +5,11 @@ import argparse import subprocess from tqdm import tqdm -from sfzparser import SFZFile -from wavio import read_wave_file -from utils import normalized -from record import RATE, save_to_file -from constants import bit_depth +from .sfzparser import SFZFile +from .wavio import read_wave_file +from .utils import normalized +from .record import RATE, save_to_file +from .constants import bit_depth def full_path(sfzfile, filename): diff --git a/lib/flacize.py b/lib/flacize.py index 967d209..20da19a 100644 --- a/lib/flacize.py +++ b/lib/flacize.py @@ -5,9 +5,9 @@ import argparse import subprocess from tqdm import tqdm -from sfzparser import SFZFile, Group -from wavio import read_wave_file -from utils import group_by_attr, note_name +from .sfzparser import SFZFile, Group +from .wavio import read_wave_file +from .utils import group_by_attr, note_name def full_path(sfzfile, filename): @@ -66,7 +66,7 @@ def flacize_after_sampling( for key, key_regions in group_by_attr(group.regions, [ 'key', 'pitch_keycenter' - ]).iteritems()], []) + ]).items()], []) new_groups.append(Group(group.attributes, output)) with open(sfzfile + '.flac.sfz', 'w') as file: @@ -77,7 +77,7 @@ def flacize_after_sampling( try: os.unlink(path) except OSError as e: - print "Could not unlink path: %s: %s" % (path, e) + print("Could not unlink path: %s: %s" % (path, e)) ANTI_CLICK_OFFSET = 3 @@ -128,8 +128,8 @@ def concat_samples(regions, path, name=None): filename, note_name(key))) for key, regions in - tqdm(group_by_attr(group.regions, - 'key').iteritems())], []) - print group.just_group() + tqdm(iter(group_by_attr(group.regions, + 'key').items()))], []) + print(group.just_group()) for region in output: - print region + print(region) diff --git a/lib/graph.py b/lib/graph.py index 07c958b..eaa60aa 100644 --- a/lib/graph.py +++ b/lib/graph.py @@ -7,7 +7,7 @@ from tqdm import tqdm from tabulate import tabulate -from wavio import read_wave_file +from .wavio import read_wave_file import matplotlib.pyplot as plt diff --git a/lib/group_velcurves.py b/lib/group_velcurves.py index cff6a95..cf9a6f9 100644 --- a/lib/group_velcurves.py +++ b/lib/group_velcurves.py @@ -1,6 +1,6 @@ import argparse -from sfzparser import parse, Group -from quantize import group_by_attr +from .sfzparser import parse, Group +from .quantize import group_by_attr parser = argparse.ArgumentParser( description='quantize and compress SFZ files' @@ -18,14 +18,14 @@ def should_group_key(key): def group_by_pitch(regions): - for key, regions in group_by_attr(regions, 'key').iteritems(): + for key, regions in group_by_attr(regions, 'key').items(): # Group together all amp_velcurve_* and key params. yield Group(dict([ (key, value) for region in regions - for key, value in region.attributes.iteritems() + for key, value in region.attributes.items() if should_group_key(key) - ] + DEFAULT_ATTRIBUTES.items()), [ + ] + list(DEFAULT_ATTRIBUTES.items())), [ region.without_attributes(should_group_key) for region in regions ]) @@ -41,4 +41,4 @@ def group_by_pitch(regions): groups = parse(open(filename).read()) regions = sum([group.flattened_regions() for group in groups], []) for group in group_by_pitch(regions): - print group + print(group) diff --git a/lib/loop.py b/lib/loop.py index 155d65b..e178fd4 100644 --- a/lib/loop.py +++ b/lib/loop.py @@ -1,8 +1,8 @@ import sys import numpy from tqdm import tqdm -from truncate import read_wave_file -from audio_helpers import fundamental_frequency +from .truncate import read_wave_file +from .audio_helpers import fundamental_frequency QUANTIZE_FACTOR = 8 @@ -12,20 +12,20 @@ def compare_windows(window_a, window_b): def slide_window(file, period, start_at=0, end_before=0): - for power in reversed(xrange(7, 10)): + for power in reversed(range(7, 10)): multiple = 2 ** power window_size = int(period * multiple) # Uncomment this to search from the start_at value to the end_before # rather than just through one window's length # end_range = len(file) - (window_size * 2) - end_before end_range = start_at + window_size - for i in xrange(start_at, end_range): + for i in range(start_at, end_range): yield power, i, window_size def window_match(file): period = (1.0 / fundamental_frequency(file, 1)) * 2 - print period, 'period in samples' + print(period, 'period in samples') winner = None @@ -48,14 +48,14 @@ def window_match(file): i, abs(file[i] - file[window_start]) ) - print 'new winner', winner + print('new winner', winner) lowest_difference, winning_window_size, winning_index, gap = winner - print "Best loop match:", lowest_difference - print "window size", winning_window_size - print "winning index", winning_index - print "winning gap", gap + print("Best loop match:", lowest_difference) + print("window size", winning_window_size) + print("winning index", winning_index) + print("winning gap", gap) return winning_index, winning_window_size @@ -71,7 +71,7 @@ def find_similar_sample_index( ): reference_slope = slope_at_index(file, reference_index) > 0 best_match = None - search_range = xrange( + search_range = range( search_around_index - search_size, search_around_index + search_size ) @@ -93,12 +93,12 @@ def find_similar_sample_index( def zero_crossing_match(file): period = (1.0 / fundamental_frequency(file, 1)) * 2 - print period, 'period in samples' + print(period, 'period in samples') period_multiple = 64 period = period * period_multiple - for i in reversed(xrange(2 * len(file) / 3, 5 * len(file) / 6)): + for i in reversed(range(2 * len(file) / 3, 5 * len(file) / 6)): if file[i] >= 0 and file[i + 1] < 0 and \ file[int(i + period)] >= 0 and \ file[int(i + 1 + period)] < 0 and \ @@ -234,7 +234,7 @@ def process(aif, sample_rate=48000): file = file[0] - print 'start, end', loop_start, loop_end + print('start, end', loop_start, loop_end) plt.plot(file[loop_start:loop_end]) plt.plot(file[loop_end:loop_start + (2 * loop_size)]) diff --git a/lib/midi_helpers.py b/lib/midi_helpers.py index 23fe634..2c7cac0 100644 --- a/lib/midi_helpers.py +++ b/lib/midi_helpers.py @@ -49,7 +49,7 @@ def open_midi_port(midi_port_name): def set_program_number(midiout, midi_channel, program_number): if program_number is not None: - print "Sending program change to program %d..." % program_number + print("Sending program change to program %d..." % program_number) # Bank change (fine) to (program_number / 128) midiout.send_message([ CC_CHANNEL_OFFSET + midi_channel, @@ -64,7 +64,7 @@ def set_program_number(midiout, midi_channel, program_number): ]) # All notes off, but like, a lot - for _ in xrange(0, 2): + for _ in range(0, 2): all_notes_off(midiout, midi_channel) time.sleep(0.5) diff --git a/lib/quantize.py b/lib/quantize.py index 28f363d..083d416 100644 --- a/lib/quantize.py +++ b/lib/quantize.py @@ -1,8 +1,8 @@ import os import sys import argparse -from sfzparser import SFZFile -from utils import group_by_attr +from .sfzparser import SFZFile +from .utils import group_by_attr import itertools from collections import defaultdict @@ -14,8 +14,8 @@ def quantize_pitch(regions, pitch_levels=25): - lowestkey = min(map(lambda x: int(x.attributes['key']), regions)) - highestkey = max(map(lambda x: int(x.attributes['key']), regions)) + lowestkey = min([int(x.attributes['key']) for x in regions]) + highestkey = max([int(x.attributes['key']) for x in regions]) keyspan = highestkey - lowestkey pitch_skip = keyspan / pitch_levels @@ -25,17 +25,17 @@ def quantize_pitch(regions, pitch_levels=25): # a dict of sample_pitch -> [lokey, hikey, pitch_keycenter] pitchmapping = {} - for key in xrange( + for key in range( lowestkey + (pitch_skip / 2), highestkey + 1 + (pitch_skip / 2), pitch_skip): pitchmapping[key] = { 'lokey': key - (pitch_skip / 2), 'pitch_keycenter': key, - 'hikey': key + (pitch_skip / 2) - (0 if evenly_divided else 1), + 'hikey': key + int(pitch_skip / 2) - (0 if evenly_divided else 1), } - for key, regions in group_by_attr(regions, 'key').iteritems(): + for key, regions in group_by_attr(regions, 'key').items(): if int(key) in pitchmapping: for region in regions: region.attributes.update(pitchmapping[int(key)]) @@ -44,8 +44,8 @@ def quantize_pitch(regions, pitch_levels=25): def quantize_velocity(regions, velocity_levels=5): - lowestvel = min(map(lambda x: int(x.attributes['xfin_loivel']), regions)) - highestvel = max(map(lambda x: int(x.attributes['xfin_hivel']), regions)) + lowestvel = min([int(x.attributes['xfin_loivel']) for x in regions]) + highestvel = max([int(x.attributes['xfin_hivel']) for x in regions]) velspan = 127 pitch_skip = velspan / velocity_levels @@ -55,17 +55,17 @@ def quantize_velocity(regions, velocity_levels=5): # a dict of sample_pitch -> [lokey, hikey, pitch_keycenter] pitchmapping = {} - for key in xrange( - lowestkey + (pitch_skip / 2), - highestkey + 1 + (pitch_skip / 2), + for key in range( + lowestkey + int(pitch_skip / 2), + highestkey + 1 + int(pitch_skip / 2), pitch_skip): pitchmapping[key] = { - 'lokey': key - (pitch_skip / 2), + 'lokey': key - int(pitch_skip / 2), 'pitch_keycenter': key, - 'hikey': key + (pitch_skip / 2) - (0 if evenly_divided else 1), + 'hikey': key + int(pitch_skip / 2) - (0 if evenly_divided else 1), } - for key, regions in group_by_attr(regions, 'key').iteritems(): + for key, regions in group_by_attr(regions, 'key').items(): if int(key) in pitchmapping: for region in regions: region.attributes.update(pitchmapping[int(key)]) @@ -111,6 +111,6 @@ def compute_sample_size(filename, regions): ) ) for region in output: - print region + print(region) # for group in groups: # print group diff --git a/lib/record.py b/lib/record.py index 298876e..81abdac 100644 --- a/lib/record.py +++ b/lib/record.py @@ -1,8 +1,8 @@ import sys import numpy from struct import pack -from constants import bit_depth, NUMPY_DTYPE, SAMPLE_RATE -from utils import percent_to_db, dbfs_as_percent +from .constants import bit_depth, NUMPY_DTYPE, SAMPLE_RATE +from .utils import percent_to_db, dbfs_as_percent import pyaudio import wave @@ -41,7 +41,7 @@ def get_input_device_index(py_audio, audio_interface_name=None): input_interface_names = get_input_device_names(py_audio, info) if audio_interface_name: - for index, name in input_interface_names.iteritems(): + for index, name in input_interface_names.items(): if audio_interface_name.lower() in name.lower(): return index else: @@ -56,7 +56,7 @@ def get_input_device_name_by_index(audio_interface_index): info = py_audio.get_host_api_info_by_index(0) input_interface_names = get_input_device_names(py_audio, info) - for index, name in input_interface_names.iteritems(): + for index, name in input_interface_names.items(): if index == audio_interface_index: return name else: @@ -68,9 +68,9 @@ def get_input_device_name_by_index(audio_interface_index): def list_input_devices(device_names): lines = [] - for index, name in sorted(device_names.iteritems()): - lines.append(u"{:3d}. {}".format(index, name)) - return u"\n".join(lines).encode("ascii", "ignore") + for index, name in sorted(device_names.items()): + lines.append("{:3d}. {}".format(index, name)) + return "\n".join(lines).encode("ascii", "ignore") def record( @@ -256,7 +256,7 @@ def save_to_file(path, sample_width, data, sample_rate=SAMPLE_RATE): flattened = numpy.asarray(data.flatten('F'), dtype=NUMPY_DTYPE) write_chunk_size = 512 - for chunk_start in xrange(0, len(flattened), write_chunk_size): + for chunk_start in range(0, len(flattened), write_chunk_size): chunk = flattened[chunk_start:chunk_start + write_chunk_size] packstring = '<' + ('h' * len(chunk)) wf.writeframes(pack(packstring, *chunk)) @@ -264,5 +264,5 @@ def save_to_file(path, sample_width, data, sample_rate=SAMPLE_RATE): if __name__ == '__main__': - print record_to_file('./demo.wav', sys.argv[1] if sys.argv[1] else None) + print(record_to_file('./demo.wav', sys.argv[1] if sys.argv[1] else None)) print("done - result written to demo.wav") diff --git a/lib/send_notes.py b/lib/send_notes.py index dded31f..ef84a78 100644 --- a/lib/send_notes.py +++ b/lib/send_notes.py @@ -1,23 +1,23 @@ import os import time from tqdm import tqdm -from record import save_to_file, get_input_device_name_by_index -from sfzparser import SFZFile, Region -from pitch import compute_zones, Zone -from utils import trim_data, \ +from .record import save_to_file, get_input_device_name_by_index +from .sfzparser import SFZFile, Region +from .pitch import compute_zones, Zone +from .utils import trim_data, \ note_name, \ first_non_none, \ warn_on_clipping -from constants import bit_depth, SAMPLE_RATE -from volume_leveler import level_volume -from flacize import flacize_after_sampling -from loop import find_loop_points -from midi_helpers import Midi, all_notes_off, \ +from .constants import bit_depth, SAMPLE_RATE +from .volume_leveler import level_volume +from .flacize import flacize_after_sampling +from .loop import find_loop_points +from .midi_helpers import Midi, all_notes_off, \ open_midi_port, \ open_midi_port_by_index, \ set_program_number, \ CHANNEL_OFFSET -from audio_helpers import sample_threshold_from_noise_floor, \ +from .audio_helpers import sample_threshold_from_noise_floor, \ generate_sample, \ check_for_clipping @@ -244,7 +244,7 @@ def sample_program( ) time.sleep(PORTAMENTO_PRESAMPLE_WAIT) - for attempt in xrange(0, MAX_ATTEMPTS): + for attempt in range(0, MAX_ATTEMPTS): try: region = generate_and_save_sample( limit=limit, diff --git a/lib/sfzparser.py b/lib/sfzparser.py index 4e44b4f..7c469ef 100644 --- a/lib/sfzparser.py +++ b/lib/sfzparser.py @@ -64,7 +64,7 @@ def flattened_regions(self): def just_group(self): return "\n".join( [""] + - ['%s=%s' % (k, v) for k, v in self.attributes.iteritems()] + ['%s=%s' % (k, v) for k, v in self.attributes.items()] ) def __repr__(self): @@ -91,7 +91,7 @@ def __repr__(self): def __str__(self): return "\n".join( [""] + - ['%s=%s' % (k, v) for k, v in self.attributes.iteritems()] + ['%s=%s' % (k, v) for k, v in self.attributes.items()] ) def exists(self, root=None): @@ -103,7 +103,7 @@ def exists(self, root=None): def without_attributes(self, discard=lambda x: False): return Region(dict([ (k, v) - for k, v in self.attributes.iteritems() + for k, v in self.attributes.items() if not discard(k) ])) @@ -111,7 +111,7 @@ def merge(self, other_attrs): return Region(dict( (k, v) for d in [self.attributes, other_attrs] - for k, v in d.iteritems() + for k, v in d.items() )) @@ -125,4 +125,4 @@ def merge(self, other_attrs): for fn in args.files: file = SFZFile(open(fn).read()) for group in file.groups: - print group + print(group) diff --git a/lib/spectrogram.py b/lib/spectrogram.py index 427a5a8..1922cad 100644 --- a/lib/spectrogram.py +++ b/lib/spectrogram.py @@ -8,7 +8,7 @@ import numpy as np from matplotlib import pyplot as plt from numpy.lib import stride_tricks -from wavio import read_wave_file +from .wavio import read_wave_file def stft(sig, frame_size, overlap_fac=0.5, window=np.hanning): diff --git a/lib/starts_with_click.py b/lib/starts_with_click.py index f83a0af..0613b2f 100644 --- a/lib/starts_with_click.py +++ b/lib/starts_with_click.py @@ -1,6 +1,6 @@ import sys -from wavio import read_wave_file -from constants import bit_depth +from .wavio import read_wave_file +from .constants import bit_depth default_threshold_samples = (0.001 * float(2 ** (bit_depth - 1))) diff --git a/lib/truncate.py b/lib/truncate.py index 73884bd..d0ebf7b 100644 --- a/lib/truncate.py +++ b/lib/truncate.py @@ -1,13 +1,13 @@ import sys -from wavio import read_wave_file -from utils import start_of, end_of +from .wavio import read_wave_file +from .utils import start_of, end_of def chop(aif): file = read_wave_file(aif) start, end = min([start_of(chan) for chan in file]), \ max([end_of(chan) for chan in file]) - print aif, start, end, float(end) / len(file[0]) + print(aif, start, end, float(end) / len(file[0])) # outfile = aif + '.chopped.aif' # r = wave.open(aif, 'rb') diff --git a/lib/utils.py b/lib/utils.py index 65ed281..d8212df 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -3,7 +3,7 @@ import math from numpy import inf -from constants import default_silence_threshold, bit_depth +from .constants import default_silence_threshold, bit_depth from collections import defaultdict diff --git a/lib/volume_leveler.py b/lib/volume_leveler.py index 293803f..c65b447 100644 --- a/lib/volume_leveler.py +++ b/lib/volume_leveler.py @@ -1,18 +1,18 @@ import numpy import argparse -from constants import bit_depth -from sfzparser import SFZFile, Group -from wavio import read_wave_file -from utils import group_by_attr -from flacize import full_path -from itertools import tee, izip +from .constants import bit_depth +from .sfzparser import SFZFile, Group +from .wavio import read_wave_file +from .utils import group_by_attr +from .flacize import full_path +from itertools import tee def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = tee(iterable) next(b, None) - return izip(a, b) + return zip(a, b) def max_amp(filename): @@ -23,12 +23,12 @@ def peak_rms(data, window_size=480, limits=960): index = max([numpy.argmax(channel) for channel in data]) maxlimit = max([len(channel) for channel in data]) max_so_far = 0 - for i in xrange( - max(index - limits, (window_size / 2)), - min(index + limits, maxlimit - (window_size / 2)) + for i in range( + max(index - limits, int(window_size / 2)), + min(index + limits, maxlimit - int(window_size / 2)) ): for channel in data: - window = channel[i - (window_size / 2):i + (window_size / 2)] + window = channel[i - int(window_size / 2):i + int(window_size / 2)] if len(window) == 0: raise Exception("Cannot take mean of empty slice! Channel " "size %d, index %d, window size %d" % ( @@ -69,8 +69,8 @@ def level_volume(regions, dirname): ) ) except ZeroDivisionError: - print "Got ZeroDivisionError with high sample path: %s" % \ - high.attributes['sample'] + print("Got ZeroDivisionError with high sample path: %s" % \ + high.attributes['sample']) raise for attr in REMOVE_ATTRS: if attr in high.attributes: @@ -105,5 +105,5 @@ def level_volume(regions, dirname): for filename in args.files: sfz = SFZFile(open(filename).read()) regions = sum([group.regions for group in sfz.groups], []) - for key, regions in group_by_attr(regions, 'key').iteritems(): - print level_volume(regions) + for key, regions in group_by_attr(regions, 'key').items(): + print(level_volume(regions)) diff --git a/lib/wavio.py b/lib/wavio.py index d8f78ff..4b5c76a 100644 --- a/lib/wavio.py +++ b/lib/wavio.py @@ -3,7 +3,7 @@ import numpy import subprocess -from constants import NUMPY_DTYPE +from .constants import NUMPY_DTYPE def read_flac_file(filename, use_numpy=False): @@ -35,8 +35,8 @@ def read_wave_file(filename, use_numpy=False): else: return [ a[i::w.getnchannels()] - for i in xrange(w.getnchannels()) + for i in range(w.getnchannels()) ] except wave.Error: - print "Could not open %s" % filename + print("Could not open %s" % filename) raise diff --git a/record.py b/record.py index a06c157..bb719d6 100755 --- a/record.py +++ b/record.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Command-line interface for SampleScanner.""" From 42221c2934ade5ac3f5935674831e28b58d404b8 Mon Sep 17 00:00:00 2001 From: n-kremeris Date: Mon, 10 Oct 2022 16:34:16 +0100 Subject: [PATCH 2/3] Updates to support Python3, fix broken indices --- lib/audio_helpers.py | 2 +- lib/compare.py | 34 ++++++++++++++++++--------------- lib/{constants.py => consts.py} | 0 lib/deflac.py | 10 +++++----- lib/flacize.py | 5 +++-- lib/graph.py | 2 +- lib/group_velcurves.py | 8 ++++---- lib/loop.py | 11 +++++++++-- lib/midi_helpers.py | 2 +- lib/quantize.py | 24 +++++++++++------------ lib/record.py | 8 ++++---- lib/send_notes.py | 3 ++- lib/spectrogram.py | 2 +- lib/starts_with_click.py | 4 ++-- lib/truncate.py | 1 + lib/utils.py | 3 ++- lib/volume_leveler.py | 14 ++++++++++---- lib/wavio.py | 2 +- samplescanner | 1 - record.py => samplescanner.py | 2 +- 20 files changed, 79 insertions(+), 59 deletions(-) rename lib/{constants.py => consts.py} (100%) delete mode 120000 samplescanner rename record.py => samplescanner.py (99%) diff --git a/lib/audio_helpers.py b/lib/audio_helpers.py index 04068b0..f7e81be 100644 --- a/lib/audio_helpers.py +++ b/lib/audio_helpers.py @@ -2,7 +2,7 @@ import numpy from .utils import note_name, percent_to_db from .record import record -from .constants import CLIPPING_THRESHOLD, \ +from .consts import CLIPPING_THRESHOLD, \ CLIPPING_CHECK_NOTE, \ EXIT_ON_CLIPPING, \ SAMPLE_RATE diff --git a/lib/compare.py b/lib/compare.py index af1641d..8e918ce 100644 --- a/lib/compare.py +++ b/lib/compare.py @@ -7,9 +7,9 @@ from tqdm import tqdm from tabulate import tabulate -from .utils import normalized, trim_mono_data -from .audio_helpers import fundamental_frequency -from .wavio import read_wave_file +from utils import normalized, trim_mono_data +from audio_helpers import fundamental_frequency +from wavio import read_wave_file import matplotlib.pyplot as plt @@ -106,18 +106,21 @@ def process_all(aifs): ) results = sorted( results, - key=lambda dl_dr_pl_pr_freqd_fa_fb: dl_dr_pl_pr_freqd_fa_fb[0] + dl_dr_pl_pr_freqd_fa_fb[1] + abs(dl_dr_pl_pr_freqd_fa_fb[4] - 1)) + key=lambda (dl, dr, + pl, pr, + freqd, + fa, fb): dl + dr + abs(freqd - 1)) with open('results.csv', 'wb') as f: writer = csv.writer(f) writer.writerows([headers]) writer.writerows(results) - print("%d results" % len(results)) - print(tabulate( + print "%d results" % len(results) + print tabulate( results, headers=headers, floatfmt='.4f' - )) + ) def graph_ffts(): @@ -136,9 +139,10 @@ def graph_ffts(): idx = numpy.argmax(numpy.abs(w)) freq = freqs[idx] plt.plot(w) - print(freq) - print(fundamental_frequency(normalized(list)), \ - fundamental_frequency(normalized(left + right))) + print freq + print \ + fundamental_frequency(normalized(list)), \ + fundamental_frequency(normalized(left + right)) # plt.show() @@ -185,11 +189,11 @@ def freq_shift(): shifted_diffr = normalized_difference(*aligned_sublists(wavea[1], waveb_shifted[1])) - print(files) - print('diffs\t\t', diffl, diffr) - print('shifted diffs\t', shifted_diffl, shifted_diffr) - print('freqs', freqd) - print('shifted freqs', shifted_freqd) + print files + print 'diffs\t\t', diffl, diffr + print 'shifted diffs\t', shifted_diffl, shifted_diffr + print 'freqs', freqd + print 'shifted freqs', shifted_freqd if __name__ == "__main__": diff --git a/lib/constants.py b/lib/consts.py similarity index 100% rename from lib/constants.py rename to lib/consts.py diff --git a/lib/deflac.py b/lib/deflac.py index 1813092..1e84ea3 100644 --- a/lib/deflac.py +++ b/lib/deflac.py @@ -5,11 +5,11 @@ import argparse import subprocess from tqdm import tqdm -from .sfzparser import SFZFile -from .wavio import read_wave_file -from .utils import normalized -from .record import RATE, save_to_file -from .constants import bit_depth +from sfzparser import SFZFile +from wavio import read_wave_file +from utils import normalized +from record import RATE, save_to_file +from consts import bit_depth def full_path(sfzfile, filename): diff --git a/lib/flacize.py b/lib/flacize.py index 20da19a..ab5e8c1 100644 --- a/lib/flacize.py +++ b/lib/flacize.py @@ -5,6 +5,7 @@ import argparse import subprocess from tqdm import tqdm + from .sfzparser import SFZFile, Group from .wavio import read_wave_file from .utils import group_by_attr, note_name @@ -128,8 +129,8 @@ def concat_samples(regions, path, name=None): filename, note_name(key))) for key, regions in - tqdm(iter(group_by_attr(group.regions, - 'key').items()))], []) + tqdm(group_by_attr(group.regions, + 'key').items())], []) print(group.just_group()) for region in output: print(region) diff --git a/lib/graph.py b/lib/graph.py index eaa60aa..07c958b 100644 --- a/lib/graph.py +++ b/lib/graph.py @@ -7,7 +7,7 @@ from tqdm import tqdm from tabulate import tabulate -from .wavio import read_wave_file +from wavio import read_wave_file import matplotlib.pyplot as plt diff --git a/lib/group_velcurves.py b/lib/group_velcurves.py index cf9a6f9..88c516a 100644 --- a/lib/group_velcurves.py +++ b/lib/group_velcurves.py @@ -1,6 +1,6 @@ import argparse -from .sfzparser import parse, Group -from .quantize import group_by_attr +from sfzparser import parse, Group +from quantize import group_by_attr parser = argparse.ArgumentParser( description='quantize and compress SFZ files' @@ -25,7 +25,7 @@ def group_by_pitch(regions): for region in regions for key, value in region.attributes.items() if should_group_key(key) - ] + list(DEFAULT_ATTRIBUTES.items())), [ + ] + DEFAULT_ATTRIBUTES.items()), [ region.without_attributes(should_group_key) for region in regions ]) @@ -41,4 +41,4 @@ def group_by_pitch(regions): groups = parse(open(filename).read()) regions = sum([group.flattened_regions() for group in groups], []) for group in group_by_pitch(regions): - print(group) + print group diff --git a/lib/loop.py b/lib/loop.py index e178fd4..023798b 100644 --- a/lib/loop.py +++ b/lib/loop.py @@ -1,6 +1,7 @@ import sys import numpy from tqdm import tqdm + from .truncate import read_wave_file from .audio_helpers import fundamental_frequency @@ -131,7 +132,13 @@ def fast_autocorrelate(x): f = numpy.fft.fft(xp) p = numpy.absolute(numpy.power(f, 2)) pi = numpy.fft.ifft(p) - result = numpy.real(pi)[:x.size / 2] / numpy.sum(numpy.power(xp, 2)) + + index = int(x.size / 2) + top = numpy.real(pi)[:index] + bottom = numpy.sum(numpy.power(xp, 2)) + result = top / bottom + + return result @@ -164,7 +171,7 @@ def find_loop_from_autocorrelation( min_loop_width_in_seconds=0.2, sample_rate=48000 ): - search_start /= 2 + search_start = int(search_start/2) max_autocorrelation_peak_width = int( min_loop_width_in_seconds * sample_rate ) diff --git a/lib/midi_helpers.py b/lib/midi_helpers.py index 2c7cac0..e2217ce 100644 --- a/lib/midi_helpers.py +++ b/lib/midi_helpers.py @@ -1,7 +1,6 @@ import time import rtmidi - CHANNEL_OFFSET = 0x90 - 1 CC_CHANNEL_OFFSET = 0xB0 - 1 @@ -37,6 +36,7 @@ def all_notes_off(midiout, midi_channel): def open_midi_port(midi_port_name): midiout = rtmidi.MidiOut() ports = midiout.get_ports() + for i, port_name in enumerate(ports): if not midi_port_name or midi_port_name.lower() in port_name.lower(): midiout.open_port(i) diff --git a/lib/quantize.py b/lib/quantize.py index 083d416..0d29829 100644 --- a/lib/quantize.py +++ b/lib/quantize.py @@ -1,8 +1,8 @@ import os import sys import argparse -from .sfzparser import SFZFile -from .utils import group_by_attr +from sfzparser import SFZFile +from utils import group_by_attr import itertools from collections import defaultdict @@ -14,8 +14,8 @@ def quantize_pitch(regions, pitch_levels=25): - lowestkey = min([int(x.attributes['key']) for x in regions]) - highestkey = max([int(x.attributes['key']) for x in regions]) + lowestkey = min(map(lambda x: int(x.attributes['key']), regions)) + highestkey = max(map(lambda x: int(x.attributes['key']), regions)) keyspan = highestkey - lowestkey pitch_skip = keyspan / pitch_levels @@ -32,7 +32,7 @@ def quantize_pitch(regions, pitch_levels=25): pitchmapping[key] = { 'lokey': key - (pitch_skip / 2), 'pitch_keycenter': key, - 'hikey': key + int(pitch_skip / 2) - (0 if evenly_divided else 1), + 'hikey': key + (pitch_skip / 2) - (0 if evenly_divided else 1), } for key, regions in group_by_attr(regions, 'key').items(): @@ -44,8 +44,8 @@ def quantize_pitch(regions, pitch_levels=25): def quantize_velocity(regions, velocity_levels=5): - lowestvel = min([int(x.attributes['xfin_loivel']) for x in regions]) - highestvel = max([int(x.attributes['xfin_hivel']) for x in regions]) + lowestvel = min(map(lambda x: int(x.attributes['xfin_loivel']), regions)) + highestvel = max(map(lambda x: int(x.attributes['xfin_hivel']), regions)) velspan = 127 pitch_skip = velspan / velocity_levels @@ -56,13 +56,13 @@ def quantize_velocity(regions, velocity_levels=5): # a dict of sample_pitch -> [lokey, hikey, pitch_keycenter] pitchmapping = {} for key in range( - lowestkey + int(pitch_skip / 2), - highestkey + 1 + int(pitch_skip / 2), + lowestkey + (pitch_skip / 2), + highestkey + 1 + (pitch_skip / 2), pitch_skip): pitchmapping[key] = { - 'lokey': key - int(pitch_skip / 2), + 'lokey': key - (pitch_skip / 2), 'pitch_keycenter': key, - 'hikey': key + int(pitch_skip / 2) - (0 if evenly_divided else 1), + 'hikey': key + (pitch_skip / 2) - (0 if evenly_divided else 1), } for key, regions in group_by_attr(regions, 'key').items(): @@ -111,6 +111,6 @@ def compute_sample_size(filename, regions): ) ) for region in output: - print(region) + print region # for group in groups: # print group diff --git a/lib/record.py b/lib/record.py index 81abdac..c7b64d9 100644 --- a/lib/record.py +++ b/lib/record.py @@ -1,7 +1,8 @@ import sys import numpy from struct import pack -from .constants import bit_depth, NUMPY_DTYPE, SAMPLE_RATE + +from .consts import bit_depth, NUMPY_DTYPE, SAMPLE_RATE from .utils import percent_to_db, dbfs_as_percent import pyaudio @@ -55,7 +56,6 @@ def get_input_device_name_by_index(audio_interface_index): py_audio = pyaudio.PyAudio() info = py_audio.get_host_api_info_by_index(0) input_interface_names = get_input_device_names(py_audio, info) - for index, name in input_interface_names.items(): if index == audio_interface_index: return name @@ -69,8 +69,8 @@ def get_input_device_name_by_index(audio_interface_index): def list_input_devices(device_names): lines = [] for index, name in sorted(device_names.items()): - lines.append("{:3d}. {}".format(index, name)) - return "\n".join(lines).encode("ascii", "ignore") + lines.append(u"{:3d}. {}".format(index, name)) + return u"\n".join(lines).encode("ascii", "ignore") def record( diff --git a/lib/send_notes.py b/lib/send_notes.py index ef84a78..5378181 100644 --- a/lib/send_notes.py +++ b/lib/send_notes.py @@ -1,6 +1,7 @@ import os import time from tqdm import tqdm + from .record import save_to_file, get_input_device_name_by_index from .sfzparser import SFZFile, Region from .pitch import compute_zones, Zone @@ -8,7 +9,7 @@ note_name, \ first_non_none, \ warn_on_clipping -from .constants import bit_depth, SAMPLE_RATE +from .consts import bit_depth, SAMPLE_RATE from .volume_leveler import level_volume from .flacize import flacize_after_sampling from .loop import find_loop_points diff --git a/lib/spectrogram.py b/lib/spectrogram.py index 1922cad..427a5a8 100644 --- a/lib/spectrogram.py +++ b/lib/spectrogram.py @@ -8,7 +8,7 @@ import numpy as np from matplotlib import pyplot as plt from numpy.lib import stride_tricks -from .wavio import read_wave_file +from wavio import read_wave_file def stft(sig, frame_size, overlap_fac=0.5, window=np.hanning): diff --git a/lib/starts_with_click.py b/lib/starts_with_click.py index 0613b2f..9ed42aa 100644 --- a/lib/starts_with_click.py +++ b/lib/starts_with_click.py @@ -1,6 +1,6 @@ import sys -from .wavio import read_wave_file -from .constants import bit_depth +from wavio import read_wave_file +from consts import bit_depth default_threshold_samples = (0.001 * float(2 ** (bit_depth - 1))) diff --git a/lib/truncate.py b/lib/truncate.py index d0ebf7b..9e7182b 100644 --- a/lib/truncate.py +++ b/lib/truncate.py @@ -1,4 +1,5 @@ import sys + from .wavio import read_wave_file from .utils import start_of, end_of diff --git a/lib/utils.py b/lib/utils.py index d8212df..b49fd7f 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -3,7 +3,8 @@ import math from numpy import inf -from .constants import default_silence_threshold, bit_depth +from .consts import default_silence_threshold, bit_depth + from collections import defaultdict diff --git a/lib/volume_leveler.py b/lib/volume_leveler.py index c65b447..c5edff9 100644 --- a/lib/volume_leveler.py +++ b/lib/volume_leveler.py @@ -1,10 +1,13 @@ import numpy import argparse -from .constants import bit_depth + +from .consts import bit_depth from .sfzparser import SFZFile, Group from .wavio import read_wave_file from .utils import group_by_attr from .flacize import full_path + +#from itertools import tee, izip from itertools import tee @@ -12,6 +15,7 @@ def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = tee(iterable) next(b, None) + #return izip(a, b) return zip(a, b) @@ -24,11 +28,13 @@ def peak_rms(data, window_size=480, limits=960): maxlimit = max([len(channel) for channel in data]) max_so_far = 0 for i in range( - max(index - limits, int(window_size / 2)), - min(index + limits, maxlimit - int(window_size / 2)) + max(index - limits, (window_size / 2)), + min(index + limits, maxlimit - (window_size / 2)) ): for channel in data: - window = channel[i - int(window_size / 2):i + int(window_size / 2)] + index1 = int(i - (window_size / 2)) + index2 = int(i + (window_size / 2)) + window = channel[index1:index2] if len(window) == 0: raise Exception("Cannot take mean of empty slice! Channel " "size %d, index %d, window size %d" % ( diff --git a/lib/wavio.py b/lib/wavio.py index 4b5c76a..76b0582 100644 --- a/lib/wavio.py +++ b/lib/wavio.py @@ -3,7 +3,7 @@ import numpy import subprocess -from .constants import NUMPY_DTYPE +from .consts import NUMPY_DTYPE def read_flac_file(filename, use_numpy=False): diff --git a/samplescanner b/samplescanner deleted file mode 120000 index c020662..0000000 --- a/samplescanner +++ /dev/null @@ -1 +0,0 @@ -record.py \ No newline at end of file diff --git a/record.py b/samplescanner.py similarity index 99% rename from record.py rename to samplescanner.py index bb719d6..a06c157 100755 --- a/record.py +++ b/samplescanner.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """Command-line interface for SampleScanner.""" From 077bc95e6ed96307b265903b7853c667238d0758 Mon Sep 17 00:00:00 2001 From: Norbert Kremeris Date: Mon, 10 Oct 2022 16:40:25 +0100 Subject: [PATCH 3/3] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2dfe3e2..c2acee4 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ SampleScanner is a command-line tool to turn MIDI instruments (usually hardware) ## Installation -Requires a working `python` (version 2.7), `pip`, and `ffmpeg` to be installed on the system. +Requires a working `python` (version 3.10), `pip`, and `ffmpeg` to be installed on the system. ``` git clone git@github.com:psobot/SampleScanner @@ -29,7 +29,7 @@ pip install -r requirements.txt ## How to run -Run `./samplescanner -h` for a full argument listing: +Run `./samplescanner.py -h` for a full argument listing: ```contentsof usage: samplescanner [-h] [--cc-before [CC_BEFORE [CC_BEFORE ...]]]