Browse Source

update template

master
Nils 9 months 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;
}
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"))
//parameters: "client:port", key
//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):
"""Return a map of program identifiers to program objects."""
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 Status:
@ -1111,6 +1114,8 @@ class SamplerProgram(DocObj):
return {self.get_global() : self.get_global().get_hierarchy()}
def get_control_inits(self):
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):
# Obsolete
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
"""
import base64 # for icons
import os.path
#get_thing
@ -47,6 +47,20 @@ class Metadata:
return TypeError("value {} must be int or str but was {}".format(value, type(value)))
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
def remove_property(port, key):
"""port is the portname as string System:out_1"""
@ -102,42 +116,47 @@ class Metadata:
@staticmethod
def _set_icon_name(port, freeDeskopIconName):
"""Internal function used in set_icon_small and set_icon_large"""
def set_icon_name(freeDeskopIconName):
"""
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:
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:
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
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
NxN (with 32 < N <= 128) image to be used when displaying a visual representation of that
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.
Setting icons to ports seems to be technically possible, but this is not the function
for port-icons.
This function does not check if base64png has the correct format.
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"
This function checks if the data is actually base64 and a shallow test if the data is PNG.
"""
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")
self._set_icon_name(port, freeDeskopIconName)
if not "PNG" in repr(testDecode)[:16]:
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
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
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.
Setting icons to ports seems to be technically possible, but this is not the function
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.
The name is the basename without extension like so:
/usr/share/icons/hicolor/32x32/apps/patroneo.png -> "patroneo"
"""
self.set_property(port, "http://jackaudio.org/metadata/icon-large", base64png, jackPropertyType="image/png;base64")
self._set_icon_name(port, freeDeskopIconName)
if not "PNG" in repr(testDecode)[:16]:
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-large", base64png, jackPropertyType="image/png;base64")

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);
}
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 (!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];
uint8_t last_polyaft, last_chanaft;
uint8_t keyswitch_state[MAX_KEYSWITCH_GROUPS];
uint8_t keyswitch_lastkey[MAX_KEYSWITCH_GROUPS];
};
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>.
#If there is no <global> tag in the .sfz this will still create a root SamplerLayer
print ("Global:", globalHierarchy)
print ("Groups:", pgm.get_keyswitch_groups())
print("\nShow all SamplerLayer in their global/master/group/region hierarchy through indentations.\n" + "=" * 80)
def recurse(item, level = 0):
@ -157,7 +157,10 @@ def findKeyswitches(program):
keyswitches = findKeyswitches(pgm)
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

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)
{
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)
{
const struct sampler_keyswitch_group *ksg = c->program->rll->keyswitch_groups[i];
if (ksg->def_value == 255)
{
c->keyswitch_state[i] = ksg->key_offsets[0];
c->keyswitch_lastkey[i] = ksg->lo;
}
else
{
c->keyswitch_state[i] = ksg->key_offsets[ksg->def_value];
c->keyswitch_lastkey[i] = ksg->def_value + ksg->lo;
}
}
}
}
@ -257,7 +264,10 @@ 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];
if (note >= ks->lo && note <= ks->hi)
{
c->keyswitch_lastkey[i] = note;
c->keyswitch_state[i] = ks->key_offsets[note - ks->lo];
}
}
}

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 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);
}

Loading…
Cancel
Save