Проигрывание PCM данных (Windows,Linux)

Примеры , позволяющие проиграть PCM данные. Можно задать для проигрывания указатель на данные , с возможностью задать кол-во каналов (моно. стерео), частоту дискретизации, кол-во бит на семпл (8 , 16). Для Windows подобных примеров предостаточно , но вот для Linux замучаешься искать. И хотя здесь два примера (для Windows и Linux), но интерфейс у них идинтичный.

Платформы: Windows, Linux
Автор: angros47

1) для Windows:

#INCLUDE "Windows.bi"
#INCLUDE Once "crt/string.bi"
#INCLUDE "win/mmsystem.bi"
'#define WAVE_MAPPER -1

Dim Shared _hWaveOut As HWAVEOUT
Dim Shared cs As CRITICAL_SECTION

Type QueueWAVEHDR
   _wavehdr As WAVEHDR
   _next As QueueWAVEHDR Ptr
End Type

Dim Shared doneWaveHdrs As QueueWAVEHDR Ptr
Dim Shared waitingBytes As Integer

Sub dsp_callback Stdcall (_hWaveOut As HWAVEOUT, msg As UINT, instance As DWORD, p1 As DWORD, p2 As DWORD)
   If msg = WOM_DONE Then
      Dim p As QueueWAVEHDR Ptr
      p = Cast(QueueWAVEHDR Ptr, p1)
      EnterCriticalSection(@cs)
      p->_next = doneWaveHdrs
      doneWaveHdrs = p
      LeaveCriticalSection(@cs)
   End If
End Sub

Sub clean_unprepares()
   EnterCriticalSection(@cs)
   Do While (doneWaveHdrs)
      Dim p As QueueWAVEHDR Ptr
      p = doneWaveHdrs
      doneWaveHdrs = p->_next
      waitingBytes -= p->_wavehdr.dwBufferLength
      waveOutUnprepareHeader(_hWaveOut, @p->_wavehdr, Sizeof(WAVEHDR))
      Deallocate(p->_wavehdr.lpData)
           Deallocate(p)
   Loop
   LeaveCriticalSection(@cs)
End Sub

Sub dsp_finalize()
   If _hWaveOut Then
      waveOutPause(_hWaveOut)
      waveOutReset(_hWaveOut)
      clean_unprepares()
      waveOutClose(_hWaveOut)
      _hWaveOut = NULL
      DeleteCriticalSection(@cs)
   End If
End Sub

Sub SoundSet(frequency As Integer, channels As Integer, bits As Integer)
   Dim As WAVEFORMATEX wfx

   dsp_finalize()

   memset(@wfx, 0, Sizeof(wfx))
   wfx.wFormatTag = WAVE_FORMAT_PCM
   wfx.nChannels = channels
   wfx.nSamplesPerSec = frequency
   wfx.wBitsPerSample = bits
   wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8
   wfx.nAvgBytesPerSec =  wfx.nBlockAlign * wfx.nSamplesPerSec
   wfx.cbSize = 0
   If waveOutOpen(@_hWaveOut, WAVE_MAPPER, @wfx, Cast(DWORD, @dsp_callback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR Then
      _hWaveOut = NULL
   Else
           waveOutRestart(_hWaveOut)
      InitializeCriticalSection(@cs)
   End If
End Sub

Sub playbuffer (soundBuffer As Any Ptr, buffersize As Integer)
   If _hWaveOut Then
      Dim p As WAVEHDR Ptr = Allocate(Sizeof(QueueWAVEHDR))
      memset(p, 0, Sizeof(WAVEHDR))
      p->lpData = Allocate(buffersize)

      If p->lpData=0 Then
         Deallocate(p)
      Else
         memcpy(p->lpData, SoundBuffer, buffersize)
         p->dwBufferLength = buffersize: p->dwBytesRecorded = buffersize
         waveOutPrepareHeader(_hWaveOut, p, Sizeof(WAVEHDR))
         If waveOutWrite(_hWaveOut, p, Sizeof(WAVEHDR)) = MMSYSERR_NOERROR Then
            waitingBytes += p->dwBufferLength
         Else
            waveOutUnprepareHeader(_hWaveOut, p, Sizeof(WAVEHDR))
            Deallocate(p->lpData)
            Deallocate(p)
         End If
      End If
           clean_unprepares()
   End If
End Sub

Dim MyBuffer(200000) As Short

SoundSet 44100,2,16

Dim i2 As Integer
For a As integer=0 To 80
   For i As integer=1 To 512*4 Step 2
      i2=i2+1
      MyBuffer(i)= (i2 Mod 20)*i2/10
      MyBuffer(i+1)= (i2 Mod 20)*i2/10
   Next
   playbuffer @MyBuffer(0),1024*4
Next


Sleep 


2) для Linux:

Dim Shared As Any Ptr alsa
Dim Shared As Integer OSS


'ALSA declarations
alsa = Dylibload("asound")

Const EAGAIN                       = -11 ' Try again
Const EPIPE                        = -32 ' Broken pipe
Const ESTRPIPE                     = -86 ' Streams pipe error

Const BLOCK                        = 0
Const NONBLOCK                     = 1
Const ASYNC                        = 2

Const SND_PCM_STREAM_PLAYBACK      = 0
Const SND_PCM_STREAM_CAPTURE       = 1
Const SND_PCM_FORMAT_S16_LE        = 2
Const SND_PCM_ACCESS_RW_INTERLEAVED= 3

#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 Integer) 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 Integer, _
Byval mode         As Integer) As Integer
snd_pcm_open= Dylibsymbol(alsa, "snd_pcm_open")

Dim Shared snd_pcm_close As Function ( _
Byval pcm          As snd_pcm_t) As Integer
snd_pcm_close= Dylibsymbol(alsa, "snd_pcm_close")

Dim Shared snd_pcm_start As Function ( _
Byval pcm          As snd_pcm_t) As Integer
snd_pcm_start= Dylibsymbol(alsa, "snd_pcm_start")

Dim Shared snd_pcm_drain As Function ( _
Byval pcm          As snd_pcm_t) As Integer
snd_pcm_drain= Dylibsymbol(alsa, "snd_pcm_drain")

Dim Shared snd_pcm_hw_free As Function ( _
Byval pcm          As snd_pcm_t) As Integer
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 Integer) As Integer
snd_pcm_nonblock= Dylibsymbol(alsa, "snd_pcm_nonblock")

Dim Shared snd_pcm_prepare As Function ( _
Byval pcm          As snd_pcm_t) As Integer
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 Integer) As Integer
snd_pcm_writei= Dylibsymbol(alsa, "snd_pcm_writei")

Dim Shared snd_pcm_recover As Function ( _
Byval pcm          As snd_pcm_t, _
Byval Err          As Integer, _
Byval silent       As Integer) As Integer
snd_pcm_recover= Dylibsymbol(alsa, "snd_pcm_recover")

Dim Shared snd_pcm_avail_update As Function ( _
Byval pcm          As snd_pcm_t) As Integer
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 Integer
snd_pcm_delay= Dylibsymbol(alsa, "snd_pcm_delay")

Dim Shared snd_pcm_wait As Function ( _
Byval pcm          As snd_pcm_t, _
Byval msec As Integer) As Integer
snd_pcm_wait= Dylibsymbol(alsa, "snd_pcm_wait")

Dim Shared snd_pcm_resume As Function ( _
Byval pcm          As snd_pcm_t) As Integer
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 Integer
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 Integer
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 Integer) As Integer
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 Integer) As Integer
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 Integer) As Integer
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 Integer Ptr) As Integer
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 Integer Ptr, _
Byval lpDir        As Integer Ptr) As Integer
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 Integer Ptr, _
Byval lpDir        As Integer Ptr) As Integer
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 Integer Ptr, _
Byval lpDir        As Integer Ptr) As Integer
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 Integer Ptr, _
Byval lpDir        As Integer Ptr) As Integer
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 Integer Ptr, _
Byval lpDir        As Integer Ptr) As Integer
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 Integer Ptr) As Integer
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 Integer Ptr) As Integer
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 Integer
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 As snd_pcm_t Ptr           hDevice
Dim Shared As snd_pcm_hw_params_t Ptr hw
Dim Shared As Integer                 ret,value,direction,buffersize,nFrames,Periodsize
Dim Shared As Zstring Ptr             strRet


'OSS declarations

#INCLUDE "file.bi"

OSS=FileExists("/dev/dsp")

Declare Function fileno Cdecl Alias "fileno" (Byval As Any Ptr) As Integer
Declare Function ioctl Cdecl Alias "ioctl" (Byval hDevice As Integer,Byval io_cmd As Integer,Byval lpArg As Integer Ptr) As Integer


#DEFINE _IOC_NRBITS    8
#DEFINE _IOC_TYPEBITS  8
#DEFINE _IOC_SIZEBITS 14
#DEFINE _IOC_DIRBITS   2

#DEFINE _IOC_NRMASK   ((1 Shl _IOC_NRBITS  )-1)
#DEFINE _IOC_TYPEMASK ((1 Shl _IOC_TYPEBITS)-1)
#DEFINE _IOC_SIZEMASK ((1 Shl _IOC_SIZEBITS)-1)
#DEFINE _IOC_DIRMASK  ((1 Shl _IOC_DIRBITS )-1)

#DEFINE _IOC_NRSHIFT    0
#DEFINE _IOC_TYPESHIFT  (_IOC_NRSHIFT   + _IOC_NRBITS)
#DEFINE _IOC_SIZESHIFT  (_IOC_TYPESHIFT + _IOC_TYPEBITS)
#DEFINE _IOC_DIRSHIFT   (_IOC_SIZESHIFT + _IOC_SIZEBITS)

' Direction bits.
#DEFINE _IOC_NONE  0U
#DEFINE _IOC_WRITE 1U
#DEFINE _IOC_READ  2U

#DEFINE _IOC(Dir,t,nr,size) (((Dir) Shl _IOC_DIRSHIFT) Or ((t) Shl _IOC_TYPESHIFT) Or ((nr)  Shl _IOC_NRSHIFT) Or ((size) Shl  _IOC_SIZESHIFT))

' used to create numbers
#DEFINE _IO(t,nr)        _IOC(_IOC_NONE,(t),(nr),0)
#DEFINE _IOR(t,nr,size)  _IOC(_IOC_READ,(t),(nr),Sizeof(size))
#DEFINE _IOW(t,nr,size)  _IOC(_IOC_WRITE,(t),(nr),Sizeof(size))
#DEFINE _IOWR(t,nr,size) _IOC(_IOC_READ Or _IOC_WRITE,(t),(nr),Sizeof(size))

' used to decode ioctl numbers..
#DEFINE _IOC_DIR(nr)   (((nr) Shr _IOC_DIRSHIFT)  And _IOC_DIRMASK)
#DEFINE _IOC_TYPE(nr)  (((nr) Shr _IOC_TYPESHIFT) And _IOC_TYPEMASK)
#DEFINE _IOC_NR(nr)    (((nr) Shr _IOC_NRSHIFT)   And _IOC_NRMASK)
#DEFINE _IOC_SIZE(nr)  (((nr) Shr _IOC_SIZESHIFT) And _IOC_SIZEMASK)

' ...and for the drivers/sound files...
#DEFINE IOC_IN        ( _IOC_WRITE    Shl _IOC_DIRSHIFT)
#DEFINE IOC_OUT       ( _IOC_READ     Shl _IOC_DIRSHIFT)
#DEFINE IOC_INOUT     ((_IOC_WRITE    Or  _IOC_READ) Shl _IOC_DIRSHIFT)
#DEFINE IOCSIZE_MASK  ( _IOC_SIZEMASK Shl _IOC_SIZESHIFT)
#DEFINE IOCSIZE_SHIFT ( _IOC_SIZESHIFT)

' IOCTL commands for /dev/dsp and /dev/audio
#DEFINE SNDCTL_DSP_RESET          _IO  (Asc("P"), 0)
#DEFINE SNDCTL_DSP_SYNC           _IO  (Asc("P"), 1)
#DEFINE SNDCTL_DSP_SPEED          _IOWR(Asc("P"), 2, Integer)
#DEFINE SNDCTL_DSP_STEREO         _IOWR(Asc("P"), 3, Integer)
#DEFINE SNDCTL_DSP_GETBLKSIZE     _IOWR(Asc("P"), 4, Integer)
#DEFINE SNDCTL_DSP_SETFMT         _IOWR(Asc("P"), 5, Integer) ' Selects ONE fmt
#DEFINE SNDCTL_DSP_CHANNELS       _IOWR(Asc("P"), 6, Integer)
#DEFINE SOUND_PCM_WRITE_CHANNELS  SNDCTL_DSP_CHANNELS
#DEFINE SOUND_PCM_WRITE_FILTER    _IOWR(Asc("P"), 7, Integer)
#DEFINE SNDCTL_DSP_POST           _IO  (Asc("P"), 8)
#DEFINE SNDCTL_DSP_SUBDIVIDE      _IOWR(Asc("P"), 9, Integer)
#DEFINE SNDCTL_DSP_SETFRAGMENT    _IOWR(Asc("P"),10, Integer)
#DEFINE SNDCTL_DSP_GETFMTS        _IOR (Asc("P"),11, Integer) ' Returns a mask
#DEFINE SNDCTL_DSP_SAMPLESIZE     SNDCTL_DSP_SETFMT
#DEFINE SNDCTL_DSP_GETODELAY   _IOR (Asc("P"), 23, Integer)


'arg for SNDCTL_DSP_SETFMT cmd
Const AFMT_MU_LAW    = &H00000001
Const AFMT_A_LAW     = &H00000002
Const AFMT_IMA_ADPCM = &H00000004
Const AFMT_U8        = &H00000008
Const AFMT_S16_LE    = &H00000010  ' Little endian signed
Const AFMT_S16_BE    = &H00000020  ' Big endian signed 16
Const AFMT_S8        = &H00000040
Const AFMT_U16_LE    = &H00000080  ' Little endian U16
Const AFMT_U16_BE    = &H00000100  ' Big endian U16
Const AFMT_MPEG      = &H00000200  ' MPEG (2) audio
Const AFMT_AC3       = &H00000400  ' Dolby Digital AC3










Sub SoundSet(frequency As Integer, channels As Integer, bits As Integer)
   If alsa Then
      If bits=16 Then PeriodSize=2 Else Periodsize=1
      Periodsize*=channels

      ret = snd_pcm_open(@hDevice,"default", SND_PCM_STREAM_PLAYBACK, BLOCK)

      snd_pcm_hw_params_malloc(@hw)
      ret = snd_pcm_hw_params_any(hDevice,hw)
      ret = snd_pcm_hw_params_set_access(hDevice, hw, SND_PCM_ACCESS_RW_INTERLEAVED)

      ret = snd_pcm_hw_params_set_format(hDevice,hw,Iif(bits=16,2,1))
      ret = snd_pcm_hw_params_set_channels(hDevice,hw,channels)

      value=frequency 'set speed
      ret = snd_pcm_hw_params_set_rate_near(hDevice,hw,@value,@direction)

      ret = snd_pcm_hw_params(hDevice,hw)
      snd_pcm_hw_params_free (hw)
      snd_pcm_prepare(hDevice)
   Elseif OSS Then
      If OSS>0 Then Close OSS
      OSS=FreeFile
      Open "/dev/dsp" For Output As #1

      Dim As Integer arg
      arg=IIF(bits=16, AFMT_S16_LE, AFMT_U8)
      ioctl fileno(Cast (Any Ptr,Fileattr(1,2))), SNDCTL_DSP_SETFMT, @arg

      arg=IIF(channels<>0, 1, 0)
      ioctl fileno(Cast (Any Ptr,Fileattr(1,2))), SNDCTL_DSP_STEREO, @arg

      arg=channels
      ioctl fileno(Cast (Any Ptr,Fileattr(1,2))), SNDCTL_DSP_CHANNELS, @arg

      arg=frequency
      ioctl fileno(Cast (Any Ptr,Fileattr(1,2))), SNDCTL_DSP_SPEED, @arg


   End If

End Sub

Sub playbuffer (soundBuffer As Any Ptr, buffersize As Integer)
   If alsa Then
      buffersize/=Periodsize
      ret=snd_pcm_writei(hDevice, soundBuffer, buffersize)
      If ret=EPIPE Then
         ret=snd_pcm_recover(hDevice, ret,1)

         If ret=0 Then ret=snd_pcm_writei(hDevice, soundBuffer, buffersize)
      
      End If
   Elseif OSS>0 Then
      Put #OSS, ,*cast(Ubyte Ptr, soundBuffer), buffersize
   End If
End Sub

Function SoundQueue() As Integer
   If alsa Then
      Dim value As Integer
      snd_pcm_delay(hDevice, @value)
      Return value*Periodsize
   Elseif OSS>0 Then
      Dim value As Integer
      ioctl fileno(Cast (Any Ptr,Fileattr(1,2))), SNDCTL_DSP_GETODELAY, @value
      Return value
   End If
End Function

Dim MyBuffer(200000) As Short

SoundSet 44100,2,16

Dim i2 As Integer
For a As integer=0 To 80
    For i As integer=1 To 512*4 Step 2
        i2=i2+1
        MyBuffer(i)= (i2 Mod 20)*i2/10
        MyBuffer(i+1)= (i2 Mod 20)*i2/10
    Next
    playbuffer @MyBuffer(0),1024*4
Next


Sleep