I'm trying to implement iOS audio recording using RoboVM using the Apple's AudioQueue guide and their sample SpeakHere project and am running into this error:
No @Marshaler found for parameter 1 of @Callback method <AQRecorder: void HandleInputBuffer(AQRecorder,org.robovm.apple.audiotoolbox.AudioQueue,org.robovm.apple.audiotoolbox.AudioQueueBuffer,org.robovm.apple.coreaudio.AudioTimeStamp,int,org.robovm.apple.coreaudio.AudioStreamPacketDescription)>
Any ideas? Here's the code I'm using:
Main.java:
import org.robovm.apple.coregraphics.CGRect;
import org.robovm.apple.foundation.NSAutoreleasePool;
import org.robovm.apple.uikit.UIApplication;
import org.robovm.apple.uikit.UIApplicationDelegateAdapter;
import org.robovm.apple.uikit.UIApplicationLaunchOptions;
import org.robovm.apple.uikit.UIButton;
import org.robovm.apple.uikit.UIButtonType;
import org.robovm.apple.uikit.UIColor;
import org.robovm.apple.uikit.UIControl;
import org.robovm.apple.uikit.UIControlState;
import org.robovm.apple.uikit.UIEvent;
import org.robovm.apple.uikit.UIScreen;
import org.robovm.apple.uikit.UIWindow;
public class IOSDemo extends UIApplicationDelegateAdapter {
private UIWindow window = null;
@Override
public boolean didFinishLaunching(UIApplication application,
UIApplicationLaunchOptions launchOptions) {
final AQRecorder aqRecorder = new AQRecorder();
final UIButton button = UIButton.create(UIButtonType.RoundedRect);
button.setFrame(new CGRect(115.0f, 121.0f, 91.0f, 37.0f));
button.setTitle("Start", UIControlState.Normal);
button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener() {
@Override
public void onTouchUpInside(UIControl control, UIEvent event) {
if(button.getTitle(UIControlState.Normal) == "Stop"){
aqRecorder.stopRecord();
button.setTitle("Start", UIControlState.Normal);
}
else{
aqRecorder.startRecord();
button.setTitle("Stop", UIControlState.Normal);
}
}
});
window = new UIWindow(UIScreen.getMainScreen().getBounds());
window.setBackgroundColor(UIColor.lightGray());
window.addSubview(button);
window.makeKeyAndVisible();
try {
aqRecorder.setUpAudioFormat();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
public static void main(String[] args) {
try (NSAutoreleasePool pool = new NSAutoreleasePool()) {
UIApplication.main(args, null, IOSDemo.class);
}
}
}
AQRecorder.java:
import org.robovm.apple.audiotoolbox.AudioFile;
import org.robovm.apple.audiotoolbox.AudioQueue;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer;
import org.robovm.apple.audiotoolbox.AudioQueue.AudioQueuePtr;
import org.robovm.apple.coreaudio.AudioFormat;
import org.robovm.apple.coreaudio.AudioStreamBasicDescription;
import org.robovm.apple.coreaudio.AudioStreamPacketDescription;
import org.robovm.apple.coreaudio.AudioTimeStamp;
import org.robovm.rt.bro.annotation.Callback;
import org.robovm.rt.bro.ptr.FunctionPtr;
import org.robovm.rt.bro.ptr.VoidPtr;
public class AQRecorder {
AudioStreamBasicDescription mDataFormat; // 2
AudioQueue mQueue; // 3
//AudioQueueBufferRef mBuffers[kNumberBuffers]; // 4
AudioFile mAudioFile; // 5
int bufferByteSize; // 6
int mCurrentPacket; // 7
boolean mIsRunning; // 8
public void startRecord(){
mQueue.start(null);
}
public void stopRecord(){
mQueue.stop(true);
}
@Callback
static void HandleInputBuffer(
AQRecorder aqData,
AudioQueue inAQ,
AudioQueueBuffer inBuffer,
AudioTimeStamp inStartTime,
int inNumPackets,
AudioStreamPacketDescription inPacketDesc
) {
AQRecorder pAqData = aqData; // 1
if (inNumPackets == 0 && pAqData.mDataFormat.mBytesPerPacket() != 0)
inNumPackets = inBuffer.mAudioDataByteSize() / pAqData.mDataFormat.mBytesPerPacket();
if (!aqData.mIsRunning) // 5
return;
System.out.println(inBuffer.mAudioData());
}
void setUpAudioFormat() throws NoSuchMethodException{
mDataFormat = new AudioStreamBasicDescription(
16000, // mSampleRate
AudioFormat.LinearPCM, // mFormatID
(1 << 2), // mFormatFlags
512, // mBytesPerPacket
1, // mFramesPerPacket
512, // mBytesPerFrame
1, // mChanneslPerFrame
16, // mBitsPerChannel
0 // mReserved
);
AudioQueuePtr mQueuePtr = new AudioQueuePtr();
mQueuePtr.set(mQueue);
VoidPtr self = new VoidPtr();
@SuppressWarnings("rawtypes")
Class[] cArg = new Class[6];
cArg[0] = AQRecorder.class;
cArg[1] = AudioQueue.class;
cArg[2] = AudioQueueBuffer.class;
cArg[3] = AudioTimeStamp.class;
cArg[4] = int.class;
cArg[5] = AudioStreamPacketDescription.class;
FunctionPtr handleInputBuffer = new FunctionPtr((AQRecorder.class).getDeclaredMethod("HandleInputBuffer", cArg));
AudioQueue.newInput(mDataFormat, handleInputBuffer, self, null, "", 0, mQueuePtr);
}
};
With RoboVM 1.0.0-beta-3 I was finally able to get audio record and playback working. Not sure why the recording audio queue takes up to 20 seconds to stop, but here is some sample code that works in the simulator and on my iPhone 4:
Main Class:
AQRecorderState:
AudioRecord:
AQPlayerState:
AudioTrack: