The MMIO routines are just used for reading the information and the uncompressed audiodata out of a wave file. If you have other routines (DirectShow, Avisynth, Libavcodec, etc.) for decompressing to obtain uncompressed PCM audiodata from a given file (ac3, mp3, mp4, etc.) you can just replace the MMIO routines with the needed specific ones.
EDIT: Codeupdate. Now Refillingrequest is watched by a PB Windowcallback- makes it independand from user Windowaccess.
Code: Select all
; Mini PB example for audio streaming using MMIO/WINMM routines, 2006 by Inc.
; Based on an MMIO example from "API-Guide" at All-API(dot)net.
; EnableExplicit
#NUM_BUFFERS = 5;8 ; 8 should be enough.
#BUFFER_SECONDS = 0.1;0.5 ; the bigger the buffer, the higher the latency; the lower the buffer the bigger the risk of stuttering.
Structure WAVEFORMATEX Extends WAVEFORMAT
cbSize.w
EndStructure
Global rc.l ; Return code
Global hmmioIn.l ; file handle
Global DataOffset.l ; start of audio data in wave file
Global audioLength.l ; number of bytes in audio data
Global startPos.l ; sample where we started playback from
Global Format.WAVEFORMATEX ; waveformat structure
Global Dim hmem.l(#NUM_BUFFERS-1) ; memory handles
Global Dim pmem.l(#NUM_BUFFERS-1) ; memory pointers
Global Dim hdr.WAVEHDR(#NUM_BUFFERS-1) ; wave headers
Global bufferSize.l ; size of output buffers
Global fPlaying.b ; is file currently playing
Global fFileOpen.b ; is file currently open
Global hWaveOut.l ; waveout handle
Global msg.s=Space(250) ; message buffer
Global hwnd.l ; window handle
Procedure Position()
Protected tm.MMTIME, Position.l
tm\wType = #TIME_BYTES
rc = waveOutGetPosition_(hWaveOut, @tm, SizeOf(MMTIME))
If rc = #MMSYSERR_NOERROR
Position = (startPos + tm\u\cb) / Format\nBlockAlign
Else
Position = (mmioSeek_(hmmioIn, 0, #SEEK_CUR) - DataOffset + bufferSize * #NUM_BUFFERS) / Format\nBlockAlign
EndIf
ProcedureReturn Position
EndProcedure
Procedure ServiceBuffers(*wavhdr.WAVEHDR)
Protected dataRemaining.l, i.l
If fPlaying = #True
Debug "refilling buffers ... at second "+Str(Position()/Format\nSamplesPerSec)
dataRemaining = (DataOffset + audioLength - mmioSeek_(hmmioIn, 0, #SEEK_CUR))
If bufferSize < dataRemaining
rc = mmioRead_(hmmioIn, *wavhdr\lpData, bufferSize)
Else
rc = mmioRead_(hmmioIn, *wavhdr\lpData, dataRemaining)
fPlaying = #False
Debug "... rest remaining data: "+Str(dataRemaining)
EndIf
*wavhdr\dwBufferLength = rc
rc = waveOutWrite_(hWaveOut, *wavhdr, SizeOf(WAVEHDR))
Else
Debug "... finishing buffering ... at second "+Str(Position()/Format\nSamplesPerSec)
For i = 0 To #NUM_BUFFERS-1
waveOutUnprepareHeader_( hWaveOut, @hdr(i), SizeOf(WAVEHDR))
Next
waveOutClose_( hWaveOut)
EndIf
EndProcedure
Procedure CloseTheFile()
Protected i.l
For i = 0 To (#NUM_BUFFERS-1)
GlobalFree_(hmem(i))
;Debug "Memory "+Str(i)+" allocated at "+Str(pmem(i))+ " released"
Next
If fPlaying = #True
For i = 0 To #NUM_BUFFERS-1
waveOutUnprepareHeader_( hWaveOut, @hdr(i), SizeOf(WAVEHDR))
Next
waveOutClose_( hWaveOut)
EndIf
mmioClose_(hmmioIn, 0)
fFileOpen = #False
EndProcedure
Procedure OpenTheFile(soundfile.s, WinNo.l)
Protected total.f, i.l, mmckinfoParentIn.MMCKINFO, mmckinfoSubchunkIn.MMCKINFO, mmioinf.MMIOINFO
hwnd = WindowID(WinNo)
fPlaying = #False
startPos = 0
; checking if still playing
If fPlaying
fPlaying = #False
EndIf
; close previously open file (if any)
If fFileOpen = #True
CloseTheFile()
EndIf
If soundfile = ""
Debug "File not found"
ProcedureReturn #False
EndIf
; Open the input file
hmmioIn = mmioOpen_(@soundfile, @mmioinf, #MMIO_READ)
If hmmioIn = 0
MessageRequester("Error", "Error opening input File, rc = "+PeekS(@mmioinf\wErrorRet))
ProcedureReturn #False
EndIf
; Check if this is a wave file
mmckinfoParentIn\fccType = mmioStringToFOURCC_("WAVE", 0)
rc = mmioDescend_(hmmioIn, @mmckinfoParentIn, 0, #MMIO_FINDRIFF)
If rc <> #MMSYSERR_NOERROR
CloseTheFile()
MessageRequester("Error", "Not a wave File")
ProcedureReturn #False
EndIf
; Get format info
mmckinfoSubchunkIn\ckid = mmioStringToFOURCC_("fmt", 0)
rc = mmioDescend_(hmmioIn, @mmckinfoSubchunkIn, @mmckinfoParentIn, #MMIO_FINDCHUNK)
If rc <> #MMSYSERR_NOERROR
CloseTheFile()
MessageRequester("Error", "Couldn;t get format chunk")
ProcedureReturn #False
EndIf
rc = mmioRead_(hmmioIn, @Format, mmckinfoSubchunkIn\ckSize)
If rc = -1
CloseTheFile()
MessageRequester("Error", "Error reading format")
ProcedureReturn #False
EndIf
Debug " "
Debug "FormatTag: "+Str(Format\wFormatTag)
Debug "Channels: "+Str(Format\nChannels)
Debug "SamplesPerSec: "+Str(Format\nSamplesPerSec)+" Hz"
Debug "AvgBytesPerSec: "+Str(Format\nAvgBytesPerSec)
Debug "BlockAlign: "+Str(Format\nBlockAlign)+" bytes"
Debug "Resolution: "+Str(Format\cbSize)+" bits"
Debug " "
rc = mmioAscend_(hmmioIn, @mmckinfoSubchunkIn, 0)
; Find the data subchunk
mmckinfoSubchunkIn\ckid = mmioStringToFOURCC_("data", 0)
rc = mmioDescend_(hmmioIn, @mmckinfoSubchunkIn, @mmckinfoParentIn, #MMIO_FINDCHUNK)
If rc <> #MMSYSERR_NOERROR
CloseTheFile()
MessageRequester("Error", "Couldn't get data chunk")
ProcedureReturn #False
EndIf
DataOffset = mmioSeek_(hmmioIn, 0, #SEEK_CUR)
; Get the length of the audio
audioLength = mmckinfoSubchunkIn\ckSize
; Allocate audio buffers
bufferSize = Format\nSamplesPerSec * Format\nBlockAlign * Format\nChannels * #BUFFER_SECONDS
bufferSize = bufferSize - (bufferSize % Format\nBlockAlign)
For i = 0 To (#NUM_BUFFERS-1)
GlobalFree_(hmem(i))
hmem(i) = GlobalAlloc_(#GMEM_ZEROINIT|#GMEM_MOVEABLE, bufferSize)
pmem(i) = GlobalLock_(hmem(i))
;Debug Str(bufferSize/1024)+"kb allocated at Memory "+Str(i)+" at adress "+Str(pmem(i))
total+(bufferSize/1024/1024)
Next
Debug StrF(total,3)+" MBs in total for "+Str(#NUM_BUFFERS)+" Buffers used"
Debug " "
fFileOpen = #True
ProcedureReturn #True
EndProcedure
Procedure Play()
Protected i.l
If fPlaying
ProcedureReturn #True
EndIf
rc = waveOutOpen_(@hWaveOut, #WAVE_MAPPER, @Format, hwnd, #Null, #CALLBACK_WINDOW)
If rc <> #MMSYSERR_NOERROR
waveOutGetErrorText_(rc, @msg, Len(msg))
Debug msg
ProcedureReturn #False
EndIf
For i = 0 To #NUM_BUFFERS-1
hdr(i)\lpData = pmem(i)
hdr(i)\dwBufferLength = bufferSize
hdr(i)\dwFlags = 0
hdr(i)\dwLoops = 0
rc = waveOutPrepareHeader_(hWaveOut, @hdr(i), SizeOf(WAVEHDR))
If rc <> #MMSYSERR_NOERROR
waveOutGetErrorText_(rc, @msg, Len(msg))
Debug msg
ProcedureReturn #False
EndIf
Next
fPlaying = #True
startPos = mmioSeek_(hmmioIn, 0, #SEEK_CUR) - DataOffset
; send a MM_WOM_DONE message to the callback so the buffers get filled and playback starts
For i = 0 To #NUM_BUFFERS-1
PostMessage_(hwnd, #MM_WOM_DONE, 0, @hdr(i))
Next
ProcedureReturn #True
EndProcedure
Procedure FileSeekTo(Position.l)
Protected bytepos.l
bytepos = Position * Format\nBlockAlign
If fFileOpen = #False Or bytepos <0>= audioLength
ProcedureReturn #False
EndIf
rc = mmioSeek_(hmmioIn, bytepos + DataOffset, #SEEK_SET)
If rc = #MMSYSERR_NOERROR
startPos = rc
ProcedureReturn #True
EndIf
EndProcedure
Procedure StopPlay()
fPlaying = #False
FileSeekTo(Position())
waveOutReset_( hWaveOut)
EndProcedure
Procedure pause()
If fPlaying
fPlaying = #False
FileSeekTo(Position())
waveOutPause_(hWaveOut)
Else
fPlaying = #True
WaveOutRestart_(hWaveOut)
EndIf
EndProcedure
Procedure.f Length() ; ... in Audioblocks!
ProcedureReturn audioLength / Format\nBlockAlign
EndProcedure
Procedure Playing()
Protected tm.MMTIME
tm\wType = #TIME_BYTES
rc = waveOutGetPosition_(hWaveOut, @tm, SizeOf(MMTIME))
If rc = #MMSYSERR_NOERROR
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure WaveCallback(WindowID, message, wParam, lParam)
Protected result = #PB_ProcessPureBasicEvents
If message = #MM_WOM_DONE ; Buffers need to be refilled?
ServiceBuffers(lParam)
EndIf
ProcedureReturn result
EndProcedure
;DisableExplicit
;------------------------------------- Example --------------------------------
CompilerIf #PB_Compiler_Debugger = #False
MessageRequester("Info","Please run this example using the debugger and see its output while choosing a media file")
End
CompilerEndIf
OpenWindow(0,20,20,400,100,"PB audio streaming example using MMIO/WINMM routines.", #PB_Window_ScreenCentered|#PB_Window_SystemMenu )
SetWindowCallback(@WaveCallback())
File.s = OpenFileRequester("Choose a PCM Wave file","","Wave files (*.wav)|*.wav",0)
If File
If OpenTheFile(File,0)
Debug "Lenght: "+StrF(Length()/Format\nSamplesPerSec,3)+" sec"
Debug""
If Play()
Repeat
event = WaitWindowEvent()
lprm = EventlParam()
wprm = EventwParam()
If event = #WM_KEYDOWN
If wprm = #VK_ESCAPE ; Quit appl.
StopPlay()
CloseTheFile()
End
ElseIf wprm = #VK_LEFT ; Seeking backwards in 5% steps
temp = fPlaying
pause()
FileSeekTo(Position()-Int(Length()/20))
If temp
Play()
EndIf
ElseIf wprm = #VK_RIGHT; Seeking forward in 5% steps
temp = fPlaying
pause()
FileSeekTo(Position()+Int(Length()/20))
If temp
Play()
EndIf
ElseIf wprm = #VK_P
Debug Position()
EndIf
EndIf
Until event = #PB_Event_CloseWindow
StopPlay()
CloseTheFile()
End
Else
CloseTheFile()
Debug "error playing"
EndIf
End
Else
Debug "error initializing"
EndIf
Else
Debug "error opening"
EndIf