Loop Supreme, part 2: Adding a Metronome
2022-11-04 12:32:38 +0000 UTCThis is part 2 in a series about building a browser-based live looper
- Part 12: v1.0 release, and project retro
- Part 11: Exporting stems and changing inputs
- Part 10: Keyboard bindings
- Part 9: Visualizing the waveform
- Part 8: Building and hosting
- Part 7: Latency and adding Track functionality
- Part 6: Workers and AudioWorklets
- Part 5: Record and loop a track
- Part 4: Adding a Scene
- Part 3: Metronome click
- Part 2: Adding a Metronome
- Part 1: New project: building a web-based audio looper!
Goal
I need to add a metronome!
In my ideal looper app, the metronome is the heart of the entire application. By default, I want the metronome to determine the length of the loop (number of measures 𝗑 time signature 𝗑 BPM), as well as synchronizing the start and end of recording for each loop. The user should be able to arm a track for recording, but recording should actually begin when the metronome starts the beginning of the loop.
Implementation
Since the metronome is going to have properties that are shared among many components, I have 2 “obvious” options that I’m aware of in the React ecosystem:
- create a Context that will store some “global” state about the current tick, BPM, etc
- create an instance of a “Metronome class” and pass it around everywhere that it is needed
I’m going to try using a Context because it feels a little more “React-y” and I think it might by easier than passing a Metronome down through oodles of different components.
Learnings
I’ve never seen or used such a complex Context before, and tbh I’m not sure if it’s idiomatic for React Contexts. It does feel a little strange to be managing an interval inside the component, but in simple tests it seems to work OK. I’m also not at all convinced that it is good practice to pass state updater functions to the context provider. Again, seems to work in a simple test, but we’ll see when I get the Scene
and Track
elements going if this actually works as expected.
I also learned that for Tailwind to work properly, you need to point it at all the relevant source files. This is probably obvious to anybody who has used Tailwind before but I couldn’t figure out why my styles for my header weren’t working. I moved my header into the static HTML file (eventually I’ll optimize this file a bit more for SEO) and wanted to use Tailwind styles for it. I had to add my public/index.html
file to my Tailwind config in order to make this work. After that, both npm run css
and npm start
compile the CSS properly and apply the styles as expected. Magic! ✨
State of the app
- Merged PR: https://github.com/ericyd/loop-supreme/pull/3
- The app runs!
npm start
MetronomeContext
counts the beats. It works for various time signatures, BPMs, a measure countsMetronome
component displays current metronome settings and updates them- There is a bug where the
MetronomeContext
is creating two intervals, as demonstrated by a simple console log. This is probably due to double-mounting done by React in development mode. Need to test in production mode to see if this will be an issue long term.
Time logging
- spent about an hour adding the basic MetronomeContext and Metronome component
- spent about an hour on validation and styling improvements