summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/al.zig57
-rw-r--r--src/main.zig58
2 files changed, 102 insertions, 13 deletions
diff --git a/src/al.zig b/src/al.zig
index e806e90..bfb93b6 100644
--- a/src/al.zig
+++ b/src/al.zig
@@ -1,4 +1,4 @@
-// Audio Library wrapper
+// OpenAL wrapper
 // Copyright (C) 2021  Nguyễn Gia Phong
 //
 // This file is part of zeal.
@@ -20,11 +20,26 @@ const Child = @import("std").meta.Child;
 
 usingnamespace @cImport(@cInclude("AL/al.h"));
 
+pub const Error = error {
+    /// Bad name (ID) passed to an OpenAL function.
+    InvalidName,
+    /// Invalid enum parameter passed to an OpenAL function.
+    InvalidEnum,
+    /// Invalid value parameter passed to an OpenAL function.
+    InvalidValue,
+    /// Requested operation invalid.
+    InvalidOperation,
+    /// Requested operation resulted in OpenAL running out of memory.
+    OutOfMemory,
+};
+
 pub const POSITION = AL_POSITION;
 pub const ORIENTATION = AL_ORIENTATION;
 
 pub const listener = struct {
-    pub fn set(comptime T: type, param: ALenum, value: T) void {
+    /// Set a property for the listener.
+    pub fn set(param: ALenum, value: anytype) !void {
+        const T = @TypeOf(value);
         switch (T) {
             f32 => alListenerf(param, value),
             i32 => alListeneri(param, value),
@@ -34,12 +49,48 @@ pub const listener = struct {
                 else => unreachable,
             }
         }
+
+        switch (alGetError()) {
+            AL_NO_ERROR => {},
+            AL_INVALID_VALUE => return Error.InvalidValue,
+            AL_INVALID_ENUM => return Error.InvalidEnum,
+            AL_INVALID_OPERATION => return Error.InvalidOperation,
+            else => unreachable,
+        }
     }
 };
 
+/// Generate one buffer for audio data and return its reference.
+pub fn genBuffer() !u32 {
+    var reference: u32 = undefined;
+    alGenBuffers(1, &reference);
+    return switch (alGetError()) {
+        AL_NO_ERROR => reference,
+        AL_INVALID_VALUE => Error.InvalidValue,
+        AL_OUT_OF_MEMORY => Error.OutOfMemory,
+        else => unreachable,
+    };
+}
+
+// TODO: genBuffers
+
+/// Free resources used by one buffer.
+/// Buffers attached to a source cannot be deleted.
+pub fn deleteBuffer(reference: *const u32) !void {
+    alDeleteBuffers(1, reference);
+    switch (alGetError()) {
+        AL_NO_ERROR => {},
+        AL_INVALID_OPERATION => return Error.InvalidOperation,
+        AL_INVALID_NAME => return Error.InvalidName,
+        AL_INVALID_VALUE => return Error.InvalidValue,
+        else => unreachable,
+    }
+}
+
+// TODO: deleteBuffers
+
 // alBufferData
 // alDeleteSources
-// alGenBuffers
 // alGenSources
 // alGetSourcei
 // alSourcef
diff --git a/src/main.zig b/src/main.zig
index fc13fa8..21e4f3a 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -18,7 +18,7 @@
 
 const al = @import("al.zig");
 const alc = @import("alc.zig");
-const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
 const eql = std.meta.eql;
 const std = @import("std");
 
@@ -73,34 +73,72 @@ pub const Listener = struct {
 
     pub fn setPosition(self: Listener, position: [3]f32) !void {
         try checkContext(self.context);
-        al.listener.set([3]f32, al.POSITION, position);
+        try al.listener.set(al.POSITION, position);
     }
 
     pub fn setOrientation(self: Listener, at: [3]f32, up: [3]f32) !void {
         try checkContext(self.context);
         const orientation = [_]f32{ at[0], at[1], at[2], up[0], up[1], up[2] };
-        al.listener.set(@TypeOf(orientation), al.ORIENTATION, orientation);
+        try al.listener.set(al.ORIENTATION, orientation);
     }
 };
 
-test "temporary" {
+pub const Buffer = struct {
+    context: Context,
+    reference: u32,
+
+    pub fn init(context: Context, name: []const u8) !Buffer {
+        try checkContext(context);
+        const reference = try al.genBuffer();
+        return Buffer{ .context = context, .reference = reference };
+    }
+
+    pub fn deinit(self: Buffer) !void {
+        try checkContext(self.context);
+        try al.deleteBuffer(&self.reference);
+    }
+};
+
+test "Device" {
     const device = try Device.init("OpenAL Soft");
+    try device.deinit();
+}
+
+test "Context" {
+    const device = try Device.init(null);
     defer device.deinit() catch unreachable;
     const context = try Context.init(device, &.{ alc.HRTF, alc.TRUE });
     defer context.deinit() catch unreachable;
 
-    try expect(null == try currentContext());
-    if (checkContext(context)) |_| unreachable else |err| switch (err) {
+    try expectEqual(try currentContext(), null);
+    if (checkContext(context)) unreachable else |err| switch (err) {
         error.UncurrentContext => {},
     }
 
     try useContext(context);
     const current_context = (try currentContext()).?;
-    try expect(eql(current_context, context));
-    try expect(eql(current_context.device, device));
+    try expectEqual(current_context, context);
+    try expectEqual(current_context.device, device);
     try checkContext(context);
+}
+
+test "Listener" {
+    const context = try Context.init(try Device.init(null), &.{});
+    defer context.device.deinit() catch unreachable;
+    defer context.deinit() catch unreachable;
 
+    try useContext(context);
     const listener = Listener{ .context = context };
-    try listener.setPosition(.{ 0, 0, 0 });
-    try listener.setOrientation(.{ 0, 0, 0 }, .{ 0, 0, 0 });
+    try listener.setPosition(.{ 1, 2, 3 });
+    try listener.setOrientation(.{ 4, 5, 6 }, .{ 7, 8, 9 });
+}
+
+test "Buffer" {
+    const context = try Context.init(try Device.init(null), &.{});
+    defer context.device.deinit() catch unreachable;
+    defer context.deinit() catch unreachable;
+
+    try useContext(context);
+    const buffer = try Buffer.init(context, "");
+    defer buffer.deinit() catch unreachable;
 }