SKAction playsoundfilenamed fails to load sound - swift

No matter what wav file I tried to play in an project, I keep getting the same error. The error states: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Resource namedfile.wav can not be loaded'
I cannot get any sound of any kind to load using SKAction.playSoundFilenamed. I have made sure that the file is names correctly and that doesn't seem to be the problem.
I have tested this in several projects, including the following test Game project wherein I use all default code except for a call to the SKAction
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.text = "Hello, World!";
myLabel.fontSize = 65;
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
let soundfile = SKAction.playSoundFileNamed("soundProject.wav", waitForCompletion: false)
runAction(soundfile)
self.addChild(myLabel)
}
I cannot get any sound of any kind to load using SKAction.playSoundFilenamed. I already checked to made sure that the file is named correctly and that it exits in the bundle. Any help would be greatly appreciated. Thank you
UPDATE I Ran my attached code on a different computer, and it compliled and ran perfectly. There must be something wrong with my xcode/simulator. Does anyone know how to reset it? Thanks

When you select your sound file in XCode, access the inspector on the right side, then make sure the file is selected for your target(s).

What color is your Sounds folder?
If it's blue, it means it's a Folder Reference.
If it's yellow, it's a Group.
You Choose Folder Reference -vs- Group when you import your folder.
I've found that SKAction.playSoundFilenamed functions correctly only when it's loaded as a Group (Yellow).

For me, this was the issue. Both of these compiled fine and gave no errors, but only the latter actually played the sound.
Did not work:
SKAction.playSoundFileNamed("combo.mp3", waitForCompletion: true)
Did work:
node.run(SKAction.playSoundFileNamed("combo.mp3", waitForCompletion: true))
Good luck!

Here is another lead that might help you.
Sometimes SKAction is not very handy with audio files.
I haven't found in which case exactly (might be : size file, sound length, ...).
In that case, you would want to use AVAudioPlayerinstead of it.
In order to not write your own "player", I suggest you to use an existing one. Here is one I've already used (SKTAudio) : https://github.com/raywenderlich/SKTUtils/blob/master/SKTUtils/SKTAudio.swift
Here is how to use it :
// For background audio (playing continuously)
SKTAudio.sharedInstance().playBackgroundMusic("music.wav") // Start the music
SKTAudio.sharedInstance().pauseBackgroundMusic() // Pause the music
SKTAudio.sharedInstance().resumeBackgroundMusic() // Resume the music
// For short sounds
SKTAudio.sharedInstance().playSoundEffect("sound.wav") // Play the sound once
Please let me know if the file is not played even with AVAudioPlayer. If so, it might be a problem with it and not the way it's played.

for swift 3.0:
SKAction.run {
SKAction.playSoundFileNamed("soundProject.wav",
waitForCompletion: false)
}
and of course, verify if the resource is in project/copy Phases.

I identified the issue. I had to change the sound settings on my mac through the system preferences. Once I changes all of the settings to use only the internal speakers, the problem was resolved.

swift 5:
Instead of
SKAction.repeatForever(SKAction.playSoundFileNamed("Sounds/theme.mp3",
waitForCompletion: true))
I wrote
SKNode().run(
SKAction.repeatForever(SKAction.playSoundFileNamed("Sounds/theme.mp3",
waitForCompletion: true))
)
And it works good.

Related

SWIFT: Load different reality Scenes via Variable

So, i am trying to load different Scenes made in RealityComposer, depending on a variable.
What worked so far:
let SceneAnchor = try! Experience1.loadScene()
arView.scene.anchors.append(SceneAnchor)
return arView
Now i looked into apples Documentation and saw the possibility of:
if let anchor = try? Entity.loadAnchor(named: "Scene") {
arView.scene.addAnchor(anchor)
}
where i thought i could just change "Scene" to "Scene(myVar)"
But once i have more than one scene in my file the first solution doesnt work anymore
and the second one doesnt work as well.
What am i missing?
I also looked into working with filenames and was able to make an array of all my .reality Files and Store them in an Array, so i thought i could recall that via the index, but
arrayName[1].loadScene() doesnt seem to work either, eventhough i can print the filenames to console.
Thanks in advance :)
The fact is that Reality Composer creates a separate static method for each scene load. The name of such method is load+scene name. So, if you have 2 scenes in your Exprerience.xcproject with the names Scene and Scene1, then you have 2 static methods
let scene = Experience.loadScene()
let scene1 = Experience.loadScene1()
Unfortunately it is not possible to use the scene name as a parameter, so, you need to use the switch statement in your app to select the appropriate method.

Swift/SpriteKit - Any way to pool/cache SKReferenceNodes?

Is there a way to pool/cache SKReferenceNodes in SpriteKit using Swift?
I am creating a game using xCodes visual level editor. I am creating different .sks files with the visual level editor that I am than calling in code when I need to. I am calling them in code because I am using them to create random levels or obstacles so I don't need all of them added to the scene at once.
At the moment I am doing it like this
I create a convince init method for SKReferenceNodes to init them with URLs. I am doing this because there is some sort of bug calling SKReferenceNodes by file name directly (https://forums.developer.apple.com/thread/44090).
Using such an extension makes makes the code a bit cleaner.
extension SKReferenceNode {
convenience init(roomName: String) {
let path: String
if let validPath = NSBundle.mainBundle().pathForResource(roomName, ofType: "sks") {
path = validPath
} else {
path = NSBundle.mainBundle().pathForResource("RoomTemplate", ofType: "sks")! // use empty roomTemplate as backup so force unwrap
}
self.init(URL: NSURL(fileURLWithPath: path))
}
}
and than in my scenes I can create them and add them like so (about every 10 seconds)
let room = SKReferenceNode(roomName: "Room1") // will cause lag without GCD
room.position = ...
addChild(room)
This works ok but I am getting some lag/stutter when creating these. So I am using GCD to reduce this to basically no stutter. It works well but I am wondering if I can preload all .sks files first.
I tried using arrays to do this but I am getting crashes and it just doesn't seem to work (I also get a message already adding node that has a parent).
I was trying to preload them like so at app launch
let allRooms = [SKReferenceNode]()
for i in 0...3 {
let room = SKReferenceNode(roomName: "Room\(i)")
allRooms.append(room)
}
and than use the array when I need too. This doesn't work however and I am getting a crash when trying to use code like this
let room = allRooms[0]
room.position =
addChild(room) // causes error/crash -> node already has parent
Has anyone done something similar? Is there another way I can pool/cache those reference nodes?. Am i missing something here?
Speaking about the SKReferenceNode preload, I think the policy to be followed is to load your object, find what kind they are and use the official preloading methods available in Sprite-Kit:
SKTextureAtlas.preloadTextureAtlases(_:withCompletionHandler:)
SKTexture.preloadTextures(_:withCompletionHandler:)
To avoid this kind of error you should create separate instances of the nodes.
Try to doing this:
let room = allRooms[0]
room.position = ...
room.removeFromParent()
addChild(room)
I just figured it out, I was just being an idiot.
Using arrays like I wanted to is fine, the problem I had which caused the crash was the following.
When the game scene first loads I am adding 3 rooms but when testing with the arrays I kept adding the same room
let room = allRooms[0]
instead of using a randomiser.
This obviously meant I was adding the same instance multiple times, hence the crash.
With a randomiser, that doesn't repeat the same room, this does not happen.
Furthermore I make sure to remove the room from the scene when I no longer need it. I have a node in the rooms (roomRemover) which fires a method to remove/create a new room once it comes into contact with the player.
This would be the code in DidBeginContact.
guard let roomToRemove = secondBody?.node.parent?.parent?.parent?.parent else { return }
// secondBody is the roomRemover node which is a child of the SKReferenceNode.
// If I want to remove the whole SKReferenceNode I need to add .parent 4 times.
// Not exactly sure why 4 times but it works
for room in allRooms {
if room == roomToRemove {
room.removeFromParent()
}
}
loadRandomRoom()
Hope this helps someone trying to do the same.

CUICatalog: Invalid Request: requesting subtype without specifying idiom (Where is it coming from and how to fix it?)

When I run my SpriteKit game, I receive this error multiple times in the console. As far as I can tell (though I'm not completely sure), the game itself is unaffected, but the error might have some other implications, along with crowding the debug console.
I did some research into the error, and found a few possible solutions, none of which seem to have completely worked. These solutions include turning ignoresSiblingOrder to false, and specifying textures as SKTextureAtlas(named: "atlasName").textureNamed("textureName"), but these did not work.
I think the error is coming somewhere from the use of textures and texture atlases in the assets catalogue, though I'm not completely sure. Here is how I am implementing some of these textures/images:
let Texture = SKTextureAtlas(named: "character").textureNamed("\character1")
character = SKSpriteNode(texture: Texture)
also:
let Atlas = SKTextureAtlas(named: "character")
var Frames = [SKTexture]()
let numImages = Atlas.textureNames.count
for var i=1; i<=numImages; i++ {
let textureName = "character(i)"
Frames.append(Atlas.textureNamed(textureName))
}
for var i=numImages; i>=1; i-- {
let TextureName = "character(i)"
Frames.append(Atlas.textureNamed(textureName))
}
let firstFrame = Frames[0]
character = SKSpriteNode(texture: firstFrame)
The above code is just used to create an array from which to animate the character, and the animation runs completely fine.
For all my other sprite nodes, I initialize with SKSpriteNode(imageNamed: "imageName") with the image name from the asset catalogue, but not within a texture atlas. All the images have #1x, #2x, and #3x versions.
I'm not sure if there are any other possible sources for the error message, or if the examples above are the sources of the error.
Is this just a bug with sprite kit, or a legitimate error with my code or assets?
Thanks!
I have this error too. In my opinion, it's the Xcode 7.2 bug and not your fault. I've updated Xcode in the middle of making an app and this message starts to show up constantly in the console. According to this and that links, you have nothing to fear here.
Product > Clean
seems to do the trick.
Error seems to start popping up when you delete an Item from Asset Catalogue but its reference still stay buried in code somewhere. (In my case it was the default spaceship asset which I deleted.)

SWIFT - Is it possible to save audio from AVAudioEngine, or from AudioPlayerNode? If yes, how?

I've been looking around Swift documentation to save an audio output from AVAudioEngine but I couldn't find any useful tip.
Any suggestion?
Solution
I found a way around thanks to matt's answer.
Here a sample code of how to save an audio after passing it through an AVAudioEngine (i think that technically it's before)
newAudio = AVAudioFile(forWriting: newAudio.url, settings: nil, error: NSErrorPointer())
//Your new file on which you want to save some changed audio, and prepared to be bufferd in some new data...
var audioPlayerNode = AVAudioPlayerNode() //or your Time pitch unit if pitch changed
//Now install a Tap on the output bus to "record" the transformed file on a our newAudio file.
audioPlayerNode.installTapOnBus(0, bufferSize: (AVAudioFrameCount(audioPlayer.duration)), format: opffb){
(buffer: AVAudioPCMBuffer!, time: AVAudioTime!) in
if (self.newAudio.length) < (self.audioFile.length){//Let us know when to stop saving the file, otherwise saving infinitely
self.newAudio.writeFromBuffer(buffer, error: NSErrorPointer())//let's write the buffer result into our file
}else{
audioPlayerNode.removeTapOnBus(0)//if we dont remove it, will keep on tapping infinitely
println("Did you like it? Please, vote up for my question")
}
}
Hope this helps !
One issue to solve:
Sometimes, your outputNode is shorter than the input: if you accelerate the time rate by 2, your audio will be 2 times shorter. This is the issue im facing for now since my condition for saving the file is (line 10)
if(newAudio.length) < (self.audioFile.length)//audiofile being the original(long) audio and newAudio being the new changed (shorter) audio.
Any help here?
Yes, it's quite easy. You simply put a tap on a node and save the buffer into a file.
Unfortunately this means you have to play through the node. I was hoping that AVAudioEngine would let me process one sound file into another directly, but apparently that's impossible - you have to play and process in real time.
Offline rendering Worked for me using GenericOutput AudioUnit. Please check this link, I have done mixing two,three audios offline and combine it to a single file. Not the same scenario but it may help you for getting some idea. core audio offline rendering GenericOutput

Playing a Sound With Monotouch

No matter what I try (build -> content, NSUrl, filename) I get a 'null exception': file not found when I try to play a .caf sound file in monotouch.
//var path = NSBundle.MainBundle.PathForResource("MatchGame", "caf");
//var gameSong = SystemSound.FromFile( new NSUrl(path, false));
var gameSong = SystemSound.FromFile("MatchGame.caf");
gameSong.PlaySystemSound();
I also try combinations using the folder name "images/MatchGame.caf" and moving MatchGame.caf into the root folder.
What am I missing? Thanks a lot.
Here is a link to a video of adding the sound in monotouch. http://www.screencast.com/t/MmE0ZmFh What is wrong?
Bryan,
From looking at your screencast - you are trying to play a mp3 file and not a caf file. Mp3 files are encoded differently and will not play with the SystemSound class that's there (I can't remember if you can do this in Obj-C or not.)
You'll want to use the AVFoundation Namespace and AVAudioPlayer class.
using Monotouch.AVFoundation;
var mediaFile = NSUrl.FromFilename("myMp3.mp3");
var audioPlayer = AVAudioPlayer.FromUrl(mediaFile);
audioPlayer.FinishedPlaying += delegate { audioPlayer.Dispose(); };
audioPlayer.Play();
You might need to tweak the code above - I don't have MonoDevelop to hand but that should help you a little further.
Cheers,
ChrisNTR
You need the first line:
var path = NSBundle.MainBundle.PathForResource("MatchGame", "caf");
Then make sure that your audio file is included in the application by making sure that your CAF file is flagged as "Content" in the Properties pane, otherwise the file is not copied to the resulting application package (your .app)
You can follow the steps documented here (they are for images, but apply the same to audio files):
http://wiki.monotouch.net/HowTo/Images/Add_an_Image_to_your_Project
Additionally, this is a good resource for where you should store files:
http://wiki.monotouch.net/HowTo/Files/HowTo%3a_Store_Files