diff --git a/engine/drumkit.c b/engine/drumkit.c index 97a4fdccee40d2a790d6a5c5bf10ab9d7d1618ab..0193446c1a3322c419229cd7deac716901f72b3f 100644 --- a/engine/drumkit.c +++ b/engine/drumkit.c @@ -13,6 +13,7 @@ #include "drumkit.h" +// Set default values on an instrument. static void instrument_set_defaults(struct instrument *ins) { ins->pan_l = ins->pan_r = 1; ins->gain = ins->volume = 1; @@ -24,6 +25,7 @@ static void instrument_set_defaults(struct instrument *ins) { ins->note = 0; } +// Return a sndfile format value in human-readable format. static char *sf_format_name(int format) { static char fmtbuf[32]; const char *major = "weird", *subtype = NULL; @@ -52,6 +54,7 @@ static char *sf_format_name(int format) { return fmtbuf; } +// Dump instrument metadata to standard output. static void print_instrument(struct instrument *ins) { int i; printf("instrument %s: pan=%g/%g, gain=%g\n", ins->name, ins->pan_l, @@ -64,8 +67,11 @@ static void print_instrument(struct instrument *ins) { } } +// Return a string with the content of the result of an XPath +// expression (which should evaluate the text() of a single node), or +// NULL if the expression returned no nodes. static const char *xpath_value_as_string(xmlNodePtr node, const char *xpathExpr, - xmlXPathContextPtr xpathCtx) { + xmlXPathContextPtr xpathCtx) { xmlXPathObjectPtr res; const char *content; res = xmlXPathNodeEval(node, (const xmlChar *)xpathExpr, xpathCtx); @@ -80,6 +86,9 @@ static const char *xpath_value_as_string(xmlNodePtr node, const char *xpathExpr, return content; } +// Evaluate an XPath expression and store the result as a float. The +// value pointed by out won't be modified if the XPath expression has +// no result. static void xpath_get_float(float *out, xmlNodePtr node, const char *xpathExpr, xmlXPathContextPtr xpathCtx) { const char *s; @@ -91,6 +100,9 @@ static void xpath_get_float(float *out, xmlNodePtr node, const char *xpathExpr, *out = strtof(s, NULL); } +// Evaluate an XPath expression and store the result as an +// integer. The value pointed by out won't be modified if the XPath +// expression has no result. static void xpath_get_int(int *out, xmlNodePtr node, const char *xpathExpr, xmlXPathContextPtr xpathCtx) { const char *s; @@ -102,12 +114,16 @@ static void xpath_get_int(int *out, xmlNodePtr node, const char *xpathExpr, *out = atoi(s); } +// Evaluate an XPath expression and store the result as a string. The +// value pointed by out won't be modified if the XPath expression has +// no result. The string will always use newly allocated memory. static void xpath_get_string(char **out, xmlNodePtr node, const char *xpathExpr, xmlXPathContextPtr xpathCtx) { *out = strdup(xpath_value_as_string(node, xpathExpr, xpathCtx)); } +// Count the number of nodes returned by an XPath expression. static int xpath_count(xmlNodePtr node, const char *xpathExpr, xmlXPathContextPtr xpathCtx) { xmlXPathObjectPtr res; int n; @@ -134,12 +150,15 @@ static char *path_dirname(const char *filename) { return buf; } +// Join two path components. Return a newly allocated string. static char *path_join(const char *base, const char *filename) { char *buf = (char *)malloc(strlen(base) + strlen(filename) + 2); sprintf(buf, "%s/%s", base, filename); return buf; } +// Load audio file data into a sample. Will convert the sample rate to +// the desired target using libsamplerate. static int load_sample_data(struct sample *sample, SNDFILE *sndfile, int target_samplerate) { SF_INFO *info = &sample->info; float *tmp, *p, *wl, *wr; @@ -192,24 +211,25 @@ static int load_sample_data(struct sample *sample, SNDFILE *sndfile, int target_ return 1; } +// Load an audio file into a sample. Will convert the sample rate to +// the desired target using libsamplerate. static int load_sample(struct sample *sample, const char *path, int target_samplerate) { SF_INFO *info = &sample->info; SNDFILE *sndfile; - int r = 1; + int r; sndfile = sf_open(path, SFM_READ, info); if (!sndfile) { - printf("failed to open sample %s: %s\n", path, sf_strerror(NULL)); + fprintf(stderr, "failed to open sample %s: %s\n", path, sf_strerror(NULL)); return 0; } - // Read all the sample data. - if (!load_sample_data(sample, sndfile, target_samplerate)) { - printf("failed to load sample data from %s\n", path); - r = 0; - } - + // Read the sample data and convert it to the desired format/rate. + r = load_sample_data(sample, sndfile, target_samplerate); sf_close(sndfile); + if (!r) { + fprintf(stderr, "failed to load sample data from %s\n", path); + } return r; } @@ -222,6 +242,7 @@ static void free_sample(struct sample *sample) { } } +// Parse an instrument layer from an XML document. static int parse_layer(struct layer *layer, xmlNodePtr node, xmlXPathContextPtr xpathCtx, const char *basedir, int target_samplerate) { @@ -246,6 +267,7 @@ static void free_layer(struct layer *layer) { free_sample(&layer->sample); } +// Parse an instrument from an XML document. static int parse_instrument(struct instrument *ins, xmlNodePtr node, xmlXPathContextPtr xpathCtx, const char *basedir, int target_samplerate) { @@ -309,17 +331,19 @@ static void free_instrument(struct instrument *ins) { free(ins->name); } +// Find the instrument layer corresponding to a specific velocity. struct layer *instrument_find_layer_by_velocity(struct instrument *ins, float velocity) { int i; for (i = 0; i < ins->num_layers; i++) { - if (ins->layers[i].max > velocity) { + if (velocity >= ins->layers[i].min && velocity <= ins->layers[i].max) { return &ins->layers[i]; } } return &ins->layers[ins->num_layers - 1]; } +// Parse a list of instruments from an XML document. static int parse_instruments(struct instrument **instruments, xmlNodeSetPtr nodes, xmlXPathContextPtr xpathCtx, const char *basedir, int target_samplerate) { @@ -345,7 +369,11 @@ static void free_instruments(struct instrument *instruments, int n) { } } -void fix_midi_notes(struct drumkit *kit) { +// Some drumkits lack an explicit MIDI note assignment: these are +// recognizable because they set all instruments to the same MIDI +// note. We can detect this situation and fix it with a default note +// assignment. +static void fix_midi_notes(struct drumkit *kit) { int note_count[127] = {0}; int note_cardinality = 0; int i; @@ -377,6 +405,7 @@ struct drumkit *drumkit_new(const char *filename, int target_samplerate) { return kit; } +// Assign the Hydrogen drumkit namespace to all XML nodes in a document. static void fix_bad_xml(xmlNodePtr cur, xmlNsPtr ns) { for (; cur; cur = cur->next) { if (cur->type == XML_ELEMENT_NODE) {