audio: rename to 'sysaudio'
This commit is contained in:
parent
0fb712d19e
commit
f8f4dcf55f
22 changed files with 3 additions and 3 deletions
2
sysaudio/.gitattributes
vendored
Normal file
2
sysaudio/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
* text=auto eol=lf
|
||||
upstream/** linguist-vendored
|
||||
20
sysaudio/.gitignore
vendored
Normal file
20
sysaudio/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# This file is for zig-specific build artifacts.
|
||||
# If you have OS-specific or editor-specific files to ignore,
|
||||
# such as *.swp or .DS_Store, put those in your global
|
||||
# ~/.gitignore and put this in your ~/.gitconfig:
|
||||
#
|
||||
# [core]
|
||||
# excludesfile = ~/.gitignore
|
||||
#
|
||||
# Cheers!
|
||||
# -andrewrk
|
||||
|
||||
zig-cache/
|
||||
zig-out/
|
||||
/release/
|
||||
/debug/
|
||||
/build/
|
||||
/build-*/
|
||||
/docgen_tmp/
|
||||
|
||||
upstream/soundio/src/config.h
|
||||
3
sysaudio/.gitmodules
vendored
Normal file
3
sysaudio/.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "upstream"]
|
||||
path = upstream
|
||||
url = https://github.com/hexops/soundio
|
||||
13
sysaudio/LICENSE
Normal file
13
sysaudio/LICENSE
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
Copyright 2022, Hexops Contributors (given via the Git commit history).
|
||||
|
||||
All documentation, image, sound, font, and 2D/3D model files are CC-BY-4.0 licensed unless
|
||||
otherwise noted. You may get a copy of this license at https://creativecommons.org/licenses/by/4.0
|
||||
|
||||
Files in a directory with a separate LICENSE file may contain files under different license terms,
|
||||
described within that LICENSE file.
|
||||
|
||||
All other files are licensed under the Apache License, Version 2.0 (see LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
or the MIT license (see LICENSE-MIT or http://opensource.org/licenses/MIT), at your option.
|
||||
|
||||
All files in the project without exclusions may not be copied, modified, or distributed except
|
||||
according to the terms above.
|
||||
202
sysaudio/LICENSE-APACHE
Normal file
202
sysaudio/LICENSE-APACHE
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
25
sysaudio/LICENSE-MIT
Normal file
25
sysaudio/LICENSE-MIT
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2022 Hexops Contributors (given via the Git commit history).
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
136
sysaudio/build.zig
Normal file
136
sysaudio/build.zig
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
|
||||
const soundio_path = thisDir() ++ "/upstream/soundio";
|
||||
|
||||
pub const pkg = std.build.Pkg{
|
||||
.name = "sysaudio",
|
||||
.source = .{ .path = thisDir() ++ "/src/main.zig" },
|
||||
};
|
||||
|
||||
const soundio_pkg = std.build.Pkg{
|
||||
.name = "soundio",
|
||||
.source = .{ .path = thisDir() ++ "/soundio/main.zig" },
|
||||
};
|
||||
|
||||
pub const Options = struct {};
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
const soundio_tests = b.addTest("soundio/main.zig");
|
||||
soundio_tests.setBuildMode(mode);
|
||||
link(b, soundio_tests, .{});
|
||||
|
||||
const main_tests = b.addTest("src/main.zig");
|
||||
main_tests.setBuildMode(mode);
|
||||
main_tests.addPackage(soundio_pkg);
|
||||
link(b, main_tests, .{});
|
||||
|
||||
const test_step = b.step("test", "Run library tests");
|
||||
test_step.dependOn(&soundio_tests.step);
|
||||
test_step.dependOn(&main_tests.step);
|
||||
|
||||
inline for ([_][]const u8{
|
||||
"soundio-sine-wave",
|
||||
}) |example| {
|
||||
const example_exe = b.addExecutable("example-" ++ example, "examples/" ++ example ++ ".zig");
|
||||
example_exe.setBuildMode(mode);
|
||||
example_exe.setTarget(target);
|
||||
example_exe.addPackage(soundio_pkg);
|
||||
link(b, example_exe, .{});
|
||||
example_exe.install();
|
||||
|
||||
const example_compile_step = b.step("example-" ++ example, "Compile '" ++ example ++ "' example");
|
||||
example_compile_step.dependOn(b.getInstallStep());
|
||||
|
||||
const example_run_cmd = example_exe.run();
|
||||
example_run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
example_run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const example_run_step = b.step("run-example-" ++ example, "Run '" ++ example ++ "' example");
|
||||
example_run_step.dependOn(&example_run_cmd.step);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void {
|
||||
_ = options;
|
||||
const soundio_lib = buildSoundIo(b, step);
|
||||
step.linkLibrary(soundio_lib);
|
||||
step.addIncludePath(soundio_path);
|
||||
}
|
||||
|
||||
fn buildSoundIo(b: *Builder, step: *std.build.LibExeObjStep) *std.build.LibExeObjStep {
|
||||
// TODO(build-system): https://github.com/hexops/mach/issues/229#issuecomment-1100958939
|
||||
ensureDependencySubmodule(b.allocator, "upstream") catch unreachable;
|
||||
|
||||
const config_base =
|
||||
\\#ifndef SOUNDIO_CONFIG_H
|
||||
\\#define SOUNDIO_CONFIG_H
|
||||
\\#define SOUNDIO_VERSION_MAJOR 2
|
||||
\\#define SOUNDIO_VERSION_MINOR 0
|
||||
\\#define SOUNDIO_VERSION_PATCH 0
|
||||
\\#define SOUNDIO_VERSION_STRING "2.0.0"
|
||||
\\
|
||||
;
|
||||
|
||||
var config_file = std.fs.cwd().createFile(soundio_path ++ "/src/config.h", .{}) catch unreachable;
|
||||
defer config_file.close();
|
||||
config_file.writeAll(config_base) catch unreachable;
|
||||
|
||||
const lib = b.addStaticLibrary("soundio", null);
|
||||
lib.setTarget(step.target);
|
||||
lib.setBuildMode(step.build_mode);
|
||||
lib.linkLibC();
|
||||
lib.addIncludePath(soundio_path);
|
||||
lib.addCSourceFiles(soundio_sources, &.{});
|
||||
|
||||
const target = (std.zig.system.NativeTargetInfo.detect(b.allocator, step.target) catch unreachable).target;
|
||||
if (target.isDarwin()) {
|
||||
lib.addCSourceFile(soundio_path ++ "/src/coreaudio.c", &.{});
|
||||
lib.linkFramework("AudioToolbox");
|
||||
lib.linkFramework("CoreFoundation");
|
||||
lib.linkFramework("CoreAudio");
|
||||
config_file.writeAll("#define SOUNDIO_HAVE_COREAUDIO\n") catch unreachable;
|
||||
} else if (target.os.tag == .linux) {
|
||||
lib.addCSourceFile(soundio_path ++ "/src/alsa.c", &.{});
|
||||
lib.linkSystemLibrary("asound");
|
||||
config_file.writeAll("#define SOUNDIO_HAVE_ALSA\n") catch unreachable;
|
||||
} else if (target.os.tag == .windows) {
|
||||
lib.addCSourceFile(soundio_path ++ "/src/wasapi.c", &.{});
|
||||
config_file.writeAll("#define SOUNDIO_HAVE_WASAPI\n") catch unreachable;
|
||||
}
|
||||
|
||||
config_file.writeAll("#endif\n") catch unreachable;
|
||||
|
||||
lib.install();
|
||||
return lib;
|
||||
}
|
||||
|
||||
fn ensureDependencySubmodule(allocator: std.mem.Allocator, path: []const u8) !void {
|
||||
if (std.process.getEnvVarOwned(allocator, "NO_ENSURE_SUBMODULES")) |no_ensure_submodules| {
|
||||
if (std.mem.eql(u8, no_ensure_submodules, "true")) return;
|
||||
} else |_| {}
|
||||
var child = std.ChildProcess.init(&.{ "git", "submodule", "update", "--init", path }, allocator);
|
||||
child.cwd = thisDir();
|
||||
child.stderr = std.io.getStdErr();
|
||||
child.stdout = std.io.getStdOut();
|
||||
|
||||
_ = try child.spawnAndWait();
|
||||
}
|
||||
|
||||
fn thisDir() []const u8 {
|
||||
return std.fs.path.dirname(@src().file) orelse ".";
|
||||
}
|
||||
|
||||
const soundio_sources = &[_][]const u8{
|
||||
soundio_path ++ "/src/soundio.c",
|
||||
soundio_path ++ "/src/util.c",
|
||||
soundio_path ++ "/src/os.c",
|
||||
soundio_path ++ "/src/dummy.c",
|
||||
soundio_path ++ "/src/channel_layout.c",
|
||||
soundio_path ++ "/src/ring_buffer.c",
|
||||
};
|
||||
76
sysaudio/examples/soundio-sine-wave.zig
Normal file
76
sysaudio/examples/soundio-sine-wave.zig
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
const std = @import("std");
|
||||
const soundio = @import("soundio");
|
||||
const c = soundio.c;
|
||||
const SoundIo = soundio.SoundIo;
|
||||
const OutStream = soundio.OutStream;
|
||||
|
||||
var seconds_offset: f32 = 0;
|
||||
|
||||
fn write_callback(
|
||||
maybe_outstream: ?[*]c.SoundIoOutStream,
|
||||
frame_count_min: c_int,
|
||||
frame_count_max: c_int,
|
||||
) callconv(.C) void {
|
||||
_ = frame_count_min;
|
||||
const outstream = OutStream{ .handle = @ptrCast(*c.SoundIoOutStream, maybe_outstream) };
|
||||
const layout = outstream.layout();
|
||||
const float_sample_rate = outstream.sampleRate();
|
||||
const seconds_per_frame = 1.0 / @intToFloat(f32, float_sample_rate);
|
||||
var frames_left = frame_count_max;
|
||||
|
||||
while (frames_left > 0) {
|
||||
var frame_count = frames_left;
|
||||
|
||||
var areas: [*]c.SoundIoChannelArea = undefined;
|
||||
outstream.beginWrite(
|
||||
@ptrCast([*]?[*]c.SoundIoChannelArea, &areas),
|
||||
&frame_count,
|
||||
) catch |err| std.debug.panic("write failed: {s}", .{@errorName(err)});
|
||||
|
||||
if (frame_count == 0) break;
|
||||
|
||||
const pitch = 440.0;
|
||||
const radians_per_second = pitch * 2.0 * std.math.pi;
|
||||
var frame: c_int = 0;
|
||||
while (frame < frame_count) : (frame += 1) {
|
||||
const sample = std.math.sin((seconds_offset + @intToFloat(f32, frame) *
|
||||
seconds_per_frame) * radians_per_second);
|
||||
{
|
||||
var channel: usize = 0;
|
||||
while (channel < @intCast(usize, layout.channelCount())) : (channel += 1) {
|
||||
const channel_ptr = areas[channel].ptr;
|
||||
const sample_ptr = &channel_ptr[@intCast(usize, areas[channel].step * frame)];
|
||||
@ptrCast(*f32, @alignCast(@alignOf(f32), sample_ptr)).* = sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
seconds_offset += seconds_per_frame * @intToFloat(f32, frame_count);
|
||||
outstream.endWrite() catch |err| std.debug.panic("end write failed: {s}", .{@errorName(err)});
|
||||
frames_left -= frame_count;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
const sio = try SoundIo.init();
|
||||
defer sio.deinit();
|
||||
try sio.connect();
|
||||
sio.flushEvents();
|
||||
|
||||
const default_output_index = sio.defaultOutputDeviceIndex() orelse return error.NoOutputDeviceFound;
|
||||
|
||||
const device = sio.getOutputDevice(default_output_index) orelse return error.OutOfMemory;
|
||||
defer device.unref();
|
||||
|
||||
std.debug.print("Output device: {s}\n", .{device.name()});
|
||||
|
||||
const outstream = try device.createOutStream();
|
||||
defer outstream.deinit();
|
||||
|
||||
outstream.setFormat(.float32LE);
|
||||
outstream.setWriteCallback(write_callback);
|
||||
|
||||
try outstream.open();
|
||||
try outstream.start();
|
||||
|
||||
while (true) sio.waitEvents();
|
||||
}
|
||||
1
sysaudio/main.zig
Normal file
1
sysaudio/main.zig
Normal file
|
|
@ -0,0 +1 @@
|
|||
//
|
||||
11
sysaudio/soundio/ChannelLayout.zig
Normal file
11
sysaudio/soundio/ChannelLayout.zig
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const c = @import("c.zig");
|
||||
const intToError = @import("error.zig").intToError;
|
||||
const Error = @import("error.zig").Error;
|
||||
|
||||
const ChannelLayout = @This();
|
||||
|
||||
handle: c.SoundIoChannelLayout,
|
||||
|
||||
pub fn channelCount(self: ChannelLayout) i32 {
|
||||
return self.handle.channel_count;
|
||||
}
|
||||
33
sysaudio/soundio/Device.zig
Normal file
33
sysaudio/soundio/Device.zig
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
const std = @import("std");
|
||||
const c = @import("c.zig");
|
||||
const InStream = @import("InStream.zig");
|
||||
const OutStream = @import("OutStream.zig");
|
||||
const Format = @import("enums.zig").Format;
|
||||
|
||||
const Device = @This();
|
||||
|
||||
handle: *c.SoundIoDevice,
|
||||
|
||||
pub fn unref(self: Device) void {
|
||||
c.soundio_device_unref(self.handle);
|
||||
}
|
||||
|
||||
pub fn id(self: Device) [:0]const u8 {
|
||||
return std.mem.span(self.handle.*.id);
|
||||
}
|
||||
|
||||
pub fn name(self: Device) [:0]const u8 {
|
||||
return std.mem.span(self.handle.*.name);
|
||||
}
|
||||
|
||||
pub fn createInStream(self: Device) error{OutOfMemory}!InStream {
|
||||
return InStream{ .handle = c.soundio_instream_create(self.handle) orelse return error.OutOfMemory };
|
||||
}
|
||||
|
||||
pub fn createOutStream(self: Device) error{OutOfMemory}!OutStream {
|
||||
return OutStream{ .handle = c.soundio_outstream_create(self.handle) orelse return error.OutOfMemory };
|
||||
}
|
||||
|
||||
pub fn supportsFormat(self: Device, format: Format) bool {
|
||||
return c.soundio_device_supports_format(self.handle, @enumToInt(format));
|
||||
}
|
||||
55
sysaudio/soundio/InStream.zig
Normal file
55
sysaudio/soundio/InStream.zig
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
const c = @import("c.zig");
|
||||
const intToError = @import("error.zig").intToError;
|
||||
const Error = @import("error.zig").Error;
|
||||
const Format = @import("enums.zig").Format;
|
||||
const ChannelLayout = @import("ChannelLayout.zig");
|
||||
|
||||
const InStream = @This();
|
||||
|
||||
pub const WriteCallback = fn (stream: ?[*]c.SoundIoInStream, frame_count_min: c_int, frame_count_max: c_int) callconv(.C) void;
|
||||
|
||||
handle: *c.SoundIoInStream,
|
||||
|
||||
pub fn deinit(self: InStream) void {
|
||||
c.soundio_instream_destroy(self.handle);
|
||||
}
|
||||
|
||||
pub fn open(self: InStream) Error!void {
|
||||
try intToError(c.soundio_instream_open(self.handle));
|
||||
}
|
||||
|
||||
pub fn start(self: InStream) Error!void {
|
||||
try intToError(c.soundio_instream_start(self.handle));
|
||||
}
|
||||
|
||||
pub fn beginWrite(self: InStream, areas: [*]?[*]c.SoundIoChannelArea, frame_count: *i32) Error!void {
|
||||
try intToError(c.soundio_instream_begin_write(
|
||||
self.handle,
|
||||
areas,
|
||||
frame_count,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn endWrite(self: InStream) Error!void {
|
||||
try intToError(c.soundio_instream_end_write(self.handle));
|
||||
}
|
||||
|
||||
pub fn setFormat(self: InStream, format: Format) void {
|
||||
self.handle.*.format = @enumToInt(format);
|
||||
}
|
||||
|
||||
pub fn setWriteCallback(self: InStream, callback: WriteCallback) void {
|
||||
self.handle.*.write_callback = callback;
|
||||
}
|
||||
|
||||
pub fn layout(self: InStream) ChannelLayout {
|
||||
return ChannelLayout{ .handle = self.handle.*.layout };
|
||||
}
|
||||
|
||||
pub fn sampleRate(self: InStream) i32 {
|
||||
return self.handle.*.sample_rate;
|
||||
}
|
||||
|
||||
pub fn layoutError(self: InStream) Error!void {
|
||||
try intToError(self.handle.*.layout_error);
|
||||
}
|
||||
55
sysaudio/soundio/OutStream.zig
Normal file
55
sysaudio/soundio/OutStream.zig
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
const c = @import("c.zig");
|
||||
const intToError = @import("error.zig").intToError;
|
||||
const Error = @import("error.zig").Error;
|
||||
const Format = @import("enums.zig").Format;
|
||||
const ChannelLayout = @import("ChannelLayout.zig");
|
||||
|
||||
const OutStream = @This();
|
||||
|
||||
pub const WriteCallback = fn (stream: ?[*]c.SoundIoOutStream, frame_count_min: c_int, frame_count_max: c_int) callconv(.C) void;
|
||||
|
||||
handle: *c.SoundIoOutStream,
|
||||
|
||||
pub fn deinit(self: OutStream) void {
|
||||
c.soundio_outstream_destroy(self.handle);
|
||||
}
|
||||
|
||||
pub fn open(self: OutStream) Error!void {
|
||||
try intToError(c.soundio_outstream_open(self.handle));
|
||||
}
|
||||
|
||||
pub fn start(self: OutStream) Error!void {
|
||||
try intToError(c.soundio_outstream_start(self.handle));
|
||||
}
|
||||
|
||||
pub fn beginWrite(self: OutStream, areas: [*]?[*]c.SoundIoChannelArea, frame_count: *i32) Error!void {
|
||||
try intToError(c.soundio_outstream_begin_write(
|
||||
self.handle,
|
||||
areas,
|
||||
frame_count,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn endWrite(self: OutStream) Error!void {
|
||||
try intToError(c.soundio_outstream_end_write(self.handle));
|
||||
}
|
||||
|
||||
pub fn setFormat(self: OutStream, format: Format) void {
|
||||
self.handle.*.format = @enumToInt(format);
|
||||
}
|
||||
|
||||
pub fn setWriteCallback(self: OutStream, callback: WriteCallback) void {
|
||||
self.handle.*.write_callback = callback;
|
||||
}
|
||||
|
||||
pub fn layout(self: OutStream) ChannelLayout {
|
||||
return ChannelLayout{ .handle = self.handle.*.layout };
|
||||
}
|
||||
|
||||
pub fn sampleRate(self: OutStream) i32 {
|
||||
return self.handle.*.sample_rate;
|
||||
}
|
||||
|
||||
pub fn layoutError(self: OutStream) Error!void {
|
||||
try intToError(self.handle.*.layout_error);
|
||||
}
|
||||
86
sysaudio/soundio/SoundIo.zig
Normal file
86
sysaudio/soundio/SoundIo.zig
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
const c = @import("c.zig");
|
||||
const intToError = @import("error.zig").intToError;
|
||||
const Error = @import("error.zig").Error;
|
||||
const Aim = @import("enums.zig").Aim;
|
||||
const Backend = @import("enums.zig").Backend;
|
||||
const Device = @import("Device.zig");
|
||||
|
||||
const SoundIo = @This();
|
||||
|
||||
handle: *c.SoundIo,
|
||||
|
||||
pub fn init() error{OutOfMemory}!SoundIo {
|
||||
return SoundIo{ .handle = c.soundio_create() orelse return error.OutOfMemory };
|
||||
}
|
||||
|
||||
pub fn deinit(self: SoundIo) void {
|
||||
c.soundio_destroy(self.handle);
|
||||
}
|
||||
|
||||
pub fn connect(self: SoundIo) Error!void {
|
||||
try intToError(c.soundio_connect(self.handle));
|
||||
}
|
||||
|
||||
pub fn connectBackend(self: SoundIo, backend: Backend) Error!void {
|
||||
try intToError(c.soundio_connect_backend(self.handle, @enumToInt(backend)));
|
||||
}
|
||||
|
||||
pub fn disconnect(self: SoundIo) void {
|
||||
c.soundio_disconnect(self.handle);
|
||||
}
|
||||
|
||||
pub fn flushEvents(self: SoundIo) void {
|
||||
c.soundio_flush_events(self.handle);
|
||||
}
|
||||
|
||||
pub fn waitEvents(self: SoundIo) void {
|
||||
c.soundio_wait_events(self.handle);
|
||||
}
|
||||
|
||||
pub fn wakeup(self: SoundIo) void {
|
||||
c.soundio_wakeup(self.handle);
|
||||
}
|
||||
|
||||
pub fn defaultInputDeviceIndex(self: SoundIo) ?u16 {
|
||||
const index = c.soundio_default_input_device_index(self.handle);
|
||||
return if (index < 0) null else @intCast(u16, index);
|
||||
}
|
||||
|
||||
pub fn defaultOutputDeviceIndex(self: SoundIo) ?u16 {
|
||||
const index = c.soundio_default_output_device_index(self.handle);
|
||||
return if (index < 0) null else @intCast(u16, index);
|
||||
}
|
||||
|
||||
pub fn inputDeviceCount(self: SoundIo) ?u16 {
|
||||
const count = c.soundio_input_device_count(self.handle);
|
||||
return if (count < 0) null else @intCast(u16, count);
|
||||
}
|
||||
|
||||
pub fn outputDeviceCount(self: SoundIo) ?u16 {
|
||||
const count = c.soundio_output_device_count(self.handle);
|
||||
return if (count < 0) null else @intCast(u16, count);
|
||||
}
|
||||
|
||||
pub fn getInputDevice(self: SoundIo, index: u16) ?Device {
|
||||
return Device{
|
||||
.handle = c.soundio_get_input_device(self.handle, index) orelse return null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getOutputDevice(self: SoundIo, index: u16) ?Device {
|
||||
return Device{
|
||||
.handle = c.soundio_get_output_device(self.handle, index) orelse return null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getInputDeviceFromID(self: SoundIo, id: [:0]const u8, is_raw: bool) ?Device {
|
||||
return Device{
|
||||
.handle = c.soundio_get_input_device_from_id(self.handle, id.ptr, is_raw) orelse return null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getOutputDeviceFromID(self: SoundIo, id: [:0]const u8, is_raw: bool) ?Device {
|
||||
return Device{
|
||||
.handle = c.soundio_get_output_device_from_id(self.handle, id.ptr, is_raw) orelse return null,
|
||||
};
|
||||
}
|
||||
3
sysaudio/soundio/c.zig
Normal file
3
sysaudio/soundio/c.zig
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub usingnamespace @cImport({
|
||||
@cInclude("soundio/soundio.h");
|
||||
});
|
||||
149
sysaudio/soundio/enums.zig
Normal file
149
sysaudio/soundio/enums.zig
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
const c = @import("c.zig");
|
||||
|
||||
pub const ChannelId = enum(u7) {
|
||||
invalid = c.SoundIoChannelIdInvalid,
|
||||
|
||||
front_left = c.SoundIoChannelIdFrontLeft,
|
||||
front_right = c.SoundIoChannelIdFrontRight,
|
||||
front_center = c.SoundIoChannelIdFrontCenter,
|
||||
lfe = c.SoundIoChannelIdLfe,
|
||||
back_left = c.SoundIoChannelIdBackLeft,
|
||||
back_right = c.SoundIoChannelIdBackRight,
|
||||
front_left_center = c.SoundIoChannelIdFrontLeftCenter,
|
||||
front_right_center = c.SoundIoChannelIdFrontRightCenter,
|
||||
back_center = c.SoundIoChannelIdBackCenter,
|
||||
side_left = c.SoundIoChannelIdSideLeft,
|
||||
side_right = c.SoundIoChannelIdSideRight,
|
||||
top_center = c.SoundIoChannelIdTopCenter,
|
||||
top_front_left = c.SoundIoChannelIdTopFrontLeft,
|
||||
top_front_center = c.SoundIoChannelIdTopFrontCenter,
|
||||
top_front_right = c.SoundIoChannelIdTopFrontRight,
|
||||
top_back_left = c.SoundIoChannelIdTopBackLeft,
|
||||
top_back_center = c.SoundIoChannelIdTopBackCenter,
|
||||
top_back_right = c.SoundIoChannelIdTopBackRight,
|
||||
|
||||
back_left_center = c.SoundIoChannelIdBackLeftCenter,
|
||||
back_right_center = c.SoundIoChannelIdBackRightCenter,
|
||||
front_left_wide = c.SoundIoChannelIdFrontLeftWide,
|
||||
front_right_wide = c.SoundIoChannelIdFrontRightWide,
|
||||
front_left_high = c.SoundIoChannelIdFrontLeftHigh,
|
||||
front_center_high = c.SoundIoChannelIdFrontCenterHigh,
|
||||
front_right_high = c.SoundIoChannelIdFrontRightHigh,
|
||||
top_front_left_center = c.SoundIoChannelIdTopFrontLeftCenter,
|
||||
top_front_right_center = c.SoundIoChannelIdTopFrontRightCenter,
|
||||
top_side_left = c.SoundIoChannelIdTopSideLeft,
|
||||
top_side_right = c.SoundIoChannelIdTopSideRight,
|
||||
left_lfe = c.SoundIoChannelIdLeftLfe,
|
||||
right_lfe = c.SoundIoChannelIdRightLfe,
|
||||
lfe2 = c.SoundIoChannelIdLfe2,
|
||||
bottom_center = c.SoundIoChannelIdBottomCenter,
|
||||
bottom_left_center = c.SoundIoChannelIdBottomLeftCenter,
|
||||
bottom_right_center = c.SoundIoChannelIdBottomRightCenter,
|
||||
|
||||
// Mid/side recording
|
||||
ms_mid = c.SoundIoChannelIdMsMid,
|
||||
ms_side = c.SoundIoChannelIdMsSide,
|
||||
|
||||
// first order ambisonic channels
|
||||
ambisonic_w = c.SoundIoChannelIdAmbisonicW,
|
||||
ambisonic_x = c.SoundIoChannelIdAmbisonicX,
|
||||
ambisonic_y = c.SoundIoChannelIdAmbisonicY,
|
||||
ambisonic_z = c.SoundIoChannelIdAmbisonicZ,
|
||||
|
||||
// X-Y Recording
|
||||
x_y_x = c.SoundIoChannelIdXyX,
|
||||
x_y_y = c.SoundIoChannelIdXyY,
|
||||
|
||||
headphones_left = c.SoundIoChannelIdHeadphonesLeft,
|
||||
headphones_right = c.SoundIoChannelIdHeadphonesRight,
|
||||
click_track = c.SoundIoChannelIdClickTrack,
|
||||
foreign_language = c.SoundIoChannelIdForeignLanguage,
|
||||
hearing_impaired = c.SoundIoChannelIdHearingImpaired,
|
||||
narration = c.SoundIoChannelIdNarration,
|
||||
haptic = c.SoundIoChannelIdHaptic,
|
||||
dialog_centric_mix = c.SoundIoChannelIdDialogCentricMix,
|
||||
|
||||
aux = c.SoundIoChannelIdAux,
|
||||
aux0 = c.SoundIoChannelIdAux0,
|
||||
aux1 = c.SoundIoChannelIdAux1,
|
||||
aux2 = c.SoundIoChannelIdAux2,
|
||||
aux3 = c.SoundIoChannelIdAux3,
|
||||
aux4 = c.SoundIoChannelIdAux4,
|
||||
aux5 = c.SoundIoChannelIdAux5,
|
||||
aux6 = c.SoundIoChannelIdAux6,
|
||||
aux7 = c.SoundIoChannelIdAux7,
|
||||
aux8 = c.SoundIoChannelIdAux8,
|
||||
aux9 = c.SoundIoChannelIdAux9,
|
||||
aux10 = c.SoundIoChannelIdAux10,
|
||||
aux11 = c.SoundIoChannelIdAux11,
|
||||
aux12 = c.SoundIoChannelIdAux12,
|
||||
aux13 = c.SoundIoChannelIdAux13,
|
||||
aux14 = c.SoundIoChannelIdAux14,
|
||||
aux15 = c.SoundIoChannelIdAux15,
|
||||
};
|
||||
|
||||
pub const ChannelLayoutId = enum(u5) {
|
||||
mono = c.SoundIoChannelLayoutIdMono,
|
||||
stereo = c.SoundIoChannelLayoutIdStereo,
|
||||
_2point1 = c.SoundIoChannelLayoutId2Point1,
|
||||
_3point0 = c.SoundIoChannelLayoutId3Point0,
|
||||
_3point0_back = c.SoundIoChannelLayoutId3Point0Back,
|
||||
_3point1 = c.SoundIoChannelLayoutId3Point1,
|
||||
_4point0 = c.SoundIoChannelLayoutId4Point0,
|
||||
quad = c.SoundIoChannelLayoutIdQuad,
|
||||
quadSide = c.SoundIoChannelLayoutIdQuadSide,
|
||||
_4point1 = c.SoundIoChannelLayoutId4Point1,
|
||||
_5point0_back = c.SoundIoChannelLayoutId5Point0Back,
|
||||
_5point0_side = c.SoundIoChannelLayoutId5Point0Side,
|
||||
_5point1 = c.SoundIoChannelLayoutId5Point1,
|
||||
_5point1_back = c.SoundIoChannelLayoutId5Point1Back,
|
||||
_6point0_side = c.SoundIoChannelLayoutId6Point0Side,
|
||||
_6point0_front = c.SoundIoChannelLayoutId6Point0Front,
|
||||
hexagonal = c.SoundIoChannelLayoutIdHexagonal,
|
||||
_6point1 = c.SoundIoChannelLayoutId6Point1,
|
||||
_6point1_back = c.SoundIoChannelLayoutId6Point1Back,
|
||||
_6point1_front = c.SoundIoChannelLayoutId6Point1Front,
|
||||
_7point0 = c.SoundIoChannelLayoutId7Point0,
|
||||
_7point0_front = c.SoundIoChannelLayoutId7Point0Front,
|
||||
_7point1 = c.SoundIoChannelLayoutId7Point1,
|
||||
_7point1_wide = c.SoundIoChannelLayoutId7Point1Wide,
|
||||
_7point1_wide_back = c.SoundIoChannelLayoutId7Point1WideBack,
|
||||
octagonal = c.SoundIoChannelLayoutIdOctagonal,
|
||||
};
|
||||
|
||||
pub const Backend = enum(u3) {
|
||||
none = c.SoundIoBackendNone,
|
||||
jack = c.SoundIoBackendJack,
|
||||
pulseaudio = c.SoundIoBackendPulseAudio,
|
||||
alsa = c.SoundIoBackendAlsa,
|
||||
coreaudio = c.SoundIoBackendCoreAudio,
|
||||
wasapi = c.SoundIoBackendWasapi,
|
||||
dummy = c.SoundIoBackendDummy,
|
||||
};
|
||||
|
||||
pub const Aim = enum(u1) {
|
||||
input = c.SoundIoDeviceAimInput,
|
||||
output = c.SoundIoDeviceAimOutput,
|
||||
};
|
||||
|
||||
pub const Format = enum(u5) {
|
||||
invalid = c.SoundIoFormatInvalid,
|
||||
S8 = c.SoundIoFormatS8,
|
||||
U8 = c.SoundIoFormatU8,
|
||||
S16LE = c.SoundIoFormatS16LE,
|
||||
S16BE = c.SoundIoFormatS16BE,
|
||||
U16LE = c.SoundIoFormatU16LE,
|
||||
U16BE = c.SoundIoFormatU16BE,
|
||||
S24LE = c.SoundIoFormatS24LE,
|
||||
S24BE = c.SoundIoFormatS24BE,
|
||||
U24LE = c.SoundIoFormatU24LE,
|
||||
U24BE = c.SoundIoFormatU24BE,
|
||||
S32LE = c.SoundIoFormatS32LE,
|
||||
S32BE = c.SoundIoFormatS32BE,
|
||||
U32LE = c.SoundIoFormatU32LE,
|
||||
U32BE = c.SoundIoFormatU32BE,
|
||||
float32LE = c.SoundIoFormatFloat32LE,
|
||||
float32BE = c.SoundIoFormatFloat32BE,
|
||||
float64LE = c.SoundIoFormatFloat64LE,
|
||||
float64BE = c.SoundIoFormatFloat64BE,
|
||||
};
|
||||
62
sysaudio/soundio/error.zig
Normal file
62
sysaudio/soundio/error.zig
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
const std = @import("std");
|
||||
const c = @import("c.zig");
|
||||
|
||||
pub const Error = error{
|
||||
OutOfMemory,
|
||||
/// The backend does not appear to be active or running.
|
||||
InitAudioBackend,
|
||||
/// A system resource other than memory was not available.
|
||||
SystemResources,
|
||||
/// Attempted to open a device and failed.
|
||||
OpeningDevice,
|
||||
NoSuchDevice,
|
||||
/// The programmer did not comply with the API.
|
||||
Invalid,
|
||||
/// libsoundio was compiled without support for that backend.
|
||||
BackendUnavailable,
|
||||
/// An open stream had an error that can only be recovered from by
|
||||
/// destroying the stream and creating it again.
|
||||
Streaming,
|
||||
/// Attempted to use a device with parameters it cannot support.
|
||||
IncompatibleDevice,
|
||||
/// When JACK returns `JackNoSuchClient`
|
||||
NoSuchClient,
|
||||
/// Attempted to use parameters that the backend cannot support.
|
||||
IncompatibleBackend,
|
||||
/// Backend server shutdown or became inactive.
|
||||
BackendDisconnected,
|
||||
Interrupted,
|
||||
/// Buffer underrun occurred.
|
||||
Underflow,
|
||||
/// Unable to convert to or from UTF-8 to the native string format.
|
||||
EncodingString,
|
||||
};
|
||||
|
||||
pub fn intToError(err: c_int) Error!void {
|
||||
return switch (err) {
|
||||
c.SoundIoErrorNone => {},
|
||||
c.SoundIoErrorNoMem => Error.OutOfMemory,
|
||||
c.SoundIoErrorInitAudioBackend => Error.InitAudioBackend,
|
||||
c.SoundIoErrorSystemResources => Error.SystemResources,
|
||||
c.SoundIoErrorOpeningDevice => Error.OpeningDevice,
|
||||
c.SoundIoErrorNoSuchDevice => Error.NoSuchDevice,
|
||||
c.SoundIoErrorInvalid => Error.Invalid,
|
||||
c.SoundIoErrorBackendUnavailable => Error.BackendUnavailable,
|
||||
c.SoundIoErrorStreaming => Error.Streaming,
|
||||
c.SoundIoErrorIncompatibleDevice => Error.IncompatibleDevice,
|
||||
c.SoundIoErrorNoSuchClient => Error.NoSuchClient,
|
||||
c.SoundIoErrorIncompatibleBackend => Error.IncompatibleBackend,
|
||||
c.SoundIoErrorBackendDisconnected => Error.BackendDisconnected,
|
||||
c.SoundIoErrorInterrupted => Error.Interrupted,
|
||||
c.SoundIoErrorUnderflow => Error.Underflow,
|
||||
c.SoundIoErrorEncodingString => Error.EncodingString,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
test "error convertion" {
|
||||
const expectError = @import("std").testing.expectError;
|
||||
|
||||
try intToError(c.SoundIoErrorNone);
|
||||
try expectError(Error.OutOfMemory, intToError(c.SoundIoErrorNoMem));
|
||||
}
|
||||
29
sysaudio/soundio/main.zig
Normal file
29
sysaudio/soundio/main.zig
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
pub usingnamespace @import("enums.zig");
|
||||
pub const c = @import("c.zig");
|
||||
pub const SoundIo = @import("SoundIo.zig");
|
||||
pub const Device = @import("Device.zig");
|
||||
pub const InStream = @import("InStream.zig");
|
||||
pub const OutStream = @import("OutStream.zig");
|
||||
pub const Error = @import("error.zig").Error;
|
||||
|
||||
test {
|
||||
refAllDecls(@import("SoundIo.zig"));
|
||||
refAllDecls(@import("Device.zig"));
|
||||
refAllDecls(@import("OutStream.zig"));
|
||||
refAllDecls(@import("ChannelLayout.zig"));
|
||||
}
|
||||
|
||||
fn refAllDecls(comptime T: type) void {
|
||||
@setEvalBranchQuota(10000);
|
||||
inline for (comptime @import("std").meta.declarations(T)) |decl| {
|
||||
if (decl.is_pub) {
|
||||
if (@TypeOf(@field(T, decl.name)) == type) {
|
||||
switch (@typeInfo(@field(T, decl.name))) {
|
||||
.Struct, .Enum, .Union, .Opaque => refAllDecls(@field(T, decl.name)),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
_ = @field(T, decl.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
140
sysaudio/src/main.zig
Normal file
140
sysaudio/src/main.zig
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
const builtin = @import("builtin");
|
||||
const Backend = switch (builtin.os.tag) {
|
||||
.linux,
|
||||
.windows,
|
||||
.macos,
|
||||
.ios,
|
||||
=> @import("soundio.zig"),
|
||||
else => @compileError("unsupported os"),
|
||||
};
|
||||
pub const Error = Backend.Error;
|
||||
pub const Device = Backend.Device;
|
||||
pub const DeviceIterator = Backend.DeviceIterator;
|
||||
|
||||
pub const Mode = enum {
|
||||
input,
|
||||
output,
|
||||
};
|
||||
|
||||
pub const Format = enum {
|
||||
U8,
|
||||
S16,
|
||||
S24,
|
||||
S32,
|
||||
F32,
|
||||
};
|
||||
|
||||
pub const DeviceDescriptor = struct {
|
||||
mode: ?Mode = null,
|
||||
format: ?Format = null,
|
||||
is_raw: ?bool = null,
|
||||
channels: ?u8 = null,
|
||||
sample_rate: ?u32 = null,
|
||||
id: ?[:0]const u8 = null,
|
||||
name: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
const Audio = @This();
|
||||
|
||||
backend: Backend,
|
||||
|
||||
pub fn init() Error!Audio {
|
||||
return Audio{
|
||||
.backend = try Backend.init(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Audio) void {
|
||||
self.backend.deinit();
|
||||
}
|
||||
|
||||
pub fn waitEvents(self: Audio) void {
|
||||
self.backend.waitEvents();
|
||||
}
|
||||
|
||||
pub fn requestDevice(self: Audio, config: DeviceDescriptor) Error!Device {
|
||||
return self.backend.requestDevice(config);
|
||||
}
|
||||
|
||||
pub fn inputDeviceIterator(self: Audio) DeviceIterator {
|
||||
return self.backend.inputDeviceIterator();
|
||||
}
|
||||
|
||||
pub fn outputDeviceIterator(self: Audio) DeviceIterator {
|
||||
return self.backend.outputDeviceIterator();
|
||||
}
|
||||
|
||||
test "list devices" {
|
||||
const a = try init();
|
||||
defer a.deinit();
|
||||
|
||||
var iter = a.inputDeviceIterator();
|
||||
while (try iter.next()) |_| {}
|
||||
}
|
||||
|
||||
test "connect to device" {
|
||||
const a = try init();
|
||||
defer a.deinit();
|
||||
|
||||
const d = try a.requestDevice(.{ .mode = .output });
|
||||
defer d.deinit();
|
||||
}
|
||||
|
||||
test "connect to device from descriptor" {
|
||||
const a = try init();
|
||||
defer a.deinit();
|
||||
|
||||
var iter = a.outputDeviceIterator();
|
||||
var device_desc = (try iter.next()) orelse return error.NoDeviceFound;
|
||||
|
||||
const d = try a.requestDevice(device_desc);
|
||||
defer d.deinit();
|
||||
}
|
||||
|
||||
test "requestDevice behavior: null is_raw" {
|
||||
const a = try init();
|
||||
defer a.deinit();
|
||||
|
||||
var iter = a.outputDeviceIterator();
|
||||
var device_desc = (try iter.next()) orelse return error.NoDeviceFound;
|
||||
|
||||
const bad_desc = DeviceDescriptor{
|
||||
.is_raw = null,
|
||||
.mode = device_desc.mode,
|
||||
.id = device_desc.id,
|
||||
};
|
||||
try testing.expectError(error.InvalidParameter, a.requestDevice(bad_desc));
|
||||
}
|
||||
|
||||
test "requestDevice behavior: null mode" {
|
||||
const a = try init();
|
||||
defer a.deinit();
|
||||
|
||||
var iter = a.outputDeviceIterator();
|
||||
var device_desc = (try iter.next()) orelse return error.NoDeviceFound;
|
||||
|
||||
const bad_desc = DeviceDescriptor{
|
||||
.is_raw = device_desc.is_raw,
|
||||
.mode = null,
|
||||
.id = device_desc.id,
|
||||
};
|
||||
try testing.expectError(error.InvalidParameter, a.requestDevice(bad_desc));
|
||||
}
|
||||
|
||||
test "requestDevice behavior: invalid id" {
|
||||
const a = try init();
|
||||
defer a.deinit();
|
||||
|
||||
var iter = a.outputDeviceIterator();
|
||||
var device_desc = (try iter.next()) orelse return error.NoDeviceFound;
|
||||
|
||||
const bad_desc = DeviceDescriptor{
|
||||
.is_raw = device_desc.is_raw,
|
||||
.mode = device_desc.mode,
|
||||
.id = "wrong-id",
|
||||
};
|
||||
try testing.expectError(error.DeviceUnavailable, a.requestDevice(bad_desc));
|
||||
}
|
||||
159
sysaudio/src/soundio.zig
Normal file
159
sysaudio/src/soundio.zig
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
const std = @import("std");
|
||||
const Mode = @import("main.zig").Mode;
|
||||
const DeviceDescriptor = @import("main.zig").DeviceDescriptor;
|
||||
const c = @import("soundio").c;
|
||||
const Aim = @import("soundio").Aim;
|
||||
const SoundIo = @import("soundio").SoundIo;
|
||||
const SoundIoDevice = @import("soundio").Device;
|
||||
const SoundIoInStream = @import("soundio").InStream;
|
||||
const SoundIoOutStream = @import("soundio").OutStream;
|
||||
const SoundIoStream = union(Mode) {
|
||||
input: SoundIoInStream,
|
||||
output: SoundIoOutStream,
|
||||
};
|
||||
|
||||
const Audio = @This();
|
||||
|
||||
pub const DataCallback = fn (device: Device, frame_count: u32) void;
|
||||
pub const Device = struct {
|
||||
handle: SoundIoStream,
|
||||
data_callback: ?DataCallback = null,
|
||||
user_data: ?*anyopaque = null,
|
||||
|
||||
pub fn setCallback(self: Device, callback: DataCallback, data: *anyopaque) void {
|
||||
self.data_callback = callback;
|
||||
self.user_data = data;
|
||||
}
|
||||
|
||||
pub fn deinit(self: Device) void {
|
||||
return switch (self.handle) {
|
||||
.input => |d| d.deinit(),
|
||||
.output => |d| d.deinit(),
|
||||
};
|
||||
}
|
||||
};
|
||||
pub const DeviceIterator = struct {
|
||||
ctx: Audio,
|
||||
mode: Mode,
|
||||
device_len: u16,
|
||||
index: u16,
|
||||
|
||||
pub fn next(self: *DeviceIterator) IteratorError!?DeviceDescriptor {
|
||||
if (self.index < self.device_len) {
|
||||
const device_desc = switch (self.mode) {
|
||||
.input => self.ctx.handle.getInputDevice(self.index) orelse return null,
|
||||
.output => self.ctx.handle.getOutputDevice(self.index) orelse return null,
|
||||
};
|
||||
self.index += 1;
|
||||
return DeviceDescriptor{
|
||||
.mode = switch (@intToEnum(Aim, device_desc.handle.aim)) {
|
||||
.input => .input,
|
||||
.output => .output,
|
||||
},
|
||||
.is_raw = device_desc.handle.is_raw,
|
||||
.id = device_desc.id(),
|
||||
.name = device_desc.name(),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const IteratorError = error{OutOfMemory};
|
||||
pub const Error = error{
|
||||
OutOfMemory,
|
||||
InvalidDeviceID,
|
||||
InvalidParameter,
|
||||
NoDeviceFound,
|
||||
AlreadyConnected,
|
||||
CannotConnect,
|
||||
UnsupportedOS,
|
||||
UnsupportedBackend,
|
||||
DeviceUnavailable,
|
||||
};
|
||||
|
||||
handle: SoundIo,
|
||||
|
||||
pub fn init() Error!Audio {
|
||||
var self = Audio{
|
||||
.handle = try SoundIo.init(),
|
||||
};
|
||||
self.handle.connect() catch |err| {
|
||||
return switch (err) {
|
||||
error.SystemResources, error.NoSuchClient => error.CannotConnect,
|
||||
error.Invalid => error.AlreadyConnected,
|
||||
error.OutOfMemory => error.OutOfMemory,
|
||||
else => unreachable,
|
||||
};
|
||||
};
|
||||
self.handle.flushEvents();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: Audio) void {
|
||||
self.handle.deinit();
|
||||
}
|
||||
|
||||
pub fn waitEvents(self: Audio) void {
|
||||
self.handle.waitEvents();
|
||||
}
|
||||
|
||||
pub fn requestDevice(self: Audio, config: DeviceDescriptor) Error!Device {
|
||||
return Device{
|
||||
.handle = blk: {
|
||||
var sio_device: SoundIoDevice = undefined;
|
||||
|
||||
if (config.id) |id| {
|
||||
if (config.mode == null or config.is_raw == null)
|
||||
return error.InvalidParameter;
|
||||
|
||||
sio_device = switch (config.mode.?) {
|
||||
.input => self.handle.getInputDeviceFromID(id, config.is_raw.?),
|
||||
.output => self.handle.getOutputDeviceFromID(id, config.is_raw.?),
|
||||
} orelse {
|
||||
return if (switch (config.mode.?) {
|
||||
.input => self.handle.inputDeviceCount().?,
|
||||
.output => self.handle.outputDeviceCount().?,
|
||||
} == 0)
|
||||
error.NoDeviceFound
|
||||
else
|
||||
error.DeviceUnavailable;
|
||||
};
|
||||
} else {
|
||||
if (config.mode == null) return error.InvalidParameter;
|
||||
|
||||
const id = switch (config.mode.?) {
|
||||
.input => self.handle.defaultInputDeviceIndex(),
|
||||
.output => self.handle.defaultOutputDeviceIndex(),
|
||||
} orelse return error.NoDeviceFound;
|
||||
sio_device = switch (config.mode.?) {
|
||||
.input => self.handle.getInputDevice(id),
|
||||
.output => self.handle.getOutputDevice(id),
|
||||
} orelse return error.DeviceUnavailable;
|
||||
}
|
||||
|
||||
break :blk switch (config.mode.?) {
|
||||
.input => SoundIoStream{ .input = try sio_device.createInStream() },
|
||||
.output => SoundIoStream{ .output = try sio_device.createOutStream() },
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn outputDeviceIterator(self: Audio) DeviceIterator {
|
||||
return .{
|
||||
.ctx = self,
|
||||
.mode = .output,
|
||||
.device_len = self.handle.outputDeviceCount() orelse 0,
|
||||
.index = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn inputDeviceIterator(self: Audio) DeviceIterator {
|
||||
return .{
|
||||
.ctx = self,
|
||||
.mode = .input,
|
||||
.device_len = self.handle.inputDeviceCount() orelse 0,
|
||||
.index = 0,
|
||||
};
|
||||
}
|
||||
1
sysaudio/upstream
Submodule
1
sysaudio/upstream
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 434854ee49fd4233394310174298536381cfa0f1
|
||||
Loading…
Add table
Add a link
Reference in a new issue