SEGA Megadrive/Genesis YM2612 + SN76489 synth

Status
Not open for further replies.

nagual

Well-known member
ym2612_chip.jpgarduino ym2612 pastryhat.JPG

Goal 1: adapt (port) the following test code that is already written for the YM2612: https://github.com/skywodd/avr_vgm_player/tree/master/ym2612_test

Goal 2: everything else

List of useful links collected from this thread (continually updated:)
----------------------------------------------------------


>>YM2612 Information:

Teensy ++http://gendev.spritesmind.net/forum/viewtopic.php?t=1470
same, but earlier in time: http://gendev.spritesmind.net/forum/viewtopic.php?t=1249&sid=b92fecf9819f5b462de9dfb014590114

detailed description of the YM2612 chip: http://www.smspower.org/maxim/Documents/YM2612

ym2612 with Teensy example #1 https://youtu.be/6c8LeQxxFuo

ym2612 with Arduino example #2 https://youtu.be/e_JoRv6Pj2c

ym2612 with Arduino example #3 https://youtu.be/_OiOyxsI5n8

'MIDIbox'-type ym2612 synth: https://www.youtube.com/watch?v=jf10Ta8NnUc source code: https://mega.co.nz/#!3coCnSBC!bIcht1XchB3Zh2Il_cfpJi_8Q-cy1KXdwg_vz4NtVAM

Test code for YM2612:
Now for some code to play with. This sketch is modelled on the example in #13. It does compile for a Teensy 3.1
but I have no YM2612 to play with so I cannot tell how it will work (and there might still be bugs).
It is intended to show how the Teensy 3.1 could send data to the YM2612, that is the bus interface.
I have no idea of the inner workings of the chip, what registers should be written with what values to make music.
I took that from the example in #13.

It is also not clear if 3.3 logical input to the Yamaha chip will be seen as logical 1, but its possible.

Hopefully the structure is clear enough to see what is going on.

Code:
/* Map Teensy pins to YM2612 pins */
/* RD can be tied to 3.3 V for now */
#define YM_A0   0
#define YM_A1   1
#define YM_CS   2
#define YM_CLK  3
#define YM_WR   4
#define YM_D0   5
#define YM_D1   6
#define YM_D2   7
#define YM_D3   8
#define YM_D4   9
#define YM_D5   10
#define YM_D6   11
#define YM_D7   12
#define YM_IC   14

/* transfer one byte to data lines */
/* This code can be optimized later */
void WriteYMData(uint32_t data) {
  digitalWriteFast( YM_D0, data&0x01);
  digitalWriteFast( YM_D1, data&0x02);
  digitalWriteFast( YM_D2, data&0x04);
  digitalWriteFast( YM_D3, data&0x08);
  digitalWriteFast( YM_D4, data&0x10);
  digitalWriteFast( YM_D5, data&0x20);
  digitalWriteFast( YM_D6, data&0x40);
  digitalWriteFast( YM_D7, data&0x80);  
}

void setreg(uint32_t addr, uint32_t data) {
  digitalWriteFast( YM_A0, 0);    /* A0 = 0 for register addres write */
  delayMicroseconds(2);
  digitalWriteFast( YM_CS, 0);    /* CS low to select */
  WriteYMData(addr);
  digitalWriteFast( YM_WR, 0);
  delayMicroseconds(2);
  digitalWriteFast( YM_WR, 1);
  digitalWriteFast( YM_CS, 1);    /* CS high */
  delayMicroseconds(2);
  digitalWriteFast( YM_A0, 1);    /* A0 = 1 for register data write */
  delayMicroseconds(2);
  digitalWriteFast( YM_CS, 0);    /* CS low to select */
  WriteYMData(data);
  digitalWriteFast( YM_WR, 0);
  delayMicroseconds(2);
  digitalWriteFast( YM_WR, 1);
  digitalWriteFast( YM_CS, 1);    /* CS high */
}


//////////////////////////////////////////
//  SETUP AND LOOP
//////////////////////////////////////////


void setup() {
  pinMode(YM_A0, OUTPUT);
  pinMode(YM_A1, OUTPUT);
  pinMode(YM_CS, OUTPUT);
  pinMode(YM_WR, OUTPUT);
  pinMode(YM_IC, OUTPUT);
  pinMode(YM_D0, OUTPUT);
  pinMode(YM_D1, OUTPUT);
  pinMode(YM_D2, OUTPUT);
  pinMode(YM_D3, OUTPUT);
  pinMode(YM_D4, OUTPUT);
  pinMode(YM_D5, OUTPUT);
  pinMode(YM_D6, OUTPUT);
  pinMode(YM_D7, OUTPUT);

  analogWriteFrequency(YM_CLK, 4000000);
  analogWrite(YM_CLK, 128);

  digitalWriteFast(YM_A1, 0);
  digitalWriteFast(YM_CS, 1);
  digitalWriteFast(YM_WR, 1);
  digitalWriteFast(YM_IC, 1);

  /* Now try to setup for making sounds */

  /* Reset YM2612 */
  digitalWriteFast(YM_IC, 0);
  delay(10);
  digitalWriteFast(YM_IC, 1);
  delay(10);

  
  /* YM2612 Test code */ 
  setreg(0x22, 0x00); // LFO off
  setreg(0x27, 0x00); // Note off (channel 0)
  setreg(0x28, 0x01); // Note off (channel 1)
  setreg(0x28, 0x02); // Note off (channel 2)
  setreg(0x28, 0x04); // Note off (channel 3)
  setreg(0x28, 0x05); // Note off (channel 4)
  setreg(0x28, 0x06); // Note off (channel 5)
  setreg(0x2B, 0x00); // DAC off
  setreg(0x30, 0x71); //
  setreg(0x34, 0x0D); //
  setreg(0x38, 0x33); //
  setreg(0x3C, 0x01); // DT1/MUL
  setreg(0x40, 0x23); //
  setreg(0x44, 0x2D); //
  setreg(0x48, 0x26); //
  setreg(0x4C, 0x00); // Total level
  setreg(0x50, 0x5F); //
  setreg(0x54, 0x99); //
  setreg(0x58, 0x5F); //
  setreg(0x5C, 0x94); // RS/AR 
  setreg(0x60, 0x05); //
  setreg(0x64, 0x05); //
  setreg(0x68, 0x05); //
  setreg(0x6C, 0x07); // AM/D1R
  setreg(0x70, 0x02); //
  setreg(0x74, 0x02); //
  setreg(0x78, 0x02); //
  setreg(0x7C, 0x02); // D2R
  setreg(0x80, 0x11); //
  setreg(0x84, 0x11); //
  setreg(0x88, 0x11); //
  setreg(0x8C, 0xA6); // D1L/RR
  setreg(0x90, 0x00); //
  setreg(0x94, 0x00); //
  setreg(0x98, 0x00); //
  setreg(0x9C, 0x00); // Proprietary
  setreg(0xB0, 0x32); // Feedback/algorithm
  setreg(0xB4, 0xC0); // Both speakers on
  setreg(0x28, 0x00); // Key off
  setreg(0xA4, 0x22); // 
  setreg(0xA0, 0x69); // Set frequency
  
}





void loop() {
    delay(1000);
    setreg(0x28, 0xF0); // Key on
    delay(1000);
    setreg(0x28, 0x00); // Key off
}



>>SN76489 Synth Chip Information:

http://little-scale.blogspot.co.uk/2008/02/cool-its-midi-controlled-sega-master.html

http://little-scale.blogspot.co.uk/2013/02/how-to-build-sn76489-usb-midi-module.html



MISC
------------------------------------------------------------------
a 'similar' chip, in greater detail (for comparison) http://nemesis.hacking-cult.org/Mega...Translated.PDF

An attempt to describe (the mysterious) YM2612 test register ($21)
http://web.archive.org/web/20050126211419/http://uru.ath.cx/uru/archive/FM_datasheet/ym2612.txt

In answer to the Teensy 3.1 being underpowered for the 5v hungry YM2612, this has been suggested:
bi-directional voltage shifter (not sure if this will work) http://www.paynetech.co.uk/adafruit...e-Bi-directional-Logic-Level-Converter-BSS138

on this page, it is stated that a Yamaha OPL type chip can take 3.3v, except it needs 5v for the reset bit.
http://midibox.org/forums/topic/1907...21-on-stm32f4/

Just got this revised test code for the Teensy 3.1 and YM2612 from mlu:



THANKS TO:
------------------------------------------------------------------
mlu, mxxx, TasuLife, Pensive, nemesis, ma1e0, pastryhat, heli Atck, Furrtek, vext01 (updating this list shortly!)
 
Last edited:
Hello!

I bought a Teensy 3.1 because it is a popular microcontroller being used to build a synthesizer using the Yamaha YM2612 (Sega Megadrive) chip.

There are several examples of youtube videos of exactly this being done. I thought the PJRC forum would be as good a place as any to ask where to start.

example on Youtube:
https://youtu.be/6c8LeQxxFuo

don't know about this particular chip or how hugely different the driver code stuff would be but there's something called midibox FM v2 which uses the OPL3/YMF262 w/ either LPC17xx or stm32f4; so might be worth looking into that.
 
here is a link to a different "chip synth" that could perhaps help you understand better how this works:

http://www.instructables.com/id/Teensy-Synth/

https://github.com/brianmarkpeters/...lob/master/TEENSY SYNTH/TEENSY_SYNTH_1_01.ino

what's the "teensy synth" got to do with it? it's just a teensy doing PWM .. and it doesn't have anything in the way of a user interface.

if you want to build something around a yamaha FM chip, apart from driving that chip, you'd mostly have to think about a/the UI, no? ... which is why i mentioned the midibox thing FM V2: being FM, its interface is massive of course, but it gives you some sense of how quickly things can get out of hand if you wanted to directly access all the parameters. at the other extreme end of the spectrum -- if you just want to control it with midi, that's easy. and there's all sorts of options in between.

i guess people could give better advice if they knew what exactly you have in mind or what exactly your question is. is it about interfacing the YM2612?
 
Last edited:
ok nevermind the 'teensy synth' which has nothing to do with the YM2612. Instead please refer to my first post.

I don't care about the UI yet, I would be glad to 'hard code' parameters in Teensy just to be able to use the MIDI library to send test notes to it.
once the chip starts singing the way the Youtube user in the original post does, then I will build a massive controller with a knob for each parameter!

The YM2612 has a built in DAC, so you just plug it directly to preamp/speaker. I need to know what sort of data the registers and buffers in the YM2612 want. Supposedly '8 bit wide' or something, not that I know what that means in the context of Integrated Circuits.

my goal for the time being is to find out what kind of data type the teensy needs to send the YM2612 FM synthesizer chip so the chip is ready to receive midi.

here is a bunch of information about the YM2612 IC:
http://web.archive.org/web/20090809053129/http://www.smspower.org/maxim/docs/ym2612/
mirror:
http://www.smspower.org/maxim/Documents/YM2612

more information from http://web.archive.org/web/20050126211419/http://uru.ath.cx/uru/archive/FM_datasheet/ym2612.txt
Code:
Here's the format of the YM2612 test register ($21), as best as I can
tell:

bit 7 - If bit 6 set, select 0=LSB, 1=MSB of serial data
bit 6 - If set, reading status returns serial data LSB or MSB instead of
status flags
bit 5 - If set, only the attack phase and possibly decay occur, there is
no sustain o release. It sounds like each channel was keyed-on rapidly.
When the bit is cleared, the channels resume their original position
within the envelope so this bit doesn't have any lasting change on
them.       
bit 4 - Data sent to DAC is inverted - this is *really* loud, so turn
down the volume before using this bit. ;)
bit 3 - Audio output is muted, (the PSG still works of course) bits 4,5
will make sound faintly audible when set in conjunction.
bit 2 - Timer A and B run 24 times faster. 
bits 1,0 - No effect.

Bits 7, 6 allow you to read the serial data sent from the YM2612 to the
DAC. (the other chip, YM3012 - not the DAC at $2A/$2B) I'm unsure of the
exact format, it's probably similar to the YM2151. Bit 4 inverts the
data sent to the DAC, but not the data read from the status register.
Compared to the YM2151 test register documented in MAME, the last 3 bits
may have something to do with the LFO. And I'm also not entirely sure
about the MSB/LSB order for bit 6. Other than that the two work in a
mostly identical way.
 
Last edited:
well, as mentioned, there's probably going to be two parts to this software.

A) the driver code --- the part where you talk to the chip.

and

B) the UI code -- the part where you control the talking to the chip.

as to A), this mostly seems to be solved; at least google turns up stuff like https://github.com/skywodd/avr_vgm_player/tree/master/ym2612_test, which is for AVR of course but should be fairly straight forward to adapt. assuming this works, this mostly shows you how to set the registers and what those registers are.

B) is mostly up to you. if you want midi, best to start here https://www.pjrc.com/teensy/td_midi.html or here https://www.pjrc.com/teensy/td_libs_MIDI.html i suppose, depending what kind of midi you want.

then you'd have to piece things together; for example, in terms of the ym2612_test thing, replace the simple note on/note off loop* with something controlled by midi.

* ie this loop:
Code:
for(;;) {
		_delay_ms(1000);
		setreg(0x28, 0xF0); // Key on
		_delay_ms(1000);
		setreg(0x28, 0x00); // Key off
	}


you'd still have to think up some way to control things. i'm guessing tablet > usb_midi > teensy > YM2612 ?

as for the hardware, looks like the YM2612 needs 12 datalines, i couldn't find a datasheet, so don't know if this works with 3v3. fwiw, OPL3 does work with 3v3 logic (except #RS).
 
Last edited:
I need to know what sort of data the registers and buffers in the YM2612 want. Supposedly '8 bit wide' or something, not that I know what that means in the context of Integrated Circuits.

my goal for the time being is to find out what kind of data type the teensy needs to send the YM2612 FM synthesizer chip so the chip is ready to receive midi.

ok, just seen you've added more stuff --

"8 bit wide" in this context refers to YM2612 pins D0-D7, that's where the data is clocked in. when you look at the ym2612_test.c code linked above, there's a function called static void write_ym(uint8_t data), where "uint8_t data" would correspond to those 8 bits; the function sets the entire port (all 8 pins) at once (YM_DATA_PORT = data), that code would look different for teensy 3.1, which is a different kind of mcu. one thing to figure out.

you'll also note there's more signals involved (/CS, /RD, /WR, /IC, A0, A1); as well as "øM", which is the clock signal. you'll also need to generate this clock signal. wikipedia says "7.67 MHz recommended"; the example code, if i see it right, uses 8MHz (that's the stuff at lines #83-88). another thing to figure out.

finally, keep in mind teensy 3.1 operates on 3v3; there's no guarantee such an ancient device will cooperate. a/the datasheet should tell you more. probably it would be easier for you to use a teensy 2++, which works on 5v.
 
"8 bit wide" in this context refers to YM2612 pins D0-D7, that's where the data is clocked in. when you look at the ym2612_test.c code linked above, there's a function called static void write_ym(uint8_t data), where "uint8_t data" would correspond to those 8 bits; the function sets the entire port (all 8 pins) at once (YM_DATA_PORT = data), that code would look different for teensy 3.1, which is a different kind of mcu. one thing to figure out.

Excellent. How are the value sent? does 8 bit refer to 8 flipflop switches or something that cumulates in a hex value?

you'll also note there's more signals involved (/CS, /RD, /WR, /IC, A0, A1); as well as "øM", which is the clock signal. you'll also need to generate this clock signal. wikipedia says "7.67 MHz recommended"; the example code, if i see it right, uses 8MHz (that's the stuff at lines #83-88). another thing to figure out.

I have a 7.68MHz crystal, will look into where to plug it in.

finally, keep in mind teensy 3.1 operates on 3v3; there's no guarantee such an ancient device will cooperate. a/the datasheet should tell you more. probably it would be easier for you to use a teensy 2++, which works on 5v.

It seems like I am getting different voltage values on my Teensy 3.1, think my meter is bad.
 
Excellent. How are the value sent? does 8 bit refer to 8 flipflop switches or something that cumulates in a hex value?

8 bit refers to 8 bit as in 8 x zero or one. ie it'll just be some number in between 0 and 255, hence uint8_t (or char or byte), that's the data type.

they're clocked out in parallel in this case, ie the state of those pins represents your 8 bit number. so you could either use 8 individual pins to connect to D0-D7, or, as in the one video you've linked, use what is called a shift register (serial in / parallel out), which will save some pins and probably won't be a bad idea re voltage levels.

I have a 7.68MHz crystal, will look into where to plug it in.

pin 24

It seems like I am getting different voltage values on my Teensy 3.1, think my meter is bad.

if it's working, it can't be seriously off? anyways, i was mentioning it because chances are the YM2612 thing needs VCC=5V, and all those videos above use 5V MCUs, so there's no guarantee it'll work with more contemporary, ie 3v3 voltage levels. you'll probably need a bunch of level shifters.
 
Last edited:
presumably the datatype is digital not analog right?

'im getting 5 volt readings on my 3.3v pins
 
Last edited:
presumably the datatype is digital not analog right?

well, it's nothing complicated anyways -- that chip has a parallel interface, ie the 8 data pins called D0-D8. either one of them can be either high or low, corresponding to 256 possible states or 8 bits. so in this sense, digital. but that's physical, hardware. there's 6 more signals, which determine for instance to which register you write, etc.

"datatype" is a more abstract concept -- https://en.wikipedia.org/wiki/Data_type


best to take a look at the write sequence from the test example (assuming this is about working): static void setreg(uint8_t reg, uint8_t data)
 
I have started to modify the test code, but it doesn't work yet. I'm using pins 0-7 for the D0-D7 registers and I'm using pins 13-18 on the Teensy for the /IC /IRQ /CD /WR /RD A0 and A1
There's something in Arduino called 'Port D' and I'm not sure what that is in Teensy 3.1

Code:
/**
 * YM2612 test code for AVR.
 * MODIFIED for TEENSYDUINO July.8.2015
 * This program is a simple test code for the YM2612 FM sound chip using an AVR ATmega328p mcu.
 * This program configure the YM2612 to sound like a "grand piano" and play note on / note off in loop.
 * For more informations about wiring please see: http://en.wikipedia.org/wiki/Yamaha_YM2612
 * For more informations about YM2612 registers please see: http://www.smspower.org/maxim/Documents/YM2612
 *
 * @remarks This test code is heavly based on Furrtek's YM2612 test code. Big thanks Furrtek for the help !
 * @warning This test code is made to run on an ATmega328/ATmega168 mcu with a 16MHz external crystal.
 * 
 * @author Fabien Batteix <skywodd@gmail.com>
 * @link http://skyduino.wordpress.com My Blog about electronics
 */

/* Dependencies */
#include <avr/io.h>     // For I/O and other AVR registers
#include <util/delay.h> // For timing

/* Pinmap (Arduino UNO compatible) */
#define YM_IC (13) // PC5 (= pin 13 for Teensy 3.1)
#define YM_CS (14) // PC4 (= pin 14 for Teensy 3.1)
#define YM_WR (15) // PC3 (= pin 15 for Teensy 3.1)
#define YM_RD (16) // PC2 (= pin 16 for Teensy 3.1)
#define YM_A0 (17) // PC1 (= pin 17 for Teensy 3.1)
#define YM_A1 (18) // PC0 (= pin 18 for Teensy 3.1)
#define YM_CTRL_DDR DDRC
#define YM_CTRL_PORT PORTC
#define YM_DATA_DDR DDRD
#define YM_DATA_PORT PORTD // Whole PORT D for data bus (= pins D0 to D7 for Arduino UNO)
//#define YM_MCLOCK (1) // PB1 = OC1A (= pin D9 for Arduino UNO)
//#define YM_MCLOCK_DDR DDRB

/* ----- BIG WARNING FOR ARDUINO USERS -----
 * Normally ICSP port is used to program the AVR chip (see the makefile for details). 
 * So, if you use an arduino UNO board to run this test code BE VERY CAREFULL.
 * If you don't known what you make you will be in trouble.
 *
 * To avoid problems you can compile and upload this code using the Arduino IDE BUT you will need to disconnect pins Rx/Tx before upload !
 */

/**
 * Send raw data to the YM2612
 * 
 * @author Furrtek
 * @param data Data to write
 */
static void write_ym(uint8_t data) {
  YM_CTRL_PORT &= ~_BV(YM_CS); // CS LOW
  YM_DATA_PORT = data;
  _delay_us(1);
  YM_CTRL_PORT &= ~_BV(YM_WR); // Write data
  _delay_us(5);
  YM_CTRL_PORT |= _BV(YM_WR);
  _delay_us(5);
  YM_CTRL_PORT |= _BV(YM_CS); // CS HIGH
}

/**
 * Write data into a specific register of the YM2612
 *
 * @author Furrtek
 * @param reg Destination register address
 * @param data Data to write
 */
static void setreg(uint8_t reg, uint8_t data) {
  YM_CTRL_PORT &= ~_BV(YM_A0); // A0 low (select register)
  write_ym(reg);
  YM_CTRL_PORT |= _BV(YM_A0);  // A0 high (write register)
  write_ym(data);
}

/** Program entry point */
int main(void) {

  /* Pins setup */
  YM_CTRL_DDR |= _BV(YM_IC) | _BV(YM_CS) | _BV(YM_WR) | _BV(YM_RD) | _BV(YM_A0) | _BV(YM_A1);
  YM_DATA_DDR = 0xFF;
  // YM_MCLOCK_DDR |= _BV(YM_MCLOCK);
  YM_CTRL_PORT |= _BV(YM_IC) | _BV(YM_CS) | _BV(YM_WR) | _BV(YM_RD); /* IC, CS, WR and RD HIGH by default */
  YM_CTRL_PORT &= ~(_BV(YM_A0) | _BV(YM_A1)); /* A0 and A1 LOW by default */
  
  /* F_CPU / 2 clock generation */
  // commented out because im using a 7.68MHz crystal
  // TCCR1A = _BV(COM1A0);            /* Toggle OCA1 on compare match */
  // TCCR1B = _BV(WGM12) | _BV(CS10); /* CTC mode with prescaler /1 */
  // TCCR1C = 0;                      /* Flag reset */
  // TCNT1 = 0;                       /* Counter reset */
  // OCR1A = 0;                       /* Divide base clock by two */
  
  /* Reset YM2612 */
  YM_CTRL_PORT &= ~_BV(YM_IC);
  _delay_ms(10);
  YM_CTRL_PORT |= _BV(YM_IC);
  _delay_ms(10);

  /* YM2612 Test code */ 
  setreg(0x22, 0x00); // LFO off
  setreg(0x27, 0x00); // Note off (channel 0)
  setreg(0x28, 0x01); // Note off (channel 1)
  setreg(0x28, 0x02); // Note off (channel 2)
  setreg(0x28, 0x04); // Note off (channel 3)
  setreg(0x28, 0x05); // Note off (channel 4)
  setreg(0x28, 0x06); // Note off (channel 5)
  setreg(0x2B, 0x00); // DAC off
  setreg(0x30, 0x71); //
  setreg(0x34, 0x0D); //
  setreg(0x38, 0x33); //
  setreg(0x3C, 0x01); // DT1/MUL
  setreg(0x40, 0x23); //
  setreg(0x44, 0x2D); //
  setreg(0x48, 0x26); //
  setreg(0x4C, 0x00); // Total level
  setreg(0x50, 0x5F); //
  setreg(0x54, 0x99); //
  setreg(0x58, 0x5F); //
  setreg(0x5C, 0x94); // RS/AR 
  setreg(0x60, 0x05); //
  setreg(0x64, 0x05); //
  setreg(0x68, 0x05); //
  setreg(0x6C, 0x07); // AM/D1R
  setreg(0x70, 0x02); //
  setreg(0x74, 0x02); //
  setreg(0x78, 0x02); //
  setreg(0x7C, 0x02); // D2R
  setreg(0x80, 0x11); //
  setreg(0x84, 0x11); //
  setreg(0x88, 0x11); //
  setreg(0x8C, 0xA6); // D1L/RR
  setreg(0x90, 0x00); //
  setreg(0x94, 0x00); //
  setreg(0x98, 0x00); //
  setreg(0x9C, 0x00); // Proprietary
  setreg(0xB0, 0x32); // Feedback/algorithm
  setreg(0xB4, 0xC0); // Both speakers on
  setreg(0x28, 0x00); // Key off
  setreg(0xA4, 0x22); // 
  setreg(0xA0, 0x69); // Set frequency

  /* Program loop */
  for(;;) {
    _delay_ms(1000);
    setreg(0x28, 0xF0); // Key on
    _delay_ms(1000);
    setreg(0x28, 0x00); // Key off
  }
  
  /* Compiler fix */
  return 0;
}
 
Last edited:
I have started to modify the test code, but it doesn't work yet. I'm using pins 0-7 for the D0-D7 registers and I'm using pins 13-18 on the Teensy for the /IC /IRQ /CD /WR /RD A0 and A1
There's something in Arduino called 'Port D' and I'm not sure what that is in Teensy 3.1

it's not surprising it doesn't work.

the voltage level thing apart (have you found a datasheet?), this code is for AVR, not ARM (teensy 3.1 being ARM). these are two different architectures and this code is fairly low level, manipulating the registers (the stuff called DDRC, PORTC, etc).

to the extent this compiles without errors, that's because teensyduino comes with some AVR emulation code which will translate those statements into stuff teensy can understand. that said, ARM has roughly equivalent registers, but they're named and work differently (GPIO_PDIR, etc) and do not map onto the pins as nicely. if you're interested, see here : https://forum.pjrc.com/archive/index.php/t-17532.html

for your purposes, it probably makes more sense at this point to A) ascertain that yamaha chip will work with 3v3 levels (unlikely) and B) try to understand what is happening in that code. for instance, the first few lines below /* Pins setup */ set what is called the data direction register (on AVR). a more familiar-looking way of doing this (and one that works with teensy) might be:

Code:
 /* Pins setup */
pinMode(YM_IC, OUTPUT);
pinMode(YM_CS, OUTPUT);
pinMode(YM_WR, OUTPUT);
//... etc

the subsequent lines set the data registers (PORTC, PORTD -- on AVR); you'd achieve the same by doing:

Code:
/* IC, CS, WR and RD HIGH by default */
digitalWrite(YM_IC, HIGH);
digitalWrite(YM_CS, HIGH);
digitalWrite(YM_WR, HIGH);
// ... etc

and so on.
 
Last edited:
the voltage on my Teensy 3.1 is running at 4.94 volts. plugged in via usb.
(and its not stable. starts out at 3v and gradually works its way up to 5v while the voltmeter is attached.)

teensy voltage.jpg
 
Last edited:
the voltage on my Teensy 3.1 is running at 4.94 volts. plugged in via usb.
(and its not stable. starts out at 3v and gradually works its way up to 5v while the voltmeter is attached.)

oh, surely that's the Vin pin only? you're measuring the usb voltage.

there's a 3.3v voltage regulator onboard, which supplies the power to the processor.

try this and measure pin 13:

Code:
const uint16_t measure_this_pin = 13; // or try 0,1,2,3 etc

void setup() 
{                
  pinMode(measure_this_pin, OUTPUT);  
  digitalWrite(measure_this_pin, HIGH);     
}


void loop() 
{   
  while(1)
  {
    }         
}
 
thank you, but no, it's creeping up to 5v even there. slowly, from 3v towards 5v
the '3.3v' pin reads 5.2v now, and pin 13 reads an even 5.
I have removed all wires, and now only the teensy is plugged into the breadboard.
i have now tested both 3.3v pins, and both say 5v +
 
Last edited:
How does one write 8 'bits' (Registers D0 to D7) at once? is that a sort of array 'buffer' command? should it be a pulse, quick on and quick off or do these 8 bit patterns just set and hold the 3.3v state on the IC?

edit

are IC's normally sitting and waiting for certain 'bit values' to arrive on the electrical contacts? must these voltages stay, or are they serialized so that only one can arrive at any given time? are these 'combinations' (bit value patterns) being fed to the YM2612 via some sort of buffer?

any insight on the general principle would be great. i can write a program from the ground up if i know that.

This program looks really useful in general for midi interfacing. http://www.ucapps.de/mios_studio.html
 
Last edited:
thank you, but no, it's creeping up to 5v even there. slowly, from 3v towards 5v
the '3.3v' pin reads 5.2v now, and pin 13 reads an even 5.
I have removed all wires, and now only the teensy is plugged into the breadboard.
i have now tested both 3.3v pins, and both say 5v +

ok, but that means either you're doing something wrong, or your multimeter is broken, or your teensy is damaged. the 3.3v pins are called 3.3v because they're supposed to put out 3.3v, of course.

you connect the one probe (black) to ground, the other one (red) to the pin you want to measure, right?


How does one write 8 'bits' (Registers D0 to D7) at once?

take a look at the thread i've linked above, it's all explained there. but you really should figure out a few more basic things first. it's no use getting bogged down with this as long as you haven't confirmed your teensy is working properly and that it could work in principle.

to repeat: teensy, if working as it is supposed to, will put out 3.3v logic; there's a big if some Yamaha chip from the 1980s will like that.

it looks as if there isn't a whole lot of technical information available re: YM2612, which doesn't help it. apparently, YM2612 "stripped down version" of the YM2608. google turns up some technical documents for this one, albeit in Japanese.

Screen Shot 2015-07-10 at 07.35.31.jpg



i can't read this, but it doesn't look like it's giving away information on logic levels/thresholds.

if you just want to talk midi to the thing, is there any reason why you don't use a teensy 2 ? doing that would eliminate a few unknown unknowns. trying with a better documented chip (such as OPL3) would make things easier, too.
 
ok, but that means either you're doing something wrong, or your multimeter is broken, or your teensy is damaged. the 3.3v pins are called 3.3v because they're supposed to put out 3.3v, of course.
you connect the one probe (black) to ground, the other one (red) to the pin you want to measure, right?

My voltmeter seems screwed. I bought a cheap one without auto poweroff and the battery is low, and i think that's what is giving me these wrong readings.

take a look at the thread i've linked above, it's all explained there. but you really should figure out a few more basic things first. it's no use getting bogged down with this as long as you haven't confirmed your teensy is working properly and that it could work in principle.

You're right, that thread made no sense to me. Does it mean that sending 8 signals simultaneously is not possible with a Teensy? If so, then it's actually starting to look like I need a plain old Arduino.

if you just want to talk midi to the thing, is there any reason why you don't use a teensy 2 ? doing that would eliminate a few unknown unknowns. trying with a better documented chip (such as OPL3) would make things easier, too.

I am completely new to Teensy, so there is no way I could have known to get the old one until now. There is a teensy 2.0 and a teensy ++ 2.0 does it matter which one?

other people have managed to get the ym2612 to work with Teensy and Arduino, and the Motorola PIC processor, so that's at least 3 other people who accidentally got it right, I can too! I will do an OPL3 synth too, but right now i have some YM2612 chips.
 
apparently, YM2612 "stripped down version" of the YM2608. google turns up some technical documents for this one, albeit in Japanese.

View attachment 4656

i can't read this, but it doesn't look like it's giving away information on logic levels/thresholds.

thanks!

here's a translated version of the tech documents for the ym2608
http://nemesis.hacking-cult.org/MegaDrive/Documentation/YM2608J Translated.PDF

also, the controller IC the Sega Megadrive used was the Motorola 68000
isn't it possible to know how much voltage that this microcontroller sends out on its 8 bit databus?
http://cache.freescale.com/files/32bit/doc/ref_manual/MC68000UM.pdf

also, according to this link, YM2612 can run on 3.3v to 5v (not that i particularly trust the link)
http://www.aliexpress.com/item/YM2612/32218868933.html

on this page, it is stated that a Yamaha OPL type chip can take 3.3v, except it needs 5v for the reset bit.
http://midibox.org/forums/topic/19073-midibox-fm-v21-on-stm32f4/
 
Last edited:
You're right, that thread made no sense to me. Does it mean that sending 8 signals simultaneously is not possible with a Teensy? If so, then it's actually starting to look like I need a plain old Arduino.

no, it doesn't say that. it is possible, but the code looks a bit different.

I am completely new to Teensy, so there is no way I could have known to get the old one until now. There is a teensy 2.0 and a teensy ++ 2.0 does it matter which one?

other people have managed to get the ym2612 to work with Teensy and Arduino, and the Motorola PIC processor, so that's at least 3 other people who accidentally got it right, I can too! I will do an OPL3 synth too, but right now i have some YM2612 chips.

i didn't say it wouldn't work, all i meant to say is: all those examples seem to use an AVR, thus 5v. what this means is that even if you get the code right, it might not work with teensy 3.x, because it puts out 3v3 logic. that's purely a hardware thing. many 5v things do work with 3v3 logic, but not all of them do. so there's no guarantee. the available datasheets don't seem to tell. a level shifter or teensy 2.x will rule out that kind of uncertainty.

you could just experiment, but it makes life easier to know if things could work in principle/theory. re teensy 2, no i wouldn't matter, unless you plan to use a lot of pins for other purposes than driving the FM chip.

on this page, it is stated that a Yamaha OPL type chip can take 3.3v, except it needs 5v for the reset bit.
http://midibox.org/forums/topic/19073-midibox-fm-v21-on-stm32f4/

that's what i said in post #6 above
 
does the link that you supplied earlier say how i should go about creating an 8 bit data bus? would i do that by creating a function?

Also, i hear you about the 3 versus 5 volt issue. but there's not much i can do about that now. still quite a bit of work to do on the test code before i can even test it anyway.
 
Last edited:
This has been done already! http://gendev.spritesmind.net/forum/...pic.php?t=1470

I'm not sure if the below code is complete, but looks good.

https://youtu.be/sqC9TVTOV_M

Code:
/* ym2612 sound driver
 rnx

Dual YM2612 hardware synthesizer Copyright 2013

For Teensy ++

code by 
grybranix@netscape.net

From forum topic:
http://gendev.spritesmind.net/forum/viewtopic.php?t=1470&postdays=0&postorder=asc&start=0
 */

#include <Encoder.h>
#include <MIDI.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <LedControl.h>
#include <SPI.h>

#define disk1 0x51 //address of 32k eeprom chip
#define disk2 0x50 //address of 32k eeprom chip
#define INST_STRIDE 72 //size of instrument data in bytes

#define CH1 0 //magic constant for channel 1
#define CH2 1 //magic constant for channel 2
#define CH3 2 //magic constant for channel 3
#define CH4 4 //magic constant for channel 4
#define CH5 5 //magic constant for channel 5
#define CH6 6 //magic constant for channel 6
#define CH7 7 //magic constant for channel 7
#define CH8 8 //magic constant for channel 8
#define CH9 9 //magic constant for channel 9
#define CH10 11 //magic constant for channel 10
#define CH11 12 //magic constant for channel 11
#define CH12 13 //magic constant for channel 12
#define BANK1 0
#define BANK2 1
#define MODE_MONO 0
#define MODE_POLY 1

typedef struct
{
  byte dt1;
  byte mul;
  byte tl;
  byte rs;
  byte ar;
  byte ams;
  byte d1r;
  byte d2r;
  byte d1l;
  byte rr;
  byte lfo;
} 
Operator;

typedef struct
{
  Operator op[4];
  byte feedback; //AKA FL in OPM
  byte algorithm; //AKA CON in OPM
}
Voice;

Voice voice[14];
const unsigned char CHANNEL[13] = {
  0, CH1, CH2, CH3, CH4, CH5, CH6, CH7, CH8, CH9, CH10, CH11, CH12};

//typedef enum {false, true} bool;

const int LCDPin = 25; //LCD communication
const int backlight = 0; //LCD backlight
const int BUSPIN = 10; //8 data pins starting here
const int IRQPIN = 18; //timer interrupt pin

byte chctl = 0;

SoftwareSerial LCD = SoftwareSerial(0, LCDPin);

Encoder knob1(19, 20);
long knob1pos = 0;

/* Chip select. Active-low. When high, this makes the data pins
 high-impedance, removing it from the bus and freeing the pins. */
const int CS2 = 45; //chip select ym2612 #1
const int CS = 40; //chip select ym2612 #2

const int AA1 = 41; //A1 bit, active high
const int AA0 = 42; //A0 bit, active high
const int RD = 43; //read bit, active low
const int WR = 44; //write bit, active low

const int SSP = 5;

const int RESET = 6; //reset bit, active low

volatile int TIMER1 = LOW;
elapsedMicros TIMER;
unsigned long DTIMER = 0;

unsigned char timerreg = 0;

LedControl matrix = LedControl(7, 8, 9, 2);

/* Primary frequency table, in fnums */
//C4 is setpitch(3, freq[1], ...)
//FIXME: calculate proper numbers by tuning with an oscope and using sethz().
const unsigned int freq[13] = {
  617, 653, 692, 733, 777, 823, 872, 924, 979, 1037, 1099, 1164, 1232};

unsigned int KEYS[14] = {
  0, 0, 0, 0xFFFD, 0, 0, 0, 0, 0, 0, 0xFFFC, 0, 0, 0};
unsigned int KEYFREQ[14] = {
  0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned int KEYBLOCK[14] = {
  0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned int MODE = MODE_POLY;

/* Sets 8-bit bus to write mode. */
void output()
{
  /*int i=BUSPIN;
   for(i;i<(BUSPIN + 8);i++)
   {
   pinMode(i, OUTPUT);
   }*/

  DDRC = 0xFF;
}

/* Sets 8-bit bus to read mode */
void input()
{
  int i=BUSPIN;
  for(i;i<(BUSPIN + 8);i++)
  {
    pinMode(i, INPUT);
  }
}

/* Sets pin state on the 8-bit data bus. */
void writebus(unsigned char state)
{
  output();
  PORTC = state;
}

/* Reads pin state of the 8-bit data bus */
unsigned int readbus()
{
  unsigned int state=0;
  int i=BUSPIN;

  input();
  for(i;i<(i+8);i++)
  {
    if( digitalRead(i) == 1)
    {
      state = state ^ 1<<i;
    }
  }
  return(state);
}

//* Selects a register "reg" in the YM2612 */
//FIXME: combine selectreg and selectreg2
void selectreg(unsigned char reg)
{
  /*digitalWrite(CS, LOW); //40
   digitalWrite(RD, HIGH); //43
   digitalWrite(WR, HIGH); //44
   digitalWrite(AA0, LOW); //42
   digitalWrite(AA1, LOW); //41*/
  PORTF = B11100000;
  delayMicroseconds(2);
  writebus(reg);
  PORTF = B10100000;
  PORTF = B11100000;
  PORTF = B11100100;
  /*digitalWrite(WR, LOW);
   digitalWrite(WR, HIGH);
   digitalWrite(CS, HIGH);*/
}

/* Selects a register "reg" in the YM2612 */
void selectreg2(unsigned char reg)
{
  /*  digitalWrite(CS, LOW);
   digitalWrite(RD, HIGH);
   digitalWrite(WR, HIGH);
   digitalWrite(AA0, LOW);
   digitalWrite(AA1, HIGH);*/
  PORTF = B11101000;
  delayMicroseconds(1);
  writebus(reg);  
  PORTF = B10101000;
  PORTF = B11101000;
  PORTF = B11101100;
  /*  digitalWrite(WR, LOW);
   digitalWrite(WR, HIGH);
   digitalWrite(CS, HIGH);*/
  //delayMicroseconds(10);
}

void selectreg3(unsigned char reg, unsigned int bank, unsigned int chip)
{
  unsigned int chipselect = 0;

  if ( chip == 1)
  {
    chipselect = CS;
  }
  else
  {
    chipselect = CS2;
  }

  digitalWrite(chipselect, LOW);
  digitalWrite(RD, HIGH);
  digitalWrite(WR, HIGH);

  if (bank == BANK1)
  {
    digitalWrite(AA0, LOW);
    digitalWrite(AA1, LOW);
  }
  else if (bank == BANK2)
  {
    digitalWrite(AA0, LOW);
    digitalWrite(AA1, HIGH);
  }

  writebus(reg);
  digitalWrite(WR, LOW);
  digitalWrite(WR, HIGH);
  digitalWrite(chipselect, HIGH);
}

/* Writes "state" to the currently selected register in bank 1. */
//FIXME: combine writereg and writereg2
void writereg(unsigned char state)
{
  /*digitalWrite(CS, LOW); //40
   digitalWrite(RD, HIGH); //43
   digitalWrite(WR, HIGH); //44
   digitalWrite(AA1, LOW); //41
   digitalWrite(AA0, HIGH);//42*/
  PORTF = B11110000;
  delayMicroseconds(2);

  writebus(state);
  PORTF = B10110000;
  PORTF = B11110000;
  PORTF = B11110100;
  /*digitalWrite(WR, LOW);
   digitalWrite(WR, HIGH);
   digitalWrite(CS, HIGH);*/
}

/* Writes "state" to the currently selected register in bank 2. */
void writereg2(unsigned char state)
{
  /*digitalWrite(CS, LOW);
   digitalWrite(RD, HIGH);
   digitalWrite(WR, HIGH);
   digitalWrite(AA1, HIGH);
   digitalWrite(AA0, HIGH);*/
  PORTF = B11111000;
  delayMicroseconds(2);

  writebus(state);
  PORTF = B10111000;
  PORTF = B11111000;
  PORTF = B11111100;

  /*digitalWrite(WR, LOW);
   digitalWrite(WR, HIGH);
   digitalWrite(CS, HIGH);*/
  //delayMicroseconds(10);
}

void writereg3(unsigned char state, unsigned int bank, unsigned int chip)
{
  unsigned int chipselect = 0;

  if ( chip == 1)
  {
    chipselect = CS;
  }
  else
  {
    chipselect = CS2;
  }

  digitalWrite(chipselect, LOW);
  digitalWrite(RD, HIGH);
  digitalWrite(WR, HIGH);

  if (bank == BANK1)
  {
    digitalWrite(AA1, LOW);
    digitalWrite(AA0, HIGH);
  }
  else if (bank == BANK2)
  {
    digitalWrite(AA1, HIGH);
    digitalWrite(AA0, HIGH);
  }

  writebus(state);
  digitalWrite(WR, LOW);
  digitalWrite(WR, HIGH);
  digitalWrite(chipselect, HIGH);
  //delayMicroseconds(10);
}

/* Writes "state" to the register "address" */
void reg(unsigned char address, unsigned char state, unsigned char chip)
{
  selectreg3(address, BANK1, chip);
  writereg3(state, BANK1, chip);
}
void reg2(unsigned char address, unsigned char state)
{
  selectreg3(address, BANK2, 1);
  writereg3(state, BANK2, 1);
}
void reg3(unsigned int address, unsigned int state, unsigned char channel)
{
  unsigned char bank;
  unsigned char chip;

  chip = 1;

  if (channel > CH6)
  {
    chip = 2;
  }

  if (channel <= CH3)
  {
    bank = BANK1;
  }
  else if (channel <= CH6)
  {
    bank = BANK2;
  }
  else if (channel <= CH9)
  {
    bank = BANK1;
  }
  else if (channel <= CH12)
  {
    bank = BANK2; 
  }

  selectreg3(address, bank, chip);
  writereg3(state, bank, chip);
}

/* Selects and loads an instrument from the EEPROM. */
void selectinst(unsigned int instrument, unsigned char channel)
{
  unsigned int inst = instrument * INST_STRIDE;
  unsigned char buf = 0;
  unsigned char offset;

  if (channel <= CH3)
  {
    offset = channel;
  }
  else if (channel <= CH6)
  {
    offset = channel - 4;
  }
  else if (channel <= CH9)
  {
    offset = channel - 7;
  }
  else if (channel <= CH12)
  {
    offset = channel -11;
  }

  reg3(0x28, 0x00 + channel, channel); //Mute channel before modifying it

  buf = readEEPROM(disk1, inst + 36); //DT1 (3 bits)
  voice[channel].op[0].dt1 = buf;
  buf <<= 4;
  voice[channel].op[0].mul = readEEPROM(disk1, inst + 35); //MUL (nibble)
  buf += voice[channel].op[0].mul;
  reg3(0x30 + offset, buf, channel); //Detune + Multiply Operator 1

  buf = readEEPROM(disk1, inst + 47); //DT1
  voice[channel].op[1].dt1 = buf;
  buf <<= 4;
  voice[channel].op[1].mul = readEEPROM(disk1, inst + 46); //MUL
  buf += voice[channel].op[1].mul;
  reg3(0x34 + offset, buf, channel); //Detune + Multiply Operator 2

  buf = readEEPROM(disk1, inst + 58); //DT1
  voice[channel].op[2].dt1 = buf;
  buf <<= 4;
  voice[channel].op[2].mul = readEEPROM(disk1, inst + 57); //MUL
  buf += voice[channel].op[2].mul;
  reg3(0x38 + offset, buf, channel); //Detune + Multiply Operator 3

  buf = readEEPROM(disk1, inst + 69); //DT1
  voice[channel].op[3].dt1 = buf;
  buf <<= 4;
  voice[channel].op[3].mul = readEEPROM(disk1, inst + 68); //MUL
  buf += voice[channel].op[3].mul;
  reg3(0x3C + offset, buf, channel); //Detune + Multiply Operator 4

  buf = readEEPROM(disk1, inst + 33); //TL
  voice[channel].op[0].tl = buf;
  reg3(0x40 + offset, buf, channel); //Total Level Operator 1
  buf = readEEPROM(disk1, inst + 44); //TL
  voice[channel].op[1].tl = buf;
  reg3(0x44 + offset, buf, channel); //Total Level Operator 2
  buf = readEEPROM(disk1, inst + 55); //TL
  voice[channel].op[2].tl = buf;
  reg3(0x48 + offset, buf, channel); //Total Level Operator 3
  buf = readEEPROM(disk1, inst + 66); //TL
  voice[channel].op[3].tl = buf;
  reg3(0x4C + offset, buf, channel); //Total Level Operator 4

  buf = readEEPROM(disk1, inst + 34); //RS
  voice[channel].op[0].rs = buf;
  buf <<= 6;
  voice[channel].op[0].ar = readEEPROM(disk1, inst + 28); //AR
  buf += voice[channel].op[0].ar;
  reg3(0x50 + offset, buf, channel); //Rate Scaling + Attack Rate Operator 1

  buf = readEEPROM(disk1, inst + 45); //RS
  voice[channel].op[1].rs = buf;
  buf <<= 6;
  voice[channel].op[1].ar = readEEPROM(disk1, inst + 39); //AR
  buf += voice[channel].op[1].ar;
  reg3(0x54 + offset, buf, channel); //Rate Scaling + Attack Rate Operator 2

  buf = readEEPROM(disk1, inst + 56); //RS
  voice[channel].op[2].rs = buf;
  buf <<= 6;
  voice[channel].op[2].ar = readEEPROM(disk1, inst + 50); //AR
  buf += voice[channel].op[2].ar;
  reg3(0x58 + offset, buf, channel); //Rate Scaling + Attack Rate Operator 3

  buf = readEEPROM(disk1, inst + 67); //RS
  voice[channel].op[3].rs = buf;
  buf <<= 6;
  voice[channel].op[3].ar = readEEPROM(disk1, inst + 61); //AR
  buf += voice[channel].op[3].ar;
  reg3(0x5C + offset, buf, channel); //Rate Scaling + Attack Rate Operator 4

  buf = readEEPROM(disk1, inst + 38); //AMS-EN
  voice[channel].op[0].ams = buf;
  buf <<= 7;
  voice[channel].op[0].d1r = readEEPROM(disk1, inst + 29); //D1R
  buf += voice[channel].op[0].d1r;
  reg3(0x60 + offset, buf, channel); //Decay rate + Amplitude Modulation, Operator 1
  buf = readEEPROM(disk1, inst + 49); //AMS-EN
  voice[channel].op[1].ams = buf;
  buf <<= 7;
  voice[channel].op[1].d1r = readEEPROM(disk1, inst + 40); //D1R
  buf += voice[channel].op[1].d1r;
  reg3(0x64 + offset, buf, channel); //Decay rate (keyon) Operator 2
  buf = readEEPROM(disk1, inst + 60); //AMS-EN
  voice[channel].op[2].ams = buf;  
  buf <<= 7;
  voice[channel].op[2].d1r= readEEPROM(disk1, inst + 51); //D1R
  buf += voice[channel].op[2].d1r;
  reg3(0x68 + offset, buf, channel); //Decay rate (keyon) Operator 3
  buf = readEEPROM(disk1, inst + 71); //AMS-EN
  voice[channel].op[3].ams = buf;
  buf <<= 7;
  voice[channel].op[3].d1r = readEEPROM(disk1, inst + 62); //D1R
  buf += voice[channel].op[3].d1r;
  reg3(0x6C + offset, buf, channel); //Decay rate (keyon) Operator 4

  buf = readEEPROM(disk1, inst + 30); //D2R (low nibble)
  voice[channel].op[0].d2r = buf;
  reg3(0x70 + offset, buf, channel); //Secondary decay (keyon) Operator 1
  buf = readEEPROM(disk1, inst + 41); //D2R
  voice[channel].op[1].d2r = buf;
  reg3(0x74 + offset, buf, channel); //Secondary decay (keyon) Operator 2
  buf = readEEPROM(disk1, inst + 52); //D2R
  voice[channel].op[2].d2r = buf;
  reg3(0x78 + offset, buf, channel); //Secondary decay (keyon) Operator 3
  buf = readEEPROM(disk1, inst + 63); //D2R
  voice[channel].op[3].d2r = buf;
  reg3(0x7C + offset, buf, channel); //Secondary decay (keyon) Operator 4

  buf = readEEPROM(disk1, inst + 32) << 4; //D1L (high nibble)
  buf += readEEPROM(disk1, inst + 31); //RR (low nibble)
  reg3(0x80 + offset, buf, channel); //Secondary amplitude + release rate Operator 1
  buf = readEEPROM(disk1, inst + 43) << 4; //D1L (high nibble)
  buf += readEEPROM(disk1, inst + 42); //RR (low nibble)
  reg3(0x84 + offset, buf, channel); //Secondary amplitude/release rate Operator 2
  buf = readEEPROM(disk1, inst + 54) << 4; //D1L (high nibble)
  buf += readEEPROM(disk1, inst + 53); //RR (low nibble)
  reg3(0x88 + offset, buf, channel); //Secondary amplitude/release rate Operator 3
  buf = readEEPROM(disk1, inst + 65) << 4; //D1L (high nibble)
  buf += readEEPROM(disk1, inst + 64); //RR (low nibble)
  reg3(0x8C + offset, buf, channel); //Secondary amplitude/release rate Operator 4

  buf = readEEPROM(disk1, inst + 22); //FL, feedback (3 bits)
  voice[channel].feedback = buf;
  buf <<= 3;
  voice[channel].algorithm = readEEPROM(disk1, inst + 23); //CON, algorithm (3 bits)
  buf += voice[channel].algorithm;
  reg3(0xB0 + offset, buf, channel); //Feedback/algorithm select

  reg3(0xB4 + offset, 0xC0, channel); //Both speakers on
  reg3(0x28, channel, channel); //Key off
}
/* Selects and loads an instrument from channel 0. */
void selectvoice(unsigned char channel)
{
  unsigned char buf = 0;
  unsigned char offset;

  if (channel <= CH3)
  {
    offset = channel;
  }
  else if (channel <= CH6)
  {
    offset = channel - 4;
  }
  else if (channel <= CH9)
  {
    offset = channel - 7;
  }
  else if (channel <= CH12)
  {
    offset = channel -11;
  }

  reg3(0x28, 0x00 + channel, channel); //Mute channel before modifying it

  buf = voice[0].op[0].dt1; //DT1
  voice[channel].op[0].dt1 = buf;
  buf <<= 4;
  voice[channel].op[0].mul = voice[0].op[0].mul; //MUL
  buf += voice[0].op[0].mul;
  reg3(0x30 + offset, buf, channel); //Detune + Multiply Operator 1

  buf = voice[0].op[1].dt1; //DT1
  voice[channel].op[1].dt1 = buf;
  buf <<= 4;
  voice[channel].op[1].mul = voice[0].op[1].mul; //MUL
  buf += voice[0].op[1].mul;
  reg3(0x34 + offset, buf, channel); //Detune + Multiply Operator 2

  buf = voice[0].op[2].dt1; //DT1
  voice[channel].op[2].dt1 = buf;
  buf <<= 4;
  voice[channel].op[2].mul = voice[0].op[2].mul; //MUL
  buf += voice[0].op[2].mul;
  reg3(0x38 + offset, buf, channel); //Detune + Multiply Operator 3

  buf = voice[0].op[3].dt1; //DT1
  voice[channel].op[3].dt1 = buf;
  buf <<= 4;
  voice[channel].op[3].mul = voice[0].op[3].mul; //MUL
  buf += voice[0].op[3].mul;
  reg3(0x3C + offset, buf, channel); //Detune + Multiply Operator 4

  buf = voice[0].op[0].tl; //TL
  voice[channel].op[0].tl = buf;
  reg3(0x40 + offset, buf, channel); //Total Level Operator 1
  buf = voice[0].op[1].tl; //TL
  voice[channel].op[1].tl = buf;
  reg3(0x44 + offset, buf, channel); //Total Level Operator 2
  buf = voice[0].op[2].tl; //TL
  voice[channel].op[2].tl = buf;
  reg3(0x48 + offset, buf, channel); //Total Level Operator 3
  buf = voice[0].op[3].tl; //TL
  voice[channel].op[3].tl = buf;
  reg3(0x4C + offset, buf, channel); //Total Level Operator 4

  buf = voice[0].op[0].rs; //RS
  voice[channel].op[0].rs = buf;
  buf <<= 6;
  voice[channel].op[0].ar = voice[0].op[0].ar; //AR
  buf += voice[channel].op[0].ar;
  reg3(0x50 + offset, buf, channel); //Rate Scaling + Attack Rate Operator 1

  buf = voice[0].op[1].rs; //RS
  voice[channel].op[1].rs = buf;
  buf <<= 6;
  voice[channel].op[1].ar = voice[0].op[1].ar; //AR
  buf += voice[channel].op[1].ar;
  reg3(0x54 + offset, buf, channel); //Rate Scaling + Attack Rate Operator 2

  buf = voice[0].op[2].rs; //RS
  voice[channel].op[2].rs = buf;
  buf <<= 6;
  voice[channel].op[2].ar = voice[0].op[2].ar; //AR
  buf += voice[channel].op[2].ar;
  reg3(0x58 + offset, buf, channel); //Rate Scaling + Attack Rate Operator 3

  buf = voice[0].op[3].rs; //RS
  voice[channel].op[3].rs = buf;
  buf <<= 6;
  voice[channel].op[3].ar = voice[0].op[3].ar; //AR
  buf += voice[channel].op[3].ar;
  reg3(0x5C + offset, buf, channel); //Rate Scaling + Attack Rate Operator 4

  buf = voice[0].op[0].ams; //AMS-EN
  voice[channel].op[0].ams = buf;
  buf <<= 7;
  voice[channel].op[0].d1r = voice[0].op[0].d1r; //D1R
  buf += voice[channel].op[0].d1r;
  reg3(0x60 + offset, buf, channel); //Decay rate + Amplitude Modulation, Operator 1
  buf = voice[0].op[1].ams; //AMS-EN
  voice[channel].op[1].ams = buf;
  buf <<= 7;
  voice[channel].op[1].d1r = voice[0].op[1].d1r; //D1R
  buf += voice[channel].op[1].d1r;
  reg3(0x64 + offset, buf, channel); //Decay rate (keyon) Operator 2
  buf = voice[0].op[2].ams; //AMS-EN
  voice[channel].op[2].ams = buf;  
  buf <<= 7;
  voice[channel].op[2].d1r= voice[0].op[2].d1r; //D1R
  buf += voice[channel].op[2].d1r;
  reg3(0x68 + offset, buf, channel); //Decay rate (keyon) Operator 3
  buf = voice[0].op[3].ams; //AMS-EN
  voice[channel].op[3].ams = buf;
  buf <<= 7;
  voice[channel].op[3].d1r = voice[0].op[3].d1r; //D1R
  buf += voice[channel].op[3].d1r;
  reg3(0x6C + offset, buf, channel); //Decay rate (keyon) Operator 4
  
  //RANIX

  buf = readEEPROM(disk1, inst + 30); //D2R (low nibble)
  voice[channel].op[0].d2r = buf;
  reg3(0x70 + offset, buf, channel); //Secondary decay (keyon) Operator 1
  buf = readEEPROM(disk1, inst + 41); //D2R
  voice[channel].op[1].d2r = buf;
  reg3(0x74 + offset, buf, channel); //Secondary decay (keyon) Operator 2
  buf = readEEPROM(disk1, inst + 52); //D2R
  voice[channel].op[2].d2r = buf;
  reg3(0x78 + offset, buf, channel); //Secondary decay (keyon) Operator 3
  buf = readEEPROM(disk1, inst + 63); //D2R
  voice[channel].op[3].d2r = buf;
  reg3(0x7C + offset, buf, channel); //Secondary decay (keyon) Operator 4

  buf = readEEPROM(disk1, inst + 32) << 4; //D1L (high nibble)
  buf += readEEPROM(disk1, inst + 31); //RR (low nibble)
  reg3(0x80 + offset, buf, channel); //Secondary amplitude + release rate Operator 1
  buf = readEEPROM(disk1, inst + 43) << 4; //D1L (high nibble)
  buf += readEEPROM(disk1, inst + 42); //RR (low nibble)
  reg3(0x84 + offset, buf, channel); //Secondary amplitude/release rate Operator 2
  buf = readEEPROM(disk1, inst + 54) << 4; //D1L (high nibble)
  buf += readEEPROM(disk1, inst + 53); //RR (low nibble)
  reg3(0x88 + offset, buf, channel); //Secondary amplitude/release rate Operator 3
  buf = readEEPROM(disk1, inst + 65) << 4; //D1L (high nibble)
  buf += readEEPROM(disk1, inst + 64); //RR (low nibble)
  reg3(0x8C + offset, buf, channel); //Secondary amplitude/release rate Operator 4

  buf = readEEPROM(disk1, inst + 22); //FL, feedback (3 bits)
  voice[channel].feedback = buf;
  buf <<= 3;
  voice[channel].algorithm = readEEPROM(disk1, inst + 23); //CON, algorithm (3 bits)
  buf += voice[channel].algorithm;
  reg3(0xB0 + offset, buf, channel); //Feedback/algorithm select

  reg3(0xB4 + offset, 0xC0, channel); //Both speakers on
  reg3(0x28, channel, channel); //Key off
}

/* Reads status information from the YM2612
 
 Registers are READ-ONLY and you will ONLY get status.
 Returns:
 0: Ready to write.
 Anything else: Not ready.
 */
unsigned int readreg()
{
  unsigned int state = 0;

  digitalWrite(CS, LOW);
  digitalWrite(RD, HIGH);
  digitalWrite(WR, HIGH);
  digitalWrite(AA1, LOW);
  digitalWrite(AA0, HIGH);

  digitalWrite(RD, LOW);
  state = readbus();
  digitalWrite(RD, HIGH);
  digitalWrite(CS, HIGH);
  return(state);
}

/* Selects a note to play. */
void setpitch(unsigned int block, unsigned int fnumber, unsigned int channel)
{
  int i;
  unsigned int state = 0;
  unsigned int state2 = 0;
  unsigned int tchan = channel;

  KEYFREQ[channel] = fnumber;
  KEYBLOCK[channel] = block;

  for(i=0; i<3; i++)
  {
    if( (block & 1<<i) != 0)
    {
      state |= 1<<(i+3);
    }
  }

  for(i=0; i<3; i++)
  {
    if( (fnumber & 1<<(i+8)) != 0)
    {
      state |= (1<<i);
    }
  }

  for(i=0; i<8; i++)
  {
    if( (fnumber & 1<<i) !=0)
    {
      state2 |= (1<<i);
    }
  }

  if (tchan > CH6) tchan = channel -7;

  //FIXME: comment this shit wtf
  if ( channel <= CH3 )
  {
    reg3( 0xA4 + tchan, state, channel);
    reg3( 0xA0 + tchan, state2, channel);
  }
  else if (channel <= CH6)
  {
    reg3( 0xA4 + tchan -4, state, channel);
    reg3( 0xA0 + tchan -4, state2, channel);
  }
  else if ( channel <= CH9 )
  {
    reg3( 0xA4 + tchan, state, channel);
    reg3( 0xA0 + tchan, state2, channel);
  }
  else if (channel <= CH12)
  {
    reg3( 0xA4 + tchan -4, state, channel);
    reg3( 0xA0 + tchan -4, state2, channel);
  }
}
void pitchbend(unsigned int bend, unsigned int channel)
{
  int i;
  unsigned int state = 0;
  unsigned int state2 = 0;
  unsigned int tchan = channel;
  unsigned int fnumber = KEYFREQ[channel] + bend;
  unsigned int block = KEYBLOCK[channel];

  for(i=0; i<3; i++)
  {
    if( (block & 1<<i) != 0)
    {
      state |= 1<<(i+3);
    }
  }

  for(i=0; i<3; i++)
  {
    if( (fnumber & 1<<(i+8)) != 0)
    {
      state |= (1<<i);
    }
  }

  for(i=0; i<8; i++)
  {
    if( (fnumber & 1<<i) !=0)
    {
      state2 |= (1<<i);
    }
  }

  if (tchan > CH6) tchan = channel -7;

  //FIXME: comment this shit wtf
  if ( channel <= CH3 )
  {
    reg3( 0xA4 + tchan, state, channel);
    reg3( 0xA0 + tchan, state2, channel);
  }
  else if (channel <= CH6)
  {
    reg3( 0xA4 + tchan -4, state, channel);
    reg3( 0xA0 + tchan -4, state2, channel);
  }
  else if ( channel <= CH9 )
  {
    reg3( 0xA4 + tchan, state, channel);
    reg3( 0xA0 + tchan, state2, channel);
  }
  else if (channel <= CH12)
  {
    reg3( 0xA4 + tchan -4, state, channel);
    reg3( 0xA0 + tchan -4, state2, channel);
  }
}

/* Selects a frequency in hz. */
void sethz(double hz)
{
  double fnum;
  int block = 3;

  while(1)
  {
    fnum = hz / pow(2, block - 21) / 106666.66; //FIXME: correct this constant
    if( fnum >=1250 )
    {
      block++;
    }
    else if( fnum <= 600 )
    {
      block--;
    }
    else break;
  }
  setpitch(block, fnum, CH1);
}
/* Plays a note on channel 1. */
//FIXME: Delete this, it's debug code
void note1()
{
  reg(0x28, 0xF0, 1);
  delay(50);
  reg(0x28, 0x00, 1);
  delay(50);
}

/* Play a note on channel 4. */
//FIXME: Delete this, it's debug code
/*
void note2()
 {
 reg(0x28, 0xF0 + CH4);
 delay(500);
 reg(0x28, CH4);
 delay(500);
 }
 */

/* Press a key. */
void keyDown(int channel, int note)
{
  int harbl, baz = 3;
  unsigned int chip;

  if (MODE == MODE_POLY)
  {
    for(int i=0; i<14; i++)
    {      
      if (KEYS[i] == 0)
      {
        channel = i;
        break;
      }
    }
  }
  if(KEYS[channel] != 0) return;

  KEYS[channel] = note;
  harbl = note / 12;
  harbl -= 1;
  baz = note % 12;

  setpitch(harbl, freq[baz+1], channel);

  if (channel <= CH6)
  {
    chip = 1;
    matrix.setColumn(1, channel +1, 0xFF);
  }
  else if (channel <= CH12)
  {
    chip = 2;
    channel -=7;
    matrix.setColumn(0, channel +1, 0xFF);
  }
  reg(0x28, 0xF0 + channel, chip);
}

/* Release a key. */
void keyUp(int channel, int note)
{
  unsigned int chip = 0;

  if (MODE == MODE_POLY)
  {
    for(int i=0;i<14;i++)
    {
      if( (channel == 3) || (channel == 10)) continue;
      if(KEYS[i] == note)
        channel = i;
    }
  }

  if(KEYS[channel] > 0)
  {
    KEYS[channel] = 0;

    if (channel <= CH6)
    {
      chip = 1;
      matrix.setColumn(1, channel +1, B00000);
    }
    else if (channel <= CH12)
    {
      chip = 2;
      channel -= 7;
      matrix.setColumn(0, channel +1, B00000);
    }
    reg(0x28, channel, chip);
  }
}

/* Wipes across a range of notes. */
//FIXME: delete this, it's debug code
void wipe()
{
  sethz(246.94);
  note1();
  sethz(261.63);
  note1();
  sethz(277.18);
  note1();
  sethz(293.66);
  note1();
  sethz(311.13);
  note1();
  sethz(329.63);
  note1();
  sethz(349.23);
  note1();
  sethz(369.99);
  note1();
  sethz(392.00);
  note1();
  sethz(415.30);
  note1();
  sethz(440.00);
  note1();
  sethz(466.16);
  note1();
  sethz(493.88);
  note1(); 
}

/* Write a byte to the 32k EEPROM. */
void writeEEPROM( unsigned char prom, unsigned int eeaddress, byte data ) 
{
  Wire.beginTransmission(prom);
  Wire.send((int)(eeaddress >> 8));   // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.send(data);
  Wire.endTransmission();

  delay(5);
}

/* Read a byte from the 32k EEPROM. */
byte readEEPROM(unsigned char prom, unsigned int eeaddress) 
{
  byte rdata = 0xFF;

  Wire.beginTransmission(prom);
  Wire.send((int)(eeaddress >> 8));   // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();

  Wire.requestFrom(disk1,1);

  if (Wire.available())
  {
    rdata = Wire.receive();
    return rdata;
  } 
  return 0;
}

void writeram(unsigned short address, unsigned char *data, unsigned int count)
{
  int i;

  PORTD &= ~(1 << 5);
  SPI.transfer(0x02);
  SPI.transfer((address & 0xFF00) >> 8);
  SPI.transfer(address & 0x00FF);
  for (i=0; i<count; i++)
  {
    SPI.transfer(*data);
  }
  PORTD |= 1 << 5;
}

void readram(unsigned short address, byte *buffer, unsigned int count)
{
  byte rec;
  int i;
  PORTD &= ~(1 << 5);
  delayMicroseconds(12);
  SPI.transfer(0x03);
  SPI.transfer((address & 0xFF00) >> 8);
  SPI.transfer(address & 0x00FF);
  for (i=0; i<count; i++)
  {
    buffer[i] = SPI.transfer(0);
  }
  PORTD |= 1 << 5;
}

/* Flash the 32k EEPROM with instrument data. */
void flashEEPROM()
{
  int ibyte = 0;
  int stride = 0;

  Serial.begin(921600);

  while(stride < 12456) //FIXME: error check this
    if (Serial.available() > 0)
    {
      ibyte = Serial.read();
      if (ibyte >= 0)
      {
        writeEEPROM(disk1, stride, (char)ibyte);
        stride++;
      }
    }   
}

void dtime(unsigned long time)
{
  if ( TIMER < DTIMER )
  {
    DTIMER += time;
  }
  else
  {
    TIMER = 0;
    DTIMER = time; 
  }
}

void vgmplay()
{
  unsigned char ibyte = 0;
  int rdy = 0;
  int n = 0;
  unsigned char ibyte2 = 0;
  unsigned char buff[1280];
  unsigned char* buf1 = buff;
  unsigned char* buf = buf1;
  unsigned char* buf2 = buff + 64;
  byte ram[128];
  byte * ram1 = ram;
  unsigned short ramptr = 0;
  int more = 0;
  int cnt = 0;
  int lize = 0;
  int errcnt = 0;
  int exit = 0;


  LCD.print("VGM Player Mode");
  lcdline2();

  while(1)
  { 
    if ( more == 0)
    {
      while( !(RawHID.recv(buf1, 0) > 0) );
      buf = buf1;
    }
    else
    {
      if (buf1 >= buff + (1280 - 65))
      {
        buf1 = buff;
        buf = buf1;
      }
      else
      {
        buf1 += 64;
        buf = buf1;
      }
      more--;
    }

    exit = 0;

    while (exit < 3)
    {
      //check timer, wait if necessary
      while ( TIMER < DTIMER)
      {
        if ( (DTIMER > (TIMER + 50))  )
        {
          if ( (more < 18) )
          {
            if (RawHID.recv(buf2, 0) > 0)
            {
              if ( buf2 >= buff + (1280 -65))
              {
                buf2 = buff;
              }
              else buf2 += 64;
              more++;
            }
            if (buf2 == buf1)
            {
              lcdinit();
              LCD.print("MASSIVE ERROR");
            }
          }
        }
      }

      TIMER = 0;
      DTIMER = 0;

      if (*buf == 39)
      {
        buf++; 
        if (*buf == 42)
        {
          buf++;
          if (*buf == 26)
          {
            exit = 3;
            continue;
          }
          else
          {
            buf -= 2; 
          }
        }
        else
        {
          buf--;
        }          
      }

      if ((*buf & 0xF0) == 0x60)
      {
        dtime((*buf &0x0F) * 22.6757);
        //buf++;
        //ibyte = *buf;
        if (ram1 == ram + 127)
        {
          readram(ramptr, ram, 128);
          ram1 = ram;
        }

        selectreg(0x2A);
        writereg(*ram1);
        ram1++;
        ramptr++;
        buf++;
        continue;
      }

      switch(*buf)
      {
      case 1: //register 1 write
        buf++;
        ibyte = *buf;
        buf++;
        ibyte2 = *buf;

        if(ibyte == 0x28)
        {
          if ( lize == 0) lize = 1;
        }

        if (lize == 0) delay(2);
        selectreg(ibyte);
        writereg(ibyte2);
        break;
      case 2: //register 2 write
        buf++;
        ibyte = *buf;
        buf++;
        ibyte2 = *buf;
        if (lize == 0) delay(2);
        selectreg2(ibyte);
        writereg2(ibyte2);
        break;
      case 3: //incoming PCM data
        buf++;
        writeram(ramptr, buf, 1);
        ramptr++;
        break;
      case 4: //1 byte wait data
        buf++;
        ibyte = *buf;
        dtime(ibyte * 22.6757);
        break;
      case 5: //2 byte wait data
        unsigned int wait;
        buf++;
        ibyte = *buf;
        buf++;
        ibyte2 = *buf;
        wait = (ibyte << 8) + ibyte2;
        dtime((unsigned long)(wait * 22.6757));
        break;
        //case 6 is used above in a pre-switch if
      case 7: //static time delay
        dtime(735 * 22.6757);
        break;
      case 8: //static time delay
        dtime(882 * 22.6757);
        break;
      case 9:
        ramptr = 0;
        break;
      case 10: //stream begin
        lize = 0;
        digitalWrite(RESET, LOW);
        delay(100);
        digitalWrite(RESET, HIGH); //Active low
        delay(100);
        ramptr = 0;
        break;
      case 11:
        buf++;
        ramptr = *buf << 8;
        buf++;
        ramptr += *buf;
        readram(ramptr, ram, 128);
        ram1 = ram;
        break;
      default:
        lcdinit();
        LCD.print("MAJOR ERROR");
        lcdline2();
        LCD.print(String(*buf, DEC));
      }
      buf++;
    }
  }  
}

void lcdinit()
{

  LCD.write((byte)0xFE);
  LCD.write((byte)0x51); //clear
  LCD.write((byte)0x00);

  delayMicroseconds(150);
}
void lcdline2()
{
  LCD.write((byte)0xFE);
  LCD.write((byte)0x45);
  LCD.write((byte)0x40);
}

void lcdwrite(const char* data)
{
  LCD.print(data);
  delayMicroseconds(150);  
}

/* Initialize the Teensy and the YM2612. */
void setup()
{
  MIDI.begin(MIDI_CHANNEL_OMNI);

  //Serial.begin(921600); //DEBUG
  //Serial.println("MIDI Input Test");

  Wire.begin();

  pinMode(LCDPin, OUTPUT);
  pinMode(backlight, OUTPUT);
  pinMode(CS, OUTPUT);
  pinMode(CS2, OUTPUT);
  pinMode(RD, OUTPUT);
  pinMode(WR, OUTPUT);
  pinMode(AA0, OUTPUT);
  pinMode(AA1, OUTPUT);
  pinMode(RESET, OUTPUT);
  pinMode(SSP, OUTPUT);
  digitalWrite(SSP, HIGH);

  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  LCD.begin(9600);

  //Always keep CS high when not writing.
  digitalWrite(CS, HIGH);
  digitalWrite(CS2, HIGH);

  //Reset the YM2612. Power cycle is insufficient.
  digitalWrite(RESET, LOW);
  delay(100);
  digitalWrite(RESET, HIGH); //Active low
  delay(100);

  lcdinit();

  for(int i = 0; i < matrix.getDeviceCount(); i++)
  {
    matrix.shutdown(i,false);
  }

  matrix.setIntensity(0, 8);
  matrix.setIntensity(1, 8);
  matrix.clearDisplay(0);
  matrix.clearDisplay(1);
  /*  
   for(int j=0; j < 8; j++)
   for(int i=0; i < 5; i++)
   {
   matrix.setLed(0, i, j, true);
   matrix.setLed(1, i, j, true);
   delay(100);
   matrix.setLed(0, i, j, false);
   matrix.setLed(1, i, j, false);
   }
   */
  //flashEEPROM();
  /*
  reg(0x22, 0x00); //LFO off
   reg(0x27, 0x00); //Channel 3 mode normal
   reg(0x28, 0x00); //Channel 1 Key Off
   reg(0x28, 0x01); //Channel 2 Key Off
   reg(0x28, 0x02); //Channel 3 Key Off
   reg(0x28, 0x04); //Channel 4 Key Off
   reg(0x28, 0x05); //Channel 5 Key Off
   reg(0x28, 0x06); //Channel 6 Key Off
   reg(0x2B, 0x00); //DAC off
   
   reg(0x30, 0x71); //Detune/Multiply Ch 1 Operator 1 set
   reg(0x31, 0x71); //Detune/Multiply Ch 2 Operator 1 set
   reg(0x32, 0x71); //Detune/Multiply Ch 3 Operator 1 set
   reg2(0x30, 0x71); //Detune/Multiply Ch 4 Operator 1 set
   reg2(0x31, 0x71); //Detune/Multiply Ch 5 Operator 1 set
   reg2(0x32, 0x71); //Detune/Multiply Ch 6 Operator 1 set
   
   reg(0x34, 0x0D); //Detune/Multiply Ch 1 Operator 2 set
   reg(0x35, 0x0D); //Detune/Multiply Ch 2 Operator 2 set
   reg(0x36, 0x0D); //Detune/Multiply Ch 3 Operator 2 set
   reg2(0x34, 0x0D); //Detune/Multiply Ch 4 Operator 2 set
   reg2(0x35, 0x0D); //Detune/Multiply Ch 5 Operator 2 set
   reg2(0x36, 0x0D); //Detune/Multiply Ch 6 Operator 2 set
   
   reg(0x38, 0x33); //Detune/Multiply Ch 1 Operator 3 set
   reg(0x39, 0x33); //Detune/Multiply Ch 2 Operator 3 set
   reg(0x3A, 0x33); //Detune/Multiply Ch 3 Operator 3 set
   reg2(0x38, 0x33); //Detune/Multiply Ch 4 Operator 3 set
   reg2(0x39, 0x33); //Detune/Multiply Ch 5 Operator 3 set
   reg2(0x3A, 0x33); //Detune/Multiply Ch 6 Operator 3 set
   
   reg(0x3C, 0x01); //Detune/Multiply Ch 1 Operator 4 set
   reg(0x3D, 0x01); //Detune/Multiply Ch 2 Operator 4 set
   reg(0x3E, 0x01); //Detune/Multiply Ch 3 Operator 4 set
   reg2(0x3C, 0x01); //Detune/Multiply Ch 4 Operator 4 set
   reg2(0x3D, 0x01); //Detune/Multiply Ch 5 Operator 4 set
   reg2(0x3E, 0x01); //Detune/Multiply Ch 6 Operator 4 set
   
   reg(0x40, 0x23); //Total Level Ch 1 Operator 1 set
   reg(0x41, 0x23); //Total Level Ch 2 Operator 1 set
   reg(0x42, 0x23); //Total Level Ch 3 Operator 1 set
   reg2(0x40, 0x23); //Total Level Ch 4 Operator 1 set
   reg2(0x41, 0x23); //Total Level Ch 5 Operator 1 set
   reg2(0x42, 0x23); //Total Level Ch 6 Operator 1 set
   
   reg(0x44, 0x2D); //Total Level Ch 1 Operator 2 set
   reg(0x45, 0x2D); //Total Level Ch 2 Operator 2 set
   reg(0x46, 0x2D); //Total Level Ch 3 Operator 2 set
   reg2(0x44, 0x2D); //Total Level Ch 4 Operator 2 set
   reg2(0x45, 0x2D); //Total Level Ch 5 Operator 2 set
   reg2(0x46, 0x2D); //Total Level Ch 6 Operator 2 set
   
   reg(0x48, 0x26); //Total Level Ch 1 Operator 3 set
   reg(0x49, 0x26); //Total Level Ch 2 Operator 3 set
   reg(0x4A, 0x26); //Total Level Ch 3 Operator 3 set
   reg2(0x48, 0x26); //Total Level Ch 4 Operator 3 set
   reg2(0x49, 0x26); //Total Level Ch 5 Operator 3 set
   reg2(0x4A, 0x26); //Total Level Ch 6 Operator 3 set
   
   reg(0x4C, 0x00); //Total Level Ch 1 Operator 4 set
   reg(0x4D, 0x00); //Total Level Ch 2 Operator 4 set
   reg(0x4E, 0x00); //Total Level Ch 3 Operator 4 set
   reg2(0x4C, 0x00); //Total Level Ch 4 Operator 4 set
   reg2(0x4D, 0x00); //Total Level Ch 5 Operator 4 set
   reg2(0x4E, 0x00); //Total Level Ch 6 Operator 4 set
   
   reg(0x50, 0x5F); //Attack rate (keyon) Ch 1 Operator 1 set
   reg(0x51, 0x5F); //Attack rate (keyon) Ch 2 Operator 1 set
   reg(0x52, 0x5F); //Attack rate (keyon) Ch 3 Operator 1 set
   reg2(0x50, 0x5F); //Attack rate (keyon) Ch 4 Operator 1 set
   reg2(0x51, 0x5F); //Attack rate (keyon) Ch 5 Operator 1 set
   reg2(0x52, 0x5F); //Attack rate (keyon) Ch 6 Operator 1 set
   
   reg(0x54, 0x5F); //Attack rate (keyon) Ch 1 Operator 2 set
   reg(0x55, 0x5F); //Attack rate (keyon) Ch 2 Operator 2 set
   reg(0x56, 0x5F); //Attack rate (keyon) Ch 3 Operator 2 set
   reg2(0x54, 0x5F); //Attack rate (keyon) Ch 4 Operator 2 set
   reg2(0x55, 0x5F); //Attack rate (keyon) Ch 5 Operator 2 set
   reg2(0x56, 0x5F); //Attack rate (keyon) Ch 6 Operator 2 set
   
   reg(0x58, 0x5F); //Attack rate (keyon) Ch 1 Operator 3 set
   reg(0x59, 0x5F); //Attack rate (keyon) Ch 2 Operator 3 set
   reg(0x5A, 0x5F); //Attack rate (keyon) Ch 3 Operator 3 set
   reg2(0x58, 0x5F); //Attack rate (keyon) Ch 4 Operator 3 set
   reg2(0x59, 0x5F); //Attack rate (keyon) Ch 5 Operator 3 set
   reg2(0x5A, 0x5F); //Attack rate (keyon) Ch 6 Operator 3 set
   
   reg(0x5C, 0x94); //Attack rate (keyon) Ch 1 Operator 4 set
   reg(0x5D, 0x94); //Attack rate (keyon) Ch 2 Operator 4 set
   reg(0x5E, 0x94); //Attack rate (keyon) Ch 3 Operator 4 set
   reg2(0x5C, 0x94); //Attack rate (keyon) Ch 4 Operator 4 set
   reg2(0x5D, 0x94); //Attack rate (keyon) Ch 5 Operator 4 set
   reg2(0x5E, 0x94); //Attack rate (keyon) Ch 6 Operator 4 set
   
   reg(0x60, 5); //Decay rate (keyon) Ch 1 Operator 1 set
   reg(0x61, 5); //Decay rate (keyon) Ch 2 Operator 1 set
   reg(0x62, 5); //Decay rate (keyon) Ch 3 Operator 1 set
   reg2(0x60, 5); //Decay rate (keyon) Ch 4 Operator 1 set
   reg2(0x61, 5); //Decay rate (keyon) Ch 5 Operator 1 set
   reg2(0x62, 5); //Decay rate (keyon) Ch 6 Operator 1 set
   
   reg(0x64, 5); //Decay rate (keyon) Ch 1 Operator 2 set
   reg(0x65, 5); //Decay rate (keyon) Ch 2 Operator 2 set
   reg(0x66, 5); //Decay rate (keyon) Ch 3 Operator 2 set
   reg2(0x64, 5); //Decay rate (keyon) Ch 4 Operator 2 set
   reg2(0x65, 5); //Decay rate (keyon) Ch 5 Operator 2 set
   reg2(0x66, 5); //Decay rate (keyon) Ch 6 Operator 2 set
   
   reg(0x68, 5); //Decay rate (keyon) Ch 1 Operator 3 set
   reg(0x69, 5); //Decay rate (keyon) Ch 2 Operator 3 set
   reg(0x6A, 5); //Decay rate (keyon) Ch 3 Operator 3 set
   reg2(0x68, 5); //Decay rate (keyon) Ch 4 Operator 3 set
   reg2(0x69, 5); //Decay rate (keyon) Ch 5 Operator 3 set
   reg2(0x6A, 5); //Decay rate (keyon) Ch 6 Operator 3 set
   
   reg(0x6C, 7); //Decay rate (keyon) Ch 1 Operator 4 set
   reg(0x6D, 7); //Decay rate (keyon) Ch 2 Operator 4 set
   reg(0x6E, 7); //Decay rate (keyon) Ch 3 Operator 4 set
   reg2(0x6C, 7); //Decay rate (keyon) Ch 4 Operator 4 set
   reg2(0x6D, 7); //Decay rate (keyon) Ch 5 Operator 4 set
   reg2(0x6E, 7); //Decay rate (keyon) Ch 6 Operator 4 set
   
   reg(0x70, 2); //Secondary decay (keyon) Ch 1 Operator 1 set
   reg(0x71, 2); //Secondary decay (keyon) Ch 2 Operator 1 set
   reg(0x72, 2); //Secondary decay (keyon) Ch 3 Operator 1 set
   reg2(0x70, 2); //Secondary decay (keyon) Ch 4 Operator 1 set
   reg2(0x71, 2); //Secondary decay (keyon) Ch 5 Operator 1 set
   reg2(0x72, 2); //Secondary decay (keyon) Ch 6 Operator 1 set
   
   reg(0x74, 2); //Secondary decay (keyon) Ch 1 Operator 2 set
   reg(0x75, 2); //Secondary decay (keyon) Ch 2 Operator 2 set
   reg(0x76, 2); //Secondary decay (keyon) Ch 3 Operator 2 set
   reg2(0x74, 2); //Secondary decay (keyon) Ch 4 Operator 2 set
   reg2(0x75, 2); //Secondary decay (keyon) Ch 5 Operator 2 set
   reg2(0x76, 2); //Secondary decay (keyon) Ch 6 Operator 2 set
   
   reg(0x78, 2); //Secondary decay (keyon) Ch 1 Operator 3 set
   reg(0x79, 2); //Secondary decay (keyon) Ch 2 Operator 3 set
   reg(0x7A, 2); //Secondary decay (keyon) Ch 3 Operator 3 set
   reg2(0x78, 2); //Secondary decay (keyon) Ch 4 Operator 3 set
   reg2(0x79, 2); //Secondary decay (keyon) Ch 5 Operator 3 set
   reg2(0x7A, 2); //Secondary decay (keyon) Ch 6 Operator 3 set
   
   reg(0x7C, 2); //Secondary decay (keyon) Ch 1 Operator 4 set
   reg(0x7D, 2); //Secondary decay (keyon) Ch 2 Operator 4 set
   reg(0x7E, 2); //Secondary decay (keyon) Ch 3 Operator 4 set
   reg2(0x7C, 2); //Secondary decay (keyon) Ch 4 Operator 4 set
   reg2(0x7D, 2); //Secondary decay (keyon) Ch 5 Operator 4 set
   reg2(0x7E, 2); //Secondary decay (keyon) Ch 6 Operator 4 set
   
   reg(0x80, 0x11); //Secondary amplitude/release rate Ch 1 Oper 1 set
   reg(0x81, 0x11); //Secondary amplitude/release rate Ch 2 Oper 1 set
   reg(0x82, 0x11); //Secondary amplitude/release rate Ch 3 Oper 1 set
   reg2(0x80, 0x11); //Secondary amplitude/release rate Ch 4 Oper 1 set
   reg2(0x81, 0x11); //Secondary amplitude/release rate Ch 5 Oper 1 set
   reg2(0x82, 0x11); //Secondary amplitude/release rate Ch 6 Oper 1 set
   
   reg(0x84, 0x11); //Secondary amplitude/release rate Ch 1 Oper 2 set
   reg(0x85, 0x11); //Secondary amplitude/release rate Ch 2 Oper 2 set
   reg(0x86, 0x11); //Secondary amplitude/release rate Ch 3 Oper 2 set
   reg2(0x84, 0x11); //Secondary amplitude/release rate Ch 4 Oper 2 set
   reg2(0x85, 0x11); //Secondary amplitude/release rate Ch 5 Oper 2 set
   reg2(0x86, 0x11); //Secondary amplitude/release rate Ch 6 Oper 2 set
   
   reg(0x88, 0x11); //Secondary amplitude/release rate Ch 1 Oper 3 set
   reg(0x89, 0x11); //Secondary amplitude/release rate Ch 2 Oper 3 set
   reg(0x8A, 0x11); //Secondary amplitude/release rate Ch 3 Oper 3 set
   reg2(0x88, 0x11); //Secondary amplitude/release rate Ch 4 Oper 3 set
   reg2(0x89, 0x11); //Secondary amplitude/release rate Ch 5 Oper 3 set
   reg2(0x8A, 0x11); //Secondary amplitude/release rate Ch 6 Oper 3 set
   
   reg(0x8C, 0xA6); //Secondary amplitude/release rate Ch 1 Oper 4 set
   reg(0x8D, 0xA6); //Secondary amplitude/release rate Ch 2 Oper 4 set
   reg(0x8E, 0xA6); //Secondary amplitude/release rate Ch 3 Oper 4 set
   reg2(0x8C, 0xA6); //Secondary amplitude/release rate Ch 4 Oper 4 set
   reg2(0x8D, 0xA6); //Secondary amplitude/release rate Ch 5 Oper 4 set
   reg2(0x8E, 0xA6); //Secondary amplitude/release rate Ch 6 Oper 4 set
   
   reg(0x90, 0); //Sega doesn't know what this does
   reg(0x94, 0); //Sega doesn't know what this does
   reg(0x98, 0); //Sega doesn't know what this does
   reg(0x9C, 0); //Sega doesn't know what this does
   reg2(0x90, 0); //Sega doesn't know what this does
   reg2(0x94, 0); //Sega doesn't know what this does
   reg2(0x98, 0); //Sega doesn't know what this does
   reg2(0x9C, 0); //Sega doesn't know what this does
   
   reg(0xB0, 0x32); //Feedback/algorithm select, Ch 1
   reg(0xB1, 0x33); //Feedback/algorithm select, Ch 2
   reg(0xB2, 0x34); //Feedback/algorithm select, Ch 3
   reg2(0xB0, 0x35); //Feedback/algorithm select, Ch 4
   reg2(0xB1, 0x37); //Feedback/algorithm select, Ch 5
   reg2(0xB2, 0x31); //Feedback/algorithm select, Ch 6
   
   reg(0xB4, 0xC0); //Both speakers on CH1
   reg(0xB5, 0xC0); //Both speakers on CH2
   reg(0xB6, 0xC0); //Both speakers on CH3
   reg2(0xB4, 0xC0); //Both speakers on CH4
   reg2(0xB5, 0xC0); //Both speakers on CH5
   reg2(0xB6, 0xC0); //Both speakers on CH6
   
   reg(0x28, 0x00); //Key off
   reg(0x28, 0x01); //Key off
   reg(0x28, 0x02); //Key off
   reg(0x28, 0x04); //Key off
   reg(0x28, 0x05); //Key off
   reg(0x28, 0x06); //Key off
  /* Now we are ready to set the frequency and play a note. */

  selectinst(0, CH1);
  selectinst(0, CH2);
  selectinst(0, CH3);
  selectinst(0, CH4);
  selectinst(0, CH5);
  selectinst(0, CH6);
  selectinst(0, CH7);
  selectinst(0, CH8);
  selectinst(0, CH9);
  selectinst(0, CH10);
  selectinst(0, CH11);
  selectinst(0, CH12);

  //vgmplay();


  //  analogWrite(backlight, 30);

  LCD.print("Welcome to");
  lcdline2();
  LCD.print("YM2612 synth");
}

void loop()
{
  int type, note, velocity, channel, d1, d2;
  byte ch, offset;

  long pos = knob1.read();

  if (pos != knob1pos)
  {
    knob1pos = pos;

    if ((pos % 4 == 0) && (pos >= 0))
    {
      lcdinit();
      LCD.print("YM2612 Synth --");
      lcdline2();
      LCD.print("Instrument: #");
      LCD.print(pos/4);
      selectinst(pos/4, CH1);
      selectinst(pos/4, CH2);
      selectinst(pos/4, CH3);
      selectinst(pos/4, CH4);
      selectinst(pos/4, CH5);
      selectinst(pos/4, CH6);
      selectinst(pos/4, CH7);
      selectinst(pos/4, CH8);
      selectinst(pos/4, CH9);
      selectinst(pos/4, CH10);
      selectinst(pos/4, CH11);
      selectinst(pos/4, CH12);

      for( int i=0; i<14; i++)
      {
        if( KEYS[i] < 0xFFFC)
        {
          KEYS[i] = 0;
        } 
      }
    }
  }


  //while(1) wipe();

  if (MIDI.read())
  {
    byte type = MIDI.getType();
    switch (type)
    {
    case NoteOn:
      note = MIDI.getData1();
      velocity = MIDI.getData2();
      channel = MIDI.getChannel();

      if (velocity > 0)
      {
        //Serial.println(String("Note On:  ch=") + channel + ", note=" + note + ", velocity=" + velocity);
        if (MODE == MODE_POLY) keyDown(CH1, note);
        else
          if(channel == 1) keyDown(CH1, note);
          else
            if(channel == 2) keyDown(CH2, note);
            else
              if(channel == 3) keyDown(CH3, note);
              else
                if(channel == 4) keyDown(CH4, note);
                else
                  if(channel == 5) keyDown(CH5, note);
                  else
                    if(channel == 6) keyDown(CH6, note);
                    else
                      if(channel == 7) keyDown(CH7, note);
                      else
                        if(channel == 8) keyDown(CH8, note);
                        else
                          if(channel == 9) keyDown(CH9, note);
                          else
                            if(channel == 10) keyDown(CH10, note);
                            else
                              if(channel == 11) keyDown(CH11, note);
                              else
                                if(channel == 12) keyDown(CH12, note);
      }
      else
      {
        //Serial.println(String("Note Off: ch=") + channel + ", note=" + note);
        if (MODE == MODE_POLY) keyUp(CH1, note);
        else
          if(channel == 1) keyUp(CH1, note);
          else
            if(channel == 2) keyUp(CH2, note);
            else
              if(channel == 3) keyUp(CH3, note);
              else
                if(channel == 4) keyUp(CH4, note);
                else
                  if(channel == 5) keyUp(CH5, note);
                  else
                    if(channel == 6) keyUp(CH6, note);
                    else
                      if(channel == 7) keyUp(CH7, note);
                      else
                        if(channel == 8) keyUp(CH8, note);
                        else
                          if(channel == 9) keyUp(CH9, note);
                          else
                            if(channel == 10) keyUp(CH10, note);
                            else
                              if(channel == 11) keyUp(CH11, note);
                              else
                                if(channel == 12) keyUp(CH12, note);
      }
      break;
    case NoteOff:
      note = MIDI.getData1();
      velocity = MIDI.getData2();
      channel = MIDI.getChannel();
      //Serial.println(String("Note Off: ch=") + channel + ", note=" + note + ", velocity=" + velocity);
      if (MODE == MODE_POLY) keyUp(CH1, note);
      else
        if(channel == 1) keyUp(CH1, note);
        else
          if(channel == 2) keyUp(CH2, note);
          else
            if(channel == 3) keyUp(CH3, note);
            else
              if(channel == 4) keyUp(CH4, note);
              else
                if(channel == 5) keyUp(CH5, note);
                else
                  if(channel == 6) keyUp(CH6, note);
                  else
                    if(channel == 7) keyUp(CH7, note);
                    else
                      if(channel == 8) keyUp(CH8, note);
                      else
                        if(channel == 9) keyUp(CH9, note);
                        else
                          if(channel == 10) keyUp(CH10, note);
                          else
                            if(channel == 11) keyUp(CH11, note);
                            else
                              if(channel == 12) keyUp(CH12, note);
      break;
    case ControlChange:
      d1 = MIDI.getData1();
      d2 = MIDI.getData2();
      channel = MIDI.getChannel();
      ch = CHANNEL[channel];
      byte buf;

      if (ch <= CH3)
      {
        offset = ch;
      }
      else if (ch <= CH6)
      {
        offset = ch - 4;
      }
      else if (ch <= CH9)
      {
        offset = ch - 7;
      }
      else if (ch <= CH12)
      {
        offset = ch -11;
      }

      switch (d1)
      {
      case 21:
        lcdinit();
        LCD.print("ALG set CH");
        LCD.print(String(ch, DEC));
        buf = voice[ch].feedback << 3;
        buf += voice[ch].algorithm = d2;
        lcdline2();
        LCD.print(String(voice[ch].feedback, DEC));
        LCD.print(" ");
        LCD.print(String(voice[ch].algorithm, DEC)); 
        reg3(0xB0 + offset, buf, ch);
        break;
      case 22:
        lcdinit();
        LCD.print("LFO");
        LCD.print(String(d2, DEC));
        LCD.print(" ");

        if (d2 == 0)
        {
          reg3(0x22, 0, ch);
        }
        else
        {
          reg3(0x22, d2 -1 + (1 << 3), ch);
        }

        break;
      case 50:
        lcdinit();
        LCD.print("FB set ");
        LCD.print(String(ch, DEC));
        buf = (voice[ch].feedback = d2 ) << 3;
        buf += voice[ch].algorithm;
        lcdline2();
        LCD.print(String(voice[ch].feedback, DEC));
        LCD.print(" ");
        LCD.print(String(voice[ch].algorithm, DEC)); 
        reg3(0xB0 + offset, buf, ch);
        break;
      case 42: //dt1 set
        lcdinit();
        LCD.print("Set DT1, CH");
        LCD.print(String(channel, DEC));
        lcdline2();
        LCD.print("Oper(s): ");

        for (int i=0; i<4; i++)
        {
          if ((chctl & (1 << i)) > 0)
          {
            LCD.print(String(i + 1, DEC));
            buf = (voice[ch].op[i].dt1 = d2) << 4;
            buf += voice[ch].op[i].mul;
            reg3(0x30 + (i * 4) + offset, buf, ch);
          }
        }
        break;
      case 43: //mul set
        lcdinit();
        LCD.print("Set MUL, CH");
        LCD.print(String(channel, DEC));
        lcdline2();
        LCD.print("Oper(s): ");

        for (int i=0; i<4; i++)
        {
          if ((chctl & (1 << i)) > 0)
          {
            LCD.print(String(i + 1, DEC));
            buf = voice[ch].op[i].dt1 << 4;
            buf += voice[ch].op[i].mul = d2;
            reg3(0x30 + (i * 4) + offset, buf, ch);
          }
        }
        break;
      case 44: //TL set
        //lcdinit();
        //LCD.print("Set TL, CH");
        //LCD.print(String(channel, DEC));
        //lcdline2();
        //LCD.print("Oper(s): ");

        for (int i=0; i<4; i++)
        {
          if ((chctl & (1 << i)) > 0)
          {
            //LCD.print(String(i + 1, DEC));
            buf = voice[ch].op[0].tl = d2;
            reg3(0x40 + (i * 4) + offset, buf, ch);
          }
        }
        break;
      case 45: //attack rate set
        lcdinit();
        LCD.print("Set Atk Rate, CH");
        LCD.print(String(channel, DEC));
        lcdline2();
        LCD.print("Oper(s): ");

        for (int i=0; i<4; i++)
        {
          if ((chctl & (1 << i)) > 0)
          {
            LCD.print(String(i + 1, DEC));
            buf = voice[ch].op[i].rs << 6;
            buf += voice[ch].op[i].ar = d2;
            reg3(0x50 + (i * 4) + offset, buf, ch);
          }
        }
        break;
      case 49: //channel LFO sensitivity set
        lcdinit();
        LCD.print("LFO CH");
        LCD.print(String(channel, DEC));
        lcdline2();

        if (d2 == 0)
        {
/*          for (int i=0; i<4; i++)
          {
            buf = voice[ch].op[i].d1r;
            reg3(0x60 + (i * 4) + offset, buf, ch);
            LCD.print(String(buf, DEC));
            LCD.print(" ");
          }*/
          reg3(0xB4 + offset, 0xC0, ch);
        }
        else
        {
          reg3(0xB4 + offset, 0xC0 + (d2 << 4), ch);
        }

        break;
      case 59: //LFO toggle
        lcdinit();
        LCD.print("LFO");

        for (int i=0; i<4; i++)
        {
          if ((chctl & (1 << i)) > 0)
          {
            buf = voice[ch].op[i].d1r;
            buf += (voice[ch].op[i].lfo = d2) << 7;

            reg3(0x60 + (i * 4) + offset, buf, ch);
          }
        }
        break;
      case 51:
        if (d2 == 127) chctl |= 1 << 0;
        if (d2 == 0) chctl &= ~(1 << 0);
        break;
      case 52:
        if (d2 == 127) chctl |= 1 << 1;
        if (d2 == 0) chctl &= ~(1 << 1);
        break;
      case 53:
        if (d2 == 127) chctl |= 1 << 2;
        if (d2 == 0) chctl &= ~(1 << 2);
        break;
      case 54:
        if (d2 == 127) chctl |= 1 << 3;
        if (d2 == 0) chctl &= ~(1 << 3);
        break;
      case 58:
        break;
      }
      break;
    case ProgramChange:
      d1 = MIDI.getData1();
      channel = MIDI.getChannel();

      lcdinit();
      LCD.print("YM2612 Chan. ");
      LCD.print(String(channel, DEC));
      lcdline2();
      LCD.print("Instrument: #");
      LCD.print(d1);
      if ( MODE == MODE_POLY)
      {
        for ( int i=0; i <= 12; i++ )
          selectinst(d1, CHANNEL[i]);
      }
      else
        selectinst(d1, CHANNEL[channel]);
      break;
    case PitchBend:
      d1 = MIDI.getData1();
      d2 = MIDI.getData2();

      pitchbend(d2 - 64, CH1);
      pitchbend(d2 - 64, CH2);
      pitchbend(d2 - 64, CH3);

      break;
      /*default:
       
       d1 = MIDI.getData1();
       d2 = MIDI.getData2();
       
       
       
       lcdinit();
       LCD.print(String("Message, type=") + String(type, DEC));
       lcdline2();
       LCD.print(String("data = ") + String(d1, DEC) + " " + String(d2, DEC));
       LCD.print(" ");
       LCD.print(String(AfterTouchChannel, DEC));
       */
    }
  }
}

edit: found another thread about this: http://gendev.spritesmind.net/forum/viewtopic.php?t=1122
 
Last edited:
Status
Not open for further replies.
Back
Top