I googled around and I find a million results to this subject. But none of the pages helps me. I think that I have a very common problem. I'm playing around with audio programming especially working with audio queues. The purpose of my program does not matter for explaining the problem. But in a nutshell: I get an error when I try to call an objective-c function from c++ code. So here is my code that contains the error: AudioRecorder.h:
#import <Foundation/Foundation.h>
@interface AudioRecorder : NSObject {
}
-(void)setup;
-(void)startRecording;
-(void)endRecording;
-(void)playAlarmSound;
@end
And this is the implementation: AudioRecorder.mm:
#import "AudioRecorder.h"
#include <AudioToolbox/AudioToolbox.h>
#include <iostream>
using namespace std;
@implementation AudioRecorder
static const int kNumberBuffers = 3;
...
static void HandleInputBuffer (void *aqData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumPackets,
const AudioStreamPacketDescription *inPacketDesc ) {
AQRecorderState *pAqData = (AQRecorderState *) aqData;
if (inNumPackets == 0 &&
pAqData->mDataFormat.mBytesPerPacket != 0)
inNumPackets =
inBuffer->mAudioDataByteSize / pAqData->mDataFormat.mBytesPerPacket;
UInt32 size;
AudioQueueGetPropertySize ( inAQ, kAudioQueueProperty_CurrentLevelMeter, &size );
char* levelMeterData = new char[size];
AudioQueueGetProperty ( inAQ, kAudioQueueProperty_CurrentLevelMeter, levelMeterData, &size );
AudioQueueLevelMeterState* meterState = reinterpret_cast<AudioQueueLevelMeterState*>(levelMeterData);
cout << "mAveragePower = " << meterState->mAveragePower << endl;
cout << "mPeakPower = " << meterState->mPeakPower << endl;
delete levelMeterData;
[self playAlarmSound]; //<--- here I get the error开发者_如何学编程: Use of undeclared identifier 'self'
if (pAqData->mIsRunning == 0)
return;
AudioQueueEnqueueBuffer ( pAqData->mQueue, inBuffer, 0, NULL );
}
...
-(void)playAlarmSound {
NSLog(@"Alarmsound....");
}
When I omit "[self playAlarmSound];" then everything works fine. So how do I call this Objective-C function from my C++ code?
self
only exists in Objective-C methods and that is a C style function. You need to pass self
from an Objective-C method to the inUserData when you set up the callback, then cast it back to the correct type.
//This is an example for using AudioQueueNewInput
//Call this in an Objective-C method passing self to inUserData
AudioQueueNewInput (
const AudioStreamBasicDescription *inFormat,
AudioQueueInputCallback inCallbackProc,
// this is where you will pass (void*)self
void *inUserData,
CFRunLoopRef inCallbackRunLoop,
CFStringRef inCallbackRunLoopMode,
UInt32 inFlags,
AudioQueueRef *outAQ
);
And your original implementation
static void HandleInputBuffer (void *aqData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumPackets,
const AudioStreamPacketDescription *inPacketDesc )
{
AudioRecorder *ar_instance = (AudioRecorder*)aqData;
...
[ar_instance playAlarmSound];
...
}
This is indeed a common problem. self
doesn't work here because this is not a method of the AudioRecorder
class, not because it's Objective-C code. You're in an Objective-C++ file, so all valid Objective-C code will work. [anAudioRecorder playAlarmSound]
will work fine, provided you have a good reference to anAudioRecorder
.
So how do we get a reference if we don't have access to self
? The usual way is to use the void* aqData
argument of this function as a pointer to your AudioRecorder
object. When you registered this callback, you told it what the void*
argument would be, in this case a pointer to your AQRecorderState
object or struct, which you don't seem to use anyway. Instead you can use a pointer to self
when you register so that you can use that object here.
Another option would be to use a shared AudioRecorder
object, in which case you would call something like [AudioRecorder sharedInstance]
(a class, not an instance, method) to get the AudioRecorder
object you want. Because the other answer here elaborates on the first method, here's how to use the shared instance option: Add a static instance of AudioRecorder
and a class method sharedInstance
to your AudioRecorder
object, like this:
static AudioRecorder* sharedMyInstance = nil;
+ (id) sharedInstance {
@synchronized(self) {
if( sharedMyInstance == nil )
sharedMyInstance = [[super allocWithZone:NULL] init];
}
return sharedMyInstance;
} // end sharedInstance()
Then, when you want to use the AudioRecorder
from your callback, you can get the shared instance using [AudioRecorder sharedInstance]
. This is a very useful paradigm if there's only going to be one AudioRecorder
- it eliminates a lot of reference passing.
精彩评论