On 08/10/14 13:36, Raine M. Ekman
wrote:
Phil CM <philcm@gnu.org> wrote:
Now the only think that is left is a
portamento function, to go from
one note to another (/exactly/ like so-404
<http://d00m.org/%7Esomeone/so404/> does).
Does anyone know the algorithm to
implement that? That would really
help me.
Quoting Philipp Überbacher <murks@tuxfamily.org>:
I have never done this but I suggest to
look at the calf monosynth
code. AFAIR it has a couple of different ways to handle
successive/overlapping notes.
Another synth that comes to mind and has portamento is phasex.
Well, if it has to be exactly the same, I'd suggest digging in the
so-404 code and not in those other synths. Looks like it adjusts
frequency at samplerate/100, calculating:
freq = ((portamento/127.0)*0.9)*freq +
(1.0-((portamento/127.0)*0.9))*tfreq;
I guess tfreq is the target frequency of the note being
portamented towards.
You are probably right ; Philip's advice was good too, as the calf
code is somehow simpler. Comparatively, the so-404 is very
monolithic / mathematical and hard to decipher.
About this tfreq calculation, a few newbie questions. Here is my entire
synth code :
#include <lv2synth.hpp>
#include "ksi.peg"
class BeepVoice : public LV2::Voice {
public:
BeepVoice(double rate)
: m_key(LV2::INVALID_KEY), m_rate(rate), m_period(10),
m_counter(0) {
}
void on(unsigned char key, unsigned char velocity) {
m_key = key;
m_period = m_rate * 4.0 / LV2::key2hz(m_key);
m_envelope = velocity / 128.0;
}
void off(unsigned char velocity) {
m_key = LV2::INVALID_KEY;
}
unsigned char get_key() const {
return m_key;
}
void render(uint32_t from, uint32_t to) {
if (m_key == LV2::INVALID_KEY)
return;
for (uint32_t i = from; i < to; ++i) {
float pwm = *p(p_pwm) + (1 - *p(p_pwm)) * m_envelope;
float s = -0.25 + 0.5 * (m_counter > m_period * (1
+ pwm) / 2);
// float s = -0.25 + 0.5 * (m_counter > m_period /
2);
m_counter = (m_counter + 1) % m_period;
p(p_left)[i] += s;
p(p_right)[i] += s;
if (m_envelope > 0)
m_envelope -= 0.5 / m_rate;
}
}
protected:
unsigned char m_key;
double m_rate;
uint32_t m_period;
uint32_t m_counter;
float m_envelope;
};
class Beep : public LV2::Synth<BeepVoice, Beep> {
public:
Beep(double rate)
: LV2::Synth<BeepVoice, Beep>(p_n_ports, p_midi)
{
add_voices(new BeepVoice(rate));
add_audio_outputs(p_left, p_right);
}
void post_process(uint32_t from, uint32_t to) {
for (uint32_t i = from; i < to; ++i) {
p(p_left)[i] *= *p(p_gain);
p(p_right)[i] *= *p(p_gain);
}
}
};
static int _ = Beep::register_class(p_uri);
Apart from that there is a ksi.ttl with a
lv2:port [
a ev:EventPort, lv2:InputPort;
lv2:index 0;
ev:supportsEvent
<http://lv2plug.in/ns/ext/midi#MidiEvent>;
lv2:symbol "midi";
lv2:name "MIDI";
],
(...)
Witch I understand is what somehow calls a public method in the Beep
class, that in turns uses public methods in the BeepVoice class, who
does the final rendering, magically connecting to
[
a lv2:AudioPort, lv2:OutputPort;
lv2:index 1;
lv2:symbol "left";
lv2:name "Left";
pg:membership [
pg:group
<http://ll-plugins.nongnu.org/lv2/lv2pftci/ksi/out>;
pg:role pg:leftChannel;
];
],
[
a lv2:AudioPort, lv2:OutputPort;
lv2:index 2;
lv2:symbol "right";
lv2:name "Right";
pg:membership [
pg:group
<http://ll-plugins.nongnu.org/lv2/lv2pftci/ksi/out>;
pg:role pg:rightChannel;
];
],
To output sound (and it does, and it sounds good).
In order to implement the frequency calculation, what would be the
rough roadmap, the synthetic (so to speak) bullet points ?
Thank you for your patience.
--
Philippe