denemo-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: MIDI (was Re: [Denemo-devel] Anacrusis script needed)


From: Richard Shann
Subject: Re: MIDI (was Re: [Denemo-devel] Anacrusis script needed)
Date: Fri, 11 Sep 2009 11:18:56 +0100

On Thu, 2009-09-10 at 16:02 -0500, Jeremiah Benham wrote:
> On Thu, 2009-09-10 at 21:00 +0100, Richard Shann wrote:
> > On Thu, 2009-09-10 at 14:06 -0500, Jeremiah Benham wrote:
> > > On Thu, 2009-09-10 at 17:15 +0100, Richard Shann wrote:
> > > > On Thu, 2009-09-10 at 08:08 -0500, Jeremiah Benham wrote:
> > > > > On Wed, 2009-09-09 at 22:09 +0100, Richard Shann wrote:
> > > > > 
> > > > > > I don't have any understanding of this stuff at the moment, so I
> > > > > can't
> > > > > > help without reading up on it. Usually there is an example that
> > > > > comes
> > > > > > with these libraries that you can modify and integrate - is that
> > > > > where
> > > > > > you started? Or did you work from the docs? 
> > > > > 
> > > > > I started with the docs and then went to the manual. After studying a
> > > > > few examples I feel like I am getting to grips with it. I just pushed
> > > > > some code to git that emulates a "monosynth" (a synth incapable of
> > > > > polyphony). It is not a synth though because it just writes midi data
> > > > > to
> > > > > the jack port. It sends an all note off
> > > > Is sending all note off just something to workaround some bug? You would
> > > > presumably just want to send a noteon and then a noteoff after the right
> > > > amount of time?
> > > > What happens when you try to do this?
> > > 
> > > If I do just noteon then noteoff a user would to hit "c" then "d" they
> > > would hear c and d at the same time until there corresponding noteoffs
> > > were reached. I did not want that.
> > I can imagine two sorts of audio feedback people might want when
> > entering notes at the keyboard
> >      1. a short pitch is emitted corresponding to the note at the cursor
> >         when the keystroke has been acted on. So c followed by sharp
> >         would result in two short notes. If the player put in notes
> >         quickly enough they might overlap a bit.
> >      2. a pitch sounded for the prevailing duration, but this would only
> >         really be useful when inputting folk type melodies not involving
> >         adding an accidental. (Otherwise two notes would sound, and the
> >         merit of the system - being able to play music in in time -
> >         would be lost).
> > 
> > However if the user were to define commands that combined inserting a
> > note with adding an accidental, this might change:
> >     c  -> the note c
> >     Shift-c  -> the note c-sharp
> >     Alt-c -> the note c-flat
> > If someone were to define such an entry method, they would include their
> > desired audio output in the script for each command, I guess.
> 
> This sounds a bit complicated and tedious just for immediate playback. 
well, someone wouldn't be doing it *for* immediate playback, they would
do it because they would get <entry of a note with an accidental> in
just one keypress, rather than a two-key sequence. But, anyway, that's
another story.
> 
> > 
> > There is a built-in "playback notes entered immediately code"; this code
> > I think should just result in short notes as in (1) above as this would
> > generally be the most useful thing.
> > 
> > >  Currently if not compiled with jack
> > > and the user enters a note via script like this:
> > > 
> > > (d-C)
> > > (d-Sharpen)
> > > 
> > > They do not here "c" and "cis" at the same time. The only here "cis".
> > 
> > I can confirm that this happens. 
> 
> Yes. I actually thought this was a feature not a bug. I like it this
> way. Oh well.

Well, it is not exactly a bug. It is just that a script plays in notes
very quickly, and if you typed in notes as quickly that is what you
would get - the new note gets into the output buffer overwriting the
note already there.
> 
> 
> > I can't imagine why!
So, overnight I have figured this out. The portaudio output keeps coming
back for more data, while every time denemo runs the <playback this note
routine> the data gets changed and the pointer set back to the start of
the buffer. So (d-C) puts a short note of pitch C into the buffer at
offset 0 and sets the current output offset to 0,  and (d-Sharpen) puts
a short note of pitch C-sharp into the same buffer at the same offset 0
and resets the output pointer to offset 0. And all this happens before
portaudio has interrupted for more data. (If portaudio did interrupt you
would get a tiny bit of the other note, which you wouldn't notice).

>  Very strange. Even
> > if you do d-RefreshDisplay you only get the one note. 
> 
> If you want to hear both pitches you can do this:
> 
> (d-C)
> (sleep 1)
> (d-Sharpen)
> 
> You can sleep less than that actually. I don't remember what the scheme
> procedure is at the moment but I think the smallest was .5 seconds to
> hear both pitches if IIRC.
> 
> > And
> > 
> > (d-C)
> > (d-RefreshDisplay)
> >  (d-Sharpen)
> >  (d-CursorUp)
> >  (d-AddNoteToChord)
> > issues only one note.
> > 
> > This is all accidental. I think scripts should take control of the audio
> > output if they want it. (Not sure how you would stop the effect we are
> > seeing - perhaps it is only one note per script?)
> 
> No. Just putting a type of wait or sleep of sorts in there. 
> 
> > > This is the behavior I am trying to emulate with this particular jack
> > > callback function. Do you have another recommendation? 
> > "jack callback function"?
> > Not too clear what you meant: is it clear that I think that all you need
> > to do is wire in a short note output at the same time and in the same
> > way as the current output via portaudio?
> 
> I don't know anything about portaudio. I would have to read the docs and
> study that code also. They way all the jack midi examples I have seen
> work like this (If I finally understand correctly). There is a callback
> thread
you say thread - in fact this is, I suspect, (in general) an interrupt.
This means that the C-code may be in the middle of executing an
instruction when the jackmidi callback routine executes. For this reason
you have to be very cautious what you do inside the routine - calling
printf, malloc etc are all likely to cause insidious havoc.

>  that runs a callback every $SAMPLERATE/$FRAMES_SIZE. A frame size
> of 1024 would process 1024 samples each time the callback is called. I
> learned that the callback itself can be used as a timer for events
> because of this. I tried writing to the buffer 
by "the buffer" I guess you mean the thing that jackmidi is consuming
data from.

> outside this callback
> thread and for some reason it sounded awful.
yes, this would be disastrous since while you are writing to it the
interrupt may happen and the output callback can run taking a mixture of
new and old data, half-written bytes ...

> I heard more than one
> NOTE_ON even though I know it was only called once. So what I am doing
> is creating a global buffer to hold the information that the callback
> needs to process. At the moment the global buffer only holds one note at
> time. I will need to change this if I want to do more complicated
> things. I have to have something that remembers to turn off previously
> sounded notes when it is there time. Perhaps I will create a structure
> and hold the structure in a stack in a GList and then free the list
> member after NOTE_OFF has sent. If not a GList maybe a smf from libsmf.
> Its still kind of fuzzy in my head how to do this. At least I learned
> how to use jack as a timer. 

You have got yourself into the right ball-park here. You actually don't
need to exploit jack as a timer - it is better not to, as it is an
accidental feature of jack that its interrupts are fairly regularly
timed. That's not an important point. What you have got right is that
you need to be thinking of a reservoir of data waiting to be sent to
jack - your "global buffer" which you write to outside of the interrupt
code, ie outside of the callback. But what it sounds like you are
missing is any protection to prevent the "global buffer" being sent to
the output while you are still writing it. And, correspondingly, you
don't refer to the callback routine setting any flag to say that it has
copied the data from your "global buffer" into the jackmidi buffer.

HERE IS MY SUGGESTION: 

Define
  
static volatile gboolean BufferReady=TRUE;

in jackmidi.c, where volatile is to tell the compiler not to optimize
out references to it.


        midiplay(note, vol, duration, channel) first checks BufferReady to see
if it is TRUE ie. if the "global buffer" has been consumed. If not, set
it TRUE and issue a warning about a missed note (and perhaps more, turn
off all notes?).
 Then write the NOTEON data to the "global buffer", and then set
BufferBusy to FALSE. Then set a timer to fire off after duration seconds
with the NOTEOFF data as user_data. The callback function for this timer
I'll describe below.

 The jackmidi callback routine (which should be getting called at
regular intervals, ie. not turned off unless no immediate playback is
needed) should do the following:
 Check for BufferReady, if TRUE just return without doing anything
      if FALSE copy the data from "global buffer" to the MIDI buffer and
set BufferReady to TRUE
      
O.K. that just leaves the timer callback. It is basically the same thing
as before, you have some user_data (which is a NOTEOFF message) and you
have to check BufferReady - if it is TRUE the data has been consumed, so
write the data to "global buffer" and set BufferReady to FALSE. As
before if BufferReady is FALSE on entry it just means you are putting
stuff out to fast, set it TRUE and replace the output data with your new
data.
I have put this up somewhat edited at
http://denemo.org/index.php/Immediate_Playback_via_MIDI

"global buffer" just needs to be a space for a single MIDI message I
think. If you want to output chords then "global buffer" would become a
fifo, and the management becomes a bit more involved - do the simple
case first.
BTW I am several years away from having messed with such things - in
particular the volatile keyword is just dredged up from my memory -
there are things that have to happen at a very low level to force the
instructions you have in mind to actually happen as you imagine they
would (pipelining etc), but I think what I have written is ok.


Richard


> 
> Jeremiah 
> 
> 
> 
> > So entering a triad you would hear the notes as you typed them in/added
> > them to the chord.
> > 
> > Richard
> > 
> > 
> > > One drawback to
> > > this is that if user enters a triad only the lowest note is heard. I was
> > > trying to recreate consistency amongst the types of midi/audio out. I
> > > can modify this function to check if more than one note exists do all
> > > note off followed by a triad of noteons then a triad of noteoffs. I say
> > > triad but it can be any arbitrary number of notes. 
> > > 
> > > Jeremiah
> > > 
> > > > Richard
> > > > 
> > > > 
> > > > 
> > > > 
> > > > _______________________________________________
> > > > Denemo-devel mailing list
> > > > address@hidden
> > > > http://lists.gnu.org/mailman/listinfo/denemo-devel
> > > 
> > 
> 





reply via email to

[Prev in Thread] Current Thread [Next in Thread]