In a recent design session we were looking to recreate the sounds drop down menu from iCal. This menu is attached to an alarm within iCal. One of the interesting things about this menu is that as you change the sound name, the sound is played. Borrowing from this cocoa dev article, I dived into converting the sample code into RubyCocoa. During my conversion, I ran into some snags that I would like to point out before we get to my methods.
Tips & Tricks
1) I tried many times to get the fileExistsAtPath_isDirectory function to return a boolean in the "isDir" variable that is passed into the function, but in the end I could not get that function to work correctly. That is why I added the lines below it to get the attributes of the file to find out if it is a directory. This is an extra step, but in my testing it seemed to be a necessary one.
2) The NSSound class makes many references to playing sounds by their "name", but there is no mention to returning a list of available sound names. The functions below should help you keep a list of sound names without having to hardcode their "names"
3) Make sure to check the documentation for the NSFileManager class. This class has many different methods for parsing a directory, and some return a list of file names, while others return file objects.
Functions:
The function below loops through some system directories and finds folders named "Sounds". Once a folder has been located it will loop through the files and load them into the NSMutableDictionary with the key being the name of the file without the extension. For instance Basso.aiff will become Basso. Once we have a list of sounds stored in a dictionary, I created simple wrapper methods to help retrieve the sound files and a list of the sound names.
Method To Create A Sounds Dictionary
def generateSoundsList()
@soundsList = OSX::NSMutableDictionary.alloc.init
knownSoundTypes = OSX::NSSound.soundUnfilteredFileTypes()
libs = OSX::NSSearchPathForDirectoriesInDomains(OSX::NSLibraryDirectory, OSX::NSUserDomainMask | OSX::NSLocalDomainMask | OSX::NSSystemDomainMask, true);
fm = OSX::NSFileManager.defaultManager()
libs.each { | dir |
fileExists = nil
isDir = nil
error = nil
(fileExists, isDir) = fm.fileExistsAtPath_isDirectory(dir, isDir)
attrs = fm.attributesOfItemAtPath_error(dir, nil)
if (fm.fileExistsAtPath(dir))
attrs = fm.attributesOfItemAtPath_error(dir, nil)
if ((attrs["NSFileType"] == "NSFileTypeDirectory"))
fm.contentsOfDirectoryAtPath_error(dir, error).each { | fileName |
if (fileName == "Sounds")
fm.enumeratorAtPath(dir + "/" + fileName).allObjects.each { | file |
if (knownSoundTypes.containsObject(file.pathExtension))
@soundsList.setValue_forKey(dir + "/" + fileName + "/" + file, file.stringByDeletingPathExtension)
end
}
end
}
end
end
}
end
Method To Get A Sound By Name
def getSound(name)
return @soundsList.objectForKey(name)
end
Method To Create A Sounds Dictionary
def getSoundNames()
return @soundsList.keysSortedByValueUsingSelector ("caseInsensitiveCompare:")
end
Hopefully these methods will help you in your next rubycocoa project.
Comments