TODO:
 * add comments to the .evt output
 * OS X port
  - fix waveform()s (endian?)
  - fix keyboard.c keymap
  - fix performance (FP? misuse of event model? blocking operations?)
  - sound buffer size?
 * NEXT VERSION: (a naming convention?  shall I be bold and label this 20050601)
  - cmd: 'graph <fn_call>' to make a gnuplot file of that function call...
(because it will be useful)
  - try it with all FP?  should be pretty easy...  see if it gets rid of any of the crappiness or is slower/faster etc...
  - work with ALSA or at least SDL or something like it.  availability of ALSA 0.9.0 tutorial gives hope, let's see if it even works ahem?
  - think of a way to do a scripted language for the tracker thing
  - think of a way to do phase shifting
  - need a 'loop' flag on waveform
  - implement 'loop' (on trackers) and however tracker rehash should work
  - detect infinite loop in flatten_ent() recursion..
  - make the .ggb_history not pollute every current directory forever..  (or decide that this is desired behaviour)
  - make it not die out if you get strings into the compiler
  - make it recognize when you use ""s inappropriately, i.e., instead of '' for notes
  - make more feedback for midi playback
  - some way to split the keyboard into octaves or something, like a 'map' shortcut, for autochord and the like...
  - invent samples i can play with, especially for percussive sounds (snare, cymbal, tom, timpany, bass, hihat)
  - func: 'exponential' and 'logarithmic' functions (mostly for envelopes)...
  - func: 'pink' and 'blue' functions (averaging)
  - left-associate...  a/b*c => (a/b)*c not a/(b*c)  augh
 * AFTER THAT:
  - figure out how to cut out some of the noise in samples
  - make a sample recorder, and make gga work better for cutting up samples
  - add a general 'set' variable interface?
  - really cool idea: develop a way to add events to a layered (even heirarchical?) loop that is going on in real time, for evolving beats -- might be a good upgrade to that tracker scripting language.  that tracker file will eventually need a from the bottom up overhaul, when i settle on a methodology.
  - FM to try:
    - op2r harmonic: "weighted spectra", inharmonic: "wild"
    - try modulating the multiplier.... (try lfo and high freq)
    - try modulating by noise (?)
  - think about how we would implement subtractive synthesis (square, saw, triangle => filter)
  - try additive synthesis (just adsr applied to harmonic multiples of the base freq)
  - fm synthesis from publicly available OPL3 GENMIDI files
  - completion for command names, function names, maybe a hotkey to see a definition for a function (most specifically, the argument names)
 * band names (heh)
  + soft contender
  + fragmentary illusion
  + echosoft
  + softecho
  + sought echo
  + bounce nation
  + sparkle nation
  + sparkle flux
  + sparkle
  + sparkle prowess (beats the google test!)
  + pouty sparkle
  + chipotle sparkle    !
  + sparkle frond
  + sparkle fiddy
  + sparkle six
  + now sparkle zero
  + sparkleshire
 * ALBUM 1:
  + names? acid is a four letter word
  + several other songs (don't love anyone?)
  + kitz on the cover
  + HAPPY SONG:
    - coughing, maybe syncopated?
    - maybe a round using an instrument?
    - maybe something to play off the arpeggios?
    - line-draw music video?  maybe make a 3D linedraw interaction program that uses the glove?  with a crazy object heirarchy, perhaps even the same scripting language!  that would be kind of sweet.  i suspect really a most inappropriate mixing of projects.
  + 
 * ALBUM 2:
  + log song cover

11-09-03.txt: ######################################################

Design for FM synth program.

The overall shape will be an interactive realtime music editing and playing
environment.  It will have a 'scripting language' which should be
generic enough to control all levels of sound generation from editing
to real time user interface to the actual generated waveform.  The
scripting language will use GCC as a back-end because I am lazy.  GCC
will generate .so files which will then be transparently linked into
the running executable.  Stupid but works everywhere!  This is not so
dissimilar from an all-C approach except that we get the added power
of a full parser instead of the C preprocessor to organize the code
most efficiently and we get an interactive environment to hide the C
compiler from us, and avoid restarting the program (or even
necessarily stopping playback) to add new waveforms etc.

Is this problem ammenable to treatment with object orientation?  I am
unconvinced.  While it would be nice to have an 'envelope' base class
or template, I do not think it is actually a good idea.  Every
function should be defined over ranges for its inputs.  And ranges
should be full expressions, capable of holding variables determined
even at run-time (such as the time that a 'key up' event occurs, or
the frequency of the note being played).

v(t,A,D,S,R,t_S) =
	[0, A: t/A]
	[A, A+D: 1-(t-A)*S/D]
	[A+D, A+D+t_S: S]
	[A+D+t_S, A+D+t_S+R: S-(t-(A+D+t_S))*S/R]

Over all other ranges it should be 0, or perhaps undef to indicate
when the note is over?

t_S in that case is not defined until runtime, perhaps, as it is the
length of the sustain period.  It should probably be specified in a
different form, perhaps as the total duration of the key down, and it
should have a range specified somehow, so it is always >= A+D.

It cleans up a bit if we add the variable 'last' which just stands for
the end of the previous range (this is optional, and is used to
make it easier to have like a specific chain of events and satisfy
yourself that they should never be simultaneous).  Perhaps when a
time variable is used in the computation of the endpoint of a range,
it should assume that the variable will be greater than the start
time, and enforce this.

v(t,A,D,S,R,t_up) =
	[0, A: t/A]
	[last, last+D: 1-(t-last)*S/D]
	[last, t_up: S]
	[last, last+R: S-(t-last)*S/R]

What if we specify times as like waypoints.

v_ADSR(A,D,S,R)[t] =
	[t[0], t[0]+A: t-t[0]/A]
	[last, last+D: 1-(t-last)*S/D]
	[last, t[1]: S]
	[last, last+R: S-(t-last)*S/R]

I don't like that, but I also want to be able to consider multiple
events on a note, perhaps even unordered.  I think for a given
formula they should be ordered, but there should be independent ones,
like there could be controls to trigger 'next waypoint' for any number
of attributes about a signal.  

Screw that t[0] concept, but waypoints are good.  Also, specifying
just the duration (and instead of the duration we can specify the
waypoint it must reach), and having t always be since the beginning
of this portion of the range makes stuff easier:
v_ADSR(A,D,S,R) =
	[A: t/A]
	[D: 1-t*S/D]
	[t_1: S]
	[R: S-t*S/D]
I think this is best.  If t has to be cumulative for this to be
efficient, let the back end cobble that together through arbitrary
wizardry.  We don't care if it's only efficient at a specific subset
of its functionality, so long as it is fully functional.

When t_X is used in the RHS, it will evaluate to the total duration of
the tone prior to that event.  I.e., it will be almost useless.

Now I should figure out how to do frequency modulation in this format,
but I must go.

11-13-03.txt: ######################################################

So I have been thinking again about how to make an algorithmic music
maker.  I am happy with the system I defined in 11-09-03.txt.

So essentially we make just a bunch of mathematical formulas that
define a waveform, be it one used for modulation (frequency or
amplitude) or for direct use as an audio waveform.  Each formula has
"arguments", or "free variables."  The final top-level formula for the
song will presumably not have any free variables, except of course
"t".  Individual instrument waveforms will for the most part just have
a free variable for frequency and note-up (i.e., when to switch from
"sustain" to "release").  Waveforms can be a straight formula like
f() = sin(2.0*t), or they can be defined over ranges, which would be
specified just by the duration of each region.  All waveforms start at
t=0 for the waveform, but musical "phrases" or "measures" or "loops"
or "tracks" will consist of a special formula that will be defined
something like
    f(tempo) = g()@0 + g()@tempo + g()@(tempo*2) + g()@(tempo*3),
which would play whatever the tone for g() is 4 times.  @ would
therefore be a special operator that controls the offset from f()'s t
to g()'s t.

There should be strength reduction on anything multiplied by t.
For more details see 11-09-03.txt -- in particular, this strength
reduction should be "flawed" in a useful way so that we can do FM
synthesis easily.

All time values should be normalized such that 1.0 = 1 second, and all
amplitude values should be normalized such that 1.0 = full volume.
This may be hard to pull off, because full volume is 32767 but a full
second will probably be 44100.  The only thing I can think of is that
any formula inside a time-domain (i.e., an arg to a built-in waveform
such as sin() or even dsp()) will have 44100-based arithmetic, and any 
other contexts are treated as 32767-based arithmetic.

The language should be syntactically and semantically the same at all
levels, but it should have some sort of keyword to differentiate a
low-level function from a high-level function.  Any function called
from a low-level function will be completely inlined, constant
propagated, and compiled by gcc.

This 44100 vs 32767 thing has me worried.  Consider simple FM:
   sin((100+sin(t))*t)
100+sin(t) is simple arithmetic, so it can proceed in either and it
doesn't matter so long as the 100 and the result of the inner sin()
are represented by the same multiplier.  But for the strength
reduction, since it is itself an argument to the outer sin(), it is
best thought of in 44100-base.  But the sin() table's values will
probably be in 32767-base, to facilitate playing as a sound.  Augh,
it is such a nuissance, programming efficiently is.  Also we are
going to constantly be bumping against 32-bit limits if I am not
careful.  I guess the x*44100/32760 conversion is not very expensive.

I think it all works out.  The strength reduction is key and prevents
us from overflowing anything.  So let's pretend sin() returns a
*32767, and all evaluation is done *32767, except that t is *44100, so
in order to get t=1 (t' = 44100) to be *32767, we must multiply by
32767/44100
   sin((3276700+sin(32767*t*32767/44100))*t*32767/44100)
Actually, let's try leaving t alone, because we'll multiply by
44100/32767 to get the result back to sin()'s natural range...
  sin((3276700+sin(32767*t))*t)
Now strength-reduce...  For every step of t+=1/44100...
  sin((3276700+sin(32767/44100+

Fuck, this isn't working.  I know it can't be that hard.  I'll
probably figure it out in bed.

Okay, try this...  Multiply everythingy (well, not necessarily
everything, we should figure out carefully the actual rules for that)
by 32767...
   sin((3276700+sin(32767*t))*t)
Then *44100/32767 if it feeds into a sin():
   sin((3276700+sin(44100*t))*t*44100/32767)
Then strength reduce, considering t to increment 1/44100 every step.
   sin((3276700+sin(44100/44100+prev0))*44100/(32767*44100)+prev1)
Now reduce the divisions using LCD:
   sin((3276700+sin(1+prev0))/32767+prev1)
We can't reduce out that 32767 because dividing sin() by 32767 isn't
really happening.

In addition, that *44100/32767 step amove, it should probably be
*4*44100/32767, make the sin() and other waveforms sample at 176400 so
we get reduced rounding error.  t still increments by 44100, though.
To be used as an operand to an @, it should be *44100/32767, that will
not be increased accuracy.

So we need something that builds a tree with doubles everywhere, and
every node would then have a 'double', '*32767', '*44100', or
'*4', or '*1' field.  Very first pass will underline everything under the
'low-level' mark.  Then one pass will convert everything from doubles
to whatever it should be as integers, perhaps using long longs just
in case.  Then the next pass should perform basic simplification and
reduction to normal int32.  Then a pass should convert everything below
the 'low-level' mark into C code and link it in.  Then a UI will
simply take additional function definitions and calls and add them
into the same tree.  This is not really very interactive, that's a
disadvantage -- you wouldn't want to just set up a beat and start
adding to it.  It's fairly easy to convert keyboard events into
function calls though.

Another weird thing...so far this is a totally side-effect-free
language (they are all implicit in relation to t).  I think it should
stay that way.  The only really complicated thing I want to add is
{start, step, count}, which would be legitimate I guess in @
expressions to say "play this at start, then again every step later
for count times."  We could have {start} instead of @.

So our syntax looks like:
   statement: fn_def ; | fn_call ;
   fn_def:    lhs assn expr
   lhs:       sym ) | sym ( formals
   formals:   sym | sym , formals
   assn:      = | :=                 := would be the low-level "compile this"
   expr:      term | [ term_seq | str
   term:      factor + term | factor - term | factor
   factor:    value * factor | value / factor | value
   value:     sym | number | - number | ( expr ) | fn_call
                                     distinguishing fn_call from sym = blow
   term_seq:  term_seq1 | term_seq1 [ term_seq
   term_seq1: term : expr
   fn_call:   sym ( arg_list | sym { time ( arg_list
   arg_list:  expr ) | expr , arg_list
   time:      term } | term , term , term }

Our lexer must recognize:
   ; ( ) [ ] { } , : := + - * / 
   sym: [a-zA-Z_][a-zA-Z0-9_]
   number: [0-9]*\.[0-9]*e[+-][0-9]*
   str: ".*"

The lexer should be pretty easy.

This project will be called ggb.  Greg's Great Beeps.

The convert to integer pass will probably be the hardest, but the
strength reducer is not necessarily a picknick.  Free variables should
have their type inferred from how they're used -- i.e., they're only
strings if they're used as arguments to a built-in such as wavefile()
that takes a string.  In fact, I have no respect for the string.
wavefile() should take a handle, so in the C code it will just be
passing int32s around...it will be the responsibility of the
interpreter to convert strings into file handles.

The interactive program should be structured with two threads just
like gga, one of which will be just for readline.  There will be no X
interaction, but eventually there will be MIDI.

Let's accept comments as anything beginning with a #, to the end of
the line.

I guess it's time to start laying down code.

I think a reasonable plan of attack is to make a bare lexer, then a
bare parser, then the integerizer and strength-reducer, then an
interpretter, then the compiler, then the sound output and command-line.


November 23rd....
I laid out a bare lexer, it is fine I guess.  I expect it to work on
the first try even though it has never been executed.  Laying out a
syntax definition is tough work.  It is so tempting to realize "this
is useless information" and drop it...Like semicolons between
statements, or commas between function arguments.  Why does everyone
else use them?  Are they really that helpful?  I don't think they
really disambiguate anything...  Parentheses have meaning and role but
even in C I think commas in function calls are useless.

I posed the question in the channel and Dennis quickly got to the
heart of the way I was thinking of it, are there any operators that
can implicitly occur between two pieces of data, or would you only
have two pieces of data in a row for the purpose of generating a list
of arguments?  But Neil got to the heart of it, what about unary
operators?  I can give up ++, but I want to be able to use - as both a
unary operator and binary.


November 25th...
On error do we return NULL or use setjmp/longjmp?  If we are willing to
depend on setjmp/longjmp then will that even really help us continue
if the buffer runs out?  I do not think so...

I think this is a characteristic of other compilers, that they are able
to continue after an error condition...  They just start generating
valid but inaccurate structures (i.e., structures that have valid
pointers but represent a program different from what the user typed in).
And they set flags so the final stage doesn't run that generates the
object code (or, in this case, integrates this statement into the
run-time environment).

I think it's obvious I should do it this way because this is already
what the lexer does, it doesn't even generate the error messages.
The actual check for an "error state" would be performed at the top in
scan_statement, to decide whether or not to either integrate a
definition into the dictionary or executes the code.

Another pretty much unrelated question is: should compilation be
incremental?  I.e., whenever a := word is defined, should it compile
it and add it to the pre-compiled dictionary, or should we only do so
whenever a := word is first executed, and at that point compile
everything needed at once?

I guess my only real change in scanning behaviour after encountering
an error will be to only report the first error, as the later ones are
probably cascades?  What about scanning past a semicolon?  That should
probably be impossible - nothing should actually 'consume' a token
unless it's got a use for it.  A semicolon might get inappropriately
scanned somehow but it would be rare or difficult.  Of course a
missing semicolon would be all sorts of bad, it would presumably scan
one token at a time trying to find the end of the statement, but it
never would.  I guess really we'd have to make it just drop everything
in the input buffer when an error is encountered in the top-level
routine, to prevent potential infinite looping if we have a token
nobody can consume.


January 8th 2004.

Most everything works.  Even compiling.  Occaisonally tones get clipping
when I feel it would be inappropriate, but it's fairly rare.  Perhaps
even just a CPU speed problem rather than clipping.  It is not as fast
as I would like -- a likely excuse is that gcc SUCKS at 64-bit math.
Wait, no it doesn't.  It gets multiplies good, but divides are still a
function call.  But what can we do about it?  I don't think I can
implement a much better divide.  I could get a 64-bit processor, but I
bet most of the CPU time is spent outside of the interesting code --
function call overhead and the like.  I wonder if all these stupid
casts in the interesting code make any difference at all.  Hmmm.

So I have 3-operator synthesis going on and everything...  I think it
is to the point where tomorrow with luck I could start running
soundblaster "GENMIDI" patches on it with a simple C translation
program.  We'll see.  I may need some sort of reverb concept, or some
other forms of distorts...but I think I can fake it.  I'm a bit
curious what like "drum mode" is all about.


January 12th, 2004.

Fixed some bugs in the compiler, especially involving termseqs.
Bug fixes aren't exciting...so...I added a new "evt.c" to handle recording
and then playing back streams of events.  The idea is we save a stream
of events, then go at it with a text editor to make everything line up
just so.  I haven't decided if this format allows us to really do what
i want to with regards to overlapping sequences.  Right now it looks
like we're best off storing overlapping sequences in a separate file.
But I think with some effort we could store overlapping sequences in
the same file, but the syntax is hard to decide.  I guess if we run
with the separate file construct then we want separate files to
potentially just be separate functions within one file.  But then I'm
inventing a whole new function syntax.  And just using "play foo.evt"
as a kind of function call, I think that's actually pretty sweet.

I also added conversions so that strings can contain note
specifications that work like the numerical frequency:
    C-5  middle C
    G#3  G in octave 3
    Cb5  same as B-4
Note that my octave starts at C, but maybe it should start at A.
Prior art?  MIDI users seem to start at A, but MOD tracker people and
MIDI actual technical people think of it as starting at C.  It has
been decided - start at C.


January 18th, 2004.

I have decided I need to make a tracker.  I think it needs the basic
file format of a starting line that identifies it as a tracker object
with a "bpm" setting, and maybe some other optional flags.  Then the
actual lines will be one beat per line (so a beat should be a 16th or
32nd note or something).  A line starts out with an event for the
first track, then an optional sequence of "|" followed by further
events for other tracks.  Really, mixing one track with another is
just gonna fuck with your head, there's no problem with it.  And
there's no problem with multiple notes being active for one track.  I
am going to stop relying on the "noteup" command and instead interpret
the special "@1" (lasts for one beat until note up) opcode.  snd_add
will return a reference of some kind to the structure in which note_up
should be modified (to be used by the tracker and the midi part).  It
needs to be decided how "noteup" will be handled in the old-style .evt
files, which should mostly stick around..

The tracker will provide some commands like tload (load a tracker
object into memory for fucking around with), tplay (play a loaded
tracker object), tsave (save changes, maybe make this implicit on
exit, with like a ttrash to drop them), tloop (play the tracker over
and over again, to be mixed with tedit), and tedit (load vi in a new
screen with the waveform open [tmpfile], and poll so it is reloaded
when vi saves).  

Tracker "current location" should be marked with indexes, not
pointers, so as to make it more ammenable to reloading..

Converting a .evt file to a .gtr file will be almost but not quite
non-trivial.

I have been playing with waveforms a bit trying to come up with, for
example, percussive sounds.  Using op1 we can survey a large number of
timbres, which are difficult to characterize or remember.  op2r
(modulating the modulator) is just op1 with distortion, so find the
general tone with op1 then tune it with op2r, that's my bet.  op2a
(added) is totally unpredictable for me at this stage, i think, except
that it sounds like op1+op1 with the two different modulators.  For
the "amount" arguments (A1, etc), I have been using "300", but I think
I should be using "x/2", so that it scales as like a percent of
frequency.

I want to try modulating a wave with white noise.  I just did.  It's
pretty boring...just kind of gurgly.  Might be nice for some drum
effects.

I think all of the interesting waveforms i want to play with require
lots of over-time changes.  like adsr on the FM or even some
mutilation of the frequency.


January 21st, 2004.

Mostly everything is working fine.  I decided to through out the "evt"
crap because it didn't really fit all the existing framework that
well.  I vetoed making the tracker do load/save, it just plays direct
from the file one line at a time, with basically the syntax discussed
above. (events separated by |, with as many events as you want on a
line, with @ specifying when the note up happens).  I think I need to
make a "record to tracker" functionality of some sort, and display
functions for the existing tracker stuff (just status, not actual
displays of the tracked data, probably).

The number at the top of a .trk file is "ticks per minute" (tpm).  One
tick per line, as you would imagine.  The loop functionality and
everything works on the first try now that I've got it written...

I started to get some jumpiness, so I increased buffering from 2x to
4x 4096-byte buffers (which should be about 25ms each).  It still
seems responsive, I don't even really notice the difference.  But just
two 3-operator sounds at once eats 10% CPU, so I fear in order to do
serious music, I will eventually hit the limit, so I should figure out
what needs more optimization..  From a performance perspective, it
would be unacceptable to need to "burp" for recompiles, but I think
the current framework could actually be kept and we just make all the
pre-compiled instruments happen at original load time, then use the
interpretter to direct everything from there.  I should probably
profile it -- is the compiled code eating that much CPU?  Also should
play to decide if I should be using gcc-3.3 -O3 or what.


March 26th, 2004.

I sure have done a good job at building up a huge todo list.  And the
little things I've been fucking around with have all not been on it.

I've decided my first real project is going to be a cover of Belle and
Sebastian's I Don't Love Anyone.  The biggest issue that I haven't
even begun to firm up is how to deal with vocals -- I'm tempted to run
my voice through a pitch-shifter or similar distortions, but that
would require essentially a whole new program (maybe plugins for gga?
I'd have to finish writing gga then...) to deal with waveform audio.
Alternatively, I could somehow make these plugins work in ggb.  Which
is also feasible, but it would require a framework for
effects-processing in ggb, which has so far been untouched.

A framework for effects-processing may be a good idea regardless, as I
would like to be able to apply various filters to white noise to aid
in the generation of percussive sounds.  I guess I could make a new
type of function taht allocates a lot more temporary space and uses it
for linear convolvements or whatever the fuck that's called.  In this
case I should perhaps start out by throwing together some basic filter
programs just to make sure I understand the math before I break my
main program.  Or maybe I should just break my main program because
it's easier to experiment here.

I'm trying to figure out the best way to solve the performance
problem.  There is a definite tendency to simply buy better hardware,
even though that's a peonic approach.  I have decided what I want is
an iBook G5 (64-bit w00t).  They are not yet manufactured.

But the more immediate concern is how to write a tracker.  I think the
idea so far is to define a set of dictionaries.  Each dictionary
defines a mapping of [a-zA-Z] to either a track or a command (52
distinct letters in each dictionary).  Each track specifies which
 dictionary it uses and the number of ticks for each event (which
may be zero) and a chain of events which are played separated by ticks.
The "main" dictionary has all letters played at once.  When defining
something in a tracker file it looks like this:

  dict_X fromdict 128 [a b a b a b a b a b a b ccbbccbba b a b ]
  dict_Y: command
  # comment

the first example defines a letter "X" in dictionary "dict" using
fromdict_a, fromdict_b, fromdict_c played at 128-tick intervals.
The next line defines letter "Y" in dictionary "dict" to execute
command as if it had been typed, but with a special @xx syntax to
generate a "note-up" event 16 ticks later.

The normal command "rate 123" sets the tick rate in ticks per minute.

Ideally the tracker file is read in its entirety and reduced to a
stream of events in memory identical to the current format.  The
"next event" pointer should also maintain a "total elapsed ticks"
counter so if the file is re-read while it is playing, it can jump
into the new data structure.


April 3rd, 2004.

Implemented the tracker pretty much exactly as specified above.  There
were some "oh shit" moments as I ran into unexpected difficulties
actually using it, but once I wrapped my mind around the act of using
it, it's actually very flexible and fun to change around.  The lack of
a 'stop' tracker command is actually the most annoying limitation...
*sigh* finishing touches are butt.

As far as making the pink() and blue() functions, it should be pretty
trivial...  The only concern is I want the buffer length to be (within
reason) variable.  I think I should make it just allocate a static 16
or 32-sample buffer and cap the variable at that.  So in temps it would
need a buffer index (counting down?) and the slots for the buffer...then
actually computing the value would count up that many steps into the
buffer.  The pink filter would add the samples and divide by num (the
number of samples).  The blue filter is a little bit more debatable...
I think it should be half of the first sample minus the other samples
divided by 2*(num-1).  I guess we really should accumulate everything,
so we would do it as (num-1)*samp[0], then for samp[1] to samp[n-1] it
would just be subtracted, and in the end we divide by (num-1)*2.  The
blue will probably be overall quieter than the pink, it is perhaps
dumb, I'm not sure normalizing by absolute value is necessary, but
let's honour the principle of least surprise applied to clipping.

I looked back at old entries and discovered I spelled out what GGB is,
so it's not necessarily going to be one of those forever-mysteries.
*sigh*  and it's a really lame name, it was clearly meant to be a
forever-mystery.  Unfortunately, spelling this program's original
design out in a stoned fashion has not been kind to what details were
recorded :)


May 2nd, 2004.

Looking for some busywork, I have implemented a basic stupid online help
system.  Also added some useless crap to cmd.c.  Yay.

I'm increasingly convinced that my cover of I Don't Love Anyone should
be extremely abstract.  It should have essentially the same lyrics,
and I think I would keep the chord progression, but that is all.
Rhythms, in particular, are going to be totally mechanized, and the
vocal melodies will be smashed.  For this I will definitely want
distortions.  I originally thought I'd try to sing it and use
distortions to conceal the fact that I can't sing, but I think that
should become central.  Read it like a poem, then maybe muss it up a
bit.  I think excessive repetition may be key here.  And perhaps also
vocal-overlapping (like a round).  Very cut-up.  Tension created by
holding onto a repetition or a chord riff longer than natural.

I still think I ought to pound out the keyboard solo, that can
definitely be taken and run with, especially once it is understood.


July 7, 2004.

The things that frustrate me when I sit down and play with it are my
poor variety of instruments and an inability to do any sort of
recording.  So I think I ought to make something that records to .mid
(or similar) and then other tools can extract from that to whatever
sort of information I need for tracking (i.e., they can solve the
quantization problem).  I don't know what to do about the instruments,
except to become more comfortable tweaking them.


September 1, 2004.

Looks like a good place to start would be with saving MIDI events with
timing information...Will be a simple text-format file that looks as
follows:
   [A-Z][0-9]+,[0-9]+
The first character will be a command, then an integer number, then
another integer number.  The integers are optional and default to 0 if
omitted.
   T123       delay 123 ms
   N123,123   press key 123 with velocity 123
   U123       release key 123
   S123       sysex event #123
Okay, done.  My taste is being offended, but I don't know a better way
to be building this stuff.  I will count on experience to show me my
errors, rather than looking for them.
Not tested.


September 2, 2004.

Time for some waveform() functioning!  Every time things get a little
uglier, I guess, but waveform() works now.  A valuable thing to know
about a project, especially when it is getting ugly, is that you will
finish it.  I don't know if I know that about this project, but if I
did, I'd know that I'd work through (refactor, if it becomes obvious)
any mistakes in this code, so I wouldn't worry about them discouraging
further progress.


October 18, 2004.

So I have played around with some modifications to the tone over time.
Some fairly subtle effects with chirps and wahs by changing the FM
synthesis arguments.  Then I added a volume tremolo to ADSR as part
of a mistake and it became quite another diety.  You get polyrhythms
between the tremolo in the FM synthesis and the tremolo on the main
volume.  It's pretty cool.


November 20, 2004.

Disco beat:
beat       1+2+3+4+
hihat      xxxxxxxo
rhythmguit  x x x x
bass       x ? x ?
snare        x   x

Or maybe it should be 2 measures of 4/4, with a more sophisticated rhythm guit
beat       1 2 3 4 1 2 3 4
hihat      x x x x x x x o
rhythmguit xxX xxX xxX xxX
bass       x   ?   x   ?
snare          x       x

It looks like this rhythm guitar moves the emphasis off of the off
beats (2&4), but the eight-note on beats.  Two smaller beats don't
sound significant when leading up to a stronger beat.


December 25, 2004.

So I was playing around and Katy wanted to play, but she hated all the
instruments, so she had me build a xylophone instrument ('fi()' was our
final creation).  She kept on giving me ideas to make it a little
better until she finally could say that it didn't suck completely.  So
it was the first time I really refined an instrument until it didn't
really suck, and it was a positive experience and came out with a very
nice sounding instrument (at least compared to the other ones).  So
I'm gonna add the 'write' command to give some demo for IRC. heh.


January 2, 2005.

I've been playing around with some more 'termseq' stuff, like using it
to make auto-chord sort of stuff.  And I'm finding I really want a
loop construct.  I think the best way to do it is a termseq that, when
it ends, automatically restarts.  Easy enough.  It would be kind of
nice if it would reset any embedded termseqs as well, but i don't
think that's really plausible.  To leave the possibility open, though,
I think I will avoid embedding finite ones inside of infinite ones --
only vice versa.  The new syntax will be [0.1: x, ..., 0.1: y]:

That was pretty fun.  There might even be good ways to exploit the
unintended behaviour for nested termseqs to give indefinitely
increasing sequences?

I'm also adding an 'inst' command to maintain a list of instruments.
It's pretty simple, pretty much the minimum hack necessary to have the
Scroll keys on my keyboard select instruments from the big list..

And finally documenting the language in ggb_help.


January 3, 2005.

Everything is pretty much documented.  I made a 'make release' target,
integrated the meow demo, and made a README file.  It'd be release
quality if it weren't for all the incomplete technical issues.

I noticed there's really no point in building the sine table at compile
time instead of run time.  That was a remnant from running a very early
pre-version of this program on my iPAQ with a StrongARM 206MHz,
computing the sine table took real time.  Jon pointed out that the
same is true of my extensive and painstaking use of fixed point
arithmetic.  He may actually be right -- even the lowest Duron today
has some sort of superscalar megamagical FPU.  It is tempting because
it would simplify some code, it would get rid of some aliasing errors
(which are becoming more audible as I get used to the sound of it), it
might even be faster.  On the other hand I think it is very cool to
use fixed point, and it is still possible (reluctantly) to run this on
like a StrongARM or other non-FPU processor.  It would be a neat
experiment to try.  I should probably do some benchmarking first.


January 5, 2005.

I'm on vacation so I needed to see if it would run on my laptop.
Thankfully whatever issue there was with the OSS output has sorted
itself out.  So I added a keyboard module that displays a graphical
keyboard in a window and when you type in that window, it generates
note events.  It also displays note events that are happening...


January 15, 2005.

I implemented a path search function, so that you can run ggb in a
project subdirectory and it will find the global .ggbrc and fm files.
Also in the directory happy is my bong's theme song.  In doing this
I'm discovering a dramatic limitation of the tracker format.

Really, I'd like to be able to specify note and octave and loudness
and maybe one other thing for each note (i.e., in a sequence
definition), but I really like the easy editability of having a single
letter represent each note.  It is true that we will almost never
need more than 52 unique events in a single sequence, but it kind of
sucks to define a new vocabulary for every little track, and it makes
it pretty much a write-only format.  Which is maybe a livable
restriction.

But I think I will definitely be wanting ways to change the low-level
sound characteristics at even a very high level (i.e., through
several levels of sequence nesting), so a totally new system will be
necessary.  I can't think of any good ideas for this.  I remember I
once had an idea for it that I lost, but rediscovered in that book
about the history of electronic audio, so perhaps I should look
through the photos I took of that...

It does kind of suck how it reads one .ggb_history, then writes another.
There is a better way -- perhaps remembering which one it read and
always writing to that one, so it will never create a new one unless
it couldn't find one in the default path (.:..:$HOME).


January 16, 2005.

I've been thinking about how to make the squencer more powerful.
There are two concepts that are needed...one is to be able to do
complete function calls (like "inst('A-5',0.4);") inside of a
sequence definition.  The other is to be able to have arguments to
dictionaries, or something.

The former can be solved by a new definition format:
   mary_a 8 {
     inst('E-5',0.5); @ 1

     inst('D-5',0.5); @ 1

     inst('C-5',0.5); @ 1
     ...
   }
Basically, one line per sequence point instead of one character.

The other can be solved by somehow giving parameters to either whole
dictionaries or individual definitions.  The parameters should have
default values, and there should be a way to pass them even in the
condensed sequencer definition format.

Consider putting the arguments into dict_0-dict_9...they would be
inserted into the input stream through some sort of macro expansion
capability as like $0-$9, and they would have defaults from regular
definitions, and passing arguments to them would be done on regular
sequencer lines with just parens after the dictionary name, and on
extended sequencer definitions you would need to have a way to call a
tracker element (special character, like colon?):
  melody_0: 1.0   # default loudness
  melody_a: inst('A-5',$0);
  melody_z melody 8 [a a ]
  main_a melody(0.5) 32 [zz  ]
  main_b 32 {


    :melody_z(1.0)
    :melody_z(2.0)
  }
    
When invoking self without specifying arguments the ones passed to us
should be used, so that it doesn't go back to default when calling
local samples.  So like for calls to self from one-line sequence
definitions, implicitly pass ($1,$2,...).  Multi-line sequence
definitions, everything should be explicit.

And god said, let it be.

And it works pretty well after just the usual half hour brush with
the debugger.  Now I need to decide how it is best used.  I'm going to
convert the melody (ha - py - ha - py - bong - bong) to it then mess
around.

It made the crescendo convenient.  Now I just need to decide how I
want to pursue the variation verses.  There should be one very quiet,
one "broken down" without any melody, one with high bouncy arpeggios,
one with "fun" instead of the first bong.  Also of course need to add
bubbling throughout the song.

So as far as the tracker goes, the only feature really lacking now is
some sort of 'reload from file' so I can change it and continue
playing it.  That should be pretty easy, but I may want to add some
sort of "add this starting at the next 8-aligned tick", to make improv
easier..

Also it is becoming very clear that we're going to need some sort of
compiled instrument cache.  Maybe generate an md5 for the complete
text of the function and all called functions, and make a .so with
that name.  If it exists, use that, otherwise recompile and save with
that name.

Implemented an initial version of this .so cache concept, it is now
working well.  Yay!  Instant loading now.  I wonder how long until I
bother to clean out my cache directory.


January 20, 2004.

You know what I am finding really annoying about this program is
there's no way to start halfway through a tracker file.  The song
I am writing I am writing concatenatively (perhaps this is an
argument against it, heh), and so I am always working on the very end
of the song but listening to the very beginning of it over and over.
I really think it would be nice to solve it in the shape of this
existing scripting language, but I don't have any obvious ideas that
sound great off the top of my head.

I suppose I could split main much more coarsely, and have it call an
intermediate layer that triggers the individual instrument
dictionaries?  That way you could even put arguments to the instrument
dictionaries in the middle of the song more easily.  Except there's no
good way to do that because of this offset issue.  The song is
generally on a 10 or 12-bar pattern (I can't remember), but it has
two-bar intros scattered throughout, so we'd need a new way to divide
up time.

The best overall solution I can come up with is to include longer
sequences in the instrument dictionaries, but to still track the main
sequences very coarsely.  This has a more abstract level of control
but it still has the obnoxious very long blocks in main, and it
doesn't really solve any of the real issues.  

I am getting some mileage out of this really elementary idea of
putting instrument definitions in events.  It doesn't solve every
problem, but it allows some of the same sort of manipulation that I
was trying to get at before with the argument concept (which is almost
totally unused).

I keep forgetting that there is a way to reload the tracker file once
it is already playing.

And there's another extended mix for IRC, woo.


February 1, 2005.

I have been considering this concept of making the tracker file a
language in its own right that, when executed, adds events to the
tracker queue.  It could be executed either immediately or from
events (I guess).  It would need to be based either on for loops (and
counters and other support functionality) or perhaps tail recursion
or some continuation passing style.  The syntax and capabilities of
this language still evade me but that such a thing must someday exist,
even if it is totally backwards compatible, seems inevitable now.


February 5, 2005.

What about phase shifting from left to right?  Would this provide an
interesting '3D sound' sort of effect when used with headphones?  I
don't really know but the trouble is, I don't think this language
supports phase shifting, it would mess up evaluation of timest.  That's
something I think I really want, in the next version, in addition to
being FP-based.  Also, I should finally comprende ALSA.  This means
comprehend it, not JUST write up a flame in the why alsa sucks
article.  Post some code in there to really show em :P

I'd like to ideally do these things in a new program for some reason,
in hopes of shaking the dust out of this one.  But I think this one
has reached the point where it is worthwhile, almost necessary, to
chase down the bugs from within it.  The hardest big step will be
switching to FP without totally committing to it.  I guess I can just
rely on RCS to save me if that turns out to be a nightmare in
performance world.  It probably wouldn't be such a big change.

Maybe instead of ALSA I should use SDL!!  Or some other ALSA library.
Do more research on this later!

While I'm at it, I should clear every single todo item!

Especially that language for the tracker scripting!

I've been playing a little with this real nit pick of an issue:
  - way to begin recording automatically at the beginning of a tracker, and stop at the end

I'd gotten to the point already of putting 'write' commands into the
tracker file so the track is of a fixed length.  But the process of
recording the track was still not automated, the shell script could
not run ggb because ggb would not terminate.  I think I've simply
decided that I will put a 'quit' command in the tracker file
and I will just disable this when I'm working with it, and enable it
when I release.  For some reason I feel it is important that this is
the sort of compromise that is made in this project, keep the code as
simple as possible where it's poorly formed, so we don't have layers
of crap piling up on top of eachother in that way that scares off
newcomers.

Now my crap is in distinct little piles, thank you very much for
noticing.

Okay, I have to make my happy bong song into a music video also, with
frame-by-frame style animation created somehow.  So I need a drawing
program that makes it easy for me to draw extremely simple stick
figure stuff.  Ideally the tablet would work well and I'd use that,
hah!  Maybe I should buy a real tablet.  Anyways, I should get back to
specifying all the stuff that I need for next version.  In the TODO
list, I am going to branch off a 'next version' and a 'someday'
version of the list.

Wow this is quite satisfying, I am deleting things out of the list
left and right because they've already been implemented.

I have no idea really how to deal with this phase shift / 3D sound
kind of idea.  I'd kind of like it to be as easy (or easier) to access
as the modifier system in place already for stereo sounds, but
integrating any of this functionality into the thing might be a bad
idea.  It would probably be better to just make it so that the
timest-oriented stuff can handle phase shifts, basically, can handle
addition of more than one timest element to make a function argument.
should be pretty easy.

I'm trying to put all of the "well, this is just a lot of tedious
work, but really should be done" things on the 'next version' list,
and most of the real 'dream a little dream' items on the 'after that'
list.  What I'm learning about project management!  The only one I
have no idea on so far is an important one though, it is making the
new tracker format.  The previous hacks did little to address its
usability hurdles.  Everything those hacks helped with, I found other
hacks that worked with theold way of doing things that worked just as
well, I think, except for maybe in the case of the
ha-py-ha-py-bong-bong tracks, because they featured 3 different
samples in a directly connected rhythm (but thinking too much like
this is hurtful because there's no way to trigger two events on the
same channel this way).  It seems a common approach to solving the
problem I am seeing is to lay out a track as just a long straight-line
sequence rather than trying to enforce internal heirarchy.  I reject
this approach as un-Greg-like although I can see it's tremendous
appeal.  

And so we need for example a language that will at least allow me to
build up tracks based on a more arbitrary time scale, so that I can,
for example, clip out the 'verse' in the happy song.  Also, perhaps a
more 'aspect-oriented' way we should be able to specify the various
additions.  By that I mean we can specify where a tracker element goes
where the tracker element is defined, rather than in some master
sequencer definition..  I should decide if that will be sufficient.
It very well might be.  The only trouble I see is "inserting" tracker
elements, I don't see any way to make that correct the aspects of
things correctly, without involving some clever editor macros or just
doing it by hand.  I guess if sequencer elements could just have an
offset in ticks from their virtual start to when their sounds start
executing, that would probably be sufficient.  That would screw the
aspect-oriented nature, but it would get across the functionality
that is actually needed maybe.  Tempted to implement it and see what
happens, but I'm scared it will enter the same world that my
'dictionary arguments' concept went (a useless piece of code).  I
guess one more piece of useless code will just give me a better
excuse to really gut the tracker format when I do perform the
inevitable gutting.


March 1, 2005.

Well the OS X (Darwin) port using CoreAudio is nearly done.  CoreAudio
appears to prefer Float32 (but will happily convert from SInt16 for us,
apparently), so that is one interesting hint for me about a potential
future direction.

The dynamic linker is a PITA on OS X.  Not so simple to build dylib
files, and no good way to have them reference externs (such as
sine_table).  So a simple init_globals() function to set all that
up doesn't present too much difficulty, and makes the code much more
generally portable (i.e., no longer relies on special gnu binutils
dynamic lib features).

A big issue is the way the sound architecture works...you register a
call back and then it is called by magic.  Unfortunately, magic is not
an accepted computing process.  It is well-demonstrated because if our
render callback goes into an inf loop, it takes out OS X completely
for about ten seconds until (presumably) some timeout gets hit.  Very
frustrating, and very bizarre.  ktracing has given no clues as to how
these callbacks are invoked.  I have a suspicion that they're invoked
in kernel mode and this is OS X's way of saying "rm -rf me plz".

The dlopen() seems to take a while, but only sometimes.  That is to
say, even cached instruments do not sound immediately.

Also, it is abhorrently slow.  Instead of a single sound taking 3-5% CPU,
it takes about 70%.  A serious problem.

Also, I still haven't found out how to set the sound buffer size.

Also, the X keyboard mapping is all wrong.  So 4 new todo items..

And for some reason, waveforms don't work either.


March 3, 2005.

I have somehow broken the audio output on my Mac.  That makes it about
ten times more fragile than kernel-level drivers are allowed to be.

On the other hand, I seem to have figured out the X keyboard repeat
problem.  The problem is that when XAutoRepeat is enabled, the X
server generates extra down events (of course) with no stateless way
to recognize that they are not actual down events.  The problem is
compounded by the fact that there is a wrong up event generated
immediately before the down event.  The fact that the key never went
up and that therefore we have an actual loss of information situation
happening here apparently doesn't bother you if you're stupid and
writing X servers.  But the hack is to just make it so that if a down
event immediately follows an up event in the same call to
keyboard_poll() (i.e., in essentially one X protocol packet), ignore
the down event.  And to make it bulletproof, we also just queue up
events rather than performing them immediately, so that we do not
generate a note up when repeat starts.  This works great under OS X,
though I cannot hear sounds.  I will try rebooting.  *sigh*

Rebooting "corrected" the audio output problem.  

Playing around, we have no problem managing half a dozen simultaneous
notes, so long as new notes are not beginning.  That is to say that if
you pound on the notes, it skips, but if you start them one at a time,
it is all happiness.  Most peculiar.  gprof shows that substantial
time is being spent in __divdi3 (64-bit integer divide I think),
indicating that perhaps life would be improved if we were floating
point.  And I guess it is possible that it is just skipping.  In fact,
it looks a lot now like that is what is happening.  So there may be a
performance problem, but the clipping appears to be an actual sore
point -- I don't know why I never noticed it with the PC version, I
suppose.

I need to learn how Apple's CoreAudio events happen.  My current
hypothesis is that somehow in a way invisible to ktrace, a separate
thread (perhaps in Mach space, rather than ?xnu? space) is created
which is responsible for handling this sort of stuff.  Perhaps these
threads are persistent?

Brain flash: the waveform() function isn't working due to Endian issues.
I guess I will just keep gga as a little-endian format, and swap for
big-endian world.


March 5, 2005.

Now that the waveform output works, it's easy to test it with a song,
and it definitely has dramatically inferior rendering capacity -- it
can't keep up with realtime.  But it's not a linear increase in CPU
time.  I wonder if it is just some ridiculous overhead or if it is
actually way too slow.  I guess I should seriously consider just
writing an FP version to see how it performs.  Make a cvs tag so I
don't regret it.


July 10, 2005.

Well, I've been considering my various scripting formats trying to come
up with a grand new design that brings them together and makes them all
easier to use.  So far I have met with failure.

However, I have another idea for an interface.  This one would exist
mostly as an interactive interface rather than a file format, though
obviously it would be savable.  I would call this one 'patterns.'  The
idea is to record a sequence (some number of measures) live from the
keyboard, then simply set it looping.  Layering would of course be
the main feature.

The main command would be "pattern", followed by an optional pattern
number, then a subcommand.  If the pattern number is left out, either
the most recently referenced pattern will be used or a new pattern
will be created.  All time will be in miliseconds.

Some basic pattern commands:

  tempo [measure length]
    This sets the global measure length, which is how long it takes
    for a single-measure pattern to repeat.  If a single-measure
    pattern is longer than the measure length, it will play on top of
    itself.  The tempo is only used to repeat patterns, not to alter/clip
    what is inside of them.  If the measure length is unspecified, the
    currently selected patterns will begin playing without any repeat,
    and the measure length will be set to correspond to when a user
    touches a key on the midi keyboard.

  new [measures [sequence]]
    Builds a new pattern (or overwrites one if one is explicitly named).
    If measures is specified, that will be how many measures to record
    for at the current tempo (the default is 1).  If sequence is
    specified, it provides the notes of the pattern.  Otherwise the
    notes are taken from the keyboard.  The sequence format is
    "offset1,note1,velocity1,duration1 o2,n2,v2,d2 ..."  Offset is time
    in ms from the beginning of the pattern for the start of the note.
    Note is the MIDI note number (0-127).  Velocity is how loud the
    note is (0-127).  Duration is how long (in ms) the note lasts.
    The notes need not be presented in any specific order.  Notes with
    a negative offset occur offset from the end of the last measure in
    the pattern.

  mute
    Toggles the mute flag for this pattern (default is unmuted).

  start
    Starts the pattern playback engine for all unmuted patterns.

  stop
    Stops the pattern playback engine (also "stop pattern").

  write <file>
    Writes all patterns (or a single pattern if one is explicitly
    specified) to the specified file, with ".pat" added if no
    extension is specified.  The files will contain a sequence of
    pattern commands to be loaded with "source."

  shift <amount>
    Shifts the current pattern amount ms in time (i.e., adds amount to
    all of the offsets).  Negative amount is of course allowed and
    should work flawlessly due to the behaviour for negative offsets.

  inst <instname>
    Specifies the function to be used for the instrument for the
    current pattern.

  list
    Displays a list of all patterns, with maybe some basic display
    information of the contents.


July 26, 2005.

I am having a hard time envisioning how this will be used to produce
actual structured songs.  But other than that, I am very happy with
this pattern interface, by far the most fun way to track with this
synthesizer yet.  I just pounded out an arpeggio, then layered random
stuff on top.  When I realized I hadn't really planned any of the
random stuff and therefore it sucked, I simply imposed an unnatural
rhythm on it ("pattern rhythm 0 2 4") and it all came together.
Very happy.
