Streaming audio

Linux specific questions.
Post Reply
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Streaming audio

Post by angros47 »

This routine allows to stream sound data, in 8 or 16 bit format, mono or stereo, at any rate, using either ALSA or OSS. No external shared libraries are needed (unlike in FBSound).

Code: Select all

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_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

const SNDCTL_DSP_RESET      = &H00005000
const SNDCTL_DSP_SYNC       = &H00005001
const SNDCTL_DSP_POST       = &H00005008
const SNDCTL_DSP_NONBLOCK   = &H0000500e

const SNDCTL_DSP_SPEED      = &Hc0045002
const SNDCTL_DSP_STEREO     = &Hc0045003 '0=mono 1=stereo
const SNDCTL_DSP_GETBLKSIZE = &Hc0045004
const SNDCTL_DSP_SETFMT     = &Hc0045005

const SNDCTL_DSP_SAMPLESIZE = &Hc0045005 'same as _SETFMT
const SNDCTL_DSP_CHANNELS   = &Hc0045006 '1=mono 2=stereo
const SOUND_PCM_WRITE_FILTER= &Hc0045007

const SNDCTL_DSP_SUBDIVIDE  = &Hc0045009
const SNDCTL_DSP_SETFRAGMENT =&Hc004500A

'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
Although the code is different, the syntax is the same of this one: http://freebasic.net/forum/viewtopic.php?f=6&t=23099
so a program using this can be compiler both under windows and linux

The same example works in both systems:

Code: Select all

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
 
xlucas
Posts: 334
Joined: May 09, 2014 21:19
Location: Argentina

Re: Streaming audio

Post by xlucas »

Hi! I know this thread is old, but I need help with this. If angros47 is around, it'd be great. Otherwise, people, please try to illuminate me with this :P

I've been trying Angros' code and it works perfectly. I noticed that, if I trigger two PlayBuffer's, the sounds are executed one after the other, yet I can take control immediately after issuing the two calls. This is great for playing music, but I'm writing a small action game. Sometimes, I will need to execute a sound "right now" and there will be other sounds playing already, and I will want my sound to run on top of the other sounds.

I have two options:
1 - I execute a PlayBuffer on each cycle with a small amount of sound and fill that small buffer with whatever is happening at that time. This is an awful solution. ALSA clock won't necessarily be exactly like my timer. If I run too slow, sounds will start drifting and if I run too fast, there will be gaps and produce undesired noise.
2 - I can execute a long empty buffer and modify it on-the-fly. This sounds better, but there are still two problems. One is that I'm not sure whether ALSA is playing my buffer or copying it at the moment the PlayBuffer is executed. If the latter is true, this just won't work. The other problem is that even if it does work, again, my clock will surely not be exactly like ALSA's and my sound will either drift or I will start modifying parts of the buffer that have already been executed.

Question: how can I have more control of this? In particular, how can I get access to a buffer that I know is being read by ALSA and I can freely modify on the fly, and how can I know exactly what sample is being executed? Otherwise, how can I at least tell when the buffer I sent has already been completely executed? Or how can force ALSA to stop playing the buffer?

I would like to only use code I can integrate in my program, that I can modify easily. I do not want to use a third-party library. Something simple like what Angros posted is great. Thank you guys :)
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

You need to check if the buffer is full or empty... I wrote something, I'll have to find the source
xlucas
Posts: 334
Joined: May 09, 2014 21:19
Location: Argentina

Re: Streaming audio

Post by xlucas »

Thanks! Well, actually, I've been researching and I found two options, but none I know how to handle. One is, like you tell me, check when the buffer is empty. This would be the simplest to perform, although I don't know how to check that, but there's a problem I will explain. The other option would be to use "MMAP", which supposedly, would allow me to directly access the buffer as it's being executed, if I understood correctly, but it's so loosely documented, that I couldn't find a good example. I don't know how to do it. I think if I knew how to use MMAP, that'd the the idea solution.

The problem with detecting when the buffer is empty is that, say at second 15, I shoot an enemy ship, so a sound is executed, which lasts two seconds, but then, at second 16, the ship shoots at me while the other sound is only half-executed. Then I am forced to stop current sound execution and start over with a new buffer that combines the second sound and half the first. This is very complex and besides, the interruptions is bound to create a click. Also, there's no chance I will perfectly calculate the sample at which the first sound was interrupted.

I've been trying to run the MMAP, but I'm getting an error "-77" when I execute snd_pcm_mmap_begin. I have no idea what that error means.
xlucas
Posts: 334
Joined: May 09, 2014 21:19
Location: Argentina

Re: Streaming audio

Post by xlucas »

Well, I solved the problem in a non-conventional way. In case anybody would be interested, I'll explain how:

Since I couldn't find a good example on how to use MMAP and I'm not very sure if it was what I wanted anyway, I thought of what would happen if instead of opening one PCM handle, I had many. I tried with two and it worked. I tried opening 20 and it still was OK, so I used the handles as "channels" and kept track of which channels were busy playing a sound and which were available. The result is that I can't manually add an infinite number of sounds and saturate them like I wanted, but I can achieve virtually the same by playing them in parallel.
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

Sorry, I forgot... here is the new version, by the way:

Code: Select all

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
"SoundQueue" returns the number of samples in queue.
xlucas
Posts: 334
Joined: May 09, 2014 21:19
Location: Argentina

Re: Streaming audio

Post by xlucas »

Thank you, Angros. I've downloaded it and I'll take a look at the changes. I think it will be very useful to me, because I'm very new with ALSA.
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

This version should work on the most recent version of Ubuntu (that, for some reason, has no more file "libasound.so", but just "libasound.so.2"), and on 64 bit systems.

Code: Select all

Dim shared As Any Ptr alsa
dim shared as integer OSS


'ALSA declarations
alsa = DyLibLoad("asound")
if alsa=0 then alsa = DyLibLoad("libasound.so.2")

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 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 As snd_pcm_t Ptr           hDevice
Dim Shared As snd_pcm_hw_params_t Ptr hw
Dim Shared As LONG                 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 unsigned long,byval io_cmd as unsigned long,byval lpArg as long ptr) as long


#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, LONG)
#define SNDCTL_DSP_STEREO         _IOWR(asc("P"), 3, LONG)
#define SNDCTL_DSP_GETBLKSIZE     _IOWR(asc("P"), 4, LONG)
#define SNDCTL_DSP_SETFMT         _IOWR(asc("P"), 5, LONG) ' Selects ONE fmt
#define SNDCTL_DSP_CHANNELS       _IOWR(asc("P"), 6, LONG)
#define SOUND_PCM_WRITE_CHANNELS  SNDCTL_DSP_CHANNELS
#define SOUND_PCM_WRITE_FILTER    _IOWR(asc("P"), 7, LONG)
#define SNDCTL_DSP_POST           _IO  (asc("P"), 8)
#define SNDCTL_DSP_SUBDIVIDE      _IOWR(asc("P"), 9, LONG)
#define SNDCTL_DSP_SETFRAGMENT    _IOWR(asc("P"),10, LONG)
#define SNDCTL_DSP_GETFMTS        _IOR (asc("P"),11, LONG) ' Returns a mask
#define SNDCTL_DSP_SAMPLESIZE     SNDCTL_DSP_SETFMT
#define SNDCTL_DSP_GETODELAY	_IOR (asc("P"), 23, LONG)


'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 long, channels as long, bits as long)
	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 long 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 long)
	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 long
		snd_pcm_delay(hDevice, @value)
		return value*Periodsize
	elseif OSS>0 then
		dim value as long
		ioctl fileno(cast (any ptr,fileattr(1,2))), SNDCTL_DSP_GETODELAY, @value
		return value
	end if
end function
xlucas
Posts: 334
Joined: May 09, 2014 21:19
Location: Argentina

Re: Streaming audio

Post by xlucas »

Thank, Angros. Why would the previous version not work in 64bit?
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

Because it should use LONG (32 bit integer) to interface with C libraries, instead of 64 bit integers
greenink
Posts: 200
Joined: Jan 28, 2016 15:45

Re: Streaming audio

Post by greenink »

I want to write some ham radio software to decode PSK31. I kinda want to write it in Java for portability. However that would mean spending a considerable amount of time doing a JavaFX GUI. The other option is a quick piece of code using FB. Unfortunately /dev/dsp is being removed from the Linux kernel, which takes away an easy option for me. But I see in the above code there is some way to link to the ALSA library. I just need to be able to capture single channel 16 bit audio at 8000 samples a second or more. What's the simple way to do that?
greenink
Posts: 200
Joined: Jan 28, 2016 15:45

Re: Streaming audio

Post by greenink »

It seems my cheap laptop only allows 8 bit audio through arecord. That is probably a hardware limitation.

Code: Select all

screenres 512,256,32
dim as ubyte s,v(511)
dim as ulong i
var ff=freefile()
Open Pipe "arecord -q -f U8" For Input As #ff

Do While Not EOF(ff) and inkey()<>chr(27)
   get #ff,, s
   pset (i,255-v(i)),0
   pset (i,255-s)
   v(i)=s
   i=(i+1) and 511
Loop
Close #ff
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

If you want to use "/dev/dsp" on a slinux system that does not support it, you can launch your program using "padsp ./yourprogram". This will emulate the old system.
greenink
Posts: 200
Joined: Jan 28, 2016 15:45

Re: Streaming audio

Post by greenink »

Thanks. I'll do the code in FB then.
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

This version works even for amount of data larger than Alsa buffer size

Code: Select all

Dim shared As Any Ptr alsa
dim shared as integer OSS


'ALSA declarations

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 LONG) As Zstring Ptr

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

Dim Shared snd_pcm_close as Function ( _
Byval pcm          As snd_pcm_t) As LONG

Dim Shared snd_pcm_start as Function ( _
Byval pcm          As snd_pcm_t) As LONG

Dim Shared snd_pcm_drain as Function ( _
Byval pcm          As snd_pcm_t) As LONG

Dim Shared snd_pcm_hw_free as Function ( _
Byval pcm          As snd_pcm_t) As LONG

Dim Shared snd_pcm_nonblock as Function ( _
Byval pcm          As snd_pcm_t, _
Byval nonblock     As LONG) As LONG

Dim Shared snd_pcm_prepare as Function ( _
Byval pcm          As snd_pcm_t) As LONG

Dim Shared snd_pcm_writei as Function ( _
Byval pcm          As snd_pcm_t, _
Byval buffer       As Any Ptr, _
Byval size         As LONG) As LONG

Dim Shared snd_pcm_recover as Function ( _
Byval pcm          As snd_pcm_t, _
Byval err          As LONG, _
Byval silent       As LONG) As LONG

Dim Shared snd_pcm_avail_update as Function ( _
Byval pcm          As snd_pcm_t) As LONG

Dim Shared snd_pcm_avail as Function ( _
Byval pcm          As snd_pcm_t) As LONG

Dim Shared snd_pcm_delay as Function ( _
Byval pcm          As snd_pcm_t, _
Byval delayp       As snd_pcm_t) As LONG

Dim Shared snd_pcm_wait as Function ( _
Byval pcm          As snd_pcm_t, _
Byval msec As LONG) As LONG

Dim Shared snd_pcm_resume as Function ( _
Byval pcm          As snd_pcm_t) As LONG

'hardware
Dim Shared snd_pcm_hw_params_malloc as Function ( _
Byval hw           As snd_pcm_hw_params_t Ptr) As LONG

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

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

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

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

Dim Shared snd_pcm_hw_params_get_channels as Function ( _
Byval hw           As snd_pcm_hw_params_t, _
Byval lpChannels   As ULONG Ptr) As LONG

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 ULONG Ptr, _
Byval lpDir        As LONG Ptr) As LONG


Dim Shared snd_pcm_hw_params_get_periods as Function ( _
Byval hw           As snd_pcm_hw_params_t, _
Byval lpValue      As ULONG Ptr, _
Byval lpDir        As LONG Ptr) As LONG

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 ULONG Ptr, _
Byval lpDir        As LONG Ptr) As LONG

Dim Shared snd_pcm_hw_params_get_period_size as Function ( _
Byval params       As snd_pcm_hw_params_t, _
Byval lpFrames     As Any Ptr, _
Byval lpDir        As LONG Ptr) As LONG

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 Any Ptr, _
Byval lpDir        As LONG Ptr) As LONG

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 Any Ptr) As LONG

Dim Shared snd_pcm_hw_params_get_buffer_size as Function ( _
Byval hw           As snd_pcm_hw_params_t, _
Byval lpFrames     As Any Ptr) As LONG

Dim Shared snd_pcm_hw_params as Function ( _
Byval pcm          As snd_pcm_t, _
Byval hw           As snd_pcm_hw_params_t) As LONG

Dim Shared snd_pcm_hw_params_free as Sub ( _
Byval hw           As snd_pcm_hw_params_t)
     

Dim Shared As snd_pcm_t Ptr           hDevice
Dim Shared As snd_pcm_hw_params_t Ptr hw
Dim Shared As LONG                 ret,value,direction,buffer_size,SampleSize
Dim Shared As Integer		   nFrames
Dim Shared As Zstring Ptr             strRet


'OSS declarations

#include "file.bi"


declare function fileno cdecl alias "fileno" (byval as any ptr) as integer
declare function ioctl cdecl alias "ioctl" (byval hDevice as unsigned long,byval io_cmd as unsigned long,byval lpArg as long ptr) as long


#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, LONG)
#define SNDCTL_DSP_STEREO         _IOWR(asc("P"), 3, LONG)
#define SNDCTL_DSP_GETBLKSIZE     _IOWR(asc("P"), 4, LONG)
#define SNDCTL_DSP_SETFMT         _IOWR(asc("P"), 5, LONG) ' Selects ONE fmt
#define SNDCTL_DSP_CHANNELS       _IOWR(asc("P"), 6, LONG)
#define SOUND_PCM_WRITE_CHANNELS  SNDCTL_DSP_CHANNELS
#define SOUND_PCM_WRITE_FILTER    _IOWR(asc("P"), 7, LONG)
#define SNDCTL_DSP_POST           _IO  (asc("P"), 8)
#define SNDCTL_DSP_SUBDIVIDE      _IOWR(asc("P"), 9, LONG)
#define SNDCTL_DSP_SETFRAGMENT    _IOWR(asc("P"),10, LONG)
#define SNDCTL_DSP_GETFMTS        _IOR (asc("P"),11, LONG) ' Returns a mask
#define SNDCTL_DSP_SAMPLESIZE     SNDCTL_DSP_SETFMT
#define SNDCTL_DSP_GETODELAY	_IOR (asc("P"), 23, LONG)


'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 dsp_init()
	alsa = DyLibLoad("asound")
	if alsa=0 then alsa = DyLibLoad("libasound.so.2")

	if alsa<>0 then
		snd_strerror= DyLibSymbol(alsa, "snd_strerror")
		snd_pcm_open= DyLibSymbol(alsa, "snd_pcm_open")
		snd_pcm_close= DyLibSymbol(alsa, "snd_pcm_close")
		snd_pcm_start= DyLibSymbol(alsa, "snd_pcm_start")
		snd_pcm_drain= DyLibSymbol(alsa, "snd_pcm_drain")
		snd_pcm_hw_free= DyLibSymbol(alsa, "snd_pcm_hw_free")
		snd_pcm_nonblock= DyLibSymbol(alsa, "snd_pcm_nonblock")
		snd_pcm_prepare= DyLibSymbol(alsa, "snd_pcm_prepare")
		snd_pcm_writei= DyLibSymbol(alsa, "snd_pcm_writei")
		snd_pcm_recover= DyLibSymbol(alsa, "snd_pcm_recover")
		snd_pcm_avail_update= DyLibSymbol(alsa, "snd_pcm_avail_update")
		snd_pcm_avail= DyLibSymbol(alsa, "snd_pcm_avail")
		snd_pcm_delay= DyLibSymbol(alsa, "snd_pcm_delay")
		snd_pcm_wait= DyLibSymbol(alsa, "snd_pcm_wait")
		snd_pcm_resume= DyLibSymbol(alsa, "snd_pcm_resume")
		snd_pcm_hw_params_malloc= DyLibSymbol(alsa, "snd_pcm_hw_params_malloc")
		snd_pcm_hw_params_any= DyLibSymbol(alsa, "snd_pcm_hw_params_any")
		snd_pcm_hw_params_set_access= DyLibSymbol(alsa, "snd_pcm_hw_params_set_access")
		snd_pcm_hw_params_set_format= DyLibSymbol(alsa, "snd_pcm_hw_params_set_format")
		snd_pcm_hw_params_set_channels= DyLibSymbol(alsa, "snd_pcm_hw_params_set_channels")
		snd_pcm_hw_params_get_channels= DyLibSymbol(alsa, "snd_pcm_hw_params_get_channels")
		snd_pcm_hw_params_set_rate_near= DyLibSymbol(alsa, "snd_pcm_hw_params_set_rate_near")
		snd_pcm_hw_params_get_periods= DyLibSymbol(alsa, "snd_pcm_hw_params_get_periods")
		snd_pcm_hw_params_set_periods_near= DyLibSymbol(alsa, "snd_pcm_hw_params_set_periods_near")
		snd_pcm_hw_params_get_period_size= DyLibSymbol(alsa, "snd_pcm_hw_params_get_period_size")
		snd_pcm_hw_params_set_period_size_near= DyLibSymbol(alsa, "snd_pcm_hw_params_set_period_size_near")
		snd_pcm_hw_params_set_buffer_size_near= DyLibSymbol(alsa, "snd_pcm_hw_params_set_buffer_size_near")
		snd_pcm_hw_params_get_buffer_size= DyLibSymbol(alsa, "snd_pcm_hw_params_get_buffer_size")
		snd_pcm_hw_params= DyLibSymbol(alsa, "snd_pcm_hw_params")
		snd_pcm_hw_params_free= DyLibSymbol(alsa, "snd_pcm_hw_params_free")
	end if

	OSS=FileExists("/dev/dsp")

end sub




sub SoundSet(frequency as long, channels as long, bits as long)
	if alsa=0 andalso OSS=0 then dsp_init

	if alsa then
		if bits=16 then SampleSize=2 else SampleSize=1
		SampleSize*=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)
		ret = snd_pcm_hw_params_get_buffer_size(hw, @nFrames)
		nFrames/=SampleSize

		snd_pcm_hw_params_free (hw)
		snd_pcm_prepare(hDevice)

		buffer_size=snd_pcm_avail(hDevice)
	elseif OSS then
		if OSS>0 then close OSS
		OSS=FreeFile

		open "/dev/dsp" for output as #1

		dim as long 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 long)
	if alsa then
		dim Count as long
		buffersize/=SampleSize
		Count=buffersize
		if buffersize>nFrames then buffersize=nFrames
		do while Count>0 
			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) else exit do
		
			end if
			count -=ret
			soundBuffer += ret*SampleSize
		loop
	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 long
		value=snd_pcm_avail(hDevice)
		if value<0 then 
			return 0
		else
			return (buffer_size-value)*SampleSize
		end if
		'snd_pcm_delay(hDevice, @value)
		'return value*SampleSize
	elseif OSS>0 then
		dim value as long
		ioctl fileno(cast (any ptr,fileattr(1,2))), SNDCTL_DSP_GETODELAY, @value
		return value
	end if
end function

Post Reply