def generate_sample( limit, midiout, note, velocity, midi_channel, threshold, print_progress=False, audio_interface_name=None, sample_rate=SAMPLE_RATE, ): all_notes_off(midiout, midi_channel) def after_start(): midiout.send_message([CHANNEL_OFFSET + midi_channel, note, velocity]) def on_time_up(): midiout.send_message([CHANNEL_OFFSET + midi_channel, note, 0]) return True # Get the release after keyup return record( limit=limit, after_start=after_start, on_time_up=on_time_up, threshold=threshold, print_progress=print_progress, audio_interface_name=audio_interface_name, sample_rate=sample_rate, )
def check_for_clipping( midiout, midi_channel, threshold, bit_depth, audio_interface_name, ): time.sleep(1) print "Checking for clipping and balance on note %s..." % ( note_name(CLIPPING_CHECK_NOTE)) sample_width, data, release_time = generate_sample( limit=2.0, midiout=midiout, note=CLIPPING_CHECK_NOTE, velocity=127, midi_channel=midi_channel, threshold=threshold, print_progress=True, audio_interface_name=audio_interface_name, ) max_volume = (numpy.amax(numpy.absolute(data)) / float(2**(bit_depth - 1))) # All notes off, but like, a lot, again for _ in xrange(0, 2): all_notes_off(midiout, midi_channel) 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!" % ( percent_to_db(max_volume), percent_to_db(CLIPPING_THRESHOLD)) if EXIT_ON_CLIPPING: raise ValueError("Clipping detected at max volume!")
def sample_program( output_folder='foo', low_key=21, high_key=109, max_attempts=8, midi_channel=1, midi_port_name=None, audio_interface_name=None, program_number=None, flac=True, velocity_levels=VELOCITIES, key_range=1, cleanup_aif_files=True, limit=None, looping_enabled=False, print_progress=False, has_portamento=False, sample_asc=False, sample_rate=SAMPLE_RATE, ): if (key_range % 2) != 1: raise NotImplementedError("Key skip must be an odd number for now.") midiout = open_midi_port(midi_port_name) path_prefix = output_folder if program_number is not None: print "Sampling program number %d into path %s" % ( program_number, output_folder ) else: print "Sampling into path %s" % (output_folder) try: os.mkdir(path_prefix) except OSError: pass sfzfile = os.path.join(path_prefix, 'file.sfz') try: regions = sum([group.regions for group in SFZFile(open(sfzfile).read()).groups], []) regions = [region for region in regions if region.exists(path_prefix)] except IOError: regions = [] if program_number is not None: print "Sending program change to program %d..." % program_number midiout.send_message([ CHANNEL_OFFSET + midi_channel, 0xC0, program_number ]) # All notes off, but like, a lot for _ in xrange(0, 2): all_notes_off(midiout, midi_channel) threshold = sample_threshold_from_noise_floor( bit_depth, audio_interface_name ) check_for_clipping( midiout, midi_channel, threshold, bit_depth, audio_interface_name ) groups = [] note_regions = [] key_range_under = key_range / 2 key_range_over = key_range / 2 notes_to_sample = range( low_key, (high_key - key_range_over) + 1, key_range ) for note, velocity, done_note in tqdm(list(all_notes( notes_to_sample, velocity_levels, sample_asc ))): keys = range(note + key_range_under, note + key_range_over + 1) if not keys: keys = [note] already_sampled_region = first_non_none([ region for region in regions if region.attributes['hivel'] == str(velocity) and region.attributes.get( 'key', region.attributes.get( 'pitch_keycenter', None )) == str(note)]) if already_sampled_region is None: filename = os.path.join(path_prefix, filename_for(note, velocity)) if print_progress: print "Sampling %s at velocity %s..." % ( note_name(note), velocity ) if has_portamento: sample_width, data, release_time = generate_sample( limit=PORTAMENTO_PRESAMPLE_LIMIT, midiout=midiout, note=note, velocity=velocity, midi_channel=midi_channel, threshold=threshold, print_progress=print_progress, audio_interface_name=audio_interface_name, sample_rate=sample_rate, ) time.sleep(PORTAMENTO_PRESAMPLE_WAIT) for attempt in xrange(0, MAX_ATTEMPTS): try: region = generate_and_save_sample( limit=limit, midiout=midiout, note=note, velocity=velocity, midi_channel=midi_channel, filename=filename, threshold=threshold, velocity_levels=velocity_levels, keys=keys, looping_enabled=looping_enabled, print_progress=print_progress, audio_interface_name=audio_interface_name, sample_rate=sample_rate, ) if region: regions.append(region) note_regions.append(region) with open(sfzfile, 'w') as file: file.write("\n".join([str(r) for r in regions])) elif PRINT_SILENCE_WARNINGS: print "Got no sound for %s at velocity %s." % ( note_name(note), velocity ) except IOError: pass else: break else: print "Could not sample %s at vel %s: too many IOErrors." % ( note_name(note), velocity ) else: note_regions.append(already_sampled_region) if done_note and len(note_regions) > 0: groups.append(level_volume(note_regions, output_folder)) note_regions = [] # Write the volume-leveled output: with open(sfzfile + '.leveled.sfz', 'w') as file: file.write("\n".join([str(group) for group in groups])) if flac: # Do a FLAC compression pass afterwards # TODO: Do this after each note if possible # would require graceful re-parsing of FLAC-combined regions flacize_after_sampling( output_folder, groups, sfzfile, cleanup_aif_files=True )