Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iPhoneのセンサー情報をmacOSアプリでリアルタイム活用するための技術

 iPhoneのセンサー情報をmacOSアプリでリアルタイム活用するための技術

iOSDC Japan 2023 #iosdc

iPhoneのセンサー情報をmacOSアプリでリアルタイム活用するための技術
https://fortee.jp/iosdc-japan-2023/proposal/00d3250d-c32f-4466-b582-ecd9c27f5266

アバターアプリのGitHub:
https://github.com/vcamapp/app

Twitter: http://twitter.com/tanakasan2525/

Tatsuya Tanaka

September 03, 2023
Tweet

More Decks by Tatsuya Tanaka

Other Decks in Programming

Transcript

  1. ©︎ ୅දతͳiPhoneͱMacؒͷ௨৴ํ๏ Core Bluetooth Multipeer Connectivity Network ɾWiFiͳ͠Ͱܨ͛Δ 
 ɾ௿ిྗ

    ɾόοΫάϥ΢ϯυOK ɾѻ͍ʹ͍͘I/F ɾOSver౳ͷ࢓༷ࠩҟ ɾP2P ɾঢ়ଶΛಉظ͠΍͍͢ ɾApple only ɾҰൠతͳ௨৴ํ๏ ɾϞμϯͳI/F
  2. ©︎ Bonjour: θϩίϯϑΟΪϡϨʔγϣϯϓϩτίϧ Illustration by Stable Diffusion NWBrowser( for: .bonjour(type:

    "_awesomeapp._udp", domain: nil), using: parameters ) let listener = try NWListener(using: parameters, on: port) listener.service = .init(type: "_awesomeapp._udp") αʔϏε໊ + ϓϩτίϧ + υϝΠϯ
  3. ©︎ Dataܕ΁ͷม׵ public struct VCamMotion: Equatable { public var version:

    UInt32 public var head: Head public var hands: Hands public var blendShape: BlendShape public struct Head: Equatable { public var translation: SIMD3<Float> public var rotation: simd_quatf } public struct Hands: Equatable { public var right: Hand public var left: Hand } public struct Hand: Equatable { public var wrist: SIMD2<Float> public var thumbCMC: SIMD2<Float> public var littleMCP: SIMD2<Float> public var thumbTip: SIMD2<Float> public var indexTip: SIMD2<Float> public var middleTip: SIMD2<Float> public var ringTip: SIMD2<Float> public var littleTip: SIMD2<Float> } } public struct BlendShape: Equatable { public var lookAtPoint: SIMD2<Float> public var browDownLeft: Float public var browDownRight: Float public var browInnerUp: Float public var browOuterUpLeft: Float public var browOuterUpRight: Float public var cheekPuff: Float public var cheekSquintLeft: Float public var cheekSquintRight: Float public var eyeBlinkLeft: Float public var eyeBlinkRight: Float public var eyeLookDownLeft: Float public var eyeLookDownRight: Float public var eyeLookInLeft: Float public var eyeLookInRight: Float public var eyeLookOutLeft: Float public var eyeLookOutRight: Float public var eyeLookUpLeft: Float public var eyeLookUpRight: Float public var eyeSquintLeft: Float public var eyeSquintRight: Float public var eyeWideLeft: Float public var eyeWideRight: Float public var jawForward: Float public var jawLeft: Float public var jawOpen: Float public var jawRight: Float public var mouthClose: Float public var mouthDimpleLeft: Float public var mouthDimpleRight: Float public var mouthFrownLeft: Float public var mouthFrownRight: Float public var mouthFunnel: Float public var mouthLeft: Float public var mouthLowerDownLeft: Float public var mouthLowerDownRight: Float public var mouthPressLeft: Float public var mouthPressRight: Float public var mouthPucker: Float public var mouthRight: Float public var mouthRollLower: Float public var mouthRollUpper: Float public var mouthShrugLower: Float public var mouthShrugUpper: Float public var mouthSmileLeft: Float public var mouthSmileRight: Float public var mouthStretchLeft: Float public var mouthStretchRight: Float public var mouthUpperUpLeft: Float public var mouthUpperUpRight: Float public var noseSneerLeft: Float public var noseSneerRight: Float public var tongueOut: Float } Data 17% ※iPhone 12ͷ৔߹ JSONEncoderͷ৔߹ (0.003 s/frame) ඵؒͷॲཧ࣌ؒͷ ͕࢖ΘΕͯ͠·͏
  4. ©︎ Dataܕ΁ͷߴ଎ͳม׵ํ๏ struct Head: Equatable { let x: Float let

    y: Float let z: Float } var head = Head(x: 0, y: 1, z: 2) var encoded = Data(bytesNoCopy: &head, count: MemoryLayout<Head>.size, deallocator: .none) let decoded = encoded.withUnsafeBytes { $0.load(as: Head.self) } XCTAssertEqual(head, decoded) // Dataܕ΁ͷม׵ // ݩͷܕ΁ͷม׵ όΠτྻͱͯͦ͠ͷ··ม׵
  5. ©︎ ϓϩύςΟͷ࣋ͪํʹ஫ҙ͕ඞཁ struct Head: Equatable { let x: Float let

    y: Float let z: Float } var encoded = Data(bytesNoCopy: &head, count: MemoryLayout<Head>.size, deallocator: .none) let decoded = encoded.withUnsafeBytes { $0.load(as: Head.self) } XCTAssertEqual(head, decoded)
  6. ©︎ ϓϩύςΟͷ࣋ͪํʹ஫ҙ͕ඞཁ struct Head: Equatable { let x: Float let

    y: Float let z: Float let isTracking: Bool } var head = Head(x: 0, y: 1, z: 2, isTracking: true) ※64bit CPU؀ڥ var encoded = Data(bytesNoCopy: &head, count: MemoryLayout<Head>.size, deallocator: .none) let decoded = encoded.withUnsafeBytes { $0.load(as: Head.self) } XCTAssertEqual(head, decoded)
  7. ©︎ ϓϩύςΟͷ࣋ͪํʹ஫ҙ͕ඞཁ struct Head: Equatable { let x: Float let

    y: Float let z: Float let isTracking: Bool } var head = Head(x: 0, y: 1, z: 2, isTracking: true) ※64bit CPU؀ڥ var encoded = Data(bytesNoCopy: &head, count: MemoryLayout<Head>.size, deallocator: .none) let decoded = encoded.withUnsafeBytes { $0.load(as: Head.self) } XCTAssertEqual(head, decoded) struct Head: Equatable { let x: Float let y: Float let z: Float let isTracking: Bool }
  8. ©︎ ϓϩύςΟͷ࣋ͪํʹ஫ҙ͕ඞཁ var head = Head(x: 0, y: 1, z:

    2, isTracking: true) struct Head: Equatable { let isTracking: Bool let x: Float let y: Float let z: Float } var head = Head(isTracking: true, x: 0, y: 1, z: 2) ※64bit CPU؀ڥ var encoded = Data(bytesNoCopy: &head, count: MemoryLayout<Head>.size, deallocator: .none) let decoded = encoded.withUnsafeBytes { $0.load(as: Head.self) } XCTAssertEqual(head, decoded) struct Head: Equatable { let x: Float let y: Float let z: Float let isTracking: Bool }
  9. ©︎ structͷMemoryLayout let layout = MemoryLayout<Head>.self print(layout.size) // 13 print(layout.alignment)

    // 4 print(layout.stride) // 16 let layout = MemoryLayout<Head>.self print(layout.size) // 16 print(layout.alignment) // 4 print(layout.stride) // 16 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 x y z isTracking 1 word (64 bit CPUͳΒ8 byte) Ͱѻ͍΍͍͢Α͏ʹpadding͕ೖΔ 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 x y z isTracking pad struct Head: Equatable { let isTracking: Bool let x: Float let y: Float let z: Float } struct Head: Equatable { let x: Float let y: Float let z: Float let isTracking: Bool }
  10. ©︎ ҆શੑΛऔͬͯҰͭҰͭม׵͢Δ struct Head: Equatable { var x: Float var

    y: Float var z: Float var isTracking: Bool mutating func encodeToData() -> Data { return Data(bytesNoCopy: &x, count: MemoryLayout<Float>.size, deallocator: .none) + Data(bytesNoCopy: &y, count: MemoryLayout<Float>.size, deallocator: .none) + Data(bytesNoCopy: &z, count: MemoryLayout<Float>.size, deallocator: .none) + Data(bytesNoCopy: &isTracking, count: MemoryLayout<Bool>.size, deallocator: .none) } static func decode(_ data: Data) -> Self { var offset = 0 func nextValue<T>(_ pointer: UnsafeRawPointer?, as: T.Type = T.self) -> T { defer { offset += MemoryLayout<T>.size } return pointer!.advanced(by: offset).load(as: T.self) } return data.withUnsafeBytes { Head( x: nextValue($0.baseAddress), y: nextValue($0.baseAddress), z: nextValue($0.baseAddress), isTracking: nextValue($0.baseAddress) ) } } }
  11. ©︎ Swift MacrosͰ҆શͳม׵Λࣗಈੜ੒͢Δ @DataSerialize struct Head: Equatable { let x:

    Float let y: Float let z: Float let isTracking: Bool } var head = Head(x: 0, y: 1, z: 2, isTracking: true) var encoded = head.encodeToData() var decoded = Head.decode(encoded)
  12. ©︎ Swift MacrosͰ҆શͳม׵Λࣗಈੜ੒͢Δ @attached(member, names: arbitrary) public macro DataSerialize() =

    #externalMacro(module: "SerializeMacros", type: "DataSerializeMacro") public struct DataSerializeMacro: MemberMacro { public static func expansion( of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext ) throws -> [DeclSyntax] { let properties = declaration.memberBlock.members .compactMap { $0.decl.as(VariableDeclSyntax.self) } .map { ( name: $0.bindings.first!.pattern.as(IdentifierPatternSyntax.self)!.identifier.text, type: $0.bindings.first!.typeAnnotation!.type.as(IdentifierTypeSyntax.self)!.trimmedDescription ) } return [ """ static func decode(_ data: Data) -> Self { var offset = 0 func nextValue<T>(_ pointer: UnsafeRawPointer, as: T.Type = T.self) -> T { defer { offset += MemoryLayout<T>.size } return pointer.advanced(by: offset).load(as: T.self) } return data.withUnsafeBytes { Self.init( \(raw: properties.map { name, type in """ \(name): nextValue($0.baseAddress!) """ }.joined(separator: ",\n")) ) } } """, """ mutating func encodeToData() -> Data { \(raw: properties.enumerated().map { let (index, (name, type)) = $0 return """ let d\(index) = Data(bytesNoCopy: &\(name), count: MemoryLayout<\(type)>.size, deallocator: .none) """ }.joined(separator: "\n")) return \(raw: (0..<properties.count).map { "d\($0)" }.joined(separator: " + ")) } """ ] } } ※ίʔυলུͷͨΊೖΕࢠඇରԠ 😇 ͕࣌ؒͳ͍ʂ
  13. ©︎ ύϑΥʔϚϯεൺֱ: Encode + Decode ϓϩύςΟ͝ͱʹϙΠϯλͰม׵T $PEBCMFͰม׵T ҰׅͰϙΠϯλͰม׵T - ഒ

    ରCodableൺ 4XJGU.BDSPT 60ϑϨʔϜ෼ͷॲཧʹ͔͔Δ࣌ؒ ૣ͍ ૣ͍ 304 35ഒ ˞վળͷ༨஍͸·ͩ·ͩ͋Γ
  14. ©︎ ύϑΥʔϚϯεൺֱ: Encode + Decode ϓϩύςΟ͝ͱʹϙΠϯλͰม׵T $PEBCMFͰม׵T ҰׅͰϙΠϯλͰม׵T - ഒ

    ରCodableൺ 4XJGU.BDSPT 60ϑϨʔϜ෼ͷॲཧʹ͔͔Δ࣌ؒ ૣ͍ ૣ͍ 304 35ഒ ˞վળͷ༨஍͸·ͩ·ͩ͋Γ