11

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!

Steve Cooley
  • 1,485
  • 1
  • 11
  • 16
  • 1
    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. – jippie May 11 '13 at 06:56
  • 1
    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. – Andy aka May 11 '13 at 12:19
  • Yep, all of it. – Steve Cooley May 11 '13 at 16:03
  • 2
    That is a sweet looking case you have made! – shuckc Aug 02 '13 at 14:59
  • thanks! that's now the *old* case. I bought a laser and fume extractor, and now can produce acrylic cases that I like a lot better. They're *way* easier to make, too. – Steve Cooley Aug 02 '13 at 22:07
  • 3
    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. – Phil Frost Aug 03 '13 at 01:29
  • It's really not about the money. I know that's probably hard to understand, but my time is more valuable, so I need to operate under the arduino IDE. I'm sorry if my desire to do this with the arduino IDE is offensive to your sensibilities. I can respect that. I just have no reasonable way to pursue electrical engineering at the level you are at if my desired way seems lame and expensive. I also buy premade furniture. And premade food sometimes. It's really about trading cash for expertise and convenience. Believe me, it's frustrating to know as much as I do and also know it's not enough. – Steve Cooley Sep 24 '13 at 19:55

4 Answers4

5

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.

fm_andreas
  • 813
  • 5
  • 6
3

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.

Connor Wolf
  • 31,938
  • 6
  • 77
  • 137
  • 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. – Oli Glaser Aug 07 '13 at 02:23
  • @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. – Connor Wolf Aug 08 '13 at 08:03
  • 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 :-) ) – Oli Glaser Aug 08 '13 at 08:18
1

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.

crunkchitis
  • 111
  • 2
0

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.

Rob Starling
  • 475
  • 3
  • 12
  • 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. – Steve Cooley Aug 08 '13 at 00:00
  • 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. – Rob Starling Aug 08 '13 at 07:18
  • @SteveCooley - Sounds like you need to look into IO multiplexing/button-matrixes. You shouldn't need a whole dedicated IO line per button. – Connor Wolf Aug 08 '13 at 07:57
  • @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. – Connor Wolf Aug 08 '13 at 08:00
  • 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. – Connor Wolf Aug 08 '13 at 08:01
  • True, although µCs are kinda ridiculously cheap now, and an ATXMega128B is under $5, (i believe) has the # of I/O lines needed, and even has built-in hardware to speak USB if that's how you'd prefer to connect to the sequencer — http://www.mouser.com/ProductDetail/Atmel/ATXMEGA128B1-AU/?qs=sGAEpiMZZMvqv2n3s2xjsVQIURldyCCHxLsOQm%2f%252b9jo%3d – Rob Starling Aug 09 '13 at 16:39
  • I won't waste our time explaining my project design decisions here. Don't bother replying either. It's really pointless. – Steve Cooley Aug 10 '13 at 07:54