{"id":151,"date":"2005-11-04T01:08:29","date_gmt":"2005-11-03T23:08:29","guid":{"rendered":"http:\/\/blogs.bl0rg.net\/netzstaub\/?p=151"},"modified":"2005-11-04T01:08:29","modified_gmt":"2005-11-03T23:08:29","slug":"usb-midi-controller","status":"publish","type":"post","link":"https:\/\/blogs.bl0rg.net\/netzstaub\/2005\/11\/04\/usb-midi-controller\/","title":{"rendered":"USB MIDI Controller"},"content":{"rendered":"<p>I use Ableton live a lot to jam and to DJ, and using the mouse to<br \/>\ncontrol all the parameters got in the way quite often. You can&#8217;t<br \/>\nselect a scene and turn a knob at the same time, switching between<br \/>\nviews was tiresome. I first thought about building a <a href=\"http:\/\/www.ucapps.de\/\">midibox<\/a>, but then decided to build my own midi system<br \/>\nusing an AVR microcontroller, which I&#8217;m more used to. <a href=\"http:\/\/www.wenger-online.de\/blog\/\">Blueloop<\/a> helped me a lot<br \/>\nwith the schematics, showing me a nice trick to control LEDs using the<br \/>\nsame matrix as for the switches (this is not integrated yet,<br \/>\nthough).<\/p>\n<div align=\"center\"><a href=\"http:\/\/bl0rg.net\/~manuel\/midipult\/midipult-aussen.jpg\"><img decoding=\"async\" alt=\"Alt Tag Added during WordPress Conversion\" src=\"http:\/\/bl0rg.net\/~manuel\/midipult\/midipult-aussen-klein.jpg\" \/><\/a><\/div>\n<p><!--more--><\/p>\n<p>The midi controller is quite simple. It features 33 switches and 56<br \/>\npotentiometers. The switches and potentiometers are laid out in a<br \/>\nmatrix. You can select a horizontal row by pulling down a port to 0 V,<br \/>\nand then reading the switch values on another port, or by going<br \/>\nthrough the ADCs in the case of the potentiometers. The actual circuit<br \/>\nfor the microcontroller is really really simple. There is an<br \/>\noscillator and a resistor for the MIDI output. The rest of the I\/O<br \/>\npins go to a pinhead which connects the front panel to the circuit<br \/>\nboard.<\/p>\n<p>There is a second circuit board in the midi controller that<br \/>\nconverts the MIDI output of the first circuit to USB. It was an old<br \/>\nUSB programmer board I built 2 years ago that was lying around. I<br \/>\ndirectly connected the power of the first circuit board to the USB<br \/>\nconnector, and connected the MIDI output to the RX pin of the USB<br \/>\nprogrammer. I had no 12 Mhz oscillator (the one which I normally use),<br \/>\nso I used a 20 Mhz quartz out of an old network card. The board is<br \/>\nreally simple too, and consists of a PDIUSBD11 chip by Phillips, which<br \/>\ngets commands over I2C and outputs USB, and an ATMEGA8<br \/>\nmicrocontroller. Sadly, the PDIUSBD11 is not manufactured any more,<br \/>\nwhich is kind of sad, as it is the only USB controller I am aware of<br \/>\nthat uses I2C, which spared pins on the microcontroller side. I<br \/>\nimplemented the <a href=\"http:\/\/www.usb.org\/developers\/devclass_docs\/midi10.pdf\">USB MIDI<br \/>\nspecification<\/a> out of the specification, and the controller seems<br \/>\nto work under Linux and Windows (there is a problem with MacOSX, but I<br \/>\nlast checked two years ago and haven&#8217;t tried\/debugged it since<br \/>\nthen). I can&#8217;t publish the whole code for the usb-midi adapter though,<br \/>\nas most of it (including the USB stack) was written by <a href=\"http:\/\/www.beecon.de\/\">Till Harbaum<\/a>.<\/p>\n<div align=\"center\"><a href=\"http:\/\/bl0rg.net\/~manuel\/midipult\/midipult-innen.jpg\"><img decoding=\"async\" alt=\"Alt Tag Added during WordPress Conversion\" src=\"http:\/\/bl0rg.net\/~manuel\/midipult\/midipult-innen-klein.jpg\" \/><\/a><\/div>\n<p>The matrices are soldered on the front panel, which I made out of a<br \/>\nplexiglas sheet through which I drilled holes for the switches and<br \/>\npots. I had no real problems with the plexiglas, except that you have<br \/>\nto drill quite slowly through it in order not to split it, and that I<br \/>\nhad to remove the melt plastic around the holes using a rasp. Each<br \/>\nswitch has a diode at one of its outputs, so that two knobs pressed at<br \/>\nthe same time can be distinguished on the controller side. The<br \/>\npotentiometers are connected to VCC one one side, to the select port<br \/>\non the other side, and the variable output is connected to the ADC<br \/>\nusing a diode. When selecting a switch or a pot, the select pin is<br \/>\nset to GND, all the other pins are set to VCC. Because of the diodes,<br \/>\nthe pressed switches or the pots which are not selected are always<br \/>\nhigh, and the diode won&#8217;t allow them to influence the signal.<\/p>\n<div align=\"center\"><a href=\"http:\/\/bl0rg.net\/~manuel\/midipult\/midipult-schematics.png\"><img decoding=\"async\" alt=\"Alt Tag Added during WordPress Conversion\" src=\"http:\/\/bl0rg.net\/~manuel\/midipult\/midipult-schematics-klein.jpg\" \/><\/a><\/div>\n<p><\/p>\n<div align=\"center\"><a href=\"http:\/\/bl0rg.net\/~manuel\/midipult\/midipult-verdrahtung.jpg\"><img decoding=\"async\" alt=\"Alt Tag Added during WordPress Conversion\" src=\"http:\/\/bl0rg.net\/~manuel\/midipult\/midipult-verdrahtung-klein.jpg\" \/><\/a><\/div>\n<p>The code on the microcontroller is almost as simple as the circuit<br \/>\nboard. A simple ring-buffer interrupt-based UART handling is used to<br \/>\nhandle outgoing MIDI data (the midi controller can&#8217;t receive MIDI<br \/>\nevents).<\/p>\n<pre>\n#define RB_SIZE 16\n#define MIDI_BAUD 31250UL    \/* 31.25 kbps *\/\n\nvolatile char tx_buf[RB_SIZE];\nvolatile char tx_wr = 0, tx_rd = 0;\n#define TX_INCR(a, b) (((a) + (b)) &amp; (RB_SIZE - 1))\n#define UART_TX_OK (UCSRA &amp; _BV(UDRE))\n#define TX_FREE (!(TX_INCR(tx_wr, 1) == tx_rd))\n\nSIGNAL(SIG_UART_TRANS) {\n  if (tx_rd != tx_wr) {\n    UDR = tx_buf[tx_rd];\n    tx_rd = TX_INCR(tx_rd, 1);\n  }\n}\n\nvoid uart_putc(char c) {\n  if (UART_TX_OK &amp;&amp; (tx_wr == tx_rd)) {\n    UDR = c;\n  } else {\n    while (TX_INCR(tx_wr, 1) == tx_rd) ;\n    tx_buf[tx_wr] = c;\n    tx_wr = TX_INCR(tx_wr, 1);\n  }\n}\n\nvoid uart_init(void) {\n  tx_wr = tx_rd = 0;\n  \n  \/* set baudrate *\/\n  UBRRL = (((F_CPU) \/ (16 * MIDI_BAUD)) - 1);\n  \/* enable TX, interrupts *\/\n  UCSRB = _BV(TXEN) | _BV(TXCIE);\n\n  sei();\n}\n<\/pre>\n<p>The static buffer &#8220;tx_buf&#8221; is used to buffer outgoing serial<br \/>\ndata. &#8220;tx_wr&#8221; and &#8220;tx_rd&#8221; are used as indices into the ring<br \/>\nbuffer. &#8220;TX_INCR&#8221; handles the indices when a character is added to the<br \/>\nring buffer. &#8220;TX_FREE&#8221; checks if there is free space in the ring<br \/>\nbuffer. &#8220;UART_TX_OK&#8221; checks if the serial interface is ready to send<br \/>\nanother character. &#8220;SIGNAL(SIG_UART_TRANS)&#8221; is the interrupt routine<br \/>\nthat is called when a character has successfully been sent. It checks<br \/>\nif the ring buffer is empty, and sends the next character if it is<br \/>\nnot. &#8220;uart_putc&#8221; is the API to the UART code, and directly sends the<br \/>\ncharacter if the serial interface is ready and the ring buffer is<br \/>\nempty. If not, it waits until there is space in the ring buffer, and<br \/>\nadds the new character to the ring buffer. &#8220;uart_init&#8221; handles the<br \/>\ninitialization of the serial interface, sets the correctly baudrate,<br \/>\nand enables TX and the TX_TRANS interrupt. Finally, interrupts on the<br \/>\nAVR are enabled.<\/p>\n<pre>\nvoid io_init(void) {\n  \/* disable watchdog *\/\n  WDTCR = 0;\n\n  \/* DD = 1, output *\/\n  \/* PORT = 1 wenn DD = 0, dann pull-up *\/\n  \/* portd = output *\/\n  DDRD = 0XFF;\n  \n  \/* enable ADC *\/\n  DDRA = 0;\n  PORTA = 0xFF;\n  ADMUX = _BV(ADLAR);\n  ADCSRA = _BV(ADEN)|_BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0);\n  \/\/ ADCSRA = _BV(ADEN);\n\n  uart_init();\n}\n<\/pre>\n<p>The routine &#8220;io_init&#8221; initializes the Atmel, first disabling the<br \/>\nwatchdog, then setting PORTD to output (which is not really necessary,<br \/>\nas the status of the port is set by the switch polling routine). The<br \/>\nADC is enabled by setting the PORTA to input, enabling pullup<br \/>\nresistors, and enabling the ADC in a 10 bit mode, and using AVCC as<br \/>\nreference voltage. Finally the UART is initialized.<\/p>\n<pre>\n\/*** MIDI zeugs ***\/<\/p>\n<p>#define MIDI_NOTE_ON     0x9<br \/>\n#define MIDI_NOTE_OFF    0x8<br \/>\n#define MIDI_AFTERTOUCH  0xA<br \/>\n#define MIDI_CC          0xB<br \/>\n#define MIDI_PROG_CHANGE 0xC<br \/>\n#define MIDI_CHAN_PRESS  0xD<br \/>\n#define MIDI_PITCH_WHEEL 0xE<\/p>\n<p>#define MIDI_STATUS_TYPE(c) (((c) &amp; 0xF0) &gt;&gt; 4)<br \/>\n#define MIDI_STATUS_CHAN(c) ((c) &amp; 0xF)<\/p>\n<p>static inline midi_is_status(char c) {<br \/>\n  return (c &amp; 0x80);<br \/>\n}<\/p>\n<p>void midi_send_note_on(unsigned char channel, unsigned char note, unsigned char velocity) {<br \/>\n  uart_putc((MIDI_NOTE_ON <\/p>\n<p>The midi controller sends three types of midi messages. When a switch<br \/>\nis pressed, it sends a note on message, when a switch is release, it<br \/>\nsends a note off message, and when a pot is modified it sends a<br \/>\ncontrol channel message. All three messages are three bytes long and<br \/>\nsent directly to the uart using \"uart_putc\".<\/p>\n<pre>\n#define LED_BUTTON_PORT     D<br \/>\n#define PORTLED_BUTTON_PORT PORTD<br \/>\n#define DDRLED_BUTTON_PORT  DDRD<br \/>\n#define PINLED_BUTTON_PORT  PIND<\/p>\n<p>#define SET_PIN_OUTPUT(port, pin) { DDR ## port |= _BV(pin); }<br \/>\n#define SET_PIN_INPUT(port, pin) { DDR ## port &amp;= ~(_BV(pin)); }<br \/>\n#define SET_PIN_INPUT_PULLUP(port, pin) { SET_PIN_INPUT(port, pin); PORT ## port |= _BV(pin); }<br \/>\n#define SET_PIN_LOW(port, pin) { PORT ## port &amp;= ~(_BV(pin)); }<br \/>\n#define SET_PIN_HIGH(port, pin) { PORT ## port |= _BV(pin); }<br \/>\n#define PIN_VALUE(port, pin) ((PIN ## port &amp; _BV(pin)) ? 1 : 0)<\/p>\n<p>#define NUM_BUTTONS 30<\/p>\n<p>#define BUTTON_STATUS(i) (button_status[(i)] &amp; 0xF)<br \/>\n#define SET_BUTTON_STATUS(i, val) { button_status[(i)] = button_status[(i)] &amp; 0xF0 | (val); }<br \/>\n#define LED_STATUS(i) ((button_status[(i)] &gt;&gt; 4) &amp; 0xF)<br \/>\n#define SET_LED_STATUS(i, val) { button_status[(i)] = button_status[(i)] &amp; 0xF | ((val)  1 (bounce?) -&gt; 2<br \/>\n   wenn knopf auf 1 dann nachricht senden (note on), wieder auf 0 (note off)<br \/>\n*\/<\/p>\n<p>#define BUTTON_ROWS 5<br \/>\nunsigned char button_status[BUTTON_ROWS * 8];<\/p>\n<p>void init_buttons(void) {<br \/>\n  int i, j;<br \/>\n  for (i = 0; i <BUTTON><\/p>\n<p>The button polling routines are a bit longer. The routine<br \/>\n\"ask_buttons\" polls through all the buttons, and calls the routine<br \/>\n\"ask_button\" to get the status of a single switch. The result of the<br \/>\npolling is stored in the array button_status. A simple state machine<br \/>\nis used to handle press\/release status. \"ask_button\" sets the<br \/>\ncorresponding select pin low, enables the input pin and enables the<br \/>\ninternal pullup resistor. The value is then read and handled. After<br \/>\nall buttons have been read, midi_buttons is called, which reads the<br \/>\nswitch status in the \"button_status\" array, and sends the<br \/>\ncorresponding midi message.<\/p>\n<pre>\n#define POTI_SEL_PORT        B<br \/>\n#define PORTPOTI_SEL_PORT PORTB<br \/>\n#define DDRPOTI_SEL_PORT  DDRB<\/p>\n<p>#define NUM_POTIS 64<br \/>\nunsigned char poti_status[NUM_POTIS];<\/p>\n<p>void init_potis(void) {<br \/>\n  int i, j;<br \/>\n  for (i = 0; i  MAX_THRESHOLD) return 0x7F;<br \/>\n  uint32_t bla = (uint32_t)((uint32_t)adc - (uint32_t)MIN_THRESHOLD);<br \/>\n  bla *= 0x7F;<br \/>\n  bla \/= 0xDFF0;<br \/>\n  return bla;<br \/>\n}<\/p>\n<p>void ask_poti(unsigned char sel_pin, unsigned char adc) {<br \/>\n  SET_PIN_LOW(POTI_SEL_PORT, sel_pin);<br \/>\n  _delay_us(50);<br \/>\n  ADMUX = _BV(ADLAR) | adc;<br \/>\n  ADCSRA |= _BV(ADSC);<br \/>\n  loop_until_bit_is_set(ADCSRA, ADIF);<br \/>\n  loop_until_bit_is_clear(ADCSRA, ADSC);<br \/>\n  last_adc = ADC;<br \/>\n  SET_PIN_HIGH(POTI_SEL_PORT, sel_pin);<br \/>\n  unsigned char val = convert_to_cc(last_adc);<br \/>\n  unsigned char poti = sel_pin + 8 * adc;<br \/>\n  unsigned char old = poti_status[poti] &amp; 0x7f;<br \/>\n  if ((val != old) &amp;&amp; ((val == 00) || (val == 0x7F) || (abs(val - old) &gt; 1)))<br \/>\n    poti_status[poti] = 0x80 | val;<br \/>\n}<\/p>\n<p>void ask_potis(void) {<br \/>\n  int i, j;<\/p>\n<p>  DDRPOTI_SEL_PORT = 0xFF;<br \/>\n  PORTPOTI_SEL_PORT = 0xFF;<\/p>\n<p>  for (i = 0; i <\/p>\n<p>The code to read the potentiometers is pretty similar, except that the<br \/>\nroutine \"ask_poti\" reads the value using the ADC. The diodes on the<br \/>\nvariable pins of the potentiometers have a voltage drop of 0.6V, which<br \/>\nwe account for by subtracting MIN_THRESHOLD from the read ADC<br \/>\nvalue. The value is then scaled to 0-127, and sent if it differs more<br \/>\nthan one from the previous value. The value is stored into the array<br \/>\n\"poti_status\". Finally, the routine \"midi_potis\" sends the value of<br \/>\nthe potis which have changed (signalled by the flag 0x80).<\/p>\n<pre>\n\/*** Main ***\/<\/p>\n<p>int main(void) {<br \/>\n  io_init();<\/p>\n<p>  init_buttons();<br \/>\n  init_potis();<\/p>\n<p>  DDRPOTI_SEL_PORT = 0xFF;<br \/>\n  PORTPOTI_SEL_PORT = 0xFF;<br \/>\n  DDRA = 0x00;<br \/>\n  PORTA = 0xFF;<br \/>\n  ADMUX = 0;<\/p>\n<p>  int i;<br \/>\n  for (i =0 ; i <\/p>\n<p>The main routine initializes the buttons, potis, I\/O, waits for 100<br \/>\niterations of the main loop to let the pots and switch bounce around,<br \/>\nand then loops asking for switch and pot status, and sending the<br \/>\ncorresponding MIDI messages. You can find the code for the project<br \/>\n<a href=\"http:\/\/bl0rg.net\/~manuel\/midipult\/\">here<\/a><\/p>\n<p>The main problem with the midi controller is that I used \"cheap\"<br \/>\npots (though it was quite expensive to buy 56), and some of them have<br \/>\nshort circuits or jump around or don't work at all. Another problem is<br \/>\nthat I probably will have to convert the linear output of the pots to<br \/>\nsomething more approaching logarithmic scale because the resolution in<br \/>\nthe high-end is too bad. Finally, another problem with the pots is<br \/>\nthat the value sometimes oscillate due to environment noise or so, and<br \/>\nthat using live in record mode tends to overwrite some parameter<br \/>\nautomation envelopes. The next version will probably use less pots,<br \/>\nmaybe some rotary encoders, though I have to find somewhere were I can<br \/>\nget them without being too expensive.<\/p>\n<p>All in all, it was a lot of work to build the midi controller, and<br \/>\nyou can buy the same stuff for around 120 EUR from Behringer. The next<br \/>\nversion will have some features that are not easily found. I<br \/>\nabsolutely want to be able to record \"automation macros\" directly on<br \/>\nthe hardware. Press \"record pattern A\", then move some pots and knobs<br \/>\nfor a few bars, stop the recording, and then be able to play in back<br \/>\n\"in sync\". The next midi controller I am building will be a bad-ass<br \/>\nstep sequencer with those macros features and other mad stuff. I'll<br \/>\npost about it when it's ready and working.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I use Ableton live a lot to jam and to DJ, and using the mouse to control all the parameters got in the way quite often. You can&#8217;t select a scene and turn a knob at the same time, switching &hellip; <a href=\"https:\/\/blogs.bl0rg.net\/netzstaub\/2005\/11\/04\/usb-midi-controller\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,19,10],"tags":[],"class_list":["post-151","post","type-post","status-publish","format-standard","hentry","category-bastelstaub","category-english","category-programmstaub"],"_links":{"self":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/posts\/151","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/comments?post=151"}],"version-history":[{"count":0,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/posts\/151\/revisions"}],"wp:attachment":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/media?parent=151"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/categories?post=151"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/tags?post=151"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}