[comp.sys.amiga] Keynote

arensb@cvl.umd.edu (Andrew Arensburger) (12/05/90)

In article <1990Dec4.134944.16669@cbnewsl.att.com> jpg@moss.ATT.COM writes:
>>BTW, has anyone heard about Keynote language for the Amiga?  
>>I saw it in Keyboard Mag. that it's available for the Ami and I was
>>wondering where I can get a copy.
>
>send mail to Tim Thompson tjt@twitch.att.com, who developed keynote and
>he should be able to tell you how to get a copy, I think you can get it
>for $50.00. 

	I haven't even seen the beast, and I'm already sold on it. For
general information, I am including the following blurb that Mr. Thompson
was kind enough to send me (reprinted without permission, but I don't
think they'll mind me pushing their stuff):

----- Begin quoted stuff ------------------------------------------------

		Keynote User's Group

	A Keynote User's Group is being organized to provide a way for
users to share new functions and example applications through a quarterly
newsletter and a BBS archive. Keynote has also been sub-licensed, allowing
the distribution of binaries directly to end users. This will be a much
more convenient way of getting Keynote for many people, especially
personal computer owners (Amiga, Macintosh, Atari ST and IBM PC's). For
more information, please contact:

		Jon W. Backstrom
		Applied Digital Arts
		P.O. Box 176
		Bloomington, IN 47402-0176

		(812) 336-3660 (after June 1, 1990)

		Internet: media@silver.ucs.indiana.edu
		UUCP: {ames,rutgers,att}!iuvax.silver.media

	Membership in the Keynote User's Group is $20 annually, which
includes a subscription to the quarterly newsletter and access to a
growing library of Keynote functions and musical sequences.
	Sub-licensed Keynote binaries are being distributed for $49,
including a one year membership in the Keynote User's Group. (Source
code is only available from the AT&T UNIX Toolchest.)

----- End quoted stuff --------------------------------------------------

	As noted above, Keynote is to sequencing as Emacs is to text
editing: it's programmable, so if you don't like the way it does
something, rewrite it! And yes, that includes the windows, menus and
stuff.

-- 
-------------------------------------------------------------------\\\\^
Andrew Arensburger            | K&R C!   |  I hate Lisp functions      o\\\\\-
arensb@cvl.umd.edu            | ANSI no! |  that start with             /
...!uunet!mimsy!cvl!arensb    |          |  "(catch (mapcon (throw"  \_/

nichomax@cattell.psych.upenn.edu (Nicholas Maxwell) (12/05/90)

What's the language in which you must program keynote?  C?

nichomax@cattell.psych.upenn.edu

mab@druco.ATT.COM (Alan Bland) (12/08/90)

In article <34068@netnews.upenn.edu> nichomax@cattell.psych.upenn.edu (Nicholas Maxwell) writes:
>What's the language in which you must program keynote?  C?
>
>nichomax@cattell.psych.upenn.edu

The language itself is called Keynote, and is a derivative of AWK (a C-like
language available on UNIX).  Keynote is an interpreted language, so you
don't have to go through the edit-compile-link-run cycle.  As such you get
immediate feedback on changes you make at the expense of some overhead
during execution.  It's terrific for musical "hacking!"

The language includes some very powerful constructs for manipulating
phrases of MIDI notes.  For example,

	ph = 'c,d,e,f,g'

sets the phrase variable ph to contain the first five notes of a C-major
scale.  To transpose the pitch of phrase up an octave (12 tones), just type

	ph.pitch += 12

You can play the phrase backwards by

	reverse(ph)

Included below is the actual Keynote code to the "reverse" function so you
can get a feel for what the language is like.  Keynote comes with hundreds
of functions like this, and of course you can write your own.


	func reverse(ph, leng,nt,r)
	{
		if ( nargs() == 0 ) {
			print "usage: reverse(phrase)"
			return '';
		}
		leng = ph.length
		r = ''
		for ( nt in ph ) {
			nt.time = leng - nt.time - nt.dur
			r |= nt
		}
		return r;
	}

--
-- Alan Bland
-- att!drutx!mab == mab@drutx.ATT.COM
-- AT&T Bell Laboratories, Denver CO
-- (303)538-3510

jpg@moss.ATT.COM (Jeff Grundvig,14C-316,7324,ATTBL) (12/08/90)

In article <34068@netnews.upenn.edu> nichomax@cattell.psych.upenn.edu (Nicholas Maxwell) writes:
>What's the language in which you must program keynote?  C?
>
>nichomax@cattell.psych.upenn.edu


Keynote is a programming language, so you program in keynote. By
modifying the menu functions that come with it you change your menus....

jeff g.  jpg@floyd.att.com

tjt@cbnewsh.att.com (timothy.j.thompson) (12/10/90)

> >What's the language in which you must program keynote?  C?
> Keynote is a programming language, so you program in keynote. By
> modifying the menu functions that come with it you change your menus....

Here's some more info and examples to supplement what Alan Bland
gave in a previous article.

Keynote has associative arrays, just like awk, except that you can
also use musical phrases as the array subscripts.  This comes in handy
for look-up tables and other things, e.g. this made it very easy to write
a markov-chain function.

Realtime programming is easy.  The following is the complete code
for an 'echo' function that echos all incoming MIDI notes, 1 beat later:

   function echoit(a) {
        sched(a,1b)        # hard-coded echo time of 1 beat
   }
   function echo() {
        interrupt(echoit,NOTEON | NOTEOFF)
        realtime()
   }

Since it's an interpreted language, there is a limit to the processing
you can do before delays are noticeable, but you can still do fairly
interesting things.  Scheduling computations 'off the beat' is
one trick I use a lot.

The contents of all pop-up menus are user-defined, and it is trivial
to add new ones.  E.g. let's say you wrote a new function to add a
copy of a phrase to itself, transposed by a fifth:

   function addafifth(ph, r) {
	r = ph
	r.pitch += 7
        return ( ph | r )
   }

This would be added to the pop-up menus with the single statement:

   menu( "proc", "Add A Fifth", "{MODIFY(Pick=addafifth(Pick))}" )

This would add an item to the "proc" menu.  The MODIFY macro is a
convention used to include the code tha allows you to undo the
effects of any editing operation.  You can undo any number of operations,
and you can undo the last undo.  By the way, the undo facitiliy of
the graphical editor is not actually built into the Keynote language,
it is all implemented with Keynote code.  This points out an area of
potential confusion.  The graphical user interface of Keynote (which
looks a bit like your typical piano-roll sequencer) is completely
implemented with Keynote code - the language itself actually (and
purposely) has very few built-in capabilities - ie. it has just enough
built-in capabilities to make to possible to implement the rest
at the 'user level' with Keynote code.  This arrangement
has been a huge win for both development and use.

You can easily write code that simultaneously handles MIDI I/O, console I/O,
mouse actions and graphics.  So, even though the graphical features of
Keynote were somewhat tailored to providing a piano-roll sequencer interface,
you can do completely different things.  For example, the code at the
end of this article implements a complete graphical drum-pattern editor.

Any other questions, mail to tjt@twitch.att.com.  If you'd like a
hardcopy of the language reference manual, send me your postal address.

   ...Tim Thompson...AT&T Bell Labs/Holmdel/NJ...tjt@twitch.att.com...

========================================================================

# This code implements a drum-pattern editor.  To specify the MIDI
# channel and pitches of your drum machine, create a file that looks like:
#
#		chan 10
#		bass 35
#		snare 38
#		closed_hat 42
#		open_hat 46
#
# Then, invoke:
#
#		kboom(nsteps,filename)
#
# where nsteps is the number of steps you want in the drum pattern.
# The step size is 1b/8 (a 32nd) by default, but you can set it (Stepsize)
# to whatever you want.
#
# The screen will show a matrix (drums on vertical axis, beats on
# horizontal axis).  The drum pattern will be playing constantly -
# click mouse button 1 wherever you want to add/delete drum hits.
# Press 's' to start and stop the pattern, 'q' to quit.

Nsteps = 0
Stepsize = 1b/8

function kboom(nsteps,mapfile) {
	readdrums(mapfile)
	arrayinit(Dbeat)
	Nsteps = nsteps
	for ( n=0; n<Nsteps; n++ )
		Dbeat[n] = ''
	Showstart = Showlow = Showbar = 0
	Showhigh = 128
	Kbcount = -1
	Kbon = 1
	Kbxinc = (Nsteps*Stepsize)/50
	Kbxinit = Kbxinc * 12
	Kbyinc = Showhigh / (Kbdrums+1)
	Showleng = Kbxinit + Nsteps * Stepsize
	Grid = ''

	# set up actions for triggering drums and user interaction
	sched(Dbeat[0],0)	# scheduled only once
	sched(kbtrig,Stepsize/2,Stepsize)	# repeatedly scheduled
	temposet(1)		# allows tempo control from console
	action(BUTTON1DOWN,kbmouse)
	interrupt(kbcmd,CONSOLE)

	print "Press 's' to toggle sound, 'q' to quit."
	kbdrawall()

	realtime()	# realtime loop during which everything happens

	# Construct a single phrase containing completed pattern.
	Kbpattern = ''
	for ( n=0; n<Nsteps; n++ ) {
		ph = Dbeat[n]
		ph.time = n * Stepsize
		Kbpattern |= ph
	}
	Kbpattern.length = Nsteps * Stepsize
	print "Result is in Kbpattern."
}

function kbcmd(c) {			# handle keyboard commands
	if ( c == "q" )
		stop()
	else if ( c == "s" ) {		# toggle sound
		Kbon = 1 - Kbon
		if ( Kbon )
			Kbcount = -1	# reset to beginning of pattern
	}
}

function drawbeat0() {		# draw flashing marker on beat 0
	y = kbystart(0)+1
	draw(kbxstart(0)+1,y,kbxstart(1)-1,y,XOR)
}

function kbtrig(b) {	 # repeatedly executed every Stepsize beats
	if ( Kbon ) {
		b = (Kbcount++)%Nsteps
		sched(Dbeat[b],Now+Stepsize/2)
		if ( b == 0 ) {
			# make beat 0 obvious with a flashing line
			sched(drawbeat0,Now+3*Stepsize/8)
			sched(drawbeat0,Now+11*Stepsize/8)
		}
	}
}

function kbxstart(n) {		# return x value of beat n in matrix display
	return Kbxinit+n*Stepsize;
}

function kbystart(n) {		# return y value of drum n in matrix display
	return Showhigh-(n+1)*Kbyinc;
}

function kbmouse() {	# handle mouse button presses, adding/deleting drums
	kb = kd = -1
	# figure out which beat (b) and drum (d) is being chosen
	for ( b=0; b<Nsteps; b++ ) {
		if ( Mouseclick > kbxstart(b) && Mouseclick < (kbxstart(b+1))){
			kb = b;
			break;
		}
	}
	for ( d=0; d<Kbdrums; d++ ) {
		if ( Mousepitch < kbystart(d) && Mousepitch > (kbystart(d+1))){
			kd = d;
			break;
		}
	}
	if ( kb < 0 || kd < 0 )
		return;
	if ( Kbdrumnote[kd] in Dbeat[kb] )
		Dbeat[kb] -= Kbdrumnote[kd]	# if already there, delete it
	else
		Dbeat[kb] |= Kbdrumnote[kd]	# if not there, add it

	# draw (or undraw) an X in the specified box
	draw(kbxstart(kb),kbystart(kd),kbxstart(kb+1),kbystart(kd+1),XOR)
	draw(kbxstart(kb),kbystart(kd+1),kbxstart(kb+1),kbystart(kd),XOR)
}

function kbdrawall() {			# draw matrix and labels
	redraw()			# clear screen
	for ( b=0; b<Nsteps; b++ ) {
		x = kbxstart(b)
		draw(x,Showlow,x,Showhigh)	# draw line
		# show beat number at top
		draw(string(b+1),x+3*Stepsize/8,Showhigh-Kbyinc/2)
	}
	for ( d=0; d<Kbdrums; d++ ) {
		y = kbystart(d)
		draw(0,y,Showleng,y)		# draw line
		# show drum label on left
		draw(Kbdrum[d],Kbxinc,y-5*Kbyinc/8)
	}
}

function readdrums(mapfile, arr) {		# read a drum map file
	arrayinit(arr)
	Kbdrums = 0
	Kbchan = 1
	while ( read ln < mapfile ) {
		split(ln,arr)
		if ( arr[0] == "chan" ) {
			Kbchan = 0 + arr[1]
			continue
		}
		Kbdrum[Kbdrums] = arr[0]
		Kbdrumnote[Kbdrums] = makenote(0+arr[1],Stepsize,'a'.vol,Kbchan)
		Kbdrums++
	}
	close mapfile
}

dgold@basso.actrix.gen.nz (Dale Gold) (12/13/90)

Quoted from - mab@druco.ATT.COM (Alan Bland):
> Keynote is an interpreted language, so you don't have to go through 
> the edit-compile-link-run cycle.  [...]
> It's terrific for musical "hacking!"
> 
Bearing in mind of course, that "hacking" has a completely different
meaning in the musical world! :-)
--
    /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\
   <  dgold@basso.actrix.gen.nz   |   Eschew Obfuscation   >
    \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%/