Browse Source

trying to find the default path in tars

master
Nils 1 year ago
parent
commit
28c9320ae5
  1. 2
      engine/api.py
  2. 4
      engine/auditioner.py
  3. 23
      engine/instrument.py
  4. 52
      engine/main.py
  5. 41
      extra/generate-round-robin.py
  6. 1024
      extra/test.sfz
  7. 4
      qtgui/instrument.py
  8. 38
      template/calfbox/sampler_layer.c
  9. 2
      template/calfbox/tarfile.h

2
engine/api.py

@ -148,7 +148,7 @@ def auditionerInstrument(idkey:tuple):
var = originalInstrument.currentVariant
else:
var = originalInstrument.defaultVariant
session.data.auditioner.loadInstrument(originalInstrument.tarFilePath, var)
session.data.auditioner.loadInstrument(originalInstrument.tarFilePath, originalInstrument.rootPrefixPath, var)
callbacks._auditionerInstrumentChanged(libraryId, instrumentId)
def getAvailableAuditionerPorts()->dict:

4
engine/auditioner.py

@ -92,7 +92,7 @@ class Auditioner(object):
return result
def loadInstrument(self, tarFilePath, variantSfzFileName:str):
def loadInstrument(self, tarFilePath, rootPrefixPath:str, variantSfzFileName:str):
"""load_patch_from_tar is blocking. This function will return when the instrument is ready
to play.
@ -103,7 +103,7 @@ class Auditioner(object):
#newProgramNumber = self.instrumentLayer.engine.get_unused_program()
programNumber = 1
name = variantSfzFileName
self.program = self.instrumentLayer.engine.load_patch_from_tar(programNumber, tarFilePath, variantSfzFileName, name)
self.program = self.instrumentLayer.engine.load_patch_from_tar(programNumber, tarFilePath, rootPrefixPath+variantSfzFileName, name)
self.instrumentLayer.engine.set_patch(1, programNumber) #1 is the channel, counting from 1. #TODO: we want this to be on all channels.
self.currentVariant = variantSfzFileName
logger.info(f"Finished loading samples for auditioner {variantSfzFileName}")

23
engine/instrument.py

@ -95,13 +95,21 @@ class Instrument(object):
self.cboxMidiPortUid = None
self.metadata = metadata #parsed from the ini file. See self.exportMetadata for the pythonic dict
self.instrumentsInLibraryCount = None #injected by the creating process. Counted live in the program, not included in the ini file.
self.tarFilePath = tarFilePath
self.name = metadata["name"]
self.midiInputPortName = metadata["name"]
self.midiInputPortName = f"[{libraryId}-{self.id}] " + metadata["name"]
self.variants = metadata["variants"].split(",")
self.defaultVariant = metadata["defaultVariant"]
self.enabled = False #means loaded.
if "root" in metadata:
if metadata["root"].endswith("/"):
self.rootPrefixPath = metadata["root"]
else:
self.rootPrefixPath = metadata["root"] + "/"
else:
self.rootPrefixPath = ""
#Calfbox. The JACK ports are constructed without samples at first.
self.scene = cbox.Document.get_engine().new_scene()
@ -138,6 +146,7 @@ class Instrument(object):
#We could call self.load() now, but we delay that for the user experience. See docstring.
def exportStatus(self):
"""The call-often function to get the instrument status. Includes only data that can
actually change during runtime."""
@ -174,6 +183,7 @@ class Instrument(object):
result["defaultVariant"] = self.metadata["defaultVariant"] #str
result["defaultVariantWithoutSfzExtension"] = self.metadata["defaultVariant"].rstrip(".sfz") #str
result["tags"] = self.metadata["tags"].split(",") # list of str
result["instrumentsInLibraryCount"] = self.instrumentsInLibraryCount # int
#Optional Tags.
@ -224,11 +234,20 @@ class Instrument(object):
#help (self.allInstrumentLayers[self.defaultPortUid].engine) #shows the functions to load programs into channels etc.
#newProgramNumber = self.instrumentLayer.engine.get_unused_program()
programNumber = self.metadata["variants"].index(variantSfzFileName) #counts from 1
self.program = self.instrumentLayer.engine.load_patch_from_tar(programNumber, self.tarFilePath, variantSfzFileName, self.metadata["name"])
self.program = self.instrumentLayer.engine.load_patch_from_tar(programNumber, self.tarFilePath, self.rootPrefixPath+variantSfzFileName, self.metadata["name"]) #tar_name, sfz_name, display_name
self.instrumentLayer.engine.set_patch(1, programNumber) #1 is the channel, counting from 1. #TODO: we want this to be on all channels.
#self.program is always None ?
#self.instrumentLayer is type DocInstrument
#self.instrumentLayer.engine is type SamplerEngine
#self.instrumentLayer.engine.get_patches returns a dict like {0: ('Harpsichord.sfz', <calfbox.cbox.SamplerProgram object at 0x7fd324226a60>, 16)}
#Only ever index 0 is used because we have one patch per port
#self.instrumentLayer.engine.get_patches()[0][1] is the cbox.SamplerProgram. That should have been self.program, but isn't!
self.program = self.instrumentLayer.engine.get_patches()[0][1]
print (self.program.status())
self.currentVariant = variantSfzFileName
logger.info(f"Finished loading samples for instrument {variantSfzFileName} with id key {self.idKey}")
def enable(self):
self.enabled = True

52
engine/main.py

@ -22,7 +22,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging; logger = logging.getLogger(__name__); logger.info("import")
#Python Standard Lib
import os.path
import configparser
import pathlib
import tarfile
@ -77,9 +76,12 @@ class Data(TemplateData):
#Parse all tar libraries and load them all. ALL OF THEM!
#for each library in ...
self.libraries = {} # libraryId:int : Library-object
lib = Library(parentData=self, tarFilePath="/home/nils/lss/test-data.tar")
self.libraries[lib.id] = lib
basePath = pathlib.Path("/home/nils/samples/Tembro/out/")
for f in basePath.glob('*.tar'):
if f.is_file() and f.suffix == ".tar":
lib = Library(parentData=self, tarFilePath=f)
self.libraries[lib.id] = lib
def exportMetadata(self)->dict:
"""Data we sent in callbacks. This is the initial 'build-the-instrument-database' function.
@ -100,39 +102,51 @@ class Library(object):
You get all metadata from this.
The samples are not loaded when Library() returns. The API can loop over Instrument.allInstruments
and call instr.loadSamples() and send a feedback to callbacks.
There is also a shortcut. First only an external .ini is loaded, which is much faster than
unpacking the tar. If no additional data like images are needed (which is always the case
at this version 1.0) we parse this external ini directly to build our database.
"""
def __init__(self, parentData, tarFilePath):#
self.parentData = parentData
self.tarFilePath = pathlib.Path(tarFilePath)
if not tarFilePath.endswith(".tar"):
self.tarFilePath = tarFilePath #pathlib.Path()
if not tarFilePath.suffix == ".tar":
raise RuntimeError(f"Wrong file {tarFilePath}")
with tarfile.open(name=tarFilePath, mode='r:') as opentarfile:
iniFileObject = TextIOWrapper(opentarfile.extractfile("library.ini"))
self.config = configparser.ConfigParser()
self.config.read_file(iniFileObject)
#self.config is permant now. We can close the file object
"""
#Extract an image file. But only if it exists. tarfile.getmember is basically an exist-check that trows KeyError if not
try:
imageAsBytes = extractfile("logo.png").read() #Qt can handle the format
except KeyError: #file not found
imageAsBytes = None
"""
needTarData = False #TODO: If we have images etc. in the future.
if needTarData:
with tarfile.open(name=tarFilePath, mode='r:') as opentarfile:
iniFileObject = TextIOWrapper(opentarfile.extractfile("library.ini"))
self.config = configparser.ConfigParser()
self.config.read_file(iniFileObject)
#self.config is permant now. We can close the file object
"""
#Extract an image file. But only if it exists. tarfile.getmember is basically an exist-check that trows KeyError if not
try:
imageAsBytes = extractfile("logo.png").read() #Qt can handle the format
except KeyError: #file not found
imageAsBytes = None
"""
else: #the default case for now.
iniName = str(tarFilePath)[:-4] + ".ini"
self.config = configparser.ConfigParser()
self.config.read(iniName)
self.id = self.config["library"]["id"]
instrumentSections = self.config.sections()
instrumentSections.remove("library")
instrumentsInLibraryCount = len(instrumentSections)
self.instruments = {} # instrId : Instrument()
for iniSection in instrumentSections:
instrObj = Instrument(self, self.config["library"]["id"], self.config[iniSection], tarFilePath)
instrObj.instrumentsInLibraryCount = instrumentsInLibraryCount
self.instruments[self.config[iniSection]["id"]] = instrObj
#At a later point Instrument.loadSamples() must be called. This is done in the API.
def exportMetadata(self)->dict:
"""Return a dictionary with each key is an instrument id, but also a special key "library"
with our own metadata. Allows the callbacks receiver to construct a hierarchy"""

41
extra/generate-round-robin.py

@ -0,0 +1,41 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2021, Nils Hilbricht, Germany ( https://www.hilbricht.net )
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),
This application is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
"""
Outputs to standard output
Uses midi note keys as sample file names.
"""
groupstring = """
<group>
seq_length=3
key={}
<region>seq_position=1 sample={}.wav transpose=0
<region>seq_position=2 sample={}.wav transpose=-1
<region>seq_position=3 sample={}.wav transpose=-2
"""
for keynum in range(127+1):
print (groupstring.format(keynum, keynum, keynum+1, keynum+2))

1024
extra/test.sfz

File diff suppressed because it is too large

4
qtgui/instrument.py

@ -309,7 +309,9 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
elif key == "id-key": #tuple
libId, instrId = instrumentDict[key]
self.setText(index, f"{libId}-{instrId}")
#self.setText(index, f"{libId}-{str(instrId).zfill(3)}")
zeros = int(instrumentDict["instrumentsInLibraryCount"]/10)+1
self.setText(index, str(instrId).zfill(zeros))
"""
elif key == "state": #use parameter for initial value. loaded from file or default = False.

38
template/calfbox/sampler_layer.c

@ -1034,8 +1034,8 @@ static gboolean sampler_layer_process_cmd(struct cbox_command_target *ct, struct
if (!cbox_check_fb_channel(fb, cmd->command, error))
return FALSE;
if (!((!layer->parent_program || cbox_execute_on(fb, NULL, "/parent_program", "o", error, layer->parent_program)) &&
(!layer->parent || cbox_execute_on(fb, NULL, "/parent", "o", error, layer->parent)) &&
if (!((!layer->parent_program || cbox_execute_on(fb, NULL, "/parent_program", "o", error, layer->parent_program)) &&
(!layer->parent || cbox_execute_on(fb, NULL, "/parent", "o", error, layer->parent)) &&
CBOX_OBJECT_DEFAULT_STATUS(layer, fb, error)))
return FALSE;
return TRUE;
@ -1148,7 +1148,7 @@ struct sampler_layer *sampler_layer_new(struct sampler_module *m, struct sampler
memset(l, 0, sizeof(struct sampler_layer));
CBOX_OBJECT_HEADER_INIT(l, sampler_layer, doc);
cbox_command_target_init(&l->cmd_target, sampler_layer_process_cmd, l);
l->module = m;
l->child_layers = g_hash_table_new(NULL, NULL);
if (parent)
@ -1205,7 +1205,7 @@ struct sampler_layer *sampler_layer_new(struct sampler_module *m, struct sampler
#define PROC_FIELDS_CLONE_dahdsr(name, parname, index) \
DAHDSR_FIELDS(PROC_SUBSTRUCT_CLONE, name, dst, src) \
if (!copy_hasattr) \
DAHDSR_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, dst)
DAHDSR_FIELDS(PROC_SUBSTRUCT_RESET_HAS_FIELD, name, dst)
#define PROC_FIELDS_CLONE_lfo(name, parname, index) \
LFO_FIELDS(PROC_SUBSTRUCT_CLONE, name, dst, src) \
if (!copy_hasattr) \
@ -1291,11 +1291,11 @@ static inline int sampler_filter_num_stages(float cutoff, enum sampler_filter_ty
#define END_VALUE_amp_velcurve (l->amp_veltrack < 0 ? dB2gain(l->amp_veltrack * 84.0 / 100.0) : 1)
#define IS_QUADRATIC_amp_velcurve l->velcurve_quadratic
#define PROC_FIELDS_FINALISER(type, name, def_value)
#define PROC_FIELDS_FINALISER(type, name, def_value)
#define PROC_FIELDS_FINALISER_string(name)
#define PROC_FIELDS_FINALISER_midicurve(name) \
sampler_midi_curve_interpolate(&l->name, l->computed.eff_##name, START_VALUE_##name, END_VALUE_##name, IS_QUADRATIC_##name);
#define PROC_FIELDS_FINALISER_enum(type, name, def_value)
#define PROC_FIELDS_FINALISER_enum(type, name, def_value)
#define PROC_FIELDS_FINALISER_dBamp(type, name, def_value) \
l->name##_linearized = dB2gain(l->name);
#define PROC_FIELDS_FINALISER_dahdsr(name, parname, index) \
@ -1319,7 +1319,7 @@ void sampler_layer_data_finalize(struct sampler_layer_data *l, struct sampler_la
l->computed.eff_waveform = cbox_wavebank_get_waveform(p->name, p->tarfile, p->sample_dir, l->sample, &error);
if (!l->computed.eff_waveform)
{
g_warning("Cannot load waveform %s: %s", l->sample, error ? error->message : "unknown error");
g_warning("Cannot load waveform \"%s\" in sample_dir \"%s\" : \"%s\"", l->sample, p->sample_dir, error ? error->message : "unknown error");
g_error_free(error);
}
}
@ -1330,7 +1330,7 @@ void sampler_layer_data_finalize(struct sampler_layer_data *l, struct sampler_la
l->computed.eff_is_silent = !l->sample || !strcmp(l->sample, "*silence");
l->sample_changed = FALSE;
}
l->computed.eff_use_keyswitch = ((l->sw_down != -1) || (l->sw_up != -1) || (l->sw_last != -1) || (l->sw_previous != -1));
l->computed.eff_use_simple_trigger_logic =
(l->seq_length == 1 && l->seq_position == 1) &&
@ -1457,7 +1457,7 @@ void sampler_layer_load_overrides(struct sampler_layer *l, const char *cfg_secti
char *imp = cbox_config_get_string(cfg_section, "import");
if (imp)
sampler_layer_load_overrides(l, imp);
struct layer_foreach_struct lfs = {
.layer = l,
.cfg_section = cfg_section
@ -1478,7 +1478,7 @@ static void sampler_layer_apply_unknown(struct sampler_layer *l, const char *key
{
if (!l->unknown_keys)
l->unknown_keys = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
g_hash_table_insert(l->unknown_keys, g_strdup(key), g_strdup(value));
}
@ -1628,16 +1628,16 @@ gchar *sampler_layer_to_string(struct sampler_layer *lr, gboolean show_inherited
char floatbuf[G_ASCII_DTOSTR_BUF_SIZE];
int floatbufsize = G_ASCII_DTOSTR_BUF_SIZE;
SAMPLER_FIXED_FIELDS(PROC_FIELDS_TO_FILEPTR)
for(struct sampler_noteinitfunc *nd = l->voice_nifs; nd; nd = nd->next)
{
if (!nd->value.has_value && !nd->value.has_curve && !nd->value.has_step && !show_inherited)
continue;
#define PROC_ENVSTAGE_NAME(name, index, def_value) #name,
#define PROC_ENVSTAGE_NAME(name, index, def_value) #name,
static const char *env_stages[] = { DAHDSR_FIELDS(PROC_ENVSTAGE_NAME) "start" };
uint32_t v = nd->key.variant;
g_ascii_dtostr(floatbuf, floatbufsize, nd->value.value);
if (nd->key.notefunc_voice == sampler_nif_addrandom && v >= 0 && v <= 2)
g_string_append_printf(outstr, " %s_random=%s", addrandom_variants[v], floatbuf);
else if (nd->key.notefunc_voice == sampler_nif_vel2pitch)
@ -1825,7 +1825,7 @@ gchar *sampler_layer_to_string(struct sampler_layer *lr, gboolean show_inherited
while(g_hash_table_iter_next(&hti, (gpointer *)&key, (gpointer *)&value))
g_string_append_printf(outstr, " %s=%s", key, value);
}
gchar *res = outstr->str;
g_string_free(outstr, FALSE);
return res;
@ -1935,7 +1935,7 @@ static int sampler_layer_update_cmd_prepare(void *data)
struct sampler_layer_update_cmd *cmd = data;
cmd->old_data = cmd->layer->runtime;
cmd->new_data = calloc(1, sizeof(struct sampler_layer_data));
sampler_layer_data_clone(cmd->new_data, &cmd->layer->data, TRUE);
sampler_layer_data_finalize(cmd->new_data, cmd->layer->parent ? &cmd->layer->parent->data : NULL, cmd->layer->parent_program);
if (cmd->layer->runtime == NULL)
@ -1952,7 +1952,7 @@ static int sampler_layer_update_cmd_prepare(void *data)
static int sampler_layer_update_cmd_execute(void *data)
{
struct sampler_layer_update_cmd *cmd = data;
for (int i = 0; i < 16; i++)
{
FOREACH_VOICE(cmd->module->channels[i].voices_running, v)
@ -1983,7 +1983,7 @@ static int sampler_layer_update_cmd_execute(void *data)
static void sampler_layer_update_cmd_cleanup(void *data)
{
struct sampler_layer_update_cmd *cmd = data;
sampler_layer_data_destroy(cmd->old_data);
free(cmd);
}
@ -2008,13 +2008,13 @@ void sampler_layer_update(struct sampler_layer *l)
.execute = sampler_layer_update_cmd_execute,
.cleanup = sampler_layer_update_cmd_cleanup,
};
struct sampler_layer_update_cmd *lcmd = malloc(sizeof(struct sampler_layer_update_cmd));
lcmd->module = l->module;
lcmd->layer = l;
lcmd->new_data = NULL;
lcmd->old_data = NULL;
cbox_rt_execute_cmd_async(l->module->module.rt, &rtcmd, lcmd);
}

2
template/calfbox/tarfile.h

@ -38,7 +38,7 @@ struct cbox_tarfile
int refs;
GHashTable *items_byname;
GHashTable *items_byname_nc;
char *file_pathname;
char *file_pathname; //full path to the .tar file with filename.ext
};
struct cbox_tarpool

Loading…
Cancel
Save