How to play several sound in a row - swift

The goal is to play several sounds one after another (getReady -> nextExercise -> burpees).
The problem is that only the first one is being played
How it should work:
I call playGetReady() from WorkoutTabataViewController
I plays the first sound
After the first sound is finished, automatically "audioPlayerDidFinishPlaying()" is being called
It triggers "playNextSound()" func, which playing next sound
But audioPlayerDidFinishPlaying() is not being called. Or am I missing something and it should work differently?
class AudioPlayerManager: AVAudioPlayerDelegate {
var description: String
static let shared = AudioPlayerManager()
var audioPlayer: AVAudioPlayer?
var workoutVC: WorkoutTabataViewController?
var mainVC: MainTabataViewController?
var currentSound = 0
let urls: [URL]
init() {
self.description = ""
//First sound
let getReady = Bundle.main.path(forResource: "Get ready", ofType: "mp3")!
let urlGetReady = URL(fileURLWithPath: getReady)
//Second sound
let nextExercise = Bundle.main.path(forResource: "Next Exercise", ofType: "mp3")!
let urlNextExercise = URL(fileURLWithPath: nextExercise)
//Third sound
let burpees = Bundle.main.path(forResource: "Burpees", ofType: "mp3")!
let urlBurpees = URL(fileURLWithPath: burpees)
urls = [urlGetReady, urlNextExercise, urlBurpees]
}
func playGetReady() {
do {
audioPlayer = try AVAudioPlayer(contentsOf: urls[currentSound])
audioPlayer?.delegate = self
audioPlayer?.play()
} catch {
print(error)
}
}
func playNextSound() {
currentSound += 1
if currentSound < urls.count {
do {
audioPlayer = try AVAudioPlayer(contentsOf: urls[currentSound])
audioPlayer?.delegate = self
audioPlayer?.play()
} catch {
print(error)
}
}
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
if flag {
playNextSound()
}
}
}

Your audio manager class is not introspectable. Say #objc func audioPlayerDidFinishPlaying or, better, make it an NSObject.

Related

"Thread1:Fatal error:Unexpectedly found nil while unwrapping an Optional value"

1these are the wav files I used
2I attached a screenshot of where it is throwing this error.
this is what I have right now. I thought I had everything correct. I know it has something to do with using an optional but not sure how to fix it.
import UIKit
import AVFoundation
class SecondViewController: UIViewController
{
var audioPlayer: AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
do
{
let catSound = Bundle.main.path(forResource: "Cat", ofType: "wav")
let horseSound = Bundle.main.path(forResource: "Horse", ofType: "wav")
let dogSound = Bundle.main.path(forResource: "Dog", ofType: "wav")
let raccoonSound = Bundle.main.path(forResource: "Raccoon", ofType: "wav")
audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: catSound!))
audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: horseSound!))
audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: dogSound!))
audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: raccoonSound!))
}
catch
{
print(error)///
}
}
#IBAction func cat(_ sender: Any)
{
audioPlayer?.play()
}
#IBAction func horse(_ sender: Any)
{
audioPlayer?.play()
}
#IBAction func dog(_ sender: Any)
{
audioPlayer?.play()
}
#IBAction func raccoon(_ sender: Any){
audioPlayer?.play()
}
}
As #Chris and #inexcitus mentioned, your complete code would look like.
class SecondViewController: UIViewController {
var audioPlayer: AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func cat(_ sender: Any)
{
if let catSound = Bundle.main.path(forResource: "cat", ofType: "wav") {
audioPlayer = try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: catSound))
audioPlayer?.play()
}else {
print("Cat File is missing")
}
}
#IBAction func horse(_ sender: Any)
{
if let horseSound = Bundle.main.path(forResource: "horse", ofType: "wav") {
audioPlayer = try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: horseSound))
audioPlayer?.play()
}else {
print("Horse File is missing")
}
}
#IBAction func dog(_ sender: Any)
{
if let dogSound = Bundle.main.path(forResource: "dog", ofType: "wav") {
audioPlayer = try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: dogSound))
audioPlayer?.play()
}else {
print("Dog File is missing")
}
}
#IBAction func raccoon(_ sender: Any)
{
if let raccoonSound = Bundle.main.path(forResource: "raccoon", ofType: "wav") {
audioPlayer = try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: raccoonSound))
audioPlayer?.play()
}else {
print("Raccoon File is missing")
}
}}
One of your resources is nil.
You should check if your resoruce is nil before (forcefully) unwrapping it:
if let catSound = Bundle.main.path(forResource: "Cat", ofType: "wav")
{
audioPlayer = try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: catSound))
}
Do this for every resource and use the debugger to see which one is nil.
Case sensitivity matters.
The files are lowercased ("cat") but the parameter strings in the code are uppercased ("Cat"). The parameter string and the file name must match exactly.
Your code is not very useful anyway because it overwrites the audio player instance three times in viewDidLoad.
A more efficient solution is to create a method to load the file and play the sound and to use the URL related API of Bundle.
The force unwrapping is fine because all files must be in the bundle at compile time. A design mistake (file is missing or name is misspellt) can be fixed immediately.
class SecondViewController: UIViewController
{
var audioPlayer: AVAudioPlayer!
#IBAction func cat(_ sender: Any) {
playSound(withName: "cat")
}
#IBAction func horse(_ sender: Any) {
playSound(withName: "horse")
}
#IBAction func dog(_ sender: Any) {
playSound(withName: "dog")
}
#IBAction func raccoon(_ sender: Any){
playSound(withName: "raccoon")
}
func playSound(withName name : String) {
let sound = Bundle.main.url(forResource: name, withExtension: "wav")!
audioPlayer = try! AVAudioPlayer(contentsOf: sound)
audioPlayer.play()
}
}

How to make a function play different sounds in Swift

I have 10 different buttons and each button has a unique tag associated with it. All buttons are linked to the same IBAction. I have the function to play different different sounds according to the tag associated with the particular button that is clicked.
import AVFoundation
var myAudio: AVAudioPlayer!
let path = Bundle.main.path(forResource: "sound1", ofType: "wav")!
let url = URL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOf: url)
myAudio = sound
sound.play()
} catch {
//
}
}
do like
#objc func yourbtnActionName(_ sender : UIButton){
switch sender.tag {
case 1:
commonSoundCode(name: "sound1")
break
case 2:
commonSoundCode(name: "yoursecondSoundname")
break
default:
break
}
}
then common method as
func commonSoundCode(name: String){
let path = Bundle.main.path(forResource: name, ofType: "wav")!
let url = URL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOf: url)
myAudio = sound
sound.play()
} catch {
//
}
}
option 2
if your sound files are in the same sequence, for e.g (sound1.wav, sound2.wav......, sound10.wav) then call like
#objc func yourbtnActionName(_ sender : UIButton){
let path = Bundle.main.path(forResource: "sound\(sender.tag)", ofType: "wav")!
let url = URL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOf: url)
myAudio = sound
sound.play()
} catch {
//
}
}
You need to simply play sounds according to the UIButton's tag.
#IBAction func playsound(_ sender: UIButton)
{
switch sender.tag
{
case 0:
//Sound for Button with tag=0
case 1:
//Sound for Button with tag=1
default:
//Default Sound
}
}
In the above code, add more cases according to your UIButton tags.
Please add some more code for a bit more clarity around your question.
import UIKit
//Mark: Do import AVFoundation is must here!
import AVFoundation
class ViewController: UIViewController {
//Mark: Create variable for AVAudioEngine! and AVAudioplayermode!
var engine: AVAudioEngine!
var audioPlayerNode : AVAudioPlayerNode!
//Mark: Create Variable for each tone as AVAudiofile!
var Cs:AVAudioFile!
var Ds:AVAudioFile!
var Es:AVAudioFile!
var Fs:AVAudioFile!
var Gs:AVAudioFile!
var As:AVAudioFile!
var Bs:AVAudioFile!
override func viewDidLoad() {
super.viewDidLoad()
// Mark: Load engine
engine = AVAudioEngine()
// Mark : Mention each sound
Cs = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "C", ofType: "wav")!))
Ds = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "D", ofType: "wav")!))
Es = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "E", ofType: "wav")!))
Fs = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "F", ofType: "wav")!))
Gs = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "G", ofType: "wav")!))
As = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "A", ofType: "wav")!))
Bs = try? AVAudioFile(forReading: NSURL.fileURL(withPath:
Bundle.main.path(forResource: "B", ofType: "wav")!))
}
//Mark: create function(playSound) for connect AVAudioEngine and AVAudioplayermode for playing audio when button clicked
func playSound(audioFile: AVAudioFile) {
audioPlayerNode = AVAudioPlayerNode()
if(audioPlayerNode.isPlaying){
audioPlayerNode.stop()
}
if(engine.isRunning){
engine.stop()
engine.reset()
}
engine.attach(audioPlayerNode)
engine.connect(audioPlayerNode, to: engine.mainMixerNode, format: audioFile.processingFormat)
audioPlayerNode.scheduleFile(audioFile, at: nil, completionHandler: nil)
// Start the audio engine
engine.prepare()
try! engine.start()
audioPlayerNode.play()
}
//Mark: create required button for actions to connect function(playSound) and mention each sound to button
#IBAction func btnC(_ sender: UIButton) {
playSound(audioFile: Cs)
}
#IBAction func btnD(_ sender: UIButton) {
playSound(audioFile: Ds)
}
#IBAction func btnE(_ sender: UIButton) {
playSound(audioFile: Es)
}
#IBAction func btnF(_ sender: UIButton) {
playSound(audioFile: Fs)
}
#IBAction func btnG(_ sender: UIButton) {
playSound(audioFile: Gs)
}
#IBAction func btnA(_ sender: UIButton) {
playSound(audioFile: As)
}
#IBAction func btnB(_ sender: UIButton) {
playSound(audioFile: Bs)
}
}

Swift 3: AVAudioPlayer not playing sound

I have a struct with my audio player in it:
struct MT_Audio {
func playAudio(_ fileName:String, _ fileExtension:String, _ atVolume:Float) {
var audioPlayer = AVAudioPlayer()
if let audioPath = Bundle.main.path(forResource: fileName, ofType: fileExtension) {
let audioURL = URL(string:audioPath)
do {
audioPlayer = try AVAudioPlayer(contentsOf: audioURL!)
audioPlayer.volume = atVolume
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print(error)
}
}
}
}
//I'm calling it in viewDidLoad like this:
guard let fileURL = Bundle.main.url(forResource:"heartbeat-01a", withExtension: "mp3")
else {
print("can't find file")
return
}
let myAudioPlayer = MT_Audio() //<--RESOLVED THE ISSUE BY MAKING THIS A PROPERTY OF THE VIEWCONTROLLER
myAudioPlayer.playAudio("heartbeat-01a", "mp3", 1.0)
Since it doesn't crash and burn on the guard I know the file is there. I've also put a break point in after the try and I am getting to the audio player. When I go to the actual file and click on it in Xcode it plays. This fails on both the sim and device. Any help would be appreciated.
Looks like your audioPlayer is only stored within your playAudio function.
Try to keep the audioPlayer as an variable inside your class like this:
struct MT_Audio {
var audioPlayer: AVAudioPlayer?
mutating func playAudio(_ fileName:String, _ fileExtension:String, _ atVolume:Float) {
// is now member of your struct -> var audioPlayer = AVAudioPlayer()
if let audioPath = Bundle.main.path(forResource: fileName, ofType: fileExtension) {
let audioURL = URL(string:audioPath)
do {
let audioPlayer = try AVAudioPlayer(contentsOf: audioURL!)
audioPlayer.volume = atVolume
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print(error)
}
}
}
}

Create a function from code

How do I create a function from this code in swift3?
I have a button which is pressed then plays this sound "push"
How can it be simplified when there are lots of buttons? I don't want to add all codes to every button.
var myAudio = AVAudioPlayer()
// Add sound
do {
try myAudio = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath:
Bundle.main.path(forResource: "push", ofType: "mp3")!) as URL)
} catch {
NSLog("No file!")
}
//call the sound
myAudio.play()
I made this change
func play(name : String){
do {
try myAudio = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath:
Bundle.main.path(forResource: name, ofType: "mp3")!) as URL)
} catch {
NSLog("No file!")
}
}
#IBAction func buttonFirst(_ sender: UIButton) {
play(name: "push")
myAudio.play()
}
#IBAction func buttonSecond(_ sender: UIButton) {
play(name: "second")
myAudio.play()
}
I got this output:
2017-07-25 16:13:23.270349+0100 sound[1728:933024] [aqme] 254: AQDefaultDevice (173): skipping input stream 0 0 0x0
Is that a problem?
I think you forgot the prepare
var audioPlayer = AVAudioPlayer()
let sound = URL(fileURLWithPath: Bundle.main.path(forResource: "sound", ofType: "mp3")!)
try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try! AVAudioSession.sharedInstance().setActive(true)
try! audioPlayer = AVAudioPlayer(contentsOf: sound)
audioPlayer.prepareToPlay()
audioPlayer.play()
You can use it in the following way
var myAudio : AVAudioPlayer?
func playSound(){
let path = Bundle.main.path(forResource: "push", ofType:"mp3")!
let url = URL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOf: url)
self.myAudio = sound
sound.numberOfLoops = 1
sound.prepareToPlay()
sound.play()
} catch {
print("error loading file")
// couldn't load file :(
}
}
Furthermore you can use SwiftySound that lets you play sounds easily in Swift 3.
for example
Sound.play(file: "push.mp3")

Record And Play Voice in Separate Class (Swift3)

I used many codes that was for record an play the voice, but most of them are not in swift3 and they don't work in my app.
This code works, but I want to create a separate class from the viewcontroller that do recording an playing voices. Also the mentioned github code is complex an I'm searching for a simplified code.
Update:
After recording, when I check existence of the recorded file, the file doesn't exist, and it raises EXC_BAD_ACCESS error on appDelegate.
What's wrong?
Any suggestions would be appreciated.
Try to record audio by wirting line
let isRec = AudioManager.shared.record(fileName: "rec")
if isRec returned true then recording is happening else not.
To finish recording use : let recordedURL = AudioManager.shared.finishRecording()
To play recorded file send above url to setupUpPlayer() function in manager class
Not to forget to use extension code snippets give below the code snippet which are delegate functions of AVAudioRecorder and AVAudioPlayer
import Foundation
import AVFoundation
class AudioManager: NSObject {
static let shared = AudioManager()
var recordingSession: AVAudioSession?
var recorder: AVAudioRecorder?
var meterTimer: Timer?
var recorderApc0: Float = 0
var recorderPeak0: Float = 0
//PLayer
var player: AVAudioPlayer?
var savedFileURL: URL?
func setup() {
recordingSession = AVAudioSession.sharedInstance()
do {
try recordingSession?.setCategory(AVAudioSessionCategoryPlayAndRecord, with: .defaultToSpeaker)
try recordingSession?.setActive(true)
recordingSession?.requestRecordPermission({ (allowed) in
if allowed {
print("Mic Authorised")
} else {
print("Mic not Authorised")
}
})
} catch {
print("Failed to set Category", error.localizedDescription)
}
}
func record(fileName: String) -> Bool {
setup()
let url = getUserPath().appendingPathComponent(fileName + ".m4a")
let audioURL = URL.init(fileURLWithPath: url.path)
let recordSettings: [String: Any] = [AVFormatIDKey: kAudioFormatMPEG4AAC,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue,
AVNumberOfChannelsKey: 2,
AVSampleRateKey: 44100.0]
do {
recorder = try AVAudioRecorder.init(url: audioURL, settings: recordSettings)
recorder?.delegate = self
recorder?.isMeteringEnabled = true
recorder?.prepareToRecord()
recorder?.record()
self.meterTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { (timer: Timer) in
//Update Recording Meter Values so we can track voice loudness
if let recorder = self.recorder {
recorder.updateMeters()
self.recorderApc0 = recorder.averagePower(forChannel: 0)
self.recorderPeak0 = recorder.peakPower(forChannel: 0)
}
})
savedFileURL = url
print("Recording")
return true
} catch {
print("Error Handling", error.localizedDescription)
return false
}
}
func getUserPath() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}
func finishRecording() -> String {
recorder?.stop()
self.meterTimer?.invalidate()
var fileURL: String?
if let url: URL = recorder?.url {
fileURL = String(describing: url)
}
return /fileURL
}
//Player
func setupPlayer(_ url: URL) {
do {
try player = AVAudioPlayer.init(contentsOf: url)
} catch {
print("Error1", error.localizedDescription)
}
player?.prepareToPlay()
player?.play()
player?.volume = 1.0
player?.delegate = self
}
}
//MARK:- Audio Recorder Delegate
extension AudioManager: AVAudioRecorderDelegate {
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
print("AudioManager Finish Recording")
}
func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) {
print("Encoding Error", /error?.localizedDescription)
}
}
//MARK:- Audio Player Delegates
extension AudioManager: AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer,
successfully flag: Bool) {
player.stop()
print("Finish Playing")
}
func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer,
error: Error?) {
print(/error?.localizedDescription)
}
}