I just had an idea for an app that might haunt me enough to attempt at least a 1-voice POC in BASIC:
Like Foxdot/Tidal Cycles, but running on a Commodore 64 and controlling the MOS6581 instead of SuperCollider
Or, slightly less masochistic, something written in a modern language and controlling an ARMSID chip
...of course, in terms of a live-coding environment for chiptunes, TIC-80 would be masochistic in its own way because there is no way to read input (other than 4 directional keys and 4 action buttons) from a keyboard, so I'd be looking at coding an entire virtual on-screen keyboard that would basically be a shitty smart tv "tap tap tap tap to the letter you want, click a button, repeat" sort of interface, which would be an utterly miserable thing to use.
No matter, my brain has latched onto a slightly different idea that's well-suited to the whole TIC-80 ethos
... there's probably a clever trick out there for dealing with drift when rounding thousandths of a second to sixtieths of a second, but I haven't found it.
After I capitulated and pegged note lengths to even multiples of frames I thought I had a multi-voice, data-based looping POC working, but that seems to be drifting too. Probably need to re-think my "designate when to play what note for which voice" logic, but it is time to go to bed.
Solved (I think?) the synchronization problem, I *have* to put this aside for now but that's a big hurdle jumped.
And the little visualizer I hacked together is pretty cool
apologies for the too-quiet video-of-a-screen, I can't be arsed to sort out screen recording with sound right now
I need an algorithm that can recursively interlace multiple arrays with one another, IE
[a, b, c], [1, 2, 3]
or if the arrays are different lengths, they're repeated until all combinations are made
[a, b, c], [1,2]
[a1, b2, c1, a2, b1, c2]
I've gotten that far.
But what I'd also like is to interlace lists of lists, IE
[a, [b, c], d][1,2]
a1, b2, d1, a2, c1, d2
But also potentially
[a, [b, [c,d]], [e, f]][1, [2, 3]...]
and so on.
Difficulty: in Lua.
I know the logic for this exists in the FoxDot (Python) codebase, but I haven't gone spelunking very far yet.
I guess all I really need to figure out is one nested array at a time, IE
[a, [b, c], d
a, b, d, a, c, d
and once all the top-level lists are flattened, it's easy to interlace them as above.
💥I figured it out in "longhand" python, I think this shouldn't be too hard to translate to lua:
... in Lua, mostly. Still need to find or write a 'Lowest Common Multiple' function to keep the extracted pattern as small as possible:
This is getting pretty cool if I do say so myself.
Here's a test using all four of the TIC-80's voices, each one playing a different interlaced pattern of pitches and durations.
It's a little bit vanilla as they're all playing the same simple triangle waveform, and the sound is choppy because the screen recording, but still!
Time to fool around with the SFX editor and make some instruments.
I have a feeling that writing an entire text input widget from scratch (including the logic to keep track of a text buffer, etc.) is going to take as long as working out the looping stuff did, if not longer. (Although actually, somebody's blazed a lot of that trail already! https://tic80.com/play?cart=449)
* Text editor is half-baked but functional enough to enter some code
* Shift+Enter evaluates the code
* Code makes beeps and boops
* Metronome blinkenlight looks neat but needs work; need to implement a global beat/measure timer to sync everything to
Project entering the dangerous phase of "more or less working and increasingly less likely to have a stranglehold on my attention the fussier the little UI bugs get" #tic80
As predicted, I got way further into the weeds with the shrepl than I wanted to. it became more grudge than interesting exercise, but I got it mostly under control, a few cursor up/down logic tweaks pending.
I've worked out save/load logic for storing the shrepl buffer in the #tic80 cartridge persistent memory... you only get up to 1024 bytes (I'll probably limit it to 1000 to reserve a bit of space for prefs etc.) but that's probably enough for a reasonable amount of noodling.
This is getting to the neighborhood of being usable!
It's not ready for a release yet but I'm going to try and make something cohesive tonight on this, the last day of November. I'm going to go ahead at say that writing your own music software counts as a #novembeat / #noisevember project.
I solved (I think) the tempo drift problem; when I was banging my head on it before I got fixated on the main #tic80 program loop only running 60 times/second, and I overlooked the time() function, which always returns the number of milliseconds elapsed since the program was started.
So, I can use time() for the beat clock on any given frame and whenever it reaches or exceeds the right number of milliseconds per beat, I reset the beat clock to 0 and offset it by "drift since last beat * -1"; that way the tempo is never off by more than 1/60 of a second. I've tested various tempos against a metronome phone app and it's pretty solid! 32nd notes are too fast to test by ear, but according to numeric stats those stay accurate too.
I'm gonna refactor the way I store patterns and trigger events, which will make it a lot easier to do stuff like having one voice follow the pitch/duration/octave/vol/etc of another
A friendly place in the fediverse for discussing music recordings. Learn more here!