Skip to content

Instantly share code, notes, and snippets.

@jamal
Last active May 18, 2023 01:44
Show Gist options
  • Save jamal/8ee096ca98759f83b4942f22d365d449 to your computer and use it in GitHub Desktop.
Save jamal/8ee096ca98759f83b4942f22d365d449 to your computer and use it in GitHub Desktop.
Example of using CoreAudio API in Zig to play a beep
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "tmp",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
exe.linkFramework("CoreFoundation");
exe.linkFramework("CoreAudio");
exe.linkFramework("AudioToolbox");
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
const std = @import("std");
const c = @cImport({
@cInclude("CoreFoundation/CoreFoundation.h");
@cInclude("AudioUnit/AudioUnit.h");
});
const Error = error{
GenericError,
};
var theta: f32 = 0;
pub fn main() !void {
var acd = c.AudioComponentDescription{
.componentType = c.kAudioUnitType_Output,
.componentSubType = c.kAudioUnitSubType_DefaultOutput,
.componentManufacturer = c.kAudioUnitManufacturer_Apple,
.componentFlags = std.mem.zeroes(u32),
.componentFlagsMask = std.mem.zeroes(u32),
};
const output: c.AudioComponent = c.AudioComponentFindNext(null, &acd) orelse {
std.debug.panic("error finding device\n", .{});
};
var audioUnit: c.AudioUnit = undefined;
osStatusHandler(c.AudioComponentInstanceNew(output, &audioUnit)) catch |err| {
std.debug.panic("audio component instance failed: {}\n", .{err});
};
defer _ = c.AudioComponentInstanceDispose(audioUnit);
var input = c.AURenderCallbackStruct{
.inputProc = &renderSineWave,
.inputProcRefCon = std.mem.zeroes(?*anyopaque),
};
osStatusHandler(c.AudioUnitSetProperty(audioUnit, c.kAudioUnitProperty_SetRenderCallback, c.kAudioUnitScope_Input, 0, &input, @sizeOf(@TypeOf(input)))) catch |err| {
std.debug.panic("failed to set render callback: {}\n", .{err});
};
var asbd = c.AudioStreamBasicDescription{
.mFormatID = c.kAudioFormatLinearPCM,
.mFormatFlags = 0 | c.kAudioFormatFlagIsSignedInteger | c.kAudioFormatFlagIsPacked | c.kAudioFormatFlagIsNonInterleaved,
.mSampleRate = 48000,
.mBitsPerChannel = 16,
.mChannelsPerFrame = 2,
.mFramesPerPacket = 1,
.mBytesPerFrame = 2,
.mBytesPerPacket = 2,
.mReserved = 0,
};
osStatusHandler(c.AudioUnitSetProperty(audioUnit, c.kAudioUnitProperty_StreamFormat, c.kAudioUnitScope_Input, 0, &asbd, @sizeOf(@TypeOf(asbd)))) catch |err| {
std.debug.panic("failed to set stream format: {}\n", .{err});
};
osStatusHandler(c.AudioUnitInitialize(audioUnit)) catch |err| {
std.debug.panic("failed to initialize: {}\n", .{err});
};
defer _ = c.AudioUnitUninitialize(audioUnit);
osStatusHandler(c.AudioOutputUnitStart(audioUnit)) catch |err| {
std.debug.panic("failed to start audio unit: {}\n", .{err});
};
defer _ = c.AudioOutputUnitStop(audioUnit);
std.log.debug("running", .{});
std.time.sleep(5000 * std.time.ns_per_ms);
std.log.debug("done", .{});
}
fn osStatusHandler(result: c.OSStatus) !void {
if (result != c.noErr) {
// TODO: Map to specific errors
return Error.GenericError;
}
}
export fn renderSineWave(inRefCon: ?*anyopaque, ioActionFlags: [*c]c.AudioUnitRenderActionFlags, inTimestamp: [*c]const c.AudioTimeStamp, inBusNumber: u32, inNumFrames: u32, ioData: [*c]c.AudioBufferList) c.OSStatus {
_ = inBusNumber;
_ = inTimestamp;
_ = ioActionFlags;
_ = inRefCon;
// var data: ?*anyopaque = ioData.?.*.mBuffers[0].mData;
var buf: [*]i16 = @ptrCast([*]i16, @alignCast(2, ioData.?.*.mBuffers[0].mData));
var frame: u32 = 0;
while (frame < inNumFrames) {
buf[frame] = @floatToInt(i16, std.math.sin(theta) * 32767.0);
theta += std.math.tau * 440.0 / 48000.0;
if (theta > std.math.tau) {
theta -= std.math.tau;
}
frame += 1;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment