-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Includes the commands `player`/`run_player` to build and run the player demo, and `demo`/`run_demo` to build and run the zig demo.
- Loading branch information
Showing
5 changed files
with
755 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,3 +16,6 @@ test/curm/* | |
test/new/* | ||
test/newm/* | ||
|
||
# Zig folders | ||
*zig-cache/ | ||
zig-out/ |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
const std = @import("std"); | ||
|
||
const Ym2612Emu = enum { | ||
mame, | ||
nuked, | ||
gens, | ||
}; | ||
|
||
const Options = struct { | ||
// Default emulators to build (all of them! ;) | ||
ay: bool = true, | ||
gbs: bool = true, | ||
gym: bool = true, | ||
hes: bool = true, | ||
kss: bool = true, | ||
nsf: bool = true, | ||
nsfe: bool = true, | ||
sap: bool = true, | ||
spc: bool = true, | ||
vgm: bool = true, | ||
|
||
shared: bool = true, | ||
ym2612_emu: Ym2612Emu = .nuked, | ||
spc_isolated_echo_buffer: bool = false, | ||
}; | ||
|
||
fn linkZlib( | ||
b: *std.Build, | ||
cmp: *std.Build.Step.Compile, | ||
target: std.Build.ResolvedTarget, | ||
optimize: std.builtin.OptimizeMode, | ||
) !void { | ||
const z = b.addStaticLibrary(.{ | ||
.name = "z", | ||
.target = target, | ||
.optimize = optimize, | ||
}); | ||
z.linkLibC(); | ||
|
||
var files = std.ArrayList([]const u8).init(b.allocator); | ||
defer files.deinit(); | ||
for ([_][]const u8{ | ||
"adler32", "compress", "crc32", "deflate", "gzclose", "gzlib", "gzread", "gzwrite", | ||
"inflate", "infback", "inftrees", "inffast", "trees", "uncompr", "zutil", | ||
}) |s| | ||
try files.append(b.fmt("{s}.c", .{ s })); | ||
const src = b.dependency("zlib", .{ .target=target, .optimize=optimize }).path(""); | ||
z.addCSourceFiles(.{ .root=src, .files=files.items, .flags=&.{"-std=c89"} }); | ||
|
||
cmp.linkLibrary(z); | ||
cmp.addSystemIncludePath(src); | ||
cmp.defineCMacro("HAVE_ZLIB_H", null); | ||
} | ||
|
||
fn addSteps( | ||
b: *std.Build, exe: *std.Build.Step.Compile, name: []const u8, desc: []const u8, | ||
) void { | ||
const install = b.addInstallArtifact(exe, .{}); | ||
const step_install = b.step(name, b.fmt("Build {s}", .{desc})); | ||
step_install.dependOn(&install.step); | ||
|
||
const run = b.addRunArtifact(exe); | ||
run.step.dependOn(&install.step); | ||
const step_run = b.step( | ||
b.fmt("run_{s}", .{name}), b.fmt("Build and run {s}", .{desc}) ); | ||
step_run.dependOn(&run.step); | ||
if (b.args) |args| { | ||
run.addArgs(args); | ||
} | ||
} | ||
|
||
pub fn build(b: *std.Build) !void { | ||
const target = b.standardTargetOptions(.{}); | ||
const optimize = b.standardOptimizeOption(.{}); | ||
|
||
const defaults = Options{}; | ||
const opt = Options{ | ||
.ay = b.option(bool, "ay", | ||
"Enable support for Spectrum ZX music emulation") orelse defaults.ay, | ||
.gbs = b.option(bool, "gbs", | ||
"Enable support for Game Boy music emulation") orelse defaults.gbs, | ||
.gym = b.option(bool, "gym", | ||
"Enable Sega MegaDrive/Genesis music emulation") orelse defaults.gym, | ||
.hes = b.option(bool, "hes", | ||
"Enable PC Engine/TurboGrafx-16 music emulation") orelse defaults.hes, | ||
.kss = b.option(bool, "kss", | ||
"Enable MSX or other Z80 systems music emulation") orelse defaults.kss, | ||
.nsf = b.option(bool, "nsf", | ||
"Enable NES NSF music emulation") orelse defaults.nsf, | ||
.nsfe = b.option(bool, "nsfe", | ||
"Enable NES NSFE and NSF music emulation") orelse defaults.nsfe, | ||
.sap = b.option(bool, "sap", | ||
"Enable Atari SAP music emulation") orelse defaults.sap, | ||
.spc = b.option(bool, "spc", | ||
"Enable SNES SPC music emulation") orelse defaults.spc, | ||
.vgm = b.option(bool, "vgm", | ||
"Enable Sega VGM/VGZ music emulation") orelse defaults.vgm, | ||
.shared = b.option(bool, "shared", | ||
"Build shared library (set to false for static library)") orelse defaults.shared, | ||
.ym2612_emu = b.option(Ym2612Emu, "ym2612_emu", | ||
"Which YM2612 emulator to use.") orelse defaults.ym2612_emu, | ||
.spc_isolated_echo_buffer = b.option(bool, "spc_isolated_echo_buffer", | ||
"Enable isolated echo buffer on SPC emulator to allow correct playing of " ++ | ||
"\"dodgy\" SPC files made for various ROM hacks ran on ZSNES") | ||
orelse defaults.spc_isolated_echo_buffer, | ||
}; | ||
|
||
const gme = if (opt.shared) b.addSharedLibrary(.{ | ||
.name="gme", .target=target, .optimize=optimize, .pic=true | ||
}) else b.addStaticLibrary(.{ | ||
.name="gme", .target=target, .optimize=optimize | ||
}); | ||
gme.linkLibCpp(); | ||
try linkZlib(b, gme, target, optimize); | ||
|
||
const gme_src = "gme/"; | ||
var files = std.ArrayList([]const u8).init(b.allocator); | ||
defer files.deinit(); | ||
for ([_][]const u8{ | ||
"Blip_Buffer", "Classic_Emu", "Data_Reader", "Dual_Resampler", "Effects_Buffer", | ||
"Fir_Resampler", "gme", "Gme_File", "M3u_Playlist", "Multi_Buffer", "Music_Emu", | ||
}) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ gme_src, s })); | ||
|
||
if(opt.ay or opt.kss) | ||
try files.append(b.fmt("{s}Ay_Apu.cpp", .{ gme_src })); | ||
|
||
if(opt.vgm or opt.gym) { | ||
const ym_file = switch(opt.ym2612_emu) { | ||
.nuked => "Nuked", .mame => "MAME", .gens => "GENS" }; | ||
try files.append(b.fmt("{s}Ym2612_{s}.cpp", .{ gme_src, ym_file })); | ||
const ym_macro = switch(opt.ym2612_emu) { | ||
.nuked => "NUKED", .mame => "MAME", .gens => "GENS" }; | ||
gme.defineCMacro(b.fmt("VGM_YM2612_{s}", .{ ym_macro }), null); | ||
} | ||
|
||
if (opt.vgm or opt.gym or opt.kss) | ||
try files.append(b.fmt("{s}Sms_Apu.cpp", .{ gme_src })); | ||
|
||
if (opt.ay) | ||
for ([_][]const u8{ "Ay_Cpu", "Ay_Emu" }) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ gme_src, s })); | ||
|
||
if (opt.gbs) | ||
for ([_][]const u8{ "Gb_Apu", "Gb_Cpu", "Gb_Oscs", "Gbs_Emu" }) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ gme_src, s })); | ||
|
||
if (opt.gym) | ||
try files.append(b.fmt("{s}Gym_Emu.cpp", .{ gme_src })); | ||
|
||
if (opt.hes) | ||
for ([_][]const u8{ "Hes_Apu", "Hes_Cpu", "Hes_Emu" }) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ gme_src, s })); | ||
|
||
if (opt.kss) | ||
for ([_][]const u8{ "Kss_Cpu", "Kss_Emu", "Kss_Scc_Apu" }) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ gme_src, s })); | ||
|
||
if (opt.nsf or opt.nsfe) { | ||
for ([_][]const u8{ | ||
"Nsf_Emu", "Nes_Cpu", "Nes_Oscs", "Nes_Apu", "Nes_Fme7_Apu", | ||
"Nes_Namco_Apu", "Nes_Vrc6_Apu", "Nes_Fds_Apu", "Nes_Vrc7_Apu", | ||
}) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ gme_src, s })); | ||
for ([_][]const u8 { "emu2413", "panning" }) |s| | ||
try files.append(b.fmt("{s}ext/{s}.c", .{ gme_src, s })); | ||
} | ||
|
||
if (opt.nsfe) | ||
try files.append(b.fmt("{s}Nsfe_Emu.cpp", .{ gme_src })); | ||
|
||
if (opt.sap) | ||
for ([_][]const u8 { "Sap_Apu", "Sap_Cpu", "Sap_Emu" }) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ gme_src, s })); | ||
|
||
if (opt.spc) { | ||
for ([_][]const u8{ "Snes_Spc", "Spc_Cpu", "Spc_Dsp", "Spc_Emu", "Spc_Filter" }) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ gme_src, s })); | ||
if (opt.spc_isolated_echo_buffer) | ||
gme.defineCMacro("SPC_ISOLATED_ECHO_BUFFER", null); | ||
} | ||
|
||
if (opt.vgm) | ||
for ([_][]const u8{ "Vgm_Emu", "Vgm_Emu_Impl", "Ym2413_Emu" }) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ gme_src, s })); | ||
|
||
switch (target.result.cpu.arch.endian()) { | ||
.little => gme.defineCMacro("BLARGG_LITTLE_ENDIAN", "1"), | ||
.big => gme.defineCMacro("BLARGG_BIG_ENDIAN", "1"), | ||
} | ||
|
||
gme.addCSourceFiles(.{ .files=files.items, .flags=&.{"-fno-sanitize=undefined"} }); | ||
gme.addIncludePath(b.path(gme_src)); | ||
b.installArtifact(gme); | ||
|
||
const zig = "zig/"; | ||
const module = b.addModule("gme", .{ | ||
.root_source_file = b.path(zig ++ "gme.zig"), | ||
.target = target, | ||
.optimize = optimize, | ||
}); | ||
module.linkLibrary(gme); | ||
|
||
//--------------------------------------------------------------------------- | ||
// Add player demo | ||
const player = b.addExecutable(.{ | ||
.name = "gme_player", | ||
.target = target, | ||
.optimize = optimize, | ||
}); | ||
player.linkLibrary(gme); | ||
player.linkSystemLibrary("SDL2"); | ||
|
||
const unrar = b.dependency("unrar", .{ .target=target, .optimize=optimize }); | ||
player.linkLibrary(unrar.artifact("unrar")); | ||
player.addSystemIncludePath(unrar.path("src")); | ||
player.defineCMacro("RARDLL", null); | ||
player.defineCMacro("RAR_HDR_DLL_HPP", null); | ||
|
||
const player_src = "player/"; | ||
files.deinit(); | ||
files = std.ArrayList([]const u8).init(b.allocator); | ||
for ([_][]const u8{ "Audio_Scope", "Music_Player", "Archive_Reader", "player" }) |s| | ||
try files.append(b.fmt("{s}{s}.cpp", .{ player_src, s })); | ||
player.addCSourceFiles(.{ .files=files.items }); | ||
player.addIncludePath(b.path(gme_src)); | ||
addSteps(b, player, "player", "the player demo"); | ||
|
||
//--------------------------------------------------------------------------- | ||
// Add zig demo | ||
const basics = b.addExecutable(.{ | ||
.name = "demo", | ||
.root_source_file = b.path(zig ++ "basics.zig"), | ||
.target = target, | ||
.optimize = optimize, | ||
}); | ||
basics.root_module.addImport("gme", module); | ||
addSteps(b, basics, "demo", "the zig demo"); | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
.{ | ||
.name = "game-music-emu", | ||
.version = "0.6.4", | ||
.minimum_zig_version = "0.12.0", | ||
.dependencies = .{ | ||
.zlib = .{ | ||
.url = "https://github.com/madler/zlib/archive/refs/tags/v1.3.1.tar.gz", | ||
.hash = "1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb", | ||
}, | ||
.unrar = .{ | ||
.url = "https://github.com/myQwil/unrar/archive/9195ea6387623537e068e74c1b36d9ec499dd785.tar.gz", | ||
.hash = "122069a28e9d8b7ee7501a7febfadde698f1e6bc676c386c58c38ec25822a1948e13", | ||
}, | ||
}, | ||
.paths = .{ | ||
"gme", | ||
"player", | ||
"zig", | ||
"build.zig", | ||
"build.zig.zon", | ||
"license.gpl2.txt", | ||
"license.txt", | ||
"readme.txt", | ||
"test.m3u", | ||
"test.nsf", | ||
"test.vgz", | ||
}, | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
//! opens a game music file and records 10 seconds to "out.wav" | ||
const std = @import("std"); | ||
const gme = @import("gme"); | ||
|
||
const chan_count = 2; | ||
const header_size = 0x2C; | ||
|
||
pub fn main() !void { | ||
var args = std.process.args(); | ||
_ = args.skip(); | ||
const filename = args.next() orelse "test.nsf"; | ||
const track = if (args.next()) |arg| try std.fmt.parseInt(u32, arg, 10) else 0; | ||
|
||
// Open music file in new emulator | ||
const sample_rate = 48000; | ||
const emu = try gme.Emu.fromFile(filename, sample_rate); | ||
defer emu.delete(); | ||
|
||
// Start track | ||
try emu.startTrack(track); | ||
|
||
// Create a wave file | ||
const file = try std.fs.cwd().createFile("out.wav", .{}); | ||
defer file.close(); | ||
const writer = file.writer(); | ||
|
||
// Create buffer | ||
const buf_size = 4000; | ||
var buf: [buf_size]i16 = undefined; | ||
const bytes = @as([*]u8, @ptrCast(&buf))[0..buf_size * @sizeOf(i16)]; | ||
|
||
// Reserve space for header | ||
try writer.writeAll(bytes[0..header_size]); | ||
|
||
// Record 10 seconds of track | ||
const duration_secs = 10; | ||
const total_samples = duration_secs * sample_rate * chan_count; | ||
while (emu.tellSamples() < total_samples) { | ||
try emu.play(&buf); | ||
try writer.writeAll(bytes); | ||
} | ||
|
||
// Write the header | ||
try file.seekTo(0); | ||
const ds = total_samples * @sizeOf(i16); | ||
const rs = header_size - 8 + ds; | ||
const frame_size = chan_count * @sizeOf(i16); | ||
const bytes_per_second = sample_rate * frame_size; | ||
try writer.writeAll("RIFF"); | ||
try writer.writeInt(u32, rs, .little); | ||
try writer.writeAll("WAVE"); | ||
try writer.writeAll("fmt "); | ||
try writer.writeAll(&.{ | ||
0x10,0,0,0, // size of fmt chunk | ||
1,0, // uncompressed format | ||
chan_count,0, | ||
}); | ||
try writer.writeInt(u32, sample_rate, .little); | ||
try writer.writeInt(u32, bytes_per_second, .little); | ||
try writer.writeAll(&.{ | ||
frame_size,0, | ||
@bitSizeOf(i16),0, | ||
}); | ||
try writer.writeAll("data"); | ||
try writer.writeInt(u32, ds, .little); | ||
} |
Oops, something went wrong.