About Monkey 2 › Forums › Monkey 2 Projects › libxmp mod music player
This topic contains 26 replies, has 6 voices, and was last updated by
Ethernaut
9 months ago.
-
AuthorPosts
-
November 22, 2016 at 6:11 pm #5343
I like mod’s (probably my age!) I saw one of the first trackers to be released on the Amiga and was amazed… had a soft spot ever since…
I’ve not wrapped the whole library, but it should be cross platform as it compiles completely using mx2 and doesn’t access sound hardware or even a library, instead delivering on demand x samples which you can use to ram down the throat of SDL’s sound system (not tried but should work with OpenAL too) – might even work with emscripten…?
I’m posting it here as I’m off away soon, and don’t even know if there is enough interest to make it worth completing (a fair bit of the functionality is in, but I’m scratching my head at the moment about wrapping arrays of stucts inside structs)
If there is sufficient interest I would be quite happy to complete the wrapper and after document it, submit it for consideration.
enjoy!
November 22, 2016 at 6:12 pm #5344*sigh* bare with me, I’ll upload it somewhere….
November 22, 2016 at 6:16 pm #5345http://bedroomcoders.co.uk/tmp/xmp.tar.bz2
btw it does IT and s3m so plenty of channels not just stuck to 4!
November 22, 2016 at 8:00 pm #5347November 23, 2016 at 3:16 am #5369I am interested:) I still use modplug tracker for music production sometimes. And there are so many good modules out there.
November 23, 2016 at 6:47 am #5371Brilliant work
November 23, 2016 at 8:26 am #5372November 23, 2016 at 9:45 am #5373I remember playing with replay routines on the Amiga. They were not that big in size but I never learned how they worked. I was able to create exe’s that played a mod that stopped after a mouse click.
November 23, 2016 at 11:24 am #5380throwing something in the ring here:
As we now have the complete source (that isn’t a dratted monkey module). it would be possible to reverse engineer the xml player and write a soundtracker.
quickly looking at the source there is base support for 2-pole filter, reverb and chorus
It also says that there is no restriction on number of playable voices. so in theory we could have unlimited voices playing at once, which is similar to how blitzmax operated.
Now. Given all of this. it would (in theory) be possible to create a very flexible cross-platform synth/sampler with all features you would expect?
November 23, 2016 at 6:13 pm #5389in principle yes, there is actually not too much complication, but if you wanted to make a complete tracker it would probably be fairly time consuming, while many of the “mod” formats are documented (to various levels) it would probably be better to create a custom format.
The place to start would be to write a multi channel mixer/pitch bender… SDL makes it reasonably easy, as it provides a callback when it needs a new buffer, (see the callback in the demo)
anyhow bed soon and away for a little while
just to mention if you run in debug mode you’ll get a seg fault, tried to pin it down with gdb but its corrupting stack frames so difficult to see, “seems” ok in release mode, but I’ve even seen behaviour like that with and without gcc’s -g switch so I’d lean to there being some issue for sure…
might look at the “full” libxmp (as opposed to lite) source when I get back…
November 23, 2016 at 8:20 pm #5392> just to mention if you run in debug mode you’ll get a seg fault
This could be because the SDL callback happens on a different thread and mx2 is not threadsafe. This’ll probably also cause problems in release mode depending on what the code does.
November 29, 2016 at 4:24 pm #5487so there is no way to safely use the sdl callback?
I assume then that openAL is the way to go?
November 30, 2016 at 8:52 am #5504here’s an OpenAL version (sdl mutter mutter bag of bolts mutter mutter)
which doesn’t need to be thread safe…
because of a struct array inside the channel info struct I’ve had issues getting realtime feedback for the channels – its probably not worth taking too much further…
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151#import "xmp"#import "<std>"#import "<mojo>"#import "<openal>"#import "assets/"Using xmp..Using std..Using mojo..Using openal..Global ang:Floatglobal ctx:xmp_context PtrGlobal paused:Bool = FalseGlobal frameInfo:xmp_frame_infoGlobal modData:DataBufferGlobal buf:=New ALuint[2]Const bSize:UInt = 1024*5Global curBuf:Int = 0Global bufDat1:=New UByte[bSize*4]Global bufDat2:=New UByte[bSize*4]Global src:ALuintFunction fillBuffer(buffer:ALuint)Local p:UByte PtrIf curBuf=0 ThencurBuf=1p = Varptr bufDat1[0]ElsecurBuf=0p = Varptr bufDat2[0]Endifxmp_play_buffer(ctx , p, bSize , 0)alBufferData( buffer, AL_FORMAT_STEREO16, p, bSize, 22500 )alSourceQueueBuffers( src, 1, Varptr buffer )End FunctionClass xmpWindow Extends WindowMethod New()ClearColor=New Color( 1, 0, 0 )End MethodMethod OnRender( canvas:Canvas ) OverrideApp.RequestRender()ang = ang + 0.01ClearColor= New Color((Abs(Sin(ang)/2.0)+0.5),0,0)canvas.Clear( ClearColor )canvas.DrawText("space to pause / restart", 20, 60 )canvas.DrawText("right to go forward a position", 20, 80 )canvas.DrawText("left to go backward a position", 20, 100 )local Processed:ALintalGetSourcei( src, AL_BUFFERS_PROCESSED, Varptr Processed )canvas.DrawText("processed "+Processed, 20, 200 )while Processed>0Local BufID:ALuintalSourceUnqueueBuffers( src, 1, Varptr BufID )canvas.DrawText("filling buffer "+BufID, 20, 220 )fillBuffer(BufID)alGetSourcei( src, AL_BUFFERS_PROCESSED, Varptr Processed )Wendxmp_get_frame_info(ctx, Varptr frameInfo)canvas.DrawText("pos:"+frameInfo.pos, 20, 160 )canvas.DrawText("pat:"+frameInfo.pattern, 80, 160 )canvas.DrawText("row:"+frameInfo.row, 140, 160 )End MethodMethod OnKeyEvent( event:KeyEvent ) OverrideSelect event.TypeCase EventType.KeyDownSelect event.KeyCase Key.Key1ang=0Case Key.Escape' TODO release OpenAL properly here....xmp_end_player(ctx)xmp_release_module(ctx)xmp_free_context(ctx)App.Terminate()Case Key.Spacepaused = Not pausedIf paused ThenalSourcePause( src )ElsealSourcePlay( src )End IfCase Key.Rightxmp_next_position(ctx)Case Key.Leftxmp_prev_position(ctx)EndEndEndEnd ClassFunction Main()New AppInstanceNew xmpWindowctx = xmp_create_context()If ctx = Null ThenPrint "couldn't get an xmp context"ReturnEnd IfmodData = DataBuffer.Load( "asset::yoafrica-dia.mod" )'Local modData:DataBuffer = DataBuffer.Load( "asset::JEFF93.IT")If xmp_load_module_from_memory(ctx, modData.Data, modData.Length) <> 0 ThenPrint "couldn't load module"ReturnEndxmp_start_player(ctx, 22500, 0)Local device:=alcOpenDevice( Null )Assert( device,"Failed to open OpenAL device" )Local context:=alcCreateContext( device,Null )Assert( context,"Failed to create OpenAL context" )Assert( alcMakeContextCurrent( context ) )alGenSources( 1,Varptr src )alGenBuffers( 2,Varptr buf[0] )fillBuffer(buf[0])fillBuffer(buf[1])alSourcePlay( src )App.Run()End Functionenjoy!
December 13, 2016 at 11:42 am #5770can I have the other sources as this one wont compile
December 13, 2016 at 3:21 pm #5771what other sources? and won’t compile gives me no clue with which to help you…
-
AuthorPosts
You must be logged in to reply to this topic.