SwiftUI Combine - Высококачественный визуализатор громкости звука - PullRequest
0 голосов
/ 07 апреля 2020

Я пытаюсь повторить, как работает старый стерео визуализатор в SwiftUI с использованием Combine. Я могу опубликовать sh случайные данные, которые являются моим текущим объемом, и затем я хочу, чтобы объем уменьшился до нуля, если только не будет передана новая высокая точка.

  • Попытка 1 почти работает, кроме отсюда нет задержки до того, как значение начнет уменьшаться.
  • Попытка 2 кажется, что она должна работать, с задержкой после установки громкости, но тогда у меня есть проблема с установкой переменной обратно

Кто-нибудь может дать мне какие-нибудь советы о том, как создать этот эффект, но с помощью специально созданных Combine Publishers?

import SwiftUI
import Combine

/**
 Think of an old audio equalizer/visualizer, as sound is played, it visually goes high, but then fades back down
 to zero, that's what I'm trying to replicate, vut using Swift Combine Publishers

 The highest value will be assigned to $volume

 After a second of being displayed, the $volume should decrease over a short amount of time back to zero
 However, if a new higher $volume is set, then the new highest number is displayed, wait a second and start
 to decrease again
 */
class Throughput: ObservableObject {

    private var cancellables = Set<AnyCancellable>()

    /// Creates random flow volume data over time
    private let dataPublisher = Timer.publish(every: 1.0, on: .main, in: .default)
    private var dataCancelable: AnyCancellable?

    /// Remove 1 until `volume` is back to zero
    private let resetPublisher = Timer.publish(every: 0.1, on: .main, in: .default)
    private var resetCancelable: AnyCancellable?

    @Published private var internalvolume: Int = 0
    @Published private(set) var volume: Int = 0

    init() {

        /**
        Publish values over time
        */
         dataCancelable = dataPublisher
                   .autoconnect()
                   .map({ _ in Int.random(in: 0..<100) })
                   .map({
                       if $0 > self.volume {
                           return $0
                       } else {
                           return nil
                       }
                   })
                   .compactMap({ $0 })
                   .assign(to: \.volume, on: self)

        /**
        When the volume is changed, pause, then quickly decrease the value of volume to zero unless a new high point found
        */

        /**
         Attemp 1 just uses another timer to decrease from the volume, the issue here is there is no delay before it starts
         */
        resetCancelable = resetPublisher
            .autoconnect()
            .map({ _ in
                var current = self.volume
                if current > 0 {
                    current -= 1
                }
                return current
                })
            .assign(to: \.volume, on: self)

        /**
         Attempt 2 - Feels like it would be the better option, delaying the action
         */
//        $volume
//            .delay(for: .seconds(1), tolerance: .none, scheduler: RunLoop.main, options: nil)
//            .map({ _ in
//                var current = self.volume
//                if current > 0 {
//                    current -= 1
//                }
//                self.volume = current
//                })
//            .sink { () in
//
//        }.store(in: &cancellables)

    }
}

extension Int {
    var stringValue: String {
        String(self)
    }
}

struct ContentView: View {

    @ObservedObject var flow: Throughput = Throughput()

    var body: some View {
        Text(flow.volume.stringValue)
    }
}

Спасибо

...