SlideShare a Scribd company logo
Media Frameworks
Versus Swift
Chris Adamson • @invalidname
Swift by Northwest, October 2017
Slides available at slideshare.net/invalidname
Code available at github.com/invalidstream
Who the what, now?
@invalidname
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
import Cocoa
import AVFoundation
import CoreMediaIO
if let devices = AVCaptureDevice.devices(),
let avDevices = devices.filter(
{$0 is AVCaptureDevice}) as? [AVCaptureDevice] {
for device in avDevices {
print("(device.description)")
}
}
[Loopback Simulator][com.rogueamoeba.Loopback:E8577B20-0806-4472-A5E6-426CABCD6C8E]
[Loopback Line-In][com.rogueamoeba.Loopback:A00F38FD-C2B6-43FD-98B7-23BAA6FACB03]
[iMic USB audio system][AppleUSBAudioEngine:Griffin Technology, Inc:iMic USB audio system:220000:2,1]
[Loopback Keynote][com.rogueamoeba.Loopback:1936D2A3-6D0B-428E-899E-0ABE46628EA4]
[Soundflower (64ch)][SoundflowerEngine:1]
[HD Pro Webcam C920][AppleUSBAudioEngine:Unknown Manufacturer:HD Pro Webcam C920:1218B05F:3]
[Soundflower (2ch)][SoundflowerEngine:0]
[iGlasses][iGlasses]
[HD Pro Webcam C920][0x244000046d082d]
Program ended with exit code: 0
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
CMIOObjectPropertyAddress prop =
{ kCMIOHardwarePropertyAllowScreenCaptureDevices,
kCMIOObjectPropertyScopeGlobal,
kCMIOObjectPropertyElementMaster };
UInt32 allow = 1;
CMIOObjectSetPropertyData( kCMIOObjectSystemObject,
&prop, 0, NULL, sizeof(allow), &allow );
var prop = CMIOObjectPropertyAddress(
mSelector: CMIOObjectPropertySelector(
kCMIOHardwarePropertyAllowScreenCaptureDevices),
mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal),
mElement: CMIOObjectPropertyElement(
kCMIOObjectPropertyElementMaster))
var allow : UInt32 = 1
CMIOObjectSetPropertyData(CMIOObjectID(kCMIOObjectSystemObject),
&prop,
0,
nil,
UInt32(MemoryLayout<UInt32>.size),
&allow)
CMIOObjectPropertyAddress prop =
{ kCMIOHardwarePropertyAllowScreenCaptureDevices,
kCMIOObjectPropertyScopeGlobal,
kCMIOObjectPropertyElementMaster };
UInt32 allow = 1;
CMIOObjectSetPropertyData( kCMIOObjectSystemObject,
&prop, 0, NULL, sizeof(allow), &allow );
var prop = CMIOObjectPropertyAddress(
mSelector: CMIOObjectPropertySelector(
kCMIOHardwarePropertyAllowScreenCaptureDevices),
mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal),
mElement: CMIOObjectPropertyElement(
kCMIOObjectPropertyElementMaster))
var allow : UInt32 = 1
CMIOObjectSetPropertyData(CMIOObjectID(kCMIOObjectSystemObject),
&prop,
0,
nil,
UInt32(MemoryLayout<UInt32>.size),
&allow)
This
is
fine
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
var prop = CMIOObjectPropertyAddress(
mSelector: CMIOObjectPropertySelector(
kCMIOHardwarePropertyAllowScreenCaptureDevices),
mScope: CMIOObjectPropertyScope(
kCMIOObjectPropertyScopeGlobal),
mElement: CMIOObjectPropertyElement(
kCMIOObjectPropertyElementMaster))
var prop = CMIOObjectPropertyAddress(
mSelector: CMIOObjectPropertySelector(
kCMIOHardwarePropertyAllowScreenCaptureDevices),
mScope: CMIOObjectPropertyScope(
kCMIOObjectPropertyScopeGlobal),
mElement: CMIOObjectPropertyElement(
kCMIOObjectPropertyElementMaster))
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
public typealias CMIOObjectPropertySelector = UInt32
public typealias CMIOObjectPropertyScope = UInt32
public typealias CMIOObjectPropertyElement = UInt32
public struct CMIOObjectPropertyAddress {
public var mSelector: CMIOObjectPropertySelector
public var mScope: CMIOObjectPropertyScope
public var mElement: CMIOObjectPropertyElement
public init()
public init(mSelector: CMIOObjectPropertySelector,
mScope: CMIOObjectPropertyScope,
mElement: CMIOObjectPropertyElement)
}
extension CMIOObjectPropertySelector {
static let allowScreenCaptureDevices = CMIOObjectPropertySelector(
kCMIOHardwarePropertyAllowScreenCaptureDevices)
}
extension CMIOObjectPropertyScope {
static let global = CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal)
}
extension CMIOObjectPropertyElement {
static let master = CMIOObjectPropertyElement(kCMIOObjectPropertyElementMaster)
}
var prop = CMIOObjectPropertyAddress(
mSelector: .allowScreenCaptureDevices,
mScope: .global,
mElement: .master)
var prop = CMIOObjectPropertyAddress(
mSelector: CMIOObjectPropertySelector(
kCMIOHardwarePropertyAllowScreenCaptureDevices),
mScope: CMIOObjectPropertyScope(
kCMIOObjectPropertyScopeGlobal),
mElement: CMIOObjectPropertyElement(
kCMIOObjectPropertyElementMaster))
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Demo
http://github.com/invalidstream/audio-reverser
Reversing Audio
1. Decode the MP3/AAC to LPCM2. Grab a buffer from the end3. Reverse its samples in memory4. Write it to the front of a new file5. Repeat until fully baked
API Needs
• Convert from MP3/AAC to LPCM
• Write sequentially to audio file (.caf, .aif, .wav)
• Random-access read from audio file
Plan A (Swift)
• AV Foundation
• AVAssetReader/Writer can do format conversion
while reading/writing audio files
• Can’t (easily) read from arbitrary packet offsets;
meant to process everything forward
Plan B (C, Swift?)
• Audio Toolbox (part of Core Audio)
• ExtAudioFile can do format conversions while
reading/writing audio files
• AudioFile can read from arbitrary packet offset
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
// declare LPCM format we are converting to
AudioStreamBasicDescription format = {0};
format.mSampleRate = 44100.0;
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kAudioFormatFlagIsPacked |
kAudioFormatFlagIsSignedInteger;
format.mBitsPerChannel = 16;
format.mChannelsPerFrame = 2;
format.mBytesPerFrame = 4;
format.mFramesPerPacket = 1;
format.mBytesPerPacket = 4;
// declare LPCM format we are converting to
var format = AudioStreamBasicDescription(
mSampleRate: 44100.0,
mFormatID: kAudioFormatLinearPCM,
mFormatFlags: kAudioFormatFlagIsPacked +
kAudioFormatFlagIsSignedInteger,
mBytesPerPacket: 4,
mFramesPerPacket: 1,
mBytesPerFrame: 4,
mChannelsPerFrame: 2,
mBitsPerChannel: 16,
mReserved: 0)
// open AudioFile for output
AudioFileID forwardAudioFile;
err = AudioFileCreateWithURL(forwardURL,
kAudioFileCAFType,
&format,
kAudioFileFlags_EraseFile,
&forwardAudioFile);
IF_ERR_RETURN
#define IF_ERR_RETURN if (err != noErr) { return err; }
// open AudioFile for output
var forwardAudioFile: AudioFileID?
err = AudioFileCreateWithURL(forwardURL,
kAudioFileCAFType,
&format,
AudioFileFlags.eraseFile,
&forwardAudioFile)
if err != noErr { return err }
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
// open AudioFile for output
var forwardAudioFile: AudioFileID?
err = AudioFileCreateWithURL(forwardURL,
kAudioFileCAFType,
&format,
AudioFileFlags.eraseFile,
&forwardAudioFile)
if err != noErr { return err }
// open AudioFile for output
var forwardAudioFile: AudioFileID?
err = AudioFileCreateWithURL(forwardURL,
kAudioFileCAFType,
&format,
AudioFileFlags.eraseFile,
&forwardAudioFile)
if err != noErr { return err }
1. Uses a free function, rather than a method on AudioFile
// open AudioFile for output
var forwardAudioFile: AudioFileID?
err = AudioFileCreateWithURL(forwardURL,
kAudioFileCAFType,
&format,
AudioFileFlags.eraseFile,
&forwardAudioFile)
if err != noErr { return err }
2. Errors are communicated via the return value, rather than
throws
// open AudioFile for output
var forwardAudioFile: AudioFileID?
err = AudioFileCreateWithURL(forwardURL,
kAudioFileCAFType,
&format,
AudioFileFlags.eraseFile,
&forwardAudioFile)
if err != noErr { return err }
3. Some parameters are UInt32 constants, some are enums
// open AudioFile for output
var forwardAudioFile: AudioFileID?
err = AudioFileCreateWithURL(forwardURL,
kAudioFileCAFType,
&format,
AudioFileFlags.eraseFile,
&forwardAudioFile)
if err != noErr { return err }
4. Audio format is passed as an
UnsafePointer<AudioStreamBasicDescription>
// open AudioFile for output
var forwardAudioFile: AudioFileID?
err = AudioFileCreateWithURL(forwardURL,
kAudioFileCAFType,
&format,
AudioFileFlags.eraseFile,
&forwardAudioFile)
if err != noErr { return err }
5. Created object is returned via an in-out parameter
To say nothing of…
Pointer arithmetic!
// swap packets inside transfer buffer
for i in 0..<packetsToTransfer/2 {
let swapSrc = transferBuffer.advanced(by: Int(i) * Int(format.mBytesPerPacket))
let swapDst = transferBuffer.advanced(by: transferBufferSize -
(Int(i+1) * Int(format.mBytesPerPacket)))
memcpy(swapBuffer, swapSrc, Int(format.mBytesPerPacket))
memcpy(swapSrc, swapDst, Int(format.mBytesPerPacket))
memcpy(swapDst, swapBuffer, Int(format.mBytesPerPacket))
}
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Couldn’t you just…
extension AudioFileID {
init? (url: URL, fileType: UInt32,
format: AudioStreamBasicDescription, flags: AudioFileFlags) {
var fileId : AudioFileID?
var format = format
let err = AudioFileCreateWithURL(url as CFURL,
fileType,
&format,
flags,
&fileId)
guard err != noErr, let createdFile = fileId else { return nil }
self = createdFile
}
}
Been there, done that
• The Amazing Audio Engine 💀
• Novocaine (💀?)
• EZAudio 💀
• AudioKit
• Superpowered
• etc…
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
/**
Convert a source audio file (using any Core Audio-supported codec) and create LPCM .caf
files for its forward and backward versions.
- parameter sourceURL: A file URL containing the source audio to be read from
- parameter forwardURL: A file URL with the destination to write the decompressed (LPCM) forward file
- parameter backwardURL: A file URL with the destination to write the backward file
*/
OSStatus convertAndReverse(CFURLRef sourceURL, CFURLRef forwardURL, CFURLRef backwardURL);
AudioReversingC.h
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import <CoreFoundation/CoreFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
OSStatus convertAndReverse(CFURLRef sourceURL, CFURLRef forwardURL, CFURLRef backwardURL);
AudioReverser-Bridging-Header.h
if USE_SWIFT_CONVERTER {
err = convertAndReverseSwift(sourceURL: source as CFURL,
forwardURL: self.forwardURL as! CFURL,
backwardURL: self.backwardURL as! CFURL)
} else {
err = convertAndReverse(source as! CFURL,
self.forwardURL as! CFURL,
self.backwardURL as! CFURL)
}
A Minor Mea Culpa…
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
// open AVAudioFile for URL input
let sourceAudioFile = try AVAudioFile(forReading: sourceURL as URL,
commonFormat: .pcmFormatInt16,
interleaved: true)
// open AudioFile for output
var forwardAudioFile = try AVAudioFile(forWriting: forwardURL as URL,
settings: [:],
commonFormat: .pcmFormatInt16,
interleaved: true)
// convert to a flat file
let outputBufferSize: size_t = 0x8000 // 32 KB buffer
let framesPerBuffer: UInt32 = UInt32(outputBufferSize) / 4
let transferBuffer =
AVAudioPCMBuffer(pcmFormat: sourceAudioFile.processingFormat,
frameCapacity: framesPerBuffer)
var totalFrameCount: Int64 = 0
while(true) {
do {
try sourceAudioFile.read(into: transferBuffer)
} catch {
// FIXME: discern between empty read and genuine error.
// this could happen if last read(from:) exactly filled the
// buffer, and this one gets 0 frames.
// empty read should break, real error should throw
throw error
}
// this never happens, actually. earlier try fails instead
guard transferBuffer.frameLength > 0 else {
print ("done reading file")
break
}
// increment frame count
totalFrameCount += Int64(transferBuffer.frameLength)
// write to forwardFile
try forwardAudioFile.write(from: transferBuffer)
if transferBuffer.frameLength < transferBuffer.frameCapacity {
// didn't fill buffer; we're done
break
}
}
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
C APIs on iOS/macOS
• Core Foundation
• Core Audio
• Core Media
• Video Toolbox
• Keychain
• IOKit
• OpenGL
• SQLite
• Accelerate
• OpenCV
• BSD, Mach
• etc…
Going deeper…
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Audio Units
• Discrete software objects for working with audio
• Generators, I/O, Filters/Effects, Mixers,
Converters
• Typically combined in a “graph” model
• Used by Garage Band, Logic, etc.
Demo
R(t) = C(t) x M(t)
https://github.com/invalidstream/ring-modulator-v3audiounit
Ring Modulator
• Multiplication of two signals
• One is usually a long-period sine wave
• Originally implemented as a ring-shaped circuit
0 0.8 1.6 2.4 3.2 4 4.8 5.6 6.4 7.2
-2.4
-1.6
-0.8
0.8
1.6
2.4
0 0.8 1.6 2.4 3.2 4 4.8 5.6 6.4 7.2
-2.4
-1.6
-0.8
0.8
1.6
2.4
0 0.8 1.6 2.4 3.2 4 4.8 5.6 6.4 7.2
-2.4
-1.6
-0.8
0.8
1.6
2.4
0 0.8 1.6 2.4 3.2 4 4.8 5.6 6.4 7.2
-2.4
-1.6
-0.8
0.8
1.6
2.4
R(t) = C(t) x M(t)
Modulate! Modulate!
• Ring modulator best known
as the “Dalek” voice effect
on Doctor Who (circa
1963)
• Also used in early
electronic music
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Wait, what?
-(instancetype)initWithComponentDescription:(AudioComponentDescription)componentDescription

options:(AudioComponentInstantiationOptions)options

error:(NSError **)outError {
self = [super initWithComponentDescription:componentDescription

options:options
error:outError];
if (self == nil) {
return nil;
}
// ...
return self;
}
MyAudioUnit.m
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
XPC
(macOS only)
Swift 4Swift 5
https://github.com/apple/swift/blob/master/docs/
ABIStabilityManifesto.md
“First, ABI stability is the center focus of Swift 5 — and
we will pivot much of our prioritization of efforts for
Swift 5 around it. With Swift 4, ABI stability was a
strong goal. In Swift 5, it is a *requirement* of the
release. Whatever ABI we have at the end of Swift 5 is
the ABI that we will have. ABI stability is an important
inflection point for the maturity of the language, and it
cannot be delayed any longer.”
—Ted Kremenek, Aug 8, 2017

“Swift 5: start your engines”
https://lists.swift.org/pipermail/swift-evolution/Week-of-
Mon-20170807/038645.html
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
// Block which subclassers must provide to implement rendering.
- (AUInternalRenderBlock)internalRenderBlock {
// Capture in locals to avoid Obj-C member lookups.
// If "self" is captured in render, we're doing it wrong. See sample code.
return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *timestamp,
AVAudioFrameCount frameCount,
NSInteger outputBusNumber,
AudioBufferList *outputData,
const AURenderEvent *realtimeEventListHead,
AURenderPullInputBlock pullInputBlock) {
// Do event handling and signal processing here.
return noErr;
};
}
Don’t do this
• An audio unit’s render block is called on a
realtime thread
• Therefore it cannot perform any action that could
block:
• I/O (file or network)
• Waiting on a mutex or semaphore
Also, don’t do this
• Call objc_msg_send()
• Capture any Objective-C or Swift object
• Allocate memory
Basically, if you touch anything in the block other than a pre-
allocated C struct or numeric type (or a pointer to those types),
you are asking for trouble.
// Block which subclassers must provide to implement rendering.
- (AUInternalRenderBlock)internalRenderBlock {
// Capture in locals to avoid Obj-C member lookups.
// If "self" is captured in render, we're doing it wrong. See sample code.
AUValue *frequencyCapture = &frequency;
AudioStreamBasicDescription *asbdCapture = &asbd;
__block UInt64 *totalFramesCapture = &totalFrames;
AudioBufferList *renderABLCapture = &renderABL;
return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *timestamp,
AVAudioFrameCount frameCount,
NSInteger outputBusNumber,
AudioBufferList *outputData,
const AURenderEvent *realtimeEventListHead,
AURenderPullInputBlock pullInputBlock) {
// Do event handling and signal processing here.
// BLOCK IMPLEMENTATION ON NEXT SLIDE
return noErr;
};
❌
// pull in samples to filter
pullInputBlock(actionFlags, timestamp, frameCount, 0, renderABLCapture);
// copy samples from ABL, apply filter, write to outputData
size_t sampleSize = sizeof(Float32);
for (int frame = 0; frame < frameCount; frame++) {
*totalFramesCapture += 1;
for (int renderBuf = 0; renderBuf < renderABLCapture->mNumberBuffers; renderBuf++) {
Float32 *sample = renderABLCapture->mBuffers[renderBuf].mData +
(frame * asbdCapture->mBytesPerFrame);
// apply modulation
Float32 time = totalFrames / asbdCapture->mSampleRate;
*sample = *sample * sinf(M_PI * 2 * time * *frequencyCapture);
memcpy(outputData->mBuffers[renderBuf].mData +
(frame * asbdCapture->mBytesPerFrame),
sample,
sampleSize);
}
}
return noErr;
Obj-C Instance Variables!
❌
AUValue *frequencyCapture = &frequency;
AudioStreamBasicDescription *asbdCapture = &asbd;
__block UInt64 *totalFramesCapture = &totalFrames;
AudioBufferList *renderABLCapture = &renderABL;
https://github.com/apple/swift/blob/master/docs/
OwnershipManifesto.md
Certain kinds of low-level programming
require stricter performance guarantees.
Often these guarantees are less about
absolute performance than predictable
performance. For example, keeping up with
an audio stream is not a taxing job for a
modern processor, even with significant per-
sample overheads, but any sort of
unexpected hiccup is immediately noticeable
by users.
—“Swift Ownership Manifesto”,

February 2017
We believe that these problems can be addressed with an opt-in
set of features that we collectively call ownership. […]
Swift already has an ownership system, but it's “under the
covers”: it's an implementation detail that programmers have
little ability to influence. What we are proposing here is easy to
summarize:
• We should add a core rule to the ownership system,
called the Law of Exclusivity […]
• We should add features to give programmers more
control over the ownership system […]
• We should add features to allow programmers to express
types with unique ownership […]
And yet…
“[Swift] is the first industrial-quality systems
programming language that is as expressive and
enjoyable as a scripting language.”
https://developer.apple.com/library/content/documentation/
Swift/Conceptual/Swift_Programming_Language/
Well, now that you mention it…
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
“systems programming language” removed
So… when?
???Big Bang Heat Death of
the Universe
Waiting…
• ABI stability — Will be in Swift 5 (2018)
• Ownership — unclear
• Are these traits sufficient?
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Media Frameworks Versus Swift (Swift by Northwest, October 2017)
Strategies
• Use AV Foundation if you can
• It does most of what anyone needs at this point
• Learn to balance C and Swift
• “Render undo C-sar what is C-sar’s…”
• The goal is to have idiomatic Swift, not Swift
that may work but looks like C
Media Frameworks
versus Swift
Chris Adamson • @invalidname
Swift by Northwest, October 2017
Slides available at slideshare.net/invalidname
Code available at github.com/invalidstream

More Related Content

PDF
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
PDF
Forward Swift 2017: Media Frameworks and Swift: This Is Fine
PDF
Web Audio API + AngularJS
PPTX
How Functions Work
PDF
Trading with opensource tools, two years later
PDF
Introduction to kotlin coroutines
PDF
When symfony met promises
PDF
PHP Enums - PHPCon Japan 2021
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
Forward Swift 2017: Media Frameworks and Swift: This Is Fine
Web Audio API + AngularJS
How Functions Work
Trading with opensource tools, two years later
Introduction to kotlin coroutines
When symfony met promises
PHP Enums - PHPCon Japan 2021

What's hot (20)

PDF
What's new in PHP 8.0?
PDF
Java Keeps Throttling Up!
PDF
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
PDF
HTTP APIs as first class procedures in your language: cutting out SDK complex...
PDF
QConSP 2015 - Dicas de Performance para Aplicações Web
PPTX
Running Ruby on Solaris (RubyKaigi 2015, 12/Dec/2015)
PDF
Spl in the wild
PDF
PHP Performance Trivia
KEY
Morpheus configuration engine (slides from Saint Perl-2 conference)
PPT
Php my sql - functions - arrays - tutorial - programmerblog.net
PDF
PHP 8: What's New and Changed
PPTX
Z ray plugins for dummies
PDF
Opaque Pointers Are Coming
PDF
Building Custom PHP Extensions
PDF
Cli the other sapi pbc11
TXT
Adb instructions
PDF
Create your own PHP extension, step by step - phpDay 2012 Verona
PPT
Introduction to web and php mysql
PDF
Deep Dive Java 17 Devoxx UK
What's new in PHP 8.0?
Java Keeps Throttling Up!
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
HTTP APIs as first class procedures in your language: cutting out SDK complex...
QConSP 2015 - Dicas de Performance para Aplicações Web
Running Ruby on Solaris (RubyKaigi 2015, 12/Dec/2015)
Spl in the wild
PHP Performance Trivia
Morpheus configuration engine (slides from Saint Perl-2 conference)
Php my sql - functions - arrays - tutorial - programmerblog.net
PHP 8: What's New and Changed
Z ray plugins for dummies
Opaque Pointers Are Coming
Building Custom PHP Extensions
Cli the other sapi pbc11
Adb instructions
Create your own PHP extension, step by step - phpDay 2012 Verona
Introduction to web and php mysql
Deep Dive Java 17 Devoxx UK
Ad

Similar to Media Frameworks Versus Swift (Swift by Northwest, October 2017) (20)

PDF
Voice That Matter 2010 - Core Audio
PDF
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
PDF
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
KEY
Core Audio in iOS 6 (CocoaConf Portland, Oct. '12)
PDF
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
PDF
Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]
PDF
Core Audio in iOS 6 (CocoaConf San Jose, April 2013)
PDF
Advanced AV Foundation (CocoaConf, Aug '11)
PDF
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
PDF
Introduction to AV Foundation
PDF
Building Modern Audio Apps with AVAudioEngine
PDF
Stupid Video Tricks
PDF
KKBOX WWDC17 Airplay 2 - Dolphin
PDF
Movi presentation Singapore video tech meetup
PDF
Mastering Media with AV Foundation
PDF
Stupid Video Tricks, CocoaConf Seattle 2014
PDF
Utilizing AVFoundation at dubsmash
PDF
Composing and Editing Media with AV Foundation
PDF
iOS Media APIs (MobiDevDay Detroit, May 2013)
PDF
Stupid Video Tricks, CocoaConf Las Vegas
Voice That Matter 2010 - Core Audio
Video Killed the Rolex Star (CocoaConf Columbus, July 2015)
Video Killed the Rolex Star (CocoaConf San Jose, November, 2015)
Core Audio in iOS 6 (CocoaConf Portland, Oct. '12)
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
Core Audio: Don't Be Afraid to Play it LOUD! [360iDev, San Jose 2010]
Core Audio in iOS 6 (CocoaConf San Jose, April 2013)
Advanced AV Foundation (CocoaConf, Aug '11)
Glitch-Free A/V Encoding (CocoaConf Boston, October 2013)
Introduction to AV Foundation
Building Modern Audio Apps with AVAudioEngine
Stupid Video Tricks
KKBOX WWDC17 Airplay 2 - Dolphin
Movi presentation Singapore video tech meetup
Mastering Media with AV Foundation
Stupid Video Tricks, CocoaConf Seattle 2014
Utilizing AVFoundation at dubsmash
Composing and Editing Media with AV Foundation
iOS Media APIs (MobiDevDay Detroit, May 2013)
Stupid Video Tricks, CocoaConf Las Vegas
Ad

More from Chris Adamson (19)

PDF
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
PDF
Whatever Happened to Visual Novel Anime? (JAFAX 2018)
PDF
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
PDF
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
PDF
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
PDF
Firebase: Totally Not Parse All Over Again (Unless It Is)
PDF
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
PDF
Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...
PDF
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
PDF
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
PDF
Stupid Video Tricks (CocoaConf DC, March 2014)
PDF
Introduction to the Roku SDK
PDF
Get On The Audiobus (CocoaConf Atlanta, November 2013)
PDF
Get On The Audiobus (CocoaConf Boston, October 2013)
PDF
Core Audio in iOS 6 (CocoaConf DC, March 2013)
PDF
Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)
PDF
Core Audio Intro (Detroit Mobile City 2013)
PDF
Objective-C Is Not Java
PDF
Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)
Whatever Happened to Visual Novel Anime? (AWA/Youmacon 2018)
Whatever Happened to Visual Novel Anime? (JAFAX 2018)
Fall Premieres: Media Frameworks in iOS 11, macOS 10.13, and tvOS 11 (CocoaCo...
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Building A Streaming Apple TV App (CocoaConf San Jose, Nov 2016)
Firebase: Totally Not Parse All Over Again (Unless It Is)
Building A Streaming Apple TV App (CocoaConf DC, Sept 2016)
Revenge of the 80s: Cut/Copy/Paste, Undo/Redo, and More Big Hits (CocoaConf C...
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using (CocoaConf Columbus 2014)
Stupid Video Tricks (CocoaConf DC, March 2014)
Introduction to the Roku SDK
Get On The Audiobus (CocoaConf Atlanta, November 2013)
Get On The Audiobus (CocoaConf Boston, October 2013)
Core Audio in iOS 6 (CocoaConf DC, March 2013)
Mobile Movies with HTTP Live Streaming (CocoaConf DC, March 2013)
Core Audio Intro (Detroit Mobile City 2013)
Objective-C Is Not Java
Core Audio in iOS 6 (CocoaConf Raleigh, Dec. '12)

Recently uploaded (20)

PDF
Hindi spoken digit analysis for native and non-native speakers
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
A Presentation on Artificial Intelligence
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Hybrid model detection and classification of lung cancer
PDF
Univ-Connecticut-ChatGPT-Presentaion.pdf
PDF
August Patch Tuesday
PDF
1 - Historical Antecedents, Social Consideration.pdf
PDF
A comparative study of natural language inference in Swahili using monolingua...
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PPTX
cloud_computing_Infrastucture_as_cloud_p
PDF
From MVP to Full-Scale Product A Startup’s Software Journey.pdf
PDF
DP Operators-handbook-extract for the Mautical Institute
PDF
Microsoft Solutions Partner Drive Digital Transformation with D365.pdf
PDF
Zenith AI: Advanced Artificial Intelligence
PDF
Accuracy of neural networks in brain wave diagnosis of schizophrenia
PDF
DASA ADMISSION 2024_FirstRound_FirstRank_LastRank.pdf
PDF
Heart disease approach using modified random forest and particle swarm optimi...
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Hindi spoken digit analysis for native and non-native speakers
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Encapsulation_ Review paper, used for researhc scholars
A Presentation on Artificial Intelligence
Building Integrated photovoltaic BIPV_UPV.pdf
Hybrid model detection and classification of lung cancer
Univ-Connecticut-ChatGPT-Presentaion.pdf
August Patch Tuesday
1 - Historical Antecedents, Social Consideration.pdf
A comparative study of natural language inference in Swahili using monolingua...
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
cloud_computing_Infrastucture_as_cloud_p
From MVP to Full-Scale Product A Startup’s Software Journey.pdf
DP Operators-handbook-extract for the Mautical Institute
Microsoft Solutions Partner Drive Digital Transformation with D365.pdf
Zenith AI: Advanced Artificial Intelligence
Accuracy of neural networks in brain wave diagnosis of schizophrenia
DASA ADMISSION 2024_FirstRound_FirstRank_LastRank.pdf
Heart disease approach using modified random forest and particle swarm optimi...
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf

Media Frameworks Versus Swift (Swift by Northwest, October 2017)

  • 1. Media Frameworks Versus Swift Chris Adamson • @invalidname Swift by Northwest, October 2017 Slides available at slideshare.net/invalidname Code available at github.com/invalidstream
  • 2. Who the what, now? @invalidname
  • 16. import Cocoa import AVFoundation import CoreMediaIO if let devices = AVCaptureDevice.devices(), let avDevices = devices.filter( {$0 is AVCaptureDevice}) as? [AVCaptureDevice] { for device in avDevices { print("(device.description)") } }
  • 17. [Loopback Simulator][com.rogueamoeba.Loopback:E8577B20-0806-4472-A5E6-426CABCD6C8E] [Loopback Line-In][com.rogueamoeba.Loopback:A00F38FD-C2B6-43FD-98B7-23BAA6FACB03] [iMic USB audio system][AppleUSBAudioEngine:Griffin Technology, Inc:iMic USB audio system:220000:2,1] [Loopback Keynote][com.rogueamoeba.Loopback:1936D2A3-6D0B-428E-899E-0ABE46628EA4] [Soundflower (64ch)][SoundflowerEngine:1] [HD Pro Webcam C920][AppleUSBAudioEngine:Unknown Manufacturer:HD Pro Webcam C920:1218B05F:3] [Soundflower (2ch)][SoundflowerEngine:0] [iGlasses][iGlasses] [HD Pro Webcam C920][0x244000046d082d] Program ended with exit code: 0
  • 19. CMIOObjectPropertyAddress prop = { kCMIOHardwarePropertyAllowScreenCaptureDevices, kCMIOObjectPropertyScopeGlobal, kCMIOObjectPropertyElementMaster }; UInt32 allow = 1; CMIOObjectSetPropertyData( kCMIOObjectSystemObject, &prop, 0, NULL, sizeof(allow), &allow );
  • 20. var prop = CMIOObjectPropertyAddress( mSelector: CMIOObjectPropertySelector( kCMIOHardwarePropertyAllowScreenCaptureDevices), mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal), mElement: CMIOObjectPropertyElement( kCMIOObjectPropertyElementMaster)) var allow : UInt32 = 1 CMIOObjectSetPropertyData(CMIOObjectID(kCMIOObjectSystemObject), &prop, 0, nil, UInt32(MemoryLayout<UInt32>.size), &allow)
  • 21. CMIOObjectPropertyAddress prop = { kCMIOHardwarePropertyAllowScreenCaptureDevices, kCMIOObjectPropertyScopeGlobal, kCMIOObjectPropertyElementMaster }; UInt32 allow = 1; CMIOObjectSetPropertyData( kCMIOObjectSystemObject, &prop, 0, NULL, sizeof(allow), &allow ); var prop = CMIOObjectPropertyAddress( mSelector: CMIOObjectPropertySelector( kCMIOHardwarePropertyAllowScreenCaptureDevices), mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal), mElement: CMIOObjectPropertyElement( kCMIOObjectPropertyElementMaster)) var allow : UInt32 = 1 CMIOObjectSetPropertyData(CMIOObjectID(kCMIOObjectSystemObject), &prop, 0, nil, UInt32(MemoryLayout<UInt32>.size), &allow) This is fine
  • 23. var prop = CMIOObjectPropertyAddress( mSelector: CMIOObjectPropertySelector( kCMIOHardwarePropertyAllowScreenCaptureDevices), mScope: CMIOObjectPropertyScope( kCMIOObjectPropertyScopeGlobal), mElement: CMIOObjectPropertyElement( kCMIOObjectPropertyElementMaster)) var prop = CMIOObjectPropertyAddress( mSelector: CMIOObjectPropertySelector( kCMIOHardwarePropertyAllowScreenCaptureDevices), mScope: CMIOObjectPropertyScope( kCMIOObjectPropertyScopeGlobal), mElement: CMIOObjectPropertyElement( kCMIOObjectPropertyElementMaster))
  • 26. public typealias CMIOObjectPropertySelector = UInt32 public typealias CMIOObjectPropertyScope = UInt32 public typealias CMIOObjectPropertyElement = UInt32 public struct CMIOObjectPropertyAddress { public var mSelector: CMIOObjectPropertySelector public var mScope: CMIOObjectPropertyScope public var mElement: CMIOObjectPropertyElement public init() public init(mSelector: CMIOObjectPropertySelector, mScope: CMIOObjectPropertyScope, mElement: CMIOObjectPropertyElement) }
  • 27. extension CMIOObjectPropertySelector { static let allowScreenCaptureDevices = CMIOObjectPropertySelector( kCMIOHardwarePropertyAllowScreenCaptureDevices) } extension CMIOObjectPropertyScope { static let global = CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal) } extension CMIOObjectPropertyElement { static let master = CMIOObjectPropertyElement(kCMIOObjectPropertyElementMaster) }
  • 28. var prop = CMIOObjectPropertyAddress( mSelector: .allowScreenCaptureDevices, mScope: .global, mElement: .master) var prop = CMIOObjectPropertyAddress( mSelector: CMIOObjectPropertySelector( kCMIOHardwarePropertyAllowScreenCaptureDevices), mScope: CMIOObjectPropertyScope( kCMIOObjectPropertyScopeGlobal), mElement: CMIOObjectPropertyElement( kCMIOObjectPropertyElementMaster))
  • 31. Reversing Audio 1. Decode the MP3/AAC to LPCM2. Grab a buffer from the end3. Reverse its samples in memory4. Write it to the front of a new file5. Repeat until fully baked
  • 32. API Needs • Convert from MP3/AAC to LPCM • Write sequentially to audio file (.caf, .aif, .wav) • Random-access read from audio file
  • 33. Plan A (Swift) • AV Foundation • AVAssetReader/Writer can do format conversion while reading/writing audio files • Can’t (easily) read from arbitrary packet offsets; meant to process everything forward
  • 34. Plan B (C, Swift?) • Audio Toolbox (part of Core Audio) • ExtAudioFile can do format conversions while reading/writing audio files • AudioFile can read from arbitrary packet offset
  • 36. // declare LPCM format we are converting to AudioStreamBasicDescription format = {0}; format.mSampleRate = 44100.0; format.mFormatID = kAudioFormatLinearPCM; format.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; format.mBitsPerChannel = 16; format.mChannelsPerFrame = 2; format.mBytesPerFrame = 4; format.mFramesPerPacket = 1; format.mBytesPerPacket = 4;
  • 37. // declare LPCM format we are converting to var format = AudioStreamBasicDescription( mSampleRate: 44100.0, mFormatID: kAudioFormatLinearPCM, mFormatFlags: kAudioFormatFlagIsPacked + kAudioFormatFlagIsSignedInteger, mBytesPerPacket: 4, mFramesPerPacket: 1, mBytesPerFrame: 4, mChannelsPerFrame: 2, mBitsPerChannel: 16, mReserved: 0)
  • 38. // open AudioFile for output AudioFileID forwardAudioFile; err = AudioFileCreateWithURL(forwardURL, kAudioFileCAFType, &format, kAudioFileFlags_EraseFile, &forwardAudioFile); IF_ERR_RETURN #define IF_ERR_RETURN if (err != noErr) { return err; }
  • 39. // open AudioFile for output var forwardAudioFile: AudioFileID? err = AudioFileCreateWithURL(forwardURL, kAudioFileCAFType, &format, AudioFileFlags.eraseFile, &forwardAudioFile) if err != noErr { return err }
  • 41. // open AudioFile for output var forwardAudioFile: AudioFileID? err = AudioFileCreateWithURL(forwardURL, kAudioFileCAFType, &format, AudioFileFlags.eraseFile, &forwardAudioFile) if err != noErr { return err }
  • 42. // open AudioFile for output var forwardAudioFile: AudioFileID? err = AudioFileCreateWithURL(forwardURL, kAudioFileCAFType, &format, AudioFileFlags.eraseFile, &forwardAudioFile) if err != noErr { return err } 1. Uses a free function, rather than a method on AudioFile
  • 43. // open AudioFile for output var forwardAudioFile: AudioFileID? err = AudioFileCreateWithURL(forwardURL, kAudioFileCAFType, &format, AudioFileFlags.eraseFile, &forwardAudioFile) if err != noErr { return err } 2. Errors are communicated via the return value, rather than throws
  • 44. // open AudioFile for output var forwardAudioFile: AudioFileID? err = AudioFileCreateWithURL(forwardURL, kAudioFileCAFType, &format, AudioFileFlags.eraseFile, &forwardAudioFile) if err != noErr { return err } 3. Some parameters are UInt32 constants, some are enums
  • 45. // open AudioFile for output var forwardAudioFile: AudioFileID? err = AudioFileCreateWithURL(forwardURL, kAudioFileCAFType, &format, AudioFileFlags.eraseFile, &forwardAudioFile) if err != noErr { return err } 4. Audio format is passed as an UnsafePointer<AudioStreamBasicDescription>
  • 46. // open AudioFile for output var forwardAudioFile: AudioFileID? err = AudioFileCreateWithURL(forwardURL, kAudioFileCAFType, &format, AudioFileFlags.eraseFile, &forwardAudioFile) if err != noErr { return err } 5. Created object is returned via an in-out parameter
  • 47. To say nothing of…
  • 48. Pointer arithmetic! // swap packets inside transfer buffer for i in 0..<packetsToTransfer/2 { let swapSrc = transferBuffer.advanced(by: Int(i) * Int(format.mBytesPerPacket)) let swapDst = transferBuffer.advanced(by: transferBufferSize - (Int(i+1) * Int(format.mBytesPerPacket))) memcpy(swapBuffer, swapSrc, Int(format.mBytesPerPacket)) memcpy(swapSrc, swapDst, Int(format.mBytesPerPacket)) memcpy(swapDst, swapBuffer, Int(format.mBytesPerPacket)) }
  • 51. extension AudioFileID { init? (url: URL, fileType: UInt32, format: AudioStreamBasicDescription, flags: AudioFileFlags) { var fileId : AudioFileID? var format = format let err = AudioFileCreateWithURL(url as CFURL, fileType, &format, flags, &fileId) guard err != noErr, let createdFile = fileId else { return nil } self = createdFile } }
  • 52. Been there, done that • The Amazing Audio Engine 💀 • Novocaine (💀?) • EZAudio 💀 • AudioKit • Superpowered • etc…
  • 54. /** Convert a source audio file (using any Core Audio-supported codec) and create LPCM .caf files for its forward and backward versions. - parameter sourceURL: A file URL containing the source audio to be read from - parameter forwardURL: A file URL with the destination to write the decompressed (LPCM) forward file - parameter backwardURL: A file URL with the destination to write the backward file */ OSStatus convertAndReverse(CFURLRef sourceURL, CFURLRef forwardURL, CFURLRef backwardURL); AudioReversingC.h // // Use this file to import your target's public headers that you would like to expose to Swift. // #import <CoreFoundation/CoreFoundation.h> #import <AudioToolbox/AudioToolbox.h> OSStatus convertAndReverse(CFURLRef sourceURL, CFURLRef forwardURL, CFURLRef backwardURL); AudioReverser-Bridging-Header.h
  • 55. if USE_SWIFT_CONVERTER { err = convertAndReverseSwift(sourceURL: source as CFURL, forwardURL: self.forwardURL as! CFURL, backwardURL: self.backwardURL as! CFURL) } else { err = convertAndReverse(source as! CFURL, self.forwardURL as! CFURL, self.backwardURL as! CFURL) }
  • 56. A Minor Mea Culpa…
  • 58. // open AVAudioFile for URL input let sourceAudioFile = try AVAudioFile(forReading: sourceURL as URL, commonFormat: .pcmFormatInt16, interleaved: true) // open AudioFile for output var forwardAudioFile = try AVAudioFile(forWriting: forwardURL as URL, settings: [:], commonFormat: .pcmFormatInt16, interleaved: true) // convert to a flat file let outputBufferSize: size_t = 0x8000 // 32 KB buffer let framesPerBuffer: UInt32 = UInt32(outputBufferSize) / 4 let transferBuffer = AVAudioPCMBuffer(pcmFormat: sourceAudioFile.processingFormat, frameCapacity: framesPerBuffer) var totalFrameCount: Int64 = 0
  • 59. while(true) { do { try sourceAudioFile.read(into: transferBuffer) } catch { // FIXME: discern between empty read and genuine error. // this could happen if last read(from:) exactly filled the // buffer, and this one gets 0 frames. // empty read should break, real error should throw throw error } // this never happens, actually. earlier try fails instead guard transferBuffer.frameLength > 0 else { print ("done reading file") break } // increment frame count totalFrameCount += Int64(transferBuffer.frameLength) // write to forwardFile try forwardAudioFile.write(from: transferBuffer) if transferBuffer.frameLength < transferBuffer.frameCapacity { // didn't fill buffer; we're done break } }
  • 61. C APIs on iOS/macOS • Core Foundation • Core Audio • Core Media • Video Toolbox • Keychain • IOKit • OpenGL • SQLite • Accelerate • OpenCV • BSD, Mach • etc…
  • 64. Audio Units • Discrete software objects for working with audio • Generators, I/O, Filters/Effects, Mixers, Converters • Typically combined in a “graph” model • Used by Garage Band, Logic, etc.
  • 65. Demo R(t) = C(t) x M(t) https://github.com/invalidstream/ring-modulator-v3audiounit
  • 66. Ring Modulator • Multiplication of two signals • One is usually a long-period sine wave • Originally implemented as a ring-shaped circuit
  • 67. 0 0.8 1.6 2.4 3.2 4 4.8 5.6 6.4 7.2 -2.4 -1.6 -0.8 0.8 1.6 2.4 0 0.8 1.6 2.4 3.2 4 4.8 5.6 6.4 7.2 -2.4 -1.6 -0.8 0.8 1.6 2.4 0 0.8 1.6 2.4 3.2 4 4.8 5.6 6.4 7.2 -2.4 -1.6 -0.8 0.8 1.6 2.4 0 0.8 1.6 2.4 3.2 4 4.8 5.6 6.4 7.2 -2.4 -1.6 -0.8 0.8 1.6 2.4 R(t) = C(t) x M(t)
  • 68. Modulate! Modulate! • Ring modulator best known as the “Dalek” voice effect on Doctor Who (circa 1963) • Also used in early electronic music
  • 72. -(instancetype)initWithComponentDescription:(AudioComponentDescription)componentDescription
 options:(AudioComponentInstantiationOptions)options
 error:(NSError **)outError { self = [super initWithComponentDescription:componentDescription
 options:options error:outError]; if (self == nil) { return nil; } // ... return self; } MyAudioUnit.m
  • 77. “First, ABI stability is the center focus of Swift 5 — and we will pivot much of our prioritization of efforts for Swift 5 around it. With Swift 4, ABI stability was a strong goal. In Swift 5, it is a *requirement* of the release. Whatever ABI we have at the end of Swift 5 is the ABI that we will have. ABI stability is an important inflection point for the maturity of the language, and it cannot be delayed any longer.” —Ted Kremenek, Aug 8, 2017
 “Swift 5: start your engines” https://lists.swift.org/pipermail/swift-evolution/Week-of- Mon-20170807/038645.html
  • 81. // Block which subclassers must provide to implement rendering. - (AUInternalRenderBlock)internalRenderBlock { // Capture in locals to avoid Obj-C member lookups. // If "self" is captured in render, we're doing it wrong. See sample code. return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp *timestamp, AVAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList *outputData, const AURenderEvent *realtimeEventListHead, AURenderPullInputBlock pullInputBlock) { // Do event handling and signal processing here. return noErr; }; }
  • 82. Don’t do this • An audio unit’s render block is called on a realtime thread • Therefore it cannot perform any action that could block: • I/O (file or network) • Waiting on a mutex or semaphore
  • 83. Also, don’t do this • Call objc_msg_send() • Capture any Objective-C or Swift object • Allocate memory Basically, if you touch anything in the block other than a pre- allocated C struct or numeric type (or a pointer to those types), you are asking for trouble.
  • 84. // Block which subclassers must provide to implement rendering. - (AUInternalRenderBlock)internalRenderBlock { // Capture in locals to avoid Obj-C member lookups. // If "self" is captured in render, we're doing it wrong. See sample code. AUValue *frequencyCapture = &frequency; AudioStreamBasicDescription *asbdCapture = &asbd; __block UInt64 *totalFramesCapture = &totalFrames; AudioBufferList *renderABLCapture = &renderABL; return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp *timestamp, AVAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList *outputData, const AURenderEvent *realtimeEventListHead, AURenderPullInputBlock pullInputBlock) { // Do event handling and signal processing here. // BLOCK IMPLEMENTATION ON NEXT SLIDE return noErr; }; ❌
  • 85. // pull in samples to filter pullInputBlock(actionFlags, timestamp, frameCount, 0, renderABLCapture); // copy samples from ABL, apply filter, write to outputData size_t sampleSize = sizeof(Float32); for (int frame = 0; frame < frameCount; frame++) { *totalFramesCapture += 1; for (int renderBuf = 0; renderBuf < renderABLCapture->mNumberBuffers; renderBuf++) { Float32 *sample = renderABLCapture->mBuffers[renderBuf].mData + (frame * asbdCapture->mBytesPerFrame); // apply modulation Float32 time = totalFrames / asbdCapture->mSampleRate; *sample = *sample * sinf(M_PI * 2 * time * *frequencyCapture); memcpy(outputData->mBuffers[renderBuf].mData + (frame * asbdCapture->mBytesPerFrame), sample, sampleSize); } } return noErr;
  • 86. Obj-C Instance Variables! ❌ AUValue *frequencyCapture = &frequency; AudioStreamBasicDescription *asbdCapture = &asbd; __block UInt64 *totalFramesCapture = &totalFrames; AudioBufferList *renderABLCapture = &renderABL;
  • 88. Certain kinds of low-level programming require stricter performance guarantees. Often these guarantees are less about absolute performance than predictable performance. For example, keeping up with an audio stream is not a taxing job for a modern processor, even with significant per- sample overheads, but any sort of unexpected hiccup is immediately noticeable by users. —“Swift Ownership Manifesto”,
 February 2017
  • 89. We believe that these problems can be addressed with an opt-in set of features that we collectively call ownership. […] Swift already has an ownership system, but it's “under the covers”: it's an implementation detail that programmers have little ability to influence. What we are proposing here is easy to summarize: • We should add a core rule to the ownership system, called the Law of Exclusivity […] • We should add features to give programmers more control over the ownership system […] • We should add features to allow programmers to express types with unique ownership […]
  • 91. “[Swift] is the first industrial-quality systems programming language that is as expressive and enjoyable as a scripting language.” https://developer.apple.com/library/content/documentation/ Swift/Conceptual/Swift_Programming_Language/ Well, now that you mention it…
  • 95. ???Big Bang Heat Death of the Universe
  • 96. Waiting… • ABI stability — Will be in Swift 5 (2018) • Ownership — unclear • Are these traits sufficient?
  • 99. Strategies • Use AV Foundation if you can • It does most of what anyone needs at this point • Learn to balance C and Swift • “Render undo C-sar what is C-sar’s…” • The goal is to have idiomatic Swift, not Swift that may work but looks like C
  • 100. Media Frameworks versus Swift Chris Adamson • @invalidname Swift by Northwest, October 2017 Slides available at slideshare.net/invalidname Code available at github.com/invalidstream