mirror of https://github.com/AxioDL/boo.git
Initial audio interface classes
This commit is contained in:
parent
be37b22dd5
commit
43376ff416
|
@ -32,7 +32,8 @@ if(WIN32)
|
||||||
lib/inputdev/HIDListenerWinUSB.cpp
|
lib/inputdev/HIDListenerWinUSB.cpp
|
||||||
lib/inputdev/HIDDeviceWinUSB.cpp
|
lib/inputdev/HIDDeviceWinUSB.cpp
|
||||||
lib/graphicsdev/D3D11.cpp
|
lib/graphicsdev/D3D11.cpp
|
||||||
lib/graphicsdev/D3D12.cpp)
|
lib/graphicsdev/D3D12.cpp
|
||||||
|
lib/audiodev/XAudio2.cpp)
|
||||||
|
|
||||||
list(APPEND PLAT_HDRS
|
list(APPEND PLAT_HDRS
|
||||||
include/boo/graphicsdev/D3D.hpp)
|
include/boo/graphicsdev/D3D.hpp)
|
||||||
|
@ -47,8 +48,9 @@ elseif(APPLE)
|
||||||
lib/mac/CocoaCommon.hpp
|
lib/mac/CocoaCommon.hpp
|
||||||
lib/inputdev/HIDListenerIOKit.cpp
|
lib/inputdev/HIDListenerIOKit.cpp
|
||||||
lib/inputdev/HIDDeviceIOKit.cpp
|
lib/inputdev/HIDDeviceIOKit.cpp
|
||||||
lib/graphicsdev/Metal.mm)
|
lib/graphicsdev/Metal.mm
|
||||||
set_source_files_properties(lib/mac/ApplicationCocoa.mm
|
lib/audiodev/AQS.cpp)
|
||||||
|
set_source_files_properties(lib/mac/ApplicationCocoa.mm
|
||||||
lib/mac/WindowCocoa.mm
|
lib/mac/WindowCocoa.mm
|
||||||
lib/graphicsdev/Metal.mm
|
lib/graphicsdev/Metal.mm
|
||||||
PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
|
@ -78,7 +80,8 @@ else(NOT GEKKO)
|
||||||
lib/inputdev/HIDDeviceUdev.cpp
|
lib/inputdev/HIDDeviceUdev.cpp
|
||||||
lib/graphicsdev/GL.cpp
|
lib/graphicsdev/GL.cpp
|
||||||
lib/graphicsdev/GLX.cpp
|
lib/graphicsdev/GLX.cpp
|
||||||
lib/graphicsdev/glew.c)
|
lib/graphicsdev/glew.c
|
||||||
|
lib/audiodev/ALSA.cpp)
|
||||||
|
|
||||||
# list(APPEND PLAT_HDRS )
|
# list(APPEND PLAT_HDRS )
|
||||||
|
|
||||||
|
@ -156,6 +159,8 @@ add_library(Boo
|
||||||
include/boo/IGraphicsContext.hpp
|
include/boo/IGraphicsContext.hpp
|
||||||
include/boo/graphicsdev/IGraphicsDataFactory.hpp
|
include/boo/graphicsdev/IGraphicsDataFactory.hpp
|
||||||
include/boo/graphicsdev/IGraphicsCommandQueue.hpp
|
include/boo/graphicsdev/IGraphicsCommandQueue.hpp
|
||||||
|
include/boo/audiodev/IAudioVoice.hpp
|
||||||
|
include/boo/audiodev/IAudioVoiceAllocator.hpp
|
||||||
include/boo/IWindow.hpp
|
include/boo/IWindow.hpp
|
||||||
include/boo/IApplication.hpp
|
include/boo/IApplication.hpp
|
||||||
include/boo/System.hpp
|
include/boo/System.hpp
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef BOO_AUDIOMATRIX_HPP
|
||||||
|
#define BOO_AUDIOMATRIX_HPP
|
||||||
|
|
||||||
|
#include "IAudioVoice.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
class AudioMatrixMono
|
||||||
|
{
|
||||||
|
AudioChannelSet m_setOut;
|
||||||
|
float m_coefs[8];
|
||||||
|
std::vector<int16_t> m_interleaveBuf;
|
||||||
|
public:
|
||||||
|
AudioMatrixMono(AudioChannelSet setOut)
|
||||||
|
: m_setOut(setOut) {setDefaultMatrixCoefficients();}
|
||||||
|
|
||||||
|
AudioChannelSet setOut() const {return m_setOut;}
|
||||||
|
void setDefaultMatrixCoefficients();
|
||||||
|
void setMatrixCoefficients(const float coefs[8])
|
||||||
|
{
|
||||||
|
for (int i=0 ; i<8 ; ++i)
|
||||||
|
m_coefs[i] = coefs[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufferMonoSampleData(IAudioVoice& voice, const int16_t* data, size_t samples);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioMatrixStereo
|
||||||
|
{
|
||||||
|
AudioChannelSet m_setOut;
|
||||||
|
float m_coefs[8][2];
|
||||||
|
std::vector<int16_t> m_interleaveBuf;
|
||||||
|
public:
|
||||||
|
AudioMatrixStereo(AudioChannelSet setOut)
|
||||||
|
: m_setOut(setOut) {setDefaultMatrixCoefficients();}
|
||||||
|
|
||||||
|
AudioChannelSet setOut() const {return m_setOut;}
|
||||||
|
void setDefaultMatrixCoefficients();
|
||||||
|
void setMatrixCoefficients(const float coefs[8][2])
|
||||||
|
{
|
||||||
|
for (int i=0 ; i<8 ; ++i)
|
||||||
|
{
|
||||||
|
m_coefs[i][0] = coefs[i][0];
|
||||||
|
m_coefs[i][1] = coefs[i][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufferStereoSampleData(IAudioVoice& voice, const int16_t* data, size_t frames);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BOO_AUDIOMATRIX_HPP
|
|
@ -0,0 +1,64 @@
|
||||||
|
#ifndef BOO_IAUDIOVOICE_HPP
|
||||||
|
#define BOO_IAUDIOVOICE_HPP
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class AudioChannelSet
|
||||||
|
{
|
||||||
|
Stereo,
|
||||||
|
Quad,
|
||||||
|
Surround51,
|
||||||
|
Surround71
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AudioChannel
|
||||||
|
{
|
||||||
|
FrontLeft,
|
||||||
|
FrontRight,
|
||||||
|
RearLeft,
|
||||||
|
RearRight,
|
||||||
|
FrontCenter,
|
||||||
|
LFE,
|
||||||
|
SideLeft,
|
||||||
|
SideRight
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChannelMap
|
||||||
|
{
|
||||||
|
unsigned m_channelCount = 0;
|
||||||
|
AudioChannel m_channels[8] = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned ChannelCount(AudioChannelSet layout)
|
||||||
|
{
|
||||||
|
switch (layout)
|
||||||
|
{
|
||||||
|
case AudioChannelSet::Stereo:
|
||||||
|
return 2;
|
||||||
|
case AudioChannelSet::Quad:
|
||||||
|
return 4;
|
||||||
|
case AudioChannelSet::Surround51:
|
||||||
|
return 6;
|
||||||
|
case AudioChannelSet::Surround71:
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IAudioVoice
|
||||||
|
{
|
||||||
|
/** Get voice's actual channel-map based on client request and HW capabilities */
|
||||||
|
virtual const ChannelMap& channelMap() const=0;
|
||||||
|
|
||||||
|
/** Called by client in response to IAudioVoiceCallback::needsNextBuffer()
|
||||||
|
* Supplying channel-interleaved sample data */
|
||||||
|
virtual void bufferSampleData(const int16_t* data, size_t frames)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BOO_IAUDIOVOICE_HPP
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef BOO_IAUDIOVOICEALLOCATOR_HPP
|
||||||
|
#define BOO_IAUDIOVOICEALLOCATOR_HPP
|
||||||
|
|
||||||
|
#include "IAudioVoice.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
struct IAudioVoiceCallback
|
||||||
|
{
|
||||||
|
/** Boo calls this on behalf of the audio platform to request more audio
|
||||||
|
* frames from the client */
|
||||||
|
virtual void needsNextBuffer(IAudioVoice* voice, size_t frames)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IAudioVoiceAllocator
|
||||||
|
{
|
||||||
|
/** Client calls this to request allocation of new mixer-voice.
|
||||||
|
* Returns empty unique_ptr if necessary resources aren't available.
|
||||||
|
* ChannelLayout automatically reduces to maximum-supported layout by HW. */
|
||||||
|
virtual std::unique_ptr<IAudioVoice> allocateNewVoice(AudioChannelSet layoutOut,
|
||||||
|
unsigned sampleRate,
|
||||||
|
IAudioVoiceCallback* cb)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BOO_IAUDIOVOICEALLOCATOR_HPP
|
|
@ -0,0 +1,163 @@
|
||||||
|
#include "boo/audiodev/IAudioVoiceAllocator.hpp"
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <LogVisor/LogVisor.hpp>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
static LogVisor::LogModule Log("boo::ALSA");
|
||||||
|
|
||||||
|
struct ALSAAudioVoice : IAudioVoice
|
||||||
|
{
|
||||||
|
ChannelMap m_map;
|
||||||
|
IAudioVoiceCallback* m_cb;
|
||||||
|
snd_pcm_t* m_pcm = nullptr;
|
||||||
|
snd_async_handler_t* m_handler = nullptr;
|
||||||
|
snd_pcm_uframes_t m_bufSize;
|
||||||
|
snd_pcm_uframes_t m_periodSize;
|
||||||
|
|
||||||
|
const ChannelMap& channelMap() const {return m_map;}
|
||||||
|
|
||||||
|
static void Callback(snd_async_handler_t* handler)
|
||||||
|
{
|
||||||
|
ALSAAudioVoice* voice = static_cast<ALSAAudioVoice*>(snd_async_handler_get_callback_private(handler));
|
||||||
|
voice->m_cb->needsNextBuffer(voice, voice->m_periodSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALSAAudioVoice(AudioChannelSet set, unsigned sampleRate, IAudioVoiceCallback* cb)
|
||||||
|
: m_cb(cb)
|
||||||
|
{
|
||||||
|
if (snd_pcm_open(&m_pcm, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC) < 0)
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::Error, "unable to allocate ALSA voice");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned chCount = ChannelCount(set);
|
||||||
|
int err;
|
||||||
|
while ((err = snd_pcm_set_params(m_pcm, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||||
|
chCount, sampleRate, 1, 100000)) < 0)
|
||||||
|
{
|
||||||
|
if (set == AudioChannelSet::Stereo)
|
||||||
|
break;
|
||||||
|
set = AudioChannelSet(int(set) - 1);
|
||||||
|
chCount = ChannelCount(set);
|
||||||
|
}
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
snd_pcm_close(m_pcm);
|
||||||
|
m_pcm = nullptr;
|
||||||
|
Log.report(LogVisor::Error, "unable to set ALSA voice params");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_chmap_query_t** chmaps = snd_pcm_query_chmaps(m_pcm);
|
||||||
|
if (!chmaps)
|
||||||
|
{
|
||||||
|
snd_pcm_close(m_pcm);
|
||||||
|
m_pcm = nullptr;
|
||||||
|
Log.report(LogVisor::Error, "unable to query ALSA voice chmaps");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snd_pcm_chmap_t* foundChmap = nullptr;
|
||||||
|
for (snd_pcm_chmap_query_t** chmap = chmaps ; *chmap != nullptr ; ++chmap)
|
||||||
|
{
|
||||||
|
if ((*chmap)->map.channels == chCount)
|
||||||
|
{
|
||||||
|
snd_pcm_chmap_t* chm = &(*chmap)->map;
|
||||||
|
uint64_t chBits = 0;
|
||||||
|
for (int c=0 ; c<chm->channels ; ++c)
|
||||||
|
chBits |= 1 << chm->pos[c];
|
||||||
|
|
||||||
|
bool good = false;
|
||||||
|
switch (set)
|
||||||
|
{
|
||||||
|
case AudioChannelSet::Stereo:
|
||||||
|
if ((chBits & (1 << SND_CHMAP_FL)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_FR)) != 0)
|
||||||
|
good = true;
|
||||||
|
break;
|
||||||
|
case AudioChannelSet::Quad:
|
||||||
|
if ((chBits & (1 << SND_CHMAP_FL)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_FR)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_RL)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_RR)) != 0)
|
||||||
|
good = true;
|
||||||
|
break;
|
||||||
|
case AudioChannelSet::Surround51:
|
||||||
|
if ((chBits & (1 << SND_CHMAP_FL)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_FR)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_RL)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_RR)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_FC)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_LFE)) != 0)
|
||||||
|
good = true;
|
||||||
|
break;
|
||||||
|
case AudioChannelSet::Surround71:
|
||||||
|
if ((chBits & (1 << SND_CHMAP_FL)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_FR)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_RL)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_RR)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_FC)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_LFE)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_SL)) != 0 &&
|
||||||
|
(chBits & (1 << SND_CHMAP_SR)) != 0)
|
||||||
|
good = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (good)
|
||||||
|
{
|
||||||
|
foundChmap = chm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundChmap)
|
||||||
|
{
|
||||||
|
snd_pcm_close(m_pcm);
|
||||||
|
m_pcm = nullptr;
|
||||||
|
snd_pcm_free_chmaps(chmaps);
|
||||||
|
Log.report(LogVisor::Error, "unable to find matching ALSA voice chmap");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_map.m_channelCount = chCount;
|
||||||
|
for (int c=0 ; c<foundChmap->channels ; ++c)
|
||||||
|
m_map.m_channels[c] = AudioChannel(foundChmap->pos[c] - 3);
|
||||||
|
snd_pcm_set_chmap(m_pcm, foundChmap);
|
||||||
|
snd_pcm_free_chmaps(chmaps);
|
||||||
|
|
||||||
|
snd_async_add_pcm_handler(&m_handler, m_pcm, snd_async_callback_t(Callback), this);
|
||||||
|
snd_pcm_get_params(m_pcm, &m_bufSize, &m_periodSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ALSAAudioVoice()
|
||||||
|
{
|
||||||
|
if (m_handler)
|
||||||
|
snd_async_del_handler(m_handler);
|
||||||
|
if (m_pcm)
|
||||||
|
snd_pcm_close(m_pcm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufferSampleData(const int16_t* data, size_t frames)
|
||||||
|
{
|
||||||
|
if (m_pcm)
|
||||||
|
snd_pcm_writei(m_pcm, data, frames);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ALSAAudioVoiceAllocator : IAudioVoiceAllocator
|
||||||
|
{
|
||||||
|
std::unique_ptr<IAudioVoice> allocateNewVoice(AudioChannelSet layoutOut,
|
||||||
|
unsigned sampleRate,
|
||||||
|
IAudioVoiceCallback* cb)
|
||||||
|
{
|
||||||
|
ALSAAudioVoice* newVoice = new ALSAAudioVoice(layoutOut, sampleRate, cb);
|
||||||
|
std::unique_ptr<IAudioVoice> ret(newVoice);
|
||||||
|
if (!newVoice->m_pcm)
|
||||||
|
return {};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
#include "boo/audiodev/AudioMatrix.hpp"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
void AudioMatrixMono::setDefaultMatrixCoefficients()
|
||||||
|
{
|
||||||
|
memset(m_coefs, 0, sizeof(m_coefs));
|
||||||
|
switch (m_setOut)
|
||||||
|
{
|
||||||
|
case AudioChannelSet::Mono:
|
||||||
|
case AudioChannelSet::Stereo:
|
||||||
|
case AudioChannelSet::Quad:
|
||||||
|
m_coefs[AudioChannel::FrontLeft] = 1.0;
|
||||||
|
m_coefs[AudioChannel::FrontRight] = 1.0;
|
||||||
|
break;
|
||||||
|
case AudioChannelSet::Surround51:
|
||||||
|
case AudioChannelSet::Surround71:
|
||||||
|
m_coefs[AudioChannel::FrontCenter] = 1.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMatrixMono::bufferMonoSampleData(IAudioVoice& voice, const int16_t* data, size_t samples)
|
||||||
|
{
|
||||||
|
const ChannelMap& chmap = voice.channelMap();
|
||||||
|
m_interleaveBuf.clear();
|
||||||
|
m_interleaveBuf.reserve(samples * chmap.m_channelCount);
|
||||||
|
for (size_t s=0 ; s<samples ; ++s, ++data)
|
||||||
|
for (int c=0 ; c<chmap.m_channelCount ; ++c)
|
||||||
|
m_interleaveBuf.push_back(data[0] * m_coefs[chmap.m_channels[c]]);
|
||||||
|
voice.bufferSampleData(m_interleaveBuf.data(), samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMatrixStereo::setDefaultMatrixCoefficients()
|
||||||
|
{
|
||||||
|
memset(m_coefs, 0, sizeof(m_coefs));
|
||||||
|
switch (m_setOut)
|
||||||
|
{
|
||||||
|
case AudioChannelSet::Mono:
|
||||||
|
m_coefs[AudioChannel::FrontLeft][0] = 0.5;
|
||||||
|
m_coefs[AudioChannel::FrontLeft][1] = 0.5;
|
||||||
|
m_coefs[AudioChannel::FrontRight][0] = 0.5;
|
||||||
|
m_coefs[AudioChannel::FrontRight][1] = 0.5;
|
||||||
|
break;
|
||||||
|
case AudioChannelSet::Stereo:
|
||||||
|
case AudioChannelSet::Quad:
|
||||||
|
m_coefs[AudioChannel::FrontLeft][0] = 1.0;
|
||||||
|
m_coefs[AudioChannel::FrontRight][1] = 1.0;
|
||||||
|
break;
|
||||||
|
case AudioChannelSet::Surround51:
|
||||||
|
case AudioChannelSet::Surround71:
|
||||||
|
m_coefs[AudioChannel::FrontLeft][0] = 1.0;
|
||||||
|
m_coefs[AudioChannel::FrontRight][1] = 1.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMatrixStereo::bufferStereoSampleData(IAudioVoice& voice, const int16_t* data, size_t frames)
|
||||||
|
{
|
||||||
|
const ChannelMap& chmap = voice.channelMap();
|
||||||
|
m_interleaveBuf.clear();
|
||||||
|
m_interleaveBuf.reserve(frames * chmap.m_channelCount);
|
||||||
|
for (size_t f=0 ; f<frames ; ++f, data += 2)
|
||||||
|
for (int c=0 ; c<chmap.m_channelCount ; ++c)
|
||||||
|
m_interleaveBuf.push_back(data[0] * m_coefs[chmap.m_channels[c][0]] +
|
||||||
|
data[1] * m_coefs[chmap.m_channels[c][1]]);
|
||||||
|
voice.bufferSampleData(m_interleaveBuf.data(), frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue