11
\$\begingroup\$

I build these music sequencers.

enter image description here

Only it's not exactly a sequencer, it's a physical interface for a sequencer. The sequencer is an application that runs on a laptop that the sequencer connects to, this thing lets the user make drum loops on the fly. It's pretty fun, but it requires a laptop because the sequencer isn't 'on-board'.

What I would love is to do the sequencing on-board my device.

Now let's assume I know how to solve for class compliance for USB MIDI connectivity, and let's also assume I can figure out how to wire up an arduino to send MIDI notes from a 5 pin DIN port. What I'm most concerned about is tempo drift over time due to inconsistent timing in minute amounts over every run of the event loop.

Some things I know:

  1. You should not rely on delay() to control the tempo loop. Delay stops all operation of the firmware, and that can't work because i need to poll the physical user interface for changes while the sequence is running.

  2. Calculations based on millis() are better because the firmware can continue to operate and act when a certain count has elapsed.

  3. Even though none of my physical controls are triggering interrupt routines, some operations can delay the main loop() from running. If I design a function that waits for user input, that can obviously cause a problem of missing a "deadline" to act if the millis() count is way over. I know this problem is of my own design...

Questions:

A. Is the AVR-based arduino an appropriate microcontroller to poll a user interface and run a mission critical timing loop? I know there's an ARM based Arduino now that's a lot faster. Would a Teensy 3.0 be a better alternative? Both of these are 3.3V boards, so that's another set of issues to work with... but I'll ignore that for now.

B. Should I split the task into two microprocessors? One to handle polling and updating the user interface and one for mission critical timing loop.

c. Something else?

My main goal is to not have to use a computer at all. I also want to calculate for swing, but in this case, swing don't mean a thing if I ain't got a locked and timing accurate tempo. Thanks for your advice!

\$\endgroup\$
7
  • 1
    \$\begingroup\$ Arduino always sets up some interrupt routines, causing jitter. In many cases this is not a problem, but it is good to be aware of it. noInterrupts(); stops the jitter, but also stops all wanted interrupts. \$\endgroup\$
    – jippie
    Commented May 11, 2013 at 6:56
  • 1
    \$\begingroup\$ When you say "do sequencing on-board", does that mean set up the beats per bar, BPM and a tick-track on-board? Then presumably you want to remember the button presses that occured within the bar so that the "brains" of the device can feed midi-notes to your laptop? Then do you want to remove certain percussion sounds if you hit them again over the note that was previously recorded? Etc.. how far do you want to go? Storage of your beats? Creating a sequence of bars that correspond to a full track? Editing specific bars? Tempo alteration of specific bars? It all eats CPU so go for the best CPU. \$\endgroup\$
    – Andy aka
    Commented May 11, 2013 at 12:19
  • \$\begingroup\$ Yep, all of it. \$\endgroup\$ Commented May 11, 2013 at 16:03
  • 2
    \$\begingroup\$ That is a sweet looking case you have made! \$\endgroup\$
    – shuckc
    Commented Aug 2, 2013 at 14:59
  • 3
    \$\begingroup\$ In addition to what others have said, this looks like a think that maybe you intend to produce and sell. An Arduino costs $20, while an AVR costs $2. Not only will you gain the control over the hardware your application requires, but you will save a lot of money. \$\endgroup\$
    – Phil Frost
    Commented Aug 3, 2013 at 1:29

4 Answers 4

5
+50
\$\begingroup\$

Interrupts are your friend for timing sensitive tasks, but only if you put the timing critical aspects into the interrupt, and there are no other interrupts happening that have a higher priority. The microcontrollers on the "AVR-based" Arduino (e.g. the ATmega328P) have fixed interrupt priorities as detailed on page 58ff of the datasheet. So if you used TIMER2 COMPA as your critical timing interrupt and no other interrupts you should be OK (as it has the highest priority). If you also want to use lower priority interrupts, you need to make sure that all of them re-enable global interrupts when entering their interrupt service routine:

When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts are disabled. The user software can write logic one to the I-bit to enable nested interrupts. All enabled interrupts can then interrupt the current interrupt routine.

(p. 14 of the datasheet)

This is slightly different on ARM based Arduinos as their Cortex-M3 core has a "Nested Vector Interrupt Controller", where the priorities aren't fixed (can be set in software), and nested interrupt handling is the norm. So for timing critical applications, the ARM based Arduino would give you more flexibility. However, I don't think that's really necessary for your application.

The bigger question is really how easy these things can be implemented with the Arduino libraries. To get the best performance you will probably have to code outside the libraries to some degree, at least for the timing critical bits, i.e. avoid things like delay() or millis() altogether.

Whether or not you need to split depends on how much processing you intend to do. Again, going outside the libraries can potentially give you better performance.

\$\endgroup\$
3
\$\begingroup\$

This can, with the appropriate programming, most definitely be done on an ATmega328P (depending somewhat on the drum-loop's complexity. I'm assuming ~<50 drum events in the loop. Is that reasonable?).

Note that I said ATmega328P, not necessarily an Arduino.

The Arduino environment has a lot of default stuff going on in the background, that makes extremely deterministic programming (as you will need for something timing-critical) challenging.

The real question you need to ask here is how interested are you in programming, vs how interested are you in developing a instrument?

While I'm quite confident it's possible to do everything you want on a single ATmega (drum loop, multiple analog inputs, LCD, buttons, MIDI interface), the real question is how much work will it be to squeeze everything in? Again, do you want to learn to optimize embedded MCU code, or build instruments? It's quite easy to simply go to a faster MCU if needed, but you need to determine the MCU performance you need now, so six months of work in, you don't realize you can't quite get everything to work as fast as you need.


If I were you, the first thing I'd do is get it working without arduino stuff (basically, treat it as a raw ATmega, and use AVR studio or similar). Then, you can much more effectively analyse what kind of performance you need, and if the ATmega can manage it.

Once you're free of the arduino stuff, you're much more free to use different MCUs (they're generally more similar then different. If you can figure out one from it's documentation, you can probably do the same for others).

I've been working with the ATxmega devices a LOT recently, and they're really nice. You get three interrupt priorities, which make managing time-critical stuff waaaay easier. They're also really nice to work with (Sane peripheral designs! Convenient port structs! Etc...).

There are also the LPC devices from NXP, which are ARM based, as well as some of Atmel's ARM devices (as used on the Arduino Due), or the STM32 MCUs from ST. Any of these will have significantly more performance then an ATmega, or even an ATxmega.

The primary downside of a bigger, more powerful processor is price, but unless you're making thousands of those units, the per-unit assembly and manufacturing costs are so greatly going to exceed the cost difference (which will likely be only a few dollars) that it's basically irrelevant.

\$\endgroup\$
3
  • \$\begingroup\$ Nicely put - for a commercial product an Arduino is simply not the way to go - they are power hungry, slow, and the IDE is not designed for optimal (fast/small) code, rather convenience and easy learning. For less cost you could even have an STM32 F4 (32-bit Cortex M4 > 100MHz) or similar, though this would be overkill. I think something like one of the smaller PIC32's, Cortex M3s or an AVR32 is probably the way to go, as you mention. Numerous interrupt priorities, DMA, sophisticated peripherals, fast/low power and plenty of RAM makes it an easy choice compared to an Arduino. \$\endgroup\$
    – Oli Glaser
    Commented Aug 7, 2013 at 2:23
  • \$\begingroup\$ @OliGlaser - I think you need to clearly delineate between an Arduino and a ATmega. You can do small, fast code on an ATmega, and that ATmega can even be on an Arduino board. The Arduino "IDE", on the other hand, is one of the shittiest code editors I have ever used. On the other hand, the OptiBoot bootloader is very nice. Just because some parts are crap doesn't mean you should throw the whole thing away. \$\endgroup\$ Commented Aug 8, 2013 at 8:03
  • \$\begingroup\$ Absolutely - I was referring to the Arduino as a whole, board and IDE included - not the ATmega, which I'm sure is as good as any other comparable uC (PIC16/18F, etc) I would have included it in my list but I think with the price between 8-bit and 16/32-bit being so close nowadays it's probably a good idea to spend an extra $1 and know you have the processor power to spare (unless as you mention, we're talking huge numbers and built to the absolute lowest price, but then I doubt an Arduino would have been considered :-) ) \$\endgroup\$
    – Oli Glaser
    Commented Aug 8, 2013 at 8:18
1
\$\begingroup\$

I needed to read up on timers before I started thinking about timing accuracy (also building a midi step sequencer with an arduino, although it's guaranteed to look less cool than those^^). This series of articles has been the most informative:

http://maxembedded.com/category/microcontrollers-2/atmel-avr/avr-timers-atmel-avr/

Right now I think that my solution to getting precise timing will be.

A. Use the AVR arduino

B. Keep the task on one microprocessor

C. Wisely use prescalers, timers and interrupts to get the precision that's needed.

UPDATE

Using the basic midi tutorial for Arduino and after looking at this article on timers and prescalers, the following code is what I came up with. The code uses timer1 and CTC mode to play a midi note on every quarter second and a note off every quarter second (which should be exactly 120 bpm). Sadly, this is still coming in just slower than 120bpm though this is the closest I've gotten...

// Includes
#include <avr/io.h>
#include <avr/interrupt.h>

int last_action=0;

void setup()
{
    //  Set MIDI baud rate:
    Serial.begin(31250);

    // initialize Timer1
    cli();          // disable global interrupts
    TCCR1A = 0;     // set entire TCCR1A register to 0
    TCCR1B = 0;     // same for TCCR1B

    // set compare match register to desired timer count:
    OCR1A = 15624;
    // turn on CTC mode:
    TCCR1B |= (1 << WGM12);
    // Set CS12 bits for 256 prescaler:
    TCCR1B |= (1 << CS12);
    // enable timer compare interrupt:
    TIMSK1 |= (1 << OCIE1A);
    // enable global interrupts:
    sei();
}

void loop()
{
    // do some crazy stuff while my midi notes are playing
}

ISR(TIMER1_COMPA_vect)
{
  // Turn notes on
  if (last_action == 0) {
    send_note(0x90, 60, 0x45);
    last_action = 1;

  // Turn notes off
  } else {
    send_note(0x90, 60, 0x00);
    last_action = 0;
  }
}

//  plays a MIDI note
void send_note(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}

UPDATE

I've been struggling with this for ~ 24 hours now and finally got some answers from the forum. I think that the code that I used above ^^ is pretty good. Using the ISR, using the CTC mode and prescalers etc. After reaching out to the forum I think that the solution is less about attaining precision on the midi sequencer, but getting my whole hardware setup (my synthesizers and samplers) hooked up to the same midi clock, whether or not the clock comes from the Arduino.

\$\endgroup\$
0
\$\begingroup\$

Depending on how gradually you want to make the transition from a tethered computer to a µC-based system, you might consider putting a Raspberry Pi inside that box ($25-35 retail). That way you can have a full (albeit low-powered) linux-based computer with USB ports and GPIO pins.

\$\endgroup\$
7
  • \$\begingroup\$ I'm sure there are expansion shields or whatever they call them for the Pi, but the stock board has 17 GPIO pins. I'm using every single pin on the arduino mega. 31 tacts + 30 LEDs, 10 analog in. 70+ I/O. \$\endgroup\$ Commented Aug 8, 2013 at 0:00
  • \$\begingroup\$ Oh, i meant if the immediate goal was to remove the external computer, you could keep the "sequencer [that] is an application that runs on a laptop" and run it on the Pi, internally connected to your existing system the same way it's connected now. \$\endgroup\$ Commented Aug 8, 2013 at 7:18
  • \$\begingroup\$ @SteveCooley - Sounds like you need to look into IO multiplexing/button-matrixes. You shouldn't need a whole dedicated IO line per button. \$\endgroup\$ Commented Aug 8, 2013 at 7:57
  • \$\begingroup\$ @SteveCooley - Hell, you don't even need a button matrice, really. You could do ALL your digital IO using only 4 rPi pins. Just hang all the buttons and LEDs off some shift registers (parallel-serial for the buttons, serial-parallel for the LEDs), and drive the shift registers from the rPi's SPI port. You should easily get > 1Khz update rates for the entire matrix with the help of the SPI hardware. \$\endgroup\$ Commented Aug 8, 2013 at 8:00
  • \$\begingroup\$ If the only reason you're using an Arduino Mega is the IO, you're spending a lot of money on something that can be done very easily with a few external devices, for <$3. \$\endgroup\$ Commented Aug 8, 2013 at 8:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.