196 lines
7.8 KiB
QBasic
196 lines
7.8 KiB
QBasic
|
Attribute VB_Name = "modCustLoop"
|
||
|
'/////////////////////////////////////////////////////////////////////////////////
|
||
|
' modCustLoop.bas - Copyright (c) 2004-2007 (: JOBnik! :) [Arthur Aminov, ISRAEL]
|
||
|
' [http://www.jobnik.org]
|
||
|
' [ jobnik@jobnik.org ]
|
||
|
' Other source: frmCustLoop.frm
|
||
|
'
|
||
|
' BASS custom looping example
|
||
|
' Originally translated from - custloop.c - Example of Ian Luck
|
||
|
'/////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
Option Explicit
|
||
|
|
||
|
Public Const BI_RGB = 0&
|
||
|
Public Const DIB_RGB_COLORS = 0& ' color table in RGBs
|
||
|
|
||
|
Public Type BITMAPINFOHEADER
|
||
|
biSize As Long
|
||
|
biWidth As Long
|
||
|
biHeight As Long
|
||
|
biPlanes As Integer
|
||
|
biBitCount As Integer
|
||
|
biCompression As Long
|
||
|
biSizeImage As Long
|
||
|
biXPelsPerMeter As Long
|
||
|
biYPelsPerMeter As Long
|
||
|
biClrUsed As Long
|
||
|
biClrImportant As Long
|
||
|
End Type
|
||
|
|
||
|
Public Type RGBQUAD
|
||
|
rgbBlue As Byte
|
||
|
rgbGreen As Byte
|
||
|
rgbRed As Byte
|
||
|
rgbReserved As Byte
|
||
|
End Type
|
||
|
|
||
|
Public Type BITMAPINFO
|
||
|
bmiHeader As BITMAPINFOHEADER
|
||
|
bmiColors(255) As RGBQUAD
|
||
|
End Type
|
||
|
|
||
|
Public Declare Function SetDIBitsToDevice Lib "gdi32.dll" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal Scan As Long, ByVal NumScans As Long, Bits As Any, BitsInfo As BITMAPINFO, ByVal wUsage As Long) As Long
|
||
|
|
||
|
Public Const TRANSPARENT = 1
|
||
|
Public Const TA_LEFT = 0
|
||
|
Public Const TA_RIGHT = 2
|
||
|
|
||
|
Public Declare Function SetTextColor Lib "gdi32" (ByVal hdc As Long, ByVal crColor As Long) As Long
|
||
|
Public Declare Function SetBkMode Lib "gdi32" (ByVal hdc As Long, ByVal nBkMode As Long) As Long
|
||
|
Public Declare Function SetTextAlign Lib "gdi32" (ByVal hdc As Long, ByVal wFlags As Long) As Long
|
||
|
Public Declare Function TextOut Lib "gdi32" Alias "TextOutA" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal lpString As String, ByVal nCount As Long) As Long
|
||
|
|
||
|
Public Const WIDTH_ = 600 ' display width
|
||
|
Public Const HEIGHT_ = 201 ' height (odd number for centre line)
|
||
|
Public bpp As Long ' stream bytes per pixel
|
||
|
Public loop_(2) As Long ' loop start & end
|
||
|
Public lsync As Long ' looping sync
|
||
|
Public killscan As Boolean
|
||
|
|
||
|
Public wavebuf() As Byte ' wave buffer
|
||
|
Public chan As Long ' stream/music handle
|
||
|
|
||
|
Public bh As BITMAPINFO ' bitmap header
|
||
|
|
||
|
' display error messages
|
||
|
Public Sub Error_(ByVal es As String)
|
||
|
Call MsgBox(es & vbCrLf & vbCrLf & "error code: " & BASS_ErrorGetCode, vbExclamation, "Error")
|
||
|
End Sub
|
||
|
|
||
|
Sub LoopSyncProc(ByVal handle As Long, ByVal channel As Long, ByVal data As Long, ByVal user As Long)
|
||
|
If (BASS_ChannelSetPosition(channel, loop_(0), BASS_POS_BYTE) = 0) Then ' try seeking to loop start
|
||
|
Call BASS_ChannelSetPosition(channel, 0, BASS_POS_BYTE) ' failed, go to start of file instead
|
||
|
End If
|
||
|
End Sub
|
||
|
|
||
|
Sub SetLoopStart(ByVal pos As Long)
|
||
|
loop_(0) = pos
|
||
|
End Sub
|
||
|
|
||
|
Sub SetLoopEnd(ByVal pos As Long)
|
||
|
loop_(1) = pos
|
||
|
Call BASS_ChannelRemoveSync(chan, lsync) ' remove old sync
|
||
|
lsync = BASS_ChannelSetSync(chan, BASS_SYNC_POS Or BASS_SYNC_MIXTIME, loop_(1), AddressOf LoopSyncProc, 0) ' set new sync
|
||
|
End Sub
|
||
|
|
||
|
' scan the peaks
|
||
|
Sub ScanPeaks(ByVal decoder As Long)
|
||
|
ReDim wavebuf(-120600 To 120600) As Byte ' set 'n clear the buffer (600 x 201 = 120600)
|
||
|
Dim cpos As Long, peak(2) As Long
|
||
|
|
||
|
Do While (Not killscan)
|
||
|
Dim Level As Long, pos As Long
|
||
|
Level = BASS_ChannelGetLevel(decoder) ' scan peaks
|
||
|
pos = BASS_ChannelGetPosition(decoder, BASS_POS_BYTE) / bpp
|
||
|
If (peak(0) < LoWord(Level)) Then peak(0) = LoWord(Level) ' set left peak
|
||
|
If (peak(1) < HiWord(Level)) Then peak(1) = HiWord(Level) ' set right peak
|
||
|
If (BASS_ChannelIsActive(decoder) = 0) Then
|
||
|
pos = -1 ' reached the end
|
||
|
Else
|
||
|
pos = BASS_ChannelGetPosition(decoder, BASS_POS_BYTE) / bpp
|
||
|
End If
|
||
|
If (pos > cpos) Then
|
||
|
Dim a As Long
|
||
|
For a = 0 To (peak(0) * (HEIGHT_ / 2) / 32768) - 1
|
||
|
' draw left peak
|
||
|
wavebuf(IIf((HEIGHT_ / 2 - 1 - a) * WIDTH_ + cpos > 120600, 120600, (HEIGHT_ / 2 - 1 - a) * WIDTH_ + cpos)) = 1 + a
|
||
|
Next a
|
||
|
For a = 0 To (peak(1) * (HEIGHT_ / 2) / 32768) - 1
|
||
|
' draw right peak
|
||
|
wavebuf(IIf((HEIGHT_ / 2 + 1 + a) * WIDTH_ + cpos > 120600, 120600, (HEIGHT_ / 2 + 1 + a) * WIDTH_ + cpos)) = 1 + a
|
||
|
Next a
|
||
|
If (pos >= WIDTH_) Then Exit Do ' gone off end of display
|
||
|
cpos = pos
|
||
|
peak(0) = 0
|
||
|
peak(1) = 0
|
||
|
End If
|
||
|
DoEvents
|
||
|
Loop
|
||
|
Call BASS_StreamFree(decoder) ' free the decoder
|
||
|
End Sub
|
||
|
|
||
|
' select a file to play, and start scanning it
|
||
|
Function PlayFile() As Boolean
|
||
|
On Local Error Resume Next ' if Cancel pressed...
|
||
|
|
||
|
With frmCustLoop.cmdCustLoop
|
||
|
.CancelError = True
|
||
|
.flags = cdlOFNExplorer Or cdlOFNFileMustExist Or cdlOFNHideReadOnly
|
||
|
.DialogTitle = "Select a file to play"
|
||
|
.Filter = "Playable files|*.mp3;*.mp2;*.mp1;*.ogg;*.wav;*.aif;*.mo3;*.it;*.xm;*.s3m;*.mtm;*.mod;*.umx|All files|*.*"
|
||
|
.ShowOpen
|
||
|
|
||
|
' if cancel was pressed, exit the procedure
|
||
|
If Err.Number = 32755 Then Exit Function
|
||
|
|
||
|
chan = BASS_StreamCreateFile(BASSFALSE, StrPtr(.filename), 0, 0, 0)
|
||
|
If (chan = 0) Then chan = BASS_MusicLoad(BASSFALSE, StrPtr(.filename), 0, 0, BASS_MUSIC_RAMPS Or BASS_MUSIC_POSRESET Or BASS_MUSIC_PRESCAN, 1)
|
||
|
|
||
|
If (chan = 0) Then
|
||
|
Call Error_("Can't play file")
|
||
|
PlayFile = False ' Can't load the file
|
||
|
Exit Function
|
||
|
End If
|
||
|
|
||
|
frmCustLoop.Show ' show form
|
||
|
|
||
|
With bh.bmiHeader
|
||
|
.biSize = Len(bh.bmiHeader)
|
||
|
.biWidth = WIDTH_
|
||
|
.biHeight = -HEIGHT_
|
||
|
.biPlanes = 1
|
||
|
.biBitCount = 8
|
||
|
.biClrUsed = HEIGHT_ / 2 + 1
|
||
|
.biClrImportant = HEIGHT_ / 2 + 1
|
||
|
End With
|
||
|
|
||
|
' setup palette
|
||
|
Dim a As Byte
|
||
|
|
||
|
For a = 1 To HEIGHT_ / 2
|
||
|
bh.bmiColors(a).rgbRed = (255 * a) / (HEIGHT_ / 2)
|
||
|
bh.bmiColors(a).rgbGreen = 255 - bh.bmiColors(a).rgbRed
|
||
|
Next a
|
||
|
|
||
|
bpp = BASS_ChannelGetLength(chan, BASS_POS_BYTE) / WIDTH_ ' bytes per pixel
|
||
|
If (bpp < BASS_ChannelSeconds2Bytes(chan, 0.02)) Then ' minimum 20ms per pixel (BASS_ChannelGetLevel scans 20ms)
|
||
|
bpp = BASS_ChannelSeconds2Bytes(chan, 0.02)
|
||
|
End If
|
||
|
lsync = BASS_ChannelSetSync(chan, BASS_SYNC_END Or BASS_SYNC_MIXTIME, 0, AddressOf LoopSyncProc, 0) ' set sync to loop at end
|
||
|
Call BASS_ChannelPlay(chan, BASSFALSE) ' start playing
|
||
|
frmCustLoop.tmrCustLoop.Enabled = True ' timer's interval is 100ms (10Hz)
|
||
|
|
||
|
Dim chan2 As Long
|
||
|
chan2 = BASS_StreamCreateFile(BASSFALSE, StrPtr(.filename), 0, 0, BASS_STREAM_DECODE)
|
||
|
If (chan2 = 0) Then chan2 = BASS_MusicLoad(BASSFALSE, StrPtr(.filename), 0, 0, BASS_MUSIC_DECODE, 1)
|
||
|
Call ScanPeaks(chan2) ' start scanning peaks
|
||
|
End With
|
||
|
PlayFile = True
|
||
|
End Function
|
||
|
|
||
|
Sub DrawTimeLine(ByVal dc As Long, ByVal pos As Long, ByVal col As Long, ByVal Y As Long)
|
||
|
Dim wpos As Long
|
||
|
wpos = pos / bpp
|
||
|
Dim time As Long
|
||
|
time = BASS_ChannelBytes2Seconds(chan, pos)
|
||
|
Dim text As String
|
||
|
text = time \ 60 & ":" & Format(time Mod 60, "00")
|
||
|
frmCustLoop.CurrentX = wpos
|
||
|
frmCustLoop.Line (wpos, 0)-(wpos, HEIGHT_ - 1), col
|
||
|
Call SetTextColor(dc, col)
|
||
|
Call SetBkMode(dc, TRANSPARENT)
|
||
|
Call SetTextAlign(dc, IIf(wpos >= WIDTH_ / 2, TA_RIGHT, TA_LEFT))
|
||
|
Call TextOut(dc, wpos, Y, text, Len(text))
|
||
|
End Sub
|