On Monday 09 December 2002 23.05, Steve Harris wrote:
[...]
* Getting
what you actually *need* for the synth. Is
this something that can be narrowed down to one or
two formats (frequency and period?), or is it perhaps
better left to plugins to perform the linear->"value"
conversion? Is there a point in using a host callback
to make sure you have linear pitch, if you *still*
need to convert again, into something else?
Surely this is another argument for using explicit function calls?
If they can do more than give you the linear pitch. (Which IMHO, you
should get right in your events in the first place.)
Question is, how many version do we need to make that a useful
resource to plugins, rather than just another required step along the
way to the final value?
* How do you
manage scales, if they're not handled by
"normal" plugins? Should all hosts have built-in UIs
for this? How sophisticated must this be to actually
be usable?
I was jsut thinking that there would be a scale hint on a control,
and the host could manage it. I would image that some hosts would
only support 12tET in which case they dont need to expose it.
And the way I want it, 12tET-only hosts would have nothing at all
related to scales - not even two functions that multiply or divide by
12.0. Plugins don't really have to know anything about this;
designers only need to answer *one* question:
"Do you care about notes or actual pitch?"
If "notes"; hint your pitch inputs as "note_pitch". That way, you
will always get 1.0/tone, whatever the scale is. In 12tET, this "just
happens" to be exactly the same thing as 12.0/octave.
If "actual pitch"; hint your pitch inputs as "linear_pitch". Now, you
get 12.0/octave, period. It could have been 1.0, but then someone
would need to convert back and forth, even in pure 12tET nets. Now,
you can effectively have your synth just "think" in 12tET, and still
be capable of playing in any scale. Scale conversion just expresses
the pitch of tones of the real scale as 12.0/octave - just as it
would have done with 1.0/octave; just a factor 12.0.
Again,
that's why 12.0/octave makes sense. It doesn't make a
difference technically, but it makes *this* stuff a whole lot
easier, since it just "happens" to result in note_pitch and
linear_pitch being identical as long as you stick with 12tET.
It makes note steps marinally simpler for 12tET and illogical for
other tunings. I dont see that as a big win.
It doesn't matter if they seem illogical, since your synth (the kind
of plugins that would normally want linear_pitch) can effectively
think in 12tET regardless of which scale it's being used with.
It it cared about the actual *values*, it would have asked for
note_pitch instead, or as well.
It also ecourages the
12tone way of thinking, which I dont particularly like (can you
tell ;).
Well, I must agree - but does representing note_pitch a 1.0/octave
make more sense? :-)
You could do that as well, but it means most control processors,
sequencers and stuff will have to multiply with 12.0 to get the
actual note pitch (1.0/tone) - which is pretty illogical as well,
considering that your output may well be interpretted as 16.0/octave!
Question is basically, do we "deal" with this in synths
(linear_pitch) or event processors (note_pitch)?
I prefer APIs to be culturaly neutral.
So do I, but is it worth wasting cycles and add complexity, just to
prevent synth programmers from even thinking about 12tET?
In fact, I can already see the FAQ:
"What's this 1.0/octave thing? Where are the *notes*!?"
With 12.0/octave, it's almost the opposite. 12tET brainwashed synth
coders will go "Huh? How can *my* synth play that weird scale? I
didn't implement that!" (There's definitely no way ever that will
happen with VSTis... :-)
Why make this
a special case, when it's actually just another
event processor - on that most people will never need to use?
OK, I can concede that providing scale conversion functions is a
special case, but so are conversion plugins and you still havent
explained how your going to implement conversion plugins without
host provided functions.
Sequencers, most event processors (basic appregiators and the like)
etc will send note_pitch. Those plugins think in terms of notes, and
don't care much about what exact pitch each note represents on
whatever eventually makes the sound. (This is *exactly* the situation
you're in when working with MIDI.)
Most synths will want linear_pitch. They generally have no reason to
know or care about the higher level meaning of a note. It's just like
your voice; (hopefully) it just produces exact pitch you want - and
whether that's a tone in the 12tET scale is irrelevant, as long as
that tone is what you're supposed to sing.
With 1.0/note for note_pitch, and 12.0/octave for linear_pitch, we
have a nice and extremely simple "shortcut" available for all those
that use only or mostly 12tET:
linear_pitch = note_pitch;
No converters needed for that - and linear_pitch --> actual
frequency, peroid, filter coeffs or whatever, is better done by
custom, optimized code in plugins. The plugin SDK may provide some
handy macros, inlines or functions to make life easier for plugin
authors. Maybe there could even be a few host calls for it - but I
definitely don't think that the API should *force* you to use those
by design.
Now, say you have a scale converter plugin. It should take note_pitch
for input, and output linear_pitch. When set to 12tET, it would
effectively do *nothing*. (As described above.)
If you pick a different scale, the plugin actually starts doing
something. For example, for 16tET, it would do:
linear_pitch = note_pitch * (12.0 / 16.0);
That is, "stretch" the scale so you need 16.0 note_pitch units to
span one octave. Now, all of a sudden, your synths - apparently
written for 12tET - can play 16tET. They don't know it, or understand
it, but they're playing the right notes.
BTW, there is another thing I think is rather important to realize.
If you consider that we're talking about *continous* pitch
(note_pitch is float!), you don't *really* need to mess with scales
at all.
If for example, you have a pitch tracker that generates events from a
vocal track, it doesn't matter what scale you use for mapping actual
frequency to pitch values. As long as you use the same scale for
playing those events on a synth, that synth will play *exactly* the
same pitches.
Now, you may sing 16tET if you like - the events will still be 12tET,
and despite that, the synth will still play exactly the same tones
that you sing. Indeed, it will look weird in the note_pitch based
piano roll view, but it will *work*.
Conclusion:
With continous pitch, a scale is basically little more than
a mapping that makes some forms of higher level "logic"
easier to implement. You *can* play any pitches you like
without it, but for harmonizers, appregiators, note editors
and stuff to understand what you're doing, you should
preferably use a scale that matches what you're actually
using.
Unless your
LFO or whatever *understands* scales (ie has it's own
built-in scale converter, or can actually "think" in non-linear
scales), it would *always* see and generate 12.0/octave.
If you don't like 12.0, scale and use whatever you like
internally. It doesn't change anything.
But it does mess up the API.
And forcing plugins to request information about what's going on
outside their input ports, and passing that to non-optional host
callbacks does not?
What happens when your not using 12 tone? Do you use
eg.
5.0 per octave?
Nothing. You still se, and are expected to send, 12.0/octave.
Right, wouldn't it be less confusing to just always use 1.0. Its
just that one note is 1/(number of notes), rather than 12/(number
of notes). I really, really dont think that any 12tET based
programmer is going to be confused by having to add 1/12 to go up
one note!
No, but event processor coders are going to think that multiplying
with 12.0 to get actual note_pitch is rather strange when there could
be any number of tones/octave... ;-)
I simply don't think that using 1.0/octave and 12.0/octave is worth
the cost of figuring out when to translate, figuring out *how* to
translate and actually doing it. The actual translation is trivial
for 12tET, but getting there is not.
Its what CV
synthesisers settled on after a good few years of battling, it
just makes everything simpler.
1.0 Volt is a nice and handy figure. Easy to remember, and it
goes well with SI units when you design analog cirquits. There's
nothing magical about the figure as such.
Well, it is /the/ unit, and like you said its easy to remember. Its
much more magical than 12. Conventions are useful things.
Thsi has been a long discussion, so, my position:
I do like 1.0 per octave, linear pitch
I dont like "notes" being in the API.
I agree. But how much is that allowed to cost?
I would be happy to see a clean solution for this, but so far, these
are the only alternatives we have managed come up with:
1. 1.0/note for note_pitch, 1.0/octave for linear_pitch.
Converter plugins required everywhere, even for 12tET.
Even 12tET-only hosts have to handle this.
2. 1.0/octave for linear_pitch, no note_pitch (?).
Host calls required for conversions. Plugins have to
find out what scales senders are using, or who is
sending what. How does this work if multiple senders
send to the same Channel?
3. 1.0/note for note_pitch, 12.0/octave for linear_pitch,
No conversion needed for 12tET. Converter plugins can
be used when other scales are desired. There cannot
be 12tET-only hosts, since scales are not a host side
matter.
More ideas, anyone?
//David Olofson - Programmer, Composer, Open Source Advocate
.- The Return of Audiality! --------------------------------.
| Free/Open Source Audio Engine for use in Games or Studio. |
| RT and off-line synth. Scripting. Sample accurate timing. |
`--------------------------->
http://olofson.net/audiality -'
.- M A I A -------------------------------------------------.
| The Multimedia Application Integration Architecture |
`---------------------------->
http://www.linuxdj.com/maia -'
---
http://olofson.net ---
http://www.reologica.se ---