Commit e2da2f9b authored by ale's avatar ale

Working version

parent 9c420f32
......@@ -10,11 +10,14 @@ libengine_la_SOURCES = \
engine/sampler.c engine/sampler.h
libengine_la_LIBADD = $(SNDFILE_LIBS) $(SAMPLERATE_LIBS)
noinst_PROGRAMS = load-drumkit
noinst_PROGRAMS = load-drumkit verify-voice
load_drumkit_SOURCES = engine/load-drumkit.c
load_drumkit_LDADD = libengine.la
verify_voice_SOURCES = engine/verify-voice.c
verify_voice_LDADD = libengine.la
if LV2
lv2plugin_DATA = plugin/hydrumkit.ttl plugin/manifest.ttl
......
......@@ -11,6 +11,24 @@ static float linear_interpolation_fraction(float a, float b, int num, int den) {
}
void adsr_trigger(struct adsr *adsr, int attack, int decay, float sustain, int release) {
// Normalize values.
if (attack < 0)
attack = 0;
if (decay < 0)
decay = 0;
if (sustain < 0)
sustain = 0;
if (release < 256)
release = 256;
if (attack > 100000)
attack = 100000;
if (decay > 100000)
decay = 100000;
if (sustain > 1.0)
sustain = 1.0;
if (release > 100256)
release = 100256;
adsr->attack = attack;
adsr->decay = decay;
adsr->sustain = sustain;
......@@ -52,6 +70,7 @@ float adsr_get_value(struct adsr *adsr, float step) {
case ADSR_SUSTAIN:
value = adsr->sustain;
//adsr->ticks += step;
break;
case ADSR_RELEASE:
......
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Name of package */
#define PACKAGE "hydrumkit"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "ale@incal.net"
/* Define to the full name of this package. */
#define PACKAGE_NAME "hydrumkit"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "hydrumkit 0.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "hydrumkit"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "0.1"
/* Version number of package */
#define VERSION "0.1"
......@@ -19,7 +19,7 @@ static void instrument_set_defaults(struct instrument *ins) {
ins->envelope_attack = 0;
ins->envelope_decay = 0;
ins->envelope_sustain = 1;
ins->envelope_release = 1000;
ins->envelope_release = 10000;
ins->mute_group = -1;
ins->note = 0;
}
......@@ -377,9 +377,19 @@ struct drumkit *drumkit_new(const char *filename, int target_samplerate) {
return kit;
}
static void fix_bad_xml(xmlNodePtr cur, xmlNsPtr ns) {
for (; cur; cur = cur->next) {
if (cur->type == XML_ELEMENT_NODE) {
xmlSetNs(cur, ns);
}
fix_bad_xml(cur->children, ns);
}
}
int drumkit_load(struct drumkit *kit, const char *filename, int target_samplerate) {
char *basedir = NULL;
xmlDocPtr doc = NULL;
xmlNodePtr root_element = NULL;
xmlXPathContextPtr xpathCtx = NULL;
xmlXPathObjectPtr xpathObj = NULL;
int n, r = 1;
......@@ -394,13 +404,23 @@ int drumkit_load(struct drumkit *kit, const char *filename, int target_samplerat
goto cleanup;
}
// Check for malformed XML (missing namespace). We can convince
// libxml2 to parse it anyway by setting the proper namespace on all
// elements.
root_element = xmlDocGetRootElement(doc);
if (!strcmp(root_element->name, "drumkit_info") && root_element->ns == NULL) {
xmlNsPtr ns;
fprintf(stderr, "spotted old-style (bad) XML\n");
ns = xmlNewNs(root_element, (xmlChar *)"http://www.hydrogen-music.org/drumkit", (xmlChar *)"");
fix_bad_xml(root_element, ns);
}
xpathCtx = xmlXPathNewContext(doc);
if (xpathCtx == NULL) {
fprintf(stderr, "Error: unable to create new XPath context\n");
r = 0;
goto cleanup;
}
xmlXPathRegisterNs(xpathCtx, (xmlChar *)"dk",
(xmlChar *)"http://www.hydrogen-music.org/drumkit");
......
......@@ -46,6 +46,13 @@ void sampler_noteon(struct sampler *sampler, int note, int velocity) {
return;
}
// Kill other notes in the same mute_group.
if (ins->mute_group >= 0) {
for (i = 0; i < sampler->num_voices; i++) {
voice_noteoff_if_mute_group(&sampler->voices[i], ins->mute_group);
}
}
// Find a free voice.
struct voice *voice = NULL;
for (i = 0; i < sampler->num_voices; i++) {
......@@ -58,7 +65,7 @@ void sampler_noteon(struct sampler *sampler, int note, int velocity) {
printf("all voices are busy\n");
return;
}
// Humanize: modify preamble.
voice_noteon(voice, ins, note, velocity, 0);
}
......@@ -67,7 +74,7 @@ void sampler_noteoff(struct sampler *sampler, int note) {
int i;
for (i = 0; i < sampler->num_voices; i++) {
voice_noteoff(&sampler->voices[i], note);
voice_noteoff_if_note(&sampler->voices[i], note);
}
}
......@@ -78,6 +85,7 @@ void sampler_output(struct sampler *sampler, float *out_l, float *out_r, int num
for (i = 0; i < num_frames; i++) {
out_l[i] = out_r[i] = 0;
}
// Accumulate output from all the voices.
for (i = 0; i < sampler->num_voices; i++) {
voice_process(&sampler->voices[i], out_l, out_r, num_frames);
......
#include <stdio.h>
#include <libxml/parser.h>
#include <sndfile.h>
#include "drumkit.h"
#include "voice.h"
#define SR 96000
void write_sample(const char *filename, float *data_l, float *data_r, int num_frames) {
SF_INFO info = {0};
SNDFILE *sndfile;
float *buf;
int i;
buf = (float *)malloc(2 * sizeof(float) * num_frames);
for (i = 0; i < num_frames; i++) {
buf[i*2] = data_l[i];
buf[i*2+1] = data_r[i];
}
info.samplerate = SR;
info.channels = 2;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
sndfile = sf_open(filename, SFM_WRITE, &info);
sf_writef_float(sndfile, buf, num_frames);
sf_close(sndfile);
free(buf);
}
int main(int argc, char **argv) {
int status = 0;
struct drumkit kit = {0};
struct voice voice;
struct instrument *ins;
struct layer *layer;
float *out_l, *out_r;
int total_samples;
int lead_samples = 100;
int note_samples = 100000;
int note = 36;
int velocity = 100;
int i;
if (argc != 2) {
return 2;
}
/*
* this initialize the library and check potential ABI mismatches
* between the version it was compiled for and the actual shared
* library used.
*/
LIBXML_TEST_VERSION
if (!drumkit_load(&kit, argv[1], SR)) {
status = 1;
goto cleanup;
}
ins = drumkit_find_instrument_by_note(&kit, note);
if (!ins) {
printf("no instrument for note %d\n", note);
status = 1;
goto cleanup;
}
voice_init(&voice, SR);
total_samples = lead_samples + note_samples;
out_l = (float *)malloc(sizeof(float) * total_samples);
out_r = (float *)malloc(sizeof(float) * total_samples);
for (i = 0; i < total_samples; i++) {
out_l[i] = out_r[i] = 0;
}
// Some silence first.
voice_process(&voice, out_l, out_r, lead_samples);
// Then a note (kick).
voice_noteon(&voice, ins, note, velocity, 0);
voice_process(&voice, out_l + lead_samples, out_r + lead_samples, note_samples);
// Dump the layer used by the voice.
layer = instrument_find_layer_by_velocity(ins, velocity);
write_sample("layer.wav", layer->sample.data_l, layer->sample.data_r, layer->sample.info.frames);
// Dump the output.
write_sample("out.wav", out_l, out_r, total_samples);
cleanup:
free(out_l);
free(out_r);
drumkit_free(&kit);
/*
* Cleanup function for the XML library.
*/
xmlCleanupParser();
return status;
}
......@@ -5,7 +5,8 @@
#include "voice.h"
static float velocity_to_gain(int velocity) {
return (float)velocity / 127.0;
float f = (float)velocity / 128.0;
return f * f;
}
void voice_init(struct voice *voice, int samplerate) {
......@@ -27,6 +28,7 @@ int voice_noteon(struct voice *voice, struct instrument *ins, int note, int velo
voice->pan_l = ins->pan_l;
voice->pan_r = ins->pan_r;
voice->gain = ins->volume * ins->gain * layer->gain * velocity_to_gain(velocity);
voice->mute_group = ins->mute_group;
adsr_trigger(&voice->adsr,
(int)ins->envelope_attack,
(int)ins->envelope_decay,
......@@ -45,11 +47,17 @@ int voice_noteon(struct voice *voice, struct instrument *ins, int note, int velo
return 1;
}
int voice_noteoff(struct voice *voice, int note) {
int voice_noteoff_if_note(struct voice *voice, int note) {
if ((voice->playing || voice->preamble_countdown) && voice->note == note) {
adsr_release(&voice->adsr);
voice->playing = 0;
voice->ticks = 0;
return 1;
}
return 0;
}
int voice_noteoff_if_mute_group(struct voice *voice, int mute_group) {
if ((voice->playing || voice->preamble_countdown) && voice->mute_group == mute_group) {
adsr_release(&voice->adsr);
return 1;
}
return 0;
......@@ -58,26 +66,22 @@ int voice_noteoff(struct voice *voice, int note) {
void voice_process(struct voice *voice, float *out_l, float *out_r, int num_frames) {
// Accumulate the sample to the output buffer.
while (num_frames--) {
if (voice->in_preamble) {
voice->preamble_countdown--;
if (voice->preamble_countdown <= 0) {
voice->in_preamble = 0;
voice->playing = 1;
voice->ticks = 0;
}
}
if (voice->playing) {
float gain = voice->gain * adsr_get_value(&voice->adsr, 1);
// Stop playing when the envelope is done.
if (voice->adsr.state == ADSR_IDLE) {
voice->playing = 0;
continue;
}
// Stop playing when the sample is done.
if (voice->ticks > voice->cur_sample->info.frames) {
// Stop playing when the envelope is done, or the sample is
// done, whichever comes first.
if ((voice->adsr.state == ADSR_IDLE)
|| (voice->ticks >= voice->cur_sample->info.frames)) {
voice->playing = 0;
continue;
}
......
......@@ -11,6 +11,7 @@ struct voice {
int in_preamble;
int preamble_countdown;
int note;
int mute_group;
int ticks;
float pan_l, pan_r, gain;
......@@ -20,7 +21,8 @@ struct voice {
void voice_init(struct voice *, int);
int voice_noteon(struct voice *, struct instrument *, int, int, int);
int voice_noteoff(struct voice *, int);
int voice_noteoff_if_note(struct voice *, int);
int voice_noteoff_if_mute_group(struct voice *, int);
void voice_process(struct voice *, float *, float *, int);
static inline int voice_is_playing(struct voice *voice) {
......
......@@ -19,7 +19,7 @@
<http://lv2.incal.net/plugins/hydrumkit>
a lv2:Plugin ;
doap:name "Exampler" ;
doap:name "HyDrumKit" ;
doap:license <http://opensource.org/licenses/isc> ;
lv2:project <http://lv2plug.in/ns/lv2> ;
lv2:requiredFeature state:loadDefaultState ,
......
......@@ -184,11 +184,12 @@ static void handle_event(struct sampler_plugin *plugin, LV2_Atom_Event *ev) {
case LV2_MIDI_MSG_NOTE_ON:
note = (int)msg[1];
velocity = (int)msg[2];
sampler_noteoff(plugin->sampler, note);
sampler_noteon(plugin->sampler, note, velocity);
break;
case LV2_MIDI_MSG_NOTE_OFF:
note = (int)msg[1];
sampler_noteoff(plugin->sampler, note);
//sampler_noteoff(plugin->sampler, note);
break;
default: // Make -Wall happy.
break;
......
/*
LV2 Sampler Example Plugin UI
Copyright 2011-2016 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "uris.h"
......@@ -92,6 +76,7 @@ static LV2UI_Handle instantiate(const LV2UI_Descriptor *descriptor,
struct plugin_ui *ui =
(struct plugin_ui *)calloc(1, sizeof(struct plugin_ui));
const char *missing;
char *home;
LV2_Atom *msg;
LV2_Atom_Forge_Frame frame;
GtkFileFilter *filter;
......@@ -128,8 +113,15 @@ static LV2UI_Handle instantiate(const LV2UI_Descriptor *descriptor,
ui->file_button =
gtk_file_chooser_button_new("Load Drumkit", GTK_FILE_CHOOSER_ACTION_OPEN);
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(ui->file_button), filter);
gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(ui->file_button), "/usr/share/hydrogen/data/drumkits", NULL);
gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(ui->file_button), "~/.hydrogen/data/drumkits", NULL);
home = getenv("HOME");
if (home) {
char *homebuf = (char *)malloc(strlen(home) + 48);
sprintf(homebuf, "%s/.hydrogen/data/drumkits", home);
gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(ui->file_button), homebuf, NULL);
free(homebuf);
}
gtk_container_set_border_width(GTK_CONTAINER(ui->box), 4);
gtk_box_pack_start(GTK_BOX(ui->box), ui->button_box, TRUE, TRUE, 0);
......
/*
LV2 Sampler Example Plugin
Copyright 2011-2016 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __uris_H
#define __uris_H
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment