Browse Source

update template

master
Nils 3 years ago
parent
commit
81fd601799
  1. 29
      template/calfbox/jackio.c
  2. 5
      template/calfbox/py/cbox.py
  3. 77
      template/calfbox/py/metadata.py
  4. 19
      template/calfbox/sampler.c
  5. 1
      template/calfbox/sampler.h
  6. 7
      template/calfbox/sampler_api_example5.py
  7. 10
      template/calfbox/sampler_channel.c
  8. 12
      template/calfbox/sampler_prg.c

29
template/calfbox/jackio.c

@ -1061,6 +1061,35 @@ static gboolean cbox_jack_io_process_cmd(struct cbox_command_target *ct, struct
} }
return TRUE; return TRUE;
} }
else if (!strcmp(cmd->command, "/client_set_property") && !strcmp(cmd->arg_types, "sss"))
/*same as set_property above, but works directly on our own jack client.
parameters: key, value, type according to jack_property_t (empty or NULL for string)
*/
{
const char *key = CBOX_ARG_S(cmd, 0);
const char *value = CBOX_ARG_S(cmd, 1);
const char *type = CBOX_ARG_S(cmd, 2);
char* subject;
subject = jack_get_uuid_for_client_name(jii->client, jii->client_name); //lookup our own client
if (!subject) {
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot get string UUID for our jack client.");
return FALSE;
}
jack_uuid_t j_client_uuid_t;
if (jack_uuid_parse(subject, &j_client_uuid_t)) { //from jack/uuid.h // 0 on success
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "jack_uuid_parse() couldn't parse our string client UUID %s as numerical jack_uuid_t %ld", subject, j_client_uuid_t);
return FALSE;
}
if (jack_set_property(jii->client, j_client_uuid_t, key, value, type)) // 0 on success
{
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Set client property key:'%s' value: '%s' was not successful", key, value);
return FALSE;
}
return TRUE;
}
else if (!strcmp(cmd->command, "/get_property") && !strcmp(cmd->arg_types, "ss")) else if (!strcmp(cmd->command, "/get_property") && !strcmp(cmd->arg_types, "ss"))
//parameters: "client:port", key //parameters: "client:port", key
//returns python key, value and type as strings //returns python key, value and type as strings

5
template/calfbox/py/cbox.py

@ -952,6 +952,9 @@ class SamplerEngine(NonDocObj):
def get_patches(self): def get_patches(self):
"""Return a map of program identifiers to program objects.""" """Return a map of program identifiers to program objects."""
return self.get_thing("/patches", '/patch', {int : (str, SamplerProgram, int)}) return self.get_thing("/patches", '/patch', {int : (str, SamplerProgram, int)})
def get_keyswitch_state(self, channel, group):
"""Return a map of program identifiers to program objects."""
return self.get_thing("/keyswitch_state", '/last_key', int, channel, group)
class FluidsynthEngine(NonDocObj): class FluidsynthEngine(NonDocObj):
class Status: class Status:
@ -1111,6 +1114,8 @@ class SamplerProgram(DocObj):
return {self.get_global() : self.get_global().get_hierarchy()} return {self.get_global() : self.get_global().get_hierarchy()}
def get_control_inits(self): def get_control_inits(self):
return self.get_thing("/control_inits", '/control_init', [(int, int)]) return self.get_thing("/control_inits", '/control_init', [(int, int)])
def get_keyswitch_groups(self):
return self.get_thing("/keyswitch_groups", '/key_range', [(int, int)])
def new_group(self): def new_group(self):
# Obsolete # Obsolete
return self.cmd_makeobj("/new_group") return self.cmd_makeobj("/new_group")

77
template/calfbox/py/metadata.py

@ -6,7 +6,7 @@ This file implements the JackIO Python side of Jack Medata as described here:
http://www.jackaudio.org/files/docs/html/group__Metadata.html http://www.jackaudio.org/files/docs/html/group__Metadata.html
""" """
import base64 # for icons
import os.path import os.path
#get_thing #get_thing
@ -47,6 +47,20 @@ class Metadata:
return TypeError("value {} must be int or str but was {}".format(value, type(value))) return TypeError("value {} must be int or str but was {}".format(value, type(value)))
do_cmd("/io/set_property", None, [port, key, value, jackPropertyType]) do_cmd("/io/set_property", None, [port, key, value, jackPropertyType])
@staticmethod
def client_set_property(key, value, jackPropertyType=""):
"""empty jackPropertyType leads to UTF-8 string
for convenience we see if value is a python int and send the right jack_property_t::type
This is directly for our client, which we do not need to provide here.
"""
if type(value) is int:
jackPropertyType = "http://www.w3.org/2001/XMLSchema#int"
value = str(value)
elif not type(value) is str:
return TypeError("value {} must be int or str but was {}".format(value, type(value)))
do_cmd("/io/client_set_property", None, [key, value, jackPropertyType])
@staticmethod @staticmethod
def remove_property(port, key): def remove_property(port, key):
"""port is the portname as string System:out_1""" """port is the portname as string System:out_1"""
@ -102,42 +116,47 @@ class Metadata:
@staticmethod @staticmethod
def _set_icon_name(port, freeDeskopIconName): def set_icon_name(freeDeskopIconName):
"""Internal function used in set_icon_small and set_icon_large""" """
This sets the name of the icon according to freedesktop specs.
The name is the basename without extension like so:
/usr/share/icons/hicolor/32x32/apps/patroneo.png -> "patroneo"
The name of the icon for the subject (typically client).
This is used for looking up icons on the system, possibly with many sizes or themes. Icons
should be searched for according to the freedesktop Icon
Theme Specification:
https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
"""
if not os.path.splitext(freeDeskopIconName)[0] == freeDeskopIconName: if not os.path.splitext(freeDeskopIconName)[0] == freeDeskopIconName:
raise ValueEror(f"Icon name must not have a file extension. Expected {os.path.splitext(freeDeskopIconName)[0]} but was {freeDeskopIconName}") raise ValueEror(f"Icon name must not have a file extension. Expected {os.path.splitext(freeDeskopIconName)[0]} but was {freeDeskopIconName}")
if not os.path.basename(freeDeskopIconName) == freeDeskopIconName: if not os.path.basename(freeDeskopIconName) == freeDeskopIconName:
raise ValueError(f"Icon name must not be path. Expected {os.path.basename(freeDeskopIconName)} but was {freeDeskopIconName}") raise ValueError(f"Icon name must not be path. Expected {os.path.basename(freeDeskopIconName)} but was {freeDeskopIconName}")
self.set_property(port, "http://jackaudio.org/metadata/icon-name", freeDeskopIconName) Metadata.client_set_property("http://jackaudio.org/metadata/icon-name", freeDeskopIconName)
@staticmethod @staticmethod
def set_icon_small(port, freeDeskopIconName, base64png): def set_icon_small(base64png):
""" A value with a MIME type of "image/png;base64" that is an encoding of an """ A value with a MIME type of "image/png;base64" that is an encoding of an
NxN (with 32 < N <= 128) image to be used when displaying a visual representation of that NxN (with 32 < N <= 128) image to be used when displaying a visual representation of that
client or port. client or port.
The name of the icon for the subject (typically client).
This is used for looking up icons on the system, possibly with many sizes or themes. Icons
should be searched for according to the freedesktop Icon
Theme Specification:
https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
The small icon of our JACK client. The small icon of our JACK client.
Setting icons to ports seems to be technically possible, but this is not the function Setting icons to ports seems to be technically possible, but this is not the function
for port-icons. for port-icons.
This function does not check if base64png has the correct format. This function checks if the data is actually base64 and a shallow test if the data is PNG.
This also sets the name of the icon according to freedesktop specs.
The name is the basename without extension like so:
/usr/share/icons/hicolor/32x32/apps/patroneo.png -> "patroneo"
""" """
testDecode = base64.b64decode(base64png)
if not base64png.encode("utf-8") == base64.b64encode(testDecode):
raise ValueError("Provided data must be uft-8 and base64 encoded. But it was not")
self.set_property(port, "http://jackaudio.org/metadata/icon-small", base64png, jackPropertyType="image/png;base64") if not "PNG" in repr(testDecode)[:16]:
self._set_icon_name(port, freeDeskopIconName) raise ValueError("Provided data does not seem to be a PNG image. It is missing the PNG header.")
Metadata.client_set_property("http://jackaudio.org/metadata/icon-small", base64png, jackPropertyType="image/png;base64")
@staticmethod @staticmethod
def set_icon_large(base64png): def set_icon_large(base64png):
@ -145,21 +164,17 @@ class Metadata:
NxN (with N <=32) image to be used when displaying a visual representation of that client NxN (with N <=32) image to be used when displaying a visual representation of that client
or port. or port.
The name of the icon for the subject (typically client).
This is used for looking up icons on the system, possibly with many sizes or themes. Icons
should be searched for according to the freedesktop Icon
Theme Specification:
https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
The large icon of our JACK client. The large icon of our JACK client.
Setting icons to ports seems to be technically possible, but this is not the function Setting icons to ports seems to be technically possible, but this is not the function
for port-icons. for port-icons.
This function checks if the data is actually base64 and a shallow test if the data is PNG.
"""
This function does not check if base64png has the correct format. testDecode = base64.b64decode(base64png)
if not base64png.encode("utf-8") == base64.b64encode(testDecode):
raise ValueError("Provided data must be uft-8 and base64 encoded. But it was not")
This also sets the name of the icon according to freedesktop specs. if not "PNG" in repr(testDecode)[:16]:
The name is the basename without extension like so: raise ValueError("Provided data does not seem to be a PNG image. It is missing the PNG header.")
/usr/share/icons/hicolor/32x32/apps/patroneo.png -> "patroneo"
""" Metadata.client_set_property("http://jackaudio.org/metadata/icon-large", base64png, jackPropertyType="image/png;base64")
self.set_property(port, "http://jackaudio.org/metadata/icon-large", base64png, jackPropertyType="image/png;base64")
self._set_icon_name(port, freeDeskopIconName)

19
template/calfbox/sampler.c

@ -432,6 +432,25 @@ gboolean sampler_process_cmd(struct cbox_command_target *ct, struct cbox_command
CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error); CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error);
} }
else else
if (!strcmp(cmd->command, "/keyswitch_state") && !strcmp(cmd->arg_types, "ii"))
{
int channel = CBOX_ARG_I(cmd, 0);
if (channel < 1 || channel > 16)
{
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid channel %d", channel);
return FALSE;
}
int group = CBOX_ARG_I(cmd, 1);
if (group < 0 || group >= MAX_KEYSWITCH_GROUPS)
{
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid keyswitch group %d", group);
return FALSE;
}
if (!cbox_execute_on(fb, NULL, "/last_key", "i", error, m->channels[channel - 1].keyswitch_lastkey[group]))
return FALSE;
return TRUE;
}
else
if (!strcmp(cmd->command, "/patches") && !strcmp(cmd->arg_types, "")) if (!strcmp(cmd->command, "/patches") && !strcmp(cmd->arg_types, ""))
{ {
if (!cbox_check_fb_channel(fb, cmd->command, error)) if (!cbox_check_fb_channel(fb, cmd->command, error))

1
template/calfbox/sampler.h

@ -72,6 +72,7 @@ struct sampler_channel
float floatcc[smsrc_perchan_count]; float floatcc[smsrc_perchan_count];
uint8_t last_polyaft, last_chanaft; uint8_t last_polyaft, last_chanaft;
uint8_t keyswitch_state[MAX_KEYSWITCH_GROUPS]; uint8_t keyswitch_state[MAX_KEYSWITCH_GROUPS];
uint8_t keyswitch_lastkey[MAX_KEYSWITCH_GROUPS];
}; };
struct sampler_lfo struct sampler_lfo

7
template/calfbox/sampler_api_example5.py

@ -73,7 +73,7 @@ print ("Control Inits:", pgm.get_control_inits()) #Empty . Is this <control> ?
globalHierarchy = pgm.get_global() # -> Single SamplerLayer. Literally sfz <global>. But not the global scope, e.g. no under any <tag>. globalHierarchy = pgm.get_global() # -> Single SamplerLayer. Literally sfz <global>. But not the global scope, e.g. no under any <tag>.
#If there is no <global> tag in the .sfz this will still create a root SamplerLayer #If there is no <global> tag in the .sfz this will still create a root SamplerLayer
print ("Global:", globalHierarchy) print ("Global:", globalHierarchy)
print ("Groups:", pgm.get_keyswitch_groups())
print("\nShow all SamplerLayer in their global/master/group/region hierarchy through indentations.\n" + "=" * 80) print("\nShow all SamplerLayer in their global/master/group/region hierarchy through indentations.\n" + "=" * 80)
def recurse(item, level = 0): def recurse(item, level = 0):
@ -157,7 +157,10 @@ def findKeyswitches(program):
keyswitches = findKeyswitches(pgm) keyswitches = findKeyswitches(pgm)
pprint(keyswitches) pprint(keyswitches)
scene.send_midi_event(0x80, 61 ,64) #Trigger Keyswitch c#4 . Default for this example is 60/c4 instrument.engine.set_patch(1, pgm_no)
print ("Group 0 lastkey before keyswitch:", instrument.engine.get_keyswitch_state(1, 0))
scene.send_midi_event(0x90, 61 ,64) #Trigger Keyswitch c#4 . Default for this example is 60/c4
print ("Group 0 lastkey after keyswitch:", instrument.engine.get_keyswitch_state(1, 0))
#The following were just stages during development, now handled by recurse and recurse2() above #The following were just stages during development, now handled by recurse and recurse2() above

10
template/calfbox/sampler_channel.c

@ -53,13 +53,20 @@ void sampler_channel_reset_keyswitches(struct sampler_channel *c)
if (c->program && c->program->rll) if (c->program && c->program->rll)
{ {
memset(c->keyswitch_state, 255, sizeof(c->keyswitch_state)); memset(c->keyswitch_state, 255, sizeof(c->keyswitch_state));
memset(c->keyswitch_lastkey, 255, sizeof(c->keyswitch_lastkey));
for (uint32_t i = 0; i < c->program->rll->keyswitch_group_count; ++i) for (uint32_t i = 0; i < c->program->rll->keyswitch_group_count; ++i)
{ {
const struct sampler_keyswitch_group *ksg = c->program->rll->keyswitch_groups[i]; const struct sampler_keyswitch_group *ksg = c->program->rll->keyswitch_groups[i];
if (ksg->def_value == 255) if (ksg->def_value == 255)
{
c->keyswitch_state[i] = ksg->key_offsets[0]; c->keyswitch_state[i] = ksg->key_offsets[0];
c->keyswitch_lastkey[i] = ksg->lo;
}
else else
{
c->keyswitch_state[i] = ksg->key_offsets[ksg->def_value]; c->keyswitch_state[i] = ksg->key_offsets[ksg->def_value];
c->keyswitch_lastkey[i] = ksg->def_value + ksg->lo;
}
} }
} }
} }
@ -257,9 +264,12 @@ void sampler_channel_start_note(struct sampler_channel *c, int note, int vel, gb
{ {
const struct sampler_keyswitch_group *ks = prg->rll->keyswitch_groups[i]; const struct sampler_keyswitch_group *ks = prg->rll->keyswitch_groups[i];
if (note >= ks->lo && note <= ks->hi) if (note >= ks->lo && note <= ks->hi)
{
c->keyswitch_lastkey[i] = note;
c->keyswitch_state[i] = ks->key_offsets[note - ks->lo]; c->keyswitch_state[i] = ks->key_offsets[note - ks->lo];
} }
} }
}
struct sampler_rll_iterator iter; struct sampler_rll_iterator iter;
sampler_rll_iterator_init(&iter, prg->rll, c, note, vel, random, is_first, is_release_trigger); sampler_rll_iterator_init(&iter, prg->rll, c, note, vel, random, is_first, is_release_trigger);

12
template/calfbox/sampler_prg.c

@ -238,6 +238,18 @@ static gboolean sampler_program_process_cmd(struct cbox_command_target *ct, stru
return FALSE; return FALSE;
return cbox_execute_on(fb, NULL, "/data", "b", error, blob); return cbox_execute_on(fb, NULL, "/data", "b", error, blob);
} }
if (!strcmp(cmd->command, "/keyswitch_groups") && !strcmp(cmd->arg_types, ""))
{
if (!cbox_check_fb_channel(fb, cmd->command, error))
return FALSE;
for (uint32_t i = 0; i < program->rll->keyswitch_group_count; ++i)
{
struct sampler_keyswitch_group *ksg = program->rll->keyswitch_groups[i];
if (!cbox_execute_on(fb, NULL, "/key_range", "ii", error, ksg->lo, ksg->hi))
return FALSE;
}
return TRUE;
}
return cbox_object_default_process_cmd(ct, fb, cmd, error); return cbox_object_default_process_cmd(ct, fb, cmd, error);
} }

Loading…
Cancel
Save