Изменение тональности звука "эхолота"

Bruce A. спросил: 10 мая 2018 в 04:29 в: iphone

Изменить: добавлен новый код внизу.

Я довольно новичок в кодировании, поэтому начал с создания приложения звуковой платы. Я смог использовать команду "rate" для замедления или ускорения звука, но теперь я тоже хочу изменить поле. В идеале, я бы хотел иметь два переключателя. Один, который ускоряет звук и поднимает высоту тона, тот, который замедляется и опускается, и если они оба выключены, он играет нормально. Ниже мой код работает для всего, кроме изменения высоты тона. Любой вход очень приветствуется. Для справки, sass медленный, чип быстро.

import UIKit
import AVFoundationvar Sounder1 = AVAudioPlayer()
var Sounder2 = AVAudioPlayer()let sassFloat: Float = 0.5
let myInt = Int(sassFloat)let chipFloat: Float = 3.0
let myInt2 = Int(chipFloat)class ViewController: UIViewController {@IBOutlet weak var sassSwitch: UISwitch!@IBOutlet weak var chipSwitch: UISwitch!override func viewDidLoad() {
    super.viewDidLoad()    do {
        Sounder1 = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "Sound1", ofType: "mp3")!))
        Sounder1.prepareToPlay()
        Sounder1.enableRate = true
    }catch{    }    do {
        Sounder2 = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "Sound2", ofType: "wav")!))
        Sounder2.prepareToPlay()
        Sounder2.enableRate = true
    }catch{    }
    // Do any additional setup after loading the view, typically from a nib.
}@IBAction func sassAction(_ sender: UISwitch) {
    chipSwitch.setOn(false, animated: true)
}@IBAction func chipAction(_ sender: UISwitch) {
    sassSwitch.setOn(false, animated: true)
}@IBAction func play(_ sender: Any) {
    if sassSwitch.isOn {
        Sounder1.rate = sassFloat
        Sounder1.play()
    } else if chipSwitch.isOn {
        Sounder1.rate = chipFloat
        Sounder1.play()
    } else {
        Sounder1.rate = 1.0
        Sounder1.play()
    }}@IBAction func play2(_ sender: Any) {
    if sassSwitch.isOn {
        Sounder2.rate = sassFloat
        Sounder2.play()
    } else if chipSwitch.isOn {
        Sounder2.rate = chipFloat
        Sounder2.play()
    } else {
        Sounder2.rate = 1.0
        Sounder2.play()
    }
}override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}}

Это новый код, который я разработал в соответствии с предложениями @ericl. Мне все еще нужно reset() audioEngine, но у меня также было несколько вопросов.1) Будет ли выполняться оператор if else if else эта ситуация? 2) Где добавить инструкцию reset(). 3) Нужно ли отделять узел после воспроизведения каждого звука? Или просто сбросить?

class ViewController: UIViewController {@IBOutlet weak var sassSwitch: UISwitch!
@IBOutlet weak var chipSwitch: UISwitch!@IBAction func sassAction(_ sender: UISwitch) {
    chipSwitch.setOn(false, animated: true)
}
@IBAction func chipSwitch(_ sender: UISwitch) {
    sassSwitch.setOn(false, animated: true)
}///Playback Engine
private let audioEngine = AVAudioEngine()///Player's Nodes
private let pitchPlayer = AVAudioPlayerNode()
private let timePitch = AVAudioUnitTimePitch()///Audio Files to be played
private var audioFile1 = AVAudioFile()
private var audioFile2 = AVAudioFile()override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.    if let filePath = Bundle.main.path(forResource: "PeteNope", ofType:
        "mp3") {
        let filePathURL = URL(fileURLWithPath: filePath)        setPlayerFile(filePathURL)    }    if let filePath2 = Bundle.main.path(forResource: "Law_WOW", ofType:
        "mp3") {
        let filePath2URL = URL(fileURLWithPath: filePath2)        setPlayerFile2(filePath2URL)    }
}private func setPlayerFile(_ fileURL: URL) {
    do {
        let file = try AVAudioFile(forReading: fileURL)        self.audioFile1 = file    } catch {
        fatalError("Could not create AVAudioFile instance. error: \(error).")
    }
}private func setPlayerFile2(_ fileURL: URL) {
    do {
        let file = try AVAudioFile(forReading: fileURL)        self.audioFile2 = file    } catch {
        fatalError("Could not create AVAudioFile instance. error: \(error).")
    }
}@IBAction func sound1Play(_ sender: UIButton) {
    if sassSwitch.isOn {
        timePitch.pitch = -300
        timePitch.rate = 0.5
        audioEngine.attach(pitchPlayer)
        audioEngine.attach(timePitch)        audioEngine.connect(pitchPlayer, to: timePitch, format: audioFile1.processingFormat)
        audioEngine.connect(timePitch, to: audioEngine.outputNode, format: audioFile1.processingFormat)
        pitchPlayer.scheduleFile(audioFile1, at: nil, completionHandler: nil)        // Start the engine.
        do {
            try audioEngine.start()
        } catch {
            fatalError("Could not start engine. error: \(error).")
        }        pitchPlayer.play()    } else if chipSwitch.isOn {
        timePitch.pitch = +500
        timePitch.rate = 2.0
        audioEngine.attach(pitchPlayer)
        audioEngine.attach(timePitch)        audioEngine.connect(pitchPlayer, to: timePitch, format: audioFile1.processingFormat)
        audioEngine.connect(timePitch, to: audioEngine.outputNode, format: audioFile1.processingFormat)
        pitchPlayer.scheduleFile(audioFile1, at: nil, completionHandler: nil)        // Start the engine.
        do {
            try audioEngine.start()
        } catch {
            fatalError("Could not start engine. error: \(error).")
        }        pitchPlayer.play()    } else {
        timePitch.pitch = +0
        timePitch.rate = 1.0
        audioEngine.attach(pitchPlayer)
        audioEngine.attach(timePitch)        audioEngine.connect(pitchPlayer, to: timePitch, format: audioFile1.processingFormat)
        audioEngine.connect(timePitch, to: audioEngine.outputNode, format: audioFile1.processingFormat)
        pitchPlayer.scheduleFile(audioFile1, at: nil, completionHandler: nil)        // Start the engine.
        do {
            try audioEngine.start()
        } catch {
            fatalError("Could not start engine. error: \(error).")
        }
        pitchPlayer.play()
    }}override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}}

1 ответ

Есть решение
ericl ответил: 11 мая 2018 в 01:33

Вы будете использовать AVAudioEngine, чтобы прикрепить AVAudioPlayerNode к эффекту AVAudioUnitTimePitch. Я проверил образец примера Apple AudioUnitV3Example и собрал вместе с ним рабочий пример. Вы захотите реализовать больше состояний и обработки ошибок, которые вы можете найти там. В любом случае, здесь:

Определите некоторые частные переменные в верхней части вашего класса:

   /// Playback engine.
    private let audioEngine = AVAudioEngine()    /// Engine's player node.
    private let pitchPlayer = AVAudioPlayerNode()
    private let timePitch = AVAudioUnitTimePitch()    /// File to play.
    private var audioFile: AVAudioFile?

Проверьте действительный аудиофайл:

     override func viewDidLoad() {
         super.viewDidLoad()     if let filePath = Bundle.main.path(forResource: "Sound1", ofType: 
 "mp3") {
         let filePathURL = URL(fileURLWithPath: filePath)         setPlayerFile(filePathURL)         }
     }     private func setPlayerFile(_ fileURL: URL) {
         do {
             let file = try AVAudioFile(forReading: fileURL)             self.audioFile = file         } catch {
             fatalError("Could not create AVAudioFile instance. error: \(error).")
         }
     }     @IBAction func verySlowPlayback(sender: UIButton) {    timePitch.pitch = -300
    timePitch.rate = 0.5
    audioEngine.attach(pitchPlayer)
    audioEngine.attach(timePitch)    audioEngine.connect(pitchPlayer, to: timePitch, format: audioFile?.processingFormat)
    audioEngine.connect(timePitch, to: audioEngine.outputNode, format: audioFile?.processingFormat)
    pitchPlayer.scheduleFile(audioFile!, at: nil, completionHandler: nil)    // Start the engine.
    do {
        try audioEngine.start()
    } catch {
        fatalError("Could not start engine. error: \(error).")
    }    pitchPlayer.play()}
Bruce A. ответил: 11 мая 2018 в 06:22
Спасибо за это. Это поможет мне начать работу позже, когда я смогу добраться до компьютера. Есть ли у вас какие-либо предложения о том, как я привяжу их к коммутатору, а не к кнопке? В основном я хочу два переключателя: один для быстрого и высокого уровня, один для медленного и низкого. Я хотел бы иметь несколько кнопок со звуками, воспроизводимыми нормально, но если переключатель включен, он либо ускоряет, либо замедляет его.
ericl ответил: 11 мая 2018 в 01:38
В стороне: я редактировал сообщение, чтобы добавить свойство rate в класс AVAudioUnitTimePitch: timePitch.rate = 0.5.
ericl ответил: 11 мая 2018 в 02:02
У вас могут быть play1() и play2(), изменяющие свойства AVAudioUnitTimePitch. Вы увидите, требуется ли вам reset() звуковой движок или нет.
Bruce A. ответил: 12 мая 2018 в 11:36
Спасибо за помощь @ericl. Я отредактировал сообщение с новым кодом, который я бросил вместе с вашими изменениями. Если у вас есть шанс, просто взгляните на него и сообщите мне, если это имеет какой-то смысл или я ухожу от базы.
ericl ответил: 12 мая 2018 в 02:54
Я думаю, вам следует создать более простой тестовый проект с одним файлом и одним коммутатором и заставить его работать. Взгляните на этот образец кода Apple для поездки по кроличьей дыре и подумайте о том, чтобы использовать логическое значение типа `isPlaying 'для проверки состояния. Выясните свой следующий Minimal Complete & Подтверждаемый пример для другого вопроса, и вы, вероятно, найдете ответы на этом пути! (Я тоже начинаю, см. Мой низкий балл)? В любом случае, я ответил на ваш оригинальный вопрос "как изменить подачу", поэтому, пожалуйста, примите мой ответ!