diff options
author | Jamie Bullock <postlude@users.sourceforge.net> | 2015-03-16 14:56:17 +0000 |
---|---|---|
committer | Jamie Bullock <postlude@users.sourceforge.net> | 2015-03-16 14:56:17 +0000 |
commit | ebaabd0f2e3203be4700d01260b81c5971289c14 (patch) | |
tree | d844cb423fdee133e16ea8b9f80accb23d3b5197 | |
parent | c8f157b2231ba21d40a6699ffbdeb7874b9b1abd (diff) |
Add support for a wider range of DX-based SYSEX files including those with more than 32-voices. NOTE: for now files with up to 128 voices can be “read”, but only the first 32 voices will be “loaded”
svn path=/trunk/externals/postlude/; revision=17445
-rw-r--r-- | pluginhost~/handlers_pd.c | 401 | ||||
-rw-r--r-- | pluginhost~/ph_common.c | 7 | ||||
-rw-r--r-- | pluginhost~/ph_common.h | 2 |
3 files changed, 392 insertions, 18 deletions
diff --git a/pluginhost~/handlers_pd.c b/pluginhost~/handlers_pd.c index 4604a07..1a83ba4 100644 --- a/pluginhost~/handlers_pd.c +++ b/pluginhost~/handlers_pd.c @@ -41,11 +41,12 @@ - #include <string.h> #include <stdio.h> #include <stdlib.h> +#include <errno.h> #include <stdint.h> /* for uint8_t */ +#include <stdarg.h> #include "jutils.h" #include "handlers_osc.h" @@ -71,6 +72,10 @@ #define CLASS_NAME_STR "pluginhost~" +/* From hexter_types.h */ +#define DX7_DUMP_SIZE_VOICE_SINGLE 155+8 +#define DX7_DUMP_SIZE_VOICE_BULK 4096+8 + /*From dx7_voice.h by Sean Bolton */ typedef struct _dx7_patch_t { uint8_t data[128]; @@ -80,6 +85,59 @@ typedef struct _dx7_patch_t { /*From dx7_voice_data.c by Sean Bolton */ static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +char * +dssp_error_message(const char *fmt, ...) +{ + va_list args; + char buffer[256]; + + va_start(args, fmt); + vsnprintf(buffer, 256, fmt, args); + va_end(args); + return strdup(buffer); +} + +void +dx7_patch_pack(uint8_t *unpacked_patch, dx7_patch_t *packed_patch, uint8_t number) +{ + uint8_t *up = unpacked_patch, + *pp = (uint8_t *)(&packed_patch[number]); + int i, j; + + /* ugly because it used to be 68000 assembly... */ + for (i = 6; i > 0; i--) { + for (j = 11; j > 0; j--) { + *pp++ = *up++; + } /* through rd */ + *pp++ = ((*up) & 0x03) | (((*(up + 1)) & 0x03) << 2); + up += 2; /* rc+lc */ + *pp++ = ((*up) & 0x07) | (((*(up + 7)) & 0x0f) << 3); + up++; /* pd+rs */ + *pp++ = ((*up) & 0x03) | (((*(up + 1)) & 0x07) << 2); + up += 2; /* kvs+ams */ + *pp++ = *up++; /* ol */ + *pp++ = ((*up) & 0x01) | (((*(up + 1)) & 0x1f) << 1); + up += 2; /* fc+m */ + *pp++ = *up; + up += 2; /* ff */ + } /* operator done */ + for (i = 9; i > 0; i--) { + *pp++ = *up++; + } /* through algorithm */ + *pp++ = ((*up) & 0x07) | (((*(up + 1)) & 0x01) << 3); + up += 2; /* oks+fb */ + for (i = 4; i > 0; i--) { + *pp++ = *up++; + } /* through lamd */ + *pp++ = ((*up) & 0x01) | + (((*(up + 1)) & 0x07) << 1) | + (((*(up + 2)) & 0x07) << 4); + up += 3; /* lpms+lfw+lks */ + for (i = 11; i > 0; i--) { + *pp++ = *up++; + } /* through name */ +} + /* * dx7_bulk_dump_checksum ** Taken from dx7_voice_data.c by Sean Bolton ** @@ -141,6 +199,291 @@ char *encode_7in6(uint8_t *data, int length) return buffer; } +void +dx7_patch_unpack(dx7_patch_t *packed_patch, uint8_t number, uint8_t *unpacked_patch) +{ + uint8_t *up = unpacked_patch, + *pp = (uint8_t *)(&packed_patch[number]); + int i, j; + + /* ugly because it used to be 68000 assembly... */ + for(i = 6; i > 0; i--) { + for(j = 11; j > 0; j--) { + *up++ = *pp++; + } /* through rd */ + *up++ = (*pp) & 0x03; /* lc */ + *up++ = (*pp++) >> 2; /* rc */ + *up++ = (*pp) & 0x07; /* rs */ + *(up + 6) = (*pp++) >> 3; /* pd */ + *up++ = (*pp) & 0x03; /* ams */ + *up++ = (*pp++) >> 2; /* kvs */ + *up++ = *pp++; /* ol */ + *up++ = (*pp) & 0x01; /* m */ + *up++ = (*pp++) >> 1; /* fc */ + *up = *pp++; /* ff */ + up += 2; + } /* operator done */ + for(i = 9; i > 0; i--) { + *up++ = *pp++; + } /* through algorithm */ + *up++ = (*pp) & 0x07; /* feedback */ + *up++ = (*pp++) >> 3; /* oks */ + for(i = 4; i > 0; i--) { + *up++ = *pp++; + } /* through lamd */ + *up++ = (*pp) & 0x01; /* lfo ks */ + *up++ = ((*pp) >> 1) & 0x07; /* lfo wave */ + *up++ = (*pp++) >> 4; /* lfo pms */ + for(i = 11; i > 0; i--) { + *up++ = *pp++; + } +} + + + +int +dx7_patchbank_load(const char *filename, dx7_patch_t *firstpatch, + int maxpatches, char **errmsg) +{ + FILE *fp; + long filelength; + unsigned char *raw_patch_data = NULL; + size_t filename_length; + int count; + int patchstart; + int midshift; + int datastart; + int i; + int op; + + /* this needs to 1) open and parse the file, 2a) if it's good, copy up + * to maxpatches patches beginning at firstpath, and not touch errmsg, + * 2b) if it's not good, set errmsg to a malloc'd error message that + * the caller must free. */ + + if ((fp = fopen(filename, "rb")) == NULL) { + if (errmsg) *errmsg = dssp_error_message("could not open file '%s' for reading: %s", filename, strerror(errno)); + return 0; + } + + if (fseek(fp, 0, SEEK_END) || + (filelength = ftell(fp)) == -1 || + fseek(fp, 0, SEEK_SET)) { + if (errmsg) *errmsg = dssp_error_message("couldn't get length of patch file: %s", strerror(errno)); + fclose(fp); + return 0; + } + if (filelength == 0) { + if (errmsg) *errmsg = strdup("patch file has zero length"); + fclose(fp); + return 0; + } else if (filelength > 2097152) { + if (errmsg) *errmsg = strdup("patch file is too large"); + fclose(fp); + return 0; + } else if (filelength < 128) { + if (errmsg) *errmsg = strdup ("patch file is too small"); + fclose (fp); + return 0; + } + + if (!(raw_patch_data = (unsigned char *)malloc(filelength))) { + if (errmsg) *errmsg = strdup("couldn't allocate memory for raw patch file"); + fclose(fp); + return 0; + } + + if (fread(raw_patch_data, 1, filelength, fp) != (size_t)filelength) { + if (errmsg) *errmsg = dssp_error_message("short read on patch file: %s", strerror(errno)); + free(raw_patch_data); + fclose(fp); + return 0; + } + fclose(fp); + filename_length = strlen (filename); + + /* check if the file is a standard MIDI file */ + if (raw_patch_data[0] == 0x4d && /* "M" */ + raw_patch_data[1] == 0x54 && /* "T" */ + raw_patch_data[2] == 0x68 && /* "h" */ + raw_patch_data[3] == 0x64) /* "d" */ + midshift = 2; + else + midshift = 0; + + /* scan SysEx or MIDI file for SysEx header(s) */ + count = 0; + datastart = 0; + for (patchstart = 0; patchstart + midshift + 5 < filelength; patchstart++) { + + if (raw_patch_data[patchstart] == 0xf0 && + raw_patch_data[patchstart + 1 + midshift] == 0x43 && + raw_patch_data[patchstart + 2 + midshift] <= 0x0f && + raw_patch_data[patchstart + 3 + midshift] == 0x09 && + raw_patch_data[patchstart + 5 + midshift] == 0x00 && + patchstart + 4103 + midshift < filelength && + raw_patch_data[patchstart + 4103 + midshift] == 0xf7) { /* DX7 32 voice dump */ + + memmove(raw_patch_data + count * DX7_VOICE_SIZE_PACKED, + raw_patch_data + patchstart + 6 + midshift, 4096); + count += 32; + patchstart += (DX7_DUMP_SIZE_VOICE_BULK - 1); + + } else if (raw_patch_data[patchstart] == 0xf0 && + raw_patch_data[patchstart + midshift + 1] == 0x43 && + raw_patch_data[patchstart + midshift + 2] <= 0x0f && + raw_patch_data[patchstart + midshift + 4] == 0x01 && + raw_patch_data[patchstart + midshift + 5] == 0x1b && + patchstart + midshift + 162 < filelength && + raw_patch_data[patchstart + midshift + 162] == 0xf7) { /* DX7 single voice (edit buffer) dump */ + + unsigned char buf[DX7_VOICE_SIZE_PACKED]; /* to avoid overlap in dx7_patch_pack() */ + + dx7_patch_pack(raw_patch_data + patchstart + midshift + 6, + (dx7_patch_t *)buf, 0); + memcpy(raw_patch_data + count * DX7_VOICE_SIZE_PACKED, + buf, DX7_VOICE_SIZE_PACKED); + + count += 1; + patchstart += (DX7_DUMP_SIZE_VOICE_SINGLE - 1); + } + } + + /* assume raw DX7/TX7 data if no SysEx header was found. */ + /* assume the user knows what he is doing ;-) */ + + if (count == 0) + count = filelength / DX7_VOICE_SIZE_PACKED; + + /* Dr.T and Steinberg TX7 file needs special treatment */ + if ((!strcmp(filename + filename_length - 4, ".TX7") || + !strcmp(filename + filename_length -4, ".SND") || + !strcmp(filename + filename_length -4, ".tx7") || + !strcmp(filename + filename_length - 4, ".snd")) && filelength == 8192) { + + count = 32; + filelength = 4096; + } + + /* Transform XSyn file also needs special treatment */ + if ((!strcmp(filename + filename_length - 4, ".BNK") || + !strcmp(filename + filename_length - 4, ".bnk")) && filelength == 8192) { + + for (i=0; i<32; i++) + { + memmove(raw_patch_data + 128*i, raw_patch_data + 256*i, 128); + } + count = 32; + filelength = 4096; + } + + /* Steinberg Synthworks DX7 SND */ + if ((!strcmp (filename + filename_length - 4, ".SND") || + !strcmp (filename + filename_length - 4, ".snd")) && filelength == 5216) { + + count = 32; + filelength = 4096; + } + + /* Voyetra SIDEMAN DX/TX + * Voyetra Patchmaster DX7/TX7 */ + if ((filelength == 9816 || filelength == 5663) && + raw_patch_data[0] == 0xdf && + raw_patch_data[1] == 0x05 && + raw_patch_data[2] == 0x01 && raw_patch_data[3] == 0x00) { + + count = 32; + datastart = 0x60f; + } + + /* Yamaha DX200 editor .DX2 */ + if ((!strcmp (filename + filename_length - 4, ".DX2") || + !strcmp (filename + filename_length - 4, ".dx2")) + && filelength == 326454) + { + memmove (raw_patch_data + 16384, raw_patch_data + 34, 128 * 381); + for (count = 0; count < 128; count++) + { + for (op = 0; op < 6; op++) + { + for (i = 0; i < 8; i++) + { + raw_patch_data[17 * (5 - op) + i + 128 * count] = + raw_patch_data[16384 + 35 * op + 76 + i + 381 * count]; + } + raw_patch_data[17 * (5 - op) + 8 + 128 * count] = + raw_patch_data[16384 + 35 * op + 84 + 381 * count] - 21; + raw_patch_data[17 * (5 - op) + 9 + 128 * count] = + raw_patch_data[16384 + 35 * op + 87 + 381 * count]; + raw_patch_data[17 * (5 - op) + 10 + 128 * count] = + raw_patch_data[16384 + 35 * op + 88 + 381 * count]; + raw_patch_data[17 * (5 - op) + 11 + 128 * count] = + raw_patch_data[16384 + 35 * op + 85 + 381 * count] + + raw_patch_data[16384 + 35 * op + 86 + 381 * count] * 4; + raw_patch_data[17 * (5 - op) + 12 + 128 * count] = + raw_patch_data[16384 + 35 * op + 89 + 381 * count] + + raw_patch_data[16384 + 35 * op + 75 + 381 * count] * 8; + if (raw_patch_data[16384 + 35 * op + 71 + 381 * count] > 3) + raw_patch_data[16384 + 35 * op + 71 + 381 * count] = 3; + raw_patch_data[17 * (5 - op) + 13 + 128 * count] = + raw_patch_data[16384 + 35 * op + 71 + 381 * count] / 2 + + raw_patch_data[16384 + 35 * op + 91 + 381 * count] * 4; + raw_patch_data[17 * (5 - op) + 14 + 128 * count] = + raw_patch_data[16384 + 35 * op + 90 + 381 * count]; + raw_patch_data[17 * (5 - op) + 15 + 128 * count] = + raw_patch_data[16384 + 35 * op + 72 + 381 * count] + + raw_patch_data[16384 + 35 * op + 73 + 381 * count] * 2; + raw_patch_data[17 * (5 - op) + 16 + 128 * count] = + raw_patch_data[16384 + 35 * op + 74 + 381 * count]; + } + for (i = 0; i < 4; i++) + { + raw_patch_data[102 + i + 128 * count] = + raw_patch_data[16384 + 26 + i + 381 * count]; + } + for (i = 0; i < 4; i++) + { + raw_patch_data[106 + i + 128 * count] = + raw_patch_data[16384 + 32 + i + 381 * count]; + } + raw_patch_data[110 + 128 * count] = + raw_patch_data[16384 + 17 + 381 * count]; + raw_patch_data[111 + 128 * count] = + raw_patch_data[16384 + 18 + 381 * count] + + raw_patch_data[16384 + 38 + 381 * count] * 8; + for (i = 0; i < 4; i++) + { + raw_patch_data[112 + i + 128 * count] = + raw_patch_data[16384 + 20 + i + 381 * count]; + } + raw_patch_data[116 + 128 * count] = + raw_patch_data[16384 + 24 + 381 * count] + + raw_patch_data[16384 + 19 + 381 * count] * 2 + + raw_patch_data[16384 + 25 + 381 * count] * 16; + raw_patch_data[117 + 128 * count] = + raw_patch_data[16384 + 37 + 381 * count] - 36; + for (i = 0; i < 10; i++) + { + raw_patch_data[118 + i + 128 * count] = + raw_patch_data[16384 + i + 381 * count]; + } + } + + count = 128; + filelength = 16384; + datastart = 0; + + } + + /* finally, copy patchdata to the right location */ + if (count > maxpatches) + count = maxpatches; + + memcpy(firstpatch, raw_patch_data + datastart, 128 * count); + free (raw_patch_data); + return count; +} + /* end hexter code */ @@ -692,7 +1035,7 @@ void handle_pd_dssi(ph *x, t_symbol *s, int argc, t_atom *argv) unsigned char *raw_patch_data = NULL; FILE *fp = NULL; size_t filename_length, key_size, value_size; - dx7_patch_t patchbuf[DX7_BANK_SIZE]; + dx7_patch_t patchbuf[DX7_BANK_SIZE * 4]; // 128 dx7_patch_t *firstpatch; atom_string(argv, msg_type, TYPE_STRING_SIZE); debug = NULL; @@ -712,11 +1055,11 @@ void handle_pd_dssi(ph *x, t_symbol *s, int argc, t_atom *argv) filename, instance); if(!strcmp(x->descriptor->LADSPA_Plugin->Label, "hexter") || - !strcmp(x->descriptor->LADSPA_Plugin->Label, "hexter6")) { - - key = malloc(10 * sizeof(char)); /* holds "patchesN" */ - strcpy(key, "patches0"); + !strcmp(x->descriptor->LADSPA_Plugin->Label, "hexter6")) + { + key = malloc(9 * sizeof(char)); /* holds "patchesN" */ + snprintf(key, 9, "patches0"); /* TODO: duplicates code from load_plugin() */ fd = canvas_open(x->x_canvas, filename, "", mydir, &filename, MAXPDSTRING, 0); @@ -726,12 +1069,29 @@ void handle_pd_dssi(ph *x, t_symbol *s, int argc, t_atom *argv) pathlen = strlen(mydir); temp = &mydir[pathlen]; sprintf(temp, "/%s", filename); - fp = fopen(filepath, "rb"); + //fp = fopen(filepath, "rb"); } else{ pd_error(x, "unable to get file descriptor"); } + + char *message = NULL; + int count = dx7_patchbank_load(filepath, firstpatch, 128, &message); + + if (count) { + ph_debug_post("patch load successful"); + } + else + { + pd_error(x, "unable to load patch bank"); + ph_debug_post("%s", message); + free(message); + } + //sprintf("%s", message); + + +#if 0 /*From dx7_voice_data by Sean Bolton */ if(fp == NULL){ pd_error(x, "unable to open patch file: %s", filename); @@ -833,15 +1193,24 @@ void handle_pd_dssi(ph *x, t_symbol *s, int argc, t_atom *argv) free(raw_patch_data); - if(count == 32) - value = encode_7in6((uint8_t *)&patchbuf[0].data[0], - count * DX7_VOICE_SIZE_PACKED); +#endif + + if(count != 32) + { + pd_error(x, "invalid count"); + ph_debug_post("count was: %d", count); + } + + /* Regardless of the count, try to load the first 32 voices anyway */ + value = encode_7in6((uint8_t *)&patchbuf[0], + 32 * DX7_VOICE_SIZE_PACKED); + // count * DX7_VOICE_SIZE_PACKED } else if(!strcmp(x->descriptor->LADSPA_Plugin->Label, "FluidSynth-DSSI")){ - key = malloc(6 * sizeof(char)); - strcpy(key, "load"); + key = malloc(5 * sizeof(char)); + snprintf(key, 5, "load"); value = filename; } else{ @@ -855,7 +1224,9 @@ void handle_pd_dssi(ph *x, t_symbol *s, int argc, t_atom *argv) x->project_dir = malloc((pathlen) * sizeof(char)); atom_string(&argv[1], x->project_dir, pathlen); pd_error(x, "project directory for instance %d has been set to: %s", instance, x->project_dir); - key = DSSI_PROJECT_DIRECTORY_KEY; + size_t project_directory_key_len = strlen(DSSI_PROJECT_DIRECTORY_KEY); // #defined in dssi.h + key = malloc(project_directory_key_len + 1); + snprintf(key, project_directory_key_len, DSSI_PROJECT_DIRECTORY_KEY); value = x->project_dir; } else if (!strcmp(msg_type, "dir")) { pd_error(x, "%s: %s %s: operation not supported", @@ -987,8 +1358,10 @@ void handle_pd_dssi(ph *x, t_symbol *s, int argc, t_atom *argv) } ph_configure_buffer(x, key, value, instance); } + free(key); // should be either NULL or malloc'd } - ph_debug_post("The plugin returned %s", debug); + + if(debug) ph_debug_post("The plugin returned %s", debug); } diff --git a/pluginhost~/ph_common.c b/pluginhost~/ph_common.c index e85f6fa..ca65e66 100644 --- a/pluginhost~/ph_common.c +++ b/pluginhost~/ph_common.c @@ -278,6 +278,7 @@ static void ph_init_instance(ph *x, unsigned int i) x->instances[i].pending_bank_msb = -1; x->instances[i].ui_hidden = 1; x->instances[i].ui_show = 0; + x->instances[i].ui_target = NULL; memcpy(x->instances[i].perf_buffer, &dx7_init_performance, DX7_PERFORMANCE_SIZE); //x->instances[i].plugin_port_ctlin_numbers = NULL; @@ -695,8 +696,9 @@ void ph_instance_send_osc(t_outlet *outlet, ph_instance *instance, t_int argc, t_atom *argv) { - outlet_anything(outlet, gensym("connect"), UI_TARGET_ELEMS, - instance->ui_target); + // TODO: ui_target should be host / port, but not yet implemented + //outlet_anything(outlet, gensym("connect"), UI_TARGET_ELEMS, + // instance->ui_target); outlet_anything(outlet, gensym("send"), argc, argv); outlet_anything(outlet, gensym("disconnect"), 0, NULL); @@ -1043,7 +1045,6 @@ void ph_init_plugin(ph *x) x->ports_control_out = 0; x->buf_write_index = 0; x->buf_read_index = 0; - } void ph_free_plugin(ph *x) diff --git a/pluginhost~/ph_common.h b/pluginhost~/ph_common.h index bed5751..31f30be 100644 --- a/pluginhost~/ph_common.h +++ b/pluginhost~/ph_common.h @@ -56,7 +56,7 @@ typedef struct _ph_instance { int pending_bank_msb; int ui_hidden; int ui_show; - t_atom ui_target[UI_TARGET_ELEMS]; /* host, port */ + char * ui_target; /* TODO: should be host, port */ uint8_t perf_buffer[DX7_PERFORMANCE_SIZE]; pid_t gui_pid; |