summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--build.zig17
-rw-r--r--examples/hrtf.zig67
-rw-r--r--examples/play.zig5
-rw-r--r--src/al.zig12
-rw-r--r--src/sf.zig2
-rw-r--r--src/zeal.zig17
6 files changed, 108 insertions, 12 deletions
diff --git a/build.zig b/build.zig
index afc422f..9dd9fa2 100644
--- a/build.zig
+++ b/build.zig
@@ -24,13 +24,14 @@ pub fn build(b: *std.build.Builder) void {
     const mode = b.standardReleaseOptions();
 
     const lib = b.addStaticLibrary("zeal", "src/zeal.zig");
+    lib.linkSystemLibrary("openal");
+    lib.linkSystemLibrary("sndfile");
+    lib.linkSystemLibrary("c");
     lib.setBuildMode(mode);
     lib.install();
 
     var main_tests = b.addTest("src/zeal.zig");
-    main_tests.linkSystemLibrary("openal");
-    main_tests.linkSystemLibrary("sndfile");
-    main_tests.linkSystemLibrary("c");
+    main_tests.linkLibrary(lib);
     main_tests.setBuildMode(mode);
 
     const test_step = b.step("test", "Run library tests");
@@ -38,9 +39,13 @@ pub fn build(b: *std.build.Builder) void {
 
     const example_play = b.addExecutable("zeal-play", "examples/play.zig");
     example_play.addPackagePath("zeal", "src/zeal.zig");
-    example_play.linkSystemLibrary("openal");
-    example_play.linkSystemLibrary("sndfile");
-    example_play.linkSystemLibrary("c");
+    example_play.linkLibrary(lib);
     example_play.setBuildMode(mode);
     example_play.install();
+
+    const example_hrtf = b.addExecutable("zeal-hrtf", "examples/hrtf.zig");
+    example_hrtf.addPackagePath("zeal", "src/zeal.zig");
+    example_hrtf.linkLibrary(lib);
+    example_hrtf.setBuildMode(mode);
+    example_hrtf.install();
 }
diff --git a/examples/hrtf.zig b/examples/hrtf.zig
new file mode 100644
index 0000000..723571d
--- /dev/null
+++ b/examples/hrtf.zig
@@ -0,0 +1,67 @@
+// Positional audio example with HRTF
+// Copyright (C) 2021  Nguyễn Gia Phong
+//
+// This file is part of zeal.
+//
+// Zeal is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Zeal is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with zeal.  If not, see <https://www.gnu.org/licenses/>.
+
+const std = @import("std");
+const allocator = std.heap.c_allocator;
+const args = std.process.args;
+const print = std.debug.print;
+const sleep = std.time.sleep;
+
+const zeal = @import("zeal");
+const al = zeal.al;
+const alc = zeal.alc;
+const Device = zeal.Device;
+const Context = zeal.Context;
+const Audio = zeal.Audio;
+const useContext = zeal.useContext;
+const Buffer = zeal.Buffer;
+const Source = zeal.Source;
+
+pub fn main() !void {
+    const device = try Device.init(null);
+    defer device.deinit() catch unreachable;
+
+    const context = try Context.init(device, &.{ alc.HRTF, alc.TRUE });
+    if (try device.enabledHrtf())
+        print("HRTF enabled!\n", .{})
+    else
+        print("HRTF not enabled!\n", .{});
+    defer context.deinit() catch unreachable;
+
+    var argv = args();
+    allocator.free(try argv.next(allocator).?);
+    const path = try argv.next(allocator).?;
+    defer allocator.free(path);
+    const audio = try Audio.read(allocator, path);
+    defer audio.free();
+
+    try useContext(context);
+    const buffer = try Buffer.init(context, audio);
+    defer buffer.deinit() catch unreachable;
+
+    const source = try Source.init(context);
+    defer source.deinit() catch unreachable;
+    try source.setSpatialize(al.TRUE);
+    try source.play(buffer);
+    defer print("\n", .{});
+    while (try source.isPlaying()) {
+        const offset = try source.getSecOffset();
+        try source.setPosition(.{ @sin(offset), 0, @cos(offset) });
+        sleep(10_000_000);
+    }
+}
diff --git a/examples/play.zig b/examples/play.zig
index 7297181..0a031f9 100644
--- a/examples/play.zig
+++ b/examples/play.zig
@@ -19,6 +19,7 @@
 const std = @import("std");
 const allocator = std.heap.c_allocator;
 const args = std.process.args;
+const print = std.debug.print;
 const sleep = std.time.sleep;
 
 const zeal = @import("zeal");
@@ -50,9 +51,9 @@ pub fn main() !void {
     const source = try Source.init(context);
     defer source.deinit() catch unreachable;
     try source.play(buffer);
-    defer std.debug.print("\n", .{});
+    defer print("\n", .{});
     while (try source.isPlaying()) {
         sleep(10_000_000);
-        defer std.debug.print("\r{d:.1} s", .{ source.getSecOffset() });
+        defer print("\r{d:.1} s", .{ source.getSecOffset() });
     }
 }
diff --git a/src/al.zig b/src/al.zig
index 7b2e6db..bbde02e 100644
--- a/src/al.zig
+++ b/src/al.zig
@@ -18,7 +18,10 @@
 
 const Child = @import("std").meta.Child;
 
-usingnamespace @cImport(@cInclude("AL/al.h"));
+usingnamespace @cImport({
+    @cInclude("AL/al.h");
+    @cInclude("AL/alext.h");
+});
 
 pub const Error = error {
     /// Bad name (ID) passed to an OpenAL function.
@@ -33,12 +36,17 @@ pub const Error = error {
     OutOfMemory,
 };
 
+pub const FALSE = AL_FALSE;
+pub const TRUE = AL_TRUE;
+pub const AUTO = AL_AUTO_SOFT;
+
 pub const BUFFER = AL_BUFFER;
 pub const POSITION = AL_POSITION;
 pub const ORIENTATION = AL_ORIENTATION;
 pub const PLAYING = AL_PLAYING;
 pub const SEC_OFFSET = AL_SEC_OFFSET;
 pub const SOURCE_STATE = AL_SOURCE_STATE;
+pub const SOURCE_SPATIALIZE = AL_SOURCE_SPATIALIZE_SOFT;
 
 pub const listener = struct {
     /// Set a property for the listener.
@@ -211,7 +219,5 @@ pub const source = struct {
     }
 };
 
-// alGetSourcei
 // alSourcePause
-// alSourcePlay
 // alSourceStop
diff --git a/src/sf.zig b/src/sf.zig
index a95999c..a63da19 100644
--- a/src/sf.zig
+++ b/src/sf.zig
@@ -67,7 +67,7 @@ pub const SndFile = struct {
             .channels = info.channels,
             .format = info.format,
             .sections = info.sections,
-            .seekable = if (info.seekable == 0) false else true,
+            .seekable = info.seekable != 0,
         };
     }
 
diff --git a/src/zeal.zig b/src/zeal.zig
index 26dffa8..af9fd85 100644
--- a/src/zeal.zig
+++ b/src/zeal.zig
@@ -35,6 +35,10 @@ pub const Device = struct {
         return Device{ .pimpl = try alc.openDevice(name) };
     }
 
+    pub fn enabledHrtf(self: Device) alc.Error!bool {
+        return 0 != try alc.getInt(self.pimpl, alc.HRTF);
+    }
+
     pub fn deinit(self: Device) alc.Error!void {
         try alc.closeDevice(self.pimpl);
     }
@@ -152,6 +156,19 @@ pub const Source = struct {
         try al.source.destroy(&self.reference);
     }
 
+    /// Specify if the source always has 3D spatialization features (al.ON),
+    /// never has 3D spatialization features (al.OFF), or if spatialization
+    /// is enabled based on playing a mono sound or not (al.AUTO, default).
+    pub fn setSpatialize(self: Source, value: i32) Error!void {
+        try checkContext(self.context);
+        try al.source.set(self.reference, al.SOURCE_SPATIALIZE, value);
+    }
+
+    pub fn setPosition(self: Source, position: [3]f32) Error!void {
+        try checkContext(self.context);
+        try al.source.set(self.reference, al.POSITION, position);
+    }
+
     pub fn isPlaying(self: Source) Error!bool {
         try checkContext(self.context);
         const state = try al.source.get(i32, self.reference, al.SOURCE_STATE);