BassBox has been updated to
version 1.10A new
OSCILLOSCOPE control has been added to monitor the playing audio in real time.
In order to use this feature you must set your
multiplex audio card in "
Stereo" or "
What you hear" mode.
Note: So far i didn't checked it with XP.
The zip is attached to the first post of this thread, and the screen shot has been updated.
For those who like to know what is going on behind the hood, here is the code of the "Monitoring section":
'*****************************************************************************************************
'// Monitoring audio section
'*****************************************************************************************************
%MAXPNAMELEN = 32 ' max product name length (including NULL)
%CALLBACK_WINDOW = &H10000 ' dwCallback is a HWND
%HEAP_ZERO_MEMORY = &H00000008
%MM_WIM_DATA = &H3C0
%WM_WAVE_INIT = %WM_USER + 1
TYPE WAVEHDR
lpData AS ASCIIZ PTR
dwBufferLength AS DWORD
dwBytesRecorded AS DWORD
dwUser AS DWORD
dwFlags AS DWORD
dwLoops AS DWORD
lpNext AS LONG
Reserved AS DWORD
END TYPE
TYPE WAVEINCAPS
wMid AS WORD
wPid AS WORD
vDriverVersion AS DWORD
szPname AS ASCIIZ * %MAXPNAMELEN
dwFormats AS DWORD
wChannels AS WORD
wReserved1 AS WORD
END TYPE
TYPE WAVEFORMATEX
wFormatTag AS WORD
nChannels AS WORD
nSamplesPerSec AS DWORD
nAvgBytesPerSec AS DWORD
nBlockAlign AS WORD
wBitsPerSample AS WORD
cbSize AS WORD
END TYPE
DECLARE FUNCTION waveInGetNumDevs LIB "WINMM.DLL" ALIAS "waveInGetNumDevs" () AS LONG
DECLARE FUNCTION waveInGetDevCaps LIB "WINMM.DLL" ALIAS "waveInGetDevCapsA" (BYVAL uDeviceID AS DWORD, lpCaps AS WAVEINCAPS, BYVAL uSize AS DWORD) AS LONG
DECLARE FUNCTION GetProcessHeap LIB "KERNEL32.DLL" ALIAS "GetProcessHeap" () AS LONG
DECLARE FUNCTION HeapAlloc LIB "KERNEL32.DLL" ALIAS "HeapAlloc" (BYVAL hHeap AS DWORD, BYVAL dwFlags AS DWORD, BYVAL dwBytes AS DWORD) AS DWORD
DECLARE FUNCTION HeapFree LIB "KERNEL32.DLL" ALIAS "HeapFree" (BYVAL hHeap AS DWORD, BYVAL dwFlags AS DWORD, BYVAL lpMem AS DWORD) AS LONG
DECLARE FUNCTION waveInStop LIB "WINMM.DLL" ALIAS "waveInStop" (BYVAL hWaveIn AS DWORD) AS LONG
DECLARE FUNCTION waveInReset LIB "WINMM.DLL" ALIAS "waveInReset" (BYVAL hWaveIn AS DWORD) AS LONG
DECLARE FUNCTION waveInUnprepareHeader LIB "WINMM.DLL" ALIAS "waveInUnprepareHeader" (BYVAL hWaveIn AS DWORD, lpWaveInHdr AS WAVEHDR, BYVAL uSize AS DWORD) AS LONG
DECLARE FUNCTION waveInClose LIB "WINMM.DLL" ALIAS "waveInClose" (BYVAL hWaveIn AS DWORD) AS LONG
DECLARE FUNCTION waveInOpen LIB "WINMM.DLL" ALIAS "waveInOpen" (lphWaveIn AS DWORD, BYVAL uDeviceID AS DWORD, lpFormat AS WAVEFORMATEX, BYVAL dwCallback AS DWORD, BYVAL dwInstance AS DWORD, BYVAL dwFlags AS DWORD) AS LONG
DECLARE FUNCTION waveInPrepareHeader LIB "WINMM.DLL" ALIAS "waveInPrepareHeader" (BYVAL hWaveIn AS DWORD, lpWaveInHdr AS WAVEHDR, BYVAL uSize AS DWORD) AS LONG
DECLARE FUNCTION waveInAddBuffer LIB "WINMM.DLL" ALIAS "waveInAddBuffer" (BYVAL hWaveIn AS DWORD, lpWaveInHdr AS WAVEHDR, BYVAL uSize AS DWORD) AS LONG
DECLARE FUNCTION waveInStart LIB "WINMM.DLL" ALIAS "waveInStart" (BYVAL hWaveIn AS DWORD) AS LONG
%NUMBUF = 16
FUNCTION zPaintCapture(BYVAL hWnd AS LONG, BYVAL hDC AS LONG, BYVAL Action AS LONG) AS LONG
'LOCAL rc AS RECT
LOCAL hDCTemp, hPaintBitmap AS LONG
'CALL GetClientRect(hWnd, rc)
IF Action THEN
' Create OFF screen bitmap to avoid flickering and allow smooth display
' ---------------------------------------------------------------------
IF zGetPaintBitmap(hWnd) THEN
FUNCTION = zGetPaintDC(hWnd)
ELSE
hDCTemp = CreateCompatibleDC(hDC)
hPaintBitmap = zCreateDIBSection(hDC, 128, 63, 32)
CALL SelectObject(hDCTemp, hPaintBitmap)
CALL zSetProperty(hWnd, %FORM_PaintDC, hDCTemp)
CALL zSetProperty(hWnd, %FORM_PaintBitmap, hPaintBitmap)
FUNCTION = hDCTemp
END IF
ELSE
' End of OFF screen drawing, fast BitBlt to the target display Window
' -------------------------------------------------------------------
' Draw final result to the target window
CALL BitBlt(hDC, 0, 0, 128, 63, zGetPaintDC(hWnd), 0, 0, %SRCCOPY)
END IF
END FUNCTION
SUB DrawWave(BYVAL hWnd AS LONG, BYVAL pInteger AS INTEGER PTR)
LOCAL rc AS RECT
LOCAL x, y, x1, y1, C, nVol, hDC, graphics AS LONG
LOCAL hDCView, UseColor, YFactor, n64, n127, nPlaying AS LONG
LOCAL UseGain AS SINGLE
STATIC nTick, nDelay, xPos, WasVol AS LONG, WasGain AS SINGLE
INCR nTick: IF nTick > 1 THEN nTick = 0: EXIT SUB '// To reduce the CPU usage.
CALL GetClientRect(hWnd, rc)
YFactor = 1074 ' &H0FFFF \ (rc.nBottom - 2)
hDCView = GetDC(hWnd)
hDC = zPaintCapture(hWnd, hDCView, 1)
CALL GdipCreateFromHDC(hDC, graphics)
n64 = rc.nBottom \ 2: n127 = rc.nBottom - 1
CALL zsGdipFillRect(graphics, 0, 0, rc.nRight, rc.nBottom, zColorARGB(255, 0) )
CALL zsGdipLine(graphics, 0, n64 + 1, rc.nRight, n64 + 1, zColorARGB(255, RGB(48,48,32)) )
nPlaying = 0
IF IsWindowVisible(GetDlgItem(zMainWindow(0), %ID_BTN_Play)) = 0 THEN
nVol = zGetTrackValue(zGetMainItem(%ID_TRACK_VOL))
IF WasGain = 0 THEN WasVol = -1
IF nVol <> WasVol THEN '// This to save a few CPU cycles and avoid SQR computation
UseGain = 1 '// when it is not required.
IF nVol < 50 THEN
UseGain = MAX(9 * (SQR((100 - nVol) / 50) * 1.8 - 2), 1.0)
ELSEIF nVol > 60 THEN
UseGain = MAX(SQR((110 - nVol) / 50), 0.1)
END IF
WasGain = UseGain
ELSE
UseGain = WasGain
END IF
WasVol = nVol
FOR x = 0 TO 127' 0 TO 255
y = (rc.nBottom - (((@pInteger * UseGain) + &H7FFF) \ YFactor))
IF nPlaying = 0 THEN
IF ABS(y) > 3 THEN nPlaying = -1
END IF
IF x THEN
C = 32 - CLNG(y * 0.5) ' (32 / rc.nBottom))
CALL zsGdipLine(graphics, x1, y1, x, y, zColorARGB(255, LevelColr(C)) )
END IF
x1 = x: y1 = y
FOR C = 1 TO 2: INCR pInteger: NEXT
NEXT
END IF
IF nPlaying THEN UseColor = &H303030 ELSE UseColor = &H600000&
CALL zsGdipLine(graphics, xPos, 0, xPos, n64, zColorARGB(255, UseColor) )
CALL zsGdipLine(graphics, xPos, n64 + 2, xPos, rc.nBottom, zColorARGB(255, UseColor) )
IF nDelay = 0 THEN INCR xPos: IF xPos > rc.nRight THEN xPos = 0
INCR nDelay: IF nDelay = 4 THEN nDelay = 0
CALL zPaintCapture(hWnd, hDCView, 0)
CALL GdipDeleteGraphics(graphics)
CALL ReleaseDC(hWnd, hDCView)
END SUB
FUNCTION AudioProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
LOCAL K, hResource AS LONG, pWaveHdr AS WAVEHDR PTR
STATIC gWaveBuf() AS WAVEHDR, gHwi AS LONG
SELECT CASE LONG wMsg
CASE %WM_WAVE_INIT
LOCAL wic AS WAVEINCAPS
LOCAL wfx AS WAVEFORMATEX
LOCAL nRet, nDev AS LONG
nDev = WaveInGetNumDevs()
nRet = 22 ' "No suitable device found"
FOR K = nDev - 1 TO 0 STEP -1
IF WaveInGetDevCaps(K, wic, LEN(WAVEINCAPS)) = 0 THEN
IF INSTR(LCASE$(wic.szPname), "micro") = 0 THEN ' We don't want the microphone
wfx.nChannels = 2
wfx.nSamplesPerSec = 44100
wfx.wFormatTag = 1 ' %WAVE_FORMAT_PCM
wfx.wBitsPerSample = 16
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample \ 8
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign
wfx.cbSize = 0
nRet = waveInOpen(gHwi, K, wfx, hWnd, hWnd, %CALLBACK_WINDOW)
IF nRet = 0 THEN EXIT FOR
ELSE
nRet = 22 ' "No suitable device found"
END IF
END IF
NEXT
IF nRet = 0 THEN
REDIM gWaveBuf(%NUMBUF)
FOR K = 1 TO %NUMBUF
gWaveBuf(K).lpData = HeapAlloc(GetProcessHeap(), %HEAP_ZERO_MEMORY, 1024)
IF gWaveBuf(K).lpData THEN
gWaveBuf(K).dwBufferLength = 512 ' 1024
ELSE
nRet = 1003 ' "Unable to complete this function"
EXIT FOR
END IF
NEXT
IF nRet = 0 THEN
FOR K = 1 TO %NUMBUF
nRet = waveInPrepareHeader(gHwi, gWaveBuf(K), LEN(WAVEHDR))
IF nRet THEN
EXIT FOR
ELSE
nRet = WaveInAddBuffer(gHwi, gWaveBuf(K), LEN(WAVEHDR))
IF nRet THEN EXIT FOR
END IF
NEXT
IF nRet = 0 THEN
nRet = WaveInStart(gHwi)
END IF
END IF
END IF
IF nRet THEN gHwi = 0
FUNCTION = nRet
EXIT FUNCTION
CASE %MM_WIM_DATA
' We have data
pWaveHdr = lParam
CALL DrawWave(hWnd, @pWaveHdr.lpData)
' Put the buffer back to be reused
IF WaveInPrepareHeader(gHwi, BYVAL pWaveHdr, LEN(WAVEHDR)) = 0 THEN
CALL WaveInAddBuffer(gHwi, BYVAL pWaveHdr, LEN(WAVEHDR))
END IF
EXIT FUNCTION
CASE %WM_DESTROY
' Deallocate system resources
' // Clear paint resources
hResource = zGetPaintBitmap(hWnd)
CALL zDeleteObject(hResource): CALL zSetProperty(hWnd, %FORM_PaintBitmap, hResource)
' // Delete DC
hResource = zGetPaintDC(hWnd)
IF hResource THEN CALL DeleteDC(hResource): hResource = 0: CALL zSetProperty(hWnd, %FORM_PaintDC, hResource)
IF gHwi THEN
CALL WaveInStop(gHwi)
CALL WaveInReset(gHwi)
FOR K = 1 TO %NUMBUF
CALL waveinUnprepareHeader(gHwi, gWaveBuf(K), SIZEOF(WAVEHDR))
NEXT
CALL WaveInClose(gHwi)
' DeallocBuffers
FOR K = 1 TO %NUMBUF
IF gWaveBuf(K).lpData THEN
CALL HeapFree(GetProcessHeap(), %HEAP_ZERO_MEMORY, gWaveBuf(K).lpData)
END IF
NEXT
END IF
END SELECT
FUNCTION = DefWindowProc(hWnd, wMsg, wParam, lParam)
END FUNCTION
FUNCTION zOscillo(BYVAL hParent AS LONG, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL W AS LONG, BYVAL H AS LONG, BYVAL ID AS LONG) AS LONG
LOCAL wc AS WndClassEx
LOCAL zClass AS ASCIIZ * 16
LOCAL IsInitialized, hWnd AS LONG
zClass = "ZOSCILLO"
wc.cbSize = SIZEOF(wc)
IsInitialized = GetClassInfoEx(zsInstance, zClass, wc)
IF IsInitialized = 0 THEN
wc.cbSize = SIZEOF(wc)
wc.style = 0
wc.lpfnWndProc = CODEPTR(AudioProc)
wc.cbClsExtra = 0
wc.cbWndExtra = %EXTEND_EXTRA * 4
wc.hInstance = zsInstance
wc.hIcon = %NULL
wc.hCursor = %NULL
wc.hbrBackground = %NULL
wc.lpszMenuName = %NULL
wc.lpszClassName = VARPTR(zClass)
wc.hIconSm = %NULL
IF RegisterClassEx(wc) THEN IsInitialized = -1
END IF
IF IsInitialized THEN
hWnd = CreateWindowEx(0, zClass, "", %WS_CHILD OR %WS_VISIBLE, x, y, W, H, hParent, ID, zsInstance, BYVAL %NULL)
IF hWnd THEN
IF AudioProc(hWnd, %WM_WAVE_INIT, 0, 0) = 0 THEN
FUNCTION = hWnd
ELSE
CALL DestroyWindow(hWnd)
END IF
END IF
END IF
END FUNCTION