Retro Music in BlitzMax
BlitzMax Forums/BlitzMax Programming/Retro Music in BlitzMax
| ||
Ok, I have a small demo which should demonstrate sound from NSF (Nintendo Sound Format, sound-related NES code and data isolated and ripped from the original ROM) played in BlitzMax. 1) Choose a location for all the following files (e.g. "C:\Blitz\NSF\") 2) Download the Festalon source code from here: http://www.2a03.org/software.php and extract the "festalon" directory into the directory chosen above (not the contents of the directory, the directory itself). 3) Download the Super Mario Bros 1 NSF file from the top of here: http://www.ocremix.org/list.php?type=soundarchives&offset=143&sort=gameasc (the "NSF" link in the right column) into the directory and rename it to "smb.nsf". 4) Create festalonheader.bmx in the directory and paste this code into it: Import "festalon\nsf.c" Import "festalon\x6502.c" Import "festalon\sound.c" Import "festalon\cart.c" Import "festalon\filter.c" Import "festalon\ext\ay.c" Import "festalon\ext\emu2413.c" Import "festalon\ext\fds.c" Import "festalon\ext\mmc5.c" Import "festalon\ext\n106.c" Import "festalon\ext\vrc6.c" Import "festalon\ext\vrc7.c" Extern "C" Function FESTAI_SetVolume(volume:Int) Function FESTAI_SetSoundQuality(q:Int) Function FESTAI_Sound(Rate:Int) Function FESTAI_Disable(t:Int) Function FESTAI_Load:Byte Ptr(buf:Byte Ptr, size:Int) ' Returns an NSFHeader struct Function FESTAI_NSFControl:Int(z:Int, o:Int) ' z = track, o = absolute (1) or relative (0). Returns the actual ' track, in case the chosen track is in fact not available. Function FESTAI_Emulate:Byte Ptr(Count:Int Var) ' Returns a pointer to a chunk of sample data. Count is set to the ' length of it. Function FESTAI_Close() End Extern OnEnd FESTAI_Close FESTAI_SetSoundQuality(0) FESTAI_SetVolume(100) 5) Create nsfdemo.bmx and paste this code into it: Strict Import "festalonheader.bmx" Incbin "smb.nsf" Local SMB1_Overworld:TFreeAudioSound = LoadSoundFromNSF("smb.nsf", 1, 10.0) Local SMB1_Death:TFreeAudioSound = LoadSoundFromNSF("smb.nsf", 8, 2.7) Local ch:TFreeAudioChannel = PlayNSFSound(SMB1_Overworld) While ChannelPlaying(ch) ' ... Wend PlayNSFSound(SMB1_Death, ch) While ChannelPlaying(ch) ' ... Wend End Function PlayNSFSound:TFreeAudioChannel( Snd:TFreeAudioSound, alloced_channel:TChannel = Null ) Local channel:TFreeAudioChannel,fa_channel If alloced_channel channel=TFreeAudioChannel( alloced_channel ) If Not channel Return fa_channel=channel.fa_channel EndIf fa_channel=fa_PlaySound(Snd.fa_sound,False,fa_channel) If Not fa_channel Return If channel And channel.fa_channel=fa_channel Return channel Return TFreeAudioChannel.CreateWithChannel( fa_channel ) End Function Function LoadSoundFromNSF:TFreeAudioSound(IncbinURL:String, Track:Short, Seconds:Float, Rate:Int=44100) Local SAMPLES:Int = Ceil(Rate * Seconds) FESTAI_Load(IncbinPtr(IncbinURL), IncbinLen(IncbinURL)) FESTAI_NSFControl(Track,1) FESTAI_Sound(Rate) Local fa_sound:Int = fa_CreateSound( SAMPLES,16,2,Rate ) Local buf:Byte Ptr Local sam:Int Local written:Int While (written < SAMPLES) buf = FESTAI_Emulate(sam) If (written + sam) > SAMPLES sam = SAMPLES - written End If fa_WriteSound( fa_sound,buf,sam ) written :+ sam Wend FESTAI_Close() Return TFreeAudioSound.CreateWithSound(fa_sound) End Function 6) Compile & execute nsfdemo.bmx That's it! I have only tested this on Windows, I'd be interested to hear if it works on Linux/OSX. There are some issues with this: * It can only load fixed-length samples, not properly streaming music. I would be able to do this if I knew how to reset the WritePos of the FreeAudio sample data interface, but I don't. FMOD is a possibility but I can't seem to get that working either. * There is a noticeable delay as the sound is emulated and stored in a FreeAudio buffer. I actually have another demo that uses threading and the sound plays immediately, but didn't include it as (a) it's Windows-only since I don't understand threads for anything else and (b) Festalon's internal state is one set of variables, which means that you cannot load in multiple sounds in parellel - not without changing a bunch of stuff in the Festalon source that I'm not smart enough to do. |
| ||
Jeez. I know this stuff has limited niche use but I did put some effort in and this isn't much encouragement to try to do more advanced stuff for the community. |
| ||
hey, chin up.. I'm sure there are someone out there who could use this, but it's a niche market (as you said yourself), so it's not very likely that everyone is testing it yet.. hehe personally, I currently have no use for this, at this time.. but thanks for contributing the source... :) |
| ||
denzilquixode, Thanks alot for your hard work.I tried it on Mac Os X 10.4.3 (latest) with the latest BlitzMax.Works but only for about 10 seconds even though the tune is 1 or 2 mins long. I tried with different .nsf audio files, same result. Btw: 5) Create nsfdemo.bb and paste this code into it: shoud read 5) Create nsfdemo.BMX and paste this code into it: |
| ||
I just saw this right now, I didnt have Blitzmax a month ago but am very interested it. I downloaded all the files but still couldnt get it to work after setting it up though. Got this error : Build Error: failed to compile C:/BlitzMax/NSF/festalon/nsf.c |
| ||
(Whoops, sorry for that outburst earlier. I was just feeling down at the time. Should probably edit it out.) DannyD (thanks, good to know it works on the Mac) - yeah, that's deliberate. The thing is, there is no way to tell how long an NSF track actually is. Festalon will happily generate the same looped music over and over again, or hours of silence after a short sound effect. If you look at the LoadSoundFromNSF function, the third argument is the length of time in seconds to load - you're right, it does load exactly 10.0 seconds. If you change this to 120.0 you'll get two minutes of music, but it will also cause quite a delay to generate it all (at least, it does here.) So at the moment, the system is only suitable for loading short sound effects, and you need to use trial-and-error to work out exactly how long a sound effect is to load it. To play music, I need to work out how to do streaming sound, something that TFreeAudio doesn't seem to be able to do. I did try to work out FMOD streams, but they seem to invariably get me a memory-exception error. Thanks for the typo check, I'll edit that. Neuro - possibly you need MinGW set up? I can't remember if that's just for modules or what. |
| ||
I have mingw installed and setup already....was I suppose to build the festalon source files first? |
| ||
No - it should just build them in, as easily as .bmx files. I don't know what's going on there, sorry. Anyone got an idea? |
| ||
It don't work for me -_-' I can't hear any beautiful "beep". -------------------------- Building nsfdemo Compiling:nsf.c Process complete --------------------------- No errors, no exe compiled file. nothing. -_-' MinGW installed. Don't know if it's working, I think it's only for compile new modules. |
| ||
BlackSp1der - I just tried removing the PATH and MINGW Environment Variables pointing to the MinGW bin directory and got the same result as you. Set those two up, restart Blitz and see if that helps. Edit: Neuro - since it does use MinGW, maybe you are using a different version or something? |
| ||
Thanks denzilquixode, it's working now. Finally I noticed what is the mean for "PATH" |
| ||
I actually do have the latest mingw.....maybe i should try building everything again. I'll try again when I get home... |
| ||
Nice !! |
| ||
Could you convert it into a standard mod ? |
| ||
Yeah, pretty easily, but I'd rather not do so before I'm happier with it. This is more of a proof of concept than a full system yet. |
| ||
Finally got this working with FMOD:Strict Import Fmod.Fmod Import "festalonheader.bmx" Incbin "smb.nsf" Global FestalonBuffer:TBank = CreateBank(2000 * 4) Global FestalonPtr:Byte Ptr = BankBuf(FestalonBuffer) Global BytesInBuffer:Int Graphics 300,150,0 Local Rate:Int = 22050 PlayIncbinNSF("smb.nsf",1,Rate) FSOUND_Init(44100,32,0) Local streamID:Int = FSOUND_Stream_Create( Byte Ptr(FestalonCallback), 1000 * 4, .. FSOUND_16BITS | FSOUND_STEREO, .. Rate, Null ) FSOUND_Stream_Play(FSOUND_FREE, streamID) Repeat Until KeyHit(KEY_ESCAPE) FSOUND_Stream_Close(streamID) FSOUND_Close() End Function PlayIncbinNSF(IncbinURL:String,Track:Int,Rate:Int) FESTAI_Load(IncbinPtr(IncbinURL), IncbinLen(IncbinURL)) FESTAI_NSFControl(Track,1) FESTAI_Sound(Rate) End Function Function FestalonCallback(streamObject:Byte Ptr, streamDataBuffer:Byte Ptr, bufferLength:Int, .. userData:Byte Ptr) "Win32" Local SamBuffer:Byte Ptr Local AddedSamples:Int, AddedBytes:Int While BytesInBuffer < BufferLength SamBuffer = FESTAI_Emulate(AddedSamples) AddedBytes = AddedSamples*4 MemCopy FestalonPtr + BytesInBuffer, SamBuffer, AddedBytes BytesInBuffer :+ AddedBytes End While MemCopy streamDataBuffer, FestalonPtr, bufferLength If BytesInBuffer > bufferLength MemMove FestalonPtr, FestalonPtr + bufferLength, BytesInBuffer-BufferLength End If BytesInBuffer :- bufferLength Return 1 End Function |