From fb441c46a8df05b3799d30051d59187e6ec8b98f Mon Sep 17 00:00:00 2001 From: Olivier Jolly Date: Fri, 29 Jan 2016 21:39:18 +0100 Subject: [PATCH] Add even more SFZ operand support Loop was added (but untested), sample cycling (but it prevents layering for the same instrument), pan, volume, transpose, complete amplification adshr. --- rnsutils/instrument.py | 9 +++++++ rnsutils/sfztoxrni.py | 59 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/rnsutils/instrument.py b/rnsutils/instrument.py index 333e5d2..589fd80 100644 --- a/rnsutils/instrument.py +++ b/rnsutils/instrument.py @@ -13,6 +13,9 @@ from .utils import guesstimate_audio_extension def second_to_renoise_time(duration): return math.pow(duration / 60., 1 / 3.) if duration else None +def db_to_renoise_volume(volume): + return math.exp(volume / 8.68) if volume else None + class RenoiseSample(ObjectifiedElement): pass @@ -69,6 +72,12 @@ class RenoiseModulationSet(ObjectifiedElement): class RenoiseInstrument(object): + OVERLAP_CYCLE = "Cycle" + OVERLAP_ALL = "Play All" + + LOOP_NONE = "Off" + LOOP_FORWARD = "Forward" + FILTER_NONE = 0 FILTER_CLEAN_HP = 5 FILTER_CLEAN_LP = 1 diff --git a/rnsutils/sfztoxrni.py b/rnsutils/sfztoxrni.py index 8a4da5a..9f4259d 100644 --- a/rnsutils/sfztoxrni.py +++ b/rnsutils/sfztoxrni.py @@ -25,7 +25,7 @@ import os import re from copy import deepcopy -from rnsutils.instrument import RenoiseInstrument, second_to_renoise_time +from rnsutils.instrument import RenoiseInstrument, second_to_renoise_time, db_to_renoise_volume __date__ = '2016-01-28' __updated__ = '2016-01-28' @@ -143,6 +143,7 @@ class SfzToXrni(object): renoise_instrument.root.find('GlobalProperties/*[Name="SF2 reverb"]').Value = 0 renoise_instrument.root.find('GlobalProperties/*[Name="SF2 chorus"]').Value = 0 + renoise_instrument.root.SampleGenerator.KeyzoneOverlappingMode = RenoiseInstrument.OVERLAP_ALL with open(sfz_filename, 'rt') as sfz_file: # convert instrument meta data @@ -163,7 +164,7 @@ class SfzToXrni(object): if section_name == 'group': self.convert_section(section_name, section_content, renoise_default_sample, renoise_default_modulation_set, renoise_default_sample, - renoise_default_modulation_set) + renoise_default_modulation_set, renoise_instrument) continue # convert sample meta data in xml @@ -174,7 +175,7 @@ class SfzToXrni(object): renoise_sample.ModulationSetIndex = section_idx self.convert_section(section_name, section_content, renoise_sample, renoise_modulation_set, - renoise_default_sample, renoise_default_modulation_set) + renoise_default_sample, renoise_default_modulation_set, renoise_instrument) renoise_instrument.root.SampleGenerator.Samples.append(renoise_sample) renoise_instrument.root.SampleGenerator.ModulationSets.append(renoise_modulation_set) @@ -224,14 +225,19 @@ class SfzToXrni(object): return math.pow(envelope_attenuation / 60., 1 / 3.) if envelope_attenuation else None def convert_section(self, section_name, section_content, renoise_sample, renoise_modulation_set, - renoise_default_sample, renoise_default_modulation_set): + renoise_default_sample, renoise_default_modulation_set, renoise_instrument): unused_keys = [] for key in section_content.keys(): - value = section_content[key] + value = section_content[key].lower() if key == 'sample': renoise_sample.FileName = re.sub(r'\\+', r'/', value) + + # remove first char if file separator + if renoise_sample.FileName.pyval[0] in (r'/', r'\\'): + renoise_sample.FileName = renoise_sample.FileName.pyval[1:] + # renoise_sample.Name = os.path.basename(value) elif key == 'lokey': renoise_sample.Mapping.NoteStart = sfz_note_to_midi_key(value) @@ -243,14 +249,28 @@ class SfzToXrni(object): renoise_sample.Mapping.VelocityEnd = value elif key == 'pitch_keycenter': renoise_sample.Mapping.BaseNote = sfz_note_to_midi_key(value) - elif key == 'ampeg_release': - renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Release.Value = second_to_renoise_time( - float(value)) elif key == 'ampeg_attack': renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Attack.Value = second_to_renoise_time( float(value)) + elif key == 'ampeg_hold': + renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Hold.Value = second_to_renoise_time( + float(value)) + elif key == 'ampeg_decay': + renoise_modulation_set.Devices.SampleAhdsrModulationDevice.Decay.Value = second_to_renoise_time( + 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)) elif key == 'tune': renoise_sample.Finetune = int(128 * float(value) / 100.) + elif key == 'transpose': + renoise_sample.Transpose = value + elif key == 'volume': + renoise_sample.Volume = db_to_renoise_volume(float(value)) + elif key == 'pan': + renoise_sample.Panning = 0.5 + float(value) / 200. elif key == 'fil_type': if value.lower().startswith('lp'): renoise_modulation_set.FilterType = RenoiseInstrument.FILTER_CLEAN_LP @@ -258,9 +278,32 @@ class SfzToXrni(object): renoise_modulation_set.FilterType = RenoiseInstrument.FILTER_CLEAN_HP else: renoise_modulation_set.FilterType = RenoiseInstrument.FILTER_NONE + elif key == 'loop_mode': + if value == 'one_shot': + renoise_sample.LoopMode = RenoiseInstrument.LOOP_FORWARD + else: + renoise_sample.LoopMode = RenoiseInstrument.LOOP_NONE + elif key == 'loop_start': + renoise_sample.LoopStart = value + elif key == 'loop_end': + renoise_sample.LoopEnd = value + elif key == 'seq_position': + if int(value) > 1: + logging.info("Switch the entire instrument to sample cycling") + renoise_instrument.root.SampleGenerator.KeyzoneOverlappingMode = RenoiseInstrument.OVERLAP_CYCLE + elif key == 'seq_length': + pass + elif key == 'group': + logging.info("Ignoring mute group info") + elif key == 'off_by': + logging.info("Ignoring mute group info") else: unused_keys.append(key) + # disable loop when no loop duration was set + if renoise_sample.LoopStart.pyval is None or renoise_sample.LoopEnd.pyval is None: + renoise_sample.LoopMode = RenoiseInstrument.LOOP_NONE + if unused_keys and self.show_unused: sys.stderr.write( "Unused key(s) for section {}:\n{}\n".format(section_name,