9

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?

0

2 Answers 2

6

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]; ... } 
Sign up to request clarification or add additional context in comments.

2 Comments

True, but I'd argue that the underlying reason 'self' is missing is because this is not a method of the class, not because it's a C-style function. Any Objective-C object is usable within this function - it's just that 'self' is only available in methods of a class.
I believe that is what I stated "self only exists in Objective-C methods and that is a C style function" had a typo originally where it said an but that should convey the same message.
4

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.