Last active
February 4, 2026 09:57
-
-
Save AlexanderBrevig/e07cb5f4ea1d1ac8d4edce8c408fe211 to your computer and use it in GitHub Desktop.
SUPERSAW https://atosynth.blogspot.com/2026/02/the-super-saw-code.html `g++ -o sandstorm sandstorm.cpp -lm && ./sandstorm`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <cstdio> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <cstdlib> | |
| #include <cmath> | |
| #include <ctime> | |
| #define i64(x) (static_cast<int64_t>((int32_t)x)) | |
| // Proper 24-bit signed integer type | |
| class int24_t { | |
| private: | |
| int32_t value; | |
| static constexpr int32_t MASK = 0xFFFFFF; | |
| static constexpr int32_t SIGN_BIT = 0x800000; | |
| void mask() { | |
| value = (value & MASK); | |
| // Sign extend if needed | |
| if (value & SIGN_BIT) { | |
| value |= 0xFF000000; | |
| } | |
| } | |
| public: | |
| int24_t() : value(0) {} | |
| int24_t(int32_t v) : value(v) { mask(); } | |
| // Operators | |
| int24_t& operator=(int32_t v) { value = v; mask(); return *this; } | |
| int24_t& operator+=(const int24_t& other) { value += other.value; mask(); return *this; } | |
| int24_t& operator-=(const int24_t& other) { value -= other.value; mask(); return *this; } | |
| int24_t& operator*=(const int24_t& other) { value *= other.value; mask(); return *this; } | |
| int24_t& operator&=(const int24_t& other) { value &= other.value; return *this; } | |
| int24_t operator+(const int24_t& other) const { return int24_t(value + other.value); } | |
| int24_t operator+(int32_t other) const { return int24_t(value + other); } | |
| int24_t operator-(const int24_t& other) const { return int24_t(value - other.value); } | |
| int24_t operator-(int32_t other) const { return int24_t(value - other); } | |
| int24_t operator*(const int24_t& other) const { return int24_t(value * other.value); } | |
| int24_t operator*(int32_t other) const { return int24_t(value * other); } | |
| int24_t operator&(const int24_t& other) const { return int24_t(value & other.value); } | |
| int24_t operator&(int32_t mask) const { return int24_t(value & mask); } | |
| int24_t operator>>(int bits) const { return int24_t(value >> bits); } | |
| int24_t operator<<(int bits) const { return int24_t(value << bits); } | |
| // Conversions | |
| operator int32_t() const { return value; } | |
| operator int16_t() const { return (int16_t)(value >> 8); } | |
| int32_t raw() const { return value; } | |
| }; | |
| int24_t saw[7]; | |
| const int24_t MASK_24BIT = int24_t(0xFFFFFF); | |
| const int24_t detune_table[7] = {0, 128, -128, 408, -412, 704, -720}; | |
| int24_t low_pass(int24_t sample) { | |
| static int24_t filter_state = 0; | |
| filter_state = ((int32_t)filter_state * 3 + sample) >> 2; | |
| return filter_state; | |
| } | |
| int24_t high_pass(int24_t x) { | |
| return x; | |
| } | |
| int24_t next(int24_t pitch, int24_t mix, int24_t detune) { | |
| int24_t sum = 0; | |
| for (int i = 0; i < 7; i++) { | |
| int24_t scaled_pitch = (i64(pitch) * i64(detune)) >> 23; | |
| int24_t voice_detune = ((i64(detune_table[i]) * i64(scaled_pitch)) >> 7); | |
| saw[i] += pitch + voice_detune; // phase accumulator should roll over on 24 bits | |
| if (i == 0) { | |
| sum += (int32_t)((i64(saw[i]) * 25) >> 7); | |
| } else { | |
| sum += (int32_t)((i64(saw[i]) * (mix >> 16)) >> 7); | |
| } | |
| } | |
| return high_pass(sum); | |
| } | |
| struct WAV_Header { | |
| char chunk_id[4]; | |
| int32_t chunk_size; | |
| char format[4]; | |
| char subchunk1_id[4]; | |
| int32_t subchunk1_size; | |
| int16_t audio_format; | |
| int16_t num_channels; | |
| int32_t sample_rate; | |
| int32_t byte_rate; | |
| int16_t block_align; | |
| int16_t bits_per_sample; | |
| char subchunk2_id[4]; | |
| int32_t subchunk2_size; | |
| }; | |
| struct Note { | |
| int note_num; | |
| int duration_16ths; | |
| }; | |
| float midi_note_to_freq(int note_num) { | |
| return 440.0f * powf(2.0f, (note_num - 69.0f) / 12.0f); | |
| } | |
| int main() { | |
| srand((unsigned int)time(NULL)); | |
| const int sample_rate_internal = 88200; | |
| const int sample_rate_output = 44100; | |
| const int BPM = 134; | |
| const int24_t mix_max = 0x215000; | |
| const int24_t mix = int24_t(0x105000); | |
| const int24_t detune_max = 0x028200; | |
| const int24_t detune = int24_t(0x010200); | |
| const Note pattern[] = { | |
| {71, 1}, {71, 1}, {71, 1}, {71, 1}, {71, 2}, {71, 1}, {71, 1}, | |
| {71, 1}, {71, 1}, {71, 1}, {71, 1}, {71, 2}, {76, 1}, {76, 1}, | |
| {76, 1}, {76, 1}, {76, 1}, {76, 1}, {76, 2}, {74, 1}, {74, 1}, | |
| {74, 1}, {74, 1}, {74, 1}, {74, 1}, {74, 2}, {69, 1}, {69, 2}, | |
| }; | |
| const int num_notes = sizeof(pattern) / sizeof(pattern[0]); | |
| float beat_duration = 60.0f / BPM; | |
| float sixteenth_duration = beat_duration / 4.0f; | |
| int samples_per_sixteenth = (int)(sample_rate_internal * sixteenth_duration); | |
| int total_16ths = 0; | |
| for (int i = 0; i < num_notes; i++) { | |
| total_16ths += pattern[i].duration_16ths; | |
| } | |
| int num_samples_internal = total_16ths * samples_per_sixteenth; | |
| int num_samples_output = num_samples_internal / 2; | |
| int16_t* audio_data = (int16_t*)malloc(num_samples_output * sizeof(int16_t)); | |
| int sample_idx_internal = 0; | |
| int sample_idx_output = 0; | |
| for (int note_idx = 0; note_idx < num_notes; note_idx++) { | |
| float freq = midi_note_to_freq(pattern[note_idx].note_num); | |
| int24_t pitch = int24_t((freq / sample_rate_internal) * 16777216.0f); | |
| int total_duration_samples = pattern[note_idx].duration_16ths * samples_per_sixteenth; | |
| int note_on_samples = total_duration_samples / 2; | |
| int silence_samples = total_duration_samples - note_on_samples; | |
| // Note ON | |
| for (int i = 0; i < note_on_samples; i++) { | |
| int24_t sample = next(pitch, mix, detune); | |
| if (sample_idx_internal % 2 == 0) { | |
| audio_data[sample_idx_output++] = (int16_t)low_pass(sample); | |
| } | |
| sample_idx_internal++; | |
| } | |
| // Silence (note OFF) | |
| for (int i = 0; i < silence_samples; i++) { | |
| if (sample_idx_internal % 2 == 0) { | |
| audio_data[sample_idx_output++] = 0; | |
| } | |
| sample_idx_internal++; | |
| } | |
| // Initialize with random phase | |
| for (int i = 0; i < 7; i++) { | |
| saw[i] = int24_t(((int32_t)rand() * 65536 + (int32_t)rand())) & MASK_24BIT; | |
| } | |
| } | |
| // Create WAV header | |
| WAV_Header header; | |
| memcpy(header.chunk_id, "RIFF", 4); | |
| memcpy(header.format, "WAVE", 4); | |
| memcpy(header.subchunk1_id, "fmt ", 4); | |
| memcpy(header.subchunk2_id, "data", 4); | |
| header.subchunk1_size = 16; | |
| header.audio_format = 1; | |
| header.num_channels = 1; | |
| header.sample_rate = sample_rate_output; | |
| header.bits_per_sample = 16; | |
| header.block_align = header.num_channels * header.bits_per_sample / 8; | |
| header.byte_rate = header.sample_rate * header.block_align; | |
| header.subchunk2_size = sample_idx_output * sizeof(int16_t); | |
| header.chunk_size = 36 + header.subchunk2_size; | |
| FILE* fp = fopen("sandstorm.wav", "wb"); | |
| if (!fp) { | |
| printf("Error opening file\n"); | |
| return 1; | |
| } | |
| fwrite(&header, sizeof(WAV_Header), 1, fp); | |
| fwrite(audio_data, sizeof(int16_t), sample_idx_output, fp); | |
| fclose(fp); | |
| free(audio_data); | |
| printf("Generated sandstorm_simple.wav (C++ version)\n"); | |
| printf("Duration: %.2f seconds\n", (float)sample_idx_output / sample_rate_output); | |
| printf("Notes: %d\n", num_notes); | |
| printf("Total 16th notes: %d\n", total_16ths); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment