This what I use:
if let filePath = NSBundle.mainBundle().pathForResource("Aurora", ofType: "aiff") {
//it doesn't gets here
let fileURL = NSURL(fileURLWithPath: filePath)
var soundID:SystemSoundID = 0
AudioServicesCreateSystemSoundID(fileURL, &soundID)
AudioServicesPlaySystemSound(soundID)
}
How to do this? I've read a lot about this, but nothing has worked.
Testing on a real device.
You can use the following code:
func getSoundID() -> SystemSoundID {
var soundID: SystemSoundID = 0
let soundURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), "Glass", "mp3", nil)
AudioServicesCreateSystemSoundID(soundURL, &soundID)
return soundID
}
let soundId: SystemSoundID = playSound()
AudioServicesPlaySystemSound(soundId)
You can also check your system is playing the sound or not by putting sound ID as follows:
AudioServicesPlaySystemSound(1200)
Please also check the value of the system.
Related
I'm working on a old Swift tutorial (Swift 2.0) that's posted on Ray Wenderlich's web site (https://www.raywenderlich.com/2185-how-to-make-a-letter-word-game-with-uikit-and-swift-part-3-3) and I'm running into an error when I tried to re-setup a function called "preloadAudioEffects" in Swift 4.2 . The error? appendingPathComponent' is unavailable: Use appendingPathComponent on URL instead.
I've tried to rename the old Swift code [Ex: NSBundle to Bundle , stringByAppendingPathComponent to appendingPathComponent()], but I'm still running into some syntax issues due to my inexperience with Swift.
This is the original code:
func preloadAudioEffects(effectFileNames:[String]) {
for effect in AudioEffectFiles {
//1 get the file path URL
let soundPath = NSBundle.mainBundle().resourcePath!.stringByAppendingPathComponent(effect)
let soundURL = NSURL.fileURLWithPath(soundPath)
//2 load the file contents
var loadError:NSError?
let player = AVAudioPlayer(contentsOfURL: soundURL, error: &loadError)
assert(loadError == nil, "Load sound failed")
//3 prepare the play
player.numberOfLoops = 0
player.prepareToPlay()
//4 add to the audio dictionary
audio[effect] = player
}
}
And this is what I've tried to do via following the suggestions in Xcode:
func preloadAudioEffects(effectFileNames:[String]) {
for effect in AudioEffectFiles {
//1 get the file path URL
let soundPath = Bundle.main.resourcePath!.appendingPathComponent(effect)
let soundURL = NSURL.fileURL(withPath: soundPath)
//2 load the file contents
var loadError:NSError?
let player = AVAudioPlayer(contentsOfURL: soundURL, error: &loadError)
assert(loadError == nil, "Load sound failed")
//3 prepare the play
player.numberOfLoops = 0
player.prepareToPlay()
//4 add to the audio dictionary
audio[effect] = player
}
}
Get the full path to the sound file and convert it to a URL by using NSURL.fileURLWithPath().
Call AVAudioPlayer(contentsOfURL:error:) to load a sound file in an audio player.
Set the numberOfLoops to zero so that the sound won’t loop at all. Call prepareToPlay() to preload the audio buffer for that sound.
Finally, save the player object in the audio dictionary, using the name of the file as the dictionary key.
Just replace resourcePath with resourceURL
let soundURL = Bundle.main.resourceURL!.appendingPathComponent(effect)
and you have to wrap the AVAudioPlayer initializer in a try block
func preloadAudioEffects(effectFileNames:[String]) {
for effect in AudioEffectFiles {
let soundURL = Bundle.main.resourceURL!.appendingPathComponent(effect)
//2 load the file contents
do {
let player = try AVAudioPlayer(contentsOf: soundURL)
//3 prepare the play
player.numberOfLoops = 0
player.prepareToPlay()
//4 add to the audio dictionary
audio[effect] = player
} catch { print(error) }
}
}
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)
}
}
}
}
I would like to write an app in swift 3 in order to play queued audio files without any gap, crack or noise when passing from one to another.
My first try was using AvAudioPlayer and AvAudioDelegate (AVAudioPlayer using array to queue audio files - Swift), but I don't know how to preload the next song to avoid gap. Even if I know how to do it, I am not certain it is the best way to achieve my goal.
AVQueuePlayer seems to be a better candidate for the job, it is made for that purpose, but I don't find any example to help me out.
Maybe it is only a problem of preloading or buffering? I am a bit lost in this ocean of possibilities.
Any suggestion is welcomed.
It is far to be perfect, specially if you want to do it twice or more ("file exist" error), but it can serve as a base.
What it does is taking two files (mines are aif samples of ap. 4 sec.), encode them in one file and play the resulting files. If you have hundreds of them, assembled aleatory or not, it can make great fun.
All credits for the mergeAudioFiles function goes to #Peyman and #Pigeon_39. Concatenate two audio files in Swift and play them
Swift 3
import Cocoa
import AVFoundation
var action = AVAudioPlayer()
let path = Bundle.main.path(forResource: "audiofile1.aif", ofType:nil)!
let url = URL(fileURLWithPath: path)
let path2 = Bundle.main.path(forResource: "audiofile2.aif", ofType:nil)!
let url2 = URL(fileURLWithPath: path2)
let array1 = NSMutableArray(array: [url, url2])
class ViewController: NSViewController, AVAudioPlayerDelegate
{
#IBOutlet weak var LanceStop: NSButton!
override func viewDidLoad()
{
super.viewDidLoad()
}
override var representedObject: Any?
{
didSet
{
// Update the view, if already loaded.
}
}
#IBAction func Lancer(_ sender: NSButton)
{
mergeAudioFiles(audioFileUrls: array1)
let url3 = NSURL(string: "/Users/ADDUSERNAMEHERE/Documents/FinalAudio.m4a")
do
{
action = try AVAudioPlayer(contentsOf: url3 as! URL)
action.delegate = self
action.numberOfLoops = 0
action.prepareToPlay()
action.volume = 1
action.play()
}
catch{print("error")}
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool)
{
if flag == true
{
}
}
var mergeAudioURL = NSURL()
func mergeAudioFiles(audioFileUrls: NSArray) {
//audioFileUrls.adding(url)
//audioFileUrls.adding(url2)
let composition = AVMutableComposition()
for i in 0 ..< audioFileUrls.count {
let compositionAudioTrack :AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
let asset = AVURLAsset(url: (audioFileUrls[i] as! NSURL) as URL)
let track = asset.tracks(withMediaType: AVMediaTypeAudio)[0]
let timeRange = CMTimeRange(start: CMTimeMake(0, 600), duration: track.timeRange.duration)
try! compositionAudioTrack.insertTimeRange(timeRange, of: track, at: composition.duration)
}
let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! as NSURL
self.mergeAudioURL = documentDirectoryURL.appendingPathComponent("FinalAudio.m4a")! as URL as NSURL
let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
assetExport?.outputFileType = AVFileTypeAppleM4A
assetExport?.outputURL = mergeAudioURL as URL
assetExport?.exportAsynchronously(completionHandler:
{
switch assetExport!.status
{
case AVAssetExportSessionStatus.failed:
print("failed \(assetExport?.error)")
case AVAssetExportSessionStatus.cancelled:
print("cancelled \(assetExport?.error)")
case AVAssetExportSessionStatus.unknown:
print("unknown\(assetExport?.error)")
case AVAssetExportSessionStatus.waiting:
print("waiting\(assetExport?.error)")
case AVAssetExportSessionStatus.exporting:
print("exporting\(assetExport?.error)")
default:
print("Audio Concatenation Complete")
}
})
}
}
I,m creating a app the use a lot of sfx and background music. But i can't find the best way to inherite this type of data through View Controllers. Do i have to initialize my audios in every view controller? But what if i want to stop a music that started in a preview VC?
This is the code that i'm using:
do {
// Music BG
let resourcePath = NSBundle.mainBundle().pathForResource("MusicaBg", ofType: "wav")!
let url = NSURL(fileURLWithPath: resourcePath)
try musicPlayer = AVAudioPlayer(contentsOfURL: url)
// SFX for Button
let resourcePath2 = NSBundle.mainBundle().pathForResource("botaoApertado", ofType: "wav")!
let url2 = NSURL(fileURLWithPath: resourcePath2)
try botaoApertado = AVAudioPlayer(contentsOfURL: url2)
} catch let err as NSError {
print(err.debugDescription)
}
What's the best way to do that?
You're probably looking for the Singleton pattern since you need a single canonical instance of background music, that any ViewController can interact with.
Then any time you need to change the music you simple call the corresponding method on e.g. AudioManager.sharedInstance from anywhere, and as you keep moving through the app the music will continue.
You would probably want to start the music in your AppDelegate or FirstViewController.
For example, with the code you've given, you might want something like
class AudioManager {
static let sharedInstance = AudioManager()
var musicPlayer: AVAudioPlayer?
var botaoApertado: AVAudioPlayer?
private init() {
}
func startMusic() {
do {
// Music BG
let resourcePath = NSBundle.mainBundle().pathForResource("MusicaBg", ofType: "wav")!
let url = NSURL(fileURLWithPath: resourcePath)
try musicPlayer = AVAudioPlayer(contentsOfURL: url)
// SFX for Button
let resourcePath2 = NSBundle.mainBundle().pathForResource("botaoApertado", ofType: "wav")!
let url2 = NSURL(fileURLWithPath: resourcePath2)
try botaoApertado = AVAudioPlayer(contentsOfURL: url2)
} catch let err as NSError {
print(err.debugDescription)
}
}
}
func stopMusic() { // implementation
}
As soon as you write AudioManager.sharedInstance.startMusic() the sharedInstance static variable will be initialized (once, since it's a static property) and then startMusic() will be called on it.
If you later call AudioManager.sharedInstance.stopMusic() it will use the same sharedInstance you initialized previously, and stop the music.
Post any questions you have in the comments.
I'm trying to export an AVMutableComposition using AVAssetExportSession to a mp4 file in NSTemporaryDirectory(). It works great on iOS8 but on iOS7 I got a -1100 error on exportAsynchronouslyWithCompletionHandler
The requested URL was not found on this server.
My composition contains 2 AVAssets, both getting sources from the web, one for the video, one for the audio.
At first, I though that this was a folder problem but I can create / delete a file using NSFileManager without trouble.
I don't really understand why it should work on iOS8 but not on iOS7 and I didn't spot any particular "introduced=8.0"
Here is my code:
func compose() {
var composition = AVMutableComposition()
var videoTrack = composition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
var audioTrack = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
var videoAsset = AVAsset.assetWithURL(videoUrl) as! AVAsset //Not a local URL
var audioAsset = AVAsset.assetWithURL(audioUrl) as! AVAsset //Not a local URL
var videoTimeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
var videoError: NSError?
if let tracks = videoAsset.tracksWithMediaType(mediaType) as? [AVMediaTypeVideo] where tracks.isEmpty == false {
videoTrack.insertTimeRange(videoTimeRange, ofTrack: tracks[0], atTime: duration, error: &videoError)
}
var audioTimeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration)
var audioError: NSError?
if let tracks = audioAsset.tracksWithMediaType(mediaType) as? [AVMediaTypeAudio] where tracks.isEmpty == false {
audioTrack.insertTimeRange(audioTimeRange, ofTrack: tracks[0], atTime: duration, error: &audioError)
}
self.export(composition)
}
func export(composition: AVMutableComposition) {
self.exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality)
let filename = "composition.mp4"
let outputPath = NSTemporaryDirectory().stringByAppendingPathComponent(filename)
//Check if file already exists and delete it if needed
if let fileUrl = NSURL(fileURLWithPath: outputPath) {
let manager = NSFileManager.defaultManager()
if manager.fileExistsAtPath(outputPath) {
var error: NSError? = nil
if manager.removeItemAtPath(outputPath, error: &error) == true {
println("Removed")
}
}
//File should be 30 seconds max
let maxDuration = CMTimeMakeWithSeconds(30.0, composition.duration.timescale)
let exportInterval = CMTimeRangeMake(kCMTimeZero, maxDuration)
self.exporter.outputFileType = AVFileTypeMPEG4
self.exporter.outputURL = fileUrl
self.exporter.timeRange = exportInterval
self.exporter.exportAsynchronouslyWithCompletionHandler({ () -> Void in
dispatch_async(dispatch_get_main_queue(), {
if self.exporter.status == AVAssetExportSessionStatus.Completed {
println("Success")
}
else {
println(self.exporter.error?.localizedDescription)
//The requested URL was not found on this server.
}
})
})
}
I'm getting stuck on this problem for a while now, so any advice would be much appreciated !
Thanks in advance,
Cordially, Louis.
Edit 7th July:
I found out that it is not possible to do so since AVMutableComposition does not support web URLs in iOS7.
For now, I haven't been able to find a workaround and would really appreciate any advice.