Arduino Drum Platform Is Fast

Drums are an exciting instrument to learn to play, but often prohibitive if there are housemates or close neighbors involved. For that problem there are still electronic drums which can be played much more quietly, but then the problem becomes one of price. To solve at least part of that one, [Jeremy] turned to using an Arduino to build a drum module on his own, but he still had to solve yet a third problem: how to make the Arduino fast enough for the drums to sound natural.

Playing music in real life requires precise timing, so the choice of C++ as a language poses some problems as it’s not typically as fast as lower-level languages. It is much easier to work with though, and [Jeremy] explains this in great detail over a series of blog posts detailing his drum kit’s design. Some of the solutions to the software timing are made up for with the hardware on the specific Arduino he chose to use, including an even system, a speedy EEPROM, hardware timers, and an ADC that can sample at 150k samples per second.

With that being said, the hardware isn’t the only thing standing out on this build. [Jeremy] has released the source code on his GitHub page for those curious about the build, and is planning on releasing several more blog posts about the drum kit build in the near future as well. This isn’t the only path to electronic drums, though, as we’ve seen with this build which converts an analog drumset into a digital one.

53 thoughts on “Arduino Drum Platform Is Fast

  1. “Playing music in real life requires precise timing, so the choice of C++ as a language poses some problems as it’s not typically as fast as lower-level languages.”

    Yeah, right..

    1. Yeah, seriously? Most music software is written in c++ or a higher level language.

      Also I’m fairly sure his 150k samples/second is way higher than it needs to be; I’m sure that’s a lot higher than what my Roland kit claims.

      So I think the timing issues are probably in his code or design rather than the language or ADC sample speed.

      1. Agreed.

        Even Samplerbox, which is written in Python, is fast enough for playing drums.

        Also using samples that don’t have silence at the beginning and start right at the attack might help a lot.

      2. Exactly. The language doesn’t really matter.
        If you read more about the project, you’ll see that I’ve made sure that C and C++ compile down to the exact same executable file.
        I think it’s safe to say we’ve know for a while that C++ is not “slower” than C, as the compiler does a very good job of optimizing everything for us.

        It’s possible to reach 150k samples/sec, but in practice, I use 75k samples/sec, which means I get about 9000 samples / sec per input (there are 8 inputs on the board).

        The timing issues are resolved using a timer, which guarantees samples to be converted at a precise rate, but also, using the ATMega4809 event system, to ensure the ADC uses virtually zero CPU cycles.
        The Github link doesn’t appear to be correct, the Arduino code can be found here: https://github.com/SpintroniK/exadrumino-Nano-Every

        1. How are the ADCs on the ATMega4809? The older AVRs where pretty bad on ADC performance, with noise levels being 4 bits or more. And I wonder how the latest greatest compare to that.

          I generally stay away from AVR chips if I needed an ADC because of it.

          1. There’s one 16-channel ADC.
            It’s not too noisy, quite fast (up to 150ksps) and can be configured to get a resolution of 8 or 10-bit.
            In 8-bit mode, there’s one or two bits of noise. Of course, the external voltage reference is critical!
            Another good thing is that it can be used together with the event system of the ATMega4809.
            That means, you can trigger new sample conversions using a timer or another hardware peripheral, which is pretty cool.
            If you want more info about how I used it in this particular project, please check this out: https://news.exadrums.com/article/new-arduino-nano-every-board-part4

    2. I get the feeling people forget that the C++ reaches precisely as low as plain old C does, so unless the author is talking about assembler I’m intrigued to know what his idea of “lower level languages” (plural) actually consists of.

      1. It reaches just as low, but it can reach a lot higher, so programs not written to run optimally on low-performance processors won’t run well, which is where the misguided impression of C++ as “slower” than C comes from.

    3. As Godbolt has proven over and over, C++ can be just as performant as C, especially with modern GCC or clang. It’s not like one’s C gets compiled line-for-line anyways, looking at how the compiler optimizes your code can be super illuminating.

      It’s just that when one gets sloppy with some of higher-level features available in C++ that it can become unsuitable for low-performance MCUs.

      C: Fairly powerful, and it’ll be fairly obvious once you start writing clunky code
      C++: Can be written as simply and with as much performance as C, or you can pull in one of its myriad fancy features and look at what the compiler spits out to see if you’re being clever or just shooting yourself in the foot.

      I say this as a much bigger fan of C than C++ for my personal embedded projects. However, C++ can do just as well and, used by people more knowledgeable than I am, can enable really cool things.

  2. Going as cheap as possible is an interesting exercise on its own – but the bar has been lowered quite a bit already to a complete kit at 179€ https://www.thomann.de/de/millenium_hd50_edrum_set.htm
    (of the category sortof music instruments, not kids toys)

    Even replicating the functionality would require more than just a single sample per drum – volume variation, duration variation, some handling of multi-hits while the first hit is still playing?

    I applaud the learning exercise and ambition!
    As a logo branded open source project for others to be useful – long way to go…

      1. There *aren’t* kits with ok pads but poor sound processors.

        Reason being, it’s super cheap to put good samples on a kit, and audio synths are cheap. HAD is covered with great homemade synths running on cheap processors like AVRs, ESP32s, etc.

        The cost of a decent kit is in the pads. Bought a drum kit recently, and the only reason we spent thousands not hundreds is the pads.

    1. Beware of those cheapo edrum sets. Latency, pieces sizes and position and samples quality aside (they suck also in much more expensive sets), they often leave out important features that we would take for granted in any decent musical instrument made in the last 30 years. For example, that Millennium set lacks both audio line out and MIDI out (no, USB is not a proper MIDI out). In other words is more like a nice gift for kids sold in the wrong category.

      Millennium isn’t alone in leaving out details as this practice is very common at all levels among electronics musical instruments manufacturers, Roland being among the major offenders: they make really great gear, but carefully leave out small details that would have cost next to nothing to keep, but will accelerate the instrument planned obsolescence, so that you soon will want to buy the “next version” which magically will include that feature, but lack others you will only discover later.

    1. His blog from May 1st on covers the addition of MIDI output from his board, so it is usable as a simple MIDI interface. As to why he didn’t start by putting that in first – well, his project was working quite nicely for him without MIDI so…

    2. This is a solid point. He’s using standard pads, which is good, and outputting to midi would allow any synth to be used. I’m kinda surprised if there’s nothing doing this already, but perhaps not reading the analogue data from the pads.

    1. Piezos. You strike the pad, the piezo converts the strike to a signal. The softer the strike the weaker the signal. The harder the strike the stronger the signal. The basics of from pad construction has been basically that since the first e-drum.

  3. “Playing music in real life requires precise timing, so the choice of C++ as a language poses some problems as it’s not typically as fast as lower-level languages”

    From the blogpost, this sounds like it should’ve said javascript instead of C++

    1. It is a C++ program for the Arduino Nano Every, see https://github.com/SpintroniK/exadrumino-Nano-Every
      And again, there’s no reason why it would be slower because it’s C++.
      The C++ code produces the same binary as an equivalent C program.
      The compiler is very good at optimizing everything, and I’ve checked many times whilst writing the code: C++ is as fast as C, it can even be faster in some cases, simply because the compiler is very clever.

  4. Isn’t the MIDI message going to be the slowest point anyhow? The fastest you can send a MIDI message containing a note on and a note off is something like 25 kHz – though there is no point since the result wouldn’t be audible. The Arduino can run loops around that.

    1. Indeed, sending MIDI message is crazy slow!
      That why the ADC uses the event system of the ATMega4809 (i.e. it is triggered by a timer and uses pretty much zero CPU cycles), and the conversion results are read in an interrupt.
      The interrupt takes a few CPU cycles, and I also use it to process the piezo signal, and compute the trigger state.
      The MIDI messages are send in the main loop of the program.

      Sending MIDI messages doesn’t take much CPU cycles either, in fact, once the data has been written to the register, most of the time is spent waiting for the data to be transmitted.
      If you’d like to know more about how it works, here’s the code of the main function, with the ADC event and interrupt, as well as the loop that’s used to send MIDI data: https://github.com/SpintroniK/exadrumino-Nano-Every/blob/main/main.cpp

      1. The question is, why is the timing of the sampling time so critical when you’re spending most of the time waiting for the USART to push bits out of the buffer, and the MIDI timing is the more important part for actually playing the instrument anyways?

        You could get away with 1-5 ms granularity or jitter with the MIDI transmission timing and not notice a difference, and up to 12 ms of lag between user input and MIDI output before professional musicians and studio technicians would start to complain about too much input lag. In other words, you only need to put the notes out at about 200 – 1000 Hz.

        So why do you need to sample the inputs so fast?

        1. If it were me – since there’s already an op-amp in the loop for amplifying the piezo signal – I would have simply built it with a high-pass filter and a peak detector circuit with a suitable decay time. It’s only couple more passive components per input; put all the heavy lifting on the analog side where it happens effortlessly.

          The MCU has standard voltage thresholds with known hysteresis in the digital inputs, which means you can tune the analog side to produce external interrupts for the MCU completely without using the ADC to “poll” the inputs all the time.

          1. Good point, but I wanted the board to be Raspbery Pi-compatible, and big SMD components to make it easy to solder by hand. So there’s not a lot of space, given it’s a single-side board.

            The problem with the peak detector, and the high-pass filter, is that they distort the signal, so what you get is not the actual piezo signal.
            I wanted to keep the exact signal, and be clever about the processing to extract the information I need in the shortest time possible.

            The op-amps clamp the signal, so that there’s no need for protection diodes.
            They’re also used to add a DC offset to the signal, so that it is half the ADC reference voltage.
            Thanks to that, the signal I get is exactly the piezo voltage centered around half the supply voltage.

            Of course, this made things difficult in the code… But I managed to have a high-pass filter and peak detector that work pretty well. Plus, it meets my requirements as doing all that takes less than 14µs.

          2. Technically, all the information is in the envelope of the signal so you can get away with a simple class-C transistor amplifier instead of an op-amp. It doesn’t matter that the amplifier clips half the signal away because it’s a “mirror” of the other half. As much as it isn’t, there’s no interesting information that you could use anyhow. What you want to know is how much and for how long to determine timing and intensity.

            So if you’re going for absolute minimalism, the parts count can be reduced to 1 capacitor, 2-3 resistors, 1 transistor. Add a trimpot for negative feedback gain trimming and you’re set. Works for either use case: signal peaks can hit digital input voltage levels to trigger interrupts, or you can poll with the ADC.

          3. I wouldn’t say that it doesn’t matter if we clip the negative part of the piezo voltage.
            For instance, my Roland pad has a lot of dynamics on the first negative peak, but the second peak, which is positive, doesn’t. Plus, if you want to do some more advanced stuff, such as positional sensing, you do need to keep to whole waveform (positive and negative peaks).

            Even though that’s not something I currently do, the board can be used with other microcontrollers than the Arduino Nano Every, for instance, a more powerful STM32, which would allow positional sensing computations. That way, the board can be future-proof as well.

            I considered using a common collector amplifier, thinking it would require less components, but it’s not such a good idea.
            First of all, they barely reduce the board size, as there’s always some space between a transistor and its neighbor.
            The other drawback is that transistor parameters vary a lot from one to another, so you’ll never get the same gain, and response… Plus using op-amps makes the board easy understand, to reproduce and to learn. I think it’s important that people see the board as something “simple”.

          4. >For instance, my Roland pad has a lot of dynamics on the first negative peak, but the second peak, which is positive, doesn’t.

            So reverse the piezo wires then?

          5. > transistor parameters vary a lot from one to another, so you’ll never get the same gain

            You add a bit of negative feedback anyways to stabilize it, otherwise you’d get a gain of 200-400 instead of the 2 that you need for this signal and your ADC input would simply saturate.

          6. > Then don’t reverse the piezo wires?

            But if I do so, not all pads will work…
            Some piezos will have the dynamics in the negative values, others in the positive voltages.
            The only alternative is a full-bridge rectifier, or something that behaves the same way (gives you the absolute value).

            > You add a bit of negative feedback anyways to stabilize it, otherwise you’d get a gain of 200-400 instead of the 2 that you need for this signal and your ADC input would simply saturate.

            That’s still not as straightforward as you think.
            In theory, yes it works well, in practice, the base impedance depends on beta, the output DC voltage depends on VBE, etc.
            With the op-amp, and precise resistors (which is pretty easy to find) I get a DC offset of 512 +/- 1 when I use the ADC in 10-bit mode.
            And I get that same value for all the piezos.
            I highly doubt you can get something that good with transistor. And since that’s actually something I tried, I can even confirm that you don’t get that unless you can 8 very similar transistors, which is difficult in practice.

        2. That’s the thing, once the data has been written to the USART buffer, all we do is wait.
          So it doesn’t matter if an interrupt gets fired, as we are just waiting anyway.
          That’s why the ADC data is processed in an interrupt, so that USART transfers don’t get in the way of ADC conversion and signal processing.

          In turn, the USART transfer doesn’t block the ADC conversions and signal processing, so we get 75ksps even though the USART is set-up to 115 200 bps.

          The reason why the inputs are sampled fast is that it’s required to get at least a few samples per milliseconds to process the piezo signal properly.
          That way, I can get a very good estimation of the peak value.

          I believe if you ignore the USB transfer, the latency is less than a 1ms, since it’s only determined by the baud rate of the USART.

          1. >The reason why the inputs are sampled fast is that it’s required to get at least a few samples per milliseconds to process the piezo signal properly. That way, I can get a very good estimation of the peak value.

            That’s not strictly necessary.

            Suppose you already clip the lower half of the signal away on the analog side and leave the positive half of the signal starting from 0V up. The chip has a successive approximation ADC which has some input capacitance, which you can use as an integrator of sorts by putting a big resistor in front of it and increasing the sampling time to be as long as the time interval you want to integrate over. The result is the mean signal level over that time interval.

            If you take a short sampling time and a very fast sampling rate, you’re recording small slices of the input signal rapidly just to add them all up in software. If you take a long sampling time and a slow sampling rate, you’re doing the same thing in the analog domain.

            It’s also likely that the input impedance of such “RC-filtered” ADC is so high that you don’t need the op-amp for a buffer. You also don’t need to worry about exceeding input voltage limits, because the IC’s internal diode clamps can handle the resulting peak current. Seeing that your circuit gain is about 2, it’s likely that you can just use a lower voltage reference in the AVR to get a good strong signal and reduce the parts count to exactly two: the piezo and the resistor.

          2. Partial answer here: https://hackaday.com/2022/06/28/arduino-drum-platform-is-fast/#comment-6488178

            In short: I don’t want the electronics to alter the piezo voltage at all, I’d rater process the raw signal, if that’s possible. It turns out it is possible, ans the Arduino Nano Every is powerful enough to do so. Other boards, like the STM32G031K8 which I happen to own, are compatible with the board. With more powerful microcontrollers like the STM32 family or other Arduino Nano pin-compatible boards, you can do more things, such as positional sensing etc.

            If the board were to filter some frequencies that are useful for positional sensing, then it would be tied to the Arduino Nano Every, which is something I didn’t want.
            So, yes, it requires more CPU cycles to perform the high-pass filter, peak detection, etc., but on the other hand, it makes the board usable with other microcontrollers.
            And it seems to be working pretty well that way. I’ve played drums on my Roland TD17 and didn’t notice a single glitch.

            The ATMega4809 ADC’s input resistance isn’t that high. It’s something like 10k, plus a capacitor that makes it act as a low-pass filter.
            The voltage reference I use is the 3.3V of the Arduino Nano Every.
            It’s not too noisy, and saved me a few components, which reduced the board cost.

          3. You’d add more impedance to the input to slow it down to the desired integrating window, and knowing how much you have (because you chose it) allows you to compute what the original signal was or would have been.

            There’s a difference in philosophy between trying to maintain “fidelity” where you never actually do what you think you’re doing, and knowing that you’re always distorting the signal somehow and accounting for the fact that you do.

          4. Point in case: aliasing. You’re trying to detect a very rapid peak using a discrete sampling system. The peak that you measure is probably not going to be the peak that you got even with the “non-distorted” input because you need a really mad sampling rate to capture it. Of course you won’t actually notice the difference because you can’t accurately gauge how hard you actually hit the pad.

          5. > You’d add more impedance to the input to slow it down to the desired integrating window, and knowing how much you have (because you chose it) allows you to compute what the original signal was or would have been.

            Not sure I understand what you mean…
            It sounds that you’re saying something like: “filter the signal, and then process it to recover the original signal”. Not sure I’m getting the point

            > There’s a difference in philosophy between trying to maintain “fidelity” where you never actually do what you think you’re doing, and knowing that you’re always distorting the signal somehow and accounting for the fact that you do.

            I’m not trying to maintain fidelity, I know I do so. I checked all the waveforms at the input and output of the op-amp stage using my oscilloscope. There’s no doubt, the signal undergoes nearly no distortions whatsoever.
            I don’t understand why I would add components to the board to filter the signal to save a few microseconds. Unless I’m missing something, and I would actually get something very interesting in return?

          6. Which is why people have worked hard to find that sample rates of about 8ksps per input are sufficient to detect peaks accurately.

            I mean, the values I used are from the guy who made eDrumIn.
            I believe it’s considered as a reference, and has been proven to work pretty well, so I’m quite confident with the choices I’ve made.
            Plus, like I said, I’ve tested the whole thing with my Roland TD-17, and honestly, it works very well.

            And again, the circuit is the result of quite a bit of work, and a lot of testing.
            I wouldn’t dare to submit something that doesn’t work, or isn’t well thought to HaD.

            I think I understand part of what you’re saying, but it doesn’t sound like we’ve seen the same piezo signals. They are not that “rapid”, especially it takes quite some time to get to a peak, and also quite a bit of time to fall back to zero. With 10 samples per millisecond, you definitely get the peak value withing +/-10% or less.

            And like I already said, if you get a more powerful µC, such as an STM32, you’d get a much higher sample rate, more processing power, and the µC would be compatible with the board.
            Which, like already said, would allow you to make positional sensing work, which you may not be able to do if you low-pass filter the signal a bit too much.

          7. >I checked all the waveforms at the input and output of the op-amp stage using my oscilloscope. There’s no doubt, the signal undergoes nearly no distortions whatsoever.

            The point where you hook the oscilloscope up to the piezo adds something like 50-200 pF of load to it, which distorts the signal. You’re not getting what you think you’re getting. Of course the op-amp will faithfully reproduce the input to the output, but that’s the distorted version.

            The point is that your strict timing requirements are not warranted, because the velocity and timing information – for the part that matters – can be obtained by integrating over a much longer period of time. You don’t actually need the original signal, but simply something that changes in proportion to the original signal in a well defined way.

            The “dynamics” from the individual peaks are correlated with the overall envelope shape, so you can measure that instead. Then you can spend you CPU cycles to do more interesting stuff and add other features rather than just input capture.

          8. >something that doesn’t work

            Obviously it works, and it’s not a wrong way to do it for that matter, but you have to be careful not to do cargo cult science with your stuff, thinking that it works because of the concepts you applied rather than despite the fact. There’s always other solutions that may apply better.

          9. >Not sure I understand what you mean…

            You slow the ADC down to a known RC constant by adding extra resistance to its input. That way you know the integrating time constant of your input, which allows you to average the input over a longer sample time. You can then estimate the true peak amplitude if you want to, but you don’t have to because the information you’re looking for is already there in the sample values – just in different “units”.

          10. > The point where you hook the oscilloscope up to the piezo adds something like 50-200 pF of load to it, which distorts the signal. You’re not getting what you think you’re getting. Of course the op-amp will faithfully reproduce the input to the output, but that’s the distorted version.

            My oscilloscope probe is 12pF.
            Most drum pad piezos have an output capacitance of 20-25nF.
            I don’t think 12pF, which is 2000 times less than the piezo capacitance would have a big impact on the pizeo voltage.

            > The point is that your strict timing requirements are not warranted, because the velocity and timing information – for the part that matters – can be obtained by integrating over a much longer period of time. You don’t actually need the original signal, but simply something that changes in proportion to the original signal in a well defined way.

            I did try to add a capacitor in parallel with the feedback resistor to add a low-pass filter, it doesn’t make a difference. The reason is, piezo responses are not that fast. The piezo itself is already a low-pass filter, and in my case, it filters enough high frequencies so that I don’t need an extra low-pass filter.

          11. > Obviously it works, and it’s not a wrong way to do it for that matter, but you have to be careful not to do cargo cult science with your stuff, thinking that it works because of the concepts you applied rather than despite the fact. There’s always other solutions that may apply better.

            I don’t know what makes you think that.
            Of course, the design of the circuit started with the theory.
            But then I made a few prototypes on my breadboards, then got some PCBs just for prototyping a dual piezo pad.
            And only after that worked, I tested the whole thing with the Arduino.
            After testing the whole thing again and again various pads (mesh, rubber, and cymbals), I tested it directly on the Roland TD-17 kit.
            And that’s only when all those things were validated that I started to design the final board.

            So I would say, it’s been 80% practice/tests, etc., and maybe 20% theory.
            I don’t understand why you seem to think otherwise…

          12. >I did try to add a capacitor in parallel with the feedback resistor to add a low-pass filter, it doesn’t make a difference.

            Your design is a bit different from what’s usually done with piezo elements. See:

            https://www.ti.com/lit/an/sloa033a/sloa033a.pdf

            In a charge mode amplifier (3.2 p.4) which is what your circuit resembles, adding that capacitor would act as a high pass filter. Anyways, the point isn’t to filter the signal per se, but to turn the ADC into an integrator which you are now doing in software, which you could do in hardware instead.

          13. I’m aware of this document, and the way a charge amplifier works.
            There’s also a reason why I didn’t go for that specific circuit.

            I have the feeling that we should continue this conversation in private, as this is not very useful nor relevant to the readers of this post.
            Feel free to contact me by e-mail if you want to, you’ll find it at exadrums.com (bottom of the page).
            It sounds that you’ve worked on e-drums projects, I’d be happy to know more about that.

    2. Yes it is, but…
      The ADC is using the ATMega4809 event system to start a new conversion, which uses only a few CPU cycles.
      It also uses an interrupt to get the result of that conversion, which also takes only a few CPU cycles.
      I also use this interrupt to process the signal and the triggers states as well.

      The MIDI messages are send in a “main” loop, that gets interrupted by the ADC interrupt quite frequently.
      Although sending MIDI messages takes time, it doesn’t take a lot of CPU cycles, because one the date is written to the register, all we do is wait…

      If you’d like to see how it’s done, have a loot at the code here: https://github.com/SpintroniK/exadrumino-Nano-Every/blob/main/main.cpp

    1. It’s possible, but the pads from these kits are not very bouncy. I started with one of those kits (5€ because of a loose wire somewhere) but quickly switched to 2nd hand Roland pads, those are much more fun to drum with.

  5. There are a few things that can “bloat” C++, compared to C. RTTI and exceptions are a few.(Ab-) Use of templates can explode code size but reduce execution size a bit. Apart from those few area’s (which can be (and often are) explitly disabled when programming for small uC’s assembly can be maybe 20% faster then compiled C / C++, but with 10 times the needed effort and you loose a lot of readablility and portablility. If speed is important for microcontrollers, then buying another processor that costs maybe 50ct more will get you a 4x speed increase in hardware. (Ignoring chippageddon for the moment)

    Assembly is completely dead (except for some very specialized cases) I doubt that anyone could write assembly better than a C++ compiler can for a superscalar processor with multiple cache levels, out of order execution, jump prediction and all those other things developed in the last 30 years. At least not in the time frame of a human life for a “moderately average” sized application.
    Just try to imagine how you will keep the pipelines filled for an 6-core 12 thread PC processor when writing in assembly.

    And concerning sound processing, I seem to recall that better timing than a few ms is indistinguishable for humans, and I’m sure there is plenty of research done in that area.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.