BASS Module (Mac)

BlitzMax Forums/BlitzMax Programming/BASS Module (Mac)

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

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.

/*
 *  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