Воспроизведение WAV (Linux [32-bit, 64-bit],Windows [32-bit, 64-bit])
Очень часто в своих проектах я нуждался в функциональности, связанной с воспроизведением звука. Конечно же есть различные библиотеки. Однако у чужих библиотек есть свои минусы. Например требование дополнительного лицензирования, установка дополнительного ПО , а часто просто немалое время на изучение интерфейса и даже настройка\правка\перекомпилирование. В большинстве случаев мне хватало проигрывания простых файлов в формате WAV. Если брать систему Windows , то тут не возникало абсолютно никаких проблем, поскольку программных возможностей системы (как простых , так более сложных) хватает с избытком, чего конечно не скажешь про систему Linux. В системе Linux часто не заморачиваются, а просто прибегают к помощи сторонних библиотек (FMOD, BASS, OPENAL и пр.), либо используют консольные возможности встроенных программ, вроде aplay. Поначалу я обрадовался, когда нашел код пользователя с ником angros47 , но как оказалось данный код под Linux проигрывает WAV только небольших размеров (упирается в буфер звуковой карты). Я решил сам реализовать воспроизведение звука , опираясь на опыт других исходных кодов, а так же читая документацию ALSA. Насколько хорошо это у меня получилось, судить вам! Я тестировал на системах Linux Mint 32-bit, Linux Mint 64-bit, windows 7 32-bit, Windows 10 64-bit (с компиляторами 32-bit и 64-bit) . На Linux и Windows у меня проигрываются форматы: PCM 16-LE, PCM IEEE FLOATING-POINT, а большего мне и не нужно :)
Вы можете использовать исходные коды с этой страницы как вам заблагорассудится без всяких ограничений!
Интерфейс использования под Linux и Windows одинаковый. Можно проигрывать одновременно несколько файлов (примечание: в Linux для этого должна быть правильно сконфигурирована система. На Linux Mint все сконфигурировано по умолчанию). Для компиляции в Linux должна быть установлена низкоуровневая библиотека ALSA для разработчиков libasound-dev или libasound2-dev . Ее всегда можно установить из менеджера программ, либо вручную: sudo apt-get install libasound-dev или sudo apt-get install libasound2-dev.
Описание функций:
LoadWav - загрузка WAV файла в буфер из файла
PlayWav - воспроизведение звука. При повторном вызове, во время проигрывания текущего буфера, воспроизведение начинает сначала.
PauseWav - пауза при воспроизведении. При повторном вызове, начинает проигрывание с текущего места
StopWav - останавливает воспроизведение и сбрасывает на начало проигрывание.
FreeWav - останавливает воспроизведение (если не остановлено) и освобождает все ресурсы, связанные со звуком.
StatusPlay - возвращает статус воспроизведения (0-не проигрывается, 1- проигрывается, 2-пауза)
GetLengthSeconds - возвращает длительность трека в секундах.
Файлы для Linux:
alsa.bi
Dim Shared As Any Ptr alsa 'ALSA declarations alsa = Dylibload("asound") Const EAGAIN = -11 ' Try again Const EPIPE = -32 ' Broken pipe Const ESTRPIPE = -86 ' Streams pipe error Const EIO = -5 ' I/O error Const EBADFD = -77 ' File descriptor in bad state Const BLOCK = 0 Const NONBLOCK = 1 Const ASYNC = 2 Const SND_PCM_STREAM_PLAYBACK = 0 Const SND_PCM_STREAM_CAPTURE = 1 Const SND_PCM_ACCESS_RW_INTERLEAVED= 3 Enum snd_pcm_format_t SND_PCM_FORMAT_UNKNOWN = -1 SND_PCM_FORMAT_S8 = 0 SND_PCM_FORMAT_U8 SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_U16_LE SND_PCM_FORMAT_U16_BE SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_U24_LE SND_PCM_FORMAT_U24_BE SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_U32_LE SND_PCM_FORMAT_U32_BE SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_IEC958_SUBFRAME_LE SND_PCM_FORMAT_IEC958_SUBFRAME_BE SND_PCM_FORMAT_MU_LAW, SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_IMA_ADPCM SND_PCM_FORMAT_MPEG SND_PCM_FORMAT_GSM SND_PCM_FORMAT_SPECIAL = 31 SND_PCM_FORMAT_S24_3LE = 32 SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_U24_3LE SND_PCM_FORMAT_U24_3BE SND_PCM_FORMAT_S20_3LE SND_PCM_FORMAT_S20_3BE SND_PCM_FORMAT_U20_3LE SND_PCM_FORMAT_U20_3BE SND_PCM_FORMAT_S18_3LE SND_PCM_FORMAT_S18_3BE SND_PCM_FORMAT_U18_3LE SND_PCM_FORMAT_U18_3BE SND_PCM_FORMAT_G723_24 SND_PCM_FORMAT_G723_24_1B SND_PCM_FORMAT_G723_40 SND_PCM_FORMAT_G723_40_1B SND_PCM_FORMAT_DSD_U8 SND_PCM_FORMAT_DSD_U16_LE SND_PCM_FORMAT_DSD_U32_LE SND_PCM_FORMAT_DSD_U16_BE SND_PCM_FORMAT_DSD_U32_BE SND_PCM_FORMAT_LAST = SND_PCM_FORMAT_DSD_U32_BE SND_PCM_FORMAT_S16 = SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_U16 = SND_PCM_FORMAT_U16_LE SND_PCM_FORMAT_S24 = SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_U24 = SND_PCM_FORMAT_U24_LE SND_PCM_FORMAT_S32 = SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_U32 = SND_PCM_FORMAT_U32_LE SND_PCM_FORMAT_FLOAT = SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT64 = SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_IEC958_SUBFRAME = SND_PCM_FORMAT_IEC958_SUBFRAME_LE End Enum #IFNDEF NULL #DEFINE NULL 0 #ENDIF Type snd_pcm_t As Any Ptr Type snd_pcm_hw_params_t As Any Ptr Type snd_output_t As Any Ptr ' PCM Dim Shared snd_strerror As Function ( _ Byval ecode As Long) As Zstring Ptr snd_strerror= Dylibsymbol(alsa, "snd_strerror") Dim Shared snd_pcm_open As Function ( _ Byval pcm As snd_pcm_t Ptr, _ Byval device As Zstring Ptr, _ Byval direction As Long, _ Byval mode As Long) As Long snd_pcm_open= Dylibsymbol(alsa, "snd_pcm_open") Dim Shared snd_pcm_close As Function ( _ Byval pcm As snd_pcm_t) As Long snd_pcm_close= Dylibsymbol(alsa, "snd_pcm_close") Dim Shared snd_pcm_start As Function ( _ Byval pcm As snd_pcm_t) As Long snd_pcm_start= Dylibsymbol(alsa, "snd_pcm_start") Dim Shared snd_pcm_drain As Function ( _ Byval pcm As snd_pcm_t) As Long snd_pcm_drain= Dylibsymbol(alsa, "snd_pcm_drain") Dim Shared snd_pcm_hw_free As Function ( _ Byval pcm As snd_pcm_t) As Long snd_pcm_hw_free= Dylibsymbol(alsa, "snd_pcm_hw_free") Dim Shared snd_pcm_nonblock As Function ( _ Byval pcm As snd_pcm_t, _ Byval nonblock As Long) As Long snd_pcm_nonblock= Dylibsymbol(alsa, "snd_pcm_nonblock") Dim Shared snd_pcm_prepare As Function ( _ Byval pcm As snd_pcm_t) As Long snd_pcm_prepare= Dylibsymbol(alsa, "snd_pcm_prepare") Dim Shared snd_pcm_writei As Function ( _ Byval pcm As snd_pcm_t, _ Byval buffer As Any Ptr, _ Byval size As Long) As Long snd_pcm_writei= Dylibsymbol(alsa, "snd_pcm_writei") Dim Shared snd_pcm_recover As Function ( _ Byval pcm As snd_pcm_t, _ Byval Err As Long, _ Byval silent As Long) As Long snd_pcm_recover= Dylibsymbol(alsa, "snd_pcm_recover") Dim Shared snd_pcm_avail_update As Function ( _ Byval pcm As snd_pcm_t) As Long snd_pcm_avail_update= Dylibsymbol(alsa, "snd_pcm_avail_update") Dim Shared snd_pcm_delay As Function ( _ Byval pcm As snd_pcm_t, _ Byval delayp As snd_pcm_t) As Long snd_pcm_delay= Dylibsymbol(alsa, "snd_pcm_delay") Dim Shared snd_pcm_wait As Function ( _ Byval pcm As snd_pcm_t, _ Byval msec As Long) As Long snd_pcm_wait= Dylibsymbol(alsa, "snd_pcm_wait") Dim Shared snd_pcm_resume As Function ( _ Byval pcm As snd_pcm_t) As Long snd_pcm_resume= Dylibsymbol(alsa, "snd_pcm_resume") 'hardware Dim Shared snd_pcm_hw_params_malloc As Function ( _ Byval hw As snd_pcm_hw_params_t Ptr) As Long snd_pcm_hw_params_malloc= Dylibsymbol(alsa, "snd_pcm_hw_params_malloc") Dim Shared snd_pcm_hw_params_any As Function ( _ Byval pcm As snd_pcm_t, _ Byval hw As snd_pcm_hw_params_t) As Long snd_pcm_hw_params_any= Dylibsymbol(alsa, "snd_pcm_hw_params_any") Dim Shared snd_pcm_hw_params_set_access As Function ( _ Byval pcm As snd_pcm_t, _ Byval hw As snd_pcm_hw_params_t, _ Byval mode As Long) As Long snd_pcm_hw_params_set_access= Dylibsymbol(alsa, "snd_pcm_hw_params_set_access") Dim Shared snd_pcm_hw_params_set_format As Function ( _ Byval pcm As snd_pcm_t, _ Byval hw As snd_pcm_hw_params_t, _ Byval fmt As Long) As Long snd_pcm_hw_params_set_format= Dylibsymbol(alsa, "snd_pcm_hw_params_set_format") Dim Shared snd_pcm_hw_params_set_channels As Function ( _ Byval pcm As snd_pcm_t, _ Byval hw As snd_pcm_hw_params_t, _ Byval Channels As Long) As Long snd_pcm_hw_params_set_channels= Dylibsymbol(alsa, "snd_pcm_hw_params_set_channels") Dim Shared snd_pcm_hw_params_get_channels As Function ( _ Byval hw As snd_pcm_hw_params_t, _ Byval lpChannels As Long Ptr) As Long snd_pcm_hw_params_get_channels= Dylibsymbol(alsa, "snd_pcm_hw_params_get_channels") Dim Shared snd_pcm_hw_params_set_rate_near As Function ( _ Byval pcm As snd_pcm_t, _ Byval hw As snd_pcm_hw_params_t, _ Byval lpRate As Long Ptr, _ Byval lpDir As Long Ptr) As Long snd_pcm_hw_params_set_rate_near= Dylibsymbol(alsa, "snd_pcm_hw_params_set_rate_near") Dim Shared snd_pcm_hw_params_get_periods As Function ( _ Byval hw As snd_pcm_hw_params_t, _ Byval lpValue As Long Ptr, _ Byval lpDir As Long Ptr) As Long snd_pcm_hw_params_get_periods= Dylibsymbol(alsa, "snd_pcm_hw_params_get_periods") Dim Shared snd_pcm_hw_params_set_periods_near As Function ( _ Byval pcm As snd_pcm_t, _ Byval hw As snd_pcm_hw_params_t, _ Byval lpValue As Long Ptr, _ Byval lpDir As Long Ptr) As Long snd_pcm_hw_params_set_periods_near= Dylibsymbol(alsa, "snd_pcm_hw_params_set_periods_near") Dim Shared snd_pcm_hw_params_get_period_size As Function ( _ Byval params As snd_pcm_hw_params_t, _ Byval lpFrames As Long Ptr, _ Byval lpDir As Long Ptr) As Long snd_pcm_hw_params_get_period_size= Dylibsymbol(alsa, "snd_pcm_hw_params_get_period_size") Dim Shared snd_pcm_hw_params_set_period_size_near As Function ( _ Byval pcm As snd_pcm_t Ptr, _ Byval hw As snd_pcm_hw_params_t, _ Byval lpValue As Long Ptr, _ Byval lpDir As Long Ptr) As Long snd_pcm_hw_params_set_period_size_near= Dylibsymbol(alsa, "snd_pcm_hw_params_set_period_size_near") Dim Shared snd_pcm_hw_params_set_buffer_size_near As Function ( _ Byval pcm As snd_pcm_t, _ Byval hw As snd_pcm_hw_params_t, _ Byval lpFrames As Long Ptr) As Long snd_pcm_hw_params_set_buffer_size_near= Dylibsymbol(alsa, "snd_pcm_hw_params_set_buffer_size_near") Dim Shared snd_pcm_hw_params_get_buffer_size As Function ( _ Byval hw As snd_pcm_hw_params_t, _ Byval lpFrames As Long Ptr) As Long snd_pcm_hw_params_get_buffer_size= Dylibsymbol(alsa, "snd_pcm_hw_params_get_buffer_size") Dim Shared snd_pcm_hw_params As Function ( _ Byval pcm As snd_pcm_t, _ Byval hw As snd_pcm_hw_params_t) As Long snd_pcm_hw_params= Dylibsymbol(alsa, "snd_pcm_hw_params") Dim Shared snd_pcm_hw_params_free As Sub ( _ Byval hw As snd_pcm_hw_params_t) snd_pcm_hw_params_free= Dylibsymbol(alsa, "snd_pcm_hw_params_free") Dim Shared snd_pcm_hw_params_set_period_size As Function( _ Byval pcm As snd_pcm_t , _ Byval params As snd_pcm_hw_params_t, _ Byval nFrames As Long , _ Byval pDir As Long Ptr) As Long snd_pcm_hw_params_set_period_size = Dylibsymbol(alsa, "snd_pcm_hw_params_set_period_size") Dim Shared snd_pcm_hw_params_set_buffer_size As Function( _ Byval pcm As snd_pcm_t , _ Byval hw As snd_pcm_hw_params_t, _ Byval Frames As Long) As Long snd_pcm_hw_params_set_buffer_size = Dylibsymbol(alsa, "snd_pcm_hw_params_set_buffer_size")
FBLinuxSoundWav.bas:
#INCLUDE "alsa.bi" Type FbWAV As Short shFtag As Short shCanal As Short shBits As snd_pcm_t Ptr hDevice As snd_pcm_hw_params_t Ptr hw As Long iBufferSize As Long iSampleRate As Long iByteRate As Byte Ptr bSoundBuffer As Long iPause As Long iPauseSt As Long iStop As Long iStopWav As Any Ptr Thread As Any Ptr mutexstop Declare Function LoadWav(As Zstring Ptr) As Long Declare Function Play(As FbWav Ptr) As Long Declare Static Function PlayT(As FbWav Ptr) As Long Declare Function Stop() As Long Declare Function Pause() As Long Declare Destructor() Private: Declare Function SetParam() As Long End Type Destructor FbWAV Deallocate (bSoundBuffer) End Destructor Function GetLengthSeconds(Byref obj As FbWAV Ptr) As Long Return obj->iBufferSize\obj->iByteRate End Function Function StatusPlay(Byref obj As FbWAV Ptr) As Long Dim As Long iStatusPl, iStatusPa Mutexlock(obj->mutexstop) iStatusPl = obj->iStopWav iStatusPa = obj->iPauseSt Mutexunlock(obj->mutexstop) If iStatusPl = 0 Then Return 0 Elseif iStatusPl = -1 AndAlso iStatusPa = 0 Then Return 1 Elseif iStatusPl = -1 AndAlso iStatusPa = -1 Then Return 2 Endif End Function Function FbWAV.LoadWav(szFile As Zstring Ptr) As Long If Lcase(Right (*szFile,3)) = "wav" Then Dim idFile As Long = Freefile Open *szFile For Binary As #idFile Get #idFile,21,shFtag Get #idFile,,shCanal Get #idFile,,iSampleRate Get #idFile,29,iByteRate Get #idFile,35,shBits Get #idFile,41,iBufferSize Select Case shFtag Case 1 , 3 If iBufferSize = 0 Then Print "Invalidate data!" Return 0 Endif Case Else Print "Format Unknown!" Return 0 End Select bSoundBuffer = Allocate (iBufferSize+1) If bSoundBuffer Then Get #idFile,45,*bSoundBuffer,iBufferSize Endif Close #idFile Return SetParam() Endif End Function Function FbWAV.SetParam() As Long Dim As Long iDirection , iRet If alsa Then iRet = snd_pcm_open(@hDevice, "default", SND_PCM_STREAM_PLAYBACK, BLOCK) If iRet < 0 Then Print "Error snd_pcm_open" Return 0 Endif snd_pcm_hw_params_malloc(@hw) iRet = snd_pcm_hw_params_any(hDevice, hw) If iRet < 0 Then Print "Error snd_pcm_hw_params_any" Return 0 Endif iRet = snd_pcm_hw_params_set_access(hDevice, hw, SND_PCM_ACCESS_RW_INTERLEAVED) If iRet < 0 Then Print "Error snd_pcm_hw_params_set_access" Return 0 Endif If shFtag = 1 Then If shBits = 16 Then iRet = snd_pcm_hw_params_set_format(hDevice, hw, SND_PCM_FORMAT_S16_LE) If iRet < 0 Then Print "Error snd_pcm_hw_params_set_format s16_LE" Return 0 Endif Elseif shBits = 8 Then iRet = snd_pcm_hw_params_set_format(hDevice, hw, SND_PCM_FORMAT_U8) If iRet < 0 Then Print "Error snd_pcm_hw_params_set_format u8" Return 0 Endif Endif Elseif shFtag = 3 Then iRet = snd_pcm_hw_params_set_format(hDevice, hw, SND_PCM_FORMAT_FLOAT_LE) If iRet < 0 Then Print "Error snd_pcm_hw_params_set_format FLOAT_LE" Return 0 Endif Else Print "Format Unknown!" Return 0 Endif iRet = snd_pcm_hw_params_set_channels(hDevice, hw, shCanal) If iRet < 0 Then Print "Error snd_pcm_hw_params_set_channels" Return 0 Endif iRet = snd_pcm_hw_params_set_rate_near(hDevice, hw, @iSampleRate, @iDirection) If iRet < 0 Then Print "Error snd_pcm_hw_params_set_rate_near" Return 0 Endif Dim As Long BufferSizeInFrames = 2048 iRet = snd_pcm_hw_params_set_buffer_size(hDevice, hw, BufferSizeInFrames) Dim As Long inFrames = 2048 If iRet < 0 Then iRet = snd_pcm_hw_params_set_buffer_size_near(hDevice, hw, @BufferSizeInFrames) If iRet < 0 Then Print "Error snd_pcm_hw_params_set_buffer_size_near" Return 0 Endif Endif iRet = snd_pcm_hw_params_set_period_size(hDevice, hw, inFrames, 0) If iRet < 0 Then iRet = snd_pcm_hw_params_set_period_size_near( hDevice, hw, @inFrames, 0) If iRet < 0 Then Print "Error snd_pcm_hw_params_set_period_size_near" Return 0 Endif Endif iRet = snd_pcm_hw_params(hDevice, hw) If iRet < 0 Then Print "Error snd_pcm_hw_params" Return 0 Endif snd_pcm_hw_params_free (hw) snd_pcm_prepare(hDevice) mutexstop = Mutexcreate Return -1 End If End Function Function FbWAV.Play(obj As FbWAV Ptr) As Long Dim As Long iStatusPl Mutexlock(obj->mutexstop) iStatusPl = obj->iStopWav Mutexunlock(obj->mutexstop) If iStatusPl = -1 Then obj->Stop() obj->THREAD = Threadcreate(Cast(Any Ptr,@PlayT()), Obj) Mutexlock(obj->mutexstop) obj->iStopWav = -1 Mutexunlock(obj->mutexstop) Return -1 End Function Function FbWAV.PlayT(p As FbWAV Ptr) As Long Dim As Long iRet, inErrors , inFrames , iFrameSize Dim As Any Ptr lpBuffer inFrames = 2048 iFrameSize = (p->shBits Shr 3) Shl (p->shCanal-1) lpBuffer = p->bSoundBuffer If alsa Then While (inFrames > 0) andalso (inErrors < 4) Mutexlock(p->mutexstop) If p->iStop = -1 Then Mutexunlock(p->mutexstop) Exit While Endif If p->iPause = -1 Then Sleep(1) Mutexunlock(p->mutexstop) Continue While Endif Mutexunlock(p->mutexstop) iRet = snd_pcm_writei(p->hDevice,lpBuffer,inFrames) If iRet < 0 Then Select Case iRet Case EAGAIN Sleep 1 Case EPIPE iRet = snd_pcm_prepare(p->hDevice) If (iRet < 0) Then inErrors += 1 End If Case ESTRPIPE Do iRet = snd_pcm_resume(p->hDevice) If iRet = EAGAIN Then Sleep 1 Loop While iRet = EAGAIN If (iRet < 0) Then iRet = snd_pcm_prepare(p->hDevice) If (iRet < 0) Then inErrors+=1 End If End If Case EBADFD , EIO inErrors+=1 Case Else inErrors += 1 snd_pcm_prepare(p->hDevice) End Select Else iRet *= iFrameSize lpBuffer += iRet If (lpBuffer >= p->bSoundBuffer + p->iBufferSize) Then Function = -1 Exit While Elseif (lpBuffer + iRet > p->bSoundBuffer + p->iBufferSize) Then Dim As Integer iBytes = Cast (Integer,p->bSoundBuffer + p->iBufferSize) iBytes -= Cast(Integer,lpBuffer) inFrames = Cast(Long,iBytes)\iFrameSize Function = -1 Endif End If Wend Mutexlock(p->mutexstop) p->iStopWav = 0 Mutexunlock(p->mutexstop) Endif End Function Function FbWAV.Stop() As Long Dim As Long iStatusPl Mutexlock(mutexstop) iStatusPl = iStopWav Mutexunlock(mutexstop) If iStatusPl = -1 Then Mutexlock(mutexstop) iStop = -1 Mutexunlock(mutexstop) Threadwait(THREAD) iStop = 0 iStopWav = 0 iPause = 0 iPauseSt = 0 Function = -1 Endif End Function Function FbWAV.Pause() As Long Mutexlock(mutexstop) If iStopWav = -1 Then iPause Xor= -1 iPauseSt = iPause Function = iPause Endif Mutexunlock(mutexstop) End Function Function PauseWAV(obj As FbWAV Ptr) As Long Return obj->Pause() End Function Function LoadWav(szFile As Zstring Ptr, Byref obj As FbWAV Ptr) As Long obj = New FbWAV Return obj->LoadWav(szFile) End Function Function PlayWav(obj As FbWAV Ptr) As Long Return obj->Play (obj) End Function Sub FreeWav(obj As FbWAV Ptr) Dim As Long iStatusPl Mutexlock(obj->mutexstop) iStatusPl = obj->iStopWav Mutexunlock(obj->mutexstop) If iStatusPl = -1 Then obj->Stop() Endif snd_pcm_close(obj->hDevice) Mutexdestroy(obj->mutexstop) Delete obj End Sub Sub StopWav(obj As FbWAV Ptr) obj->Stop() End Sub
Файл для Windows:
FBWindowsSoundWav.bas:
#INCLUDE "Windows.bi" #INCLUDE "win/mmsystem.bi" Type FbWAV As Short shFtag As Short shCanal As Short shBits As HWAVEOUT hWaveOut As WAVEHDR Ptr hWhdr As Long iBufferSize As Long iSampleRate As Long iByteRate As Byte Ptr bSoundBuffer As Long iPause As Long iStopWav As CRITICAL_SECTION c_s Declare Function LoadWav(As Zstring Ptr,Byref obj As FbWAV Ptr) As Long Declare Function Play() As Long Declare Function Stop() As Long Declare Function Pause() As Long Declare Static Sub SoundCallback (hwo As Any Ptr, uMsg As Ulong, dwInstance As Long, dwParam1 As Long, dwParam2 As Long ) Declare Destructor() Private: Declare Function SetParam(Byref obj As FbWAV Ptr) As Long End Type Dim Shared As FBWAV Ptr fb_info(100) Destructor FbWAV Deallocate (bSoundBuffer) End Destructor Sub FbWAV.SoundCallback (hwo As Any Ptr, uMsg As Ulong, dwInstance As Long, dwParam1 As Long, dwParam2 As Long ) If uMsg = WOM_DONE Then For i As Long = 0 To 100 If fb_info(i) <> 0 AndAlso fb_info(i)->hWaveOut = hwo Then EnterCriticalSection(@fb_info(i)->c_s) fb_info(i)->iStopWav = 0 LeaveCriticalSection(@fb_info(i)->c_s) Exit For Endif Next End If End Sub Function GetLengthSeconds(Byref obj As FbWAV Ptr) As Long Return obj->iBufferSize\obj->iByteRate End Function Function StatusPlay(Byref obj As FbWAV Ptr) As Long Dim As Long s_p EnterCriticalSection(@obj->c_s) s_p = obj->iStopWav LeaveCriticalSection(@obj->c_s) If s_p = 0 Then Return 0 Elseif s_p = -1 AndAlso obj->iPause = 0 Then Return 1 Elseif s_p = -1 AndAlso obj->iPause = -1 Then Return 2 Endif End Function Function FbWAV.LoadWav(szFile As Zstring Ptr , Byref obj As FbWAV Ptr) As Long If Lcase(Right (*szFile,3)) = "wav" Then Dim idFile As Long = Freefile Open *szFile For Binary As #idFile Get #idFile,21,shFtag Get #idFile,,shCanal Get #idFile,,iSampleRate Get #idFile,29,iByteRate Get #idFile,35,shBits Get #idFile,41,iBufferSize Select Case shFtag Case 1 , 3 If iBufferSize = 0 Then Print "Invalidate data!" Return 0 Endif Case Else Print "Format Unknown!" Return 0 End Select bSoundBuffer = Allocate (iBufferSize+1) If bSoundBuffer Then Get #idFile,45,*bSoundBuffer,iBufferSize Endif Close #idFile Return SetParam(obj) Else Return 0 Endif End Function Function FbWAV.SetParam(Byref obj As FbWAV Ptr) As Long Dim As WAVEFORMATEX wfx wfx.wFormatTag = shFtag wfx.nChannels = shCanal wfx.nSamplesPerSec = iSampleRate wfx.wBitsPerSample = shBits wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8 wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec Dim As Integer scall = Cast(Integer, @SoundCallback) If waveOutOpen(@hWaveOut, WAVE_MAPPER, @wfx, scall, 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR Then Print "Error waveOutOpen" Return NULL Else hWhdr = Allocate(Sizeof(WAVEHDR)) hWhdr->lpData = bSoundBuffer hWhdr->dwBufferLength = iBufferSize hWhdr->dwFlags = WHDR_PREPARED waveOutPrepareHeader(hWaveOut, hWhdr, Sizeof(WAVEHDR)) InitializeCriticalSection(@c_s) For i As Long = 0 To 100 If fb_info(i) = 0 Then fb_info(i) = obj Exit For Endif Next Return -1 End If End Function Function FbWAV.Play() As Long EnterCriticalSection(@c_s) If iStopWav = -1 Then LeaveCriticalSection(@c_s) Stop() Else LeaveCriticalSection(@c_s) Endif If waveOutWrite(hWaveOut, hWhdr, Sizeof(WAVEHDR)) Then waveOutUnprepareHeader(hWaveOut, hWhdr, Sizeof(WAVEHDR)) Deallocate(hWhdr) Print "Error waveOutWrite" Return 0 Else iStopWav = -1 Return -1 Endif End Function Function FbWAV.Pause() As Long EnterCriticalSection(@c_s) If iStopWav = 0 Then LeaveCriticalSection(@c_s) Exit Function Else LeaveCriticalSection(@c_s) Endif iPause Xor= -1 If iPause = 0 Then waveOutRestart(hWaveOut) Else waveOutPause(hWaveOut) Endif Return -1 End Function Function FbWAV.Stop() As Long waveOutReset(hWaveOut) iPause = 0 Return -1 End Function Function LoadWav(szFile As Zstring Ptr, Byref obj As FbWAV Ptr) As Long obj = New FbWAV Return obj->LoadWav(szFile,obj) End Function Function PlayWav(obj As FbWAV Ptr) As Long Return obj->Play () End Function Function PauseWAV(obj As FbWAV Ptr) As Long Return obj->Pause() End Function Sub FreeWav(obj As FbWAV Ptr) EnterCriticalSection(@(obj->c_s)) If obj->iStopWav = -1 Then LeaveCriticalSection(@(obj->c_s)) obj->Stop() Else LeaveCriticalSection(@(obj->c_s)) Endif waveOutClose(obj->hWaveOut) waveOutUnprepareHeader(obj->hWaveOut, obj->hWhdr, Sizeof(WAVEHDR)) For i As Long = 0 To 100 If fb_info(i) = obj Then fb_info(i) = 0 Exit For Endif Next Deallocate(obj->hWhdr) DeleteCriticalSection(@(obj->c_s)) Delete obj End Sub Sub StopWav(obj As FbWAV Ptr) obj->Stop() End Sub
Пример использования для Linux и Windows:
Подключите с помощью оператора #include:
файл FBLinuxSoundWav.bas в системе Linux
или
файл FBWindowsSoundWav.bas в системе Windows
Клавиши в примере:
клавиша "q" - выход
клавиша "Пробел" - пауза
клавиша "p" - проигрывать
клавиша "s" - стоп
Dim fb_Wav As FbWAV Ptr LoadWav("Track.wav", fb_Wav) PlayWav(fb_Wav) Do Var key = Inkey Select Case key Case " " PauseWAV(fb_Wav) Sleep(100) Case "q" Exit Do Case "s" StopWav(fb_Wav) Case "p" PlayWav(fb_Wav) End Select Var sp = StatusPlay(fb_Wav) Sleep(100) Cls If sp = 1 Then ? "Play" Elseif sp = 0 Then ? "Stop" Elseif sp = 2 Then ? "Pause" Endif ? "Length=" & GetLengthSeconds(fb_Wav) & " seconds" Loop FreeWav(fb_Wav)
P.S. клавиши "q", "p" , "s" в примере работают только в английской раскладке! Если во время работы примера у вас стоит русская раскладка, переключите на английскую, если конечно хотите использовать данные клавиши.