Initial release: procedural techno music generator
Synthesizes kick, snare, hi-hat, bass, and acid lead from scratch. 32-bar song structure with filter sweeps and pattern variation. Outputs raw float32 stereo at 44.1 kHz to stdout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
12
Makefile
Normal file
12
Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
CC = gcc
|
||||
CFLAGS = -O2 -Wall -Wextra
|
||||
LDFLAGS = -lm
|
||||
TARGET = techno-gen
|
||||
|
||||
$(TARGET): $(TARGET).c
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
|
||||
.PHONY: clean
|
||||
69
README.md
Normal file
69
README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# techno-gen
|
||||
|
||||
Generates infinite procedural techno music as raw float32 stereo audio on stdout.
|
||||
138 BPM. No samples — everything is synthesized from scratch.
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
make
|
||||
```
|
||||
|
||||
Or manually:
|
||||
|
||||
```sh
|
||||
gcc -O2 -Wall -lm -o techno-gen techno-gen.c
|
||||
```
|
||||
|
||||
## Play
|
||||
|
||||
Pipe directly into `paplay` (PulseAudio):
|
||||
|
||||
```sh
|
||||
./techno-gen | paplay --format=float32le --rate=44100 --channels=2
|
||||
```
|
||||
|
||||
> **Note:** `paplay` is part of the `pulseaudio-utils` package.
|
||||
> On Debian/Ubuntu: `sudo apt install pulseaudio-utils`
|
||||
|
||||
### Mono version (if you only want 1 channel)
|
||||
|
||||
The program outputs stereo (2 channels). If you want mono, mix down with sox:
|
||||
|
||||
```sh
|
||||
./techno-gen | sox -t raw -r 44100 -e float -b 32 -c 2 - -t raw -r 44100 -e float -b 32 -c 1 - | paplay --format=float32le --rate=44100 --channels=1
|
||||
```
|
||||
|
||||
### Record to file
|
||||
|
||||
```sh
|
||||
./techno-gen | sox -t raw -r 44100 -e float -b 32 -c 2 - output.wav trim 0 120
|
||||
```
|
||||
|
||||
This records 2 minutes (`trim 0 120`) to `output.wav`.
|
||||
|
||||
## What it sounds like
|
||||
|
||||
| Time | Section |
|
||||
|---------------|----------------------------------------------------|
|
||||
| 0–3 bars | **Intro** — kick + bass, filter closed |
|
||||
| 4–7 bars | **Rise** — hi-hats join, filter opens |
|
||||
| 8–15 bars | **Full** — snare, acid lead, LFO filter sweeps |
|
||||
| 16–19 bars | **Breakdown** — bass only, stripped down |
|
||||
| 20–23 bars | **Build** — kick returns, filter climbs |
|
||||
| 24–31 bars | **Heavy** — syncopated kick, acid variation 2 |
|
||||
| *then loops* | Back to bar 8 (never the sparse intro again) |
|
||||
|
||||
The cycle is 32 bars (~1 min 23 sec at 138 BPM), then repeats with continuous filter and pattern variation.
|
||||
|
||||
## Synthesis
|
||||
|
||||
All audio is synthesized in real time:
|
||||
|
||||
- **Kick** — sine oscillator with pitch envelope (200 → 45 Hz), amp decay, transient click
|
||||
- **Snare** — high-pass filtered noise blended with a 185 Hz tone
|
||||
- **Hi-hat** — noise through a 7 kHz high-pass; open/closed variants with different decay times
|
||||
- **Bass** — sawtooth oscillator through a resonant (Q=4.5) biquad low-pass with per-note filter envelope and glide
|
||||
- **Acid lead** — dual slightly-detuned sawtooth through a very resonant (Q=6) low-pass; filter sweeps with LFO per section
|
||||
- **Stereo width** — short delay line (~11 ms) ping-ponged between channels on hats and acid
|
||||
- **Master** — `tanh` soft saturation to keep levels honest
|
||||
428
techno-gen.c
Normal file
428
techno-gen.c
Normal file
@@ -0,0 +1,428 @@
|
||||
/*
|
||||
* techno-gen — generates infinite techno music as raw float32 stereo audio
|
||||
*
|
||||
* Build: gcc -O2 -lm -o techno-gen techno-gen.c
|
||||
* Play: ./techno-gen | paplay --format=float32le --rate=44100 --channels=2
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define SR 44100
|
||||
#define BPM 138.0
|
||||
#define TWO_PI (2.0 * M_PI)
|
||||
#define SPB ((int)(SR * 60.0 / BPM)) /* samples per beat (quarter note) */
|
||||
#define SP16 (SPB / 4) /* samples per 16th note */
|
||||
|
||||
static inline double freq_of(double root, int semitones) {
|
||||
return root * pow(2.0, semitones / 12.0);
|
||||
}
|
||||
|
||||
/* ── PRNG ── */
|
||||
static uint32_t rng_state = 0xdeadbeef;
|
||||
static double randf(void) {
|
||||
rng_state ^= rng_state << 13;
|
||||
rng_state ^= rng_state >> 17;
|
||||
rng_state ^= rng_state << 5;
|
||||
return (double)(int32_t)rng_state * (1.0 / 2147483648.0);
|
||||
}
|
||||
|
||||
/* ── Biquad filter ── */
|
||||
typedef struct { double b0, b1, b2, a1, a2, x1, x2, y1, y2; } Bq;
|
||||
|
||||
static void bq_lp(Bq *f, double fc, double q) {
|
||||
double w = TWO_PI * fc / SR;
|
||||
double alpha = sin(w) / (2.0 * q);
|
||||
double c = cos(w), a0 = 1.0 + alpha;
|
||||
f->b0 = (1.0 - c) * 0.5 / a0;
|
||||
f->b1 = (1.0 - c) / a0;
|
||||
f->b2 = f->b0;
|
||||
f->a1 = -2.0 * c / a0;
|
||||
f->a2 = (1.0 - alpha) / a0;
|
||||
}
|
||||
|
||||
static void bq_hp(Bq *f, double fc, double q) {
|
||||
double w = TWO_PI * fc / SR;
|
||||
double alpha = sin(w) / (2.0 * q);
|
||||
double c = cos(w), a0 = 1.0 + alpha;
|
||||
f->b0 = (1.0 + c) * 0.5 / a0;
|
||||
f->b1 = -(1.0 + c) / a0;
|
||||
f->b2 = f->b0;
|
||||
f->a1 = -2.0 * c / a0;
|
||||
f->a2 = (1.0 - alpha) / a0;
|
||||
}
|
||||
|
||||
static double bq_run(Bq *f, double x) {
|
||||
double y = f->b0*x + f->b1*f->x1 + f->b2*f->x2
|
||||
- f->a1*f->y1 - f->a2*f->y2;
|
||||
f->x2 = f->x1; f->x1 = x;
|
||||
f->y2 = f->y1; f->y1 = y;
|
||||
return y;
|
||||
}
|
||||
|
||||
/* ── Kick drum ──
|
||||
* Sine wave with pitch drop (200 → 45 Hz) and amp decay */
|
||||
typedef struct { double phase, amp, pitch; } Kick;
|
||||
|
||||
static void kick_trig(Kick *k) {
|
||||
k->phase = 0.0; k->amp = 1.0; k->pitch = 1.0;
|
||||
}
|
||||
|
||||
static double kick_tick(Kick *k) {
|
||||
if (k->amp < 0.001) { return 0.0; }
|
||||
double freq = 45.0 + 175.0 * k->pitch;
|
||||
k->phase += TWO_PI * freq / SR;
|
||||
if (k->phase > TWO_PI) { k->phase -= TWO_PI; }
|
||||
double click = (k->amp > 0.97) ? randf() * 0.25 : 0.0;
|
||||
double s = (sin(k->phase) * 0.95 + click) * k->amp;
|
||||
k->amp *= 0.9997;
|
||||
k->pitch *= 0.9982;
|
||||
return s * 0.85;
|
||||
}
|
||||
|
||||
/* ── Snare/clap ──
|
||||
* Noise burst + low-tone blend */
|
||||
typedef struct { double env, tone_phase; Bq hp; } Snare;
|
||||
|
||||
static void snare_trig(Snare *s) {
|
||||
s->env = 1.0; s->tone_phase = 0.0;
|
||||
}
|
||||
|
||||
static double snare_tick(Snare *s) {
|
||||
if (s->env < 0.001) { return 0.0; }
|
||||
s->tone_phase += TWO_PI * 185.0 / SR;
|
||||
if (s->tone_phase > TWO_PI) { s->tone_phase -= TWO_PI; }
|
||||
double noise = bq_run(&s->hp, randf());
|
||||
double out = (noise * 0.70 + sin(s->tone_phase) * 0.30) * s->env;
|
||||
s->env *= 0.9935;
|
||||
return out * 0.55;
|
||||
}
|
||||
|
||||
/* ── Hi-hat ──
|
||||
* Bandpass-ish noise, two modes: closed / open */
|
||||
typedef struct { double env, decay; Bq hp; } Hat;
|
||||
|
||||
static void hat_trig(Hat *h, int open) {
|
||||
h->env = 1.0;
|
||||
h->decay = open ? 0.9993 : 0.9958;
|
||||
}
|
||||
|
||||
static double hat_tick(Hat *h) {
|
||||
if (h->env < 0.001) { return 0.0; }
|
||||
double out = bq_run(&h->hp, randf()) * h->env;
|
||||
h->env *= h->decay;
|
||||
return out * 0.38;
|
||||
}
|
||||
|
||||
/* ── Bass synth ──
|
||||
* Sawtooth through resonant low-pass with filter envelope */
|
||||
typedef struct { double phase, freq, tgt, amp_env, flt_env; Bq lpf; } Bass;
|
||||
|
||||
static void bass_note(Bass *b, double freq) {
|
||||
b->tgt = freq;
|
||||
b->flt_env = 1.0;
|
||||
b->amp_env = 1.0;
|
||||
}
|
||||
|
||||
static double bass_tick(Bass *b, double cutoff_base) {
|
||||
b->freq += (b->tgt - b->freq) * 0.06;
|
||||
b->phase += TWO_PI * b->freq / SR;
|
||||
if (b->phase > TWO_PI) { b->phase -= TWO_PI; }
|
||||
double saw = (b->phase / M_PI) - 1.0;
|
||||
double fc = cutoff_base + 3500.0 * b->flt_env;
|
||||
if (fc < 60.0) { fc = 60.0; }
|
||||
if (fc > 18000.0) { fc = 18000.0; }
|
||||
bq_lp(&b->lpf, fc, 4.5);
|
||||
double out = bq_run(&b->lpf, saw) * b->amp_env;
|
||||
b->flt_env *= 0.9991;
|
||||
b->amp_env *= 0.9999;
|
||||
return out * 0.55;
|
||||
}
|
||||
|
||||
/* ── Acid lead ──
|
||||
* Sawtooth through very resonant LP, slightly detuned for bite */
|
||||
typedef struct { double phase, phase2, freq, tgt, amp_env, flt_env; Bq lpf; } Acid;
|
||||
|
||||
static void acid_note(Acid *a, double freq) {
|
||||
a->tgt = freq;
|
||||
a->flt_env = 1.0;
|
||||
a->amp_env = 1.0;
|
||||
}
|
||||
|
||||
static double acid_tick(Acid *a, double cutoff_base) {
|
||||
if (a->amp_env < 0.001) { return 0.0; }
|
||||
a->freq += (a->tgt - a->freq) * 0.09;
|
||||
a->phase += TWO_PI * a->freq / SR;
|
||||
a->phase2 += TWO_PI * (a->freq * 1.008) / SR; /* slight detune */
|
||||
if (a->phase > TWO_PI) { a->phase -= TWO_PI; }
|
||||
if (a->phase2 > TWO_PI) { a->phase2 -= TWO_PI; }
|
||||
double saw = ((a->phase / M_PI) - 1.0) * 0.6
|
||||
+ ((a->phase2 / M_PI) - 1.0) * 0.4;
|
||||
double fc = cutoff_base + 5000.0 * a->flt_env;
|
||||
if (fc < 80.0) { fc = 80.0; }
|
||||
if (fc > 18000.0) { fc = 18000.0; }
|
||||
bq_lp(&a->lpf, fc, 6.0);
|
||||
double out = bq_run(&a->lpf, saw) * a->amp_env;
|
||||
a->flt_env *= 0.9992;
|
||||
a->amp_env *= 0.9997;
|
||||
return out * 0.32;
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════
|
||||
* Patterns (16 steps per bar, -1 = rest / hold)
|
||||
* Notes are semitone offsets from root A1 = 55 Hz
|
||||
* ════════════════════════════════════════════════════════════ */
|
||||
|
||||
static const int BASS_PAT[4][16] = {
|
||||
/* 0: driving quarter-note anchor */
|
||||
{ 0,-1, 0,-1, 7,-1, 0,-1, 3,-1, 5,-1, 7,-1, 3,-1 },
|
||||
/* 1: offbeat pushes */
|
||||
{ 0, 0,-1,12, 0, 7,-1, 5, 3,-1, 3, 5, 7,-1, 7, 3 },
|
||||
/* 2: dark and sparse */
|
||||
{ 0,-1,-1, 0,-1, 7, 5,-1, 0,-1,-1, 3,-1, 5, 0,-1 },
|
||||
/* 3: more frantic */
|
||||
{ 0, 3, 0, 7, 0,12, 7, 0, 3, 0, 5, 3, 0, 7, 5, 0 },
|
||||
};
|
||||
|
||||
static const int ACID_PAT[2][16] = {
|
||||
{ 12,-1,-1,14,-1,-1,12,-1,10,-1,-1,12,-1,-1,10,-1 },
|
||||
{ 12,-1,15,-1,14,-1,12,10,-1,12,-1,15,17,-1,15,12 },
|
||||
};
|
||||
|
||||
/* 0=off, 1=closed, 2=open */
|
||||
static const int HAT_PAT[2][16] = {
|
||||
{ 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,2 },
|
||||
{ 1,1,1,0, 1,1,2,0, 1,1,1,0, 1,0,2,0 },
|
||||
};
|
||||
|
||||
/* 0=off, 1=kick */
|
||||
static const int KICK_PAT[2][16] = {
|
||||
{ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0 }, /* 4-on-the-floor */
|
||||
{ 1,0,0,0, 1,0,0,1, 1,0,0,0, 1,0,1,0 }, /* syncopated */
|
||||
};
|
||||
|
||||
/* ════════════════════════════════════════════════════════════
|
||||
* Section map (cycles every 32 bars):
|
||||
*
|
||||
* 0-3 : intro — kick + bass only, filter closed
|
||||
* 4-7 : rise — add hats, filter opens
|
||||
* 8-15 : full — kick + hats + snare + bass + acid, sweeps
|
||||
* 16-19 : break — bass only, everything stripped
|
||||
* 20-23 : build — kick returns, filter rises
|
||||
* 24-31 : heavy — syncopated kick, acid pattern 2, wide sweep
|
||||
* ════════════════════════════════════════════════════════════ */
|
||||
|
||||
typedef struct {
|
||||
int kick_pat;
|
||||
int hat_pat;
|
||||
int bass_pat;
|
||||
int acid_pat;
|
||||
int use_hats;
|
||||
int use_snare;
|
||||
int use_acid;
|
||||
/* cutoff_base for bass and acid as function of bar-in-section */
|
||||
double bass_fc_lo, bass_fc_hi;
|
||||
double acid_fc_lo, acid_fc_hi;
|
||||
} Section;
|
||||
|
||||
static void get_section(int bar, Section *s) {
|
||||
int b = bar % 32;
|
||||
double t = 0.0; /* 0..1 within section for sweeps */
|
||||
|
||||
if (b < 4) {
|
||||
t = b / 4.0;
|
||||
s->kick_pat = 0;
|
||||
s->hat_pat = 0;
|
||||
s->bass_pat = 0;
|
||||
s->acid_pat = 0;
|
||||
s->use_hats = 0;
|
||||
s->use_snare = 0;
|
||||
s->use_acid = 0;
|
||||
s->bass_fc_lo = 200.0;
|
||||
s->bass_fc_hi = 200.0 + 400.0 * t;
|
||||
s->acid_fc_lo = 0;
|
||||
s->acid_fc_hi = 0;
|
||||
} else if (b < 8) {
|
||||
t = (b - 4) / 4.0;
|
||||
s->kick_pat = 0;
|
||||
s->hat_pat = 0;
|
||||
s->bass_pat = 1;
|
||||
s->acid_pat = 0;
|
||||
s->use_hats = 1;
|
||||
s->use_snare = 0;
|
||||
s->use_acid = 0;
|
||||
s->bass_fc_lo = 350.0 + 600.0 * t;
|
||||
s->bass_fc_hi = s->bass_fc_lo + 400.0;
|
||||
s->acid_fc_lo = 0;
|
||||
s->acid_fc_hi = 0;
|
||||
} else if (b < 16) {
|
||||
t = (b - 8) / 8.0;
|
||||
s->kick_pat = 0;
|
||||
s->hat_pat = 0;
|
||||
s->bass_pat = (b < 12) ? 1 : 2;
|
||||
s->acid_pat = 0;
|
||||
s->use_hats = 1;
|
||||
s->use_snare = 1;
|
||||
s->use_acid = 1;
|
||||
/* sweeping filter: sine lfo over 4 bars */
|
||||
double lfo = sin(t * TWO_PI * 2.0) * 0.5 + 0.5;
|
||||
s->bass_fc_lo = 500.0 + 1200.0 * lfo;
|
||||
s->bass_fc_hi = s->bass_fc_lo + 600.0;
|
||||
s->acid_fc_lo = 400.0 + 1800.0 * lfo;
|
||||
s->acid_fc_hi = s->acid_fc_lo + 800.0;
|
||||
} else if (b < 20) {
|
||||
/* breakdown */
|
||||
s->kick_pat = 0;
|
||||
s->hat_pat = 0;
|
||||
s->bass_pat = 2;
|
||||
s->acid_pat = 0;
|
||||
s->use_hats = 0;
|
||||
s->use_snare = 0;
|
||||
s->use_acid = 0;
|
||||
s->bass_fc_lo = 180.0;
|
||||
s->bass_fc_hi = 240.0;
|
||||
s->acid_fc_lo = 0;
|
||||
s->acid_fc_hi = 0;
|
||||
} else if (b < 24) {
|
||||
/* build */
|
||||
t = (b - 20) / 4.0;
|
||||
s->kick_pat = 0;
|
||||
s->hat_pat = 1;
|
||||
s->bass_pat = 3;
|
||||
s->acid_pat = 0;
|
||||
s->use_hats = 1;
|
||||
s->use_snare = 1;
|
||||
s->use_acid = (t > 0.5);
|
||||
s->bass_fc_lo = 200.0 + 1200.0 * t * t;
|
||||
s->bass_fc_hi = s->bass_fc_lo + 800.0;
|
||||
s->acid_fc_lo = 200.0 + 2000.0 * t * t;
|
||||
s->acid_fc_hi = s->acid_fc_lo + 1200.0;
|
||||
} else {
|
||||
/* heavy */
|
||||
t = (b - 24) / 8.0;
|
||||
double lfo = sin(t * TWO_PI * 3.0) * 0.5 + 0.5;
|
||||
s->kick_pat = 1;
|
||||
s->hat_pat = 1;
|
||||
s->bass_pat = 3;
|
||||
s->acid_pat = 1;
|
||||
s->use_hats = 1;
|
||||
s->use_snare = 1;
|
||||
s->use_acid = 1;
|
||||
s->bass_fc_lo = 700.0 + 1500.0 * lfo;
|
||||
s->bass_fc_hi = s->bass_fc_lo + 900.0;
|
||||
s->acid_fc_lo = 600.0 + 2400.0 * lfo;
|
||||
s->acid_fc_hi = s->acid_fc_lo + 1400.0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════ */
|
||||
|
||||
int main(void) {
|
||||
double root = 55.0; /* A1 */
|
||||
|
||||
Kick kick = {0};
|
||||
Snare snare = {0};
|
||||
Hat hat = {0};
|
||||
Bass bass = {0};
|
||||
Acid acid = {0};
|
||||
|
||||
bq_hp(&snare.hp, 280.0, 0.7);
|
||||
bq_hp(&hat.hp, 7200.0, 0.9);
|
||||
|
||||
bass.freq = root;
|
||||
bass.tgt = root;
|
||||
acid.freq = freq_of(root, 12);
|
||||
acid.tgt = acid.freq;
|
||||
|
||||
/* Stereo chorus/width: short delay line */
|
||||
#define DLEN 2048
|
||||
double dbuf[DLEN] = {0};
|
||||
int dpos = 0;
|
||||
int dtap = (int)(0.011 * SR) % DLEN;
|
||||
|
||||
int step = 0;
|
||||
int bar = 0;
|
||||
int sample_in_step = 0;
|
||||
|
||||
/* For per-step cutoff interpolation */
|
||||
double bass_fc = 500.0;
|
||||
double acid_fc = 400.0;
|
||||
|
||||
while (1) {
|
||||
if (sample_in_step == 0) {
|
||||
Section sec;
|
||||
get_section(bar, &sec);
|
||||
|
||||
/* Interpolate cutoff: step position within bar 0..15 */
|
||||
double sbar_t = step / 16.0;
|
||||
bass_fc = sec.bass_fc_lo + (sec.bass_fc_hi - sec.bass_fc_lo) * sbar_t;
|
||||
acid_fc = sec.acid_fc_lo + (sec.acid_fc_hi - sec.acid_fc_lo) * sbar_t;
|
||||
|
||||
/* Kick */
|
||||
if (KICK_PAT[sec.kick_pat][step]) {
|
||||
kick_trig(&kick);
|
||||
}
|
||||
/* Hi-hats */
|
||||
if (sec.use_hats) {
|
||||
int hv = HAT_PAT[sec.hat_pat][step];
|
||||
if (hv > 0) { hat_trig(&hat, hv == 2); }
|
||||
}
|
||||
/* Snare on steps 4 and 12 */
|
||||
if (sec.use_snare && (step == 4 || step == 12)) {
|
||||
snare_trig(&snare);
|
||||
}
|
||||
/* Bass */
|
||||
{
|
||||
int note = BASS_PAT[sec.bass_pat][step];
|
||||
if (note >= 0) {
|
||||
bass_note(&bass, freq_of(root, note));
|
||||
}
|
||||
}
|
||||
/* Acid */
|
||||
if (sec.use_acid) {
|
||||
int note = ACID_PAT[sec.acid_pat][step];
|
||||
if (note >= 0) {
|
||||
acid_note(&acid, freq_of(root, note));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Synthesize ── */
|
||||
double k = kick_tick(&kick);
|
||||
double h = hat_tick(&hat);
|
||||
double sn = snare_tick(&snare);
|
||||
double b = bass_tick(&bass, bass_fc);
|
||||
double a = acid_tick(&acid, acid_fc);
|
||||
|
||||
/* Stereo mix: slight panning differences */
|
||||
double mid = k + sn * 0.9 + b;
|
||||
double l = mid + h * 0.7 + a * 0.9;
|
||||
double r = mid + h * 0.9 + a * 0.6;
|
||||
|
||||
/* Delay-based stereo width (ping-pong on hats + acid) */
|
||||
dbuf[dpos] = h * 0.25 + a * 0.18;
|
||||
double delayed = dbuf[(dpos - dtap + DLEN) % DLEN];
|
||||
l += delayed * 0.35;
|
||||
r -= delayed * 0.20;
|
||||
dpos = (dpos + 1) % DLEN;
|
||||
|
||||
/* Soft saturation (tanh limiter) */
|
||||
l = tanh(l * 0.82);
|
||||
r = tanh(r * 0.82);
|
||||
|
||||
float out[2] = { (float)l, (float)r };
|
||||
fwrite(out, sizeof(float), 2, stdout);
|
||||
|
||||
/* ── Advance sequencer ── */
|
||||
if (++sample_in_step >= SP16) {
|
||||
sample_in_step = 0;
|
||||
step = (step + 1) % 16;
|
||||
if (step == 0) { bar++; }
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user