EnableExplicit

Import "KERNEL32.lib"
  lstrlenW(*lpString)
  EndImport

#TestIT = 0
#TagInfos = 0
;#InitForEnum = 0

CompilerIf #TestIT
  #Console = 1
  CompilerElse
  #Console = 0
  CompilerEndIf

Declare.l ctSSLayer_RefreshDeviceList(hWnd.l)

CompilerIf #TestIT
  CompilerIf #Console
    #ctSSLayer_CompiledFeature_NoNeedUnicodeConversion_AtoW = 0
    #ctSSLayer_CompiledFeature_NoNeedUnicodeConversion_WtoA = 1

    XIncludeFile "ctSSLayer_Common_FullDLL.pb"
    CompilerEndIf
  CompilerEndIf

; Copyright (C) 2002-2011 Jan Vorel
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
;
; If you have any questions, suggestions or bug reports about this product,
; contact me: info@ctuser.net; Do not contact me about general B.A.S.I.C.
; language or A.P.I. issues
;
; Still some division by zero / GrabError problems with FMOD Ex
;
; Modified 2005 for FMOD Ex
;
; Currently supporting 4.02.06+ only, for future planed to support latest 4.02.?? and compatible and so on,
; till API is 'fixed'

; ctSSLayer_FlagA:
;   10 -> PlusASIO etc...?; => Enumeration &h10 for ASIO


Prototype.l ProtoFMOD_Channel_SetVolume(hChannel.l, Volume.f)


ProcedureDLL AttachProcess(Instance)
  CompilerIf #Console
    OpenConsole()
    CompilerEndIf
  ;MessageRequester("1", "0")
  Global InfoString.s
  CompilerIf #ctSSLayer_CompiledFeature_NoNeedUnicodeConversion_AtoW = 0
    Global InfoStringW.l ;= AllocateMemory(1024)
    CompilerEndIf

  Global Dim ErrorStr.s(15)

  Structure ctSSSongInfoStruct
    FormatOK.l ;bool
    Format_Frequency.l
    Format_BitRate.l
    Format_Channels.l
    Format_VBR.l ;bool
    Format_QualityMulti.l
    TagOK.l ;bool
    Tag_Year.l
    Tag_TrackID.l
    Tag_Artist.l
    Tag_Title.l
    Tag_Album.l
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
    IR_Name.l
    IR_URL.l
CompilerEndIf
    EndStructure

  Global HasWMAx.l
  #MustDebug = 0 ;$ff ;

  #OutPutListMax = 32
  Global OutPutListCount.l
  CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
    Global Dim OutPutList.l(#OutPutListMax)
    CompilerElse
    Global Dim OutPutList.s(#OutPutListMax)
    CompilerEndIf
  Global Dim OutPutListDev.l(#OutPutListMax)
  Global Dim OutPutListSSLID.s(#OutPutListMax)


  #ctSSLayer_MaxIDs = 16
  #ctSSLayer_MaxIDsX = #ctSSLayer_MaxIDs - 1
  #ctSSLayer_KeepAliveTime = 1000

;  CompilerIf #ctSSLayer_CompiledFeature_Spectrum
;    #ctSSLayer_SpectrumArrayCountX = #ctSSLayer_SpectrumArrayCount - 1
;    CompilerEndIf
  
  Global VersionID.l
  CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
    ;Global xUseCDDA.l
    Global xUseJitter.l
    CompilerEndIf
  Structure tSong
    mode.l
    Pointer.l
    ExtraA.l  ;TTime For Bass, Channel (Or DriveLetterID) For FMod
    CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
      Subsound.l
      CompilerEndIf
    Size.l
    CompilerIf #ctSSLayer_CompiledFeature_ReadTags
      Format_QualityMulti.f
      CompilerEndIf
    IsPaused.l
    CurVolume.l
    BlockWait.l
    EndStructure
  ;Global Dim Song.tSong(#ctSSLayer_MaxIDsX)
  Global xSysPath.s
  Global xSystem.l

  CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
    ;Global IsCDing.l
    CompilerEndIf

  ;Enumeration FakeEnum
  ;  FakeEnumNull = &H0                           ' All platforms - Initialize normally '
  ;  EndEnum

  ;MessageRequester("2", "0")

  CompilerIf 0
    Structure FMOD_CREATESOUNDEXINFO
      cbsize.l
      Length.l
      fileoffset.l
      Numchannels.l
      defaultfrequency.l
      Format.l ;FakeEnum
      decodebuffersize.l
      initialsubsound.l
      Numsubsounds.l
      inclusionlist.l
      inclusionlistnum.l
      pcmreadcallback.l
      pcmsetposcallback.l
      nonblockcallback.l
      dlsname.l ;String
      encryptionkey.l ;String
      maxpolyphony.l
      userdata.l
      suggestedsoundtype.l ;FakeEnum
      ;addad 40441devrelease
      useropen.l
      userclose.l
      userread.l
      userseek.l
      speakermap.l
      initialsoundgroup.l
      initialseekposition.l
      initialseekpostype.l
      EndStructure
    CompilerEndIf

  CompilerIf #ctSSLayer_CompiledFeature_ReadTags
    Structure FMOD_TAG
      xType.l ;FakeEnum
      xDataType.l ;FakeEnum
      xName.l
      xData.l
      xDataLen.l
      xUdated.l
      EndStructure
    CompilerEndIf

  Global *FModExDLL
  #DefBufferSize = 10000
  #NetBufferSize = $20000
  Global *FMOD_System_SetStreamBufferSize
  Global *FMOD_System_CreateSound
  Global *FMOD_System_PlaySound
  Global *FMOD_Channel_SetPaused
  Global *FMOD_Sound_GetLength
  Global *FMOD_Sound_GetOpenState
  Global *FMOD_Channel_SetPosition
  Global *FMOD_Channel_GetPosition
  Global *FMOD_Sound_Release
  Global *FMOD_Channel_Stop
  ;MessageRequester("3", "0")

  ;Global *FMOD_Channel_SetVolume
  CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
    Global *FMOD_Sound_GetSubSound
    CompilerEndIf



  CompilerIf #ctSSLayer_CompiledFeature_Spectrum
    Global *FMOD_System_GetSpectrum
    CompilerEndIf
  CompilerIf #ctSSLayer_CompiledFeature_ReadTags
    Global *FMOD_Sound_GetFormat
    Global *FMOD_Sound_GetDefaults
    Global *FMOD_Sound_GetNumTags
    Global *FMOD_Sound_GetTag
    CompilerEndIf


  ;Private Declare Function GetSystemDirectoryA Lib "kernel32" (ByVal lpBuffer As String, ByVal nSize As Long) As Long
  ;MessageRequester("4", "0")

  ;CompilerIf #ctSSLayer_CompiledFeature_EndCallBack
  ;  Global xCallHWnd.l
  ;  Global xCallMsg.l
  ;  Global xCallParam.l
  ;  CompilerEndIf
  EndProcedure

XIncludeFile "ctSSLayer_Common01.pb"
XIncludeFile "ctSSLayer_Common_GetTrackNumber.pb"

Procedure.l ValH(xChar1.s)
  Define.l xLong1
  Define.l xLong2
  Define.q xLong3

  xChar1 = Right("00000000" + xChar1, 8)
  For xLong1 = 1 To 8
    xLong2 = Asc(Mid(xChar1, xLong1, 1))
    Select xLong2
      Case 48 To 57
        xLong2 = xLong2 - 48
      Case 65 To 70
        xLong2 = xLong2 - 55
      Case 97 To 102
        xLong2 = xLong2 - 87
      Default
        xLong2 = 0
       EndSelect
    ;Debug "Val: " + Str(xLong2)  + " << " + Str(8 - xLong1) + " << " + Str(4 * (8 - xLong1))
    ;Long2 = Pow(16, (8 - xLong1)) * xLong2
    xLong2 = xLong2 << (4 * (8 - xLong1))
    ;Debug "Val: " + Hex(xLong2)
    xLong3 = xLong3 | xLong2
    Next
  ProcedureReturn xLong3
  EndProcedure

Procedure GetFuncs()
  CompilerIf #MustDebug
    Define.l xLong1 = ValH(InputRequester("DebugLevel", "Please enter DebugLevel (hexadecimal!)", "FF"))
    CallFunctionFast(GetFunction(*FModExDLL, "FMOD_Debug_SetLevel"), xLong1)
    CompilerEndIf

  *FMOD_System_SetStreamBufferSize = GetFunction(*FModExDLL, "FMOD_System_SetStreamBufferSize")
  *FMOD_System_CreateSound = GetFunction(*FModExDLL, "FMOD_System_CreateSound")
  *FMOD_System_PlaySound = GetFunction(*FModExDLL, "FMOD_System_PlaySound")
  *FMOD_Channel_SetPaused = GetFunction(*FModExDLL, "FMOD_Channel_SetPaused")
  *FMOD_Sound_GetLength = GetFunction(*FModExDLL, "FMOD_Sound_GetLength")
  *FMOD_Sound_GetOpenState = GetFunction(*FModExDLL, "FMOD_Sound_GetOpenState")
  *FMOD_Channel_SetPosition = GetFunction(*FModExDLL, "FMOD_Channel_SetPosition")
  *FMOD_Channel_GetPosition = GetFunction(*FModExDLL, "FMOD_Channel_GetPosition")
  *FMOD_Sound_Release = GetFunction(*FModExDLL, "FMOD_Sound_Release")
  *FMOD_Channel_Stop = GetFunction(*FModExDLL, "FMOD_Channel_Stop")
  Global FMOD_Channel_SetVolume.ProtoFMOD_Channel_SetVolume = GetFunction(*FModExDLL, "FMOD_Channel_SetVolume")
  CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
    *FMOD_Sound_GetSubSound = GetFunction(*FModExDLL, "FMOD_Sound_GetSubSound")
    CompilerEndIf


  CompilerIf #ctSSLayer_CompiledFeature_Spectrum
    *FMOD_System_GetSpectrum = GetFunction(*FModExDLL, "FMOD_System_GetSpectrum")
    CompilerEndIf
  CompilerIf #ctSSLayer_CompiledFeature_ReadTags
    *FMOD_Sound_GetFormat = GetFunction(*FModExDLL, "FMOD_Sound_GetFormat")
    *FMOD_Sound_GetDefaults = GetFunction(*FModExDLL, "FMOD_Sound_GetDefaults")
    *FMOD_Sound_GetNumTags = GetFunction(*FModExDLL, "FMOD_Sound_GetNumTags")
    *FMOD_Sound_GetTag = GetFunction(*FModExDLL, "FMOD_Sound_GetTag")
    CompilerEndIf
  EndProcedure

Procedure ClearOutputList()
  Define.l xLong1
  
  For xLong1 = 0 To OutPutListCount - 1
    CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
CompilerIf #Console
;PrintN("Freeing? " + Str(xLong1))
CompilerEndIf        
      If OutPutList(xLong1)
CompilerIf #Console        
;PrintN("Freeing! " + Str(xLong1))
CompilerEndIf        
        FreeMemory(OutPutList(xLong1))
        OutPutList(xLong1) = 0
        EndIf
      CompilerElse
      OutPutList(xLong1) = ""
      CompilerEndIf
    Next
  OutPutListCount = 0
  EndProcedure

ProcedureDLL DetachProcess(Instance)
  CompilerIf #Console
    CloseConsole()
    CompilerEndIf

  ClearOutputList()
;  If *FModExDLL
;    If xSystem
;      CallFunction(*FModExDLL, "FMOD_System_Close", xSystem)
;      CallFunction(*FModExDLL, "FMOD_System_Release", xSystem)
;      EndIf
;    CloseLibrary(*FModExDLL)
;    *FModExDLL = 0
;    EndIf
  EndProcedure

ProcedureDLL AttachThread(Instance)
  EndProcedure

ProcedureDLL DetachThread(Instance)
  EndProcedure

ProcedureDLL.l ctSSLayer_CanUseItem(Index.l)
;MessageRequester("10", "0")
  Define.l Result

  ; Index -1 may be used for load-testing!

  Select Index
CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
    Case 0 ; cdda   (force off / force on / user choses)
      Result = 1
    Case 1 ; jitter   (force off / force on / user choses)
      Result = 2
    Case 2 ; runtimecdda/jitter update   (disabled=requires ReInit of soundsystem / available)
      Result = 1
CompilerEndIf
    Case 3 ; checkaccuratestreams   (force off / force on / user choses)
      Result = 2
CompilerIf #ctSSLayer_CompiledFeature_Spectrum
    Case 4 ; UseSpectrum   (disabled / 1=mono / 2=stereo)
      Result = 2
CompilerEndIf
    Case 6 ; ModuleTimeHandling:
      ; 1 = Patterns
      ; 2 = Elapsed time (only)
      ; 4 = Real times
      Result = 4
    Case 7 ; maxDeviceCount
      Result = OutPutListCount
    Case 9 ; MidiXFade   (disabled / available)
      Result = 1
CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
    Case $A ; SupportsCD or CDDA (0/1)
      Result = 1
CompilerEndIf
    ;Case $B ; SupportsCD autodetect ???
    ;  Result = 1
    Case $C ; KeepAliveTime: leave channel stopped before unloading (buffer!)
      Result = 1000
    Case $D      ;#ctSSLayer_MaxIDs ; for future use, currently fixed
      Result = #ctSSLayer_MaxIDs
CompilerIf #ctSSLayer_CompiledFeature_RefreshDeviceList
    Case $E
      Result = 1
CompilerEndIf
;CompilerIf #ctSSLayer_CompiledFeature_EndCallBack
;    Case $10
;      Result = 2
;CompilerEndIf
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
    Case $11 ; Internet/streams; 1 = yes!
      Select VersionID
        Case 0 To $42033, $42100 To $42226, $42300 To $42417, $42500 To $42604, $42700 To $42704, $42710, $24800, $42900
        Default
          Result = 1
        EndSelect
CompilerEndIf
    Case $12 ; NonBlocking supported    (force off / force on / user choses)
      Result = 2
    Case $20
      ;    10 = should update
      ;    11 = untested
      ;    12 = new & untested
      ;    13 = hasrestrictions, up/downgrade

      ;  < 20 = ok

      ;    20 (DirectX missing)
      ;    21 (DirectX to old (or missing))
      ;    30 = to old
      ;    31 = incompatible
      ;    32 = to new
      ;    90 = cannot load (generic something missing)
      ;         should have high priority!

      Select VersionID
        Case -1, 0
          Result = 90
        Case 0 To $40205
          Result = 30
        Case $40416, $40429, $40607, $40704, $40807, $40907
          Result = 31
        Case 0 To $404ff
          Result = 10
; Note (4.22.05): Fixed crash when reinitializing FMOD, i.e. System::close() then System::init(). Bug introduced in 4.22.02.
; Note (4.23.05): Fixed crash when reinitializing FMOD, i.e. System::close() then System::init(). Bug introduced in 4.23.02.
; Seems to work here anyway...?
        Case 0 To $42033, $42100 To $42226, $42300 To $42417, $42500 To $42604, $42700 To $42704
          Result = 13
        Case $44300 To $ffffff
          Result = 12
;        Default
;          Result = 11
        EndSelect
    EndSelect
;  MessageRequester("11", "0")
  ProcedureReturn Result
  EndProcedure

ProcedureDLL ctSSLayer_Close(Fast.l)
  CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
    ;If IsCDing
    ;  EndIf
    CompilerEndIf
  CallFunction(*FModExDLL, "FMOD_System_Close", xSystem)
  CallFunction(*FModExDLL, "FMOD_System_Release", xSystem)
  xSystem = 0
  ;ClearOutputList()
  ;GrabError()
  EndProcedure

ProcedureDLL.l ctSSLayer_DllRelease()
  If *FModExDLL
;OutputDebugString_("DllRelease")
    CloseLibrary(*FModExDLL)
    *FModExDLL = 0
    EndIf
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_GetDeviceFromSSLID
  CompilerIf #ctSSLayer_CompiledFeature_GetDeviceFromSSLIDA
  ProcedureDLL.l ctSSLayer_GetDeviceFromSSLIDA(*SSLID)
  CompilerElse
  Procedure.l ctSSLayer_GetDeviceFromSSLIDA(*SSLID)
  CompilerEndIf
    Define.l Result
    Define.l xLong1
    Define.s xChar1
    Result = -1
    If *SSLID
      xChar1 = UCase(PeekS(*SSLID, -1, #PB_Ascii))
      If Left(xChar1, 7) = "FMODEX_"
        xChar1 = Mid(xChar1, 8)
        For xLong1 = 0 To OutPutListCount - 1
          If OutPutListSSLID(xLong1) = xChar1
            Result = xLong1
            Break
            EndIf
          Next
        EndIf
      EndIf
    ProcedureReturn Result
    CompilerIf #ctSSLayer_CompiledFeature_GetDeviceFromSSLIDA = 1
    EndProcedure
    CompilerElse
    EndProcedure
    CompilerEndIf
  CompilerEndIf

XIncludeFile "ctSSLayer_Common_GetDeviceFromSSLIDW.pb" ;- ctSSLayer_GetDeviceFromSSLIDW

CompilerIf #ctSSLayer_CompiledFeature_GetDeviceSSLID
  CompilerIf #ctSSLayer_CompiledFeature_GetDeviceSSLIDA
  ProcedureDLL.l ctSSLayer_GetDeviceSSLIDA(DeviceID.l)
  CompilerElse
  Procedure.l ctSSLayer_GetDeviceSSLIDA(DeviceID.l)
  CompilerEndIf
    
    If DeviceID >= 0 And DeviceID < OutPutListCount
      InfoString = "fmodex_" + OutPutListSSLID(DeviceID)
      ProcedureReturn @InfoString
      EndIf

  CompilerIf #ctSSLayer_CompiledFeature_GetDeviceSSLIDA
    EndProcedure
  CompilerElse
    EndProcedure
  CompilerEndIf
  CompilerEndIf

XIncludeFile "ctSSLayer_Common_GetDeviceSSLIDW.pb" ;- ctSSLayer_GetDeviceSSLIDW

CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameA
  ProcedureDLL.l ctSSLayer_GetDeviceNameA(DeviceID, *Flags)
    If DeviceID >= 0 And DeviceID < OutPutListCount
      If *Flags: PokeL(*Flags, OutputListDev(DeviceID)): EndIf
      CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
        If OutPutList(DeviceID) = 0
          ProcedureReturn 0
          EndIf
        InfoString = PeekS(OutPutList(DeviceID),-1,#PB_Unicode)
        CompilerElse
        InfoString = OutPutList(DeviceID)
        CompilerEndIf
      ProcedureReturn @InfoString
      EndIf
    EndProcedure
  CompilerEndIf

CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
  ProcedureDLL.l ctSSLayer_GetDeviceNameW(DeviceID, *Flags)
    If DeviceID >= 0 And DeviceID < OutPutListCount
      If *Flags: PokeL(*Flags, OutputListDev(DeviceID)): EndIf
      ProcedureReturn OutPutList(DeviceID)
      EndIf
    ProcedureReturn 0
    EndProcedure
  CompilerEndIf

Procedure DetectIncompatibilities()
  Define.l ErrorStrCount

  Select VersionID
    Case 0 To $42033, $42100 To $42226, $42300 To $42417, $42500 To $42604, $42700 To $42704, $42710, $24800, $42900
      ErrorStr(ErrorStrCount) = "Interned streams"
      ErrorStrCount = ErrorStrCount + 1
    EndSelect
  Select VersionID
    Case $41827 To $41836, $42018 To $42029, $42211 To $42222, $42402 To $42413, $42502 To $42512, $42600
      ErrorStr(ErrorStrCount) = "Unicode file access"
      ErrorStrCount = ErrorStrCount + 1
    EndSelect
  Select VersionID
    Case $42239, $42430, $42617, $42807, $42907
      ErrorStr(ErrorStrCount) = "WaveOut device names"
      ErrorStrCount = ErrorStrCount + 1
    EndSelect
  For ErrorStrCount = ErrorStrCount To 15
    ErrorStr(ErrorStrCount) = ""
    Next
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_GetInfoStringA Or #ctSSLayer_CompiledFeature_GetInfoStringW
  IncludeFile "Common_VerQueryValue.pb"
  ;IncludeFile "ctSSLayer_Common_GetFileVersionEx.pb"
  
  Procedure.s GetCopyRightFromFile(Filename.s)
    ;Define.s Result
    Define.s xChar1
    Define.s xChar2
    Define *hMem
    
    *hMem = GetFileVersionInfo(Filename)
    If *hMem
  ;    xChar1 = Trim(GetFileVersionFieldEx(Filename, #GetFileVersionField_CompanyName))
      ;XDebug ("Comments", *hMem, #GetFileVersionField_Comments)
      ;XDebug ("CompanyName", *hMem, #GetFileVersionField_CompanyName)
      ;XDebug ("FileDesc", *hMem, #GetFileVersionField_FileDesc)
      ;XDebug ("InternalName", *hMem, #GetFileVersionField_InternalName)
      ;XDebug ("LegalCopyright", *hMem, #GetFileVersionField_LegalCopyright)
      
      xChar1 = Trim(VerQueryValue(*hMem, #GetFileVersionField_LegalCopyright))
      If Left(xChar1, 10) = "Copyright "
        xChar1 = LTrim(Mid(xChar1, 11))
        EndIf
      If Left(xChar1, 1) = Chr(169)
        xChar1 = LTrim(Mid(xChar1, 2))
        EndIf
      If Right(xChar1, 20) = "Un4seen Developments"
        xChar1 = xChar1 + " Ltd. All rights reserved."
        EndIf
      If Right(xChar1, 8) = "MaresWEB"
        xChar1 = xChar1 + " (http://www.maresweb.de). All rights reserved"
        EndIf
      If Len(xChar1) < 12
        xChar2 = Trim(VerQueryValue(*hMem, #GetFileVersionField_CompanyName))
        If xChar2
          ;If xChar2 = "Un4seen Developments"
          ;  xChar2 = xChar2 + " Ltd. All rights reserved."
          ;  EndIf
          ;If xChar2 = "MaresWEB"
          ;  xChar2 = xChar2 + " (http://www.maresweb.de). All rights reserved"
          ;  EndIf
          If xChar1
            xChar1 = xChar1 + ", " + xChar2
            Else
            xChar1 = xChar2
            EndIf
          EndIf
        EndIf
  
      If xChar1
        ;Debug Caption + " / " + Mid(Filename, Len(Filename) - FindString(ReverseString(Filename), "\", 1) + 2) + ": " + xChar1
        xChar1 = "Copyright " + xChar1
        EndIf
      
      xChar2 = Trim(VerQueryValue(*hMem, #GetFileVersionField_FileVersion))
      If Left(xChar1, 8) = "Version "
        xChar2 = LTrim(Mid(xChar2, 9))
        EndIf
      ;If Not xChar2
      ;  Define.l xLong1
      ;  Define.l xLong2 = GetFileVersionEx(Filename, @xLong1)
      ;  Debug Hex(xLong2) + " / " + Hex(xLong1)
      ;  EndIf
      If xChar2
        If xChar1
          xChar1 = "Version " + xChar2 + "; " + xChar1
          Else
          xChar1 = "Version " + xChar2
          EndIf
        EndIf
      
      ;If xChar1
       ; ;Debug Caption + " / " + Mid(Filename, Len(Filename) - FindString(ReverseString(Filename), "\", 1) + 2) + ": " + xChar1
       ; ;Debug xChar1
       ; ;Else
       ; ;Debug "********* Fail Copyright *****************************" + Mid(Filename, Len(Filename) - FindString(ReverseString(Filename), "\", 1) + 2)
       ; EndIf
  
      ;XDebug ("LegalTrademarks", *hMem, #GetFileVersionField_LegalTrademarks)
      ;XDebug ("OriginalFilename", *hMem, #GetFileVersionField_OriginalFilename)
      ;XDebug ("PrivateBuild", *hMem, #GetFileVersionField_PrivateBuild)
      ;XDebug ("ProductName", *hMem, #GetFileVersionField_ProductName)
  ;  XDebug ("ProductVersion", *hMem, #GetFileVersionField_ProductVersion)
      ;XDebug ("SpecialBuild", *hMem, #GetFileVersionField_SpecialBuild)
      
      FreeFileVersionInfo(*hMem)
      ProcedureReturn xChar1
      EndIf
    EndProcedure
  
  Procedure GetPluginCPI(Caption.s, *hDLL, Filename.s, ExtraInfo.s)
    ;Global HasPI
    If *hDLL
      Filename = GetCopyRightFromFile(Filename)
      If Filename
        Filename = " " + Filename
        EndIf
      InfoString = Caption + Filename + ExtraInfo
      ;HasPI = 1
      ;Debug Caption + Filename + ExtraInfo
      EndIf
    EndProcedure

  CompilerIf #ctSSLayer_CompiledFeature_GetInfoStringA
  ProcedureDLL.l ctSSLayer_GetInfoStringA(Index.l)
  CompilerElse
  Procedure.l ctSSLayer_GetInfoStringA(Index.l)
  CompilerEndIf
    ;InfoString = ""
  
    Select Index
      Case 0 ; ShortName
        InfoString = "FMOD Ex"
      Case 1 ; FullName
        InfoString = "FMOD Ex Sound System by Firelight Technologies"
      Case 2 ; DllName
        InfoString = "FMODEx.dll"
  ;    Case 3 ; Short Version byfix
  ;      InfoString = "FMODEx"
      Case 4 ; Unique dll/versions string
        InfoString = "fmodex_" + PeekS(ctSSLayer_GetInfoStringA($30), -1, #PB_Ascii)
      ;Case $A ; Full plugin copyright info
      Case $5 ; Full copyright, info etc
        InfoString = ""
        GetPluginCPI("FMOD Ex Soundsystem", *FModExDLL, "fmodex.dll", "")
      
      Case $10 ; StreamsAll
        If HasWMAx
          InfoString = "ogg;mp2,mp3;wma,asf;flac;aac;wav;aiff;raw;"
          Else
          InfoString = "ogg;mp2,mp3;flac;aac;wav;aiff;raw;"
          EndIf
        ; .vag (ps?) With 4.03.10+
      Case $11 ; TrackerAllbutMidi
        Select VersionID
          Case $40308
          Case $40411 To $40423
            If (VersionID & 1) = 0
              InfoString = "mod,it,xm,s3m;oxm;"
              EndIf
          Default
            InfoString = "mod,it,xm,s3m;oxm;"
          EndSelect
      Case $12 ; Midi
        ; also disable for 4003x
        ;If Not VersionID = &H40034 Then
        Select VersionID
          Case $40308
          Case $40425
            InfoString = "mid,midi;"
          Case $40411 To $40423
            If (VersionID & 1) = 0
              InfoString = "mid,midi;" ;rmi,sgt;"
              EndIf
          Default
            InfoString = "mid,midi;" ;rmi,sgt;"
          EndSelect
      Case $19 ; AllDef
        InfoString = "ogg;mp2,mp3;"
      Case $30
        If VersionID > 0
          ;Define.l xLong1
          ;xLong1 = VersionID & $FF00
          ;InfoString = Str(Int(VersionID / $10000)) + "." + Right("0" + Hex(Int(xLong1 / $100)), 2) + "." + Right("0" + Hex(VersionID & $FF), 2)
          InfoString = Str((VersionID & $ffff0000) >> 16) + "." + Right("0" + Hex((VersionID & $ff00) >> 8), 2) + "." + Right("0" + Hex(VersionID & $FF), 2)
          ;ProcedureReturn @InfoString
          Else
          InfoString = ""
          ProcedureReturn 0
          EndIf
      Case $31 ; required dll files; seperate with ;!
        InfoString = "fmodex.dll;msvcrt.dll"
      Case $40 ; homepage URL
        InfoString = "http://www.fmod.org"
      Case $50 To $5f
        InfoString = ErrorStr(Index - $50)
      Default
        InfoString = ""
        ProcedureReturn 0
      EndSelect
    ProcedureReturn @InfoString
  CompilerIf #ctSSLayer_CompiledFeature_GetInfoStringA ; for PB IDE folding feature
    EndProcedure                                       ; for PB IDE folding feature
  CompilerElse                                         ; for PB IDE folding feature
    EndProcedure                                       ; for PB IDE folding feature
  CompilerEndIf                                        ; for PB IDE folding feature
  
  CompilerIf 0                                         ; for enlisting in current project, real procedure in common file
    ProcedureDLL.l ctSSLayer_GetInfoStringW(Index.l)   ; for enlisting in current project, real procedure in common file
      EndProcedure                                     ; for enlisting in current project, real procedure in common file
    CompilerEndIf                                      ; for enlisting in current project, real procedure in common file
  XIncludeFile "ctSSLayer_Common_GetInfoStringW.pb"
  CompilerEndIf
  
  ProcedureDLL.l ctSSLayer_GetPosition(*SongPointer.tSong)
    ; [0 To maxlength]
    ; [0 To maxlength[ For trackers; +1 for display is done by player
    Define.l Result
  
  CompilerIf #ctSSLayer_CompiledFeature_InetStreams
    Define.l xLong1
    Define.l Starving
    If *SongPointer\mode = #ctSSLayer_MusicTypeInetStream
      CompilerIf #Console
        PrintN("GetOpenState: " + Str(xLong1) + " / " + Str(Result) + " / " + Str(Starving))
        CompilerEndIf
      If VersionID < $43306
        CallFunctionFast(*FMOD_Sound_GetOpenState, *SongPointer\Pointer, @xLong1, @Result, @Starving)
        Else
        CallFunctionFast(*FMOD_Sound_GetOpenState, *SongPointer\Pointer, @xLong1, @Result, @Starving, 0)
        EndIf
  ;FMOD_Sound_GetOpenState (ByVal Sound As Long, ByRef Openstate As FMOD_OPENSTATE, ByRef Percentbuffered As Long, ByRef Starving As Long) As FMOD_RESULT
      CompilerIf #Console
        PrintN("GetOpenState: " + Str(xLong1) + " / " + Str(Result) + " / " + Str(Starving))
        CompilerEndIf
  
      If xLong1 = 2 Or Starving
        Result = -1
        EndIf
  ;PrintN("  done")
      Else
  CompilerEndIf
      CallFunctionFast(*FMOD_Channel_GetPosition, *SongPointer\ExtraA, @Result, 1)
  CompilerIf #ctSSLayer_CompiledFeature_InetStreams
      EndIf
  CompilerEndIf
    ProcedureReturn Result
    EndProcedure
  
CompilerIf #ctSSLayer_CompiledFeature_Spectrum
  XIncludeFile "ctSSLayer_Common_MaxF.pb"

  ProcedureDLL ctSSLayer_GetSpectrum(*xSingleA1, ArraySize.l, *SongPointer, Stereo.l)
    Static xLong1.l
    Static xLong2.l
    Static xLong4.l
    Static xSingle1.f
  
    #xWGS_GetWidth = 1024 ; x^2, min 64 Max = 8192
    #xWGS_GetWidth_ArrayLIDX = #xWGS_GetWidth * 2 - 1
    Static Dim xSingleA2.f(#xWGS_GetWidth_ArrayLIDX)
  
    If CallFunctionFast(*FMOD_System_GetSpectrum, xSystem, @xSingleA2(0), #xWGS_GetWidth, 0, 0)
CompilerIf #Console
    PrintN("SpecFail1")
CompilerEndIf
      If Stereo = 3
        RtlZeroMemory_(*xSingleA1, ArraySize * 8)
        Else
        RtlZeroMemory_(*xSingleA1, ArraySize * 4)
        EndIf
      ElseIf CallFunctionFast(*FMOD_System_GetSpectrum, xSystem, @xSingleA2(#xWGS_GetWidth), #xWGS_GetWidth, 1, 0)
CompilerIf #Console
    PrintN("SpecFail2")
CompilerEndIf
      If Stereo = 3
        RtlZeroMemory_(*xSingleA1, ArraySize * 8)
        Else
        RtlZeroMemory_(*xSingleA1, ArraySize * 4)
        EndIf
      Else
CompilerIf #Console
;    PrintN("SpecOk")
CompilerEndIf
      If ArraySize > #xWGS_GetWidth: ArraySize = #xWGS_GetWidth: EndIf
      For xLong1 = 0 To ArraySize - 1
        xLong2 = xLong1 * #xWGS_GetWidth / ArraySize
        xSingle1 = MaxF(xSingleA2(xLong2) , xSingleA2(xLong2 + #xWGS_GetWidth))
        For xLong4 = xLong2 + 1 To (xLong1 + 1) * #xWGS_GetWidth / ArraySize - 1
          xSingle1 = MaxF(MaxF(xSingle1, xSingleA2(xLong4)), xSingleA2(xLong4 + #xWGS_GetWidth))
          Next
        xSingleA2(xLong1) = xSingle1 * 1.5 ;/ (2 * (xLong3 - xLong2 + 1))
        ;xSingleA2(xLong1) = (xSingleA2(xLong1 * 1024 / ArraySize) + xSingleA2(xLong1 * 1024 / ArraySize + #xWGS_GetWidth)) ;* 1.2
        Next
      CopyMemory(@xSingleA2(0), *xSingleA1, ArraySize * 4)
      EndIf
    EndProcedure
  CompilerEndIf

ProcedureDLL.l ctSSLayer_Init(OutputDeviceID, hWnd.l, UseCDDA.l, UseJitterCorrection.l)
  Define.s xChar1
  Define.l xLong1

CompilerIf #Console And 1
  PrintN("Init1: " + Str(OutputDeviceID) + " / " + Str(OutPutListCount))
CompilerEndIf
  If OutputDeviceID >= 0 And OutputDeviceID < OutPutListCount
CompilerIf #Console And 1
  PrintN("Init2")
CompilerEndIf
    If CallFunction(*FModExDLL, "FMOD_System_Create", @xSystem) = 0
CompilerIf #Console And 1
  PrintN("Init3")
CompilerEndIf
      If ((OutputListDev(OutputDeviceID) & $FF00) >> 8) = 1
        CallFunction(*FModExDLL, "FMOD_System_SetOutput", xSystem, 6)
        Else
        CallFunction(*FModExDLL, "FMOD_System_SetDSPBufferSize", xSystem, 2048, 16)
        If VersionID = $40305
          CallFunction(*FModExDLL, "FMOD_System_SetOutput", xSystem, 8)
          Else
          CallFunction(*FModExDLL, "FMOD_System_SetOutput", xSystem, 7)
          EndIf
        EndIf
CompilerIf #Console And 1
  PrintN("Output: " + Hex(OutputListDev(OutputDeviceID)))
CompilerEndIf

      CallFunction(*FModExDLL, "FMOD_System_SetDriver", xSystem, (OutputListDev(OutputDeviceID) & $FF))
      CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
        ;xUseCDDA = UseCDDA
        xUseJitter = UseJitterCorrection
        CompilerEndIf
      If CallFunction(*FModExDLL, "FMOD_System_Init", xSystem, 256, 0, 0) = 0
CompilerIf #Console And 1
  PrintN("Init4")
CompilerEndIf
        GetFuncs()
  CompilerIf #ctSSLayer_CompiledFeature_InetStreams
        CallFunctionFast(*FMOD_System_SetStreamBufferSize, xSystem, #DefBufferSize, 1)
  CompilerElse
        CallFunctionFast(*FMOD_System_SetStreamBufferSize, xSystem, 10000, 1)  ;FMOD_TIMEUNIT_MS ;FMOD_SOUND_TYPE_RAW
        ;GrabError
  CompilerEndIf
        ProcedureReturn 1
        Else
        ;GrabError
        CallFunction(*FModExDLL, "FMOD_System_Close", xSystem)
        CallFunction(*FModExDLL, "FMOD_System_Release", xSystem)
        EndIf
      EndIf
    EndIf
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_ReadTags
  Procedure.f GetQualityMultiByFilename(Filename.s)
    Define Result.f
  
    Select LCase(GetExtensionPart(FileName))
      Case "wav"
        Result = 0.1
      Case "flac"
        Result = 0.2
      Default
        Result = 1
      EndSelect
    ProcedureReturn Result
    EndProcedure
  CompilerEndIf
  
Procedure.l ctSSLayer_LoadSong(mode.l, *Filename, Flags.l, UnicodeFlag.l) ;, TrackID.l)
  Define.l xLong1
  ;Define.FMOD_CREATESOUNDEXINFO xType1
  Define *SongPointer.tSong = AllocateMemory(SizeOf(tSong))
  Define.s FilenameAnsi
  Define.s xChar1
  Define.l FlagsNew
  Define sCDDAFix.l
  Define *CDDAFix
  
  If *SongPointer
    If UnicodeFlag
      FilenameAnsi = PeekS(*Filename, -1, #PB_Unicode)
      Select VersionID
        Case $41827 To $41836, $42018 To $42029, $42211 To $42222, $42402 To $42413, $42502 To $42512, $42600
          *Filename = @FilenameAnsi
          UnicodeFlag = 0
        EndSelect
      Else
      FilenameAnsi = PeekS(*Filename, -1, #PB_Ascii)
      EndIf
  
    If ((Flags & 1) And VersionID = $40310)
      Flags = Flags - 1
      EndIf
  
    ;If VersionID < $40441
    ;  xType1\cbsize = 76
    ;  ElseIf VersionID < $40719
    ;  xType1\cbsize = 92
    ;  ElseIf VersionID < $40723
    ;  xType1\cbsize = 96
    ;  ElseIf VersionID < $40805
    ;  xType1\cbsize = 100
    ;  Else
    ;  xType1\cbsize = 108
    ;  EndIf
    Select mode
      
  CompilerIf #ctSSLayer_CompiledFeature_InetStreams
      Case #ctSSLayer_MusicTypeInetStream
        ;PrintN("StreamOpen: " + FilenameAnsi)
        If ctSSLayer_CanUseItem($11)
          If (Flags & 2)
;            Select VersionID
;              Case $42710, $24800, $42900
;                MessageRequester("1", "1")
;              Default
;                MessageRequester("2", "2")
                FlagsNew = $10000
                *SongPointer\BlockWait = 1
;              EndSelect
            EndIf
            
;MessageRequester("a", "a")
          CallFunctionFast(*FMOD_System_SetStreamBufferSize, xSystem, #NetBufferSize, 8)
;MessageRequester("b", "b")
          CallFunctionFast(*FMOD_System_CreateSound, xSystem, @FilenameAnsi, $C9 | FlagsNew, 0, @*SongPointer\Pointer)
;MessageRequester("c", "c")
          CallFunctionFast(*FMOD_System_SetStreamBufferSize, xSystem, #DefBufferSize, 1)
;MessageRequester("d", "d")
          *SongPointer\Size = 3600000 ; 1h ; 300000 ;5 mins!
          ;PrintN("  ok")
          EndIf
  CompilerEndIf
      Case #ctSSLayer_MusicTypeStream
        If (Flags & 1)
          FlagsNew = $4000 ;FMOD_ACCURATETIME
CompilerIf #Console And 1
          PrintN("SetAccurate")
CompilerEndIf
          EndIf
        If (Flags & 2)
          *SongPointer\BlockWait = 1
          FlagsNew = FlagsNew | $10000 ; FMOD_NONBLOCKING
CompilerIf #Console And 1
          PrintN("SetNonBlocking")
CompilerEndIf
          EndIf
        CallFunctionFast(*FMOD_System_CreateSound, xSystem, *FileName, $80C9 | FlagsNew | UnicodeFlag, 0, @*SongPointer\Pointer)
        If (Flags & 2) = 0 And *SongPointer\Pointer
          CallFunctionFast(*FMOD_Sound_GetLength, *SongPointer\Pointer, @*SongPointer\Size, 1)
          EndIf
        ;GrabError
  CompilerIf #ctSSLayer_CompiledFeature_MusicPatternBased
      Case #ctSSLayer_MusicTypeTracker
        CallFunctionFast(*FMOD_System_CreateSound, xSystem, *FileName, $C0C9 + UnicodeFlag, 0, @*SongPointer\Pointer)
        If *SongPointer\Pointer
          CallFunctionFast(*FMOD_Sound_GetLength, *SongPointer\Pointer, @*SongPointer\Size, 1)
          EndIf
  CompilerEndIf
  CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
      Case #ctSSLayer_MusicTypeCDAudio
        xLong1 = Val(GetTrackNumber(FilenameAnsi)) - 1
        xChar1 = Left(FilenameAnsi, 2)
        
        Select VersionID
          Case $43305 To $43309
            sCDDAFix = 33
          Case $43302 To $43304
            sCDDAFix = 32
          EndSelect
        
        If sCDDAFix
          *CDDAFix = AllocateMemory(sCDDAFix * 4)
          If *CDDAFix
            PokeL(*CDDAFix, sCDDAFix * 4)
            If xUseJitter
              CallFunctionFast(*FMOD_System_CreateSound, xSystem, @xChar1, $28080C9, *CDDAFix, @*SongPointer\Pointer)
              Else
              CallFunctionFast(*FMOD_System_CreateSound, xSystem, @xChar1, $20080C9, *CDDAFix, @*SongPointer\Pointer)
              EndIf
            FreeMemory(*CDDAFix)
            EndIf
          Else
          If xUseJitter
            CallFunctionFast(*FMOD_System_CreateSound, xSystem, @xChar1, $28080C9, 0, @*SongPointer\Pointer)
            Else
            CallFunctionFast(*FMOD_System_CreateSound, xSystem, @xChar1, $20080C9, 0, @*SongPointer\Pointer)
            EndIf
          EndIf


        
        
        
        If *SongPointer\Pointer
          CallFunctionFast(*FMOD_Sound_GetSubSound, *SongPointer\Pointer, xLong1, @*SongPointer\Subsound)
          If *SongPointer\Subsound
            CallFunctionFast(*FMOD_Sound_GetLength, *SongPointer\Subsound, @*SongPointer\Size, 1)
            Else
            CallFunctionFast(*FMOD_Sound_Release, *SongPointer\Pointer)
            *SongPointer\Pointer = 0
            EndIf
          EndIf
  CompilerEndIf
      EndSelect
    
        ;PrintN("  morestuff")
  ;  MessageRequester(Str(*SongPointer\Pointer),"3")
    If *SongPointer\Pointer
      *SongPointer\mode = mode
      If *SongPointer\Size = -1
        *SongPointer\Size = 0
        EndIf
      ;If *SongPointer\Size = 0
      ;  *SongPointer\Size = 1
      ;  EndIf
      CompilerIf #ctSSLayer_CompiledFeature_ReadTags
        *SongPointer\Format_QualityMulti = GetQualityMultiByFilename(FilenameAnsi)
        CompilerEndIf
  ;    MessageRequester("-","4")
      ;PrintN("  return")
CompilerIf #Console And 1
          PrintN("LoadSuccess")
CompilerEndIf
      ProcedureReturn *SongPointer
      Else
      ;PrintN("  fail")
      FreeMemory(*SongPointer)
      ;PrintN("  returnfail")
      EndIf
    EndIf
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_LoadSongA
  ProcedureDLL.l ctSSLayer_LoadSongA(mode.l, *Filename, Flags.l)
    ProcedureReturn ctSSLayer_LoadSong(mode, *Filename, Flags, 0)
    EndProcedure
  CompilerEndIf

CompilerIf #ctSSLayer_CompiledFeature_LoadSongW
  ProcedureDLL.l ctSSLayer_LoadSongW(mode.l, *Filename, Flags.l)
    ProcedureReturn ctSSLayer_LoadSong(mode, *Filename, Flags, $1000000)
    EndProcedure
  CompilerEndIf

ProcedureDLL.l ctSSLayer_LoadSongFinished(*SongPointer.tSong)
  Define.l xLong1
  Define.l xResult

  If *SongPointer\BlockWait
    If VersionID < $43306
      CallFunctionFast(*FMOD_Sound_GetOpenState, *SongPointer\Pointer, @xLong1, 0, 0)
      Else
      CallFunctionFast(*FMOD_Sound_GetOpenState, *SongPointer\Pointer, @xLong1, 0, 0, 0)
      EndIf
CompilerIf #Console And 1
          PrintN("WaitState: " + Str(xLong1))
CompilerEndIf

    ;Debug "BW: " + Str(xLong1)
    Select xLong1
      Case 0
        xResult = 1
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
If Not *SongPointer\mode = #ctSSLayer_MusicTypeInetStream
CompilerEndIf
        CallFunctionFast(*FMOD_Sound_GetLength, *SongPointer\Pointer, @*SongPointer\Size, 1)
        ;If *SongPointer\Size = 0
        ;  *SongPointer\Size = 1
        ;  EndIf
;        *SongPointer\BlockWait = 0
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
EndIf
CompilerEndIf
        *SongPointer\BlockWait = 0
;CompilerIf #ctSSLayer_CompiledFeature_InetStreams
      ;Case 4
      ;  If *SongPointer\mode = #ctSSLayer_MusicTypeInetStream
      ;    xResult = 1
      ;    *SongPointer\BlockWait = 0
;     ;     *SongPointer\Size = 1
      ;    EndIf
;CompilerEndIf        
      Case 2
        xResult = -1
      EndSelect
    Else
    xResult = 1
    EndIf
CompilerIf #Console And 1
          PrintN("  Result: " + Str(xResult))
CompilerEndIf
  ProcedureReturn xResult
  EndProcedure

;Continues LoadSong
ProcedureDLL ctSSLayer_LoadSongAbort(*SongPointer.tSong)
  CallFunctionFast(*FMOD_Sound_Release, *SongPointer\Pointer)
  ;*SongPointer\mode = #ctSSLayer_MusicTypeNone
  FreeMemory(*SongPointer)
  EndProcedure
  
;Continues LoadSong
ProcedureDLL.l ctSSLayer_LoadSongPlay(*SongPointer.tSong, Volume.l, LoopSong.l, Panning.l, Paused.l)
  *SongPointer\CurVolume = Volume
  ;PrintN("Vol_Play: " + Str(*SongPointer\CurVolume))
  *SongPointer\IsPaused = Paused

;PrintN("Play...")

CompilerIf #ctSSLayer_CompiledFeature_InetStreams
  If *SongPointer\mode = #ctSSLayer_MusicTypeInetStream
    If ctSSLayer_CanUseItem($11)
      CallFunctionFast(*FMOD_System_PlaySound, xSystem, -1, *SongPointer\Pointer, 1, @*SongPointer\ExtraA)
      ;PrintN("Called")
      If *SongPointer\ExtraA
        ;PrintN("  success")
        If Paused
          FMOD_Channel_SetVolume(*SongPointer\ExtraA, 0)
          Else
          FMOD_Channel_SetVolume(*SongPointer\ExtraA, Volume / 255)
          EndIf
        CallFunctionFast(*FMOD_Channel_SetPaused, *SongPointer\ExtraA, 0)
        ;PrintN("  return")
        ProcedureReturn 1
        EndIf
      EndIf
    Else
CompilerEndIf
CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
    If *SongPointer\mode = #ctSSLayer_MusicTypeCDAudio
      CallFunctionFast(*FMOD_System_PlaySound, xSystem, -1, *SongPointer\Subsound, 1, @*SongPointer\ExtraA)
      Else
CompilerEndIf
      CallFunctionFast(*FMOD_System_PlaySound, xSystem, -1, *SongPointer\Pointer, 1, @*SongPointer\ExtraA)
CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
      EndIf
CompilerEndIf
    If *SongPointer\ExtraA
      FMOD_Channel_SetVolume(*SongPointer\ExtraA, Volume / 255)
      If Paused = 0
        CallFunctionFast(*FMOD_Channel_SetPaused, *SongPointer\ExtraA, 0)
        EndIf
      ProcedureReturn 1
      EndIf
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
    EndIf
CompilerEndIf

  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_MusicPatternBased = 2
  ProcedureDLL.l ctSSLayer_MusicGetPattern(*SongPointer)
    EndProcedure
  
  ProcedureDLL.l ctSSLayer_MusicGetPatterns(*SongPointer)
    EndProcedure
  
  ProcedureDLL.l ctSSLayer_MusicHasFinished(*SongPointer)
    EndProcedure
  
  ProcedureDLL.l ctSSLayer_MusicTime(*SongPointer)
    EndProcedure
  CompilerEndIf

XIncludeFile "ctSSLayer_Common_GetWinSysPath.pb"
XIncludeFile "ctSSLayer_Common_TestForDll.pb"

; First function called, must handle missing dll file
ProcedureDLL.l ctSSLayer_PreInit(hWnd.l)
;OutputDebugString_("PreInitBegin")
  *FModExDLL = OpenLibrary(#PB_Any, "fmodex.dll")
  If *FModExDLL
    If CallFunction(*FModExDLL, "FMOD_System_Create", @xSystem) = 0
;OutputDebugString_("PreInitOk")
      CallFunction(*FModExDLL, "FMOD_System_GetVersion", xSystem, @VersionID)
      DetectIncompatibilities()
      CallFunction(*FmodExDLL, "FMOD_System_Release", xSystem)
      If ctSSLayer_CanUseItem($20) > 19
        CloseLibrary(*FModExDLL)
        *FModExDLL = 0
        VersionID = -1
        Else
        xSysPath = GetWinSysPath()
        HasWMAx = TestForDll(xSysPath + "wmasf.dll", 7.0001)
        ctSSLayer_RefreshDeviceList(hWnd)
        ProcedureReturn OutPutListCount
        EndIf
      Else
      VersionID = -1
;OutputDebugString_("PreInitFail")
      CloseLibrary(*FModExDLL)
      *FModExDLL = 0
      EndIf
    Else
;OutputDebugString_("PreInitNoDLL")
    VersionID = -1
    EndIf
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_ReadTags
  XIncludeFile "ctSSLayer_Common_Trim.pb"

  Procedure GetTag(*FModTagStruct.FMOD_TAG, *ResultA, *ResultW, *ResultNum)
    Define.s xChar1
    Define xLong1.l
    Define xLong2.l
    Define xLong3.l
    Define xLong4.l
    Define HasPrio.l

CompilerIf #Console And #TagInfos
    Debug("*GetTag1")
CompilerEndIf
  
    If *ResultNum
      xLong1 = PeekL(*ResultNum)
      Else
      If PeekL(*ResultA) Or PeekL(*ResultW)
        xLong1 = 1
        EndIf
      EndIf
  
    ;If Not (*FModTagStruct\xType = 1 And HasValue =0 Or *FModTagStruct\xDataLen = 0)
    If ((Not *FModTagStruct\xType = 1) Or xLong1 = 0) And *FModTagStruct\xDataLen
      ; 1=FSOUND_TAGFIELD_ID3V1
CompilerIf #Console And #TagInfos
    Debug("*GetTag2, " + Str(*FModTagStruct))
    Debug("  Type=" + Str(*FModTagStruct\xDataType))
CompilerEndIf

      Select *FModTagStruct\xDataType
;        Case 0 ;& 'binary
;          MessageRequester("BinaryTag", "0")
;        Case 1 ;& 'int
;          MessageRequester("Int", "1")
;        Case 2 ;& 'floag
;          MessageRequester("Float", "2")
        
        Case 3 ; FMOD_TAGDATATYPE_STRING
          Select *FModTagStruct\xType
            Case 3, 4, 5, 6 ; ogg, shoutcast, icecast, asf
              *FModTagStruct\xDataType = 6
            ;Case 4
            ;  *FModTagStruct\xDataType = 3 ;shoutcast
          EndSelect
          ;NewPrio = 0
;MessageRequester("Ansi", "")
CompilerIf #Console And #TagInfos
          Debug("Ansi/String: " + PeekS(*FModTagStruct\xData, *FModTagStruct\xDataLen, #PB_Ascii))
          Debug("  (UTF8):    " + PeekS(*FModTagStruct\xData, *FModTagStruct\xDataLen, #PB_UTF8))
;          debug("  (UTF8-1):    " + PeekS(*FModTagStruct\xData, *FModTagStruct\xDataLen-1, #PB_UTF8))
;          debug("  (UTF8-2):    " + PeekS(*FModTagStruct\xData, *FModTagStruct\xDataLen-2, #PB_UTF8))
Define.s xC1
Define.s xC2
Define.l xL1
Define.l xL2
For xL1 = 0 To *FModTagStruct\xDataLen - 1
  xL2 = PeekB(*FModTagStruct\xData + xL1)
  xC1 = xC1 + Chr(xL2) + " "
  xC2 = xC2 + Right("0" + Hex(xL2), 2)
  Next
Debug(xC1)
Debug(xC2)
CompilerEndIf
;MessageRequester("Ansi/String", PeekS(*FModTagStruct\xData, *FModTagStruct\xDataLen, #PB_Ascii))

          Goto AnsiUTFHack
        Case 4 ; FMOD_TAGDATATYPE_STRING_UTF16
CompilerIf #Console And #TagInfos
          If *FModTagStruct\xDataLen > 1
            xLong1 = *FModTagStruct\xDataLen
            If (*FModTagStruct\xDataLen % 2)
              xLong1 - 1
              EndIf
            xLong1 = xLong1 / 2
            Debug("UTF16/String: " + PeekS(*FModTagStruct\xData, xLong1, #PB_Unicode))
            EndIf
CompilerEndIf
;MessageRequester("UTF16/String:", PeekS(*FModTagStruct\xData, *FModTagStruct\xDataLen, #PB_Unicode))
          HasPrio = 1
          xLong1 = *FModTagStruct\xData
          xLong2 = *FModTagStruct\xDataLen
          If *FModTagStruct\xDataLen > 1
            If PeekB(*FModTagStruct\xData) = -1 And PeekB(*FModTagStruct\xData + 1) = -2
              xLong1 + 2
              xLong2 - 2
              EndIf
            EndIf
          If (*FModTagStruct\xDataLen % 2)
            xLong2 - 1
            EndIf
          xLong2 / 2
UTF8Hack:
          TrimW(@xLong1, @xLong2)
          If xLong2
            xChar1 = PeekS(xLong1, xLong2, #PB_Unicode)
            If xChar1
              If *ResultNum
CompilerIf #Console And #TagInfos
          Debug("  IsNum")
CompilerEndIf
                PokeL(*ResultNum, Val(xChar1))
CompilerIf #Console And #TagInfos
          Debug("  IsNumOk")
CompilerEndIf
                Else
CompilerIf #Console And #TagInfos
          Debug("  IsStr0")
CompilerEndIf
                xLong3 = ReAllocateMemory(PeekL(*ResultA), xLong2 + 1)
                If xLong3
                  PokeS(xLong3, xChar1, xLong2, #PB_Ascii)
CompilerIf #Console And #TagInfos
          Debug("  IsStr1")
CompilerEndIf
                  PokeL(*ResultA, xLong3)
CompilerIf #Console And #TagInfos
          Debug("  IsStr2")
CompilerEndIf
                  EndIf
                EndIf
              EndIf
            If *ResultNum = 0
              If (HasPrio Or PeekL(*ResultW) = 0)
                xLong3 = ReAllocateMemory(PeekL(*ResultW), xLong2 * 2 + 2)
                If xLong3
                  CopyMemory(xLong1, xLong3, xLong2 * 2)
                  PokeW(xLong3 + xLong2 * 2, 0)
                  PokeL(*ResultW, xLong3)
;Else
;MessageRequester("ReAllocFailed", "")
                  EndIf
                EndIf
              EndIf
            EndIf
          If xLong4
            FreeMemory(xLong4)
            EndIf
;        Case 5 ; FMOD_TAGDATATYPE_STRING_UTF16BE
;MessageRequester("UTF16BE", "UTF16BE")
        Case 6 ; FMOD_TAGDATATYPE_STRING_UTF8 / shared with Ansi
CompilerIf #Console And #TagInfos
          Debug("UTF8/String: " + PeekS(*FModTagStruct\xData, *FModTagStruct\xDataLen, #PB_Ascii))
          Debug("  UTF8:      " + PeekS(*FModTagStruct\xData, *FModTagStruct\xDataLen, #PB_UTF8))
CompilerEndIf
;MessageRequester("UTF8/String:", PeekS(*FModTagStruct\xData, *FModTagStruct\xDataLen, #PB_Ascii))
AnsiUTFHack:
          If *FModTagStruct\xType = 2
            HasPrio = 1
            EndIf
          If *FModTagStruct\xDataType = 3
            xLong3 = MultiByteToWideChar_(0, 0, *FModTagStruct\xData, *FModTagStruct\xDataLen, 0, 0)
            Else
            xLong3 = MultiByteToWideChar_(65001, 0, *FModTagStruct\xData, *FModTagStruct\xDataLen, 0, 0)
            EndIf
          If xLong3
            xLong4 = AllocateMemory((xLong3 + 2) * 2)
            If xLong4
              If *FModTagStruct\xDataType = 3
                xLong2 = MultiByteToWideChar_(0, 0, *FModTagStruct\xData, *FModTagStruct\xDataLen, xLong4, xLong3 + 1)
                Else
                xLong2 = MultiByteToWideChar_(65001, 0, *FModTagStruct\xData, *FModTagStruct\xDataLen, xLong4, xLong3 + 1)
                HasPrio = 1
                EndIf
              If xLong2 > 1
                xLong1 = xLong4
                Goto UTF8Hack
                EndIf
              FreeMemory(xLong4)
              EndIf
            EndIf
 ;       Case 7
          ;cd toc
;MessageRequester("CDTOC", "")
        EndSelect
      ;debug("-> " + xChar1)
      ;ProcedureReturn Trim(xChar1)
      EndIf
    EndProcedure
  ;CompilerEndIf

Procedure.l ctSSLayer_ReadSongInfo(*SongPointer.tSong, *xSongInfo.ctSSSongInfoStruct, IsUnicode.l)
  Define.l xLong1
  Define.l xLong2
  Define.f xSingle1
  Define.ctSSSongInfoStruct xType1
  Define.FMOD_TAG xType2

;PrintN("ReadTags")
CompilerIf #Console
  Debug("EnterTagReading")
CompilerEndIf


CompilerIf #ctSSLayer_CompiledFeature_InetStreams
  If *SongPointer\mode = #ctSSLayer_MusicTypeStream Or *SongPointer\mode = #ctSSLayer_MusicTypeInetStream
CompilerElse
  If *SongPointer\mode = #ctSSLayer_MusicTypeStream
CompilerEndIf
    ;MessageRequester(Str(*SongPointer\mode), "-")
    If CallFunctionFast(*FMOD_Sound_GetLength, *SongPointer\Pointer, @xLong1, 8) = 0
      If CallFunctionFast(*FMOD_Sound_GetFormat, *SongPointer\Pointer, #Null, #Null, @xType1\Format_Channels, #Null) = 0
        If CallFunctionFast(*FMOD_Sound_GetDefaults, *SongPointer\Pointer, @xSingle1, #Null, #Null, #Null) = 0
          xType1\Format_Frequency = xSingle1
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
          If *SongPointer\mode = #ctSSLayer_MusicTypeStream
            If *SongPointer\Size
              xType1\Format_BitRate = xLong1 / *SongPointer\Size * 8000
              EndIf
            EndIf
CompilerElse
          If *SongPointer\Size
            xType1\Format_BitRate = xLong1 / *SongPointer\Size * 8000
            EndIf
CompilerEndIf
          xType1\FormatOK = 1
          ;xSongInfo = xType1
          PokeF(@xType1\Format_QualityMulti, *SongPointer\Format_QualityMulti)
          RtlMoveMemory_(*xSongInfo, @xType1, SizeOf(xType1))
          ;Else
          ;xType1\Format_Channels = 0
          EndIf
        EndIf
      EndIf

CompilerIf #Console
  Debug("MiddleTagReading")
CompilerEndIf
  
    Global strTitle.l: If strTitle: FreeMemory(strTitle): strTitle = 0: EndIf
    Global strTitleW.l: If strTitleW: FreeMemory(strTitleW): strTitleW = 0: EndIf
    Global strArtist.l: If strArtist: FreeMemory(strArtist): strArtist = 0: EndIf
    Global strArtistW.l: If strArtistW: FreeMemory(strArtistW): strArtistW = 0: EndIf
    Global strAlbum.l: If strAlbum: FreeMemory(strAlbum): strAlbum = 0: EndIf
    Global strAlbumW.l: If strAlbumW: FreeMemory(strAlbumW): strAlbumW = 0: EndIf
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
    Global strIcyName.l: If strIcyName: FreeMemory(strIcyName): strIcyName = 0: EndIf
    Global strIcyNameW.l: If strIcyNameW: FreeMemory(strIcyNameW): strIcyNameW = 0: EndIf
    Global strIcyURL.l: If strIcyURL: FreeMemory(strIcyURL): strIcyURL = 0: EndIf
    Global strIcyURLW.l: If strIcyURLW: FreeMemory(strIcyURLW): strIcyURLW = 0: EndIf
CompilerEndIf
  
    If CallFunctionFast(*FMOD_Sound_GetNumTags, *SongPointer\Pointer, @xLong2, #Null) = 0
      If xLong2
        ;MessageRequester("1", "1-" + Str(xLong2))
        For xLong1 = 0 To xLong2 - 1
          If CallFunctionFast(*FMOD_Sound_GetTag, *SongPointer\Pointer, #Null, xLong1, @xType2) = 0
            Select UCase(PeekS(xType2\xName, -1, #PB_Ascii))
              Case "TITLE", "TIT2"
                GetTag(@xType2, @strTitle, @strTitleW, 0)
              Case "ARTIST", "TPE1"
                GetTag(@xType2, @strArtist, @strArtistW, 0)
              Case "ALBUM", "TALB"
                GetTag(@xType2, @strAlbum, @strAlbumW, 0)
              Case "YEAR", "TYER"
                GetTag(@xType2, 0, 0, @*xSongInfo\Tag_Year)
              Case "TRACK", "TRCK"
                GetTag(@xType2, 0, 0, @*xSongInfo\Tag_TrackID)
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
                Case "ICY-NAME"
                  GetTag(@xType2, @strIcyName, @strIcyNameW, 0)
                  ;If strIcyNameW
                  ;  debug("NW: " + PeekS(strIcyNameW, -1, #PB_Unicode))
                  ;  EndIf
                Case "ICY-URL"
                  GetTag(@xType2, @strIcyURL, @strIcyURLW, 0)
                Case "ICY-BR"
                  If *SongPointer\mode = #ctSSLayer_MusicTypeInetStream
                    GetTag(@xType2, 0, 0, @*xSongInfo\Format_BitRate)
                    *xSongInfo\Format_BitRate * 1000
                    ;Print("Recd: " + Str(*xSongInfo\Format_BitRate))
                    EndIf
CompilerEndIf
              EndSelect
            EndIf
          Next
        *xSongInfo\TagOK = #True
        EndIf
      EndIf
  
    If IsUnicode
      *xSongInfo\Tag_Title = strTitleW
      *xSongInfo\Tag_Artist = strArtistW
      *xSongInfo\Tag_Album = strAlbumW
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
      If *SongPointer\mode = #ctSSLayer_MusicTypeInetStream
        *xSongInfo\IR_Name = strIcyNameW
        *xSongInfo\IR_URL = strIcyURLW
        EndIf
CompilerEndIf
      Else
      *xSongInfo\Tag_Title = strTitle
      *xSongInfo\Tag_Artist = strArtist
      *xSongInfo\Tag_Album = strAlbum
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
      If *SongPointer\mode = #ctSSLayer_MusicTypeInetStream
        *xSongInfo\IR_Name = strIcyName
        *xSongInfo\IR_URL = strIcyURL
        EndIf
CompilerEndIf
      EndIf
CompilerIf #Console
  Debug("LeaveTagReading")
CompilerEndIf
    ProcedureReturn 1
    EndIf
  ;ProcedureReturn 0
CompilerIf #Console
  Debug("LeaveTagReading")
CompilerEndIf
  EndProcedure
  
CompilerIf #ctSSLayer_CompiledFeature_ReadTagsA
  ProcedureDLL.l ctSSLayer_ReadSongInfoA(*SongPointer, *xSongInfo)
    ProcedureReturn ctSSLayer_ReadSongInfo(*SongPointer, *xSongInfo, 0)
    EndProcedure
  CompilerEndIf
  
CompilerIf #ctSSLayer_CompiledFeature_ReadTagsW
  ProcedureDLL.l ctSSLayer_ReadSongInfoW(*SongPointer, *xSongInfo)
    ProcedureReturn ctSSLayer_ReadSongInfo(*SongPointer, *xSongInfo, 1)
    EndProcedure
  CompilerEndIf
  CompilerEndIf

Procedure.s FModGuidToStr(*GUID, IDFix.l)
  Define.l xLong1
  Define.s Result
  For xLong1 = 0 To 15
    Result = Result + Right("00" + Hex(PeekB(*Guid + xLong1)), 2)
    Next
  If Result = "00000000000000000000000000000000"
    Result = Str(IDFix)
    EndIf
  ProcedureReturn Result
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
  XIncludeFile "Common_LocalAllocStringWFromAnsiOrUTF8Mem.pb" ;- LocalAllocStringWFromAnsiOrUTF8Mem
  XIncludeFile "Common_LocalAllocStringWFromW.pb" ;- LocalAllocStringWFromW
  CompilerEndIf

Procedure EnumerateDrv(DsID.l, hWnd.l)
  Define.l xLong1
  Define.l xLong2
  Define.l xLong3
  Define.l xSystemp
  #MemSize = 4096
CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
  #MemSizeAlloc = #MemSize * 2
  CompilerElse
  #MemSizeAlloc = #MemSize
CompilerEndIf
  Define.l *Mem
  Define *GUID

  If Not OutPutListCount = #OutPutListMax
    If CallFunction(*FModExDLL, "FMOD_System_Create", @xSystemp) = 0
      If VersionID = $40305 And DsID = 0
        CallFunction(*FModExDLL, "FMOD_System_SetOutput", xSystemp, 8)
        Else
        CallFunction(*FModExDLL, "FMOD_System_SetOutput", xSystemp, 7 - DsID)
        EndIf
      Sleep_(1)
      If CallFunction(*FModExDLL, "FMOD_System_GetNumDrivers", xSystemp, @xLong2) = 0
        *Mem = AllocateMemory(#MemSizeAlloc)
        If *Mem
          *GUID = AllocateMemory(200)
          For xLong1 = 0 To xLong2 - 1
            ;xLong3 = #MemSize
            ;OutPutList(OutPutListCount) = String(1024&, vbNullChar)
            If VersionID < $41100
              If CallFunction(*FmodExDLL, "FMOD_System_GetDriverName", xSystemp, xLong1, *Mem, #MemSize) = 0
                CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
                  OutPutList(OutPutListCount) = LocalAllocStringWFromAnsiOrUTF8Mem(*Mem, 0)
                  CompilerElse
                  OutPutList(OutPutListCount) = PeekS(*Mem, #MemSize, #PB_Ascii)
                  CompilerEndIf
                OutPutListSSLID(OutputListCount) = Str(OutPutListCount)
                EndIf
              Else
              Select VersionID
                Case $42239, $42430, $42617, $42807, $42907
;                  If DsID = 1
;                    Goto Win98WOHack
;                    EndIf
;                  If (GetVersion_() & $80000000)
;                    Goto Win98WOHack
;                    EndIf
CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
                Case 0 To $42907 ;6/7
CompilerElse
                Default
CompilerEndIf
Win98WOHack:
                  If CallFunction(*FmodExDLL, "FMOD_System_GetDriverInfo", xSystemp, xLong1, *Mem, #MemSize, *Guid) = 0
                    CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
                      OutPutList(OutPutListCount) = LocalAllocStringWFromAnsiOrUTF8Mem(*Mem, 0)
                      CompilerElse
                      OutPutList(OutPutListCount) = PeekS(*Mem, #MemSize, #PB_Ascii)
                      CompilerEndIf
                    EndIf
CompilerIf #ctSSLayer_CompiledFeature_GetDeviceNameW
                Default
                  If (GetVersion_() & $80000000)
                    Goto Win98WOHack
                    EndIf
                  If CallFunction(*FmodExDLL, "FMOD_System_GetDriverInfoW", xSystemp, xLong1, *Mem, #MemSize, *Guid) = 0
                    OutPutList(OutPutListCount) = LocalAllocStringWFromW(*Mem)
                    EndIf
CompilerEndIf
                EndSelect
              OutPutListSSLID(OutputListCount) = FModGuidToStr(*Guid, OutputListCount)
              EndIf
            OutPutListDev(OutPutListCount) = DsID * $100 + xLong1
            ;OutPutList(OutPutListCount) = Chr(DsID) & Chr(xLong1) & OutPutList(OutPutListCount)
            OutPutListCount + 1
            If OutPutListCount = #OutPutListMax: Break: EndIf
            Next
          If *GUID
            FreeMemory(*GUID)
            EndIf
          FreeMemory(*Mem)
          EndIf
        EndIf
      ;GrabError
      CallFunction(*FmodExDLL, "FMOD_System_Release", xSystemp)
      EndIf
    ;Call GrabError
    EndIf
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_RefreshDeviceList
  ProcedureDLL.l ctSSLayer_RefreshDeviceList(hWnd.l)
CompilerElse
  Procedure.l ctSSLayer_RefreshDeviceList(hWnd.l)
CompilerEndIf
    ClearOutputList()
    EnumerateDrv(0, hWnd)
    If TestForDLL(xSysPath + "dsound.dll", $40003)
      EnumerateDrv(1, hWnd)
      EndIf
    ProcedureReturn OutPutListCount
CompilerIf #ctSSLayer_CompiledFeature_RefreshDeviceList
    EndProcedure
CompilerElse
    EndProcedure
  CompilerEndIf

; Anytime, Position = either time Or pattern And matching size
ProcedureDLL.l ctSSLayer_SeekSong(*SongPointer.tSong, SeekPosition.l)
  Define.l xLong1

CompilerIf #ctSSLayer_CompiledFeature_InetStreams
  If Not *SongPointer\Mode = #ctSSLayer_MusicTypeInetStream
CompilerEndIf
    If SeekPosition > -1 And *SongPointer\Size > 0 And SeekPosition < *SongPointer\Size
      FMOD_Channel_SetVolume(*SongPointer\ExtraA, 0)
      CallFunctionFast(*FMOD_Channel_GetPosition, *SongPointer\ExtraA, @xLong1, 1)
      If xLong1 = *SongPointer\Size
        ;PrintN("Special: Stop")
        CallFunctionFast(*FMOD_Channel_Stop, *SongPointer\ExtraA)
        ;PrintN("Special: Release")
        ;CallFunctionFast(*FMOD_Sound_Release, *SongPointer\Pointer)
  CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
          If *SongPointer\mode = #ctSSLayer_MusicTypeCDAudio
            CallFunctionFast(*FMOD_System_PlaySound, xSystem, -1, *SongPointer\Subsound, 1, @*SongPointer\ExtraA)
          ;  IsCDing = 1
            Else
  CompilerEndIf
            ;PrintN("Special: PlaySound")
            CallFunctionFast(*FMOD_System_PlaySound, xSystem, -1, *SongPointer\Pointer, 1, @*SongPointer\ExtraA)
  CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
            EndIf
  CompilerEndIf
        If *SongPointer\ExtraA
          CallFunctionFast(*FMOD_Channel_SetPosition, *SongPointer\ExtraA, SeekPosition, 1)
          FMOD_Channel_SetVolume(*SongPointer\ExtraA, *SongPointer\CurVolume / 255)
          If *SongPointer\IsPaused = 0
            CallFunctionFast(*FMOD_Channel_SetPaused, *SongPointer\ExtraA, 0)
            EndIf
          ProcedureReturn 1
          EndIf
        Else
        CallFunctionFast(*FMOD_Channel_SetPosition, *SongPointer\ExtraA, SeekPosition, 1)
        FMOD_Channel_SetVolume(*SongPointer\ExtraA, *SongPointer\CurVolume / 255)
        EndIf
      ProcedureReturn 1
      EndIf
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
    EndIf
CompilerEndIf
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_EndCallBack
  ProcedureDLL.l ctSSLayer_SetEndCallback(HWnd.l, Message.l, wParam.l)
;    xCallHWnd = HWnd
;    xCallMsg = Message
;    xCallParam = wParam
    EndProcedure
  CompilerEndIf

ProcedureDLL ctSSLayer_SetPause(*SongPointer.tSong, Pause.l)
  If Not (*SongPointer\IsPaused = Pause)
    *SongPointer\IsPaused = Pause
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
    If *SongPointer\Mode = #ctSSLayer_MusicTypeInetStream
;PrintN("SetPause")
      If Pause
        FMOD_Channel_SetVolume(*SongPointer\ExtraA, 0)
        Else
        FMOD_Channel_SetVolume(*SongPointer\ExtraA, *SongPointer\CurVolume / 255)
        EndIf
;PrintN("  done")
      Else
CompilerEndIf
      If Pause
        FMOD_Channel_SetVolume(*SongPointer\ExtraA, 0)
        CallFunctionFast(*FMOD_Channel_SetPaused, *SongPointer\ExtraA, Pause)
        Else
        CallFunctionFast(*FMOD_Channel_SetPaused, *SongPointer\ExtraA, Pause)
        FMOD_Channel_SetVolume(*SongPointer\ExtraA, *SongPointer\CurVolume / 255)
        EndIf
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
      EndIf
CompilerEndIf
    EndIf
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_Spectrum
  ProcedureDLL ctSSLayer_SetSpectrum(Status.l)
    EndProcedure
  CompilerEndIf

ProcedureDLL ctSSLayer_SetVolume(*SongPointer.tSong, Volume.l)
  *SongPointer\CurVolume = Volume

CompilerIf #ctSSLayer_CompiledFeature_InetStreams
  If *SongPointer\Mode = #ctSSLayer_MusicTypeInetStream
    If *SongPointer\IsPaused = 0
      FMOD_Channel_SetVolume(*SongPointer\ExtraA, Volume / 255)
      EndIf
    Else
CompilerEndIf
    FMOD_Channel_SetVolume(*SongPointer\ExtraA, Volume / 255)
CompilerIf #ctSSLayer_CompiledFeature_InetStreams
    EndIf
CompilerEndIf
  EndProcedure

; Beware! Can be 0 ... esp. after you round it!
ProcedureDLL.l ctSSLayer_SongLength(*SongPointer.tSong)
  ProcedureReturn *SongPointer\Size
  EndProcedure

ProcedureDLL ctSSLayer_UnloadSong(*SongPointer.tSong) ;, RestoreVolume.l)
;Debug "Unload1"
  FMOD_Channel_SetVolume(*SongPointer\ExtraA, 0)
;Debug "Unload2"
  CallFunctionFast(*FMOD_Channel_Stop, *SongPointer\ExtraA)
;Debug "Unload3"
  CallFunctionFast(*FMOD_Sound_Release, *SongPointer\Pointer)
;Debug "Unload4"
  CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
    ;If *SongPointer\Mode = #ctSSLayer_MusicTypeCDAudio
    ;  IsCDing = 0
    ;  EndIf
    CompilerEndIf
;Debug "Unload5"
  FreeMemory(*SongPointer)
  EndProcedure

CompilerIf #ctSSLayer_CompiledFeature_CD_AndOr_CDDA
  ProcedureDLL ctSSLayer_UpdateCDDA(UseCDDA.l, UseJitterCorrection.l)
    ;xUseCDDA = (UseCDDA And ctSSLayer_CanUseItem(0))
    xUseJitter = UseJitterCorrection And ctSSLayer_CanUseItem(1)
    EndProcedure
  CompilerEndIf

CompilerIf #ctSSLayer_CompiledFeature_MusicPatternBased = 2
  ProcedureDLL ctSSLayer_UpdateLooping(*SongPointer, LoopSong.l)
    EndProcedure
 CompilerEndIf
  
CompilerIf #ctSSLayer_CompiledFeature_MusicPatternBased
  ProcedureDLL ctSSLayer_UpdatePanning(*SongPointer, Panning.l)
    EndProcedure
 CompilerEndIf

ProcedureDLL.l ctSSLayer_Version(OptionalLayerVersionID.l)
  Select OptionalLayerVersionID
    Case 0, $10000682, $10000690, $10001010
      ProcedureReturn $10001010
    EndSelect
  EndProcedure






CompilerIf #TestIT = 1 And #Console
AttachProcess(0)
If ctSSLayer_Version($10000682)
  Debug "1"
  If ctsslayer_preinit(0)
    ;Debug ctsslayer_canuseitem($20)
    ;Debug ctsslayer_canuseitem($21)
    Debug PeekS(ctsslayer_getinfostringa($1), -1, #PB_Ascii) + " " + PeekS(ctsslayer_getinfostringa($30), -1, #PB_Ascii)
    ;Debug VersionID
    If ctsslayer_inita(0, 0, 0, 0)
      Debug "inited"
      ;Define.l xLong1 = ctsslayer_loadsonga(#ctSSLayer_MusicTypeCDAudio, @"r:\track01.cda", 1)
      Define.l xLong1 = ctsslayer_loadsonga(#ctSSLayer_MusicTypeInetStream, @"http://91.121.13.103:8080", 3)
      If xLong1
        Repeat
          Debug "loadok, blockwaiting"
          Delay(200)
          Select ctSSLayer_LoadSongFinished(xLong1)
            Case 0: Debug "  still Waiting"
            Case 1: Debug "  ok"
              Break
            Case -1: : Debug "  failed"
              Goto NoPlay
            EndSelect
          ForEver
        Debug ctsslayer_loadsongplay(xLong1, 255, 0, 80, 0)
        ;Repeat
        ;  Delay(100)
        ;  ForEver
Debug "f1"
        Delay(2000)
;        ctSSLayer_SeekSong(xLong1, 50)
Debug "f2"
        Delay(5000)
Debug "f3"
NoPlay:
        ctsslayer_unloadsong(xLong1)
        EndIf
Debug "f4"
Debug ctsslayer_CanUseItem($11)
      ctsslayer_close(#False)
Debug "f5"
      EndIf
    EndIf
  EndIf

DetachProcess(0)
CompilerEndIf

;Debug "f6"

CompilerIf #TestIT = 2 And #Console
  AttachProcess(0)
  Define.l xLong1 = ctsslayer_preinit(0)
  If xLong1
    Debug PeekS(ctsslayer_getinfostringa($1), -1, #PB_Ascii) + " " + PeekS(ctsslayer_getinfostringa($30), -1, #PB_Ascii)
    For xLong1 = 0 To xLong1 - 1
      PrintN(PeekS(ctSSLayer_GetDeviceNameA(xLong1, 0), -1, #PB_Ascii) + "#")
      PrintN("  " + PeekS(ctSSLayer_GetDeviceSSLIDA(xLong1), -1, #PB_Ascii) + "#")
      PrintN("    " + PeekS(ctSSLayer_GetDeviceNameW(xLong1, 0), -1, #PB_Unicode) + "#")
      PrintN("    " + PeekS(ctSSLayer_GetDeviceSSLIDW(xLong1), -1, #PB_Unicode) + "#")
      Next
    EndIf
  CompilerEndIf



CompilerIf #TestIT = 3 And #Console
  Debug "1"
  AttachProcess(0)
  If ctSSLayer_Version(0)
  Debug "2"
    Define.l xLong2 = ctsslayer_preinit(0)
    If xLong2
  Debug "3"
      Debug PeekS(ctsslayer_getinfostringa($1), -1, #PB_Ascii) + " " + PeekS(ctsslayer_getinfostringa($30), -1, #PB_Ascii)
      Define.l xLong1
      For xLong1 = 0 To xLong2 - 1
        PrintN(PeekS(ctSSLayer_GetDeviceNamea(xLong1, 0), -1, #PB_Ascii) + "#")
        PrintN("  " + PeekS(ctSSLayer_GetDeviceSSLIDA(xLong1), -1, #PB_Ascii) + "#")
        PrintN("    " + Str(ctSSLayer_GetDeviceFromSSLIDA(ctSSLayer_GetDeviceSSLIDA(xLong1))))
        PrintN(PeekS(ctSSLayer_GetDeviceNameW(xLong1, 0), -1, #PB_Unicode) + "#")
        PrintN("  " + PeekS(ctSSLayer_GetDeviceSSLIDW(xLong1), -1, #PB_Unicode) + "#")
        PrintN("    " + Str(ctSSLayer_GetDeviceFromSSLIDW(ctSSLayer_GetDeviceSSLIDW(xLong1))))
        Next
  
      If ctsslayer_init(0, 0, 0, 0)
        Debug "inited"
        ;Define.l xLong1 = ctsslayer_loadsonga(#ctSSLayer_MusicTypeCDAudio, @"r:\track01.cda", 1)
        ;Define.l xLong1 = ctsslayer_loadsonga(#ctSSLayer_MusicTypeInetStream, @"http://91.121.13.103:8080", 3)
        Define.l xLong1 = ctsslayer_loadsongw(#ctSSLayer_MusicTypeStream, @"D:\DEV\Unicode\badjaptags.MP3", 3)
        If xLong1
          Repeat
            Debug "loadok, blockwaiting"
            Delay(200)
            Select ctSSLayer_LoadSongFinished(xLong1)
              Case 0: Debug "  still Waiting"
              Case 1: Debug "  ok"
                Break
              Case -1: : Debug "  failed"
                Goto NoPlay2
              EndSelect
            ForEver
          Define *Mem = AllocateMemory(1000)
          Debug ctSSLayer_ReadSongInfow(xLong1, *Mem)
          Debug ctsslayer_loadsongplay(xLong1, 255, 0, 80, 0)
          ;Repeat
          ;  Delay(100)
          ;  ForEver
NoPlay2:
          ctsslayer_unloadsong(xLong1)
          EndIf
  Debug "f4"
  Debug ctsslayer_CanUseItem($11)
        ctsslayer_close(#False)
  Debug "f5"
        EndIf
      EndIf
    EndIf

Sleep_(5000)  
  DetachProcess(0)
  CompilerEndIf

CompilerIf #TestIT = 4 And #Console
  Debug "1"
  AttachProcess(0)
  If ctSSLayer_Version($10000682)
    Define.l xLong2 = ctsslayer_preinit(0)
    PrintN(Str((VersionID & $ffff0000) >> 16) + "." + Right("0" + Hex((VersionID & $ff00) >> 8), 2) + "." + Right("0" + Hex(VersionID & $FF), 2))
    PrintN(Hex(VersionID))
    If xLong2
      Debug PeekS(ctsslayer_getinfostringa($1), -1, #PB_Ascii) + " " + PeekS(ctsslayer_getinfostringa($30), -1, #PB_Ascii)
      Define.l xLong1
      For xLong1 = 0 To xLong2 - 1
        PrintN("W->A: " + PeekS(ctSSLayer_GetDeviceNameW(xLong1, 0), -1, #PB_Unicode) + "#")
        PrintN("   A: " + PeekS(ctSSLayer_GetDeviceNameA(xLong1, 0), -1, #PB_Ascii) + "#")
        PrintN("Guid: " + PeekS(ctSSLayer_GetDeviceSSLIDA(xLong1), -1, #PB_Ascii) + "#")
        Next
      EndIf
    EndIf
;Sleep_(10000)  
  DetachProcess(0)
  CompilerEndIf


CompilerIf #TestIT = 5 And #Console
  AttachProcess(0)
  Debug "a"
If ctSSLayer_Version($10000690)
  Debug "1"
  If ctsslayer_preinit(0)
    ;Debug ctsslayer_canuseitem($20)
    ;Debug ctsslayer_canuseitem($21)
    Debug PeekS(ctsslayer_getinfostringa($1)) + " " + PeekS(ctsslayer_getinfostringa($30))
    ;Debug VersionID
    If ctsslayer_init(0, 0, 0, 0)
      Debug "inited"
      ;Define.l xLong1 = ctsslayer_loadsonga(#ctSSLayer_MusicTypeCDAudio, @"r:\track01.cda", 1)
      Define.l xLong1 = ctsslayer_loadsonga(#ctSSLayer_MusicTypeCDAudio, @"m:\track01.cda", 1)
      If xLong1
        Repeat
          Debug "loadok, blockwaiting"
          Delay(200)
          Select ctSSLayer_LoadSongFinished(xLong1)
            Case 0: Debug "  still Waiting"
            Case 1: Debug "  ok"
              Break
            Case -1: : Debug "  failed"
              Goto NoPlay
            EndSelect
          ForEver
        Debug ctsslayer_loadsongplay(xLong1, 255, 0, 80, 0)
        ;Repeat
        ;  Delay(100)
        ;  ForEver
Debug "f1"
        Delay(2000)
;        ctSSLayer_SeekSong(xLong1, 50)
Debug "f2"
        Delay(5000)
Debug "f3"
NoPlay:
        ctsslayer_unloadsong(xLong1)
        EndIf
Debug "f4"
Debug ctsslayer_CanUseItem($11)
      ctsslayer_close(#False)
Debug "f5"
      EndIf
    EndIf
  EndIf

DetachProcess(0)
CompilerEndIf

; IDE Options = PureBasic 4.51 (Windows - x86)
; ExecutableFormat = Shared Dll
; CursorPosition = 660
; FirstLine = 646
; Folding = ----------
; Executable = ..\DebugAPI\ctsslay.exe
; CompileSourceDirectory
; Debugger = Standalone
; IncludeVersionInfo
; VersionField0 = 1,0,0,5600
; VersionField1 = 1,0,0,5600
; VersionField2 = ctuser
; VersionField3 = ctSSLayer
; VersionField4 = 1.00 beta 56
; VersionField5 = 1.00 beta 56 (2006.09.23)
; VersionField6 = FModEx sound system layer for Calaym
; VersionField7 = ctSSLay
; VersionField8 = ctSSLay.dll
; VersionField9 = Copyright 2002-2006 by Jan Vorel; Published under the GNU General Public License
; VersionField16 = VFT_DLL
; VersionField18 = Comments
; VersionField21 = Published under the GNU General Public License; homepage: http://www.ctuser.net; Uses FModEx soundsystem: http://www.fmod.org; compressed with upx: http://upx.sourceforge.net