How to load mp3 on Library Folder and play it.

Hello Everyone, I'm Hiro

Now I'm trying to load mp3 on Library Folder and play it. (Originally, the mp3 file was on bundle.main.path and was stored on Library Folder. And that seems being success)

let storedURL = directoryUrl.appendingPathComponent(fileName) // same url when a mp3 file was stored on Library Folder

do {           let audioPlayer = try AVAudioPlayer(contentsOf: storedURL, fileTypeHint: AVFileType.mp3.rawValue)           audioPlayer.play()         } catch let error {           print(error.localizedDescription)         }

When I did above code, below error would happen. 2021-07-06 15:41:24.968272+0900 FileSaveSample[1438:69214] [aqsrv] AQServer.cpp:68:APIResult: Exception caught in AudioQueueInternalNotifyRunning - error -66671

And, the sound won't play on simulator and actual device.

Am I wrong on something basic?

I use XCode 12.5.1 and iOS 14.

Best regards,

Answered by OOPer in 681219022

Thanks for showing your code.

The critical thing in your code is this part:

        do {
          let audioPlayer = try AVAudioPlayer(contentsOf: storedURL, fileTypeHint: AVFileType.mp3.rawValue)
          audioPlayer.play()
          print("sound is playing")
        } catch let error {
          print("Sound Play Error.lDescription -> \(error.localizedDescription)")
          print("Sound Play Error -> \(error)")
        }

(As was shown in your first post, nice guess!)

The problem is that the variable audioPlayer is local to the do-catch block. So, it will be lost immediately after print("sound is playing"). With audioPlayer.play(), playing the audio is just started but not finished.

The instance of AVAudioPlayer needs to be held while playing. Please try something like this:

struct ContentView: View {
    let playerManager = AudioPlayerManager.shared //<-
    
    @State var message: String = "Test Message2"
    @State var storedURL: URL?
    
    var body: some View {
        Text(message)
            .onTapGesture {
                guard let path = Bundle.main.path(forResource: "selectSE", ofType: "mp3") else {
                    print("Sound file not found")
                    return
                }
                let url = URL(fileURLWithPath: path)
                do {
                    let fileData = try Data(contentsOf: url)
                    storedURL = saveDataFile(data: fileData, fileName: "test.mp3", folderName: "testFolder")
                    
                    print("File Writing on View -> Success \(storedURL?.absoluteString ?? "nil") ")
                } catch {
                    print("Data.init(contentsOf:) failed: \(error)")
                }
                
                playerManager.play(url: storedURL!) //<-
                print("end of code")
                
            }
    }
    
    //...
}

class AudioPlayerManager {
    static let shared = AudioPlayerManager()
    
    var audioPlayer: AVAudioPlayer?
    
    func play(url: URL) {
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue) //<- No `let`
            audioPlayer?.play()
            print("sound is playing")
        } catch let error {
            print("Sound Play Error -> \(error)")
        }
    }
}

You have no need to copy the sound file each time onTapGesture, but it is not critical and you may improve it later.

What do you get when you change print(error.localizedDescription) to print(error)? (And please show enough code, using the Code Block feature.)  

Thank you for your reply! I'll try that on print and share with Code Block feature. (I have learned the feature thanks to you)

For posting code block, I replies on answers.

I've tried above.

What do you get when you change print(error.localizedDescription) to print(error)?

I'm sorry. That error message seems not to be for AVPlayer. For sharing my code, I tried to write some prints in several places. Then, I found the guess. latest situation is "no error message and play "API" looks being success but actual sound is not played"

  1. code

I will share all code as below. I hope that problem will be found..

import SwiftUI
import AVFoundation

struct ContentView: View {
   
  @State var message: String = "Test Message2"
  @State var storedURL: URL
   
  var body: some View {
    Text(message)
      .onTapGesture {
        guard let path = Bundle.main.path(forResource: "selectSE", ofType: "mp3") else {
          print("Sound file not found")
          return
        }
        let url = URL(fileURLWithPath: path)
        do {
          let fileData = try Data(contentsOf: url)
          storedURL = saveDataFile(data: fileData, fileName: "test.mp3", folderName: "testFolder")
           
          print("File Writing on View -> Success \(storedURL.absoluteString) ")
        } catch {
           
        }
         
        do {
          let audioPlayer = try AVAudioPlayer(contentsOf: storedURL, fileTypeHint: AVFileType.mp3.rawValue)
          audioPlayer.play()
          print("sound is playing")
        } catch let error {
          print("Sound Play Error.lDescription -> \(error.localizedDescription)")
          print("Sound Play Error -> \(error)")
        }
        print("end of code")
         
      }
  }
   
  func saveDataFile(data:Data, fileName: String, folderName: String) -> URL {
     
    let directoryUrl = self.getLibraryDirectory().appendingPathComponent(folderName, isDirectory: true)
             
    do {
      try FileManager.default.createDirectory(at: directoryUrl, withIntermediateDirectories: true)
      print("Directory Creation -> Success \n \(directoryUrl)")
    } catch {
      print("Directory Creation -> Failed")
    }
     
     
    let url = directoryUrl.appendingPathComponent(fileName)
     
    do {
      try data.write(to: url, options: .atomic )
      // let input = try String(contentsOf: url)
      print("File Writing -> Success")
    } catch {
      print("File Writing -> Error \n \(error.localizedDescription)")
       
    }
         
    return url
  }
   
  func getLibraryDirectory() -> URL {
    // find all possible documents directories for this user
    let paths = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)

    // just send back the first one, which ought to be the only one
    return paths[0]
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView(storedURL: URL(string: "")!)
  }
}
  1. print

there is not error message but I will share for reference.

Directory Creation -> Success 
 file:///var/mobile/Containers/Data/Application/C275FFD9-2635-47D9-B349-EC945733175C/Library/testFolder/
File Writing -> Success
File Writing on View -> Success file:///var/mobile/Containers/Data/Application/C275FFD9-2635-47D9-B349-EC945733175C/Library/testFolder/test.mp3 
sound is playing
end of code
Accepted Answer

Thanks for showing your code.

The critical thing in your code is this part:

        do {
          let audioPlayer = try AVAudioPlayer(contentsOf: storedURL, fileTypeHint: AVFileType.mp3.rawValue)
          audioPlayer.play()
          print("sound is playing")
        } catch let error {
          print("Sound Play Error.lDescription -> \(error.localizedDescription)")
          print("Sound Play Error -> \(error)")
        }

(As was shown in your first post, nice guess!)

The problem is that the variable audioPlayer is local to the do-catch block. So, it will be lost immediately after print("sound is playing"). With audioPlayer.play(), playing the audio is just started but not finished.

The instance of AVAudioPlayer needs to be held while playing. Please try something like this:

struct ContentView: View {
    let playerManager = AudioPlayerManager.shared //<-
    
    @State var message: String = "Test Message2"
    @State var storedURL: URL?
    
    var body: some View {
        Text(message)
            .onTapGesture {
                guard let path = Bundle.main.path(forResource: "selectSE", ofType: "mp3") else {
                    print("Sound file not found")
                    return
                }
                let url = URL(fileURLWithPath: path)
                do {
                    let fileData = try Data(contentsOf: url)
                    storedURL = saveDataFile(data: fileData, fileName: "test.mp3", folderName: "testFolder")
                    
                    print("File Writing on View -> Success \(storedURL?.absoluteString ?? "nil") ")
                } catch {
                    print("Data.init(contentsOf:) failed: \(error)")
                }
                
                playerManager.play(url: storedURL!) //<-
                print("end of code")
                
            }
    }
    
    //...
}

class AudioPlayerManager {
    static let shared = AudioPlayerManager()
    
    var audioPlayer: AVAudioPlayer?
    
    func play(url: URL) {
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue) //<- No `let`
            audioPlayer?.play()
            print("sound is playing")
        } catch let error {
            print("Sound Play Error -> \(error)")
        }
    }
}

You have no need to copy the sound file each time onTapGesture, but it is not critical and you may improve it later.

How to load mp3 on Library Folder and play it.
 
 
Q