'//+--------------------------------------------------------------------------+
'//|                                                                          |
'//|                                BassAudio                                 |
'//|                              Version 1.00                                |
'//|                                                                          |
'//|         To ceate demoscene using Bass.dll and OpenGL altogether.         |
'//|                                                                          |
'//|                                                                          |
'//+--------------------------------------------------------------------------+
'//|                                                                          |
'//|                         Author Patrice TERRIER                           |
'//|                           copyright (c) 2010                             |
'//|                                                                          |
'//|                        pterrier@zapsolution.com                          |
'//|                                                                          |
'//|                          www.zapsolution.com                             |
'//|                                                                          |
'//+--------------------------------------------------------------------------+
'//|                  Project started on : 09-19-2010 (MM-DD-YYYY)            |
'//|                        Last revised : 09-23-2010 (MM-DD-YYYY)            |
'//+--------------------------------------------------------------------------+

TYPE TAUDIO                     '//

    AudioFile    AS ASCIIZ * 260

    Channel      AS DWORD       '// The playing audio channel
    Lpeak        AS WORD        '// The left audio channel peak value (while in play mode).
    Rpeak        AS WORD        '// The right audio channel peak value (while in play mode).
    FFTdata      AS DWORD       '// DWORD pointer to the FFT() AS SINGLE array.
    FFTsize      AS WORD        '// Size of the FFT array.

    WIMdata      AS DWORD       '// DWORD pointer to the wave MM_WIM_DATA.
    MediaLength  AS DWORD       '// Media length.
    MediaPos     AS DWORD       '// Media pos.

    MediaType    AS LONG        '// MUSIC, STREAM, etc.
    InitDone     AS LONG        '// BassInit flag.

END TYPE

%BASS_SYNC_END          = 2
%BASS_SYNC_ONETIME      = &h80000000
%BASS_STREAM_AUTOFREE   = 262144      ' automatically free the stream when it stop/ends
%BASS_STREAM_PRESCAN    = 131072      ' enable pin-point seeking on the MP3/MP2/MP1
%BASS_SAMPLE_3D         = 8           ' 3D functionality enabled
%BASS_STREAM_DECODE     = &H200000    ' don't play the stream, only decode (BASS_ChannelGetData)

' BASS_ChannelGetData flags
%BASS_DATA_AVAILABLE    = 0           ' query how much data is buffered
%BASS_DATA_FFT512       = &H80000000  ' 512 sample FFT
%BASS_DATA_FFT1024      = &H80000001  ' 1024 FFT
%BASS_DATA_FFT2048      = &H80000002  ' 2048 FFT
%BASS_DATA_FFT4096      = &H80000003  ' 4096 FFT

%BASS_DATA_FFT_INDIVIDUAL = &H10      ' FFT flag: FFT for each channel, else all combined
%BASS_DATA_FFT_NOWINDOW   = &H20      ' FFT flag: no Hanning window

%BASS_POS_BYTE        = 0             ' byte position
%BASS_POS_MUSIC_ORDER = 1             ' order.row position, MAKELONG(order,row)
%BASS_ATTRIB_VOL      = 2             ' Channel volume level
%BASS_ACTIVE_PLAYING  = 1             ' Channel is active

%STREAM    = 0
%MUSIC     = 1

$BASSMUSICEXT = ".mo3.it.xm.s3m.mtm.mod.umx."
$BASSAUDIOEXT = "mp3.ogg.wav" + $BASSMUSICEXT

DECLARE FUNCTION BASS_Init LIB "bass.dll" ALIAS "BASS_Init" (BYVAL device AS LONG, BYVAL freq AS DWORD, BYVAL flags AS DWORD, BYVAL win AS DWORD, BYVAL dsguid AS DWORD) AS LONG
DECLARE FUNCTION BASS_ChannelGetLevel LIB "bass.dll" ALIAS "BASS_ChannelGetLevel" (BYVAL nChannel AS DWORD) AS DWORD
DECLARE FUNCTION BASS_ChannelPlay LIB "bass.dll" ALIAS "BASS_ChannelPlay" (BYVAL nChannel AS DWORD, BYVAL restart AS LONG) AS LONG
DECLARE FUNCTION BASS_ChannelStop LIB "bass.dll" ALIAS "BASS_ChannelStop" (BYVAL nChannel AS DWORD) AS LONG
DECLARE FUNCTION BASS_ChannelPause LIB "bass.dll" ALIAS "BASS_ChannelPause" (BYVAL nChannel AS DWORD) AS LONG
DECLARE FUNCTION BASS_Free LIB "bass.dll" ALIAS "BASS_Free" () AS LONG
DECLARE FUNCTION BASS_StreamCreateFile LIB "bass.dll" ALIAS "BASS_StreamCreateFile" (BYVAL mem AS LONG, file AS ASCIIZ, BYVAL offset AS QUAD, BYVAL length AS QUAD, BYVAL flags AS DWORD) AS LONG
DECLARE FUNCTION BASS_MusicLoad LIB "bass.dll" ALIAS "BASS_MusicLoad" (BYVAL mem AS LONG, file AS ASCIIZ, BYVAL offset AS QUAD, BYVAL length AS DWORD, BYVAL flags AS DWORD, BYVAL freq AS DWORD) AS LONG
DECLARE FUNCTION BASS_StreamFree LIB "bass.dll" ALIAS "BASS_StreamFree" (BYVAL nChannel AS DWORD) AS LONG
DECLARE FUNCTION BASS_MusicFree LIB "bass.dll" ALIAS "BASS_MusicFree" (BYVAL nChannel AS DWORD) AS LONG
DECLARE FUNCTION BASS_ChannelSetSync LIB "bass.dll" ALIAS "BASS_ChannelSetSync" (BYVAL nChannel AS DWORD, BYVAL pbType AS LONG, BYVAL param AS QUAD, BYVAL proc AS LONG, BYVAL USER AS LONG) AS LONG
DECLARE FUNCTION BASS_SetVolume LIB "bass.dll" ALIAS "BASS_SetVolume" (BYVAL UseVolume AS SINGLE) AS LONG

DECLARE FUNCTION BASS_ChannelGetPosition LIB "bass.dll" ALIAS "BASS_ChannelGetPosition" (BYVAL nChannel AS DWORD, BYVAL nMode AS DWORD) AS DWORD
DECLARE FUNCTION BASS_ChannelSetPosition LIB "bass.dll" ALIAS "BASS_ChannelSetPosition" (BYVAL nChannel AS DWORD, BYVAL nPos AS QUAD) AS LONG
DECLARE FUNCTION BASS_ChannelGetLength LIB "bass.dll" ALIAS "BASS_ChannelGetLength" (BYVAL nChannel AS DWORD, BYVAL nMode AS DWORD) AS DWORD

DECLARE FUNCTION BASS_MusicGetOrders LIB "bass.dll" ALIAS "BASS_MusicGetOrders" (BYVAL nChannel AS DWORD) AS DWORD
DECLARE FUNCTION BASS_ErrorGetCode LIB "bass.dll" ALIAS "BASS_ErrorGetCode"() AS LONG
DECLARE FUNCTION BASS_ChannelGetData LIB "bass.dll" ALIAS "BASS_ChannelGetData" (BYVAL nChannel AS DWORD, BYVAL buffer AS DWORD, BYVAL nlength AS LONG) AS LONG

DECLARE FUNCTION BASS_ChannelBytes2Seconds LIB "bass.dll" ALIAS "BASS_ChannelBytes2Seconds" (BYVAL nChannel AS DWORD, BYVAL nPos AS QUAD) AS DOUBLE
DECLARE FUNCTION BASS_ChannelSetAttribute LIB "bass.dll" ALIAS "BASS_ChannelSetAttribute" (BYVAL nChannel AS DWORD, BYVAl nAtteib AS DWORD, BYVAL nValue AS SINGLE) AS LONG
DECLARE FUNCTION BASS_ChannelIsActive LIB "bass.dll" ALIAS "BASS_ChannelIsActive" (BYVAL nChannel AS DWORD) AS DWORD

GLOBAL ta AS TAUDIO

FUNCTION BassChannelGetData () AS DWORD
    DIM fft(255) AS STATIC SINGLE
    LOCAL nRet AS LONG
    RESET fft()
    IF ta.Channel THEN
       nRet = BASS_ChannelGetData(ta.Channel, VARPTR(fft(0)), %BASS_DATA_FFT512)
    END IF
    FUNCTION = VARPTR(fft(0))
END FUNCTION

FUNCTION BassFree () AS LONG
    IF ta.InitDone THEN FUNCTION = BASS_Free(): ta.InitDone = 0
END FUNCTION

FUNCTION BassCloseChannel () AS LONG
    LOCAL nRet AS LONG
    IF ta.Channel THEN
       IF ta.MediaType = %MUSIC THEN
          nRet = BASS_MusicFree(ta.Channel)
       ELSE
          nRet = BASS_StreamFree(ta.Channel)
       END IF
       ta.Channel = 0: ta.MediaType = 0
    END IF
    FUNCTION = nRet
END FUNCTION

FUNCTION BassInit (BYVAL hWnd AS LONG) AS LONG
    IF ta.InitDone = 0 THEN ta.InitDone = BASS_Init(1, 44100, 0, hWnd, 0)
    FUNCTION = ta.InitDone
END FUNCTION

SUB BassStreamEllapsed(nSync AS LONG, nChannel AS LONG, nData AS LONG, nUser AS LONG)
    BassCloseChannel()
END SUB

FUNCTION BassChannelPlay(BYVAL hWnd AS DWORD) AS LONG
    LOCAL So AS LONG

    BassCloseChannel()

    IF BassInit(hWnd) THEN

       So = INSTR(-1, ta.AudioFile, $Dot)
       IF So AND INSTR($BASSMUSICEXT, LCASE$(MID$(ta.AudioFile, So)) + $Dot) THEN
          ta.MediaType = %MUSIC
          ta.Channel = BASS_MusicLoad(0, ta.AudioFile, 0, 0, %BASS_STREAM_PRESCAN OR %BASS_STREAM_AUTOFREE, 0)
       ELSE
          ta.MediaType = %STREAM
          ta.Channel = BASS_StreamCreateFile(0, ta.AudioFile, 0, 0, %BASS_STREAM_AUTOFREE) ' %BASS_STREAM_PRESCAN)
       END IF

       IF ta.Channel THEN
          BASS_ChannelSetSync(ta.Channel, %BASS_SYNC_END OR %BASS_SYNC_ONETIME, 0, CODEPTR(BassStreamEllapsed), 0)

          FUNCTION = BASS_ChannelPlay(ta.Channel, 0)

'         ////////////////////////////////////////

          LOCAL nScanChannel AS LONG
          nScanChannel = BASS_StreamCreateFile(0, ta.AudioFile, 0, 0, %BASS_STREAM_DECODE)

'         ////////////////////////////////////////

       ELSE
          BassCloseChannel()
       END IF
    END IF
END FUNCTION

FUNCTION BassStreamFree() AS LONG
    IF ta.Channel THEN
       IF ta.MediaType = %MUSIC THEN
          FUNCTION = BASS_MusicFree(ta.Channel)
       ELSE
          FUNCTION = BASS_StreamFree(ta.Channel)
       END IF
       ta.Channel = 0: ta.MediaType = 0
    END IF
END FUNCTION

FUNCTION SolvePeak(BYVAL nValue AS LONG, BYVAL nTotal AS LONG) AS LONG
    LOCAL nRet AS LONG
    nRet = 0: IF nTotal THEN nRet = (nValue * 100) \ nTotal
    FUNCTION = nRet
END FUNCTION

FUNCTION BassChannelGetWimData (BYVAL nChannel AS DWORD) AS DWORD
    DIM fft(255) AS STATIC SINGLE
    LOCAL nRet AS LONG
    RESET fft()
    IF nChannel THEN
       nRet = BASS_ChannelGetData(nChannel, VARPTR(fft(0)), 1024)
    END IF
    FUNCTION = VARPTR(fft(0))
END FUNCTION
