Indeed I shall.
First off, there is a strange problem with it : Sometimes BASS just wont play streamed music when the program is run from the IDE, although through Finder its okay. Whether this also happens with Windows, I dont know.
The reason for using BASS, rather than FMOD is that the BASS DYLIB is Universal, rather than FMOD'S being Intel or PPC (which resulted in problems with my interface code). Plus, I'm more used to BASS too.
I found I had to put the LIBBASS.DYLIB into the lib directory of the home directory for the current user.
This is the interface code. There's nothing particularly hard or unusual in it, and it doesn't have everything from BASS in it.
As you can see, I've added the spectrograph routines from the BASS demo.
Okay - onto the BlitzMax stuff. As I mentioned elsewhere, you'll need the modified BMK routine, otherwise this wont work.
First off, there is a strange problem with it : Sometimes BASS just wont play streamed music when the program is run from the IDE, although through Finder its okay. Whether this also happens with Windows, I dont know.
The reason for using BASS, rather than FMOD is that the BASS DYLIB is Universal, rather than FMOD'S being Intel or PPC (which resulted in problems with my interface code). Plus, I'm more used to BASS too.
I found I had to put the LIBBASS.DYLIB into the lib directory of the home directory for the current user.
This is the interface code. There's nothing particularly hard or unusual in it, and it doesn't have everything from BASS in it.
/* * BASS.c * BASS * * Created by Nicholas Kingsley on 20/07/2007. * Copyright 2007 __MyCompanyName__. All rights reserved. * */ #include "stdio.h" #include "stdlib.h" #include "string.h" #include "math.h" #include "BASS.h" #include "/Developer/bass23-osx/bass.h" BASS_CHANNELINFO channelInfo; int specpos; //----------System extern DWORD BASSGetVersion(void) { return BASS_GetVersion(); } extern char BASSInitialise(int device,int freq,int flags) { memset(&channelInfo,(char) 0,sizeof(channelInfo)); specpos=0; return BASS_Init(device,freq,flags,0,NULL); } extern char BASSStart() { return BASS_Start(); } extern int BASSErrorGetCode(void) { return BASS_ErrorGetCode(); } extern char BASSFree(void) { return BASS_Free(); } extern char *BASSGetDeviceDescription(DWORD device) { return (char *) BASS_GetDeviceDescription(device); } //------------Samples extern DWORD BASSSampleLoad(char *fileName,DWORD offset,DWORD length,DWORD max,DWORD flags) { return (DWORD) BASS_SampleLoad(0,fileName,offset,length,max,flags); } //------------Streams extern DWORD BASSStreamCreateFile(char *fileName,DWORD offset,DWORD length,DWORD flags) { return ((DWORD) BASS_StreamCreateFile(0,fileName,offset,length,flags)); } extern char BASSStreamFree(DWORD handle) { return BASS_StreamFree((HSTREAM) handle); } //------------Channels extern char BASSChannelPlay(DWORD handle,char restart) { return BASS_ChannelPlay(handle,restart); } extern QWORD BASSChannelGetPosition(DWORD handle) { return BASS_ChannelGetPosition(handle); } extern QWORD BASSChannelGetLength(DWORD handle) { return BASS_ChannelGetLength(handle); } extern char BASSGetChannelInfo(DWORD handle) { return BASS_ChannelGetInfo(handle,&channelInfo); } extern char BASSChannelGetData(DWORD handle,char *buffer,DWORD length) { return BASS_ChannelGetData(handle,buffer,length); } //-----------Get information from structures extern DWORD BASS_ReturnNumberOfChannels(void) { return channelInfo.chans; } extern DWORD BASS_ReturnPlaybackFreq(void) { return channelInfo.freq; } extern DWORD BASS_ReturnFlags(void) { return channelInfo.flags; } extern DWORD BASS_ReturnCType(void) { return channelInfo.ctype; } extern DWORD BASS_ReturnOriginalResolution(void) { return channelInfo.origres; } extern void BASS_Spectrograph4(DWORD handle,DWORD SPECWIDTH,DWORD SPECHEIGHT,char *specBuff) { float fft[1024]; register int x,y; if (specBuff==NULL) return; BASS_ChannelGetData(handle,fft,BASS_DATA_FFT2048); // get the FFT data for (x=0;x<SPECHEIGHT;x++) { y=sqrt(fft[x+1])*3*127; // scale it (sqrt to make low values more visible) if (y>127) { y=127; // cap it } specBuff[x*SPECWIDTH+specpos]=128+y; // plot it } // move marker onto next position specpos=(specpos+1)%SPECWIDTH; for (x=0;x<SPECHEIGHT;x++) { specBuff[x*SPECWIDTH+specpos]=255; } } extern void BASS_Spectrograph3(DWORD handle,DWORD SPECWIDTH,DWORD SPECHEIGHT,char *specBuff,DWORD BANDS) { register int b0,x,sc,b1,y; float fft[1024]; float sum; if (specBuff==NULL) return; BASS_ChannelGetData(handle,fft,BASS_DATA_FFT2048); // get the FFT data b0=0; memset(specBuff,0,SPECWIDTH*SPECHEIGHT); for (x=0;x<BANDS;x++) { sum=0.0; b1=pow(2,x*10.0/(BANDS-1)); if (b1>1023) { b1=1023; } if (b1<=b0) { b1=b0+1; // make sure it uses at least 1 FFT bin } sc=10+b1-b0; for (;b0<b1;b0++) { sum+=fft[1+b0]; } y=(sqrt(sum/log10(sc))*1.7*SPECHEIGHT)-4; // scale it if (y>SPECHEIGHT) { y=SPECHEIGHT; // cap it } while (--y>=0) { memset(specBuff+y*SPECWIDTH+x*(SPECWIDTH/BANDS),y+1,SPECWIDTH/BANDS-2); // draw bar } } } extern void BASS_Spectrograph2(DWORD handle,DWORD SPECWIDTH,DWORD SPECHEIGHT,char *specBuff) { float fft[1024]; register int x,y,y1; if (specBuff==NULL) return; BASS_ChannelGetData(handle,fft,BASS_DATA_FFT2048); // get the FFT data memset(specBuff,0,SPECWIDTH*SPECHEIGHT); for (x=0;x<SPECWIDTH/2;x++) { y=sqrt(fft[x+1])*3*SPECHEIGHT-4; // scale it (sqrt to make low values more visible) //#else // y=fft[x+1]*10*SPECHEIGHT; // scale it (linearly) //#endif if (y>SPECHEIGHT) { y=SPECHEIGHT; // cap it } if (x && (y1=(y+y1)/2)) { // interpolate from previous to make the display smoother while (--y1>=0) { specBuff[y1*SPECWIDTH+x*2-1]=y1+1; } } y1=y; while (--y>=0) { specBuff[y*SPECWIDTH+x*2]=y+1; // draw level } } } extern void BASS_Spectrograph1(DWORD handle,DWORD SPECWIDTH,DWORD SPECHEIGHT,char *specBuff) { register int c,x,v,y; float *buf; if (specBuff==NULL) return; BASS_ChannelGetInfo(handle,&channelInfo); buf=alloca(channelInfo.chans*SPECWIDTH*sizeof(float)); // allocate buffer for data if (buf==NULL) { return; } BASS_ChannelGetData(handle,buf,(channelInfo.chans*SPECWIDTH*sizeof(float))|BASS_DATA_FLOAT); memset(specBuff,(char) 0,SPECWIDTH*SPECHEIGHT); y=0; for (c=0; c<channelInfo.chans; c++) { for (x=0;x<SPECWIDTH;x++) { v=(1-buf[x*channelInfo.chans+c])*SPECHEIGHT/2; // invert and scale to fit display if (v<0) { v=0; } else if (v>=SPECHEIGHT) { v=SPECHEIGHT-1; } if (!x) { y=v; } do { // draw line from previous sample... if (y<v) { y++; } else if (y>v) { y--; } specBuff[y*SPECWIDTH+x]=c&1?127:1; // left=green, right=red (could add more colours to palette for more chans) } while (y!=v); } } }
As you can see, I've added the spectrograph routines from the BASS demo.
Okay - onto the BlitzMax stuff. As I mentioned elsewhere, you'll need the modified BMK routine, otherwise this wont work.
Import "-L/Users/nicholaskingsley/Documents/BlitzMax/FMOD" Import "-lbass" Import "-lz" Import "/Users/nicholaskingsley/BASS/BASS.c" 'Import "LibBass.a" Strict Local stream:Int Local length:Long Const SPECWIDTH:Int=368 Const SPECHEIGHT:Int=127 Local bank:TBank Local specBuf:TBank Local bSize:Int Local aFloat:Float Local v:Int Local c:Int Local x:Int Local y:Int Local one:Byte ' Bass error messages Const BASS_OK:Int= 0 ' all is OK Const BASS_ERROR_MEM:Int= 1 ' memory error Const BASS_ERROR_FILEOPEN:Int= 2 ' can't open the file Const BASS_ERROR_DRIVER:Int= 3 ' can't find a free/valid driver Const BASS_ERROR_BUFLOST:Int= 4 ' the sample buffer was lost Const BASS_ERROR_HANDLE:Int= 5 ' invalid handle Const BASS_ERROR_FORMAT:Int= 6 ' unsupported sample format Const BASS_ERROR_POSITION:Int= 7 ' invalid playback position Const BASS_ERROR_INIT:Int= 8 ' BASS_Init has Not been successfully called Const BASS_ERROR_START:Int= 9 ' BASS_Start has Not been successfully called Const BASS_ERROR_ALREADY:Int= 14 ' already initialized/paused/whatever Const BASS_ERROR_NOPAUSE:Int= 16 ' Not paused Const BASS_ERROR_NOCHAN:Int= 18 ' can't get a free channel Const BASS_ERROR_ILLTYPE:Int= 19 ' an illegal Type was specified Const BASS_ERROR_ILLPARAM:Int= 20 ' an illegal parameter was specified Const BASS_ERROR_NO3D:Int= 21 ' no 3D support Const BASS_ERROR_NOEAX:Int= 22 ' no EAX support Const BASS_ERROR_DEVICE:Int= 23 ' illegal device number Const BASS_ERROR_NOPLAY:Int= 24 ' Not playing Const BASS_ERROR_FREQ:Int= 25 ' illegal sample rate Const BASS_ERROR_NOTFILE:Int= 27 ' the stream is Not a file stream Const BASS_ERROR_NOHW:Int= 29 ' no hardware voices available Const BASS_ERROR_EMPTY:Int= 31 ' the Mod music has no sequence data Const BASS_ERROR_NONET:Int= 32 ' no internet connection could be opened Const BASS_ERROR_CREATE:Int= 33 ' couldn't create the file Const BASS_ERROR_NOFX:Int= 34 ' effects are Not available Const BASS_ERROR_PLAYING:Int= 35 ' the channel is playing Const BASS_ERROR_NOTAVAIL:Int= 37 ' requested data is Not available Const BASS_ERROR_DECODE:Int= 38 ' the channel is a "decoding channel" Const BASS_ERROR_DX:Int= 39 ' a sufficient DirectX version is Not installed Const BASS_ERROR_TIMEOUT:Int= 40 ' connection timedout Const BASS_ERROR_FILEFORM:Int= 41 ' unsupported file format Const BASS_ERROR_SPEAKER:Int= 42 ' unavailable speaker Const BASS_ERROR_VERSION:Int= 43 ' invalid BASS version (used by add-ons) Const BASS_ERROR_CODEC:Int= 44 ' codec is Not available/supported Const BASS_ERROR_UNKNOWN:Int= -1 ' some other mystery error ' Initialisation variables Const BASS_DEVICE_8BITS:Int= 1 ' use 8 bit resolution, Else 16 bit Const BASS_DEVICE_MONO:Int= 2 ' use mono, Else stereo Const BASS_DEVICE_3D:Int= 4 ' enable 3D functionality ' If the BASS_DEVICE_3D flag is Not specified when initilizing BASS, ' Then the 3D flags (BASS_SAMPLE_3D And BASS_MUSIC_3D) are ignored when ' loading/creating a sample/stream/music. Const BASS_DEVICE_LATENCY:Int= 256 ' calculate device latency (BASS_INFO struct) Const BASS_DEVICE_SPEAKERS:Int= 2048 ' force enabling of speaker assignment Const BASS_DEVICE_NOSPEAKER:Int=4096 ' ignore speaker arrangement Const BASS_SAMPLE_8BITS:Int= 1 ' 8 bit Const BASS_SAMPLE_FLOAT:Int= 256 ' 32-bit floating-point Const BASS_SAMPLE_MONO:Int= 2 ' mono Const BASS_SAMPLE_LOOP:Int= 4 ' looped Const BASS_SAMPLE_3D:Int= 8 ' 3D functionality enabled Const BASS_SAMPLE_SOFTWARE:Int= 16 ' it's NOT using hardware mixing Const BASS_SAMPLE_MUTEMAX:Int= 32 ' muted at Max distance (3D only) Const BASS_SAMPLE_VAM:Int= 64 ' uses the DX7 voice allocation & management Const BASS_SAMPLE_FX:Int= 128 ' old implementation of DX8 effects are enabled Const BASS_SAMPLE_OVER_VOL:Int= $10000 ' override lowest volume Const BASS_SAMPLE_OVER_POS:Int= $20000 ' override longest playing Const BASS_SAMPLE_OVER_DIST:Int= $30000 ' override furthest from listener (3D only) Const BASS_STREAM_PRESCAN:Int= $20000 ' enable pin-point seeking (MP3/MP2/MP1) Const BASS_MP3_SETPOS:Int= BASS_STREAM_PRESCAN Const BASS_STREAM_AUTOFREE:Int= $40000 ' automatically free the stream when it stop/ends Const BASS_STREAM_RESTRATE:Int= $80000 ' restrict the download rate of internet file streams Const BASS_STREAM_BLOCK:Int= $100000' download/play internet file stream in small blocks Const BASS_STREAM_DECODE:Int= $200000' don't play the stream, only decode (BASS_ChannelGetData) Const BASS_STREAM_STATUS:Int= $800000' give server status info (HTTP/ICY tags) in DOWNLOADPROC Const BASS_MUSIC_FLOAT:Int= BASS_SAMPLE_FLOAT ' 32-bit floating-point Const BASS_MUSIC_MONO:Int= BASS_SAMPLE_MONO ' force mono mixing (less CPU usage) Const BASS_MUSIC_LOOP:Int= BASS_SAMPLE_LOOP ' loop music Const BASS_MUSIC_3D:Int= BASS_SAMPLE_3D ' enable 3D functionality Const BASS_MUSIC_FX:Int= BASS_SAMPLE_FX ' enable old implementation of DX8 effects Const BASS_MUSIC_AUTOFREE:Int= BASS_STREAM_AUTOFREE ' automatically free the music when it stop/ends Const BASS_MUSIC_DECODE:Int= BASS_STREAM_DECODE ' don't play the music, only decode (BASS_ChannelGetData) Const BASS_MUSIC_PRESCAN:Int= BASS_STREAM_PRESCAN ' calculate playback length Const BASS_MUSIC_CALCLEN:Int= BASS_MUSIC_PRESCAN Const BASS_MUSIC_RAMP:Int= $200 ' normal ramping Const BASS_MUSIC_RAMPS:Int= $400 ' sensitive ramping Const BASS_MUSIC_SURROUND:Int= $800 ' surround sound Const BASS_MUSIC_SURROUND2:Int= $1000 ' surround sound (mode 2) Const BASS_MUSIC_FT2MOD:Int= $2000 ' play .Mod as FastTracker 2 does Const BASS_MUSIC_PT1MOD:Int= $4000 ' play .Mod as ProTracker 1 does Const BASS_MUSIC_NONINTER:Int= $10000 ' non-interpolated mixing Const BASS_MUSIC_POSRESET:Int= $8000 ' stop all notes when moving position Const BASS_MUSIC_POSRESETEX :Int= $400000' stop all notes And reset bmp/etc when moving position Const BASS_MUSIC_STOPBACK:Int= $80000 ' stop the music on a backwards jump effect Const BASS_MUSIC_NOSAMPLE:Int= $100000' don't load the samples ' Speaker assignment flags Const BASS_SPEAKER_FRONT:Int= $1000000 ' front speakers Const BASS_SPEAKER_REAR:Int= $2000000 ' rear/side speakers Const BASS_SPEAKER_CENLFE:Int= $3000000 ' center & LFE speakers (5.1) Const BASS_SPEAKER_REAR2:Int= $4000000 ' rear center speakers (7.1) 'Const BASS_SPEAKER_N(n) ((n)<<24) ' n'th pair of speakers (max 15) Const BASS_SPEAKER_LEFT:Int= $10000000 ' modifier: Left Const BASS_SPEAKER_RIGHT:Int= $20000000 ' modifier: Right Const BASS_SPEAKER_FRONTLEFT:Int= BASS_SPEAKER_FRONT|BASS_SPEAKER_LEFT Const BASS_SPEAKER_FRONTRIGHT:Int= BASS_SPEAKER_FRONT|BASS_SPEAKER_RIGHT Const BASS_SPEAKER_REARLEFT :Int= BASS_SPEAKER_REAR|BASS_SPEAKER_LEFT Const BASS_SPEAKER_REARRIGHT:Int= BASS_SPEAKER_REAR|BASS_SPEAKER_RIGHT Const BASS_SPEAKER_CENTER:Int= BASS_SPEAKER_CENLFE|BASS_SPEAKER_LEFT Const BASS_SPEAKER_LFE:Int= BASS_SPEAKER_CENLFE|BASS_SPEAKER_RIGHT Const BASS_SPEAKER_REAR2LEFT:Int= BASS_SPEAKER_REAR2|BASS_SPEAKER_LEFT Const BASS_SPEAKER_REAR2RIGHT:Int= BASS_SPEAKER_REAR2|BASS_SPEAKER_RIGHT Const BASS_UNICODE:Int= $80000000 Const BASS_RECORD_PAUSE:Int= $8000 ' start recording paused Const BASS_DATA_FLOAT:Int= $40000000 Graphics 640,480 Extern Function BASSGetVersion() Function BASSInitialise(device:Int,freq:Int,flags:Int) Function BASSChannelGetPosition(handle:Int) Function BASSChannelPlay(handle:Int,restart:Int) Function BASSChannelGetLength(handle:Int) Function BASSChannelGetData(handle:Int,buffer:Byte Ptr,length:Int) Function BASSErrorGetCode() Function BASSFree() Function BASSGetDeviceDescription(device:Int) Function BASSGetChannelInfo(handle:Int) Function BASSSampleLoad(fileName:Byte Ptr,offset:Int,length:Int,_max:Int,flags:Int) Function BASSStart() Function BASSStreamCreateFile(fileName:Byte Ptr,offset:Int,length:Int,flags:Int) Function BASSStreamFree(handle:Int) Function BASS_ReturnNumberOfChannels() Function BASS_ReturnPlaybackFreq() Function BASS_ReturnFlags() Function BASS_ReturnCType() Function BASS_ReturnOriginalResolution() Function BASS_Spectrograph1(handle:Int,width:Int,height:Int,store:Byte Ptr) Function BASS_Spectrograph2(handle:Int,width:Int,height:Int,store:Byte Ptr) Function BASS_Spectrograph3(handle:Int,width:Int,height:Int,store:Byte Ptr,bars:Int) Function BASS_Spectrograph4(handle:Int,width:Int,height:Int,store:Byte Ptr) EndExtern DrawText "Version:"+Hex$(BASSGetVersion()),0,0 ' 0x0230001 for the latest version DrawText "Initialise:"+BASSInitialise(-1,44100,0),0,8 stream=BASSStreamCreateFile("/Users/nicholaskingsley/Music/pulse1.mp3",0,0,BASS_SAMPLE_FLOAT) 'stream=BASSSampleLoad("/Users/nicholaskingsley/Music/pulse1.mp3",0,0,1,1) DrawText "Stream Handle :"+stream,0,16 'BASSChannelPlay DrawText BASSChannelPlay(stream,1),0,24 'DrawText BASSStart(),0,24 DrawText "Error Code:"+BASSErrorGetCode(),0,32 'length=BASSChannelGetLength(stream) 'DrawText "Stream Length:"+length,0,48 'DrawText "Error Code:"+BASSErrorGetCode(),0,48+16 'BASSGetChannelInfo(stream) ' Allocate memory for the channel info bSize=BASS_ReturnNumberOfChannels()*SPECWIDTH*SizeOf aFloat bank=CreateBank(bSize) specBuf=CreateBank(SPECWIDTH*SPECHEIGHT) If bank=Null Or specBuf=Null End EndIf 'a$=BASSGetDeviceDescription(0) ' Look at later 'Print "Description:"+a$ While Not KeyHit(KEY_ESCAPE) Cls SetColor 255,255,255 DrawText "Current Position:"+BASSChannelGetPosition(stream),100,100 DrawText "Number of Channels:"+BASS_ReturnNumberOfChannels(),100,120 DrawText "Flags:"+BASS_ReturnFlags(),100,140 DrawText "GetData : "+BASSChannelGetData(stream,BankBuf(bank),bSize | BASS_DATA_FLOAT),100,160 DrawText "Last error:"+BASSErrorGetCode(),250,160 BASS_Spectrograph3(stream,SPECWIDTH,SPECHEIGHT,BankBuf(specBuf),8) For x=0 To SPECWIDTH-1 For y=0 To SPECHEIGHT-1 one=PeekByte(specBuf,(y*SPECWIDTH)+x) If one Select one Case 1 SetColor 255,255,255 Case 255 SetColor 255,255,255 Default SetColor 255,255,0 EndSelect Plot 100+x,100+y EndIf Next Next If KeyHit(KEY_SPACE) DrawOval 0,0,640,480 Flip EndWhile BASSStreamFree(stream) BASSFree() End