Skip to content

Instantly share code, notes, and snippets.

@jturcotte
Created February 14, 2024 23:33
Show Gist options
  • Save jturcotte/f7ddbe243c9290222c63b00bb43131a2 to your computer and use it in GitHub Desktop.
Save jturcotte/f7ddbe243c9290222c63b00bb43131a2 to your computer and use it in GitHub Desktop.

Tetris (GB) - #chiptrack

Play this song

  • In the Chiptrack Web Player.
  • In the desktop version from a command line prompt:
    chiptrack https://gist.github.com/jturcotte/f7ddbe243c9290222c63b00bb43131a2

What is this?

This gist is a Chiptrack song encoded in Markdown. It contains the following files:

  • This file: Chiptrack looks for the first .ct.md file in the gist and parses its "Song" and "Pattern [N]" and "Settings" sections.
  • instruments.wat: Contains the compiled instruments as a WebAssembly Text file. The name must be referred from the .ct.md file's Settings section.
  • instruments.zig (Optional): This is the source code for the instruments. This is attached to the gist to allow modifying and recompiling them.
  • build.zig (Optional): A zig build file that instructs the Zig compiler how to produce instruments.wat out of instruments.zig.

Other songs?

Try the GitHub Gist search.

Song

Pattern 1

S1 T1 W N
B-4. E-5. E-2 -
- - - -
- - E-3 C-4.
- - - -
G#4. B-4. E-2 -
- - - -
A-4. C-5. E-3 C-4.
- - - -
B-4. D-5. E-2 -
- - - -
E-5. - E-3 C-4.
D-5. - - C-4.
A-4. C-5. E-2 -
- - - -
G#4. B-4. E-3 C-4.
- - - -

Pattern 2

S1 T1 W N
E-4. A-4. A-2 -
- - - -
- - A-3 C-4.
- - - -
- A-4. A-2 -
- - - -
A-4. C-5. A-3 C-4.
- - - -
C-5. E-5. A-2 -
- - - -
- - A-3 C-4.
- - - -
B-4. D-5. A-2 C-4.
- - - -
A-4. C-5. A-3 C-4.
- - - -

Pattern 3

S1 T1 W N
G#4. B-4. G#2 -
G#4. - - -
E-4. - G#3 C-4.
- - - -
G#4. - G#2 -
- - - -
A-4. C-5. G#3 C-4.
- - - -
B-4. D-5. E-2 -
- - - -
- - E-3 C-4.
- - - C-4.
C-5. E-5. E-2 -
- - - -
- - E-3 C-4.
- - - -

Pattern 4

S1 T1 W N
A-4. C-5. A-2 -
- - - -
- - A-3 C-4.
- - - -
E-4. A-4. A-2 -
- - - -
- - A-3 C-4.
- - - -
E-4. A-4. A-2 -
- - - -
- - A-3 C-4.
- - - -
- - B-2 C-4.
- - - -
- - C-3 C-4.
- - - -

Pattern 5

S1 T1 W N
D-3. - D-3 -
- - - -
F-4. D-5. D-2. C-4.
- - - -
- - - -
- - - -
A-4. F-5. D-2. C-4.
- - - -
C-5. A-5. - -
- - - -
C-5. - D-2 C-4.
C-5. - - C-4.
B-4. G-5. A-2 -
- - - -
A-4. F-5. F-2 C-4.
- - - -

Pattern 6

S1 T1 W N
G-4. E-5. C-2 -
- - - -
- - C-3. C-4.
- - - -
- - - -
- - - -
E-4. C-5. C-3 C-4.
- - - -
G-4. E-5. C-2 -
- - - -
A-4. - G-2 C-4.
G-4. - - -
F-4. D-5. G-2. C-4.
- - - -
E-4. C-5. - C-4.
- - - -

Pattern 7

S1 T1 W N
G#4. B-4. B-2 -
- - - -
E-4. - B-3. C-4.
- - - -
G#4. B-4. - -
- - - -
A-4. C-5. B-3. C-4.
- - - -
B-4. D-5. - -
- - - -
G#4. - E-3. C-4.
- - - C-4.
C-5. E-5. - -
- - - -
G#4. - G#3 C-4.
- - - -

Pattern 8

S1 T1 W N
A-4. C-5. A-2 -
C-5. - - -
E-4. - E-3 C-4.
E-4. - - -
E-4. A-4. A-2 -
- - - -
- - E-3 C-4.
- - - -
E-4. A-4. A-2 -
- - - -
- - - C-4.
- - - -
E-4. - - C-4.
- - - -
- - - C-4.
- - - -

Pattern 9

S2 T2 W N
C-4 E-4 A-3 -
- - - -
- - E-4 C-4.
- - - -
- - A-3 -
- - - -
- - E-4 C-4.
- - - -
A-3 C-4 A-3 -
- - - -
- - E-4 C-4.
- - - C-4.
- - A-3 -
- - - -
- - E-4 C-4.
- - - -

Pattern 10

S2 T2 W N
B-3 D-4 G#3 -
- - - -
- - E-4 C-4.
- - - -
- - G#3 -
- - - -
- - E-4 C-4.
- - - -
G#3 B-3 G#3 -
- - - -
- - E-4 C-4.
- - - -
- - G#3 C-4.
- - - -
- - E-4 C-4.
- - - -

Pattern 11

S2 T2 W N
A-3 C-4 A-3 -
- - - -
- - E-4 C-4.
- - - -
- - A-3 -
- - - -
- - E-4 C-4.
- - - -
E-3 A-3 A-3 -
- - - -
- - E-4 C-4.
- - - C-4.
- - A-3 -
- - - -
- - E-4 C-4.
- - - -

Pattern 12

S2 T2 W N
E-3 G#3 G#3 -
- - - -
- - E-4 C-4.
- - - -
- - G#3 -
- - - -
- - E-4. C-4.
- - - -
B-3 - - -
- - - -
- - - C-4.
- - - -
- . - . - C-4.
- - - -
- - - C-4.
- - - -

Pattern 13

S2 T2 W N
C-4 E-4 A-3 -
- - - -
- - E-4 C-4.
- - - -
- - A-3 -
- - - -
- - E-4 C-4.
- - - -
A-3 C-4 A-3 -
- - - -
- - E-4 C-4.
- - - C-4.
- - A-3 -
- - - -
- - E-4 C-4.
- - - -

Pattern 14

S2 T2 W N
B-3 D-4 G#3 -
- - - -
- - E-4 C-4.
- - - -
- - G#3 -
- - - -
- - E-4 C-4.
- - - -
G#3 B-3 G#3 -
- - - -
- - E-4 C-4.
- - - -
- - G#3 C-4.
- - - -
- - E-4 C-4.
- - - -

Pattern 15

S2 T2 W N
A-3 C-4 A-3 -
- - - -
- - E-4 C-4.
- - - -
C-4 E-4 A-3 -
- - - -
- - E-4 C-4.
- - - -
E-4 A-4 A-3 -
- - - -
- - E-4 C-4.
- - - C-4.
- - A-3 -
- - - -
- - E-4 C-4.
- - - -

Pattern 16

S2 T2 W N
D-4 G#4 G#3 -
- - - -
- - E-4 C-4.
- - - -
- . - G#3 -
- - - -
- - E-4. C-4.
- - - -
- - . - -
- - - -
- - - C-4.
- - - -
- - - C-4.
- - - -
- - - C-4.
- - - -

Settings

  • InstrumentsFile: instruments.wat
  • FramesPerStep: 6
// Copyright © 2024 Jocelyn Turcotte <turcotte.j@gmail.com>
// SPDX-License-Identifier: CC0-1.0
//
// After modifying the instruments Zig source code, you must re-compile the WebAssembly file that can be executed by Chiptrack.
// This file instructs the Zig compiler how to compile it and for this you need
// - The Zig compiler, available at https://ziglang.org/download/
// - ct.zig, the Chiptrack instruments support module, available at https://raw.githubusercontent.com/jturcotte/chiptrack/v0.3/instruments/ct.zig
// - wasm2wat in your PATH, available at https://github.com/WebAssembly/wabt/releases
//
// Then to rebuild the instruments .wat file from modified source code:
// zig build
//
// The compiled instruments.wat file will be in the source folder, beside the zig file.
// Chiptrack will reload it automatically if the song is currently loaded.
const std = @import("std");
pub fn build(b: *std.Build) void {
// Build options, "zig build --help" will show their usage.
const source_file =
b.option([]const u8, "source", "Instruments source file to compile (default: instruments.zig).") orelse "instruments.zig";
const ct_zig_path =
b.option([]const u8, "ct.zig", "Path to ct.zig (default: ct.zig).") orelse "ct.zig";
const wat_out =
b.option(bool, "wat", "Compile the instruments to WAT (Web Assembly Text Format) instead of binary WASM. Chiptrack can load either format but this is better for GitHub gist uploads and requires wasm2wat in PATH (default: true).") orelse true;
if (std.fs.cwd().access(ct_zig_path, .{}) == std.fs.Dir.AccessError.FileNotFound)
@panic("ct.zig was not found but is necessary to build. You can download it from https://raw.githubusercontent.com/jturcotte/chiptrack/v0.3/instruments/ct.zig .");
// Listing the module here also allows editors like VSCode with Zig language support to discover it and provide code completion.
const ct_module = b.addModule("ct", .{ .source_file = .{ .path = ct_zig_path } });
// Build the wasm file.
const wasm = b.addExecutable(.{
.name = std.fs.path.stem(source_file),
.root_source_file = .{ .path = source_file },
.target = .{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
},
.optimize = .ReleaseFast,
});
wasm.rdynamic = true;
wasm.export_table = true;
wasm.max_memory = 65536;
wasm.stack_size = 8192;
wasm.strip = !wat_out;
wasm.addModule("ct", ct_module);
const wf = b.addWriteFiles();
b.getInstallStep().dependOn(&wf.step);
if (wat_out) {
// Generate the wat file by calling wasm2wat on the compiled wasm.
const wasm2wat = b.addSystemCommand(&.{ "wasm2wat", "-f" });
wasm2wat.addFileArg(wasm.getEmittedBin());
const final_url = std.fmt.allocPrint(b.allocator, "{s}.wat", .{wasm.name}) catch unreachable;
// Copy the output wat file into the source folder, beside the zig file.
wf.addCopyFileToSource(wasm2wat.captureStdOut(), final_url);
} else {
// Copy the wasm file into the source folder, beside the zig file.
wf.addCopyFileToSource(wasm.getEmittedBin(), std.fmt.allocPrint(b.allocator, "{s}.wasm", .{wasm.name}) catch unreachable);
}
}
(module
(type (;0;) (func (param i32 i32 i32 i32 i32 i32 i32)))
(type (;1;) (func (param i32 i32)))
(type (;2;) (func))
(type (;3;) (func (param i32 i32 i32 i32)))
(type (;4;) (func (param i32 i32 i32)))
(import "env" "set_instrument_at_column" (func $set_instrument_at_column (type 0)))
(import "env" "gba_set_sound_reg" (func $gba_set_sound_reg (type 1)))
(import "env" "gba_set_wave_table" (func $gba_set_wave_table (type 1)))
(func $_start (type 2)
(call $set_instrument_at_column
(i32.const 8205)
(i32.const 0)
(i32.const 0)
(i32.const 1)
(i32.const 0)
(i32.const 0)
(i32.const 0))
(call $set_instrument_at_column
(i32.const 8199)
(i32.const 0)
(i32.const 0)
(i32.const 2)
(i32.const 3)
(i32.const 0)
(i32.const 0))
(call $set_instrument_at_column
(i32.const 8202)
(i32.const 1)
(i32.const 0)
(i32.const 4)
(i32.const 0)
(i32.const 0)
(i32.const 0))
(call $set_instrument_at_column
(i32.const 8196)
(i32.const 1)
(i32.const 0)
(i32.const 5)
(i32.const 6)
(i32.const 0)
(i32.const 0))
(call $set_instrument_at_column
(i32.const 8192)
(i32.const 2)
(i32.const 0)
(i32.const 7)
(i32.const 8)
(i32.const 0)
(i32.const 0))
(call $set_instrument_at_column
(i32.const 8194)
(i32.const 3)
(i32.const 0)
(i32.const 9)
(i32.const 0)
(i32.const 0)
(i32.const 0)))
(func $instruments.square1_1.press (type 3) (param i32 i32 i32 i32)
(call $gba_set_sound_reg
(i32.const 67108962)
(i32.const 17280))
(call $gba_set_sound_reg
(i32.const 67108964)
(i32.or
(i32.and
(i32.sub
(i32.const 0)
(i32.div_u
(i32.const 33554432)
(local.get 0)))
(i32.const 2047))
(i32.const 32768))))
(func $instruments.square1_2.press (type 3) (param i32 i32 i32 i32)
(call $gba_set_sound_reg
(i32.const 67108962)
(i32.const 12416))
(call $gba_set_sound_reg
(i32.const 67108964)
(i32.or
(i32.and
(i32.sub
(i32.const 0)
(i32.div_u
(i32.const 33554432)
(local.get 0)))
(i32.const 2047))
(i32.const 32768))))
(func $instruments.square1_2.release (type 4) (param i32 i32 i32)
(call $gba_set_sound_reg
(i32.const 67108962)
(i32.const 128))
(call $gba_set_sound_reg
(i32.const 67108964)
(i32.or
(i32.and
(i32.sub
(i32.const 0)
(i32.div_u
(i32.const 33554432)
(local.get 0)))
(i32.const 2047))
(i32.const 32768))))
(func $instruments.square2_1.press (type 3) (param i32 i32 i32 i32)
(call $gba_set_sound_reg
(i32.const 67108968)
(i32.const 33920))
(call $gba_set_sound_reg
(i32.const 67108972)
(i32.or
(i32.and
(i32.sub
(i32.const 0)
(i32.div_u
(i32.const 33554432)
(local.get 0)))
(i32.const 2047))
(i32.const 32768))))
(func $instruments.square2_2.press (type 3) (param i32 i32 i32 i32)
(call $gba_set_sound_reg
(i32.const 67108968)
(i32.const 20608))
(call $gba_set_sound_reg
(i32.const 67108972)
(i32.or
(i32.and
(i32.sub
(i32.const 0)
(i32.div_u
(i32.const 33554432)
(local.get 0)))
(i32.const 2047))
(i32.const 32768))))
(func $instruments.square2_2.release (type 4) (param i32 i32 i32)
(call $gba_set_sound_reg
(i32.const 67108968)
(i32.const 128))
(call $gba_set_sound_reg
(i32.const 67108972)
(i32.or
(i32.and
(i32.sub
(i32.const 0)
(i32.div_u
(i32.const 33554432)
(local.get 0)))
(i32.const 2047))
(i32.const 32768))))
(func $instruments.wave_1.press (type 3) (param i32 i32 i32 i32)
(local i32)
(call $gba_set_wave_table
(i32.const 8208)
(i32.const 16))
(i32.store8 offset=8224
(i32.const 0)
(i32.xor
(local.tee 4
(i32.load8_u offset=8224
(i32.const 0)))
(i32.const 1)))
(call $gba_set_sound_reg
(i32.const 67108976)
(select
(i32.const 128)
(i32.const 192)
(local.get 4)))
(call $gba_set_sound_reg
(i32.const 67108978)
(i32.const 8192))
(call $gba_set_sound_reg
(i32.const 67108980)
(i32.or
(i32.and
(i32.sub
(i32.const 0)
(i32.div_u
(i32.const 16777216)
(local.get 0)))
(i32.const 2047))
(i32.const 32768))))
(func $instruments.wave_1.release (type 4) (param i32 i32 i32)
(call $gba_set_sound_reg
(i32.const 67108978)
(i32.const 0))
(call $gba_set_sound_reg
(i32.const 67108980)
(i32.or
(i32.and
(i32.sub
(i32.const 0)
(i32.div_u
(i32.const 16777216)
(local.get 0)))
(i32.const 2047))
(i32.const 32768))))
(func $instruments.noise_1.press (type 3) (param i32 i32 i32 i32)
(call $gba_set_sound_reg
(i32.const 67108984)
(i32.const 41350))
(call $gba_set_sound_reg
(i32.const 67108988)
(i32.const 49152)))
(table (;0;) 10 10 funcref)
(memory (;0;) 1 1)
(global $__stack_pointer (mut i32) (i32.const 8192))
(export "memory" (memory 0))
(export "_start" (func $_start))
(export "__indirect_function_table" (table 0))
(elem (;0;) (i32.const 1) func $instruments.square1_1.press $instruments.square1_2.press $instruments.square1_2.release $instruments.square2_1.press $instruments.square2_2.press $instruments.square2_2.release $instruments.wave_1.press $instruments.wave_1.release $instruments.noise_1.press)
(data $.rodata (i32.const 8192) "W\00N\00T2\00S2\00T1\00S1\00\01#Eg\89\ab\cd\ef\fe\dc\ba\98vT2\10"))
const std = @import("std");
const math = std.math;
const ct = @import("ct");
const gba = ct.gba;
const square1_1 = struct {
pub fn press(freq: u32, _: u8, _: i8, _: i8) callconv(.C) void {
(gba.EnvDutyLen{ .length = 0, .duty = gba.dut_2_4, .env_interval = 3, .env_dir = gba.env_dec, .env_start = 4 }).writeTo(gba.square1);
(gba.CtrlFreq{ .trigger = 1 }).withSquareFreq(freq).writeTo(gba.square1);
}
pub const id: [*:0]const u8 = "S1";
};
const square1_2 = struct {
pub fn press(freq: u32, _: u8, _: i8, _: i8) callconv(.C) void {
(gba.EnvDutyLen{ .length = 0, .duty = gba.dut_2_4, .env_interval = 0, .env_dir = gba.env_dec, .env_start = 3 }).writeTo(gba.square1);
(gba.CtrlFreq{ .trigger = 1 }).withSquareFreq(freq).writeTo(gba.square1);
}
pub fn release(freq: u32, _: u8, _: u32) callconv(.C) void {
(gba.EnvDutyLen{ .length = 0, .duty = gba.dut_2_4, .env_interval = 0, .env_dir = gba.env_dec, .env_start = 0 }).writeTo(gba.square1);
(gba.CtrlFreq{ .trigger = 1 }).withSquareFreq(freq).writeTo(gba.square1);
}
pub const id: [*:0]const u8 = "S2";
};
const square2_1 = struct {
pub fn press(freq: u32, _: u8, _: i8, _: i8) callconv(.C) void {
(gba.EnvDutyLen{ .length = 0, .duty = gba.dut_2_4, .env_interval = 4, .env_dir = gba.env_dec, .env_start = 8 }).writeTo(gba.square2);
(gba.CtrlFreq{ .trigger = 1 }).withSquareFreq(freq).writeTo(gba.square2);
}
pub const id: [*:0]const u8 = "T1";
};
const square2_2 = struct {
pub fn press(freq: u32, _: u8, _: i8, _: i8) callconv(.C) void {
(gba.EnvDutyLen{ .length = 0, .duty = gba.dut_2_4, .env_interval = 0, .env_dir = gba.env_dec, .env_start = 5 }).writeTo(gba.square2);
(gba.CtrlFreq{ .trigger = 1 }).withSquareFreq(freq).writeTo(gba.square2);
}
pub fn release(freq: u32, _: u8, _: u32) callconv(.C) void {
(gba.EnvDutyLen{ .length = 0, .duty = gba.dut_2_4, .env_interval = 0, .env_dir = gba.env_dec, .env_start = 0 }).writeTo(gba.square2);
(gba.CtrlFreq{ .trigger = 1 }).withSquareFreq(freq).writeTo(gba.square2);
}
pub const id: [*:0]const u8 = "T2";
};
const wave_1 = struct {
const table = gba.wav(0x0123456789abcdeffedcba9876543210);
pub fn press(freq: u32, _: u8, _: i8, _: i8) callconv(.C) void {
gba.WaveRam.setTable(&table);
(gba.WaveVolLen{ .length = 0, .volume = gba.vol_100 }).writeTo(gba.wave);
(gba.CtrlFreq{ .trigger = 1 }).withWaveFreq(freq).writeTo(gba.wave);
}
pub fn release(freq: u32, _: u8, _: u32) callconv(.C) void {
(gba.WaveVolLen{ .volume = gba.vol_0 }).writeTo(gba.wave);
(gba.CtrlFreq{ .trigger = 1 }).withWaveFreq(freq).writeTo(gba.wave);
}
pub const id: [*:0]const u8 = "W";
};
const noise_1 = struct {
pub fn press(_: u32, _: u8, _: i8, _: i8) callconv(.C) void {
(gba.EnvDutyLen{ .length = 6, .duty = gba.dut_2_4, .env_interval = 1, .env_dir = gba.env_dec, .env_start = 10 }).writeTo(gba.noise);
(gba.NoiseCtrlFreq{ .freq_div = gba.div_8, .width = gba.wid_15, .freq = 0, .length_enabled = 1, .trigger = 1 }).writeTo(gba.noise);
}
pub const id: [*:0]const u8 = "N";
};
pub fn main() void {
ct.registerInstrument(square1_1, 0);
ct.registerInstrument(square1_2, 0);
ct.registerInstrument(square2_1, 1);
ct.registerInstrument(square2_2, 1);
ct.registerInstrument(wave_1, 2);
ct.registerInstrument(noise_1, 3);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment