Streaming audio

Windows specific questions.
Post Reply
angros47
Posts: 2323
Joined: Jun 21, 2005 19:04

Streaming audio

Post by angros47 »

I converted it from a C source called EASYWAVE:

Code: Select all

#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
You can create a sound with SoundSet rate, no_channels, bits (so, you can have mono or stereo, 8 or 16 bit audio); then, you use the command PlayBuffer data_pointer, no_bytes to send raw sound data, that will be queued and played.

Here is an example:

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
angros47
Posts: 2323
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

I made also a linux version: http://freebasic.net/forum/viewtopic.php?f=5&t=23122

And here is another example:

Code: Select all

#DEFINE MusicFunction (i shr 6 or i or i shr (i shr 16))*10+((i shr 11) and 7)

Dim Mybuffer(1000) as ubyte

SoundSet 8000,1,8

dim as integer i

do until multikey(1)
	MyBuffer(i mod 1000)=MusicFunction
	i+=1
	if i mod 1000=999 then playbuffer @MyBuffer(0),1000:sleep 1
loop

sleep


CrisDEV
Posts: 37
Joined: Jun 07, 2016 18:51
Location: Brasil - Sorocaba -SP

Re: Streaming audio

Post by CrisDEV »

This is fantastic !!!!
I like the music from the second example. Chiptunes music !!!!

Now I will study how to play *.wav and *.ogg
CrisDEV
Posts: 37
Joined: Jun 07, 2016 18:51
Location: Brasil - Sorocaba -SP

Re: Streaming audio

Post by CrisDEV »

Finaly I have discovered the simplest way to play a WAV music.

Code: Select all

#include once "windows.bi"
#include once "win/mmsystem.bi"  'here have the function to play music.
 
   sndPlaySoundA("teste.wav", 1) '1 to play asynchronously

print "Press any key to exit ...!"
while inkey()=""
  sleep 100
wend
I spend 3 days searching in this forum and nothing. I have find in Google something in Visual Basic and search in the "mmsystem.bi" the function.
And I'am very happy!!!!
angros47
Posts: 2323
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

A simple question: can anyone tell me if my code works in windows 64 bit, too?
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Streaming audio

Post by dodicat »

Win 10
64 bits
line 4
#define wavemapper -1 (wave_mapper seems to clash)
Line 22
Lne 71

I have written (here) above the three tweaks.

I get a terrible screeching, like a banshee, the cat is terrified.
System:
FBIde: 0.4.6
fbc: FreeBASIC Compiler - Version 1.05.0 (01-31-2016), built for win64 (64bit)
OS: Windows NT 6.2 (build 9200)

Tweaked code:

Code: Select all

#include "Windows.bi"
#Include Once "crt/string.bi"
#include "win/mmsystem.bi"
'         here
#define Wavemapper -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
      '                          here
      p = cast(QueueWAVEHDR ptr, @p1)
      '#print typeof(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
   
   '                                           here 
   if waveOutOpen(@_hWaveOut, Wavemapper, @wfx, 0, 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 
angros47
Posts: 2323
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

Thanks, but, by doing that, you removed the callback control, so now it would not work any more.

A question: my original code would compile, if you replace all the "dword" with "unsigned integer"?
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Streaming audio

Post by dodicat »

Yes, uinteger does the job win 10, 64 bit.

#define WAVEMAPPER -1
instead of
WAVE_MAPPER, which is defined already somewhere.
Runs with no warnings or hiccups
DamageX
Posts: 130
Joined: Nov 21, 2009 8:42

Re: Streaming audio

Post by DamageX »

CrisDEV wrote:Finaly I have discovered the simplest way to play a WAV music.
Yes! Playing WAV data from memory buffer is also similar. The only thing needed from includes is a single declaration. Why didn't anybody tell me it was this easy?

Code: Select all

' Windows PCM audio demo
' take care not to blow up your speaker

declare function sndPlaySound lib "winmm" alias "sndPlaySoundA" (byval as any ptr, byval as uinteger) as integer

dim as integer x
dim shared sndbuf(0 to &H40015) as short => _
{ &H4952,&H4646,&H0024,&H0008,&H4157,&H4556,&H6D66,&H2074, _
  &H0010,&H0000,&H0001,&H0001,&HAC44,&H0000,&H5888,&H0001, _
  &H0002,&H0010,&H6164,&H6174,&H0000,&H0008 }

for x=22 to &H40015
sndbuf(x)=((x and 127) shl ((x shr 12) and 15))
next x

x=sndPlaySound(@sndbuf(0),5) 

print "playing..."
sleep 10000
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Streaming audio

Post by MrSwiss »

angros47 wrote:A question: my original code would compile, if you replace all the "dword" with "unsigned integer"?
No, I'd use ULong instead, because we are dealing with a "unsigned 32-bit integer", aka: C's UINT,
which in FB, is ULong (fixed data type = 32-bit) on all platforms: 32/64 bit's.

Alternative: UInteger<32> (but, to much typing for me!)
angros47
Posts: 2323
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

MrSwiss wrote:No, I'd use ULong instead, because we are dealing with a "unsigned 32-bit integer", aka: C's UINT,
which in FB, is ULong (fixed data type = 32-bit) on all platforms: 32/64 bit's.
Have you tried on Windows 64 bit? Does it work? Because DWORD, too, is 32 bit... and it does not work (I have no windows 64, so I can't test it)
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: Streaming audio

Post by D.J.Peters »

angros47 it must be DWORD_PTR not DWORD !

Joshy

Code: Select all

#Include once "crt/string.bi"
#include once "Windows.bi"
#include once "win/mmsystem.bi"

#ifndef WAVE_MAPPER
 #define WAVE_MAPPER -1
#endif

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_PTR, _
                          p1 as DWORD_PTR, _
                          p2 as DWORD_PTR)
   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_PTR, @dsp_callback), _
                  cptr(DWORD_PTR, 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
angros47
Posts: 2323
Joined: Jun 21, 2005 19:04

Re: Streaming audio

Post by angros47 »

Thank you!
Post Reply