Slide 1

Slide 1 text

MacͱΦʔσΟΦ࠶ੜ 2024/11/02 Yusuke Ito Symposium #10 macOS

Slide 2

Slide 2 text

About me • Yusuke Ito • Web (TypeScript, React, Node.js) macOS ωΠςΟϒΞϓϦ (Minutes) iOS ωΠςΟϒΞϓϦ (App Store Best of 2014) • ϝΠϯ͸Web͕ͩɺΦʔσΟΦػثͷ։ൃɾ ੡଄+macOSΞϓϦ https://audio.current.directory/

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Ի੠σʔλͷجຊ

Slide 5

Slide 5 text

PCM (LPCM) Linear Pulse Code Modulation Ϗοτਂ౓=ৼ෯ (16, 24bit) αϯϓϦϯάप೾਺(1ඵ͋ͨΓͷ෼ׂ਺) 44.1kHz(CD) 48kHz, 96kHz (Ի੍ָ࡞) Ի੠ͷੜσʔλ 0 -32768 32767

Slide 6

Slide 6 text

LPCMͷσʔλྔ • 44.1kHz (CD) αϯϓϦϯάप೾਺ • 16bit (CD) Ϗοτਂ౓ • 3෼ (180ඵ) • 44100 (sample/sec) × 2 (byte, 16bit) × 2 (channel, L R) × 180 (secs) • = 31,752,000 (bytes) ≈ 30 MB (megabytes)

Slide 7

Slide 7 text

ίϯςφͱίʔσοΫ ίϯςφ: QuickTime (.mov) MPEG-4 (.m4a, .m4v) Ի੠ AAC ө૾ H.264 ϝλσʔλ ΞʔςΟετ໊… ΞϧόϜ໊… ϝλσʔλ ίʔσοΫ: H.264 AAC Apple Lossless MP3, ATRAC (MD) JPEG PNG ϑΝΠϧͷ಺෦

Slide 8

Slide 8 text

ίʔσοΫͱσίʔυ Ի੠ AAC σίʔυ LPCM Ի੠ੜσʔλ Τϯίʔυ ίʔσοΫ

Slide 9

Slide 9 text

Mixing & Playback Ի੠σʔλͷྲྀΕ Χʔωϧ ϛΩαʔ σόΠε 🔊 App A App B γεςϜܯࠂԻ App C ഉଞϞʔυ OR (αϯϓϦϯάप೾਺ม׵) 96kHz 48kHz 44.1kHz 96kHz LPCM LPCM

Slide 10

Slide 10 text

ΦʔσΟΦAPIΛ࢖͏

Slide 11

Slide 11 text

Audio APIs on Mac • AVFoundation: ؆୯ʹ࢖͑ΔΫϥε܈ • AVAudioPlayerͳͲ, ϑΝΠϧ(M4A…)Ϩϕϧͷ΍ΓऔΓ • Audio Toolbox: • σίʔυɾΤϯίʔυɾαϯϓϦϯάप೾਺ม׵(SRC)ͳͲ • Ի੠σʔλ(PCM)Ϩϕϧͷ΍ΓऔΓ • Core Audio: ϋʔυ΢ΣΞͱͷ௨৴ • ϋʔυ΢ΣΞΛ௚઀ར༻͢Δ৔߹ Core Audio Hardware Audio Toolbox AVFoundation

Slide 12

Slide 12 text

Core Audio Λ࢖͏ ࠷௿ϨΠϠʔͷAPI • ΦʔσΟΦσόΠεʹग़ྗ͞ΕΔσʔλΛࣗ෼ͰίϯτϩʔϧͰ͖Δ • ඞཁͳλΠϛϯάͰίʔϧόοΫ͕ݺ͹ΕΔͷͰԻ੠σʔλ(PCMੜσʔλ)Λ ౉͢ • σʔλͷ४උ͕࠶ੜʹؒʹ߹͏Α͏ʹ͢Δ • ΠϯλʔϑΣʔε͸Cݴޠ • Audio Toolbox, Core Audio

Slide 13

Slide 13 text

ԻָPlayerΛ࡞Δ σʔλͷྲྀΕ αϯϓϦϯά प೾਺ม׵ (SRC) σίʔμʔ ϑΝΠϧ .m4a Apple Lossless όοϑΝ Core Audio 🔊 औΓʹ͍͘ Audio Toolbox Core Audio LPCM LPCM LPCM Apple Lossless App C ഉଞϞʔυ

Slide 14

Slide 14 text

ഉଞϞʔυ • API: Hog Mode • ΧʔωϧϛΩαʔΛհ͞ͳ͍ • →αϯϓϦϯάप೾਺ม׵ͳͲ͕ߦΘΕͳ͍ • →ଞͷΞϓϦέʔγϣϯͷԻ͸ग़ྗ͞Εͳ͍ • ΦʔσΟΦϋʔυ΢ΣΞʹ௚઀ΞΫηε • Ի੠(LPCM)σʔλΛૹͬͯ࠶ੜ

Slide 15

Slide 15 text

Swift & Concurrency

Slide 16

Slide 16 text

Swift & Realtime • ϦΞϧλΠϜॲཧͷอূ͸͞Ε͍ͯͳ͍(͸ͣ) • Swiftಛ༗ͷॲཧͷΦʔόʔϔου͕͋ΔՄೳੑ • ΫϦςΟΧϧͳͷ͸࠷ޙͷίʔϧόοΫՕॴͷΈ • ࣮ࡍ͸SwiftͰ໰୊ͳ͠ • ໰୊͋ΓͳΒCͰॻ͘ • ͦΕҎ֎ͷՕॴ͸ઈରϦΞϧλΠϜͰͳͯ͘΋໰୊ͳ͠

Slide 17

Slide 17 text

Concurrency • (Իָ࠶ੜPlayerͰ͸) ฒߦॲཧͷඞཁੑ͸ബ͍ • ͨͩ͠… • ಉ͡ΦϒδΣΫτΛڞ༗͢Δ • όοϑΝʔ(ϝϞϦɾϑΝΠϧ), ΦʔσΟΦσόΠε, σίʔμʔ • ಉ࣌ʹΞΫηε͠ͳ͍࢓૊Έ͸ඞཁ

Slide 18

Slide 18 text

ಉظॲཧɾഉଞॲཧ • 1ͭͷϦιʔε(ΦϒδΣΫτ)ʹಉ࣌ʹΞΫηε͠ͳ͍Α͏ʹ͢Δ • ಉ࣌ΞΫηεͷྫ • ಡΈࠐΈதʹॻ͖ࠐΉ • εϨουAͰಡΈࠐΈதʹεϨουBͰ΋ಡΈࠐΉ

Slide 19

Slide 19 text

ഉଞॲཧ • ର৅͕ಉظؔ਺ͷ৔߹ • NIOLock - swift-nio (All Platforms) • OSAllocatedUnfairLock (macOS 13~)

Slide 20

Slide 20 text

struct AudioSytemObject { let id: AudioObjectID = ... let lock = NIOLock() func get(addr: T) throws -> [T.DataType] { try lock.withLock { let memory = ... let statusData = AudioObjectGetPropertyData(id, &propAddr, 0, nil, &propSize, memory) return (0..

Slide 21

Slide 21 text

όοϑΝ (ϝϞϦ) εϨουB εϨουA ॻ͖ࠐΈ ಡΈࠐΈ

Slide 22

Slide 22 text

final class PlaybackBuffer { var buffer = Data() let lock = NIOLock() func write(_ data: Data) { lock.withLockVoid { buffer.append(data) } } func read() -> Data? { return lock.withLock { if buffer.isEmpty { return nil } let buf = buffer buffer.removeAll() // clear data return buf } } }

Slide 23

Slide 23 text

Actor vs Lock • Actorͩͱisolated context (isolation boundary)Λ௒͑ΒΕͳ͍ • ݺͼग़͠ݩ͕CͷίʔϧόοΫ(ಉظ)ͷ৔߹ͳͲ • nonisolated ʹ͢Δ? →ࣗલͷഉଞॲཧ͕ඞཁ • await ͢Δ? →ಉظઐ༻ίʔϧόοΫͳͷͰෆՄ func AudioIOProc(buffer: PlaybackBuffer, outOutputData: UnsafeMutablePointer) -> OSStatus { let data = buffer.read() memcpy(outOutputData, data, length) ...

Slide 24

Slide 24 text

ඇಉظؔ਺ͷഉଞॲཧ • ϑΝΠϧΛόοϑΝʔͱͯ࣋ͭ͠ • ಡΈࠐΈத͸ॻ͖ࠐ·ͳ͍ (ಡΈࠐΈ͕ऴΘΔ·Ͱ଴ͭ) • ॻ͖ࠐΈத͸ಡΈࠐ·ͳ͍ (ॻ͖ࠐΈ͕ऴΘΔ·Ͱ଴ͭ) • ϩοΫ(ͦͷ··)͸࢖͑ͳ͍ (σουϩοΫ͢Δ) actor FileCache { func write(_ data: Data) async func read() async -> Data? }

Slide 25

Slide 25 text

όοϑΝ (ϑΝΠϧ) εϨουB εϨουA ॻ͖ࠐΈ ಡΈࠐΈ

Slide 26

Slide 26 text

actor FileCache { let queue = DispatchQueue(label: "FileCacheQueue", qos: .utility) let semaphore = DispatchSemaphore(value: 1) func write(_ data: Data) async { await withCheckedContinuation { continuation in queue.async { self.semaphore.wait() Task { await self.write_(data) continuation.resume() self.semaphore.signal() } } } } func read() async -> Data? { await withCheckedContinuation { continuation in queue.async { self.semaphore.wait() Task { let data = await self.read_() continuation.resume(returning: data) self.semaphore.signal() } } } } • ඇಉظؔ਺͸ Serial Queue & Semaphore(or Mutex) • Actor͚ͩͰ͸ಉ࣌ಡΈॻ͖Λ๷͛ͳ͍৔߹͕͋Δ • อޢ͍ͨ͠Օॴ͕ඇಉظؔ਺

Slide 27

Slide 27 text

εϨουB εϨουA ϑΝΠϧ ೚ҙεϨου ࣌ؒ ॻ͖ࠐΈத Semaphore 1 0 wait() wait() DispatchQueue signal() Task ೚ҙεϨου 1 ಡΈࠐΈ 0 signal() write() read() 1 Queue Block…

Slide 28

Slide 28 text

ࢀߟจݙ • Core Audio Overview (Apple) https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/ CoreAudioOverview/WhatisCoreAudio/WhatisCoreAudio.html • Xcode 16 & Swift 6 ΩϟονΞοϓ: Swift Concurrencyͷجૅͱ࠷ॏཁϙΠϯτΛ૯෮श (QonceptInc) https://www.youtube.com/watch?v=jJgEtjx8KHY • ʲSwiftʳGrand Central Dispatch (GCD)ͱOperationQueue ·ͱΊ (@shiz) https://qiita.com/shiz/items/693241f41344a9df6d6f • Linuxͷsemaphoreͱmutexͷ࣮૷ (@watatuki) https://www.docswell.com/s/watatuki/Z7VNJG-2023-08-24-014308

Slide 29

Slide 29 text

No content