midilab / uClock

A tight BPM clock generator for Arduino and PlatformIO using hardware timer interruption. AVR, Teensy, STM32xx, ESP32 and RP2040 support
https://midilab.co/umodular
MIT License
161 stars 22 forks source link

Shuffle implementation #27

Closed grayxr closed 1 year ago

grayxr commented 1 year ago

Hi, I was wondering what your plans are on implementing the shuffle feature.

midilab commented 1 year ago

Soon, the idea is to have different shuffle signatures on code base to use on 16ppqn calls only(since 96 are for clock we can't mess with it).

Do you have any kinda of shuffle in mind? 909? mpc60? do you own some of hardware gear with suffle that we can try to emulate?

The implementation is not complicated, but lacks me time to research about a gear or types of shuffle/groove. Any help would be welcome here.

grayxr commented 1 year ago

I was thinking about trying to emulate the 909 shuffle, I was reading on a forum that "the shuffle of the TR-909 delays each even-numbered 1/16th by 2/96 of a beat for shuffle setting 1, 4/96 for 2, 6/96 for 3, 8/96 for 4, 10/96 for 5 and 12/96 for 6."

Maybe that's a good place to start for a basic shuffle feature? I'm sure other shuffle/groove mechanisms can be implemented too, and it could even be configurable.

midilab commented 1 year ago

I was thinking about trying to emulate the 909 shuffle, I was reading on a forum that "the shuffle of the TR-909 delays each even-numbered 1/16th by 2/96 of a beat for shuffle setting 1, 4/96 for 2, 6/96 for 3, 8/96 for 4, 10/96 for 5 and 12/96 for 6."

thanks for the info! this is the same kinda of pattern in MPC60 by roger linn. very funky stuff....

Maybe that's a good place to start for a basic shuffle feature? I'm sure other shuffle/groove mechanisms can be implemented too, and it could even be configurable.

i was able to implement the shuffle mechanism on uClock, feels groovy, need more test and a final way to create and share custom grooves.

soon i will release something for test.

Jackson-Devices commented 1 year ago

If anyone wants to take a deep dive into drum machine quantisation/shuffle, this sample pack provides 478 MIDI groove templates from 27 drum machines and is free https://samplesfrommars.com/products/grooves-from-mars

midilab commented 1 year ago

@Jackson-Devices thanks for the collection!

I've implemented a flexible swing layout following the same mechanics that MPC60 has based on his manual: mpc60-swing

We can manage to have any groove signature of drummachines that are 96ppqn resolution based, and not only mpc60.

I think we can create a new folder inside the source code with files for different drumachines groove and user created definitions to be easly applied via uClock API. something like:

uCtrl.setShuffle(MPC60_66);
uCtrl.setShuffle(TR909_P3);
...

At the library level you need to tell how many steps your groove signature will have(2 to 16), and for each step you set how many 1/96ppqn pulses tick earlier or late.

// MPC60 init 66% groove
shuffle.size = 2;
shuffle.step[0] = 0;
shuffle.step[1] = 3;

i'll try to release something thie weekend for tests.

midilab commented 1 year ago

Just had something working and also implemented within aciduino project, looks great and groovy!

The shuufle effect is applied only on 16PPQN callback, 96PPQN has no change at all since it is used mainly as clock source.

  // MPC60 groove signatures?
  uint8_t current_shuffle = 0;
  int8_t shuffle_54[2] = {0, 1};
  int8_t shuffle_58[2] = {0, 2};
  int8_t shuffle_62[2] = {0, 3};
  int8_t shuffle_66[2] = {0, 4};
  int8_t shuffle_71[2] = {0, 5};
  String shuffle_name[5] = {"54%", "58%", "62%", "66%", "71%"};
  int8_t* shuffle_templates[5] = {shuffle_54, shuffle_58, shuffle_62, shuffle_66, shuffle_71};

  // enable/disable shuffle
  uClock.setShuffle(true);

  // set a template for shuffle
  uClock.setShuffleTemplate(shuffle_templates[current_shuffle]);

This is the MPC60 and also same as TR909 templates, since they only change the even notes, no need for the template to go long as 2 steps setup as example. but you can setup 16 steps template too or more if you change the MAX_SHUFFLE_TEMPLATE_SIZE on uClock.h.

If you going to get some hiphop off tempo style you can do it on each tick how much you feel its good for the groove.

  // off-tempo hiphop style? 
  int8_t shuffle_off_tempo[16] = {0, -1, 0, -2, 0, -1, 0, 2, 0, -1, 0, -2, 0, 3, 0, -1};
  uClock.setShuffleTemplate(shuffle_off_tempo);

I didn't test this hiphop signature, it is just example on how flexible it can be. tick setup range is -5 to 5 only (96PPQN only have 5 ticks in between notes)

And since groove is not only about where the note is playing but also how long or shorter is one from another you have a new method called uClock.getShuffleLength() wich returns a int8_t with how much ticks you should add or remove from the note already recorded based on the groove setup. So when you are about to shot a note on, you add or remove the returned value of uClock.getShuffleLength() to the note length.

A visual example of groove placement+length here: groove-in-ableton-live

I will soon write a nice doc to add to the readme along with some examples.

Let me know how it groovy from your side.

grayxr commented 1 year ago

Wow thanks for getting this up and running so fast! I was able to test out the MPC60 style grooves and the hip-hop one as well, they sound pretty good so far. I can try and post a video tomorrow using my prototype groovebox I'm making.

grayxr commented 1 year ago

Here's a video where I'm cycling through the various 909/mpc60 style shuffle templates you've added on my groovebox I'm building. It doesn't sound 100% like the 909 shuffle I remember, but it can definitely add some funk in the mix.

midilab commented 1 year ago

I must say, your groovebox is truly impressive! I'm particularly fond of its design. Could you share some insights into how you created the panel?

From what I've gathered about the swing signature on various classic machines, that operate on a 96PPQN basis. Swing isn't just about altering the timing of ticks; it also involves the internal mechanics of triggering sounds and code performance/predictability.

The time it takes for an internal trigger to produce an audible sound varies from one machine to another. To emulate the original groove perfectly, you'd need to measure the time it takes for each drum component to go from trigger to sound output at every step. Then, you can adjust the sampler's trigger time on your machine accordingly. I've heard that the timing accuracy of the Roland TR-909, for instance, can be quite loose, and this contributes to its unique shuffle sound it has.

In my research, I've found that the faster and more precise your system processes and outputs sounds, the better your groove will be. Take the MPC60, for example, with its excellent internal clock and swift, precise sample triggering to sound output system.

Consider factors like the microcontroller you're using, its CPU power, and how you've coded your sequencer and sampler engine. Losing groove can happen if your interrupt calls, like 16PPQNCallback or 96PPQNCallback, take too long to execute, ideally they should be concise. Alternatively, groove can be compromised if the time from processing the first sound to the last isn't entirely predictable or takes too long due to extensive effects or pre/post-processing on audio or sampling. Many elements can affect clock timing and groove in this context.

What we're currently doing with uClock to achieve better shuffle is just the initial step. Writing efficient and performant code is the next significant challenge in our pursuit of perfect timing and groove.

So in resume achieve exactly same groove as 909 will be quite a challenge because of all those factors that is not only related to the way we manage ticks to trigger early or later notes.

grayxr commented 1 year ago

Thanks for the kind words!

Could you share some insights into how you created the panel?

I started out with like a sketch on paper maybe, then started modeling out a rough layout using something like Tinkercad (a really basic 3D modeling web application), and then used Fusion 360 to actually design the enclosure. I also use Fusion 360 for the electronics designs as well. This box has multiple PCBs and all of the alignments and tolerances were precise and Fusion 360 really helped with that because I could have all of the hardware components in 3D space and see how it would be assembled. This is the first enclosure I've designed so I watched some videos and just thought about how it would fasten together, had to learn about considering sheet metal bending, etc.

It's not production ready yet, but I will hopefully be demoing it at Machina Bristronica next week!

In regards to the groove timing stuff, yeah my situation is probably more just that I need to revisit how I've implemented the sound generation and noteOn/noteOff event handling, as well as just how I've integrated uClock. But uClock has been such a breeze to use and I'm so happy that I can get reliable clock even while reading/writing from an SD card, writing to an OLED, and various other things.

Thanks again!

midilab commented 1 year ago

I started out with like a sketch on paper maybe, then started modeling out a rough layout using something like Tinkercad (a really basic 3D modeling web application), and then used Fusion 360 to actually design the enclosure. I also use Fusion 360 for the electronics designs as well. This box has multiple PCBs and all of the alignments and tolerances were precise and Fusion 360 really helped with that because I could have all of the hardware components in 3D space and see how it would be assembled. This is the first enclosure I've designed so I watched some videos and just thought about how it would fasten together, had to learn about considering sheet metal bending, etc.

Well done job! This fusion360 service is so expensive, looks like it really worth for production and more serious stuffs.

It's not production ready yet, but I will hopefully be demoing it at Machina Bristronica next week!

Sounds nice man! i wish good luck!

But uClock has been such a breeze to use and I'm so happy that I can get reliable clock even while reading/writing from an SD card, writing to an OLED, and various other things.

Im glad to hear that, since this is the main goal of the project, keep all the realtime implementation complexities behind a simple, safe and easy to use API. By doing that people can focus on the creative part of the job to develop new instruments.