My library supports mixing two sound channels, actually (and it also allows to play two different sounds on right and left channel). It does not support 3d sound, because 3d sound is actually an abstraction, that is rendered in different ways depending on the available hardware: if the audio output is mono, 3D sounds is nothing more than distance attenuation, if the audio output is stereo, panning can be adjusted according to the sound source relative position. But if the audio is played using a surround system, a good 3d sound system must be able to detect it, and use the most appropriate speaker to simulate the right direction sound will come from. On the other hand, if headphones are used, a 3d sound system should use Head Related Transfer Function (HTRF), to produce a real binaural sound. So, the right method to produce a sound should be decided according on the available hardware, and for that reason 3d sound should use the integrated driver (just as 3d graphic could be rendered from FreeBasic, but it's better to use drivers like Direct3D or OpenGL)
About the suggestion to use two or more buffers, and swap them, to provide seamless audio output (in a way similar of how the "flip" command works for graphic), I would invite you to look at the DMA.BAS file in the dos subdirectory, and at the DSP.BAS file in the windows subdirectory: you will see that it's exactly what my library already does.
Audio library for FreeBasic - Features
Re: Audio library for FreeBasic - Features
angros47 wrote:Designing an API is likely harder than implementing it, since any future change will break a lot of code.
I looked through some of my codes. I regret not having finished and published more. I think I have tried fbsound, sdl, and fmod sound libraries. My API tends to look something like this:
Code: Select all
#ifndef __SOUND_BI__INCLUDE__
#define __SOUND_BI__INCLUDE__
declare function AUDIO_Init() as integer
declare function AUDIO_Exit() as integer
declare sub SOUND_Stop( byval channel as const integer = -1 )
declare sub SOUND_Pause( byval channel as const integer = -1 )
declare sub SOUND_Resume( byval channel as const integer = -1 )
declare function SOUND_Load( byref filename as const string ) as integer
declare function SOUND_Play _
( _
byref filename as const string, _
byval channel as const integer = -1, _
byval loops as const integer = 0, _
byval volume as integer = -1 _
) as integer
declare sub SOUND_Volume( byval volume as integer, byval channel as integer = -1 )
declare sub MUSIC_Stop()
declare sub MUSIC_Pause()
declare sub MUSIC_Resume()
declare function MUSIC_Load( byref filename as const string ) as integer
declare function MUSIC_Play _
( _
byref filename as const string _
) as integer
declare sub MUSIC_Volume( byval volume as integer )
#endif
The major commonality is:
1) loading resources from files
2) channels for each sound
For the simple stuff I have tried, this works for me. I have not bothered with namespaces as I am only consumer (currently) of the code. The idea though is that the API stays the same and some module handles the interaction with the actual sound library to be used.
Re: Audio library for FreeBasic - Features
For what is worth, I abstracted the SDL2 mixer like this, when I was coding my entry for Lachies' compo:
Needless to say, angros47 lib can also be easily accomodated ;)
Code: Select all
#include once "engine/engine.bi"
namespace SDL2
/'
Music sample resource
'/
class _
MusicSample _
implements Engine.IResource
public:
declare constructor( _
byref as const Engine.Identifier, _
byref as const string )
declare destructor() override
declare operator _
cast() as mix_music ptr
declare function _
load() as MusicSample ptr override
protected:
declare constructor()
as mix_music ptr _
m_sample
as string _
m_fileName
as boolean _
m_loadFromFile
end class
constructor _
MusicSample()
end constructor
constructor _
MusicSample( _
byref anID as const Engine.Identifier, _
byref aFileName as const string )
base( anID )
if( fileExists( aFileName ) ) then
isAvailable()
m_fileName = aFileName
m_loadFromFile = true
end if
end constructor
destructor _
MusicSample()
mix_FreeMusic( m_sample )
end destructor
operator _
MusicSample.cast() _
as mix_music ptr
return( m_sample )
end operator
function _
MusicSample.load() _
as MusicSample ptr
if( available ) then
if( m_loadFromFile ) then
'' Load from file
m_sample = mix_LoadMUS( m_fileName )
if( m_sample = Null ) then
'' Sample not available
isUnavailable()
end if
else
'' Load from memory
end if
else
'' Sample is available for playing
isAvailable()
end if
return( @this )
end function
/'
Sound sample resource
'/
class _
SoundSample _
implements Engine.IResource
public:
declare constructor( _
byref as const Engine.Identifier, _
byref as const string )
declare destructor() override
declare operator _
cast() as mix_chunk ptr
declare function _
load() as SoundSample ptr override
protected:
declare constructor()
as mix_chunk ptr _
m_sample
as string _
m_fileName
as boolean _
m_loadFromFile
end class
constructor _
SoundSample()
end constructor
constructor _
SoundSample( _
byref anID as const Engine.Identifier, _
byref aFileName as const string )
base( anID )
if( fileExists( aFileName ) ) then
isAvailable()
m_fileName = aFileName
m_loadFromFile = true
else
isUnavailable()
end if
end constructor
destructor _
SoundSample()
mix_freeChunk( m_sample )
end destructor
operator _
SoundSample.cast() _
as mix_chunk ptr
return( m_sample )
end operator
function _
SoundSample.load() _
as SoundSample ptr
if( available ) then
if( m_loadFromFile ) then
'' Load from file
m_sample = mix_loadWAV( m_fileName )
if( m_sample = Null ) then
'' Could not load sound
isUnavailable()
end if
else
'' TODO: Load from memory
end if
else
'' Sound is unavailable to play
isUnavailable()
end if
return( @this )
end function
class _
SoundManager _
implements Engine.IObject
public:
declare constructor()
declare destructor()
declare property _
available() as boolean
declare property _
samplingRate() as long
declare property _
channels() as long
declare property _
audioFormat() as Uint16
declare property _
bufferSize() as long
declare property _
musicVolume() as Math.float
declare property _
channelVolume( _
byval as integer ) _
as Math.float
declare function _
play( _
byval as MusicSample ptr ) _
as SoundManager ptr
declare function _
play( _
byval as SoundSample ptr ) _
as SoundManager ptr
declare function _
setMusicVolume( _
byval as Math.float ) _
as SoundManager ptr
declare function _
setChannelVolume( _
byval as integer, _
byval as Math.float ) _
as SoundManager ptr
declare function _
setAllChannelsVolume( _
byval as Math.float ) _
as SoundManager ptr
private:
as long _
m_samplingRate, _
m_channels, _
m_bufferSize
as Uint16 _
m_audioFormat
as boolean _
m_available
as Math.float _
m_musicVolume, _
m_channelVolume( any )
as integer _
m_soundChannels, _
m_currentSoundChannel, _
m_reservedChannels, _
m_channelGroups, _
m_currentChannelGroup
end class
constructor _
SoundManager()
m_samplingRate = 44100
m_audioFormat = MIX_DEFAULT_FORMAT
m_channels = 2
m_bufferSize = 4096
if( mix_OpenAudio( _
m_samplingRate, _
m_audioFormat, _
m_channels, _
m_bufferSize ) = Null ) then
m_available = false
else
mix_QuerySpec( _
@m_samplingRate, _
@m_audioFormat, _
@m_channels )
m_available = true
end if
m_soundChannels = 32
mix_allocateChannels( m_soundChannels )
redim _
m_channelVolume( 0 to m_soundChannels - 1 )
m_reservedChannels = m_soundChannels
mix_reserveChannels( m_reservedChannels )
m_channelGroups = 24
'' Interleave the channels in different channel groups to
'' have the maximum possible sound effects playing at once
for _
i as integer = 0 _
to m_soundChannels - 1
mix_groupChannel( i, i mod m_channelGroups )
m_channelVolume( i ) = 1.0
next
setMusicVolume( 1.0 )
end constructor
destructor _
SoundManager()
mix_haltMusic()
mix_AllocateChannels( 0 )
end destructor
property _
SoundManager.available() _
as boolean
return( m_available )
end property
property _
SoundManager.samplingRate() _
as long
return( m_samplingRate )
end property
property _
SoundManager.audioFormat() _
as Uint16
return( m_audioFormat )
end property
property _
SoundManager.channels() _
as long
return( m_channels )
end property
property _
SoundManager.bufferSize() _
as long
return( m_bufferSize )
end property
property _
SoundManager.musicVolume() _
as Math.float
return( m_musicVolume )
end property
property _
SoundManager.channelVolume( _
byval index as integer ) _
as Math.float
return( m_channelVolume( index ) )
end property
function _
SoundManager.play( _
byval aMusicSample as MusicSample ptr ) _
as SoundManager ptr
if( aMusicSample->available ) then
mix_playMusic( *aMusicSample, -1 )
end if
return( @this )
end function
function _
SoundManager.play( _
byval aSoundSample as SoundSample ptr ) _
as SoundManager ptr
if( aSoundSample->available ) then
if( _
not cbool( mix_playing( m_currentSoundChannel ) ) ) then
mix_playChannel( _
m_currentSoundChannel, _
*aSoundSample, _
0 )
end if
m_currentSoundChannel = _
( m_currentSoundChannel + 1 ) mod m_soundChannels
else
'? "Not available!"
end if
return( @this )
end function
function _
SoundManager.setMusicVolume( _
byval volume as Math.float ) _
as SoundManager ptr
m_musicVolume = Math.clamp( _
volume, 0.0, 1.0 )
mix_volumeMusic( m_musicVolume * MIX_MAX_VOLUME )
return( @this )
end function
function _
SoundManager.setChannelVolume( _
byval index as integer, _
byval volume as Math.float ) _
as SoundManager ptr
volume = Math.clamp( _
volume, 0.0, 1.0 )
m_channelVolume( index ) = volume
mix_volume( _
m_channelVolume( index ), _
volume * MIX_MAX_VOLUME )
return( @this )
end function
function _
SoundManager.setAllChannelsVolume( _
byval volume as Math.float ) _
as SoundManager ptr
volume = Math.clamp( _
volume, 0.0, 1.0 )
for _
i as integer = 0 _
to m_soundChannels - 1
m_channelVolume( i ) = volume
next
mix_volume( _
-1, _
volume * MIX_MAX_VOLUME )
return( @this )
end function
end namespace
/'
Main test
'/
using Engine
using SDL2
SDL_Init( SDL_INIT_AUDIO )
/'
To load samples, a SoundManager needs to be created first, and the
SoundManager assumes that all the setup needed has been previously
done when initializing the program.
'/
var _
sm = new SoundManager()
'' Create and load samples
var _
ms1 = new MusicSample( _
"AngryAngus", _
"Angry_Angus.mp3" )->load(), _
ms2 = new MusicSample( _
"BarStoolsHurt", _
"Bar_Stools_Hurt_Man.mp3" )->load(), _
ss1 = new SoundSample( _
"bumperhit", _
"data/snd/bumper.wav" )->load(), _
ss2 = new SoundSample( _
"flipperactivate", _
"data/snd/flipper.wav" )->load()
dim as boolean _
done = false
dim as string _
k
do while( not done )
k = inkey()
if( k = chr( 27 ) ) then
done = true
end if
if( k = "1" ) then
sm->play( ms1 )
end if
if( k = "2" ) then
sm->play( ms2 )
end if
if( k = "3" ) then
sm->setMusicVolume( sm->musicVolume + 0.1 )
end if
if( k = "4" ) then
sm->setMusicVolume( sm->musicVolume - 0.1 )
end if
if( k = "5" ) then
sm->play( ss1 )
end if
if( k = "6" ) then
sm->play( ss2 )
end if
if( k = "7" ) then
sm->setAllChannelsVolume( 0.2 )
end if
if( k = "8" ) then
sm->setAllChannelsVolume( 1.0 )
end if
sleep( 1, 1 )
loop
delete( ms1 )
delete( ms2 )
delete( ss1 )
delete( ss2 )
delete( sm )
SDL_Quit()
Needless to say, angros47 lib can also be easily accomodated ;)
Return to “Community Discussion”
Who is online
Users browsing this forum: No registered users and 9 guests