Log In  


As usual with PCM audio, be careful of your volume, and if this doesn't play back well in your browser, try it in desktop Pico-8. Earlier versions had filter instability problems that could result in loud sounds showing up out of nowhere - I think these are fixed now (I've let this play for over an hour with no problems) but I can't 100% promise it won't happen again.

Cart #acid_jam_512-3 | 2022-06-22 | Code ▽ | Embed ▽ | No License
20

An entry to the Pico-8 512-Char Jam. Kick, hat, acid.

Jam entry
Itch page

Update 1: Moved the delay time slightly off the beat. Also saved a few characters thanks to @SmellyFishstiks, which let me thicken the oscilloscope line.

Update 2: Improved visualization and stability.

Update 3: More improvements to visualization and filter stability to avoid horrible loud sounds coming out of nowhere after letting the cart play for a long time. Hopefully the last update!

20


1

Really cool! I don't make "tweet"carts a lot so I find the use of the 2 labels really interesting having one for draw and one for grabbing the pcm


1

Thanks! Yeah, I needed to be able to skip synthesis if the buffer was full, and just sticking a label right before flip() seemed like the most expedient approach.

But now that you've got me looking at it, I've realized that just using a big if block actually saves a few characters, so if I make any more changes to this I should probably do that instead. Thanks for making me think about this a bit more!


gah! I like the labels though but glad I could help!


2

Cart #tweet_acid_jam-2 | 2022-06-22 | Code ▽ | Embed ▽ | No License
2

With enough sacrifices it's possible to get this down to tweetcart length! I'll probably noodle on this one a bit before actually posting to Twitter, I think I can get this down a few more chars and maybe use those to get some of the quality back?

Changes:

  • No delay
  • No hihat
  • Tuning is less accurate and there are fewer possible notes
  • Synth plays every 16th note instead of only sometimes
  • No oversampling
  • 2-pole instead of 4-pole filter
  • No saturation in filter
  • Simpler visualization (dots instead of lines, no color)
  • Favoring shorter values over better-sounding values

The lessons learned from the tweet-shortening process also got the 512-char version down to 487 characters, which is probably enough space to do something interesting.

Update 1: visualization with dots instead of circles, less flickery display.

Update 2: slightly improved visualization and sound.


Infinitely listenable. So good.


@bikibird Thank you! I'm really happy that you enjoyed it.


Whoa this is super cool! Do you fill the audio buffer manually or how do you manage to do this? Oh and do you have somewhere where I can read about it if I want to do it myself?


1

So so cool!
THAT is my jam @luchak

I had a go at the tweetcart version and brought it down to 268bytes

x=0y=0p=0g=0::_::n=min(512-stat(108),188)for i=0,n-1do
if(g%646<1)q=rnd({1,1.8,2,2.1})<<9e=.3+.1*sin(t()/6)
if(g<1)k=62g=2584
k*=.999e*=.999g-=1o=p>>11o-=9*(y-o)x+=e*(o-x)y+=e*(x-y)p+=q
v=y+sin((g>>10)^3)*k+64poke(i,v+64)
?"+",i,v,9
end
serial(2056,0,n)?"⁶1⁶c0"
goto _

Why didn't I learn about this Pico8 512 chars jam before :p


@p01 Nice! I think I have some real ?-blindness, I'll have to remember these tricks for next time. :) Glad you liked it!

Sending 1 byte at a time let me shave another 8 bytes:

x=0y=0p=0g=0::_::for i=0,min(511-stat(108),187)do
if(g%646<1)q=rnd({1,1.8,2,2.1})<<9e=.3+.1*sin(t()/6)
if(g<1)k=62g=2584
k*=.999e*=.999g-=1o=p>>11o-=9*(y-o)x+=e*(o-x)y+=e*(x-y)v=y+sin((g>>10)^3)*k+64poke(0,v+64)serial(2056,0,1)p+=q?"+",i,v,9
end?"⁶1⁶c0"
goto _

4

@Wistpotion Yeah, this is using the PCM output - the last paragraph of this comment gives a summary of how to use it. Looking at the #pcm tag should also turn up a bunch more examples.

Here's an annotated version of the source (with @p01's improvements) if that helps:

x=0 y=0 -- Filter internal state
p=0 -- Oscillator phase
g=0 -- Samples left in beat
::_:: -- Label for start of frame

-- Make sure we have at least 512 samples in the buffer, but also don't get
-- too far ahead. Since we clear the screen every frame, the output will be
-- very flickery if we skip synthesis too often. 5512.5/30=183.75, but I've
-- found that adding a few extra samples helps avoid audio dropouts.
for i=0,min(511-stat(108),187) do

-- 646 samples per 16th note at 128 bpm.
-- q is the oscillator phase increment, chosen from a bag of pitch ratios
-- quantized to 1 decimal place.
-- e is the filter envelope amount, which is modulated with an LFO.
if(g%646<1)q=rnd({1,1.8,2,2.1})<<9e=.3+.1*sin(t()/6)

-- 2584 samples per beat at 128 bpm.
-- k is the kick drum amplitude. 62 is just what sounded best to me.
if(g<1)k=62g=2584

k*=.999 e*=.999 -- apply decay to kick amplitude and filter env
g-=1 -- advance sample counter
o=p>>11 -- we use overflow for osc phase reset, so we need to scale down
o-=9*(y-o) -- filter feedback (so we have some nice resonance)
x+=e*(o-x) y+=e*(x-y) -- this filter is just 2 1-pole filters in series

-- Mix in the kick drum. The cubic exponent was necessary to get a reasonable
-- amount of pitch decay. 64 is added since we need offsets of 64 and 128
-- later and adding one offset here helps keep the char count down.
v=y+sin((g>>10)^3)*k+64

poke(0,v+64) -- write output audio to memory
serial(2056,0,1) -- send audio byte to output
p+=q -- increment oscillator phase
?"+",i,v,9 -- draw oscilloscope dot
end
?"⁶1⁶c0" -- end frame and clear screen
goto _ -- next frame

1

@luchak thanks a bunch, reading super minimized code is still a bit hard for me, but that helps a ton. Thanks!



[Please log in to post a comment]