Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin-level support for host time #354

Merged
merged 2 commits into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 167 additions & 1 deletion lv2/sfizz.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ typedef struct
LV2_URID sample_rate_uri;
LV2_URID atom_object_uri;
LV2_URID atom_float_uri;
LV2_URID atom_double_uri;
LV2_URID atom_int_uri;
LV2_URID atom_long_uri;
LV2_URID atom_urid_uri;
LV2_URID atom_path_uri;
LV2_URID patch_set_uri;
Expand All @@ -150,6 +152,12 @@ typedef struct
LV2_URID sfizz_check_modification_uri;
LV2_URID sfizz_active_voices_uri;
LV2_URID time_position_uri;
LV2_URID time_bar_uri;
LV2_URID time_bar_beat_uri;
LV2_URID time_beat_unit_uri;
LV2_URID time_beats_per_bar_uri;
LV2_URID time_beats_per_minute_uri;
LV2_URID time_speed_uri;

// Sfizz related data
sfizz_synth_t *synth;
Expand All @@ -166,6 +174,14 @@ typedef struct
float sample_rate;
atomic_int must_update_midnam;

// Timing data
int bar;
float bar_beat;
int beats_per_bar;
int beat_unit;
float bpm_tempo;
float speed;

// Paths
char bundle_path[MAX_BUNDLE_PATH_SIZE];
} sfizz_plugin_t;
Expand All @@ -187,6 +203,14 @@ enum
SFIZZ_ACTIVE_VOICES = 12,
};

enum
{
SFIZZ_TIMEINFO_POSITION = 1 << 0,
SFIZZ_TIMEINFO_SIGNATURE = 1 << 1,
SFIZZ_TIMEINFO_TEMPO = 1 << 2,
SFIZZ_TIMEINFO_SPEED = 1 << 3,
};

static void
sfizz_lv2_state_free_path(LV2_State_Free_Path_Handle handle,
char *path)
Expand All @@ -210,7 +234,9 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
self->nominal_block_length_uri = map->map(map->handle, LV2_BUF_SIZE__nominalBlockLength);
self->sample_rate_uri = map->map(map->handle, LV2_PARAMETERS__sampleRate);
self->atom_float_uri = map->map(map->handle, LV2_ATOM__Float);
self->atom_double_uri = map->map(map->handle, LV2_ATOM__Double);
self->atom_int_uri = map->map(map->handle, LV2_ATOM__Int);
self->atom_long_uri = map->map(map->handle, LV2_ATOM__Long);
self->atom_path_uri = map->map(map->handle, LV2_ATOM__Path);
self->atom_urid_uri = map->map(map->handle, LV2_ATOM__URID);
self->atom_object_uri = map->map(map->handle, LV2_ATOM__Object);
Expand All @@ -230,6 +256,68 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification);
self->time_position_uri = map->map(map->handle, LV2_TIME__Position);
self->time_bar_uri = map->map(map->handle, LV2_TIME__bar);
self->time_bar_beat_uri = map->map(map->handle, LV2_TIME__barBeat);
self->time_beat_unit_uri = map->map(map->handle, LV2_TIME__beatUnit);
self->time_beats_per_bar_uri = map->map(map->handle, LV2_TIME__beatsPerBar);
self->time_beats_per_minute_uri = map->map(map->handle, LV2_TIME__beatsPerMinute);
self->time_speed_uri = map->map(map->handle, LV2_TIME__speed);
}

static bool
sfizz_atom_extract_real(sfizz_plugin_t *self, const LV2_Atom *atom, double *real)
{
if (!atom)
return false;

const LV2_URID type = atom->type;

if (type == self->atom_int_uri && atom->size >= sizeof(int32_t)) {
*real = ((const LV2_Atom_Int *)atom)->body;
return true;
}
if (type == self->atom_long_uri && atom->size >= sizeof(int64_t)) {
*real = ((const LV2_Atom_Long *)atom)->body;
return true;
}
if (type == self->atom_float_uri && atom->size >= sizeof(float)) {
*real = ((const LV2_Atom_Float *)atom)->body;
return true;
}
if (type == self->atom_double_uri && atom->size >= sizeof(double)) {
*real = ((const LV2_Atom_Double *)atom)->body;
return true;
}

return false;
}

static bool
sfizz_atom_extract_integer(sfizz_plugin_t *self, const LV2_Atom *atom, int64_t *integer)
{
if (!atom)
return false;

const LV2_URID type = atom->type;

if (type == self->atom_int_uri && atom->size >= sizeof(int32_t)) {
*integer = ((const LV2_Atom_Int *)atom)->body;
return true;
}
if (type == self->atom_long_uri && atom->size >= sizeof(int64_t)) {
*integer = ((const LV2_Atom_Long *)atom)->body;
return true;
}
if (type == self->atom_float_uri && atom->size >= sizeof(float)) {
*integer = (int64_t)((const LV2_Atom_Float *)atom)->body;
return true;
}
if (type == self->atom_double_uri && atom->size >= sizeof(double)) {
*integer = (int64_t)((const LV2_Atom_Double *)atom)->body;
return true;
}

return false;
}

static void
Expand Down Expand Up @@ -326,6 +414,19 @@ sfizz_lv2_get_default_scala_path(LV2_Handle instance, char *path, size_t size)
snprintf(path, size, "%s/%s", self->bundle_path, DEFAULT_SCALA_FILE);
}

static void
sfizz_lv2_update_timeinfo(sfizz_plugin_t *self, int delay, int updates)
{
if (updates & SFIZZ_TIMEINFO_POSITION)
sfizz_send_time_position(self->synth, delay, self->bar, self->bar_beat);
if (updates & SFIZZ_TIMEINFO_SIGNATURE)
sfizz_send_time_signature(self->synth, delay, self->beats_per_bar, self->beat_unit);
if (updates & SFIZZ_TIMEINFO_TEMPO)
sfizz_send_tempo(self->synth, delay, 60.0f / self->bpm_tempo);
if (updates & SFIZZ_TIMEINFO_SPEED)
sfizz_send_playback_state(self->synth, delay, self->speed > 0);
}

static LV2_Handle
instantiate(const LV2_Descriptor *descriptor,
double rate,
Expand Down Expand Up @@ -361,6 +462,14 @@ instantiate(const LV2_Descriptor *descriptor,
self->check_modification = false;
self->sample_counter = 0;

// Initial timing
self->bar = 0;
self->bar_beat = 0;
self->beats_per_bar = 4;
self->beat_unit = 4;
self->bpm_tempo = 120;
self->speed = 1;

// Get the features from the host and populate the structure
for (const LV2_Feature *const *f = features; *f; f++)
{
Expand Down Expand Up @@ -472,6 +581,8 @@ instantiate(const LV2_Descriptor *descriptor,
sfizz_load_file(self->synth, self->sfz_file_path);
sfizz_load_scala_file(self->synth, self->scala_file_path);

sfizz_lv2_update_timeinfo(self, 0, ~0);

return (LV2_Handle)self;
}

Expand Down Expand Up @@ -751,6 +862,8 @@ run(LV2_Handle instance, uint32_t sample_count)

LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev)
{
const int delay = (int)ev->time.frames;

// If the received atom is an object/patch message
if (ev->body.type == self->atom_object_uri)
{
Expand Down Expand Up @@ -779,7 +892,60 @@ run(LV2_Handle instance, uint32_t sample_count)
}
else if (obj->body.otype == self->time_position_uri)
{
// TODO: Handle time position atom
const LV2_Atom *bar_atom = NULL;
const LV2_Atom *bar_beat_atom = NULL;
const LV2_Atom *beat_unit_atom = NULL;
const LV2_Atom *beats_per_bar_atom = NULL;
const LV2_Atom *beats_per_minute_atom = NULL;
const LV2_Atom *speed_atom = NULL;

lv2_atom_object_get(
obj,
self->time_bar_uri, &bar_atom,
self->time_bar_beat_uri, &bar_beat_atom,
self->time_beats_per_bar_uri, &beats_per_bar_atom,
self->time_beats_per_minute_uri, &beats_per_minute_atom,
self->time_beat_unit_uri, &beat_unit_atom,
self->time_speed_uri, &speed_atom,
0);

int updates = 0;

int64_t bar;
double bar_beat;
if (sfizz_atom_extract_integer(self, bar_atom, &bar)) {
self->bar = (int)bar;
updates |= SFIZZ_TIMEINFO_POSITION;
}
if (sfizz_atom_extract_real(self, bar_beat_atom, &bar_beat)) {
self->bar_beat = (float)bar_beat;
updates |= SFIZZ_TIMEINFO_POSITION;
}

double beats_per_bar;
int64_t beat_unit;
if (sfizz_atom_extract_real(self, beats_per_bar_atom, &beats_per_bar)) {
self->beats_per_bar = (int)beats_per_bar;
updates |= SFIZZ_TIMEINFO_SIGNATURE;
}
if (sfizz_atom_extract_integer(self, beat_unit_atom, &beat_unit)) {
self->beat_unit = (int)beat_unit;
updates |= SFIZZ_TIMEINFO_SIGNATURE;
}

double tempo;
if (sfizz_atom_extract_real(self, beats_per_minute_atom, &tempo)) {
self->bpm_tempo = (float)tempo;
updates |= SFIZZ_TIMEINFO_TEMPO;
}

double speed;
if (sfizz_atom_extract_real(self, speed_atom, &speed)) {
self->speed = (float)speed;
updates |= SFIZZ_TIMEINFO_SPEED;
}

sfizz_lv2_update_timeinfo(self, delay, updates);
}
else
{
Expand Down
36 changes: 33 additions & 3 deletions src/sfizz.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,43 @@ SFIZZ_EXPORTED_API void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay,
SFIZZ_EXPORTED_API void sfizz_send_aftertouch(sfizz_synth_t* synth, int delay, char aftertouch);

/**
* @brief Send a tempo event. (CURRENTLY UNIMPLEMENTED)
* @brief Send a tempo event.
*
* @param synth The synth.
* @param delay The delay.
* @param seconds_per_quarter The seconds per quarter.
* @param seconds_per_beat The seconds per beat.
*/
SFIZZ_EXPORTED_API void sfizz_send_tempo(sfizz_synth_t* synth, int delay, float seconds_per_quarter);
SFIZZ_EXPORTED_API void sfizz_send_tempo(sfizz_synth_t* synth, int delay, float seconds_per_beat);

/**
* @brief Send the time signature.
*
* @param synth The synth.
* @param delay The delay.
* @param beats_per_bar The number of beats per bar, or time signature numerator.
* @param beat_unit The note corresponding to one beat, or time signature denominator.
*/
SFIZZ_EXPORTED_API void sfizz_send_time_signature(sfizz_synth_t* synth, int delay, int beats_per_bar, int beat_unit);

/**
* @brief Send the time position.
*
* @param synth The synth.
* @param delay The delay.
* @param bar The current bar.
* @param bar_beat The fractional position of the current beat within the bar.
*/
SFIZZ_EXPORTED_API void sfizz_send_time_position(sfizz_synth_t* synth, int delay, int bar, float bar_beat);

/**
* @brief Send the playback state.
*
* @param synth The synth.
* @param delay The delay.
* @param playback_state The playback state, 1 if playing, 0 if stopped.
*/
SFIZZ_EXPORTED_API void sfizz_send_playback_state(sfizz_synth_t* synth, int delay, int playback_state);


/**
* @brief Render a block audio data into a stereo channel. No other channel
Expand Down
32 changes: 29 additions & 3 deletions src/sfizz.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,39 @@ class SFIZZ_EXPORTED_API Sfizz
void aftertouch(int delay, uint8_t aftertouch) noexcept;

/**
* @brief Send a tempo event to the synth. (CURRENTLY UNIMPLEMENTED)
* @brief Send a tempo event to the synth.
*
* @param delay the delay at which the event occurs; this should be lower than the size of
* the block in the next call to renderBlock().
* @param secondsPerQuarter the new period of the quarter note.
* @param secondsPerBeat the new period of the beat.
*/
void tempo(int delay, float secondsPerQuarter) noexcept;
void tempo(int delay, float secondsPerBeat) noexcept;

/**
* @brief Send the time signature.
*
* @param delay The delay.
* @param beats_per_bar The number of beats per bar, or time signature numerator.
* @param beat_unit The note corresponding to one beat, or time signature denominator.
*/
void timeSignature(int delay, int beatsPerBar, int beatUnit);

/**
* @brief Send the time position.
*
* @param delay The delay.
* @param bar The current bar.
* @param bar_beat The fractional position of the current beat within the bar.
*/
void timePosition(int delay, int bar, float barBeat);

/**
* @brief Send the playback state.
*
* @param delay The delay.
* @param playback_state The playback state, 1 if playing, 0 if stopped.
*/
void playbackState(int delay, int playbackState);

/**
* @brief Render an block of audio data in the buffer. This call will reset
Expand Down
23 changes: 23 additions & 0 deletions src/sfizz/Synth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,29 @@ void sfz::Synth::tempo(int /* delay */, float /* secondsPerQuarter */) noexcept
{
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
}
void sfz::Synth::timeSignature(int delay, int beatsPerBar, int beatUnit)
{
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };

(void)delay;
(void)beatsPerBar;
(void)beatUnit;
}
void sfz::Synth::timePosition(int delay, int bar, float barBeat)
{
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };

(void)delay;
(void)bar;
(void)barBeat;
}
void sfz::Synth::playbackState(int delay, int playbackState)
{
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };

(void)delay;
(void)playbackState;
}

int sfz::Synth::getNumRegions() const noexcept
{
Expand Down
23 changes: 23 additions & 0 deletions src/sfizz/Synth.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,29 @@ class Synth final : public Voice::StateListener, public Parser::Listener {
* @param secondsPerQuarter the new period of the quarter note
*/
void tempo(int delay, float secondsPerQuarter) noexcept;
/**
* @brief Send the time signature.
*
* @param delay The delay.
* @param beats_per_bar The number of beats per bar, or time signature numerator.
* @param beat_unit The note corresponding to one beat, or time signature denominator.
*/
void timeSignature(int delay, int beatsPerBar, int beatUnit);
/**
* @brief Send the time position.
*
* @param delay The delay.
* @param bar The current bar.
* @param bar_beat The fractional position of the current beat within the bar.
*/
void timePosition(int delay, int bar, float barBeat);
/**
* @brief Send the playback state.
*
* @param delay The delay.
* @param playback_state The playback state, 1 if playing, 0 if stopped.
*/
void playbackState(int delay, int playbackState);
/**
* @brief Render an block of audio data in the buffer. This call will reset
* the synth in its waiting state for the next batch of events. The size of
Expand Down
Loading