Skip to content

Commit

Permalink
IInstrument, envelope fade param, thread safety
Browse files Browse the repository at this point in the history
  • Loading branch information
ahigerd committed Sep 22, 2022
1 parent 4eb5695 commit 20146f1
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 83 deletions.
32 changes: 30 additions & 2 deletions src/seq/sequenceevent.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
#include "sequenceevent.h"
#include <iostream>
#include <cmath>
#include <atomic>

static uint64_t _nextPlaybackID = 1;
static std::atomic_uint64_t _nextPlaybackID = 1;

bool SequenceEvent::isNoteEvent() const
{
return false;
}

bool BaseNoteEvent::isNoteEvent() const
{
return true;
}

inline uint64_t BaseNoteEvent::nextPlaybackID()
{
return _nextPlaybackID++;
return _nextPlaybackID.fetch_add(1, std::memory_order_relaxed);
}

BaseNoteEvent::BaseNoteEvent()
Expand All @@ -21,6 +34,7 @@ void BaseNoteEvent::setEnvelope(double attack, double decay, double sustain, dou
this->attack = attack;
this->decay = decay;
this->sustain = sustain;
this->fade = HUGE_VAL;
this->release = release;
}

Expand All @@ -32,6 +46,7 @@ void BaseNoteEvent::setEnvelope(double attack, double hold, double decay, double
this->hold = hold;
this->decay = decay;
this->sustain = sustain;
this->fade = HUGE_VAL;
this->release = release;
}

Expand All @@ -43,6 +58,19 @@ void BaseNoteEvent::setEnvelope(double start, double attack, double hold, double
this->hold = hold;
this->decay = decay;
this->sustain = sustain;
this->fade = HUGE_VAL;
this->release = release;
}

void BaseNoteEvent::setEnvelope(double start, double attack, double hold, double decay, double sustain, double fade, double release)
{
useEnvelope = true;
this->startGain = start;
this->attack = attack;
this->hold = hold;
this->decay = decay;
this->sustain = sustain;
this->fade = fade;
this->release = release;
}

Expand Down
77 changes: 64 additions & 13 deletions src/seq/sequenceevent.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,52 @@ class SequenceEvent {
double timestamp;

virtual int eventType() const = 0;
virtual bool isNoteEvent() const;

template<typename T>
const T* cast() const {
inline const T* cast() const {
if (eventType() == T::TypeID) {
return static_cast<const T*>(this);
}
return nullptr;
}

template<typename T>
T* cast() {
inline T* cast() {
if (eventType() == T::TypeID) {
return static_cast<T*>(this);
}
return nullptr;
}

template<typename T, typename SE>
static inline std::shared_ptr<T> castShared(std::shared_ptr<SE>& event) {
if (event->eventType() == T::TypeID) {
return std::static_pointer_cast<T>(event);
}
return nullptr;
}
};

template <int TYPE_ID>
template <class THIS, int TYPE_ID>
class BaseEvent : public SequenceEvent {
public:
enum { TypeID = TYPE_ID };

virtual int eventType() const { return TypeID; }

template <typename SE>
static inline std::shared_ptr<THIS> castShared(std::shared_ptr<SE>& event) {
return SequenceEvent::castShared<THIS>(event);
}
};

struct BaseNoteEvent {
struct BaseNoteEvent : public SequenceEvent {
static uint64_t nextPlaybackID();
BaseNoteEvent();

virtual bool isNoteEvent() const;

uint64_t playbackID; // for modulation
double duration;
double volume;
Expand All @@ -60,41 +76,76 @@ struct BaseNoteEvent {
bool useEnvelope : 1;
bool expAttack : 1;
bool expDecay : 1;
double startGain, attack, hold, decay, sustain, release;
double startGain, attack, hold, decay, sustain, fade, release;
void setEnvelope(double attack, double decay, double sustain, double release);
void setEnvelope(double attack, double hold, double decay, double sustain, double release);
void setEnvelope(double start, double attack, double hold, double decay, double sustain, double release);
void setEnvelope(double start, double attack, double hold, double decay, double sustain, double fade, double release);

template <typename SE>
static inline std::shared_ptr<BaseNoteEvent> castShared(std::shared_ptr<SE>& event) {
if (event->isNoteEvent()) {
return std::static_pointer_cast<BaseNoteEvent>(event);
}
return nullptr;
}
};

template <int TYPE_ID>
class NoteEvent : public BaseEvent<TYPE_ID>, public BaseNoteEvent {
template <>
inline const BaseNoteEvent* SequenceEvent::cast<BaseNoteEvent>() const
{
if (isNoteEvent()) {
return static_cast<const BaseNoteEvent*>(this);
}
return nullptr;
}

template <>
inline BaseNoteEvent* SequenceEvent::cast<BaseNoteEvent>()
{
if (isNoteEvent()) {
return static_cast<BaseNoteEvent*>(this);
}
return nullptr;
}

template <class THIS, int TYPE_ID>
class NoteEvent : public BaseNoteEvent {
public:
enum { TypeID = TYPE_ID };

virtual int eventType() const { return TypeID; }

template <typename SE>
static inline std::shared_ptr<THIS> castShared(std::shared_ptr<SE>& event) {
return SequenceEvent::castShared<THIS>(event);
}
};

class SampleEvent : public NoteEvent<SequenceEvent::Sample> {
class SampleEvent : public NoteEvent<SampleEvent, SequenceEvent::Sample> {
public:
SampleEvent();

uint64_t sampleID; // looked up in sound bank
double pitchBend;
};

class OscillatorEvent : public NoteEvent<SequenceEvent::Oscillator> {
class OscillatorEvent : public NoteEvent<OscillatorEvent, SequenceEvent::Oscillator> {
public:
OscillatorEvent();

uint64_t waveformID; // passed to PSG
double frequency;
};

class AudioNodeEvent : public NoteEvent<SequenceEvent::AudioNode> {
class AudioNodeEvent : public NoteEvent<AudioNodeEvent, SequenceEvent::AudioNode> {
public:
AudioNodeEvent(std::shared_ptr<::AudioNode> node);

std::shared_ptr<::AudioNode> node;
};

class ModulatorEvent : public BaseEvent<SequenceEvent::Modulator> {
class ModulatorEvent : public BaseEvent<ModulatorEvent, SequenceEvent::Modulator> {
public:
ModulatorEvent(uint64_t playbackID, int32_t param, double value);

Expand All @@ -103,15 +154,15 @@ class ModulatorEvent : public BaseEvent<SequenceEvent::Modulator> {
double value;
};

class KillEvent : public BaseEvent<SequenceEvent::Kill> {
class KillEvent : public BaseEvent<KillEvent, SequenceEvent::Kill> {
public:
KillEvent(uint64_t playbackID, double timestamp);

uint64_t playbackID;
bool immediate;
};

class ChannelEvent : public BaseEvent<SequenceEvent::Channel> {
class ChannelEvent : public BaseEvent<ChannelEvent, SequenceEvent::Channel> {
public:
ChannelEvent(uint32_t param, double value);

Expand Down
85 changes: 28 additions & 57 deletions src/synth/channel.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
#include "channel.h"
#include "oscillator.h"
#include "sampler.h"
#include "synthcontext.h"
#include "s2wcontext.h"
#include "iinstrument.h"
#include "seq/itrack.h"
#include "seq/sequenceevent.h"
#include <iostream>

Channel::Note::Note(std::shared_ptr<SequenceEvent> event, std::shared_ptr<AudioNode> source, double duration)
Channel::Note::Note()
: event(nullptr), source(nullptr), duration(0), kill(true)
{
// initializers only
}

Channel::Note::Note(std::shared_ptr<BaseNoteEvent> event, std::shared_ptr<AudioNode> source, double duration)
: event(event), source(source), duration(duration), kill(false)
{
// initializers only
}

Channel::Channel(const SynthContext* ctx, ITrack* track)
: gain(ctx, 0.5), pan(ctx, 0.5), ctx(ctx), track(track), nextEvent(nullptr)
: AudioParamContainer(ctx), track(track), nextEvent(nullptr)
{
instrument = ctx->defaultInstrument();
gain = addParam(AudioNode::Gain, 0.5).get();
pan = addParam(AudioNode::Pan, 0.5).get();
timestamp = 0;
}

Expand All @@ -42,71 +50,34 @@ uint32_t Channel::fillBuffer(std::vector<int16_t>& buffer, ssize_t numSamples)
nextEvent = event;
break;
}
std::shared_ptr<AudioNode> noteNode = nullptr;
BaseNoteEvent* noteEvent = nullptr;
Note* note = nullptr;
if (ChannelEvent* chEvent = event->cast<ChannelEvent>()) {
// TODO: queuing
// TODO: more types
if (chEvent->param == AudioNode::Gain) {
gain = chEvent->value;
} else if (chEvent->param == AudioNode::Pan) {
pan = chEvent->value;
if (auto chEvent = ChannelEvent::castShared(event)) {
instrument->channelEvent(this, chEvent);
} else if (auto modEvent = ModulatorEvent::castShared(event)) {
instrument->modulatorEvent(this, modEvent);
} else if (auto noteEvent = BaseNoteEvent::castShared(event)) {
Note* note = instrument->noteEvent(this, noteEvent);
if (note && !note->kill) {
notes.emplace(std::make_pair(note->event->playbackID, note));
}
//std::cout << "ChannelEvent " << gain.valueAt(timestamp) << std::endl;
} else if (ModulatorEvent* modEvent = event->cast<ModulatorEvent>()) {
auto noteIter = notes.find(modEvent->playbackID);
if (noteIter != notes.end()) {
auto param = noteIter->second->source->param(modEvent->param);
if (param) {
param->setConstant(modEvent->value);
}
} else if (event->eventType() >= SequenceEvent::UserBase) {
// This must go after the BaseNoteEvent case because user-defined
// note events should be sent there.
Note* note = instrument->userEvent(this, event);
if (note && !note->kill) {
notes.emplace(std::make_pair(note->event->playbackID, note));
}
} else if (AudioNodeEvent* nodeEvent = event->cast<AudioNodeEvent>()) {
noteEvent = nodeEvent;
noteNode = nodeEvent->node;
note = new Note(event, noteNode, 0);
notes.emplace(std::make_pair(nodeEvent->playbackID, note));
} else if (OscillatorEvent* oscEvent = event->cast<OscillatorEvent>()) {
noteEvent = oscEvent;
BaseOscillator* osc = BaseOscillator::create(ctx, oscEvent->waveformID, oscEvent->frequency, oscEvent->volume, oscEvent->pan);
noteNode.reset(osc);
note = new Note(event, noteNode, oscEvent->duration);
notes.emplace(std::make_pair(oscEvent->playbackID, note));
} else if (SampleEvent* sampEvent = event->cast<SampleEvent>()) {
noteEvent = sampEvent;
SampleData* sampleData = ctx->s2wContext()->getSample(sampEvent->sampleID);
if (!sampleData) {
std::cerr << "ERROR: sample " << std::hex << sampEvent->sampleID << std::dec << " not found" << std::endl;
continue;
}
Sampler* samp = new Sampler(ctx, sampleData, sampEvent->pitchBend);
noteNode.reset(samp);
samp->param(AudioNode::Gain)->setConstant(sampEvent->volume);
samp->param(AudioNode::Pan)->setConstant(sampEvent->pan);
note = new Note(event, noteNode, sampEvent->duration != 0 ? sampEvent->duration : sampleData->duration());
notes.emplace(std::make_pair(sampEvent->playbackID, note));
} else if (KillEvent* killEvent = event->cast<KillEvent>()) {
auto noteIter = notes.find(killEvent->playbackID);
if (noteIter != notes.end()) {
noteIter->second->duration = killEvent->timestamp - noteIter->second->event->timestamp;
noteIter->second->kill = noteIter->second->kill || killEvent->immediate;
}
}
if (noteEvent && noteEvent->useEnvelope) {
Envelope* env = new Envelope(ctx, noteEvent->attack, noteEvent->hold, noteEvent->decay, noteEvent->sustain, noteEvent->release);
env->expAttack = noteEvent->expAttack;
env->expDecay = noteEvent->expDecay;
env->param(Envelope::StartGain)->setConstant(noteEvent->startGain);
env->connect(noteNode);
noteNode.reset(env);
note->source = noteNode;
}
}
} while (event);
int pos = 0;
while (pos < buffer.size() && !isFinished()) {
double panValue = ctx->outputChannels > 1 ? pan.valueAt(timestamp) : 1;
double panValue = ctx->outputChannels > 1 ? pan->valueAt(timestamp) : 1;
for (int ch = 0; ch < ctx->outputChannels; ch++) {
int32_t sample = 0;
std::vector<uint64_t> toErase;
Expand Down Expand Up @@ -139,7 +110,7 @@ uint32_t Channel::fillBuffer(std::vector<int16_t>& buffer, ssize_t numSamples)
for (uint64_t id : toErase) {
notes.erase(id);
}
buffer[pos] = sample * gain.valueAt(timestamp) * panValue;
buffer[pos] = sample * gain->valueAt(timestamp) * panValue;
panValue = 1 - panValue;
++pos;
}
Expand Down
Loading

0 comments on commit 20146f1

Please sign in to comment.