Merge pull request #354 from jpcima/host-time

Plugin-level support for host time
This commit is contained in:
JP Cimalando 2020-08-11 20:52:51 +02:00 committed by GitHub
commit 6076f17173
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 345 additions and 11 deletions

View file

@ -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;
@ -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;
@ -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;
@ -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)
@ -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);
@ -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
@ -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,
@ -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++)
{
@ -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;
}
@ -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)
{
@ -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
{

View file

@ -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

View file

@ -291,13 +291,39 @@ public:
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

View file

@ -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
{

View file

@ -392,6 +392,29 @@ public:
* @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

View file

@ -153,9 +153,24 @@ void sfz::Sfizz::aftertouch(int delay, uint8_t aftertouch) noexcept
synth->aftertouch(delay, aftertouch);
}
void sfz::Sfizz::tempo(int delay, float secondsPerQuarter) noexcept
void sfz::Sfizz::tempo(int delay, float secondsPerBeat) noexcept
{
synth->tempo(delay, secondsPerQuarter);
synth->tempo(delay, secondsPerBeat);
}
void sfz::Sfizz::timeSignature(int delay, int beatsPerBar, int beatUnit)
{
synth->timeSignature(delay, beatsPerBar, beatUnit);
}
void sfz::Sfizz::timePosition(int delay, int bar, float barBeat)
{
synth->timePosition(delay, bar, barBeat);
}
void sfz::Sfizz::playbackState(int delay, int playbackState)
{
synth->playbackState(delay, playbackState);
}
void sfz::Sfizz::renderBlock(float** buffers, size_t numSamples, int /*numOutputs*/) noexcept

View file

@ -160,6 +160,21 @@ void sfizz_send_tempo(sfizz_synth_t* synth, int delay, float seconds_per_quarter
auto self = reinterpret_cast<sfz::Synth*>(synth);
self->tempo(delay, seconds_per_quarter);
}
void sfizz_send_time_signature(sfizz_synth_t* synth, int delay, int beats_per_bar, int beat_unit)
{
auto self = reinterpret_cast<sfz::Synth*>(synth);
self->timeSignature(delay, beats_per_bar, beat_unit);
}
void sfizz_send_time_position(sfizz_synth_t* synth, int delay, int bar, float bar_beat)
{
auto self = reinterpret_cast<sfz::Synth*>(synth);
self->timePosition(delay, bar, bar_beat);
}
void sfizz_send_playback_state(sfizz_synth_t* synth, int delay, int playback_state)
{
auto self = reinterpret_cast<sfz::Synth*>(synth);
self->playbackState(delay, playback_state);
}
void sfizz_render_block(sfizz_synth_t* synth, float** channels, int num_channels, int num_frames)
{

View file

@ -12,8 +12,6 @@
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include <cstring>
#pragma message("TODO: send tempo") // NOLINT
template<class T>
constexpr int fastRound(T x)
{
@ -55,6 +53,13 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
_currentStretchedTuning = 0.0;
loadSfzFileOrDefault(*_synth, {});
_synth->tempo(0, 0.5);
_timeSigNumerator = 4;
_timeSigDenominator = 4;
_synth->timeSignature(0, _timeSigNumerator, _timeSigDenominator);
_synth->timePosition(0, 0, 0);
_synth->playbackState(0, 0);
return result;
}
@ -143,6 +148,9 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
{
sfz::Sfizz& synth = *_synth;
if (data.processContext)
updateTimeInfo(*data.processContext);
if (Vst::IParameterChanges* pc = data.inputParameterChanges)
processParameterChanges(*pc);
@ -198,6 +206,29 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
return kResultTrue;
}
void SfizzVstProcessor::updateTimeInfo(const Vst::ProcessContext& context)
{
sfz::Sfizz& synth = *_synth;
if (context.state & context.kTempoValid)
synth.tempo(0, 60.0f / context.tempo);
if (context.state & context.kTimeSigValid) {
_timeSigNumerator = context.timeSigNumerator;
_timeSigDenominator = context.timeSigDenominator;
synth.timeSignature(0, _timeSigNumerator, _timeSigDenominator);
}
if (context.state & context.kProjectTimeMusicValid) {
double beats = context.projectTimeMusic * 0.25 * _timeSigDenominator;
double bars = beats / _timeSigNumerator;
beats -= int(bars) * _timeSigNumerator;
synth.timePosition(0, int(bars), float(beats));
}
synth.playbackState(0, (context.state & context.kPlaying) != 0);
}
void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc)
{
uint32 paramCount = pc.getParameterCount();

View file

@ -64,6 +64,11 @@ private:
uint32 _fileChangeCounter = 0;
uint32 _fileChangePeriod = 0;
// time info
int _timeSigNumerator = 0;
int _timeSigDenominator = 0;
void updateTimeInfo(const Vst::ProcessContext& context);
// messaging
struct RTMessage {
const char* type;