: ExpressibleByStringInterpolation {
- typealias StringInterpolation = StreamingInterpolation
-
- init(printer: P, stringInterpolation: StreamingInterpolation
) {
- self.interpolation = stringInterpolation
- }
-
- init(stringInterpolation: StreamingInterpolation
) {
- self.interpolation = stringInterpolation
- }
-
- init(stringLiteral value: StaticString) {
- self.interpolation = StreamingInterpolation(
- literalCapacity: 0, interpolationCount: 0)
- self.interpolation.appendLiteral(value)
- }
-
- var printer: P { interpolation.printer }
-
- private var interpolation: StreamingInterpolation
-}
diff --git a/harmony/Sources/Application/Main.swift b/harmony/Sources/Application/Main.swift
deleted file mode 100644
index 23eb9897..00000000
--- a/harmony/Sources/Application/Main.swift
+++ /dev/null
@@ -1,387 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-// swift-format-ignore: AlwaysUseLowerCamelCase, NeverForceUnwrap
-
-struct A2DPStreamEndpoint {
- var a2dp_local_seid: UInt8 = 0
- var media_sbc_codec_configuration: (UInt8, UInt8, UInt8, UInt8) = (0, 0, 0, 0)
-}
-
-var hci_event_callback_registration = btstack_packet_callback_registration_t()
-var stream_endpoint = A2DPStreamEndpoint()
-
-// we support all configurations with bitpool 2-53
-var media_sbc_codec_capabilities: (UInt8, UInt8, UInt8, UInt8) = (
- //(AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
- 0xFF,
- //(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS,
- 0xFF,
- 2, 53
-)
-
-// FIXME: use Vector
-let sdp_avdtp_sink_service_buffer = UnsafeMutableRawBufferPointer.allocate(
- byteCount: 150,
- alignment: MemoryLayout.alignment)
-// FIXME: use Vector
-let sdp_avrcp_target_service_buffer = UnsafeMutableRawBufferPointer.allocate(
- byteCount: 150,
- alignment: MemoryLayout.alignment)
-// FIXME: use Vector
-let sdp_avrcp_controller_service_buffer =
- UnsafeMutableRawBufferPointer.allocate(
- byteCount: 200,
- alignment: MemoryLayout.alignment)
-// FIXME: use Vector
-let device_id_sdp_service_buffer = UnsafeMutableRawBufferPointer.allocate(
- byteCount: 100,
- alignment: MemoryLayout.alignment)
-
-// FIXME: use `Vector`
-func buttonCallback(pin: UInt32, event: UInt32) {
- guard event & UInt32(GPIO_IRQ_EDGE_FALL.rawValue) != 0 else { return }
- Application.shared.buttonPressed(pin: pin)
-}
-
-let BUFFER_SAMPLE_CAPACITY = 512
-
-
-let LED_STRIP_LED_COUNT = 20
-
-let MUTE_BUTTON_PIN: UInt32 = 6
-let ROTARY_ENCODER_A_PIN : UInt32 = 7
-let ROTARY_ENCODER_B_PIN: UInt32 = 8
-let PLAY_PAUSE_BUTTON_PIN: UInt32 = 9
-let PREVIOUS_BUTTON_PIN: UInt32 = 10
-let NEXT_BUTTON_PIN: UInt32 = 11
-let LED_STRIP_PIN: UInt32 = 17
-let EM_DRIVE_PIN: UInt32 = 18
-
-let WIRELESS_LED_PIN = UInt32(CYW43_WL_GPIO_LED_PIN)
-
-struct Application: ~Copyable {
- var audioEngine = AudioEngine()
-
- var audioAnalyzer = AudioAnalyzer()
-
- let wirelessLedBlinkPeriodMs: UInt32 = 1000
- var wirelessLedBlinkTimer = btstack_timer_source_t()
- var wirelessLedBlinkState = false
-
- let ledStripUpdatePeriodMs: UInt32 = 1000
- var ledStripUpdateTimer = btstack_timer_source_t()
- var ledStrip = LEDStrip(
- dataPin: LED_STRIP_PIN,
- ledCount: LED_STRIP_LED_COUNT,
- pio: 0,
- pioSm: 1)
-
- let volumeKnobSamplerPeriodMs: UInt32 = 100
- var volumeKnobSamplerTimer = btstack_timer_source_t()
- var volumeKnob = QuadratureEncoder(
- pinA: ROTARY_ENCODER_A_PIN,
- pinB: ROTARY_ENCODER_B_PIN,
- pio: 1,
- pioSm: 0)
-
- var previousPressTimes = ButtonTimes()
-
- var muteButton = Button(
- pin: MUTE_BUTTON_PIN,
- onPress: buttonCallback)
- var nextButton = Button(
- pin: NEXT_BUTTON_PIN,
- onPress: buttonCallback)
- var playPauseButton = Button(
- pin: PLAY_PAUSE_BUTTON_PIN,
- onPress: buttonCallback)
- var previousButton = Button(
- pin: PREVIOUS_BUTTON_PIN,
- onPress: buttonCallback)
-
- mutating func run() {
- stdio_init_all()
- i2c_init()
-
- multicore_launch_core1 {
- log("core1_main")
- Application.shared.audioAnalyzer.run()
- }
-
- log("Hello!")
- log("sys clock running at \(clock_get_hz(clk_sys)) Hz")
- log("Initializing cyw43_driver")
- precondition(cyw43_arch_init() == 0, "cyw43_arch_init failed")
- wirelessLedBlink(count: 2)
-
- gpio_init(EM_DRIVE_PIN)
- gpio_set_dir(EM_DRIVE_PIN, true)
-
- var sdp = ServiceDiscoveryProtocol()
- _setup_demo(&sdp)
-
-
- // turn on!
- log("Starting BTstack ...")
- hci_power_control(HCI_POWER_ON)
- wirelessLedBlink(count: 2)
- log("[main] Started, starting btstack run loop")
-
- btstack_run_loop_set_timer_handler(&self.volumeKnobSamplerTimer) { timer in
- guard let timer else { return }
- Application.shared.volumeKnobSamplerHandler(&timer.pointee)
- }
- btstack_run_loop_set_timer(&self.volumeKnobSamplerTimer, volumeKnobSamplerPeriodMs)
- btstack_run_loop_add_timer(&self.volumeKnobSamplerTimer)
-
- btstack_run_loop_set_timer_handler(&self.ledStripUpdateTimer) { timer in
- guard let timer else { return }
- Application.shared.ledStripUpdateHandler(&timer.pointee)
- }
- btstack_run_loop_set_timer(&self.ledStripUpdateTimer, ledStripUpdatePeriodMs)
- btstack_run_loop_add_timer(&self.ledStripUpdateTimer)
-
- btstack_run_loop_set_timer_handler(&self.wirelessLedBlinkTimer) { timer in
- guard let timer else { return }
- Application.shared.wirelessLedBlinkHandler(&timer.pointee)
- }
- btstack_run_loop_set_timer(&self.wirelessLedBlinkTimer, wirelessLedBlinkPeriodMs)
- btstack_run_loop_add_timer(&self.wirelessLedBlinkTimer)
-
-
- btstack_run_loop_execute() // btstack_run_loop_execute never returns
- _ = sdp // make sure SDP lives until the runloop exits
- }
-}
-
-// Timer handlers
-extension Application {
- mutating func volumeKnobSamplerHandler(_ timer: inout btstack_timer_source_t) {
- let scaleFactor: Int32 = 5
- audioEngine.adjustVolume(by: Application.shared.volumeKnob.delta() * scaleFactor)
- btstack_run_loop_set_timer(&timer, volumeKnobSamplerPeriodMs)
- btstack_run_loop_add_timer(&timer)
- }
-
- mutating func ledStripUpdateHandler(_ timer: inout btstack_timer_source_t) {
- ledStrip.setColor(
- red: .random(in: 0...255),
- green: .random(in: 0...255),
- blue: .random(in: 0...255))
- btstack_run_loop_set_timer(&timer, ledStripUpdatePeriodMs)
- btstack_run_loop_add_timer(&timer)
- }
-
- mutating func wirelessLedBlinkHandler(_ timer: inout btstack_timer_source_t) {
- self.wirelessLedBlinkState.toggle()
- cyw43_arch_gpio_put(WIRELESS_LED_PIN, self.wirelessLedBlinkState)
- btstack_run_loop_set_timer(&timer, wirelessLedBlinkPeriodMs)
- btstack_run_loop_add_timer(&timer)
- }
-}
-
-// Button press callbacks
-extension Application {
- // FIXME: use `time_us_64`
- // This is a particularly large debounce time
- static let buttonDebounceTimeMs = 150
- mutating func buttonPressed(pin: UInt32) {
- let currentTime = to_ms_since_boot(get_absolute_time())
- guard currentTime - previousPressTimes[pin] > Self.buttonDebounceTimeMs else {
- log("soft debounce \(pin)")
- return
- }
- previousPressTimes[pin] = currentTime
-
- switch pin {
- case MUTE_BUTTON_PIN:
- self.toggleMute()
- case NEXT_BUTTON_PIN:
- self.nextTrack()
- case PLAY_PAUSE_BUTTON_PIN:
- self.playPauseTrack()
- case PREVIOUS_BUTTON_PIN:
- self.previousTrack()
- default:
- // ignore
- break
- }
- }
-
- mutating func toggleMute() {
- self.audioEngine.toggleMute()
- }
-
- mutating func nextTrack() {
- log("avrcp_controller_forward")
- avrcp_controller_forward(avrcp_connection.avrcp_cid)
- }
-
- mutating func playPauseTrack() {
- // FIXME: this state management is almost certainly wrong
- if audioEngine.running {
- log("avrcp_controller_stop")
- avrcp_controller_pause(avrcp_connection.avrcp_cid)
- } else {
- log("avrcp_controller_play")
- avrcp_controller_play(avrcp_connection.avrcp_cid)
- }
- }
-
- mutating func previousTrack() {
- log("avrcp_controller_backward")
- avrcp_controller_backward(avrcp_connection.avrcp_cid)
- }
-}
-
-extension Application {
- func wirelessLedBlink(count: UInt32) {
- for _ in 0.. Int32 {
- let value = quadrature_encoder_get_count(self.pioHw, self.pioSm)
- self.previousValue = value
- return value
- }
-
- mutating func delta() -> Int32 {
- let value = quadrature_encoder_get_count(self.pioHw, self.pioSm)
- // NOTE: Thanks to two's complement arithmetic `delta`` will always be
- // correct even when `value`` wraps around `Int32.max` / `Int32.min`.
- let delta = value &- self.previousValue
- self.previousValue = value
- return delta
- }
-}
diff --git a/harmony/Sources/Application/Stubs.swift b/harmony/Sources/Application/Stubs.swift
deleted file mode 100644
index b6e8deb0..00000000
--- a/harmony/Sources/Application/Stubs.swift
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-// Embedded Swift currently requires posix_memalign, but the C libraries in the
-// Pico SDK do not provide it. Let's implement it and forward the calls to
-// aligned_alloc(3).
-@_cdecl("posix_memalign")
-public func posix_memalign(
- memptr: UnsafeMutablePointer,
- alignment: size_t,
- size: size_t
-) -> Int32 {
- if let allocation = aligned_alloc(alignment, size) {
- memptr.pointee = allocation
- return 0
- }
- return _errno()
-}
-
-// FIXME: document
-@_cdecl("swift_isEscapingClosureAtFileLocation")
-func swift_isEscapingClosureAtFileLocation(
- object: UnsafeRawPointer,
- filename: UnsafePointer,
- filenameLength: Int32,
- line: Int32,
- column: Int32,
- type: UInt
-) -> Bool {
- false
-}
diff --git a/harmony/Sources/Audio/AudioAnalyzer.swift b/harmony/Sources/Audio/AudioAnalyzer.swift
deleted file mode 100644
index 1939fd3f..00000000
--- a/harmony/Sources/Audio/AudioAnalyzer.swift
+++ /dev/null
@@ -1,94 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-struct AnalyzedAudioBuffer: ~Copyable {
- var enableMagnet: Bool
- var buffer: AudioBuffer
-}
-
-struct AudioAnalyzer: ~Copyable {
- // FIXME: add soft limit for time magnet can be enabled
-
- var fft_instance: arm_rfft_instance_q15
- // Used for both sample input and fft magnitude output
- var dataBuffer0: UnsafeMutableBufferPointer
- // Used for fft output
- var dataBuffer1: UnsafeMutableBufferPointer
-
- init() {
- let audioBufferCapacity = BUFFER_SAMPLE_CAPACITY
- let fftOutputBufferCapacity = BUFFER_SAMPLE_CAPACITY * 2 // real + complex
-
- self.fft_instance = arm_rfft_instance_q15()
- self.dataBuffer0 = .allocate(capacity: audioBufferCapacity)
- self.dataBuffer1 = .allocate(capacity: fftOutputBufferCapacity)
- // IMPORTANT: `bitReverseFlag` must be set. I don't understand why based on
- // the documentation
- arm_rfft_init_q15(&fft_instance, UInt32(audioBufferCapacity), 0, 1)
- }
-
- deinit {
- self.dataBuffer1.deallocate()
- self.dataBuffer0.deallocate()
- }
-
- mutating func run() {
- while true {
- guard let buffer = Application.shared.audioEngine.buffers.popFullBuffer() else { continue }
-
- /// Copy data from buffer to dataBuffer0 (because the fft will modify the data)
- precondition(self.dataBuffer0.update(from: buffer.storage).index == self.dataBuffer0.count)
- // Perform the fft using the data in dataBuffer0 and store the result in dataBuffer1
- arm_rfft_q15(&self.fft_instance, self.dataBuffer0.baseAddress, self.dataBuffer1.baseAddress)
- // Calculate the magnitude of the fft output in dataBuffer1 and store the result in dataBuffer0
- arm_cmplx_mag_q15(self.dataBuffer1.baseAddress, self.dataBuffer0.baseAddress, UInt32(self.dataBuffer0.count))
-
- // NOTE: This is probably wrong becasue buffer.storage is stereo data
-
- // Given we take an fft of audio data at 44100 Hz with a window of 512
- // samples, each output bin of the fft represents a 172 Hz range
- // (44100 Hz / 2 / 512 = ~172Hz)
-
- // 1 Khz
- let lowend =
- self.dataBuffer0[00] +
- self.dataBuffer0[01] +
- self.dataBuffer0[02] +
- self.dataBuffer0[03] +
- self.dataBuffer0[04] +
- self.dataBuffer0[05] +
- self.dataBuffer0[06] +
- self.dataBuffer0[07] +
- self.dataBuffer0[08] +
- self.dataBuffer0[09] +
- self.dataBuffer0[10] +
- self.dataBuffer0[10] +
- self.dataBuffer0[11] +
- self.dataBuffer0[12] +
- self.dataBuffer0[13] +
- self.dataBuffer0[14] +
- self.dataBuffer0[15] +
- self.dataBuffer0[16] +
- self.dataBuffer0[17] +
- self.dataBuffer0[18] +
- self.dataBuffer0[19]
-
-
-
- let avg = lowend / 20
-
- let analyzedBuffer = AnalyzedAudioBuffer(
- enableMagnet: avg > (1 << 8),
- buffer: buffer)
- Application.shared.audioEngine.buffers.pushAnalyzedBuffer(analyzedBuffer)
- }
- }
-}
diff --git a/harmony/Sources/Audio/AudioBuffer.swift b/harmony/Sources/Audio/AudioBuffer.swift
deleted file mode 100644
index 49431b52..00000000
--- a/harmony/Sources/Audio/AudioBuffer.swift
+++ /dev/null
@@ -1,28 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-struct AudioBuffer: ~Copyable {
- // FIXME: Raw
- var storage: UnsafeMutableBufferPointer
- var capacity: Int { self.storage.count }
- var count: Int
-
- init(capacity: Int) {
- self.storage = .allocate(capacity: capacity)
- self.storage.initialize(repeating: 0)
- // FIXME: don't assume filled.
- self.count = capacity
- }
-
- deinit {
- self.storage.deallocate()
- }
-}
diff --git a/harmony/Sources/Audio/AudioBufferTransport.swift b/harmony/Sources/Audio/AudioBufferTransport.swift
deleted file mode 100644
index 907637d3..00000000
--- a/harmony/Sources/Audio/AudioBufferTransport.swift
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-struct AudioBufferTransport: ~Copyable {
- var emptyBuffers: Ring
- var fullBuffers: Ring
- var analyzedBuffers: Ring
-
- init(bufferCount: Int, bufferCapacity: Int) {
- // Ring buffer needs one extra slot to distinguish between empty and full.
- self.emptyBuffers = Ring(capacity: bufferCount + 1)
- self.fullBuffers = Ring(capacity: bufferCount + 1)
- self.analyzedBuffers = Ring(capacity: bufferCount + 1)
-
- for _ in 0.. AudioBuffer? {
- self.emptyBuffers.pop()
- }
-
- mutating func pushFullBuffer(_ buffer: consuming AudioBuffer) {
- self.fullBuffers.push(buffer)
- }
-
- mutating func popFullBuffer() -> AudioBuffer? {
- self.fullBuffers.pop()
- }
-
- mutating func pushAnalyzedBuffer(_ buffer: consuming AnalyzedAudioBuffer) {
- self.analyzedBuffers.push(buffer)
- }
-
- mutating func popAnalyzedBuffer() -> AnalyzedAudioBuffer? {
- self.analyzedBuffers.pop()
- }
-}
diff --git a/harmony/Sources/Audio/AudioEngine.swift b/harmony/Sources/Audio/AudioEngine.swift
deleted file mode 100644
index 660b2367..00000000
--- a/harmony/Sources/Audio/AudioEngine.swift
+++ /dev/null
@@ -1,120 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-struct AudioEngine: ~Copyable {
- var running: Bool
- var mute: Bool
- var volume: UInt8
- var rawVolume: UInt8
-
- var audio_pico: AudioPico
- var audio_i2s: AudioI2S
- var buffers: AudioBufferTransport
- var amp: MAX9744
-
- init() {
- self.running = false
- self.mute = false
- self.volume = 0
- self.rawVolume = 30
-
- self.audio_pico = AudioPico()
- self.audio_i2s = AudioI2S(
- data_pin: PICO_AUDIO_I2S_DATA_PIN,
- clock_pin_base: PICO_AUDIO_I2S_CLOCK_PIN_BASE,
- pio: 0,
- pio_sm: 0,
- // FIXME: Dont claim on each `media_processing_init`??
- dma_channel: UInt32(dma_claim_unused_channel(true)))
- self.buffers = AudioBufferTransport(bufferCount: 8, bufferCapacity: BUFFER_SAMPLE_CAPACITY)
- self.amp = MAX9744(i2c: i2c0_inst)
-
- self.set(volume: 0)
- }
-}
-
-extension AudioEngine {
- mutating func `init`(_ configuration: MediaCodecConfigurationSBC) {
- log(#function)
- SBCDecoder.configure(mode: SBC_MODE_STANDARD)
-
- // setup audio playback
- // FIXME: update channel count in resampler
- // FIXME: update output sample-rate
-
- self.audio_i2s.update_pio_frequency(
- UInt32(configuration.sampling_frequency))
-
- self.running = false
- }
-
- mutating func toggleMute() {
- self.mute.toggle()
- if self.mute {
- self.amp.set(rawVolume: 0)
- } else {
- self.amp.set(rawVolume: rawVolume)
- }
- }
-
- mutating func set(volume: UInt8) {
- guard self.volume != volume else { return }
- self.volume = volume
- // FIXME:
- avrcp_target_volume_changed(avrcp_connection.avrcp_cid, volume >> 1)
-
- // Map volume (0-255) to gain (0-63)
- let rawVolume = UInt8((UInt32(volume) * 63) / 255)
- guard self.rawVolume != rawVolume else { return }
- self.rawVolume = rawVolume
-
- guard !self.mute else { return }
- self.amp.set(rawVolume: rawVolume)
- }
-
- mutating func adjustVolume(by delta: Int32) {
- guard delta != 0 else { return }
- let volume = Int32(self.volume) + delta
- let clamped = UInt8(clamping: volume)
- log("Adjust volume by \(delta) to \(clamped)")
- self.set(volume: clamped)
- }
-
- mutating func start() {
- guard !self.running else { return }
- guard self.audio_pico.sbc_frames.count >= OPTIMAL_FRAMES_MIN else { return }
- log(#function)
- // start audio playback
- self.audio_pico.start_stream()
- self.audio_i2s.enable(true)
- self.running = true
- }
-
- mutating func pause() {
- guard self.running else { return }
- log(#function)
- self.close()
- }
-
- mutating func close() {
- log(#function)
-
- // stop audio playback
- self.running = false
- self.audio_pico.stop_stream()
- self.audio_i2s.enable(false)
-
- // discard pending data
- self.audio_pico.decoded_audio.clear()
- self.audio_pico.sbc_frame_size = 0
- self.audio_pico.sbc_frames.clear()
- }
-}
diff --git a/harmony/Sources/Audio/AudioI2S.swift b/harmony/Sources/Audio/AudioI2S.swift
deleted file mode 100644
index 52e4b96d..00000000
--- a/harmony/Sources/Audio/AudioI2S.swift
+++ /dev/null
@@ -1,174 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-var zero: UInt32 = 0
-
-// FIXME: #define __time_critical_func(func_name) __not_in_flash_func(func_name)
-// irq handler for DMA
-@_cdecl("audio_i2s_dma_irq_handler")
-func audio_i2s_dma_irq_handler() {
- Application.shared.audioEngine.audio_i2s.handle_dma_irq()
-}
-
-struct AudioI2S: ~Copyable {
- var enabled: Bool
- var freq: UInt32
- var playing_buffer: AudioBuffer?
-
- var pio: UInt32
- var pio_sm: UInt32
- var dma_channel: UInt32
- var pioHw: PIO
-
- init(
- data_pin: UInt32,
- clock_pin_base: UInt32,
- pio: UInt32,
- pio_sm: UInt32,
- // FIXME: dma_channel is already claimed
- dma_channel: UInt32,
- ) {
- self.enabled = false
- self.freq = 0
-
- self.pio = pio
- self.pio_sm = pio_sm
- self.dma_channel = dma_channel
-
- let gpioFunc: gpio_function_rp2040
- switch pio {
- case 0:
- self.pioHw = _pio0()
- gpioFunc = GPIO_FUNC_PIO0
- case 1:
- self.pioHw = _pio1()
- gpioFunc = GPIO_FUNC_PIO1
- default:
- fatalError("Invalid PIO index")
- }
-
- gpio_set_function(data_pin, gpioFunc)
- gpio_set_function(clock_pin_base, gpioFunc)
- gpio_set_function(clock_pin_base + 1, gpioFunc)
-
- pio_sm_claim(self.pioHw, self.pio_sm)
-
- let offset = withUnsafePointer(to: audio_i2s_program) {
- pio_add_program(self.pioHw, $0)
- }
-
- audio_i2s_program_init(
- self.pioHw, self.pio_sm, UInt32(offset), data_pin, clock_pin_base)
-
- __mem_fence_release()
-
- var dma_config = dma_channel_get_default_config(dma_channel)
-
- channel_config_set_dreq(
- &dma_config,
- UInt32(DREQ_PIO0_TX0.rawValue) + self.pio_sm)
-
- channel_config_set_transfer_data_size(&dma_config, i2s_dma_configure_size)
- dma_channel_configure(
- dma_channel,
- &dma_config,
- // FIXME: .advanced(by: Int(self.pio_sm))
- self.pioHw.pointer(to: \.txf), // dest
- nil, // src
- 0, // count
- false) // trigger
-
- irq_add_shared_handler(
- UInt32(DMA_IRQ_0.rawValue) + PICO_AUDIO_I2S_DMA_IRQ,
- audio_i2s_dma_irq_handler,
- UInt8(PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY))
- dma_irqn_set_channel_enabled(PICO_AUDIO_I2S_DMA_IRQ, dma_channel, true)
- }
-
- mutating func enable(_ enable: Bool) {
- guard self.enabled != enable else { return }
- self.enabled = enable
-
- irq_set_enabled(UInt32(DMA_IRQ_0.rawValue) + PICO_AUDIO_I2S_DMA_IRQ, enable)
-
- if enable {
- self.audio_start_dma_transfer()
- } else {
- // if there was a buffer in flight, it will not be freed by DMA IRQ,
- // let's do it manually
- self.audio_finish_dma_transfer()
- gpio_put(EM_DRIVE_PIN, false)
- }
-
- pio_sm_set_enabled(self.pioHw, self.pio_sm, enable)
- }
-
- mutating func update_pio_frequency(_ sample_freq: UInt32?) {
- guard let sample_freq = sample_freq else { return }
- guard sample_freq != self.freq else { return }
-
- let system_clock_frequency = clock_get_hz(clk_sys)
- precondition(system_clock_frequency < 0x4000_0000)
- // avoid arithmetic overflow
- let divider = system_clock_frequency * 4 / sample_freq
- precondition(divider < 0x1000000)
- pio_sm_set_clkdiv_int_frac(
- self.pioHw, self.pio_sm, UInt16(divider >> 8), UInt8(divider & 0xff))
- self.freq = sample_freq
- }
-
- mutating func handle_dma_irq() {
- guard dma_irqn_get_channel_status(PICO_AUDIO_I2S_DMA_IRQ, self.dma_channel)
- else { return }
- dma_irqn_acknowledge_channel(PICO_AUDIO_I2S_DMA_IRQ, self.dma_channel)
-
- // free the buffer we just finished
- self.audio_finish_dma_transfer()
- self.audio_start_dma_transfer()
- }
-
- mutating func audio_start_dma_transfer() {
- precondition(self.playing_buffer == nil)
-
- // FIXME: support dynamic frequency shifting
-
- if let ab = Application.shared.audioEngine.buffers.popAnalyzedBuffer() {
- gpio_put(EM_DRIVE_PIN, ab.enableMagnet)
-
- let ab = ab.buffer
- let buf = UnsafeMutableRawBufferPointer(ab.storage)
- self.playing_buffer = consume ab
-
- var c = dma_get_channel_config(self.dma_channel)
- channel_config_set_read_increment(&c, true)
- dma_channel_set_config(self.dma_channel, &c, false)
- dma_channel_transfer_from_buffer_now(
- self.dma_channel,
- buf.baseAddress,
- // FIXME: using capacity instead of ab count
- UInt32(buf.count) / 4)
- } else {
- gpio_put(EM_DRIVE_PIN, false)
- log("buffer pool low")
- // just play some silence
- var c = dma_get_channel_config(self.dma_channel)
- channel_config_set_read_increment(&c, false)
- dma_channel_set_config(self.dma_channel, &c, false)
- dma_channel_transfer_from_buffer_now(
- self.dma_channel, &zero, PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH)
- }
- }
-
- mutating func audio_finish_dma_transfer() {
- guard let playingBuffer = self.playing_buffer.take() else { return }
- Application.shared.audioEngine.buffers.pushEmptyBuffer(playingBuffer)
- }
-}
diff --git a/harmony/Sources/Audio/AudioPico.swift b/harmony/Sources/Audio/AudioPico.swift
deleted file mode 100644
index ef540daf..00000000
--- a/harmony/Sources/Audio/AudioPico.swift
+++ /dev/null
@@ -1,181 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-extension UnsafeMutableBufferPointer where Element: ~Copyable {
- func split(at index: Self.Index) -> (Self, Self) {
- (self.extracting(..
- var sbc_frames: RingBuffer
- var sbc_frames_in_buffer: Int {
- guard sbc_frame_size > 0 else { return 0 }
- return self.sbc_frames.count / self.sbc_frame_size
- }
-
- // overflow buffer for not fully used sbc frames, with additional frames for resampling
- let decoded_audio_buffer: UnsafeMutableBufferPointer
- var decoded_audio: RingBuffer
-
- init() {
- let CHANNELS_PER_FRAME = 2
- let capacity = (128 + 16) * CHANNELS_PER_FRAME
-
- self.fill_timer = btstack_timer_source_t()
- self.resampler = Resampler(channels: CHANNELS_PER_FRAME)
-
- self.sbc_frame_size = 0
- self.sbc_frame_buffer = UnsafeMutableBufferPointer.allocate(
- capacity: MAX_SBC_FRAME_SIZE)
- self.sbc_frames = RingBuffer(
- capacity: (OPTIMAL_FRAMES_MAX + ADDITIONAL_FRAMES) * MAX_SBC_FRAME_SIZE)
-
- self.decoded_audio_buffer = .allocate(capacity: capacity)
- self.decoded_audio = RingBuffer(capacity: capacity)
- }
-
- mutating func enqueue(sbc_frames: UnsafeMutableBufferPointer, frame_size: Int)
- {
- self.sbc_frame_size = frame_size
- if !self.sbc_frames.write(contentsOf: sbc_frames) {
- log("Error: SBC frame buffer overflow")
- }
- self.updateResamplingFactor()
- }
-
- mutating func updateResamplingFactor() {
- let nominal_factor: UInt32 = 0x10000
- let compensation: UInt32 = 0x00100
-
- let resampling_factor =
- switch self.sbc_frames_in_buffer {
- case ..) {
- // called from lower-layer but guaranteed to be on main thread
- guard self.sbc_frame_size != 0 else {
- log("Frame size is 0")
- buffer.update(repeating: 0)
- return
- }
-
- // first fill from resampled audio
- let samplesReadCount = self.decoded_audio.read(into: buffer)
- var buffer = buffer.extracting(samplesReadCount...)
-
- // then start decoding sbc frames into the buffer
- while buffer.count > 0, self.sbc_frames.count > self.sbc_frame_size {
- // decode frame
- let elementsRead = self.sbc_frames.read(
- into: self.sbc_frame_buffer, count: self.sbc_frame_size)
- precondition(
- elementsRead == self.sbc_frame_size, "sbc frame size mismatch")
-
- SBCDecoder.decode_signed_16(
- mode: SBC_MODE_STANDARD,
- packet_status_flag: 0,
- buffer: UnsafeRawBufferPointer(self.sbc_frame_buffer)
- ) { samples, num_channels, sample_rate in
- precondition(num_channels == 2, "must be stereo")
-
- // Resample audio to compensate for the amount of buffered SBC frames
- let samples = self.resampler.resample(
- samples: .init(samples),
- usingTemporaryBuffer: self.decoded_audio_buffer)
-
- // Store samples in buffer first and excess in the ring buffer.
- let (samples_to_copy, samples_to_store) = samples.split(
- at: min(samples.count, buffer.count))
- let samplesCopiedCount = buffer.moveUpdate(
- fromContentsOf: samples_to_copy)
- buffer = buffer.extracting(samplesCopiedCount...)
- if !self.decoded_audio.write(contentsOf: samples_to_store) {
- log("ERROR: PCM ring buffer full!")
- }
- }
- }
- }
-
- mutating func fill_timer(
- _ timer: UnsafeMutablePointer?
- ) {
- // refill
- self.fill_buffers()
-
- // re-set timer
- btstack_run_loop_set_timer(timer, UInt32(DRIVER_POLL_INTERVAL_MS))
- btstack_run_loop_add_timer(timer)
- }
-
- mutating func start_stream() {
- // pre-fill buffers
- self.fill_buffers()
-
- // start timer
- // FIXME: Use ctx
- // NOTE: hardcoded to `Self` because the timer callback has no context
- // argument which can be used to pass `self`
- btstack_run_loop_set_timer_handler(
- &self.fill_timer, { Application.shared.audioEngine.audio_pico.fill_timer($0) })
- btstack_run_loop_set_timer_context(&self.fill_timer, nil)
- btstack_run_loop_set_timer(
- &self.fill_timer, UInt32(DRIVER_POLL_INTERVAL_MS))
- btstack_run_loop_add_timer(&self.fill_timer)
- }
-
- mutating func stop_stream() {
- // stop timer
- btstack_run_loop_remove_timer(&self.fill_timer)
- }
-}
diff --git a/harmony/Sources/Audio/MAX9744.swift b/harmony/Sources/Audio/MAX9744.swift
deleted file mode 100644
index 6c969508..00000000
--- a/harmony/Sources/Audio/MAX9744.swift
+++ /dev/null
@@ -1,105 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-struct MAX9744: ~Copyable {
- static let i2cAddress: UInt8 = 0x4B // 7 bit address
- static let absoluteVolumeControlRegisterAddress: UInt8 = 0x0
-
- static let modulationControlRegisterAddress: UInt8 = 0x1
- static let filterlessModulationBitPattern: UInt8 = 0x0
- static let pwmModulationBitPattern: UInt8 = 0x1
-
- static let incrementalVolumeControlRegisterAddress: UInt8 = 0x3
- static let increaseVolumeBitPattern: UInt8 = 0x4
- static let decreaseVolumeBitPattern: UInt8 = 0x5
-
- var i2c: i2c_inst_t
-
- init(i2c: i2c_inst_t) {
- self.i2c = i2c
- }
-}
-
-extension MAX9744 {
- static func validAddress(_ address: UInt8) -> Bool {
- switch address {
- case Self.absoluteVolumeControlRegisterAddress: true
- case Self.filterlessModulationBitPattern: true
- case Self.incrementalVolumeControlRegisterAddress: true
- default: false
- }
- }
-
- mutating func write(address: UInt8, value: UInt8) {
- precondition(Self.validAddress(address))
- var data = (address << 6) | value
- log("attempting to write \(hex: data)")
- let size = MemoryLayout.size(ofValue: data)
- let result = i2c_write_blocking(
- &self.i2c,
- Self.i2cAddress,
- &data,
- size,
- false)
- precondition(result == size, "I2C write failed")
- }
-
- mutating func read(address: UInt8) -> UInt8 {
- precondition(Self.validAddress(address))
- var data = address << 6
- let size = MemoryLayout.size(ofValue: data)
- let readResult = i2c_read_blocking(
- &self.i2c,
- Self.i2cAddress,
- &data,
- size,
- false)
- precondition(readResult == size, "I2C read failed")
- return data
- }
-}
-
-extension MAX9744 {
- /// 6 bit value ranging from 0 (mute) to 63 (+ 9.5 dB)
- mutating func set(rawVolume: UInt8) {
- precondition(0 <= rawVolume && rawVolume <= 63)
- self.write(
- address: Self.absoluteVolumeControlRegisterAddress,
- value: rawVolume)
- }
-
- enum ModulationMode {
- case filterless
- case pwm
- }
-
- mutating func set(moduluationMode: ModulationMode) {
- let modulationBitPattern = switch moduluationMode {
- case .filterless: Self.filterlessModulationBitPattern
- case .pwm: Self.pwmModulationBitPattern
- }
- self.write(
- address: Self.modulationControlRegisterAddress,
- value: modulationBitPattern)
- }
-
- mutating func increaseVolume() {
- self.write(
- address: Self.incrementalVolumeControlRegisterAddress,
- value: Self.increaseVolumeBitPattern)
- }
-
- mutating func decreaseVolume() {
- self.write(
- address: Self.incrementalVolumeControlRegisterAddress,
- value: Self.decreaseVolumeBitPattern)
- }
-}
diff --git a/harmony/Sources/Audio/Resampler.swift b/harmony/Sources/Audio/Resampler.swift
deleted file mode 100644
index ef4cd8bb..00000000
--- a/harmony/Sources/Audio/Resampler.swift
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-struct Resampler: ~Copyable {
- var channels: Int
- var context: btstack_resample_t
-
- init(channels: Int) {
- self.channels = channels
- self.context = btstack_resample_t()
- btstack_resample_init(&self.context, Int32(channels))
- }
-
- mutating func set(channels: Int) {
- self.channels = channels
- btstack_resample_init(&self.context, Int32(channels))
- }
-
- mutating func set(factor: UInt32) {
- btstack_resample_set_factor(&self.context, factor)
- }
-
- /// Resamples the given samples using the previously set resampling factor.
- ///
- /// Returns a slice of the temporary buffer that contains the resampled audio.
- mutating func resample(
- samples: UnsafeBufferPointer,
- usingTemporaryBuffer buffer: UnsafeMutableBufferPointer
- ) -> UnsafeMutableBufferPointer {
- precondition(samples.count.isMultiple(of: self.channels))
-
- // FIXME: understand why this is not `samples.count / self.channels`
- // The documentation just calls this parameter `numFrames` which implies
- // the sample count should be divided by the channel count.
- let inputFrameCount = samples.count
- let resampledFrameCount = btstack_resample_block(
- &self.context,
- samples.baseAddress,
- UInt32(inputFrameCount),
- buffer.baseAddress)
- let resampledSampleCount = Int(resampledFrameCount) * self.channels
- return buffer.extracting(..: ~Copyable {
- // FIMXE: Use an inline allocation like `Vector`
- var storage: UnsafeMutableBufferPointer
- var readerIndex: Int
- var writerIndex: Int
-
- init(capacity: Int) {
- self.storage = .allocate(capacity: capacity)
- self.readerIndex = 0
- self.writerIndex = 0
- }
-
- deinit {
- var readerIndex = self.readerIndex
- while self.readerIndex != self.writerIndex {
- self.storage.deinitializeElement(at: readerIndex)
- readerIndex = (readerIndex + 1) % self.storage.count
- }
- // FIXME: why can't we use a mutating method here?
- // while _ = self.pop() { }
- self.storage.deallocate()
- }
-}
-
-extension Ring where Element: ~Copyable {
- mutating func push(_ element: consuming Element) {
- let nextWriterIndex = (self.writerIndex + 1) % self.storage.count
- guard nextWriterIndex != self.readerIndex else { fatalError("Overflow") }
- self.storage.initializeElement(at: self.writerIndex, to: element)
- __dsb() // Make sure the element is written before updating the index
- self.writerIndex = nextWriterIndex
- }
-
- mutating func pop() -> Element? {
- guard self.readerIndex != self.writerIndex else { return nil }
- let element = self.storage.moveElement(from: self.readerIndex)
- __dsb() // Make sure the element is read before updating the index
- self.readerIndex = (self.readerIndex + 1) % self.storage.count
- return element
- }
-}
diff --git a/harmony/Sources/Audio/RingBuffer.swift b/harmony/Sources/Audio/RingBuffer.swift
deleted file mode 100644
index 11a30d0e..00000000
--- a/harmony/Sources/Audio/RingBuffer.swift
+++ /dev/null
@@ -1,126 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-// FIXME: RingBuffer
-struct RingBuffer: ~Copyable {
- // FIMXE: Use an inline allocation like `Vector`
- var storage: UnsafeMutableBufferPointer
- var count: Int
- var readerIndex: Int
- var writerIndex: Int
-
- init(capacity: Int) {
- self.storage = .allocate(capacity: capacity)
- self.readerIndex = 0
- self.writerIndex = 0
- self.count = 0
- }
-
- deinit {
- self.storage.deallocate()
- }
-}
-
-extension RingBuffer {
- var capacity: Int { self.storage.count }
- var availableCapacity: Int { self.capacity - self.count }
- var isEmpty: Bool { self.count == 0 }
- var isFull: Bool { self.count == self.capacity }
-}
-
-extension RingBuffer {
- mutating func clear() {
- // Forget about the contents of `storage`, this is safe because
- // `Element` is `BitwiseCopyable`.
- self.count = 0
- self.readerIndex = 0
- self.writerIndex = 0
- }
-
- mutating func read(
- into buffer: UnsafeMutableBufferPointer,
- count: Int? = nil
- ) -> Int {
- let elementsToRead = min(buffer.count, count ?? Int.max, self.count)
-
- // Reading 0 elements is a no-op.
- guard elementsToRead > 0 else { return elementsToRead }
-
- // Read the initial elements from the end of the ring buffer.
- let elementsUntilEnd = self.capacity - self.readerIndex
- let elementsToReadFirstHalf = min(elementsUntilEnd, elementsToRead)
- buffer.baseAddress!.update(
- from: self.storage.baseAddress! + self.readerIndex,
- count: elementsToReadFirstHalf)
- self.readerIndex += elementsToReadFirstHalf
-
- // Update the reader index to wrap if needed.
- if self.readerIndex == self.capacity {
- self.readerIndex = 0
- }
-
- // Read the remaining elements from the beginning of the ring buffer.
- let elementsToReadSecondHalf = elementsToRead - elementsToReadFirstHalf
- precondition(elementsToReadSecondHalf >= 0)
- (buffer.baseAddress! + elementsToReadFirstHalf).update(
- from: self.storage.baseAddress! + self.readerIndex,
- count: elementsToReadSecondHalf)
- self.readerIndex += elementsToReadSecondHalf
-
- // Update bookkeeping with the new count.
- self.count -= elementsToRead
-
- return elementsToRead
- }
-
- mutating func write(
- contentsOf buffer: UnsafeMutableBufferPointer
- ) -> Bool {
- self.write(contentsOf: UnsafeBufferPointer(buffer))
- }
-
- mutating func write(
- contentsOf buffer: UnsafeBufferPointer
- ) -> Bool {
- let elementsToWrite = buffer.count
-
- // Writing 0 elements is a no-op.
- guard elementsToWrite > 0 else { return true }
- // Writing more than the available capacity is an error.
- guard elementsToWrite <= self.availableCapacity else { return false }
-
- // Write the initial elements to the end of the ring buffer.
- let elementsUntilEnd = self.capacity - self.writerIndex
- let elementsToWriteFirstHalf = min(elementsUntilEnd, elementsToWrite)
- (self.storage.baseAddress! + self.writerIndex).update(
- from: buffer.baseAddress!,
- count: elementsToWriteFirstHalf)
- self.writerIndex += elementsToWriteFirstHalf
-
- // Update the writer index to wrap if needed.
- if self.writerIndex == self.capacity {
- self.writerIndex = 0
- }
-
- // Write the remaining elements to the beginning of the ring buffer.
- let elementsToWriteSecondHalf = elementsToWrite - elementsToWriteFirstHalf
- precondition(elementsToWriteSecondHalf >= 0)
- (self.storage.baseAddress! + self.writerIndex).update(
- from: buffer.baseAddress! + elementsToWriteFirstHalf,
- count: elementsToWriteSecondHalf)
- self.writerIndex += elementsToWriteSecondHalf
-
- // Update bookkeeping with the new count.
- self.count += elementsToWrite
-
- return true
- }
-}
diff --git a/harmony/Sources/Audio/SpinLock.swift b/harmony/Sources/Audio/SpinLock.swift
deleted file mode 100644
index 8212f04f..00000000
--- a/harmony/Sources/Audio/SpinLock.swift
+++ /dev/null
@@ -1,38 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-struct SpinLock: ~Copyable {
- var _lock: UnsafeMutablePointer
- var value: Value
-
- init(index: Int, initialValue: consuming Value) {
- self._lock = spin_lock_init(UInt32(index))
- self.value = initialValue
- }
-}
-
-extension SpinLock where Value: ~Copyable {
- func lock() -> UInt32 {
- spin_lock_blocking(self._lock)
- }
-
- func unlock(irq_mask: UInt32) {
- spin_unlock(self._lock, irq_mask)
- }
-
- mutating func withLock(
- _ body: (inout Value) throws(Error) -> Result
- ) throws(Error) -> Result where Result: ~Copyable {
- let irq_mask = self.lock()
- defer { self.unlock(irq_mask: irq_mask) }
- return try body(&self.value)
- }
-}
diff --git a/harmony/Sources/Audio/TPA2016D2.swift b/harmony/Sources/Audio/TPA2016D2.swift
deleted file mode 100644
index 59b56640..00000000
--- a/harmony/Sources/Audio/TPA2016D2.swift
+++ /dev/null
@@ -1,118 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-func i2c_init() {
- i2c_init(&i2c0_inst, 100 * 1000) // 400kHz "Fast Mode"
- gpio_set_function(UInt32(PICO_DEFAULT_I2C_SDA_PIN), GPIO_FUNC_I2C)
- gpio_set_function(UInt32(PICO_DEFAULT_I2C_SCL_PIN), GPIO_FUNC_I2C)
- gpio_pull_up(UInt32(PICO_DEFAULT_I2C_SDA_PIN))
- gpio_pull_up(UInt32(PICO_DEFAULT_I2C_SCL_PIN))
-
- // I2C reserves some addresses for special purposes. We exclude these from the scan.
- // These are any addresses of the form 000 0xxx or 111 1xxx
- func reserved_addr(_ addr: UInt8) -> Bool{
- return (addr & 0x78) == 0 || (addr & 0x78) == 0x78
- }
-
- log("\nI2C Bus Scan")
- log(" 0 1 2 3 4 5 6 7 8 9 A B C D E F")
- for addr in UInt8(0) ..< (1 << 7) {
- if addr.isMultiple(of: 16) {
- log("\(addr >> 4) ", terminator: "")
- }
-
- // Perform a 1-byte dummy read from the probe address. If a slave
- // acknowledges this address, the function returns the number of bytes
- // transferred. If the address byte is ignored, the function returns
- // -1.
-
- // Skip over any reserved addresses.
- var rxdata: UInt8 = 0
- let ret = if reserved_addr(addr) {
- Int32(PICO_ERROR_GENERIC.rawValue)
- } else {
- i2c_read_blocking(&i2c0_inst, addr, &rxdata, 1, false)
- }
-
- log(ret < 0 ? "." : "@", terminator: addr % 16 == 15 ? "\n" : " ")
- }
- log("Done.\n")
-}
-
-struct TPA2016D2: ~Copyable {
- static let address: UInt8 = 0x58 // 7 bit address
- static let IC_FUNCTION_CONTROL: UInt8 = 0x1
- static let AGC_ATTACK_CONTROL: UInt8 = 0x2
- static let AGC_RELEASE_CONTROL: UInt8 = 0x3
- static let AGC_HOLD_TIME_CONTROL: UInt8 = 0x4
- static let AGC_FIXED_GAIN_CONTROL: UInt8 = 0x5
- static let AGC_CONTROL_0: UInt8 = 0x6
- static let AGC_CONTROL_1: UInt8 = 0x7
-
- var i2c: i2c_inst_t
-
- init(i2c: i2c_inst_t) {
- self.i2c = i2c
-
- for r in UInt8(0x1) ... 0x7 {
- log("Register \(hex: r); read \(hex: self.read(address: r))")
- }
-
- // Immediately configure the amp to our desired defaults.
- // Disable AGC (Automatic Gain Control).
- self.write(address: Self.AGC_CONTROL_1, value: 0x0)
- // Disable Output Limiter
- self.write(address: Self.AGC_CONTROL_0, value: 1 << 7)
- // Set the attack time to the fastest setting (0.1067 ms per step)
- self.write(address: Self.AGC_ATTACK_CONTROL, value: 1)
- // Set the release time to the fastest setting (0.0137 s per step)
- self.write(address: Self.AGC_RELEASE_CONTROL, value: 1)
- // Disable the hold time entirely
- self.write(address: Self.AGC_HOLD_TIME_CONTROL, value: 0)
- }
-}
-
-extension TPA2016D2 {
- mutating func write(address: UInt8, value: UInt8) {
- var combined: UInt16 = (UInt16(value) << 8) | UInt16(address)
- let result = i2c_write_blocking(
- &self.i2c,
- Self.address,
- &combined,
- MemoryLayout.size(ofValue: combined),
- false)
- precondition(result == 2, "I2C write failed")
- // log("Register \(hex: address); wrote \(hex: value) - read \(hex: self.read(address: address))")
- }
-
- mutating func read(address: UInt8) -> UInt8 {
- var data = address
- let writeResult = i2c_write_blocking(&self.i2c, Self.address, &data, 1, false)
- precondition(writeResult == 1, "I2C write failed")
- let readResult = i2c_read_blocking(&self.i2c, Self.address, &data, 1, false)
- precondition(readResult == 1, "I2C read failed")
- return data
- }
-}
-
-extension TPA2016D2 {
- // scale from 0 to 255
- mutating func set(gain: UInt8) {
- precondition(0 <= gain && gain <= 30)
- self.write(address: Self.AGC_FIXED_GAIN_CONTROL, value: gain)
- }
-
- mutating func mute(_ mute: Bool) {
- var value = self.read(address: Self.IC_FUNCTION_CONTROL)
- value = mute ? value | (1 << 5) : value & ~(1 << 5)
- self.write(address: Self.IC_FUNCTION_CONTROL, value: value)
- }
-}
diff --git a/harmony/Sources/Audio/Timer.swift b/harmony/Sources/Audio/Timer.swift
deleted file mode 100644
index acad420a..00000000
--- a/harmony/Sources/Audio/Timer.swift
+++ /dev/null
@@ -1,20 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-// struct Timer: ~Copyable, ~Escapable {
-// var context: UnsafePointer
-
-// init(context: borrowing Context) dependsOn(context) {
-// withUnsafePointerToInstance(context) { context in
-// self.context = context
-// }
-// }
-// }
\ No newline at end of file
diff --git a/harmony/Sources/Bluetooth/A2DP.swift b/harmony/Sources/Bluetooth/A2DP.swift
deleted file mode 100644
index ebb257f6..00000000
--- a/harmony/Sources/Bluetooth/A2DP.swift
+++ /dev/null
@@ -1,300 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-// Advanced Audio Distribution Profile
-
-struct MediaCodecConfigurationSBC {
- var reconfigure: UInt8
- var num_channels: UInt8
- var sampling_frequency: UInt16
- var block_length: UInt8
- var subbands: UInt8
- var min_bitpool_value: UInt8
- var max_bitpool_value: UInt8
- var channel_mode: btstack_sbc_channel_mode_t
- var allocation_method: btstack_sbc_allocation_method_t
-
- init() {
- self.reconfigure = 0
- self.num_channels = 0
- self.sampling_frequency = 0
- self.block_length = 0
- self.subbands = 0
- self.min_bitpool_value = 0
- self.max_bitpool_value = 0
- self.channel_mode = SBC_CHANNEL_MODE_MONO
- self.allocation_method = SBC_ALLOCATION_METHOD_LOUDNESS
- }
-
- func dump() {
- log(
- """
- - num_channels: \(self.num_channels)
- - sampling_frequency: \(self.sampling_frequency)
- - channel_mode: \(self.channel_mode.rawValue)
- - block_length: \(self.block_length)
- - subbands: \(self.subbands)
- - allocation_method: \(self.allocation_method.rawValue)
- - bitpool_value [\(self.min_bitpool_value), \(self.max_bitpool_value)]
- """)
- }
-}
-
-enum StreamState {
- case closed
- case open
- case playing
- case paused
-}
-
-struct A2DPConnection {
- static var shared = Self()
-
- var addr: bd_addr_t = (0, 0, 0, 0, 0, 0)
- var a2dp_cid: UInt16 = 0
- var a2dp_local_seid: UInt8 = 0
- var stream_state: StreamState = .closed
- var sbc_configuration: MediaCodecConfigurationSBC = .init()
-}
-
-@_cdecl("a2dp_sink_packet_handler")
-func a2dp_sink_packet_handler(
- packet_type: UInt8,
- channel: UInt16,
- packet: UnsafeMutablePointer?,
- size: UInt16
-) {
- guard packet_type == HCI_EVENT_PACKET else { return }
- guard hci_event_packet_get_type(packet) == HCI_EVENT_A2DP_META else { return }
-
- let subevent = packet?[2]
- switch subevent {
- case UInt8(A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION):
- log("A2DP Sink : Received non SBC codec - not implemented")
-
- case UInt8(A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION):
- log("A2DP Sink : Received SBC codec configuration")
- A2DPConnection.shared.sbc_configuration.reconfigure =
- a2dp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure(
- packet)
- A2DPConnection.shared.sbc_configuration.num_channels =
- a2dp_subevent_signaling_media_codec_sbc_configuration_get_num_channels(
- packet)
- A2DPConnection.shared.sbc_configuration.sampling_frequency =
- a2dp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency(
- packet)
- A2DPConnection.shared.sbc_configuration.block_length =
- a2dp_subevent_signaling_media_codec_sbc_configuration_get_block_length(
- packet)
- A2DPConnection.shared.sbc_configuration.subbands =
- a2dp_subevent_signaling_media_codec_sbc_configuration_get_subbands(packet)
- A2DPConnection.shared.sbc_configuration.min_bitpool_value =
- a2dp_subevent_signaling_media_codec_sbc_configuration_get_min_bitpool_value(
- packet)
- A2DPConnection.shared.sbc_configuration.max_bitpool_value =
- a2dp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value(
- packet)
-
- let allocation_method =
- a2dp_subevent_signaling_media_codec_sbc_configuration_get_allocation_method(
- packet)
-
- // Adapt Bluetooth spec definition to SBC Encoder expected input
- A2DPConnection.shared.sbc_configuration.allocation_method =
- (btstack_sbc_allocation_method_t)(allocation_method - 1)
-
- switch avdtp_channel_mode_t(
- a2dp_subevent_signaling_media_codec_sbc_configuration_get_channel_mode(
- packet))
- {
- case AVDTP_CHANNEL_MODE_JOINT_STEREO:
- A2DPConnection.shared.sbc_configuration.channel_mode =
- SBC_CHANNEL_MODE_JOINT_STEREO
- case AVDTP_CHANNEL_MODE_STEREO:
- A2DPConnection.shared.sbc_configuration.channel_mode =
- SBC_CHANNEL_MODE_STEREO
- case AVDTP_CHANNEL_MODE_DUAL_CHANNEL:
- A2DPConnection.shared.sbc_configuration.channel_mode =
- SBC_CHANNEL_MODE_DUAL_CHANNEL
- case AVDTP_CHANNEL_MODE_MONO:
- A2DPConnection.shared.sbc_configuration.channel_mode =
- SBC_CHANNEL_MODE_MONO
- default:
- fatalError()
- }
- A2DPConnection.shared.sbc_configuration.dump()
-
- case UInt8(A2DP_SUBEVENT_STREAM_ESTABLISHED):
- let status = a2dp_subevent_stream_established_get_status(packet)
- guard status == ERROR_CODE_SUCCESS else {
- log(
- "A2DP Sink : Streaming connection failed, status \(hex: status)"
- )
- return
- }
-
- a2dp_subevent_stream_established_get_bd_addr(
- packet, &A2DPConnection.shared.addr)
- A2DPConnection.shared.a2dp_cid =
- a2dp_subevent_stream_established_get_a2dp_cid(packet)
- A2DPConnection.shared.a2dp_local_seid =
- a2dp_subevent_stream_established_get_local_seid(packet)
- A2DPConnection.shared.stream_state = .open
-
- log(
- "A2DP Sink : Streaming connection is established, address \(cString: bd_addr_to_str(&A2DPConnection.shared.addr)), cid \(hex: A2DPConnection.shared.a2dp_cid), local seid \(A2DPConnection.shared.a2dp_local_seid)"
- )
-
- #if ENABLE_AVDTP_ACCEPTOR_EXPLICIT_START_STREAM_CONFIRMATION
- case UInt8(A2DP_SUBEVENT_START_STREAM_REQUESTED):
- log(
- "A2DP Sink : Explicit Accept to start stream, local_seid %d\n",
- a2dp_subevent_start_stream_requested_get_local_seid(packet))
- a2dp_sink_start_stream_accept(a2dp_cid, a2dp_local_seid)
- #endif
-
- case UInt8(A2DP_SUBEVENT_STREAM_STARTED):
- log("A2DP Sink : Stream started")
- A2DPConnection.shared.stream_state = .playing
- if A2DPConnection.shared.sbc_configuration.reconfigure != 0 {
- Application.shared.audioEngine.close()
- }
- // prepare media processing
- // audio playback starts when buffer reaches minimal level
- Application.shared.audioEngine.`init`(A2DPConnection.shared.sbc_configuration)
-
- case UInt8(A2DP_SUBEVENT_STREAM_SUSPENDED):
- log("A2DP Sink : Stream paused")
- A2DPConnection.shared.stream_state = .paused
- Application.shared.audioEngine.pause()
-
- case UInt8(A2DP_SUBEVENT_STREAM_RELEASED):
- log("A2DP Sink : Stream released")
- A2DPConnection.shared.stream_state = .closed
- Application.shared.audioEngine.close()
-
- case UInt8(A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED):
- log("A2DP Sink : Signaling connection released")
- A2DPConnection.shared.a2dp_cid = 0
- Application.shared.audioEngine.close()
-
- default:
- log("AVRCP Sink : Event \(hex: subevent ?? 0xff) is not parsed")
- }
-}
-
-/* @section Handle Media Data Packet
- *
- * @text Here the audio data, are received through the a2dp_sink_media_handler callback.
- * Currently, only the SBC media codec is supported. Hence, the media data consists of the media packet header and the SBC packet.
- * The SBC frame will be stored in a ring buffer for later processing (instead of decoding it to PCM right away which would require a much larger buffer).
- * If the audio stream wasn't started already and there are enough SBC frames in the ring buffer, start playback.
- */
-
-func read_media_data_header(
- _ packet: UnsafeMutablePointer?,
- _ size: Int32,
- _ offset: UnsafeMutablePointer,
- _ media_header: UnsafeMutablePointer
-) -> Bool {
- guard let packet else { return false }
- let media_header_len: Int32 = 12 // without crc
- var pos = Int(offset.pointee)
-
- if size - Int32(pos) < media_header_len {
- log(
- "Not enough data to read media packet header, expected \(media_header_len), received \(size-Int32(pos))"
- )
- return false
- }
-
- media_header.pointee.version = packet[pos] & 0x03
- media_header.pointee.padding = UInt8(get_bit16(UInt16(packet[pos]), 2))
- media_header.pointee.extension = UInt8(get_bit16(UInt16(packet[pos]), 3))
- media_header.pointee.csrc_count = (packet[pos] >> 4) & 0x0F
- pos += 1
-
- media_header.pointee.marker = UInt8(get_bit16(UInt16(packet[pos]), 0))
- media_header.pointee.payload_type = (packet[pos] >> 1) & 0x7F
- pos += 1
-
- media_header.pointee.sequence_number = UInt16(
- big_endian_read_16(packet, Int32(pos)))
- pos += 2
-
- media_header.pointee.timestamp = big_endian_read_32(packet, Int32(pos))
- pos += 4
-
- media_header.pointee.synchronization_source = big_endian_read_32(
- packet, Int32(pos))
- pos += 4
- offset.pointee = Int32(pos)
- return true
-}
-
-func read_sbc_header(
- _ packet: UnsafeMutablePointer?,
- _ size: Int32,
- _ offset: UnsafeMutablePointer,
- _ sbc_header: UnsafeMutablePointer
-) -> Bool {
- guard let packet else { return false }
- let sbc_header_len: Int32 = 12 // without crc
- var pos: Int32 = offset.pointee
-
- if size - pos < sbc_header_len {
- log(
- "Not enough data to read SBC header, expected \(sbc_header_len), received \(size-pos)"
- )
- return false
- }
-
- sbc_header.pointee.fragmentation = UInt8(
- get_bit16(UInt16(packet[Int(pos)]), 7))
- sbc_header.pointee.starting_packet = UInt8(
- get_bit16(UInt16(packet[Int(pos)]), 6))
- sbc_header.pointee.last_packet = UInt8(get_bit16(UInt16(packet[Int(pos)]), 5))
- sbc_header.pointee.num_frames = UInt8(packet[Int(pos)] & 0x0f)
- pos += 1
- offset.pointee = pos
- return true
-}
-
-@_cdecl("a2dp_sink_media_handler")
-func a2dp_sink_media_handler(
- seid: UInt8,
- packet: UnsafeMutablePointer?,
- size: UInt16
-) {
- var pos: Int32 = 0
-
- var media_header = avdtp_media_packet_header_t()
- guard read_media_data_header(packet, Int32(size), &pos, &media_header) else {
- log("Failed to read media data header")
- return
- }
-
- var sbc_header = avdtp_sbc_codec_header_t()
- guard read_sbc_header(packet, Int32(size), &pos, &sbc_header) else {
- log("Failed to read SBC header")
- return
- }
-
- let packet_length = UInt32(size) - UInt32(pos)
- let packet_begin = packet?.advanced(by: Int(pos))
- let sbc_frame_size = Int(packet_length / UInt32(sbc_header.num_frames))
-
- let packetBuffer = UnsafeMutableBufferPointer(
- start: packet_begin, count: Int(packet_length))
- Application.shared.audioEngine.audio_pico.enqueue(
- sbc_frames: packetBuffer, frame_size: sbc_frame_size)
- Application.shared.audioEngine.start()
-}
diff --git a/harmony/Sources/Bluetooth/AVRCP.swift b/harmony/Sources/Bluetooth/AVRCP.swift
deleted file mode 100644
index 6f5bc44a..00000000
--- a/harmony/Sources/Bluetooth/AVRCP.swift
+++ /dev/null
@@ -1,286 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-// Audio/Video Remote Control Profile
-
-struct AVRCPConnection {
- var addr: bd_addr_t
- var avrcp_cid: UInt16
- var playing: Bool
- var notifications_supported_by_target: UInt16
-}
-
-var avrcp_connection = AVRCPConnection(
- addr: (0, 0, 0, 0, 0, 0),
- avrcp_cid: 0,
- playing: false,
- notifications_supported_by_target: 0)
-
-@_cdecl("avrcp_packet_handler")
-func avrcp_packet_handler(
- packet_type: UInt8,
- channel: UInt16,
- packet: UnsafeMutablePointer?,
- size: UInt16
-) {
- guard packet_type == HCI_EVENT_PACKET else { return }
- guard hci_event_packet_get_type(packet) == HCI_EVENT_AVRCP_META else {
- return
- }
-
- let subevent = packet?[2]
- switch subevent {
- case UInt8(AVRCP_SUBEVENT_CONNECTION_ESTABLISHED):
- log("AVRCP_SUBEVENT_CONNECTION_ESTABLISHED")
- let local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet)
- let status = avrcp_subevent_connection_established_get_status(packet)
-
- if status != ERROR_CODE_SUCCESS {
- log("AVRCP: Connection failed, status \(hex: status)")
- avrcp_connection.avrcp_cid = 0
- return
- }
-
- avrcp_connection.avrcp_cid = local_cid
- var address: bd_addr_t = (0, 0, 0, 0, 0, 0)
- avrcp_subevent_connection_established_get_bd_addr(packet, &address)
- log(
- "AVRCP: Connected to \(cString: bd_addr_to_str(&address)), cid \(hex: avrcp_connection.avrcp_cid)"
- )
-
- avrcp_target_support_event(
- avrcp_connection.avrcp_cid, AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED)
- avrcp_target_support_event(
- avrcp_connection.avrcp_cid, AVRCP_NOTIFICATION_EVENT_BATT_STATUS_CHANGED)
- let battery_status = AVRCP_BATTERY_STATUS_WARNING
- avrcp_target_battery_status_changed(
- avrcp_connection.avrcp_cid, battery_status)
-
- // query supported events:
- avrcp_controller_get_supported_events(avrcp_connection.avrcp_cid)
-
- case UInt8(AVRCP_SUBEVENT_CONNECTION_RELEASED):
- log("AVRCP_SUBEVENT_CONNECTION_RELEASED")
- log(
- "AVRCP: Channel released: cid \(hex: avrcp_subevent_connection_released_get_avrcp_cid(packet))"
- )
- avrcp_connection.avrcp_cid = 0
- avrcp_connection.notifications_supported_by_target = 0
-
- default:
- log("AVRCP: Event \(hex: subevent ?? 0xff) is not parsed")
- }
-}
-
-@_cdecl("avrcp_controller_packet_handler")
-func avrcp_controller_packet_handler(
- packet_type: UInt8,
- channel: UInt16,
- packet: UnsafeMutablePointer?,
- size: UInt16
-) {
- guard packet_type == HCI_EVENT_PACKET else { return }
- guard hci_event_packet_get_type(packet) == HCI_EVENT_AVRCP_META else {
- return
- }
- guard avrcp_connection.avrcp_cid != 0 else { return }
-
- let subevent = packet?[2]
- switch subevent {
- case UInt8(AVRCP_SUBEVENT_GET_CAPABILITY_EVENT_ID):
- avrcp_connection.notifications_supported_by_target |=
- (1 << avrcp_subevent_get_capability_event_id_get_event_id(packet))
-
- case UInt8(AVRCP_SUBEVENT_GET_CAPABILITY_EVENT_ID_DONE):
- log("AVRCP Controller: supported notifications by target:")
- for event_id in UInt8(
- AVRCP_NOTIFICATION_EVENT_FIRST_INDEX.rawValue).. 0 else { break }
- let avrcp_subevent_value = UnsafeBufferPointer(
- start: avrcp_subevent_now_playing_title_info_get_value(packet),
- count: Int(count))
- log("AVRCP Controller: Title \(cString: avrcp_subevent_value)")
-
- case UInt8(AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO):
- let count = avrcp_subevent_now_playing_artist_info_get_value_len(packet)
- guard count > 0 else { break }
- let avrcp_subevent_value = UnsafeBufferPointer(
- start: avrcp_subevent_now_playing_artist_info_get_value(packet),
- count: Int(count))
- log("AVRCP Controller: Artist \(cString: avrcp_subevent_value)")
-
- case UInt8(AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO):
- let count = avrcp_subevent_now_playing_album_info_get_value_len(packet)
- guard count > 0 else { break }
- let avrcp_subevent_value = UnsafeBufferPointer(
- start: avrcp_subevent_now_playing_album_info_get_value(packet),
- count: Int(count))
- log("AVRCP Controller: Album \(cString: avrcp_subevent_value)")
-
- case UInt8(AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO):
- let count = avrcp_subevent_now_playing_genre_info_get_value_len(packet)
- guard count > 0 else { break }
- let avrcp_subevent_value = UnsafeBufferPointer(
- start: avrcp_subevent_now_playing_genre_info_get_value(packet),
- count: Int(count))
- log("AVRCP Controller: Genre \(cString: avrcp_subevent_value)")
-
- case UInt8(AVRCP_SUBEVENT_PLAY_STATUS):
- let songLength = avrcp_subevent_play_status_get_song_length(packet)
- let songPosition = avrcp_subevent_play_status_get_song_position(packet)
- let playStatus = avrcp_play_status2str(
- avrcp_subevent_play_status_get_play_status(packet))
- log(
- "AVRCP Controller: Song length \(songLength) ms, Song position \(songPosition) ms, Play status \(cString: playStatus)"
- )
-
- case UInt8(AVRCP_SUBEVENT_OPERATION_COMPLETE):
- let operationId = avrcp_operation2str(
- avrcp_subevent_operation_complete_get_operation_id(packet))
- log("AVRCP Controller: \(cString: operationId) complete")
-
- case UInt8(AVRCP_SUBEVENT_OPERATION_START):
- let operationId = avrcp_operation2str(
- avrcp_subevent_operation_start_get_operation_id(packet))
- log("AVRCP Controller: \(cString: operationId) start")
-
- case UInt8(AVRCP_SUBEVENT_NOTIFICATION_EVENT_TRACK_REACHED_END):
- log("AVRCP Controller: Track reached end")
-
- case UInt8(AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE):
- let commandType = avrcp_ctype2str(
- avrcp_subevent_player_application_value_response_get_command_type(packet))
- log("AVRCP Controller: Set Player App Value \(cString: commandType)")
-
- default:
- break
- }
-}
-
-@_cdecl("avrcp_target_packet_handler")
-func avrcp_target_packet_handler(
- packet_type: UInt8,
- channel: UInt16,
- packet: UnsafeMutablePointer?,
- size: UInt16
-) {
- guard packet_type == HCI_EVENT_PACKET else { return }
- guard hci_event_packet_get_type(packet) == HCI_EVENT_AVRCP_META else {
- return
- }
-
- let subevent = packet?[2]
- switch subevent {
- case UInt8(AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED):
- let volume = avrcp_subevent_notification_volume_changed_get_absolute_volume(
- packet)
- log("AVRCP Target : Volume set to [\(volume) / 127]")
- Application.shared.audioEngine.set(volume: volume << 1)
-
- case UInt8(AVRCP_SUBEVENT_OPERATION):
- let operation_id = avrcp_operation_id_t(
- avrcp_subevent_operation_get_operation_id(packet))
- let button_state: StaticString =
- avrcp_subevent_operation_get_button_pressed(packet) > 0
- ? "PRESS" : "RELEASE"
- switch operation_id {
- case AVRCP_OPERATION_ID_VOLUME_UP:
- log("AVRCP Target : VOLUME UP (\(button_state))")
- case AVRCP_OPERATION_ID_VOLUME_DOWN:
- log("AVRCP Target : VOLUME DOWN (\(button_state))")
- default:
- return
- }
-
- default:
- log("AVRCP Target : Event \(hex: subevent ?? 0xff) is not parsed")
- }
-}
diff --git a/harmony/Sources/Bluetooth/HCI.swift b/harmony/Sources/Bluetooth/HCI.swift
deleted file mode 100644
index e025e281..00000000
--- a/harmony/Sources/Bluetooth/HCI.swift
+++ /dev/null
@@ -1,30 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-// Host Controller Interface
-
-@_cdecl("hci_packet_handler")
-func hci_packet_handler(
- packet_type: UInt8,
- channel: UInt16,
- packet: UnsafeMutablePointer?,
- size: UInt16
-) {
- guard packet_type == HCI_EVENT_PACKET else { return }
- guard hci_event_packet_get_type(packet) == HCI_EVENT_PIN_CODE_REQUEST else {
- return
- }
-
- var address: bd_addr_t = (0, 0, 0, 0, 0, 0)
- log("Pin code request - using '0000'")
- hci_event_pin_code_request_get_bd_addr(packet, &address)
- gap_pin_code_response(&address, "0000")
-}
diff --git a/harmony/Sources/Bluetooth/SBC.swift b/harmony/Sources/Bluetooth/SBC.swift
deleted file mode 100644
index 2b9a3093..00000000
--- a/harmony/Sources/Bluetooth/SBC.swift
+++ /dev/null
@@ -1,61 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-enum SBCDecoder {
- typealias Callback = (
- _ data: UnsafeMutableBufferPointer,
- _ num_channels: Int32,
- _ sample_rate: Int32
- ) -> Void
-
- static var context = btstack_sbc_decoder_bluedroid_t()
- static var instance: UnsafePointer? = nil
- static var callback: Callback? = nil
-
- static func configure(mode: btstack_sbc_mode_t) {
- self.instance = btstack_sbc_decoder_bluedroid_init_instance(&context)
-
- func decode_callback(
- _ data: UnsafeMutablePointer?,
- _ num_samples: Int32,
- _ num_channels: Int32,
- _ sample_rate: Int32,
- _ context: UnsafeMutableRawPointer?
- ) {
- let data = UnsafeMutableBufferPointer(
- start: data, count: Int(num_samples))
- Self.callback?(data, num_channels, sample_rate)
- }
-
- instance?.pointee.configure(&context, mode, decode_callback, nil)
- }
-
- static func decode_signed_16(
- mode: btstack_sbc_mode_t,
- packet_status_flag: UInt8,
- buffer: UnsafeRawBufferPointer,
- callback: Callback
- ) {
- guard let instance = Self.instance else {
- preconditionFailure("Must call configure prior to decode_signed_16")
- }
-
- return withoutActuallyEscaping(callback) {
- Self.callback = $0
- instance.pointee.decode_signed_16(
- &Self.context,
- packet_status_flag,
- buffer.baseAddress,
- UInt16(buffer.count))
- Self.callback = nil
- }
- }
-}
diff --git a/harmony/Sources/Bluetooth/SDP.swift b/harmony/Sources/Bluetooth/SDP.swift
deleted file mode 100644
index d7d37fac..00000000
--- a/harmony/Sources/Bluetooth/SDP.swift
+++ /dev/null
@@ -1,50 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-// Service Discovery Protocol
-
-struct ServiceDiscoveryProtocol: ~Copyable {
- typealias ServiceRecord = UnsafePointer
- typealias ServiceRecordHandle = UInt32
-
- init() {
- sdp_init()
- }
-
- deinit {
- sdp_deinit()
- }
-
- mutating func registerService(record: ServiceRecord) {
- precondition(sdp_register_service(record) == 0)
- }
-
- mutating func registerService(record: UnsafeMutableRawBufferPointer) {
- precondition(de_get_len(record.baseAddress) <= record.count)
- precondition(sdp_register_service(record.baseAddress) == 0)
- }
-
- mutating func unregisterService(handle: ServiceRecordHandle) {
- sdp_unregister_service(handle)
- }
-
- mutating func getServiceRecordHandle(for record: ServiceRecord) -> ServiceRecordHandle {
- sdp_get_service_record_handle(record)
- }
-
- mutating func makeServiceRecordHandle() -> ServiceRecordHandle {
- sdp_create_service_record_handle()
- }
-
- mutating func getServiceRecord(for handle: ServiceRecordHandle) -> ServiceRecord? {
- ServiceRecord(sdp_get_record_for_handle(handle))
- }
-}
diff --git a/harmony/Sources/PIOPrograms/I2S.pio b/harmony/Sources/PIOPrograms/I2S.pio
deleted file mode 100644
index 7b9ab6ec..00000000
--- a/harmony/Sources/PIOPrograms/I2S.pio
+++ /dev/null
@@ -1,64 +0,0 @@
-;
-; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
-;
-; SPDX-License-Identifier: BSD-3-Clause
-;
-
-; Transmit a mono or stereo I2S audio stream as stereo
-; This is 16 bits per sample; can be altered by modifying the "set" params,
-; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
-;
-; Autopull must be enabled, with threshold set to 32.
-; Since I2S is MSB-first, shift direction should be to left.
-; Hence the format of the FIFO word is:
-;
-; | 31 : 16 | 15 : 0 |
-; | sample ws=0 | sample ws=1 |
-;
-; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
-; Fractional divider will probably be needed to get correct bit clock period,
-; but for common syslck freqs this should still give a constant word select period.
-;
-; One output pin is used for the data output.
-; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
-
-; Send 16 bit words to the PIO for mono, 32 bit words for stereo
-
-.program audio_i2s
-.side_set 2
-
- ; /--- LRCLK
- ; |/-- BCLK
-bitloop1: ; ||
- out pins, 1 side 0b10
- jmp x-- bitloop1 side 0b11
- out pins, 1 side 0b00
- set x, 14 side 0b01
-
-bitloop0:
- out pins, 1 side 0b00
- jmp x-- bitloop0 side 0b01
- out pins, 1 side 0b10
-public entry_point:
- set x, 14 side 0b11
-
-% c-sdk {
-
-static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) {
- pio_sm_config sm_config = audio_i2s_program_get_default_config(offset);
-
- sm_config_set_out_pins(&sm_config, data_pin, 1);
- sm_config_set_sideset_pins(&sm_config, clock_pin_base);
- sm_config_set_out_shift(&sm_config, false, true, 32);
- sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
-
- pio_sm_init(pio, sm, offset, &sm_config);
-
- uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
- pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
- pio_sm_set_pins(pio, sm, 0); // clear pins
-
- pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point));
-}
-
-%}
diff --git a/harmony/Sources/PIOPrograms/QuadratureEncoder.pio b/harmony/Sources/PIOPrograms/QuadratureEncoder.pio
deleted file mode 100644
index 37ed3948..00000000
--- a/harmony/Sources/PIOPrograms/QuadratureEncoder.pio
+++ /dev/null
@@ -1,148 +0,0 @@
-
-// FROM: https://github.com/raspberrypi/pico-examples/blob/master/pio/quadrature_encoder/quadrature_encoder.pio
-
-;
-; Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
-;
-; SPDX-License-Identifier: BSD-3-Clause
-;
-.pio_version 0 // only requires PIO version 0
-
-.program quadrature_encoder
-
-; the code must be loaded at address 0, because it uses computed jumps
-.origin 0
-
-
-; the code works by running a loop that continuously shifts the 2 phase pins into
-; ISR and looks at the lower 4 bits to do a computed jump to an instruction that
-; does the proper "do nothing" | "increment" | "decrement" action for that pin
-; state change (or no change)
-
-; ISR holds the last state of the 2 pins during most of the code. The Y register
-; keeps the current encoder count and is incremented / decremented according to
-; the steps sampled
-
-; the program keeps trying to write the current count to the RX FIFO without
-; blocking. To read the current count, the user code must drain the FIFO first
-; and wait for a fresh sample (takes ~4 SM cycles on average). The worst case
-; sampling loop takes 10 cycles, so this program is able to read step rates up
-; to sysclk / 10 (e.g., sysclk 125MHz, max step rate = 12.5 Msteps/sec)
-
-; 00 state
- JMP update ; read 00
- JMP decrement ; read 01
- JMP increment ; read 10
- JMP update ; read 11
-
-; 01 state
- JMP increment ; read 00
- JMP update ; read 01
- JMP update ; read 10
- JMP decrement ; read 11
-
-; 10 state
- JMP decrement ; read 00
- JMP update ; read 01
- JMP update ; read 10
- JMP increment ; read 11
-
-; to reduce code size, the last 2 states are implemented in place and become the
-; target for the other jumps
-
-; 11 state
- JMP update ; read 00
- JMP increment ; read 01
-decrement:
- ; note: the target of this instruction must be the next address, so that
- ; the effect of the instruction does not depend on the value of Y. The
- ; same is true for the "JMP X--" below. Basically "JMP Y--, "
- ; is just a pure "decrement Y" instruction, with no other side effects
- JMP Y--, update ; read 10
-
- ; this is where the main loop starts
-.wrap_target
-update:
- MOV ISR, Y ; read 11
- PUSH noblock
-
-sample_pins:
- ; we shift into ISR the last state of the 2 input pins (now in OSR) and
- ; the new state of the 2 pins, thus producing the 4 bit target for the
- ; computed jump into the correct action for this state. Both the PUSH
- ; above and the OUT below zero out the other bits in ISR
- OUT ISR, 2
- IN PINS, 2
-
- ; save the state in the OSR, so that we can use ISR for other purposes
- MOV OSR, ISR
- ; jump to the correct state machine action
- MOV PC, ISR
-
- ; the PIO does not have a increment instruction, so to do that we do a
- ; negate, decrement, negate sequence
-increment:
- MOV Y, ~Y
- JMP Y--, increment_cont
-increment_cont:
- MOV Y, ~Y
-.wrap ; the .wrap here avoids one jump instruction and saves a cycle too
-
-
-
-% c-sdk {
-
-#include "hardware/clocks.h"
-#include "hardware/gpio.h"
-
-// max_step_rate is used to lower the clock of the state machine to save power
-// if the application doesn't require a very high sampling rate. Passing zero
-// will set the clock to the maximum
-
-static inline void quadrature_encoder_program_init(PIO pio, uint sm, uint pin, int max_step_rate)
-{
- pio_sm_set_consecutive_pindirs(pio, sm, pin, 2, false);
- pio_gpio_init(pio, pin);
- pio_gpio_init(pio, pin + 1);
-
- gpio_pull_up(pin);
- gpio_pull_up(pin + 1);
-
- pio_sm_config c = quadrature_encoder_program_get_default_config(0);
-
- sm_config_set_in_pins(&c, pin); // for WAIT, IN
- sm_config_set_jmp_pin(&c, pin); // for JMP
- // shift to left, autopull disabled
- sm_config_set_in_shift(&c, false, false, 32);
- // don't join FIFO's
- sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_NONE);
-
- // passing "0" as the sample frequency,
- if (max_step_rate == 0) {
- sm_config_set_clkdiv(&c, 1.0);
- } else {
- // one state machine loop takes at most 10 cycles
- float div = (float)clock_get_hz(clk_sys) / (10 * max_step_rate);
- sm_config_set_clkdiv(&c, div);
- }
-
- pio_sm_init(pio, sm, 0, &c);
- pio_sm_set_enabled(pio, sm, true);
-}
-
-static inline int32_t quadrature_encoder_get_count(PIO pio, uint sm)
-{
- uint ret;
- int n;
-
- // if the FIFO has N entries, we fetch them to drain the FIFO,
- // plus one entry which will be guaranteed to not be stale
- n = pio_sm_get_rx_fifo_level(pio, sm) + 1;
- while (n > 0) {
- ret = pio_sm_get_blocking(pio, sm);
- n--;
- }
- return ret;
-}
-
-%}
\ No newline at end of file
diff --git a/harmony/Sources/PIOPrograms/WS2812.pio b/harmony/Sources/PIOPrograms/WS2812.pio
deleted file mode 100644
index 839ce5f2..00000000
--- a/harmony/Sources/PIOPrograms/WS2812.pio
+++ /dev/null
@@ -1,49 +0,0 @@
-;
-; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
-;
-; SPDX-License-Identifier: BSD-3-Clause
-;
-.pio_version 0 // only requires PIO version 0
-
-.program ws2812
-.side_set 1
-
-; The following constants are selected for broad compatibility with WS2812,
-; WS2812B, and SK6812 LEDs. Other constants may support higher bandwidths for
-; specific LEDs, such as (7,10,8) for WS2812B LEDs.
-
-.define public T1 3
-.define public T2 3
-.define public T3 4
-
-.wrap_target
-bitloop:
- out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
- jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
-do_one:
- jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
-do_zero:
- nop side 0 [T2 - 1] ; Or drive low, for a short pulse
-.wrap
-
-% c-sdk {
-#include "hardware/clocks.h"
-
-static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
-
- pio_gpio_init(pio, pin);
- pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
-
- pio_sm_config c = ws2812_program_get_default_config(offset);
- sm_config_set_sideset_pins(&c, pin);
- sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
- sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
-
- int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
- float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
- sm_config_set_clkdiv(&c, div);
-
- pio_sm_init(pio, sm, offset, &c);
- pio_sm_set_enabled(pio, sm, true);
-}
-%}
diff --git a/harmony/Tests/AudioTests/RingBufferTests.swift b/harmony/Tests/AudioTests/RingBufferTests.swift
deleted file mode 100644
index 70e119e9..00000000
--- a/harmony/Tests/AudioTests/RingBufferTests.swift
+++ /dev/null
@@ -1,177 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-import XCTest
-
-@testable import Core
-
-extension RingBuffer {
- mutating func write(contentsOf array: [Element]) -> Bool {
- array.withUnsafeBufferPointer {
- self.write(contentsOf: $0)
- }
- }
-
- mutating func read(into array: inout [Element], count: Int? = nil) -> Int {
- array.withUnsafeMutableBufferPointer { buffer in
- self.read(into: buffer, count: count)
- }
- }
-
- func assertState(
- count: Int,
- readerIndex: Int,
- writerIndex: Int,
- file: StaticString = #filePath,
- line: UInt = #line
- ) {
- XCTAssertEqual(
- self.availableCapacity, self.capacity - count,
- "incorrect availableCapacity", file: file, line: line)
- XCTAssertEqual(
- self.isEmpty, (count == 0), "incorrect isEmpty", file: file, line: line)
- XCTAssertEqual(
- self.isFull, (count == self.capacity), "incorrect isFull", file: file,
- line: line)
- XCTAssertEqual(self.count, count, "incorrect count", file: file, line: line)
- XCTAssertEqual(
- self.readerIndex, readerIndex, "incorrect readerIndex", file: file,
- line: line)
- XCTAssertEqual(
- self.writerIndex, writerIndex, "incorrect writerIndex", file: file,
- line: line)
- if self.isEmpty || self.isFull {
- XCTAssertEqual(self.readerIndex, self.writerIndex, file: file, line: line)
- }
- }
-}
-
-final class RingBufferTests: XCTestCase {
- func testInitialization() {
- let ringBuffer = RingBuffer(capacity: 10)
- XCTAssertEqual(ringBuffer.capacity, 10)
- ringBuffer.assertState(count: 0, readerIndex: 0, writerIndex: 0)
- }
-
- func testCapacityAndAvailableCapacity() {
- var ringBuffer = RingBuffer(capacity: 5)
- ringBuffer.assertState(count: 0, readerIndex: 0, writerIndex: 0)
-
- XCTAssertTrue(ringBuffer.write(contentsOf: [1, 2, 3]))
- ringBuffer.assertState(count: 3, readerIndex: 0, writerIndex: 3)
-
- XCTAssertTrue(ringBuffer.write(contentsOf: [4, 5]))
- ringBuffer.assertState(count: 5, readerIndex: 0, writerIndex: 0)
- }
-
- func testWriteAndRead() {
- var ringBuffer = RingBuffer(capacity: 5)
-
- // Write data to the buffer
- XCTAssertTrue(ringBuffer.write(contentsOf: [1, 2, 3]))
- ringBuffer.assertState(count: 3, readerIndex: 0, writerIndex: 3)
-
- // Attempt to read from the buffer
- var readBuffer = Array(repeating: 0, count: 3)
- let readCount = ringBuffer.read(into: &readBuffer)
- XCTAssertEqual(readCount, 3)
- XCTAssertEqual(readBuffer, [1, 2, 3])
- ringBuffer.assertState(count: 0, readerIndex: 3, writerIndex: 3)
- }
-
- func testOverwriteBehavior() {
- var ringBuffer = RingBuffer(capacity: 3)
-
- // Fill buffer to capacity
- XCTAssertTrue(ringBuffer.write(contentsOf: [1, 2, 3]))
- ringBuffer.assertState(count: 3, readerIndex: 0, writerIndex: 0)
-
- // Attempt to overwrite when full
- XCTAssertFalse(ringBuffer.write(contentsOf: [4]))
- ringBuffer.assertState(count: 3, readerIndex: 0, writerIndex: 0)
-
- // Read and check if the buffer remains unaltered
- var readBuffer = Array(repeating: 0, count: 3)
- let readCount = ringBuffer.read(into: &readBuffer)
- XCTAssertEqual(readCount, 3)
- XCTAssertEqual(readBuffer, [1, 2, 3])
- ringBuffer.assertState(count: 0, readerIndex: 0, writerIndex: 0)
- }
-
- func testClearBuffer() {
- var ringBuffer = RingBuffer(capacity: 5)
- XCTAssertTrue(ringBuffer.write(contentsOf: [1, 2, 3]))
- ringBuffer.assertState(count: 3, readerIndex: 0, writerIndex: 3)
-
- ringBuffer.clear()
- ringBuffer.assertState(count: 0, readerIndex: 0, writerIndex: 0)
- }
-
- func testWrappingBehavior() {
- var ringBuffer = RingBuffer(capacity: 5)
-
- // Step 1: Write some data to fill part of the buffer
- XCTAssertTrue(ringBuffer.write(contentsOf: [1, 2, 3]))
- ringBuffer.assertState(count: 3, readerIndex: 0, writerIndex: 3)
-
- // Step 2: Read some data, advancing the reader index
- var readBuffer = Array(repeating: 0, count: 2)
- let readCount = ringBuffer.read(into: &readBuffer)
- XCTAssertEqual(readCount, 2)
- XCTAssertEqual(readBuffer, [1, 2])
- ringBuffer.assertState(count: 1, readerIndex: 2, writerIndex: 3)
-
- // Step 3: Write more data, causing the writer index to wrap around
- XCTAssertTrue(ringBuffer.write(contentsOf: [4, 5, 6]))
- ringBuffer.assertState(count: 4, readerIndex: 2, writerIndex: 1)
-
- // Step 4: Read remaining data to verify the wrap-around behavior
- readBuffer = Array(repeating: 0, count: 4)
- let totalReadCount = ringBuffer.read(into: &readBuffer)
- XCTAssertEqual(totalReadCount, 4)
- XCTAssertEqual(readBuffer, [3, 4, 5, 6])
- ringBuffer.assertState(count: 0, readerIndex: 1, writerIndex: 1)
- }
-
- func testWrappingWriteOverflowAndWrappingReadUnderflow() {
- var ringBuffer = RingBuffer(capacity: 5)
-
- // Step 1: Fill the buffer almost to capacity
- XCTAssertTrue(ringBuffer.write(contentsOf: [1, 2, 3]))
- ringBuffer.assertState(count: 3, readerIndex: 0, writerIndex: 3)
-
- // Step 2: Read some data to advance the reader index
- var readBuffer = Array(repeating: 0, count: 2)
- let readCount = ringBuffer.read(into: &readBuffer)
- XCTAssertEqual(readCount, 2)
- XCTAssertEqual(readBuffer, [1, 2])
- ringBuffer.assertState(count: 1, readerIndex: 2, writerIndex: 3)
-
- // Step 3: Write more data to cause the writer index to wrap around
- XCTAssertTrue(ringBuffer.write(contentsOf: [4, 5, 6]))
- // Writer wraps around
- ringBuffer.assertState(count: 4, readerIndex: 2, writerIndex: 1)
-
- // Step 4: Attempt a write that overflows (fails due to lack of capacity)
- XCTAssertFalse(ringBuffer.write(contentsOf: [7, 8, 9]))
- // State remains unchanged
- ringBuffer.assertState(count: 4, readerIndex: 2, writerIndex: 1)
-
- // Step 5: Read more data than available to test wrapping underflow
- readBuffer = Array(repeating: 0, count: 5)
- let underflowReadCount = ringBuffer.read(into: &readBuffer)
- XCTAssertEqual(underflowReadCount, 4) // Should only read 4 elements
- // Validate read data
- XCTAssertEqual(readBuffer.prefix(underflowReadCount), [3, 4, 5, 6])
- // Reader wraps around
- ringBuffer.assertState(count: 0, readerIndex: 1, writerIndex: 1)
- }
-}
diff --git a/harmony/assets/hero.jpg b/harmony/assets/hero.jpg
deleted file mode 100644
index 460065f2..00000000
Binary files a/harmony/assets/hero.jpg and /dev/null differ
diff --git a/harmony/include/btstack_config.h b/harmony/include/btstack_config.h
deleted file mode 100644
index 1b969e1b..00000000
--- a/harmony/include/btstack_config.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef _PICO_BTSTACK_BTSTACK_CONFIG_H
-#define _PICO_BTSTACK_BTSTACK_CONFIG_H
-
-// BTstack features that can be enabled
-#define ENABLE_LOG_INFO
-#define ENABLE_LOG_ERROR
-#define ENABLE_PRINTF_HEXDUMP
-#define ENABLE_SCO_OVER_HCI
-
-#ifdef ENABLE_BLE
-#define ENABLE_GATT_CLIENT_PAIRING
-#define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE
-#define ENABLE_LE_CENTRAL
-#define ENABLE_LE_DATA_LENGTH_EXTENSION
-#define ENABLE_LE_PERIPHERAL
-#define ENABLE_LE_PRIVACY_ADDRESS_RESOLUTION
-#define ENABLE_LE_SECURE_CONNECTIONS
-#endif
-
-#ifdef ENABLE_CLASSIC
-#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
-#define ENABLE_GOEP_L2CAP
-#endif
-
-#if defined (ENABLE_CLASSIC) && defined(ENABLE_BLE)
-#define ENABLE_CROSS_TRANSPORT_KEY_DERIVATION
-#endif
-
-// BTstack configuration. buffers, sizes, ...
-#define HCI_OUTGOING_PRE_BUFFER_SIZE 4
-#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
-#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
-#define MAX_NR_AVDTP_CONNECTIONS 1
-#define MAX_NR_AVDTP_STREAM_ENDPOINTS 1
-#define MAX_NR_AVRCP_CONNECTIONS 2
-#define MAX_NR_BNEP_CHANNELS 1
-#define MAX_NR_BNEP_SERVICES 1
-#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2
-#define MAX_NR_GATT_CLIENTS 1
-#define MAX_NR_HCI_CONNECTIONS 2
-#define MAX_NR_HID_HOST_CONNECTIONS 1
-#define MAX_NR_HIDS_CLIENTS 1
-#define MAX_NR_HFP_CONNECTIONS 1
-#define MAX_NR_L2CAP_CHANNELS 4
-#define MAX_NR_L2CAP_SERVICES 3
-#define MAX_NR_RFCOMM_CHANNELS 1
-#define MAX_NR_RFCOMM_MULTIPLEXERS 1
-#define MAX_NR_RFCOMM_SERVICES 1
-#define MAX_NR_SERVICE_RECORD_ITEMS 4
-#define MAX_NR_SM_LOOKUP_ENTRIES 3
-#define MAX_NR_WHITELIST_ENTRIES 16
-#define MAX_NR_LE_DEVICE_DB_ENTRIES 16
-
-// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun
-#define MAX_NR_CONTROLLER_ACL_BUFFERS 3
-#define MAX_NR_CONTROLLER_SCO_PACKETS 3
-
-// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun
-#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL
-#define HCI_HOST_ACL_PACKET_LEN 1024
-#define HCI_HOST_ACL_PACKET_NUM 3
-#define HCI_HOST_SCO_PACKET_LEN 120
-#define HCI_HOST_SCO_PACKET_NUM 3
-
-// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
-#define NVM_NUM_DEVICE_DB_ENTRIES 16
-#define NVM_NUM_LINK_KEYS 16
-
-// We don't give btstack a malloc, so use a fixed-size ATT DB.
-#define MAX_ATT_DB_SIZE 512
-
-// BTstack HAL configuration
-#define HAVE_EMBEDDED_TIME_MS
-
-// map btstack_assert onto Pico SDK assert()
-#define HAVE_ASSERT
-
-// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
-#define HCI_RESET_RESEND_TIMEOUT_MS 1000
-
-#define ENABLE_SOFTWARE_AES128
-#define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS
-
-#define HAVE_BTSTACK_STDIN
-
-// To get the audio demos working even with HCI dump at 115200, this truncates long ACL packets
-//#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100
-
-#endif // _PICO_BTSTACK_BTSTACK_CONFIG_H
diff --git a/harmony/include/lwipopts.h b/harmony/include/lwipopts.h
deleted file mode 100644
index fefe1e4b..00000000
--- a/harmony/include/lwipopts.h
+++ /dev/null
@@ -1,24 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift.org open source project
-//
-// Copyright (c) 2024 Apple Inc. and the Swift project authors
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LWIPOPTS_H
-#define _LWIPOPTS_H
-
-#define NO_SYS 1
-#define LWIP_SOCKET 0
-#define LWIP_NETCONN 0
-
-// Watch out: Without this, lwip fails to initialize and crashes inside
-// memp_init_pool due to misaligned memory access (the fallback is "1").
-#define MEM_ALIGNMENT 4
-
-#endif
diff --git a/nrfx-blink-sdk/BridgingHeader.h b/nrfx-blink-sdk/BridgingHeader.h
deleted file mode 100644
index 8a4377ab..00000000
--- a/nrfx-blink-sdk/BridgingHeader.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// This source file is part of the Swift open source project
-//
-// Copyright (c) 2023 Apple Inc. and the Swift project authors.
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See https://swift.org/LICENSE.txt for license information
-//
-//===----------------------------------------------------------------------===//
-
-#include
-
-#include
-#include
-
-#define LED0_NODE DT_ALIAS(led0)
-static struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
diff --git a/nrfx-blink-sdk/CMakeLists.txt b/nrfx-blink-sdk/CMakeLists.txt
deleted file mode 100644
index 24ba2fcc..00000000
--- a/nrfx-blink-sdk/CMakeLists.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-cmake_minimum_required(VERSION 3.29)
-find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
-
-# Use the armv7em-none-none-eabi target triple for Swift
-set(CMAKE_Swift_COMPILER_TARGET armv7em-none-none-eabi)
-# Enable "wmo" as needed by Embedded Swift
-set(CMAKE_Swift_COMPILATION_MODE wholemodule)
-# FIXME: Skip checking if the compiler works
-set(CMAKE_Swift_COMPILER_WORKS true)
-
-# Create a new project called "blinky" and enable "Swift" as a supported language
-project(blinky Swift)
-
-# Set global Swift compiler flags
-add_compile_options(
- # Enable Embedded Swift
- "$<$:SHELL:-enable-experimental-feature Embedded>"
-
- # Enable function sections to enable dead code stripping on elf
- "$<$:SHELL:-Xfrontend -function-sections>"
-
- # Use software floating point operations matching GCC
- "$<$:SHELL:-Xcc -mfloat-abi=soft>"
-
- # Use compacted C enums matching GCC
- "$<$:SHELL:-Xcc -fshort-enums>"
-
- # Disable PIC
- "$<$:SHELL:-Xcc -fno-pic>"
-
- # Add Libc include paths
- "$<$:SHELL:-Xcc -I -Xcc ${ZEPHYR_SDK_INSTALL_DIR}/arm-zephyr-eabi/picolibc/include>"
-)
-
-# Add definitions from Zephyr to -Xcc flags
-get_target_property(ZEPHYR_DEFINES zephyr_interface INTERFACE_COMPILE_DEFINITIONS)
-if(ZEPHYR_DEFINES)
- foreach(flag ${ZEPHYR_DEFINES})
- # Ignore expressions like "$"
- string(FIND "${flag}" "$<" start_of_expression)
- if(NOT start_of_expression EQUAL -1)
- continue()
- endif()
-
- add_compile_options("$<$