// libsndfile wrapper // Copyright (C) 2021-2023 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 . const Allocator = std.mem.Allocator; const assert = std.debug.assert; const span = std.mem.span; const std = @import("std"); const c = @import("cimport.zig"); pub const Mode = enum { read, write, read_write, }; pub const Error = Allocator.Error || error{ UnrecognizedFormat, SystemError, MalformedFile, UnsupportedEncoding, }; pub const SndFile = struct { pimpl: *c.SNDFILE, frames: usize, sample_rate: c_int, channels: c_int, format: c_int, sections: c_int, seekable: bool, /// Open the sound file at the specified path. pub fn open(path: [:0]const u8, mode: Mode) Error!SndFile { const c_mode = switch (mode) { .read => c.SFM_READ, .write => c.SFM_WRITE, .read_write => c.SFM_RDWR, }; var info: c.SF_INFO = undefined; info.format = 0; const pimpl = c.sf_open(path.ptr, c_mode, &info); _ = c.sf_command(pimpl, c.SFC_SET_SCALE_FLOAT_INT_READ, null, c.SF_TRUE); return SndFile{ .pimpl = pimpl orelse return switch (c.sf_error(null)) { c.SF_ERR_UNRECOGNISED_FORMAT => Error.UnrecognizedFormat, c.SF_ERR_SYSTEM => Error.SystemError, c.SF_ERR_MALFORMED_FILE => Error.MalformedFile, c.SF_ERR_UNSUPPORTED_ENCODING => Error.UnsupportedEncoding, else => unreachable, }, .frames = @intCast(info.frames), .sample_rate = info.samplerate, .channels = info.channels, .format = info.format, .sections = info.sections, .seekable = info.seekable != 0, }; } /// Read the requested number of frames. /// The returned memory is managed by the caller. pub fn read(self: SndFile, allocator: Allocator, frames: usize) Error![]const i16 { const items = frames * @as(usize, @intCast(self.channels)); const memory = try allocator.alloc(i16, items); errdefer allocator.free(memory); const n = c.sf_read_short(self.pimpl, memory.ptr, @intCast(items)); return try allocator.realloc(memory, @intCast(n)); } /// Read the entire file. The returned memory is managed by the caller. pub fn readAll(self: SndFile, allocator: Allocator) Error![]const i16 { return self.read(allocator, self.frames); } /// Close the file and deallocate its internal buffers. /// Like std.os.close, this function does not return /// any indication of failure. pub fn close(self: SndFile) void { assert(c.sf_close(self.pimpl) == 0); } };