Last active
December 23, 2019 12:26
-
-
Save thara/f1a4c725d8ef743c543a4e808b06db52 to your computer and use it in GitHub Desktop.
Use libsoundio from Swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Sources/SoundIODemo/main.swift | |
import CSoundIO | |
func soundioError(_ errorCode: Int32) -> String { | |
return String(cString: soundio_strerror(errorCode)) | |
} | |
var secondsOffset: Float = 0.0 | |
func writeCallback(outstream: UnsafeMutablePointer<SoundIoOutStream>?, frameCountMin: Int32, frameCountMax: Int32) { | |
guard let o = outstream else { return } | |
let layout: SoundIoChannelLayout = o.pointee.layout | |
let secondsPerFrame = 1.0 / Float(o.pointee.sample_rate) | |
var areas: UnsafeMutablePointer<SoundIoChannelArea>? = nil | |
var framesLeft = frameCountMax | |
var err: CInt = 0 | |
while 0 < framesLeft { | |
var frameCount = framesLeft | |
err = soundio_outstream_begin_write(outstream, &areas, &frameCount) | |
if 0 < err { | |
fatalError(soundioError(err)) | |
} | |
if frameCount == 0 { | |
break | |
} | |
let pitch: Float = 440.0 | |
let radiansPerSecond = pitch * 2.0 * .pi | |
for frame in 0..<frameCount { | |
let sample = sin((secondsOffset + Float(frame) * secondsPerFrame) * radiansPerSecond) | |
if frame == 100 { | |
print("\(secondsOffset) \(frame) \(secondsPerFrame) \(radiansPerSecond) \(sample)") | |
} | |
for channel in 0..<layout.channel_count { | |
if let a = areas?[Int(channel)] { | |
let p: UnsafeMutablePointer<Int8> = a.ptr + Int(a.step * frame) | |
p.withMemoryRebound(to: Float.self, capacity: 1) { | |
$0.pointee = sample | |
} | |
} | |
} | |
} | |
secondsOffset = (secondsOffset + secondsPerFrame * Float(frameCount)).truncatingRemainder(dividingBy: 1) | |
err = soundio_outstream_end_write(outstream) | |
if 0 < err { | |
fatalError(soundioError(err)) | |
} | |
framesLeft -= frameCount | |
} | |
} | |
func main() { | |
guard let soundio: UnsafeMutablePointer<SoundIo> = soundio_create() else { | |
fatalError("out of memory") | |
} | |
var err: CInt = soundio_connect(soundio) | |
if 0 < err { | |
fatalError("error connecting: \(soundioError(err))") | |
} | |
soundio_flush_events(soundio) | |
let defaultOutDeviceIndex: CInt = soundio_default_output_device_index(soundio) | |
if defaultOutDeviceIndex < 0 { | |
fatalError("no output device found") | |
} | |
guard let device: UnsafeMutablePointer<SoundIoDevice> = soundio_get_output_device(soundio, defaultOutDeviceIndex) else { | |
fatalError("out of memory") | |
} | |
let deviceName = String(cString: device.pointee.name) | |
print("Output device: \(deviceName)") | |
guard let outstream: UnsafeMutablePointer<SoundIoOutStream> = soundio_outstream_create(device) else { | |
fatalError("out of memory") | |
} | |
outstream.pointee.format = SoundIoFormatFloat32LE; | |
outstream.pointee.write_callback = writeCallback; | |
err = soundio_outstream_open(outstream) | |
if 0 < err { | |
fatalError("unable to open device: \(soundioError(err))") | |
} | |
if 0 < outstream.pointee.layout_error { | |
fatalError("unable to set channel layout: \(soundioError(outstream.pointee.layout_error))") | |
} | |
err = soundio_outstream_start(outstream) | |
if 0 < err { | |
fatalError("unable to start device: \(soundioError(err))") | |
} | |
while true { | |
soundio_wait_events(soundio) | |
} | |
soundio_outstream_destroy(outstream) | |
soundio_device_unref(device) | |
soundio_destroy(soundio) | |
} | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module CSoundIO [system] { | |
header "shim.h" | |
link "soundio" | |
export * | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// swift-tools-version:5.0 | |
// The swift-tools-version declares the minimum version of Swift required to build this package. | |
import PackageDescription | |
let package = Package( | |
name: "SoundIOSample", | |
products: [ | |
.executable( | |
name: "SoundIODemo", | |
targets: ["SoundIODemo"]), | |
], | |
targets: [ | |
.systemLibrary( | |
name: "CSoundIO", | |
providers: [.brew(["soundio"])] | |
), | |
.target( | |
name: "SoundIODemo", | |
dependencies: ["SoundIO", "CSoundIO"]), | |
] | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Sources/CSoundIO/shim.h | |
#ifdef __APPLE__ | |
#include "/usr/local/include/soundio/soundio.h" | |
#else | |
#include <soundio/soundio.h> | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment