diff --git a/rnsutils/sfztoxrni.py b/rnsutils/sfztoxrni.py index 9f4259d..bbf3739 100644 --- a/rnsutils/sfztoxrni.py +++ b/rnsutils/sfztoxrni.py @@ -26,6 +26,7 @@ import re from copy import deepcopy from rnsutils.instrument import RenoiseInstrument, second_to_renoise_time, db_to_renoise_volume +from rnsutils.utils import ENCODING_NONE, ENCODING_FLAC, ENCODING_OGG, encode_audio_file __date__ = '2016-01-28' __updated__ = '2016-01-28' @@ -63,60 +64,11 @@ def sfz_note_to_midi_key(sfz_note): class SfzToXrni(object): - def __init__(self, sfz_path, show_unused=False, **kwargs): + def __init__(self, sfz_path, show_unused=False, encoding=ENCODING_NONE, **kwargs): + self.encoding = encoding self.sfz_path = sfz_path self.show_unused = show_unused - def convert_bag(self, sf2_bag, renoise_sample, renoise_modulation_set, default_sample, default_modulation_set): - - # sample looping - renoise_sample.LoopRelease = sf2_bag.sample_loop_on_noteoff - renoise_sample.LoopMode = "Forward" if sf2_bag.sample_loop else "Off" - renoise_sample.LoopStart = sf2_bag.cooked_loop_start - renoise_sample.LoopEnd = sf2_bag.cooked_loop_end - - # sample panning - renoise_sample.Panning = (sf2_bag.pan and sf2_bag.pan + 0.5) or default_sample.Panning - - # sample tuning - renoise_sample.Transpose = sf2_bag.tuning or default_sample.Transpose - renoise_sample.Finetune = (sf2_bag.fine_tuning and (int(128 * sf2_bag.fine_tuning) / 100.)) or ( - sf2_bag.sample and int(128 * (sf2_bag.sample.pitch_correction) / 100.)) or default_sample.Finetune - - # volume envelope - renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Attack.Value = self.to_renoise_time( - sf2_bag.volume_envelope_attack) or default_modulation_set.ahdsr_attack - - renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Decay.Value = self.to_renoise_time( - sf2_bag.volume_envelope_decay) or default_modulation_set.ahdsr_decay - - renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Hold.Value = self.to_renoise_time( - sf2_bag.volume_envelope_hold) or default_modulation_set.ahdsr_hold - - renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Sustain.Value = ( - sf2_bag.volume_envelope_sustain is not None and ( - sf2_bag.volume_envelope_sustain is not None and ( - max(0, - 1 - sf2_bag.volume_envelope_sustain / 96.))) or default_modulation_set.ahdsr_sustain) - - renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Release.Value = self.to_renoise_time( - sf2_bag.volume_envelope_release) or default_modulation_set.ahdsr_release - - # low pass filter - renoise_modulation_set.Devices.SampleMixerModulationDevice.Cutoff.Value = self.freq_to_cutoff( - sf2_bag.lp_cutoff) if sf2_bag.lp_cutoff else default_modulation_set.lp_cutoff - - # base note - renoise_sample.Mapping.BaseNote = sf2_bag.base_note or ( - sf2_bag.sample and sf2_bag.sample.original_pitch) or default_sample.Mapping.BaseNote - - # key mapping (key range and velocity) - renoise_sample.Mapping.NoteStart, renoise_sample.Mapping.NoteEnd = sf2_bag.key_range or ( - default_sample.Mapping.NoteStart, default_sample.Mapping.NoteEnd) - - renoise_sample.Mapping.VelocityStart, renoise_sample.Mapping.VelocityEnd = sf2_bag.velocity_range or ( - default_sample.Mapping.VelocityStart, default_sample.Mapping.VelocityEnd) - def load_default_sample_settings(self, renoise_global_sample, renoise_global_modulation_set): renoise_global_modulation_set.Devices.SampleMixerModulationDevice.Cutoff.Value = self.freq_to_cutoff(20000) renoise_global_modulation_set.Devices.SampleAhdsrModulationDevice.Attack.Value = 0 @@ -182,13 +134,13 @@ class SfzToXrni(object): # copy wav content from sfz to renoise sample_filename = search_case_insensitive_path( - os.path.join(self.sfz_path, str(renoise_sample.FileName))) + os.path.join(self.sfz_path, str(renoise_sample.FileName))) if sample_filename is None: logging.info("missing sample file '%s'", renoise_sample.FileName) else: with open(sample_filename, 'rb') as sample_content: - renoise_instrument.sample_data.append(sample_content.read()) + renoise_instrument.sample_data.append(encode_audio_file(sample_content.read(), self.encoding)) section_idx += 1 @@ -238,7 +190,7 @@ class SfzToXrni(object): if renoise_sample.FileName.pyval[0] in (r'/', r'\\'): renoise_sample.FileName = renoise_sample.FileName.pyval[1:] -# renoise_sample.Name = os.path.basename(value) + renoise_sample.Name, _ = os.path.splitext(os.path.basename(renoise_sample.FileName.pyval)) elif key == 'lokey': renoise_sample.Mapping.NoteStart = sfz_note_to_midi_key(value) elif key == 'hikey': @@ -254,15 +206,15 @@ class SfzToXrni(object): float(value)) elif key == 'ampeg_hold': renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Hold.Value = second_to_renoise_time( - float(value)) + float(value)) elif key == 'ampeg_decay': renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Decay.Value = second_to_renoise_time( - float(value)) + float(value)) elif key == 'ampeg_sustain': renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Sustain.Value = 1 - float(value) / 100. elif key == 'ampeg_release': renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Release.Value = second_to_renoise_time( - float(value)) + float(value)) elif key == 'tune': renoise_sample.Finetune = int(128 * float(value) / 100.) elif key == 'transpose': @@ -306,8 +258,8 @@ class SfzToXrni(object): if unused_keys and self.show_unused: sys.stderr.write( - "Unused key(s) for section {}:\n{}\n".format(section_name, - "\n".join([" - " + k for k in unused_keys]))) + "Unused key(s) for section {}:\n{}\n".format(section_name, + "\n".join([" - " + k for k in unused_keys]))) def main(argv=None): @@ -328,6 +280,8 @@ def main(argv=None): parser.add_argument("-d", "--debug", dest="debug", action="store_true", default=False, help="debug parsing [default: %(default)s]") + parser.add_argument("-e", "--encode", dest="encoding", choices=[ENCODING_NONE, ENCODING_FLAC, ENCODING_OGG], + default="none", help="encode samples into given format [default: %(default)s]") parser.add_argument("-q", "--quiet", dest="quiet", action="store_true", default=False, help="quiet operation [default: %(default)s]") parser.add_argument("-o", "--ouput-dir", dest="output_dir", diff --git a/rnsutils/utils.py b/rnsutils/utils.py index 988e959..d0fb80d 100644 --- a/rnsutils/utils.py +++ b/rnsutils/utils.py @@ -1,4 +1,52 @@ +import logging +import subprocess + +import os +import tempfile + + def guesstimate_audio_extension(data): if len(data) > 8 and data[0:8] == b'fLaC\0\0\0\x22': return "flac" + if len(data) > 3 and data[0:3] == b'Ogg': + return "ogg" return None + + +ENCODING_NONE = "none" +ENCODING_FLAC = "flac" +ENCODING_OGG = "ogg" + + +def encode_audio_file(sample_content, encoding): + if encoding == ENCODING_FLAC: + in_filename = tempfile.NamedTemporaryFile(suffix='.wav', delete=False).name + out_filename = tempfile.NamedTemporaryFile(suffix='.flac', delete=False).name + try: + with open(in_filename, "wb") as infile: + infile.write(sample_content) + subprocess.check_call(["flac", in_filename, "-f", "-o", out_filename]) + with open(out_filename, "rb") as outfile: + return outfile.read() + except: + logging.exception("Error while converting to flac") + finally: + os.remove(in_filename) + os.remove(out_filename) + + elif encoding == ENCODING_OGG: + in_filename = tempfile.NamedTemporaryFile(suffix='.wav', delete=False).name + out_filename = tempfile.NamedTemporaryFile(suffix='.ogg', delete=False).name + try: + with open(in_filename, "wb") as infile: + infile.write(sample_content) + subprocess.check_call(["oggenc", in_filename, "-o", out_filename]) + with open(out_filename, "rb") as outfile: + return outfile.read() + except: + logging.exception("Error while converting to ogg") + finally: + os.remove(in_filename) + os.remove(out_filename) + + return sample_content