diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/al.zig | 39 | ||||
-rw-r--r-- | src/alc.zig | 42 | ||||
-rw-r--r-- | src/main.zig | 96 |
3 files changed, 152 insertions, 25 deletions
diff --git a/src/al.zig b/src/al.zig new file mode 100644 index 0000000..b15f2a1 --- /dev/null +++ b/src/al.zig @@ -0,0 +1,39 @@ +// Audio Library wrapper +// 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/>. + +usingnamespace @cImport({ @cInclude("AL/al.h"); }); + +pub const POSITION = AL_POSITION; +pub const ORIENTATION = AL_ORIENTATION; + +pub const listener = struct { + pub const setFloat = alListenerf; + pub const setFloatVector = alListenerfv; +}; + +// alBufferData +// alDeleteSources +// alGenBuffers +// alGenSources +// alGetSourcei +// alSourcef +// alSourcefv +// alSourcei +// alSourcePause +// alSourcePlay +// alSourceStop diff --git a/src/alc.zig b/src/alc.zig index 68e12b3..405d8bc 100644 --- a/src/alc.zig +++ b/src/alc.zig @@ -16,21 +16,40 @@ // You should have received a copy of the GNU Lesser General Public License // along with zeal. If not, see <https://www.gnu.org/licenses/>. -usingnamespace @cImport({ @cInclude("AL/alc.h"); }); +usingnamespace @cImport({ + @cInclude("AL/alc.h"); + @cInclude("AL/alext.h"); +}); +/// Opaque device handle. pub const Device = ALCdevice; +/// Opaque context handle. pub const Context = ALCcontext; + pub const Error = error { + /// Invalid device handle. InvalidDevice, + /// Invalid context handle. InvalidContext, + /// Invalid enum parameter passed to an ALC call. InvalidEnum, + /// Invalid value parameter passed to an ALC call. InvalidValue, + /// Out of memory. OutOfMemory, }; +pub const FALSE = ALC_FALSE; +pub const TRUE = ALC_TRUE; +pub const DONT_CARE = ALC_DONT_CATE_SOFT; + +/// Context creation key to specify whether to enable HRTF +/// (either `FALSE`, `TRUE` or `DONT_CARE`). +pub const HRTF = ALC_HRTF_SOFT; + /// Create and attach a context to the given device. -pub fn createContext(device: *Device, attrlist: [*c]const ALCint) !*Context { - if (alcCreateContext(device, attrlist)) |context| +pub fn createContext(device: *Device, attributes: [:0]const i32) !*Context { + if (alcCreateContext(device, attributes.ptr)) |context| return context; return switch (alcGetError(device)) { ALC_INVALID_DEVICE => Error.InvalidDevice, @@ -42,7 +61,7 @@ pub fn createContext(device: *Device, attrlist: [*c]const ALCint) !*Context { /// Make the given context the active process-wide context. /// Passing NULL clears the active context. pub fn makeContextCurrent(context: ?*Context) !void { - if (alcMakeContextCurrent(context) == ALC_TRUE) + if (alcMakeContextCurrent(context) == TRUE) return; if (alcGetError(null) == ALC_INVALID_CONTEXT) return Error.InvalidContext; @@ -56,11 +75,9 @@ pub fn destroyContext(context: *Context) !void { return Error.InvalidContext; } -/// Returns the currently active context. -pub fn getCurrentContext() *Context { - if (alcGetCurrentContext()) |context| - return context; - unreachable; +/// Return the currently active context. +pub fn getCurrentContext() ?*Context { + return alcGetCurrentContext(); } /// Return the device that a particular context is attached to. @@ -73,8 +90,9 @@ pub fn getContextsDevice(context: *Context) !*Device { } /// Open the named playback device. -pub fn openDevice(device_name: [*c]const u8) !*Device { - if (alcOpenDevice(device_name)) |device| +pub fn openDevice(name: ?[]const u8) !*Device { + const name_ptr = if (name == null) null else name.?.ptr; + if (alcOpenDevice(name_ptr)) |device| return device; return switch (alcGetError(null)) { ALC_INVALID_VALUE => Error.InvalidValue, @@ -85,7 +103,7 @@ pub fn openDevice(device_name: [*c]const u8) !*Device { /// Close the given playback device. pub fn closeDevice(device: *Device) !void { - if (alcCloseDevice(device) == ALC_TRUE) + if (alcCloseDevice(device) == TRUE) return; if (alcGetError(device) == ALC_INVALID_DEVICE) return Error.InvalidDevice; diff --git a/src/main.zig b/src/main.zig index add48ae..249d7c7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -16,23 +16,93 @@ // 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 al = @import("al.zig"); const alc = @import("alc.zig"); +const expect = std.testing.expect; +const eql = std.meta.eql; +const std = @import("std"); -pub fn init(device_name: [*c]const u8) !void { - const device = try alc.openDevice(device_name); - const context = try alc.createContext(device, null); - try alc.makeContextCurrent(context); +pub const Device = struct { + pimpl: *alc.Device, + + pub fn init(name: ?[]const u8) alc.Error!Device { + return Device{ .pimpl = try alc.openDevice(name) }; + } + + pub fn deinit(self: Device) alc.Error!void { + try alc.closeDevice(self.pimpl); + } +}; + +pub const Context = struct { + pimpl: *alc.Context, + device: Device, + + pub fn init(device: Device, attributes: [:0]const i32) alc.Error!Context { + return Context { + .pimpl = try alc.createContext(device.pimpl, attributes), + .device = device, + }; + } + + pub fn deinit(self: Context) alc.Error!void { + try alc.makeContextCurrent(null); + try alc.destroyContext(self.pimpl); + } +}; + +pub fn useContext(context: ?Context) alc.Error!void { + try alc.makeContextCurrent(if (context == null) null else context.?.pimpl); +} +pub fn currentContext() alc.Error!?Context { + if (alc.getCurrentContext()) |pimpl| + return Context { + .pimpl = pimpl, + .device = Device { .pimpl = try alc.getContextsDevice(pimpl) }, + }; + return null; } -pub fn deinit() !void { - const context = alc.getCurrentContext(); - const device = try alc.getContextsDevice(context); - try alc.makeContextCurrent(null); - try alc.destroyContext(context); - try alc.closeDevice(device); +fn checkContext(context: Context) !void { + if (context.pimpl != alc.getCurrentContext()) + return error.UncurrentContext; } -test { - try init(null); - try deinit(); +pub const Listener = struct { + context: Context, + + pub fn setPosition(self: Listener, position: [3]f32) !void { + try checkContext(self.context); + al.listener.setFloatVector(al.POSITION, &position); + } + + pub fn setOrientation(self: Listener, at: [3]f32, up: [3]f32) !void { + try checkContext(self.context); + al.listener.setFloatVector(al.ORIENTATION, &[_]f32 { + at[0], at[1], at[2], + up[0], up[1], up[2], + }); + } +}; + +test "temporary" { + const device = try Device.init("OpenAL Soft"); + 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) { + error.UncurrentContext => {}, + } + + try useContext(context); + const current_context = (try currentContext()).?; + try expect(eql(current_context, context)); + try expect(eql(current_context.device, device)); + try checkContext(context); + + const listener = Listener{ .context = context }; + try listener.setPosition(.{ 0, 0, 0 }); + try listener.setOrientation(.{ 0, 0, 0 }, .{ 0, 0, 0 }); } |