I solved this problem. This audioplayback design was not good. Recursive call increase memory. So, I find out another design approach. Dependency Injection. And, then memory problem has been solved!
Post
Replies
Boosts
Views
Activity
Sorry. I forgot attaching my code.
VIEW
import SwiftUI
struct ContentView: View {
let apm = AudioPlayManager()
var body: some View {
Button(action: {
self.apm.startPlayAudio(){ audioMangeCompletion in
print("play is finished")
}
}, label: {
Text("Push")
})
}
}
AudioPlayManager
import SwiftUI
import AVFoundation
public class AudioPlayManager{
let items:[String] = ["sound01", "sound02", "sound03"]
let isLoopPlay = true
var playingIndex = 0
let apm = AudioPlayFunction.shared
func startPlayAudio(completion: @escaping (Bool) -> Void){
playAudio() { isAudioPlayComplete in
completion(true)
}
}
private func playAudio(completion: @escaping (Bool) -> Void){
let i = playingIndex
print("i: \(i) items.count: \(items.count)")
if(i >= items.count && isLoopPlay){
playingIndex = 0
let fileURL = URL(fileURLWithPath: Bundle.main.path(forResource: items[0], ofType: "mp3") ?? "")
print("fileURL: \(fileURL)")
apm.playMP3(url: fileURL){ playMP3complete in
self.playingIndex += 1
self.playAudio() { playAudioCompletion in
completion(true)
}
}
} else {
let fileURL = URL(fileURLWithPath: Bundle.main.path(forResource: items[i], ofType: "mp3") ?? "")
print("fileURL: \(fileURL)")
apm.playMP3(url: fileURL){ playMP3complete in
self.playingIndex += 1
self.playAudio() { playAudioCompletion in
completion(true)
}
}
}
}
}
AudioPlayFunction
import Foundation
import AVFoundation
class AudioPlayFunction: NSObject, AVAudioPlayerDelegate {
static let shared = AudioPlayFunction()
var audioPlayer: AVAudioPlayer?
private var observer: NSObjectProtocol?
override init() {
super.init()
}
func playMP3(url: URL, completion: @escaping (Bool) -> Void){
do {
print("url: \(url)")
audioPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
audioPlayer?.delegate = self
audioPlayer?.play()
print("sound is playing")
} catch let error {
print("Sound Play Error -> \(error)")
}
if(observer == nil){
print("create observer")
observer = NotificationCenter.default.addObserver(forName: NSNotification.Name("audioStatus"), object: nil, queue: .main){ (_) in
let speechStatus = UserDefaults.standard.value(forKey: "audioStatus") as? Bool ?? false
if speechStatus {
print("audio completion signal is received")
completion(true)
}
}
}
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
print("Audio Play Stop ")
UserDefaults.standard.set(true, forKey: "audioStatus")
NotificationCenter.default.post(name:NSNotification.Name("audioStatus"), object: nil)
}
public func stopSpeech(){
self.audioPlayer?.stop()
print("User stop audio play")
UserDefaults.standard.set(true, forKey: "audioStatus")
NotificationCenter.default.post(name:NSNotification.Name("audioStatus"), object: nil)
}
}
Thanks
No one may know on it. so, for kind of a matter, other design pattern may be required...
Delegation or .sink is just for sending a signal(not value..). If value pass is required, then management side should manage that?
I keep investigating on it.
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"
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: "")!)
}
}
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
For posting code block, I replies on answers.
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)
Thank you for sharing the solution.
That helps a lot.
Following the keywords that these articles write down, I searched another information for SwiftUI. Then I found that.
https://stackoverflow.com/questions/57151335/how-to-animate-images-in-swiftui-to-play-a-frame-animation
(I choose the way to split animated gifs to multiple png images)
Thanks to your information, I could reach the solution.
Thank you so much :-)
Thank you for sharing the solution.
That helps a lot.
Following the keywords that these articles write down, I searched another information for SwiftUI. Then I found that.
https://stackoverflow.com/questions/57151335/how-to-animate-images-in-swiftui-to-play-a-frame-animation
Thanks to your information, I could reach the solution.
Thank you so much :-)
@Claude31
I've tried to set as "Original image" on Attributes inspector of xcAssets.
Then, problem has been solved. The picture of the body area of png image has been shown.
Thank you so much! :-)
@OOPer
Thank you for your reply :-)