Forum Replies Created
-
AuthorPosts
-
That’s the easy part – the syncing is where it gets tricky!
App code can’t just WriteSamples at will, it will need to halt to allow ReadSamples to catch up or the buffer will get ahead. On the other hand, if app code can’t WriteSamples fast enough, well yer basically hosed…
IMO, the best thing to do is start with getting the Callback moved to the monkey side, via either polling or signalling, so the audio device pulls data from SDL code running on it’s own audio thread, which pulls data from mx2 code running on the GUI thread.
Then you could perhaps look at using a fiber to provide synchronous/blocking writes to audio. This way, the fiber can block, not the callback.
And it’s probably time to add Deque I guess…
Just some thoughts!
Can you post an issue at github for this?
I’m gonna start trying to use that from now on!
> Or yes, it could block and wait for a monkey2 signal to say the copy has been performed on main thread…
Not quite sure what you mean here, but the native callback should never block. It should just copy in audio data, signal the gui/mx2 thread that it needs more data, and return ASAP.
In a polled scenario, the mx2 polling code could just check a flag, but sync signaling is nicer – see native process.cpp, esp:
int callback=g_mojo_app_AppInstance_AddAsyncCallback( finished );
…and…
postEvent( callback|INVOKE|REMOVE );
AddAsyncCallback is called on the main thread at setup time, while postEvent is called by native code running in it’s own thread.
Here, ‘finished’ is an mx2 function that gets called when the process is finished.
For an audio mixer, ‘finished’ would be something like ‘fillBuffer’ and postEvent would be called once native code has copied out the last buffer. You could in fact double buffer here (on the native thread) and call postEvent after flipping buffers but before copying audio data. This would help allow native/mx2 threads to run concurrently.
Unfortunately, you’ll currently have to use the ‘Init’ approach for this particular code.
The problem is that you can’t currently have ‘complex’ parameter expressions when you chain new’s together (ie: the ‘New Image[]( image )’ ) – mainly because you can’t in C++, and mx2 New currently maps directly to c++ ctors. Which seemed like a good idea at the time but has proven to have some serious limitations.
I do plan to eventually fix this though, hopefully soon!
Nice idea, but I think you’ll run into problems with the complete lack of thread sync in mx2, as the audio callback is called on a different thread (I think).
An alternative would be to have the SDL callback trigger a ‘main thread’ callback. There’s actually a kludgy behind-the-scenes way to do this in mojo.app and mojo.process that I eventually want to wrap in a nicer way. This would only work as long as the main thread was running fast enough of course.
Alias SDL_AudioCallback:Void(Void Ptr,UByte Ptr,Int)
Thanks, fixed.
Can I cast a monkey2 class to Void Ptr?
Nope, but you can wrap the object in a struct and pass a pointer to that. I use this in the pixmap loader to pass the mx2 stream as ‘context’ void ptr, something like:
Monkey123456789101112131415Struct ContextField stream:StreamEndFunction Callback( context:Void Ptr )Local stream:=Cast<Context Ptr>( context )->stream...got the mx2 stream...EndFunction LoadPixmap(...)...open stream etc...Local context:Contextcontext.stream=streamstb_load_image_from_user_stream( Varptr context,Callback )EndCasting object to void ptr may happen eventually, but it’s potentially dangerous for a number of reasons. I like having the struct wrapper as it ensures the object is wrapped in a variable and will be kept ‘alive’ while the callback is happening and not inadvertantly collected, similar to sticking something in a global to keep it alive.
There is also a bit of an issue with markdown ‘#’ clashing with ‘#’ for the preprocessor…need to come up with some kind of work around for this.
There are a few issues…but what’s hanging the compiler is a stray ‘=’ before a ‘#End’ in quadtree, ie: search for ‘=#’.
Will fix!
According to the above, the debug modules are building OK.
Can you post the output from the unsuccessful debug build of the app?
If it thinks there are files missing, can you check if they are there or not?
Please post the console output from a rebuildall.
‘Then’ was originally intended to be for ‘one liner’ Ifs only, but given it turned out to be optional I have no problems supporting it for ‘block ifs’ too if there are no complaints. This is monkey1 behaviour too.
I mean ‘Operator+=’, not ‘Operator+’, eg:
Monkey12345Operator+=( v:tlVector2 ) 'no return value!x+=v.xy+=v.yEndIf you’ve defined an Operator+() but not an Operator+=(), mx2 will rewrite…
p+=v
…as…
p=p+v
…but if you’ve defined Operator+=(), mx2 will use that instead.
I’m guessing Top is the same as First?
Top is really ‘last’, ie: the last thing pushed/added. blah[0] is ‘first’. You can use a stack pretty much how you’d use a c++ std::vector or java ‘ArrayList’, ie: index with [i], so blah[blah.Length-1] is also ‘last’ or ‘Top’.
Also, just cleaning up another issue and there appear to be several places in timelinefx where you use ‘Stack’ or List’ without any type, eg:
Method GetObjectsInBox:Stack(area:tlBox, Layer:Int[], GetData:Int = False)
This was compiling before but wont with the next release. Full list of errors with my release…
D:/dev/monkey2/modules/timelinefx/quadtree.monkey2 [246] : Error : Type ‘Stack<T?> is generic
D:/dev/monkey2/modules/timelinefx/quadtree.monkey2 [291] : Error : Type ‘Stack<T?> is generic
D:/dev/monkey2/modules/timelinefx/quadtree.monkey2 [841] : Error : Type ‘Stack<T?> is generic
D:/dev/monkey2/modules/timelinefx/quadtree.monkey2 [936] : Error : Type ‘Stack<T?> is generic
D:/dev/monkey2/modules/timelinefx/collision.monkey2 [1068] : Error : Type ‘List<T?> is genericHave a look at Operator+=
Mx2 will synthesize one for you, but you can provide your own too.
That’s interesting, so if I have a method in a struct it shouldn’t modify the field values I should create a new struct with updated values and return that?
It’s up to you! I was really just trying to think up a good example for ‘when should something be a struct’, and I think if a type can be implemented as an immutable type it’s possibly a good candidate. On the other hand, if a field of a type needs to be modified independently in some way, maybe it’s not a good candidate. It’s sounding like a crapper example each time I explain it…
I do prefer to make as much stuff as immutable as possible myself though. Having methods return a ‘new’ value is more flexible and safer than methods that modify ‘Self’ IMO, as you don’t have to think about what a method or operator might be modifying (ie: nothing!), but that’s more a stylistic thing than a speed thing. But it’s also true that structs do make immutable types easier/more efficient to write, so rock on structs!
And with immutable types and operator loading, math can look completely ‘natural’, eg:
e->Position=matrix * pos * u.Cross( v ) + offset…etc
…and just like int/float math, you don’t have to worried about anything be inadvertantly modified along the way.
There likely *is* some cost overhead for this amount of simplicity/flexibility, but c++ deals incredibly well with most of it, esp. with structs, so IMO it’s worth it.
Ok, this should be being caught by mx2 not gcc, will fix, but the problem is you need to use ‘Vec2f’ not just ‘Vec2’.
Vec2 is generic so must be used with a type argument (like List or Stack).
Vec2f is an alias for Vec2<Float>, similar to the way IntList is an alias for List<Int> etc.
-
AuthorPosts