MIDI Audio:

Using, learning, programming and modding the Gigatron and anything related.
Forum rules
Be nice. No drama.
at67
Site Admin
Posts: 647
Joined: 14 May 2018, 08:29

MIDI Audio:

Post by at67 »

Here's a link to functioning MIDI, I'm yet to test it on hardware and haven't uploaded the gt1 file as of yet. I'm in the process of shoehorning the MIDI data into the roughly 10K of RAM I have left in the lower 32K page, (in this video all the MIDI data is located at 0x8000 and above). When I am finally finished with this sample I'll probably release a 32K and 64K version.

In the video you can see me messing around with the memory monitor switching between the audio channels, you can see keyL and keyH changing in response to the notes.

https://youtu.be/I9A3fj4FB7M

Initially I struggled with getting this to work, all I needed was a note on, note off and semi-accurate millisecond timer. Once I realised you can turn off individual channels by setting keyL and keyH to 0, the only difficult part left was the timing; it's achieved by constantly calling a playMidi subroutine, (as long as you call it every few milliseconds at least, the timing is accurate enough that you really can't hear any discrepancies, (although the longer songs can finish 1-2 seconds early or late).

Getting the MIDI data into a simple note on/off/delay structure was accomplished with this:
https://github.com/LenShustek/miditones
Last edited by at67 on 25 May 2018, 23:15, edited 2 times in total.
User avatar
marcelk
Posts: 488
Joined: 13 May 2018, 08:26

Re: Midi Audio:

Post by marcelk »

We knew we were not going to get anywhere near the MOS 6581 of course, but this is GREAT beeping!

Indeed, setting the key to 0 Hz acts as OFF for the channel. With that, the soundTimer control variable is merely a convenience for simpler programs. It is a minor inconvenience if you don't want it to reach zero. Well, it will shut off the sound of a runaway program, so it is also useful as a dead man switch.
Last edited by marcelk on 21 May 2018, 17:09, edited 1 time in total.
Cwiiis
Posts: 27
Joined: 14 May 2018, 09:04

Re: Midi Audio:

Post by Cwiiis »

So cool! I was about ready to give up on music, but maybe I'll give it a shot having heard that... Like the screen-shake line clear effect too, nice idea :)
Cwiiis
Posts: 27
Joined: 14 May 2018, 09:04

Re: Midi Audio:

Post by Cwiiis »

Trying to understand the sound capability, but I don't really understand the interaction between wavA and wavX, or what oscL and oscH do - looking at what the reset function does, keyH in both oscL and oscH when setting up the channel... What does that do? I notice that Snake doesn't touch oscL/oscH, is it just that they ought to be different for each channel?
at67
Site Admin
Posts: 647
Joined: 14 May 2018, 08:29

Re: Midi Audio:

Post by at67 »

Cwiiis wrote: 21 May 2018, 22:49 Trying to understand the sound capability, but I don't really understand the interaction between wavA and wavX, or what oscL and oscH do - looking at what the reset function does, keyH in both oscL and oscH when setting up the channel... What does that do? I notice that Snake doesn't touch oscL/oscH, is it just that they ought to be different for each channel?
The following is identical across the 4 channels:

- wavA is waveform/harmonics modification, (meant for future expansion as I understand it), I leave it at 0x00 for now.

- wavX selects one of the pre-built signed 64 byte waveforms at 0x0700, noise, square/pulse, triangle or ramp: I usually use ramp, 0x03, as it seems to hide the noise the most. You can also generate your own waveforms and replace these standard 4, but they will be reset on reboot.

- keyL and keyH are the 16 bit value that specifies the note, you can fetch this note from the ROM using the standard midi note, (where 69 = 440Hz), with (midi_note - 10)*2 - 2 + 0x0900, (this is in ROM). Setting these two to 0x00 disables the channel.
http://www.indiana.edu/~emusic/etext/ap ... temp.shtml
The Gigatron can do midi notes from 12 to 106, 16.4Hz to 3729.3Hz

- oscL and oscH are internal counters, you don't do anything with these.

- [0x2c] is a global enable/disable for all 4 channels that counts down from the value you stuff into it.

So in summary:
1: reset all channels, (put 0x00 into keyL/keyH for all four channels).
2: read notes from ROM based on an index, (probably midi generated).
3: stuff appropriate notes into appropriate channels
4: put a value into [0x2c], (this globally enables the sound), keep regularly stuffing it with a value, as once it clocks down, sound is disabled.
5: delay and repeat from 2: onward.

P.S. I worked most of this out by experimenting in the real time hex monitor of my emulator, stuffing values into the appropriate locations and observing the results.
User avatar
marcelk
Posts: 488
Joined: 13 May 2018, 08:26

Re: Midi Audio:

Post by marcelk »

keyL:keyH -- 15-bit frequency (bit7 of keyL should be 0). There is a lookup table on page 7 in ROM
oscL:oscH -- 15-bit phase, automatically incremented with keyL:keyH every 4 scan lines (1 channel per scan line)
wavX -- Modulation of index with XOR operation
wavA -- Modulation after table lookup with ADD operation and optional clipping (can approximate PWM with this)

The sound synthesises runs during every horizontal pulse of the VGA signal. It is implemented twice, once for visible lines and once for vertical blank. The sample update runs in the videoC lines, or their equivalents in vBlank. A video frame has 521 lines, so there is no fixed mapping from video line to channel.

Code: Select all

For every channel:
  "osc" += "key"                // Advance phase
  byte i = oscH & 0xfc          // Keep only the highest 6 bits
       i ^= wavX                // Low 2 bits select wave form. High 6 bits give effects.
       i = soundTable[i] + wavA // More sound effect options
       i = i&128 ? 63 : i&63
  sample += i

Every 4 scan lines:
  1. Emit sample & 0xf0         // The DAC has 4-bits
  2. Reset sample
       sample = 3               // Why 3? Any value ≤3 (=255-4*63) will work, and ...
       st $03,[$03]             ;; ... we have only 1 cycle available for this, so address doubles as initialiser
ROM table at $0900 (notesTable):

The ROM has a handy lookup table for the frequencies that is calculated for a 6.25 MHz system doing 200 cycles per horizontal sync.

Note: index 0 = 0 Hz = OFF

Code: Select all

notesTable:   0900 0000  ld   $00
              0901 0000  ld   $00
              0902 0045  ld   $45         ;C-0 (16.4 Hz)
              0903 0000  ld   $00
              0904 0049  ld   $49         ;C#0 (17.3 Hz)
              0905 0000  ld   $00
[...]
              09be 001a  ld   $1a         ;A#7 (3729.3 Hz)
              09bf 007a  ld   $7a
RAM table at $0700 (soundTable):

The wave forms are stored in RAM page 7. They are initialised once at boot time and then never again (so not with soft reset). [Edit: from ROM v4 onwards, these are reinitialised with each soft reset!] You select one with the low two bits of wavX. They look like below. The page is initialised as noise (0), triangle (1), pulse (2) and sawtooth (3). You can change them, but then the next application will see your changes. You shouldn't change the sawtooth waveform (3) because it doubles as a right-shift-2 lookup table for the SYS_Unpack_56 function used by Pictures.

Noise is not really noise because it repeats. It sounds a bit metallic. But I expect that it can be made better by changing wavX frequently. There are 3 bytes of entropy available in the zero page that are updated during vertical blank. You might play with that.

Note on the image: The horizontal axis is reshuffled for better visualisation!
Attachments
waves.png
waves.png (5.03 KiB) Viewed 6987 times
Last edited by marcelk on 22 May 2018, 22:06, edited 1 time in total.
at67
Site Admin
Posts: 647
Joined: 14 May 2018, 08:29

Re: Midi Audio:

Post by at67 »

marcelk wrote: 22 May 2018, 10:24 keyL:keyH -- 15-bit frequency (bit7 of keyL should be 0). There is a lookup table on page 7 in ROM
oscL:oscH -- 15-bit phase, automatically incremented with keyL:keyH every 4 scan lines (1 channel per scan line)
wavX -- Modulation of index with XOR operation
wavA -- Modulation after table lookup with ADD operation and optional clipping (can approximate PWM with this)
...
...
Excellent info, cheers!

As an update I've relaxed the timing requirements for the MIDI player to 16.66666667ms, of course this leads to timing errors when the delays present within the MIDI score are not exact multiples of 16.66666667ms; curiously enough not enough that you can notice, (I am sure an aficionado would notice and some of these great composers would be rolling in their graves), but you get some serious benefits.

- Much easier timing requirements within an already vCPU cycle starved architecture.
- You only need to call the MIDI player once per video frame, (previously you had to call it every 2-3ms, ideally every 1ms).
- Input MIDI score format has had it's delay values reduced from 2 bytes to 1 byte, this causes a maximum delay of around 2116ms, to be achievable, (this will be a problem for scores that have larger delays than this.
- I've written a utility that takes the output of miditones and produces a new gigamidi format with delays occupying only one byte, (it warns you when a delay exceeds 2116ms and I will put in some timing error calculations/logging in the near future.

Future considerations:
For a future ROM a 16 bit value that incremented precisely every one ms, (should be easy to implement in a bit banger, especially one of this caliber), would be a sensational addition that any vCPU/SYS code could access for accurate timing requirements and quasi interrupt code. Obviously you have to find the space and spot within the video synchronisation path within the ROM to achieve this and you would need to cordon off two more bytes of RAM from the user for the timer itself, but the pain may very well be worth it: just an idea to consider.
Last edited by at67 on 23 May 2018, 11:56, edited 2 times in total.
at67
Site Admin
Posts: 647
Joined: 14 May 2018, 08:29

Re: MIDI Audio:

Post by at67 »

I've updated the new MIDI converter to support multiple output formats; I don't code much in GCL or Python, so if there are any errors or the syntax looks off, let me know.

Also, if there are any other output formats anyone wants supported, it will take minutes to add them.

Code: Select all

Usage:
- gigamidi <input filename> <output filename> <format 0, 1, 2, 3> <address> <line length>
- Input, miditones binary file produced with miditones, e.g. miditones -t4 -b <filename>.bin
- Format, 0 = vCPU ASM, 1 = GCL, 2 = C/C++, 3 = Python

Example:
gigamidi game_over.bin game_over.i 0 0x8000 100

Output:
- vCPU ASM
game_overMidi       EQU     0x8000
game_overMidi       DB      0x90 0x53 0x91 0x47 0x07 0x90 0x52 0x91 0x46 0x07 0x90 0x53 0x91 0x47
                    DB      0x07 0x90 0x52 0x91 0x46 0x07 0x90 0x53 0x91 0x47 0x07 0x90 0x54
                    DB      0x91 0x48 0x07 0x90 0x53 0x91 0x47 0x07 0x90 0x52 0x91 0x46 0x07
                    DB      0x90 0x53 0x91 0x47 0x1d 0x80 0x81 0xf0

- GCL
$8000:
[def
  $90# $53# $91# $47# $07# $90# $52# $91# $46# $07# $90# $53# $91# $47# $07# $90# $52# $91# $46#
  $07# $90# $53# $91# $47# $07# $90# $54# $91# $48# $07# $90# $53# $91# $47# $07# $90# $52#
  $91# $46# $07# $90# $53# $91# $47# $1d# $80# $81# $f0#
] game_overMidi=

- C++
uint8_t game_overMidi[] =
{
    0x90,0x53,0x91,0x47,0x07,0x90,0x52,0x91,0x46,0x07,0x90,0x53,0x91,0x47,0x07,0x90,0x52,0x91,0x46,
    0x07,0x90,0x53,0x91,0x47,0x07,0x90,0x54,0x91,0x48,0x07,0x90,0x53,0x91,0x47,0x07,0x90,0x52,
    0x91,0x46,0x07,0x90,0x53,0x91,0x47,0x1d,0x80,0x81,0xf0,
};

- Python
game_overMidi = bytearray([
    0x90,0x53,0x91,0x47,0x07,0x90,0x52,0x91,0x46,0x07,0x90,0x53,0x91,0x47,0x07,0x90,0x52,0x91,0x46,
    0x07,0x90,0x53,0x91,0x47,0x07,0x90,0x54,0x91,0x48,0x07,0x90,0x53,0x91,0x47,0x07,0x90,0x52,
    0x91,0x46,0x07,0x90,0x53,0x91,0x47,0x1d,0x80,0x81,0xf0,
])
Cwiiis
Posts: 27
Joined: 14 May 2018, 09:04

Re: MIDI Audio:

Post by Cwiiis »

Brilliant, I was going to have a shot at reproducing your work, but instead I guess I'll just use your tools :) Nice work, I'll let you know if I find anything that could use some tweaking/have any issues.
at67
Site Admin
Posts: 647
Joined: 14 May 2018, 08:29

Re: MIDI Audio:

Post by at67 »

I've updated the Gigamidi tool and the Gigatron's MIDI specification, here's a link to a .gt1 file that uses the new specification; it contains three separate MIDI tracks and a game-over stream, it also runs on Phil's emulator so it should work fine on 32K hardware.

Tetris MIDI sample:
https://www.dropbox.com/s/vrz3p65124she ... s.gt1?dl=0

Gigatron MIDI documentation:
https://github.com/at67/gigatron-rom/tr ... /at67/midi

Unlisted video, I don't want copyright strikes.
https://youtu.be/I9A3fj4FB7M
Last edited by at67 on 07 Jun 2018, 00:14, edited 3 times in total.
Post Reply