How to initialize audio with Vala/SDL - audio

I've been trying to figure this out for a few hours now.
In order to start up the audio, I need to create an SDL.AudioSpec object and pass it to SDL.Audio.Open. The problem is, AudioSpec is a class with a private constructor, so when I try to create one I get:
sdl.vala:18.25-18.43: error: `SDL.AudioSpec' does not have a default constructor
AudioSpec audiospec = new SDL.AudioSpec();
^^^^^^^^^^^^^^^^^^^
And if I try to just assign values to it's member vars like a struct (it's a struct in normal sdl) I get:
sdl.vala:20.3-20.25: error: use of possibly unassigned local variable `audiospec'
audiospec.freq = 22050;
^^^^^^^^^^^^^^^^^^^^^^^
I found the valac doc here: http://valadoc.org/sdl/SDL.AudioSpec.html
But it isn't much help at all.
The offending code block looks like this:
// setup the audio configuration
AudioSpec audiospec;
AudioSpec specback;
audiospec.freq = 22050;
audiospec.format = SDL.AudioFormat.S16LSB;
audiospec.channels = 2;
audiospec.samples = 512;
// try to initialize sound with these values
if (SDL.Audio.open(audiospec, specback) < 0)
{
stdout.printf("ERROR! Check audio settings!\n");
return 1;
}
Any help would be greatly appreciated!
Another update, as I'm still having some trouble. I've changed the vapi file, and this is what I have now:
public delegate void AudioCallback (out void* userdata, out uchar stream, int len);
[CCode (cname="SDL_AudioSpec")]
[Compact]
public struct AudioSpec {
public int freq;
public AudioFormat format;
public uchar channels;
public uchar silence;
public uint16 samples;
public uint16 padding;
public uint32 size;
public AudioCallback callback;
public void* userdata;
}// AudioSpec
I have a method that (tries?) to meet this function signature:
public void callback(out void* userdata, out uchar stream, int len)
{
stream = 0;
userdata = null;
}
And assigning it as:
audiospec.callback = gen.callback;
Needless to say, this still isn't working, get lots of errors:
/home/gukid/vala/soundgen.vala.c: In function ‘sound_gen_main’:
/home/gukid/vala/soundgen.vala.c:766:12: error: ‘SDL_AudioSpec’ has no member named ‘callback_target_destroy_notify’
/home/gukid/vala/soundgen.vala.c:766:72: error: ‘SDL_AudioSpec’ has no member named ‘callback_target_destroy_notify’
/home/gukid/vala/soundgen.vala.c:766:114: error: ‘SDL_AudioSpec’ has no member named ‘callback_target’
/home/gukid/vala/soundgen.vala.c:768:11: error: ‘SDL_AudioSpec’ has no member named ‘callback_target’
/home/gukid/vala/soundgen.vala.c:769:11: error: ‘SDL_AudioSpec’ has no member named ‘callback_target_destroy_notify’
/home/gukid/vala/soundgen.vala.c:770:21: warning: assignment from incompatible pointer type [enabled by default]
/home/gukid/vala/soundgen.vala.c:771:11: error: ‘SDL_AudioSpec’ has no member named ‘callback_target’
/home/gukid/vala/soundgen.vala.c:772:11: error: ‘SDL_AudioSpec’ has no member named ‘callback_target_destroy_notify’
error: cc exited with status 256
So I'm at a bit of another sticky point.
3rd post: EUREKA! I have a solution! (debatable :P)
First off, the sdl.vapi looks like:
[CCode (cheader_filename = "SDL.h")]
public delegate void AudioCallback (void* userdata, uchar* stream, int len);
[CCode (cname="SDL_AudioSpec", has_type_id=false)]
public struct AudioSpec {
public int freq;
public AudioFormat format;
public uchar channels;
public uchar silence;
public uint16 samples;
public uint16 padding;
public uint32 size;
[CCode (delegate_target = false, type = "void*")]
public weak AudioCallback callback;
public void* userdata;
}// AudioSpec
And then I can just create a function:
public static void callback(void* userdata, uchar* stream, int len)
And:
audiospec.callback = callback;
Ahhh, finally my beautiful white noise generator is complete!

I think it's a bug in the VAPI. In sdl.vapi, try changing
[Compact]
public class AudioSpec {
to
public struct AudioSpec {
and
[CCode (cname="SDL_OpenAudio")]
public static int open(AudioSpec desired, AudioSpec obtained);
to
[CCode (cname="SDL_OpenAudio")]
public static int open(AudioSpec desired, out AudioSpec obtained);
and make your code look like:
AudioSpec audiospec = AudioSpec();
AudioSpec specback;
audiospec.freq = 22050;
audiospec.format = SDL.AudioFormat.S16LSB;
audiospec.channels = 2;
audiospec.samples = 512;
// try to initialize sound with these values
if (SDL.Audio.open(audiospec, out specback) < 0)
{
stdout.printf("ERROR! Check audio settings!\n");
return 1;
}
and give it a test. It seems to generate the correct code according to the SDL docs. If it works, consider submitting the VAPI changes to the Vala bugzilla.

Okay, the delegate:
public delegate void AudioCallback (out void* userdata, out uchar stream, int len);
has the C type:
void(*AudioCallback)(void**userdata, unsigned char* stream, int len);
where userdata and stream are write-only, which is not really what you want.
void (*callback)(void *userdata, Uint8 *stream, int len);
In this, userdata is just a pointer to some stuff for your use. In Vala's terms, it's the target of a delegate. stream is an array, but SDL has allocated it, so it's not out and len is the length of the array, so we can have Vala deal with that automatically with just uint8[] stream. Putting it all together:
[CCode(instance_pos = 0)]
public delegate void AudioCallback(uint8[] stream);
The instance_pos tells Vala where the userdata is. It normally assumes it to be last. As for the struct:
[CCode (cname="SDL_AudioSpec", destroy_function = "")]
public struct AudioSpec {
...
[CCode(delegate_target_cname = "userdata")]
public AudioCallback callback;
}
This will create a callback that stores the target in userdata. Now, to avoid leaking memory, Vala is going to try to assign a destructor to callback_target_destroy_notify...and now we're stuck because we have no member for that. So, let's back up and try again:
[CCode(has_target = false)]
public delegate void AudioCallback(void* userdata, uint8[] stream);
The has_target tells Vala there is no userdata and we include it manually. As for the struct:
[CCode (cname="SDL_AudioSpec", destroy_function = "")]
public struct AudioSpec {
...
public AudioCallback callback;
public void* userdata;
}
So, userdata will get passed to your callback, but you'll have to cast it yourself and you'll have to manage the memory. Additionally, when the structs go out of scope, Vala is going to call a destroy function. By specifying an empty string, it will do nothing. Normally, this frees memory inside the struct, but we don't need to worry here.

Related

Mapping a structure inside a union in JNA

I am attempting to map the kstat library in Solaris 11.3 to Java using JNA. While I've managed to get most of the structures working, I've spent the last 24 hours fighting with a particularly difficult union-within-a-structure-within-a-union.
I am successfully retrieving a pointer to a kstat_named structure I need using kstat_data_lookup(). My code properly retrieves most of the data (name, data_type, and non-struct members of the union) in this C structure:
typedef struct kstat_named {
char name[KSTAT_STRLEN]; /* name of counter */
uchar_t data_type; /* data type */
union {
charc[16]; /* enough for 128-bit ints */
struct {
union {
char *ptr; /* NULL-terminated string */
} addr;
uint32_t len; /* length of string */
} str;
int32_t i32;
uint32_t ui32;
int64_t i64;
uint64_t ui64;
/* These structure members are obsolete */
int32_t l;
uint32_t ul;
int64_t ll;
uint64_t ull;
} value; /* value of counter */
} kstat_named_t;
I have mapped this in JNA as follows:
class KstatNamed extends Structure {
public static class UNION extends Union {
public byte[] charc = new byte[16]; // enough for 128-bit ints
public Pointer str; // KstatNamedString
public int i32;
public int ui32;
public long i64;
public long ui64;
}
public byte[] name = new byte[KSTAT_STRLEN]; // name of counter
public byte data_type; // data type
public UNION value; // value of counter
public KstatNamed() {
super();
}
public KstatNamed(Pointer p) {
super();
this.useMemory(p);
this.read();
}
#Override
public void read() {
super.read();
switch (data_type) {
case KSTAT_DATA_CHAR:
value.setType(byte[].class);
break;
case KSTAT_DATA_STRING:
value.setType(Pointer.class);
break;
case KSTAT_DATA_INT32:
case KSTAT_DATA_UINT32:
value.setType(int.class);
break;
case KSTAT_DATA_INT64:
case KSTAT_DATA_UINT64:
value.setType(long.class);
break;
default:
break;
}
value.read();
}
#Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "name", "data_type", "value" });
}
}
This code works correctly for int32 types (KSTAT_DATA_INT32). However, when the data type is KSTAT_DATA_STRING, which corresponds to the str structure inside the union, I am not having any success in properly retrieving the data.
I have mapped the nested structure like this:
class KstatNamedString extends Structure {
public static class UNION extends Union {
public Pointer ptr; // NULL-terminated string
}
public UNION addr;
public int len; // length of string
public KstatNamedString() {
super();
}
public KstatNamedString(Pointer p) {
super();
this.useMemory(p);
this.read();
}
#Override
public void read() {
super.read();
addr.setType(Pointer.class);
addr.read();
}
#Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "addr", "len" });
}
}
Ultimately I'm trying to replicate the behavior of this C macro:
#define KSTAT_NAMED_STR_PTR(knptr) ((knptr)->value.str.addr.ptr)
I've tried multiple different methods of trying to get access to the above structure, but it never seems to read the correct data (the len value is in the millions and attempting to read the string ptr causes segfault). I've tried:
Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name);
KstatNamed data = new KstatNamed(p);
KstatNamedString str = new KstatNamedString(data.value.str);
return str.addr.ptr.getString(0); // <--- Segfault on C side
I've also tried:
Specifying KstatNamedString as the type instead of the Pointer type
Using various combinations of ByReference in both the structures and the unions
I've googled everywhere, including trying what I thought was a promising result here, but nothing seems to work.
I'm sure I'm missing something simple.
Use KstatNamedString instead of Pointer type.
Change your pointer-based constructors like this:
public KstatNamed(Pointer p) {
super(p);
this.read();
}
public KstatNamedString(Pointer p) {
super(p);
this.read();
}
and change the addr field of the str struct field to be a simple Pointer (no need for the union bits around it).
public Pointer /*UNION*/ addr;
Run your JVM with -Djna.dump_memory=true and print your newly-initialized Structure as a string. That will show you how JNA interprets the memory layout of the struct, and how the native memory is initialized. That should help you determine how to extract the string you're looking for (assuming it's there).
You can also tune your union read() method to initially read only the type field (using Structure.readField("data_type")) before setting the union type.

VC++ 2010 can access class variable vector of struct from Main() but not from class function

I have written a header file with one basic data structure.
ProdList.h
#ifndef LISTOFITEMS_H
#define LISTOFITEMS_H
struct ListOfItems
{
public:
std::string fdcustid;
std::string fdstkid;
std::string fdordisquantity;
std::string fdordsstatus; // <> 'H'
std::string fdordhtype; // <> 'A'
};
#endif /* GRANDFATHER_H */
now that I have a data structure I include it in the class definition and use the data structure "ListOfItems" in the class "ProdContainer".
ProdContainer.h
#include "ProdList.h"
class ProdContainer
{
public:
ProdContainer(void);
~ProdContainer(void);
static void SetNumberOfElements(int Elements);
std::vector<ListOfItems> Items;
}
now when i write the following in Main.
int _tmain(int argc, _TCHAR* argv[])
{
ProdContainer myobject;
myobject.Items.resize(12);
printf("The size of Items is %i \n", myobject.Items.size());
return 0;
}
All goes as expected and I get the following output.
The size of Items is 12
Which is all fine and good. However, I want to encapsulate the data within the class and only allow access through class functions.
The problem arises when I add the following code to "SetNumberOfElements" implementation.
void ProdContainer::SetNumberOfElements(int Elements)
{
Items.resize(Elements);
}
When I try to compile this "error C2228 left of '.resize' must have class/struct/union" appears and I am at a loss at what to do next.
I have searched high and low and cant seem to find any posts matching this particular problem, it's probably a schoolboy error. I've checked the MSDN site on error C2228 and as far as I can see Items is a substantiated variable of struct type ListOfItems, so I can't see why this error is appearing.
Is there a method for accessing a vector of a struct or some other aspect that I just can't see.
Please help, I am just about ready to explode.
You can only access static data from static functions. So change this
static void SetNumberOfElements(int Elements);
To this
void SetNumberOfElements(int Elements);
You cannot access non-static class members from the static function.
What you can do is to "switch" from static to non-static.
static void SetNumberOfElements( void * lParam, int Elements)
{
((ProdContainer*)lParam)->Items.resize( Elements );
}
Use it like this inside your class:
SetNumberOfELements( this, 10 );

c++ this parameter through System::Threading:ThreadStart to class member function

I'm writing in c++ a graphic user interfaced program and I need to create a threads in the program.
so I'm using System::Threading namespace to get my goal.
The function that I want to use as thread is a class member function, so here is what I've done:
Server::Server() // constructor
{
System::Threading::Thread^ T = gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(this, this->RunServer)); // Server::RunServer
T->Start();
}
since it gave me those errors:
Error 2 error C3350: 'System::Threading::ThreadStart' : a delegate
constructor expects 2 argument(s)
Error 1 error C3867: 'Server::RunServer': function call missing
argument list; use '&Server::RunServer' to create a pointer to
member
I tried this call:
Server::Server() // constructor
{
System::Threading::Thread^ T = gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(&Server::RunServer));
T->Start();
}
and received this errors:
Error 1 error C3364: 'System::Threading::ThreadStart' : invalid
argument for delegate constructor; delegate target needs to be a
pointer to a member
function
2 IntelliSense: invalid delegate initializer -- function is not a
member of a managed
class
as far as I know the second try did not succeed because Server::RunServer doesn't have address, so it's like to do &1.
by the way I tried to use the ThreadStart to create thread of none class memeber function and it worked fine.
I'm using win7 - visual studio 2012. How to make it work?
EDIT:
Server declaration:
class Server
{
public:
/* Fields */
std::string Port;
std::string Host;
WSADATA wsaData;
int ListenResult;
SOCKET ListenSocket;
SOCKET* ClientSocket;
SOCKADDR_IN* ADDR;
int ADDRSize;
struct addrinfo *result;
struct addrinfo hints;
std::vector<Client> Clients;
/* Methods */
Server();
std::wstring StringW(char* String);
void Print(std::wstring String);
std::wstring CurrentTime();
void ParseServerIni();
void RunServer();
void PartToString(Part* _Part);
void InsertListItem(std::string String);
void ClientHandler(SOCKET* _Sock, SOCKADDR_IN* _ADDR);
int ParsePacket(Packet &_Packet, int _Bytes, Byte** _PacketBlock);
};
You almost got the syntax right.
Assuming the declaration is:
public ref class Server
{
void RunServer();
};
Then you should combine your two approaches, by specifying both the object to invoke the method on, and the address of the method, with the name of the declaring class.
gcnew System::Threading::ThreadStart(this, &Server::RunServer)

Working with vector values in C++ CLI

Here is my header file in Visual C++ Express 2010 (note the last line):
/* custom class header to communicate with LynxMotion robot arm */
using namespace System;
using namespace System::IO::Ports;
public ref class LynxRobotArm
{
public:
LynxRobotArm();
~LynxRobotArm();
void connectToSerialPort(String^ portName, int baudRate);
void disconnectFromSerialPort();
void setCurrentPosition(int channel, int position);
int getCurrentPosition(int channel);
void moveToPosition(int channel, int position);
private:
void initConnection();
SerialPort^ serialPort;
array<String^> ^serialPortNames;
String^ portName;
int baudRate;
std::vector<int> *currentPosition;
};
I try to assign a value to the vector by using this function;
void LynxRobotArm::setCurrentPosition(int channel, int position)
{
currentPosition[channel] = position;
}
The compiler gives me an error C2679:
binary '=' : no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)
I looked up at MSDN and they say:
To use the operator, you must overload it for the specified type or define a conversion to a type for which the operator is defined.
I tried using currentPosition.at(channel) = position but it did not make a difference.
I am stuck... quite new to pointers, noted that using std::vector<int> currentPosition; instead of std::vector<int> *currentPosition; does not compile.
How should my set-function work? (will try getter afterwards..)

Casting a CLR type to void* and back

How to properly convert a CLR type, say Foo^, to void* and back some time later?
The scenario is, I have some unmanaged code in a DLL that can be summarized as
class Handler {
void* _obj;
void (*_call)(void* obj, int detail);
void handle_event() { _call(_obj, 1234); }
public:
void set_object(void* obj) { _obj = obj; }
void set_callback(void(*callback)(void*,int)) { _call = callback; }
};
I want to store a CLR object in the Handler's _obj field. How to implement it, taking into account that the GC may move the CLR object? (pin_ptr? gcroot?)
static void event_callback(void* obj, int detail) {
Foo^ clr_obj = undo_magic(obj);
// ^^^^^^^^^^ how?
clr_obj->DoStuff(detail);
}
public ref class Foo {
Handle* h;
public:
void Start() {
h = new Handler;
void* obj = do_magic(this);
// ^^^^^^^^ how?
h->set_object(obj);
h->set_callback(event_callback);
}
...
}
Pinning would be required. However, you are storing this 'reference', requiring the object to stay pinned. That's quite unhealthy, the garbage collector would constantly have to work around it. Another problem is that just pinning isn't enough, there has to be a recognizable reference to the object so that the GC won't collect the object. The stored void* isn't good enough. You'd normally solve this with gcroot<> but that can't work here either.
A better approach is to simply pass a 'handle'. Use a Dictionary<int, Foo^> to convert the void* back to the object. Or a List<Foo^>, now the index could be the handle.

Resources