Reputation: 41
I am trying to get my PyObjC app to record audio when the user clicks a button. I am attempting to use the AVAuidioRecorder class. My code is:
@IBAction
def startRecording_(self, sender):
audioPath = '~/Desktop/recordTest.mp3'
audioPathStr = NSString.stringByExpandingTildeInPath(audioPath)
audioURL = NSURL.fileURLWithPath_(audioPathStr)
audioSettings = {'AVFormatIDKey': 'kAudioFormatAppleIMA4', 'AVSampleRateKey': 1600.0, 'AVNumberOfChannelsKey': 1 }
audioDict = NSDictionary.dictionaryWithDictionary_(audioSettings)
(recorder, error) = AVAudioRecorder.alloc().initWithURL_settings_error_(audioURL, audioDict, objc.nil)
recorder.record()
When I run the above code, I get the following error:
<type 'exceptions.TypeError'>: 'NoneType' object is not iterable
It seems that the initWithURL_settings_error_
method is expecting an iterable object as its third parameter. However, I thought when I use a PyObjC method that calls for an error parameter, I can pass objc.nil
or None
to that parameter.
When I use a similar syntax on an NSString method:
(names, error) = NSString.stringWithContentsOfFile_encoding_error_(u"/usr/share/dict/propernames", NSASCIIStringEncoding, objc.nil)
the code runs.
Why does my call to the AVAudioRecord method not work? Is it because the method calls for an outError while the NSString method calls for an error?
Upvotes: 0
Views: 811
Reputation: 2810
The code doesn't work with this AVFoundation class because PyObjC does not have metadata descriptions for that framework. It therefore does know what kind of argument the last argument is, it only knows that it is pointer to an object but doesn't know that it is used as a pass-by-reference output argument.
To manually inspect what PyObjC knows about this argument::
>> import AVFoundation
>>> AVFoundation.AVAudioRecorder.initWithURL_settings_error_.__metadata__( ['arguments'][-1]
{'null_accepted': True, 'already_retained': False, 'type': '^@', 'already_cfretained': False}
The type should have been "o^@" here.
This is with Apple's build of PyObjC: you can use "import AVFoundation" with that build, and that will use the BridgeSupport data files inside the AVFoundation framework. Sadly enough that data is incomplete and doesn't have information on this method and that's why the type is wrong here.
It is fairly easy to fix this for this particular method by using PyObjCs metadata API:
import objc
objc.registerMetaDataForSelector(b"AVAudioRecorder", b"initWithURL:settings:error:",
dict(
arguments={
4: dict(type_modifier=objc._C_OUT),
}
))
Fixing the metadata for all of AVFoundation will be more work :-(
Upvotes: 3