Add music loading/playing using portaudio and stb_vorbis.

This commit is contained in:
IntegratedQuantum 2023-06-05 23:11:40 +02:00
parent 4d092126cc
commit 4fcfd10307
23 changed files with 5796 additions and 1 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "mach-freetype"]
path = mach-freetype
url = https://github.com/hexops/mach-freetype
[submodule "portaudio"]
path = portaudio
url = git@github.com:PortAudio/portaudio.git

View File

@ -0,0 +1,34 @@
{
"type" : "mountain_grassland",
"minHeight" : 60,
"maxHeight" : 256,
"roughness" : 10,
"mountains" : 20,
"hills" : 50,
"rivers" : true,
"ground_structure" : [
],
"stoneBlock" : "cubyz:colorado_stone",
"structures" : [
{
"id" : "cubyz:simple_tree",
"leaves" : "cubyz:colorado_stone",
"log" : "cubyz:colorado_stone",
"top" : "cubyz:colorado_stone",
"chance" : 0.001,
"type" : "round",
"height" : 3,
"height_variation" : 1
},
{
"id" : "cubyz:boulder",
"chance" : 0.001,
"block" : "cubyz:colorado_stone",
"size" : 2,
"size_variance" : 7
}
]
}

View File

@ -0,0 +1,10 @@
{
"class" : "stone",
"hardness" : 20,
"breakingPower" : 1,
"drops" : [
"auto"
],
"model" : "cube",
"texture" : "cubyz:colorado_stone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,37 @@
TotalDemented ( https://www.youtube.com/channel/UCut9PCEq4cEJogfEPUXk7Gg ):
cubyz.ogg
leaves.ogg
in_the_depths.ogg
sinanimea:
sunrise.ogg
under_the_water_sky.ogg
Kevin MacLeod
Syncerely by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/5033-sincerely
License: https://filmmusic.io/standard-license
Gymnopedie No. 1 by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3837-gymnopedie-no-1
License: https://filmmusic.io/standard-license
Dark Times by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3611-dark-times
License: https://filmmusic.io/standard-license
Eastern Thought by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3692-eastern-thought
License: https://filmmusic.io/standard-license
Mana Two Part 2 by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/5713-mana-two-part-2
License: https://filmmusic.io/standard-license
Heart of the Beast by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3862-heart-of-the-beast
License: https://filmmusic.io/standard-license

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -43,7 +43,35 @@ pub fn build(b: *std.build.Builder) !void {
std.log.err("Unsupported target: {}\n", .{ target.getOsTag() });
}
}
exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c", "lib/stb_image_write.c"}, &[_][]const u8{"-g", "-O3"});
{ // compile portaudio from source:
exe.addIncludePath("portaudio/include");
exe.addIncludePath("portaudio/src/common");
exe.addCSourceFiles(&[_][]const u8 {
"portaudio/src/common/pa_allocation.c",
"portaudio/src/common/pa_converters.c",
"portaudio/src/common/pa_cpuload.c",
"portaudio/src/common/pa_debugprint.c",
"portaudio/src/common/pa_dither.c",
"portaudio/src/common/pa_front.c",
"portaudio/src/common/pa_process.c",
"portaudio/src/common/pa_ringbuffer.c",
"portaudio/src/common/pa_stream.c",
"portaudio/src/common/pa_trace.c",
}, &[_][]const u8{"-g", "-O3"});
if(target.getOsTag() == .windows) {
// TODO: Choose a host API
} else if(target.getOsTag() == .linux) {
// unix:
exe.addCSourceFiles(&[_][]const u8 {"portaudio/src/os/unix/pa_unix_hostapis.c", "portaudio/src/os/unix/pa_unix_util.c"}, &[_][]const u8{"-g", "-O3", "-DPA_USE_ALSA"});
exe.addIncludePath("portaudio/src/os/unix");
// ALSA:
exe.addCSourceFiles(&[_][]const u8 {"portaudio/src/hostapi/alsa/pa_linux_alsa.c"}, &[_][]const u8{"-g", "-O3"});
exe.linkSystemLibrary("alsa");
} else {
std.log.err("Unsupported target: {}\n", .{ target.getOsTag() });
}
}
exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c", "lib/stb_image_write.c", "lib/stb_vorbis.c"}, &[_][]const u8{"-g", "-O3"});
exe.addAnonymousModule("gui", .{.source_file = .{.path = "src/gui/gui.zig"}});
exe.addAnonymousModule("server", .{.source_file = .{.path = "src/server/server.zig"}});
const harfbuzzModule = freetype.harfbuzzModule(b);

5584
include/stb/stb_vorbis.h Normal file

File diff suppressed because it is too large Load Diff

3
lib/stb_vorbis.c Normal file
View File

@ -0,0 +1,3 @@
#define STB_VORBIS_NO_PUSHDATA_API
#define STB_VORBIS_NO_INTEGER_CONVERSION
#include "stb/stb_vorbis.h"

1
portaudio Submodule

@ -0,0 +1 @@
Subproject commit cb8d3dcbc6fa74c67f3e236be89b12d5630da141

91
src/audio.zig Normal file
View File

@ -0,0 +1,91 @@
const std = @import("std");
const main = @import("root");
const c = @cImport ({
@cInclude("portaudio.h");
@cDefine("STB_VORBIS_HEADER_ONLY", "");
@cInclude("stb/stb_vorbis.h");
});
fn handleError(paError: c_int) void {
if(paError != c.paNoError) {
std.log.err("PortAudio error: {s}", .{c.Pa_GetErrorText(paError)});
@panic("Audio error");
}
}
// TODO: Proper sound and music system
// TODO: volume control
var stream: ?*c.PaStream = null;
var musicData: []f32 = undefined; // TODO: Add a caching system for music data.
const sampleRate = 44100;
pub fn init() !void {
handleError(c.Pa_Initialize());
handleError(c.Pa_OpenDefaultStream(
&stream,
0, // input channels
2, // stereo output
c.paFloat32,
sampleRate, // TODO: There must be some target dependant value to put here.
c.paFramesPerBufferUnspecified,
&patestCallback,
null
));
var err: c_int = 0;
const ogg_stream = c.stb_vorbis_open_filename("assets/cubyz/music/cubyz.ogg", &err, null);
defer c.stb_vorbis_close(ogg_stream);
if(ogg_stream != null) {
const ogg_info: c.stb_vorbis_info = c.stb_vorbis_get_info(ogg_stream);
std.debug.assert(sampleRate == ogg_info.sample_rate); // TODO: Handle this case
std.debug.assert(2 == ogg_info.channels); // TODO: Handle this case
const samples = c.stb_vorbis_stream_length_in_samples(ogg_stream);
musicData = try main.globalAllocator.alloc(f32, samples*@intCast(usize, ogg_info.channels));
_ = c.stb_vorbis_get_samples_float_interleaved(ogg_stream, ogg_info.channels, musicData.ptr, @intCast(c_int, samples)*ogg_info.channels);
} else {
std.log.err("Error reading file TODO", .{});
}
handleError(c.Pa_StartStream(stream));
}
pub fn deinit() void {
handleError(c.Pa_StopStream(stream));
handleError(c.Pa_CloseStream(stream));
handleError(c.Pa_Terminate());
main.globalAllocator.free(musicData);
}
var curIndex: usize = 0;
fn patestCallback(
inputBuffer: ?*const anyopaque,
outputBuffer: ?*anyopaque,
framesPerBuffer: c_ulong,
timeInfo: ?*const c.PaStreamCallbackTimeInfo,
statusFlags: c.PaStreamCallbackFlags,
userData: ?*anyopaque
) callconv(.C) c_int {
// This routine will be called by the PortAudio engine when audio is needed.
// It may called at interrupt level on some machines so don't do anything
// that could mess up the system like calling malloc() or free().
_ = inputBuffer;
_ = timeInfo; // TODO: Synchronize this to the rest of the world
_ = statusFlags;
_ = userData;
const out = @ptrCast([*]f32, @alignCast(4, outputBuffer));
for(0..framesPerBuffer) |i| {
out[2*i] = musicData[curIndex];
out[2*i+1] = musicData[curIndex+1];
curIndex = (curIndex + 2)%musicData.len;
}
return 0;
}

View File

@ -3,6 +3,7 @@ const std = @import("std");
pub const gui = @import("gui");
pub const server = @import("server");
pub const audio = @import("audio.zig");
pub const assets = @import("assets.zig");
pub const blocks = @import("blocks.zig");
pub const chunk = @import("chunk.zig");
@ -511,6 +512,9 @@ pub fn main() !void {
try graphics.init();
defer graphics.deinit();
try audio.init();
defer audio.deinit();
try gui.init();
defer gui.deinit();