Can an iOS native interface use NSData (byte[]) as a parameter or return value? - codenameone

Edit: As per the answer below, the answer is YES, it can
I tried looking through the docs but didn't find any reference to this
I tried building but there always is an error whenever the return value or a param in a native interface is NSData (byte[])
Is it not supported? Thx
For example, the following interfaces blow:
- (NSData*)someMethod{
}
- (void)someMethod:(NSData*)param{
}
The server error file never mentions any specific error when an error occurs in a native interface class so I am asking here for clarity/reference

I'm not sure about the return value but the parameter should work and is documented in Listing 22. NativeInterface definition in the developer guide as:
public void test(byte b, boolean boo, char c, short s,
int i, long l, float f, double d, String ss,
byte[] ba, boolean[] booa, char[] ca, short[] sa, int[] ia,
long[] la, float[] fa, double[] da,
PeerComponent cmp);
Which generates:
-(void)test:(char)param param1:(BOOL)param1
param2:(int)param2 param3:(short)param3 param4:(int)param4
param5:(long long)param5 param6:(float)param6
param7:(double)param7 param8:(NSString*)param8
param9:(NSData*)param9 param10:(NSData*)param10
param11:(NSData*)param11 param12:(NSData*)param12
param13:(NSData*)param13 param14:(NSData*)param14
param15:(NSData*)param15 param16:(NSData*)param16
param17:(void*)param17;
}
If you're getting an error we'll need the link for the full error log as well as the applicable native interface code.

Related

NAO robot: porting ALSoundExtractor to qi framwork

When porting from NAOqi to qi framework I achieved a partial success. I do however still have the following problem.
I do not know how to implement sound processing with ALSoundExtractor in qi framework.
In old Naoqi, there is an example:
http://doc.aldebaran.com/2-8/dev/cpp/examples/audio/soundprocessing/soundprocessing.html
where a class is created:
class ALSoundProcessing : public ALSoundExtractor
then a function overriding a virtual function is declared, one that is used for sound processing:
void process(...)
What I don't now is:
How to create a class in qi framework that inherits from the old style class ALSoundExtractor?
How to declare a function that is overriding the virtual function - technically the base class function process() expects variables in old AL:: convention.
Alternatively, is there any other way to read the audio channels?
I never worked with ALExtractor nor ALSoundExtractor, but here is what I know.
How to create a class in qi framework that inherits from the old style class ALSoundExtractor?
in the old Naoqi, an "ALExtractor"
could run either from within the main process (using autoload.ini) or from another one (known as remote mode). With the qi framework, only the remote mode is supported. 
could inherit from ALExtractor or ALAudioExtractor to get some code factored out. Those classes have not been ported to the qi framework. So if you don't want to keep using libnaoqi, you should find a way to do without them.
Good news: inheriting from them never was really needed. You'll find yourself in a similar position as in the following question where an extractor is implemented in python (and thus cannot inherit from a C++ class, nor be loaded in the main process from autoload.ini).
NAO robot remote audio problems
How to declare a function that is overriding the virtual function - technically the base class function process() expects variables in old AL:: convention.
Whenever you use the "old Naoqi" you're actually using a compatibility layer on top of the qi framework.
So whenever you use the "old Naoqi", you're already using the qi framework.
libqi's qi::AnyValue is extensible at runtime, libnaoqi extends it to let it know how to handle an ALValue: how to convert it into primitive types (floating point number, list of ints, string, buffer, etc.).
So whenever an old ALSoundExtractor receives an AL::ALvalue, it is actually a qi::AnyValue which has been converted into an ALValue just before calling the process() method.
If you don't link with libnaoqi, you won't be able to use the value as an ALValue, but you can use it as a qi::AnyValue or even use it as a primitive type.
The original prototype is (cfr doxygen http://doc.aldebaran.com/2-8/ref/libalaudio/classAL_1_1ALSoundExtractor.html) is
void ALSoundExtractor::process (const int &nbOfChannels, const int &nbrOfSamplesByChannel, const AL_SOUND_FORMAT *buffer, const ALValue &timestamp);
Since timestamp is probably a list of two ints, I would try something like this
void TmpSoundExtractor::process (const int &nbOfChannels, const int &nbrOfSamplesByChannel, qi::AnyValue buffer, const std::vector<int> &timestamp);
I'm not sure how to handle the buffer variable, but let first get the rest working.
To use this API, you must write a Qi Service that advertises this method:
void processRemote(
int nbOfChannels,
int nbrOfSamplesByChannel,
const qi::AnyValue& timestamp,
const qi::AnyValue& buffer)
{
std::pair<char*, size_t> charBuffer = value.unwrap().asRaw();
const signed short* data = (const signed short*)charBuffer.first;
// process the data like in the example.
}
Note that with the Qi framework:
AL::ALValue is replaced by qi::AnyValue.
Getting the binary data (aka "raw") is slightly different.
AL_SOUND_FORMAT is replaced by signed short*.
ALSoundExtractor is not available, so we needed to do the conversion to const AL_SOUND_FORMAT* by ourselves.
Say your service is registered as "MySoundExtractor", you will have to tell ALAudioDevice to start the sound extraction and send the data to your service as follows:
auto audio = session->service("ALAudioDevice").value();
int nNbrChannelFlag = 0; // ALL_Channels: 0, AL::LEFTCHANNEL: 1, AL::RIGHTCHANNEL: 2; AL::FRONTCHANNEL: 3 or AL::REARCHANNEL: 4.
int nDeinterleave = 0;
int nSampleRate = 48000;
audio->setClientPreferences("MySoundExtractor", nSampleRate, nNbrChannelFlag, nDeinterleave);
audio->subscribe("MySoundExtractor");
Note that I did not test this code, so let me know what may be wrong.
The following is what has eventually worked for me and concludes the topic.
// **************** service.h ****************
typedef signed short AL_SOUND_FORMAT; // copy from alaudio/alsoundextractor.h
class SoundProcessing
{
public:
SoundProcessing(qi::SessionPtr session);
void init(void); // a replacement for a function automatically called in NAOqi 2.1.4
virtual ~SoundProcessing(void);
void processRemote(const int& nbOfChannels, const int& nbrOfSamplesByChannel, const qi::AnyValue& timestamp, const qi::AnyValue& buffer);
private:
qi::SessionPtr _session;
qi::AnyObject audio;
};
// **************** service.cpp ****************
SoundProcessing::SoundProcessing(qi::SessionPtr session) : _session(session)
{
_session->waitForService("ALAudioDevice");
audio = _session->service("ALAudioDevice");
} // constructor
QI_REGISTER_MT_OBJECT(SoundProcessing, init, processRemote);
SoundProcessing::~SoundProcessing(void)
{
audio.call<qi::AnyValue>("unsubscribe", "SoundProcessing");
} // destructor
void SoundProcessing::init(void)
{
audio.call<qi::AnyValue>("setClientPreferences",
"SoundProcessing",
_FREQ48K, // 48000 Hz requested
0,
1
);
audio.call<qi::AnyValue>("subscribe", "SoundProcessing");
} // SoundProcessing::init
void SoundProcessing::processRemote(const int& nbOfChannels,const int& nbrOfSamplesByChannel, const qi::AnyValue& timestamp, const qi::AnyValue& qibuffer)
{
std::pair<char*, size_t> charBuffer = qibuffer.unwrap().asRaw();
AL_SOUND_FORMAT *buffer = (AL_SOUND_FORMAT *)charBuffer.first;
(...)
} // SoundProcessing::process
// **************** main.cpp ****************
int main(int argc, char* argv[])
{
qi::ApplicationSession app(argc, argv);
app.start();
qi::SessionPtr session = app.session();
session->registerService("SoundProcessing", qi::AnyObject(boost::make_shared<SoundProcessing>(session)));
qi::AnyObject sp = session->service("SoundProcessing");
sp.call<qi::AnyValue>("init");
app.run();
return 0;
}
The following is what I did. The code compiles, but I won't have a chance to test it on a live robot for about one week or so.
typedef signed short AL_SOUND_FORMAT; // copy from alaudio/alsoundextractor.h
void process(const int& nbOfChannels, const int& nbrOfSamplesByChannel, const AL_SOUND_FORMAT *buffer, const qi::AnyValue& timeStamp); // I do not use the timeStamp variable in my code, so AnyValue would work?
qi::AnyObject audioDevice = _session->service("ALAudioDevice"); // same variable name as in the original ALSoundExtractor module, just as a convenience
audioDevice.call<qi::AnyValue>("setClientPreferences", audioDevice.call<qi::AnyValue>("getName"), 48000, 0, 1);
audioDevice.call<qi::AnyValue>("subscribe", audioDevice.call<qi::AnyValue>("getName")); // this is the key call
audioDevice.call<qi::AnyValue>("startDetection"); // is it still necessary?
My question is - do I do it right now? If I cannot override the virtual function process(), does subscribing of my module guarantee a callback to my process(...)?

How to define the callback for an esp32 arduino ble scan result

The definition to start a BLE scan is:
bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false);
The second parameter seems to be the callback when a scan is complete, being somewhat new to this Im unsure how to define it.
fwiw Ive tried this:
void OnScanResults(BLEScanResults scanResults)
{ }
and used it like this:
scanResults = scan->start(60, OnScanResults, true);
but obvious to others, that didnt work.
Please help me decypher that signature
void (*scanCompleteCB)(BLEScanResults)
you need to add & to OnScanResults because:
void (*scanCompleteCB)(BLEScanResults)
is a pointer to a function which takes a BLEScanResults, returns nothing and is called scanCompleteCB
So your call should be:
scanResults = scan->start(60, &OnScanResults, true);
just as a pointer to a int points to the address of a int
int pointedTo;
int* ptr = &pointedTo;

Calling C++ COM-server function from C# client

Looking for a help with com server on C++.
Here is C# client code to invoke method:
public static object ComInvoke(string method, params object[] args)
{
return _comObj.GetType().InvokeMember(method, BindingFlags.InvokeMethod, Type.DefaultBinder, _comObj, args);
}
This is how I call it:
string[] result = (string[])ExplorerCore.ComInvoke("CopyFiles", new object[]{"arg1_1", "arg1_2"}, "arg2");
I always get the COMException: HRESULT: 0x80020008 (DISP_E_BADVARTYPE)).
Here is C++ getting method:
STDMETHODIMP CopyFiles(BSTR ** src, BSTR dest, BSTR ** result);
And the .IDL file interface declaration:
HRESULT CopyFiles([in, string] BSTR ** src, [in, string] BSTR dest, [out, retval] BSTR ** test);
Edit 1:
This is correct code (without arrays):
C#:
string[] result = (string[])ExplorerCore.ComInvoke("CopyFiles", "arg1", "arg2");
C++
STDMETHODIMP CopyFiles(BSTR src, BSTR dest, BSTR* result);
IDL:
HRESULT CopyFiles([in, string] BSTR src, [in, string] BSTR dest, [out, retval] BSTR* test);
Thank you.
Andrew
OP and I managed to get this working for him in an private discussion. This is a most likely an issue of what types the proxy/stub supports. I don't know all the details, but I do know that some of the out-of-the-box proxy/stubs that ship with COM have limited support for arrays.
Moreover, in my experience, when dealing with interop scenarios, it's almost always best to follow the rules for an OLE automation interface, as defined here. As indicated there, the only type of array that's supported is SAFEARRAY. This makes sense for automation, as SAFEARRAYs are the only standard array type that have enough metadata to describe their own contents and array shape.
Unfortunately, this documentation is either wrong regarding SAFEARRAYs or not informative enough. The only type of array I've ever gotten to work seamlessly between COM & .NET or COM & VBA is SAFEARRAY(VARIANT). Moreover, I have only ever gotten this working by passing it by reference (SAFEARRAY(VARIANT)*).
All that said, here's what worked for OP:
IDL:
HRESULT CopyFiles([in] SAFEARRAY(VARIANT)* src, [in] BSTR dest, [out, retval] SAFEARRAY(VARIANT)** test);
C++:
STDMETHODIMP CopyFiles(LPSAFEARRAY src, BSTR dest, LPSAFEARRAY* result)

Bad arguments for libopus create encoder

currently I'm trying to build JNI-bindings for libopus. My current problem ist that I have a C-method like this:
JNIEXPORT jobject JNICALL
Java_de_akuz_android_libopus_OpusCodecFactory_encodercreate(JNIEnv * env, jint samplingRate, jint channels, jint application)
{
jclass resultClass = (*env)->FindClass(env,"de/akuz/android/libopus/OpusFactoryResult");
jobject result = (*env)->AllocObject(env,resultClass);
jfieldID pointerFieldID = (*env)->GetFieldID(env, resultClass,"pointer","J");
jfieldID errorFieldID = (*env)->GetFieldID(env, resultClass,"errorCode","I");
int error;
OpusEncoder* encoder = opus_encoder_create(samplingRate, channels, application, &error);
char buf[100];
sprintf(buf, "Pointer address: %p", encoder); // puts string into buffer
__android_log_print(ANDROID_LOG_INFO, "OpusNative", buf);
(*env)->SetLongField(env, result, pointerFieldID, encoder);
(*env)->SetIntField(env, result, errorFieldID, error);
return result;
}
When I call this method from JNI with the values 48000 for sample rate, 2 for channels and 2049 as id for application (taken from opus defines) a always get the error code -1 and null pointer back. I already tried casting the sample rate to an opus_int32 but it didn't change anything. Also I tried to pass the defined OPUS_APPLICATION_AUDIO directly instead if its integer value but it also didn't change anything. Unfortunately I'm more of a Java guy so I would appreciate any held on this problem(s) in my C code.
I'm really sorry, but obviously I'm blind (or stupid). My method declaration is all wrong
Java_de_akuz_android_libopus_OpusCodecFactory_encodercreate(JNIEnv * env, jint samplingRate, jint channels, jint application)
I have the parameter for the JNI environment but not the parameter for the calling object. It seems that C isn't interested in the amount of arguments of the native method in the Java code or the type of the arguments. Because of the missing jobject after JNIEnv all my parameters were shifted by one with samplingRate having the value of the pointer for the missing jobject.
The correct method declaration would be
Java_de_akuz_android_libopus_OpusCodecFactory_encodercreate(JNIEnv * env, jobject object, jint samplingRate, jint channels, jint application)
I hope that anyone running into the same problem will find my answer useful.

jni callback works for java types, but not c types

I have followed the advice at
registering java function as a callback in C function and can callback with "simple" types such as integer and string, e.g.:
jstring js = (*env)->NewStringUTF(env, "hello");
(*env)->CallStaticVoidMethod(env, cls, methodid, js);
However, if I am trying to do the same with C datatypes which have been wrapped with SWIG, I am only getting null pointers in Java. In the C part they are definitely not 0. Do they need to be treated differently?
[EDIT:]
Some more information:
As stated above, char*/string is working for me as well. I am looking for a solution for C struct's, which have been wrapped by SWIG and have been allocated in Java.
E.g.:
typedef struct {
unsigned short length;
unsigned short value;
} lv_t;
is getting wrapped by SWIG, so I can use it in Java:
lv_t lv;
lv = modulename.modulename_new_lv();
lv.setLength(1);
lv.setValue(2);
Then I will give this struct from Java to C:
modulename.send(lv);
C will send it over the network, receive some reply and change the values in lv. Now, this should give the modified lv back to Java.
void jni_call_received_hook(lv_t* lv){
JNIEnv* m_env;
(*m_vm)->AttachCurrentThread(m_vm, (void**) &m_env, NULL );
jclass cls = (*m_env)->FindClass( m_env, "gui/StateMachine" );
jmethodID mid = (*m_env)->GetStaticMethodID(m_env, cls, "callReceivedEvent", "(Lcom/something/modulename/jni/lv_t;)V");
if (mid == 0){
log(E, "Unable to find method for callback");
return;
}
// what to do here to create a jobject?
jobject lv_j = ...;
(*m_env)->CallStaticVoidMethod(m_env, cls, mid, lv_j);
}
Which calls:
public static void messageHandler(lv_t lv) {
System.out.println("messageHandler().");
System.out.println("lv " + lv);
}
Sorry but I'm not able to comment in your question yet, so this is more a comment rather than an answer. Anyway, I've recently done something like that.
My callback works and is implemented as:
void jni_call_received_hook(char* username){
JNIEnv* m_env;
(*m_vm)->AttachCurrentThread(m_vm, (void**) &m_env, NULL );
jclass cls = (*m_env)->FindClass( m_env, "gui/StateMachine" );
jmethodID mid = (*m_env)->GetStaticMethodID(m_env, cls, "callReceivedEvent", "(Ljava/lang/String;)V");
if (mid == 0){
log(E, "Unable to find method for callback");
return;
}
(*m_env)->CallStaticVoidMethod(m_env, cls, mid, (*m_env)->NewStringUTF(m_env, username));
}
The variable m_vm is an instance of the JVM I've kept upon calling a method that registered this callback, like this:
JNIEXPORT void JNICALL Java_gui_StateMachine_setCallReceivedCallback(JNIEnv *e, jobject o){
(*e)->GetJavaVM(e, &m_vm );
set_call_received_hook(jni_call_received_hook);
}
Maybe your missing something. Let me know if this isn't clear enough. Hope it helps.

Resources