Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!utgpu!water!watmath!clyde!burl!codas!killer!academ!uhnix1!sugar!peter From: peter@sugar.UUCP Newsgroups: comp.sys.amiga Subject: Re: MIDI Questions Message-ID: <716@sugar.UUCP> Date: Mon, 14-Sep-87 06:00:01 EDT Article-I.D.: sugar.716 Posted: Mon Sep 14 06:00:01 1987 Date-Received: Sat, 19-Sep-87 07:24:44 EDT References: <15740@aspvax.UUCP> Organization: Sugar Land UNIX - Houston, TX Lines: 130 Summary: A couple of suggestions... I'm not a musician... these suggestions are purely from a software design standpoint and exposure to MIDI people (wow, what's that 5-pin jack in your neck for? :->). In article <15740@aspvax.UUCP>, eraps2@aspvax.UUCP writes: > [MIDI.DEVICE] > > 1) Ability to grab EVERY byte as it comes in to at least 33000 baud > (3300 bytes/sec). > 2) Ability to know WHEN the byte came in (so we can calculate the > durations between note on and note off, etc ...) I would suggest softening this requirement to know when the first byte of any MIDI packet comes in. This would require that the midi.device operate in terms of midi packets, but that's a good idea anyway. > 3) Ability to send out bytes at precise points in time (at least to > a good enough resolution to fool the ear) Again, make this "Ability to send out MIDI packets at precise points in time". > open midi.device (default everthing to MIDI parameters) In the open, specify what MIDI device number you want to be in the device field, that way you can have a number of midi programs running simultaneously without interfering with each other. You also want to echo midi messages from one program to all the other programs (sounds like SoundScape, don't it?). > while (record) > read_midi_value, place in buffer Read midi packets... > clear time value on 1st MIDI code/time element to 0 > to playback: > write_midi(1st 1024 MIDI code/time data elements in buffer) Write midi packets... > while (playback) > write_midi(next 1024 MIDI code/time data elements in buffer) > write_midi(last partial data (< 1024)) > > data format: > [MIDI code][time since last code was read] [MIDI packet][wallclock time packet arrived] > Driver Requirements: > new global: miditime last_time; /* actual time of last code received */ > > open: > same as current driver, but make all values default to those MIDI uses > (as described in the RKM). Therefore, setparms won't be used Use "unit number" in open to specify MIDI device number. > input: > when a byte is received, place it in the buffer (as is currently done). > also add to the buffer the elapsed time since the last byte was received. > this is why we need a new global (or at least a static). Thus, now each > input value is represented by 2 values, the actual code and a time-stamp. Whan a byte is received, buffer it up. When a MIDI packet is recieved, place it in the buffer or send it to the task, depending on the settings of timeout and buffer size. If a packet is interrupted by a higher priority packet (I don't remember whet these are called... system exclusive?), stack the first packet and accumulate (and pass on) the new one first. Then when it finishes unstack and finish gathering the old one. When the first byte of a MIDI packet is received, timestamp the packet. > output: > output MUST be buffered, since the task feeding us with values may be > interupted at any time (and the music would hang until it became active > again). The simplest solution is to write the values in blocks. Then > the driver may output them 1 at a time until the buffer is exhausted. The > problem with this alone is that the task feeding us may not be active at the > exact instant we (the driver) need more values. For this reason, I > suggest double buffering. Thus, while 1 buffer is being output, the task > feeding us has the length of the 1st buffer to write a second. When the > second buffer is being output, the task has that long to write the 1st > buffer. To prevent the task from overflowing our buffers (writing > a buffer we are not done with), the writes can be forced to a syncronous > mode (thus if the task trys to write a 3rd buffer [overwrite the 1st], the > request will not return [will hang the task] until space is available). This > also means we need a new (or modified status command, to see if a previous > write completed). It would be preferable to queue the packets being written up. That way, the task can be writing packets until the buffer fills (make it, like, 2K long). This would give you all the advantages of your method, but a lot more versatility. > schedule flow: (driven by user requests) > wiat for a write request to come in. room in the buffer? no - go to sleep until there is. yes - put the request in the buffer. Signal something to send. > repeat > > output flow: (driven by data output rate) wait for transmitter ready. anything to send? no - go to sleep until there is. yes - get next packet. if the packet has a non-zero timestamp wait until packet timestamp >= wallclock time. output the packet. signal room in the buffer. repeat > > loop: > NEW command, tells driver to copy any incoming byte directly to the > output (since the task can't do this in real time, any many MIDI > programs have this feature, we might as well provide it). It could do this for any device numbers that don't match open units in the midi.device as well. > Questions/problems to solve. > What form should the timer values take [... is 32 bits too big?] Not if you timestamp MIDI packets instead of bytes. You really only care about the time the first byte got in anyway. -- -- Peter da Silva `-_-' ...!hoptoad!academ!uhnix1!sugar!peter -- 'U` ^^^^^^^^^^^^^^ Not seismo!soma (blush)