Convert the backends to use proper inheritence

This commit is contained in:
Chris Robinson
2018-12-28 22:56:20 -08:00
parent 67b874328d
commit 3d92e8c4df
40 changed files with 1902 additions and 2855 deletions
+31 -30
View File
@@ -1623,7 +1623,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
* the device attributes can be updated.
*/
if((device->Flags&DEVICE_RUNNING))
V0(device->Backend,stop)();
device->Backend->stop();
device->Flags &= ~DEVICE_RUNNING;
}
@@ -1740,7 +1740,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
}
if((device->Flags&DEVICE_RUNNING))
V0(device->Backend,stop)();
device->Backend->stop();
device->Flags &= ~DEVICE_RUNNING;
UpdateClockBase(device);
@@ -1893,7 +1893,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->UpdateSize, device->NumUpdates
);
if(V0(device->Backend,reset)() == ALC_FALSE)
if(device->Backend->reset() == ALC_FALSE)
return ALC_INVALID_DEVICE;
if(device->FmtChans != oldChans && (device->Flags&DEVICE_CHANNELS_REQUEST))
@@ -2180,7 +2180,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
if(!(device->Flags&DEVICE_PAUSED))
{
if(V0(device->Backend,start)() == ALC_FALSE)
if(device->Backend->start() == ALC_FALSE)
return ALC_INVALID_DEVICE;
device->Flags |= DEVICE_RUNNING;
}
@@ -2203,7 +2203,7 @@ ALCdevice_struct::~ALCdevice_struct()
{
TRACE("%p\n", this);
DELETE_OBJ(Backend);
delete Backend;
Backend = nullptr;
size_t count{std::accumulate(BufferList.cbegin(), BufferList.cend(), size_t{0u},
@@ -2509,7 +2509,7 @@ static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
if(GlobalContext.compare_exchange_strong(origctx, nullptr))
ALCcontext_DecRef(context);
V0(device->Backend,lock)();
device->Backend->lock();
origctx = context;
newhead = context->next.load(std::memory_order_relaxed);
if(!device->ContextList.compare_exchange_strong(origctx, newhead))
@@ -2525,7 +2525,7 @@ static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
}
else
ret = !!newhead;
V0(device->Backend,unlock)();
device->Backend->unlock();
/* Make sure the context is finished and no longer processing in the mixer
* before sending the message queue kill event. The backend's lock does
@@ -2934,7 +2934,7 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
values[i++] = ALC_MINOR_VERSION;
values[i++] = alcMinorVersion;
values[i++] = ALC_CAPTURE_SAMPLES;
values[i++] = V0(device->Backend,availableSamples)();
values[i++] = device->Backend->availableSamples();
values[i++] = ALC_CONNECTED;
values[i++] = device->Connected.load(std::memory_order_relaxed);
values[i++] = 0;
@@ -2950,7 +2950,7 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
case ALC_CAPTURE_SAMPLES:
{ std::lock_guard<std::mutex> _{device->BackendLock};
values[0] = V0(device->Backend,availableSamples)();
values[0] = device->Backend->availableSamples();
}
return 1;
@@ -3443,9 +3443,9 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
alcSetError(dev.get(), err);
if(err == ALC_INVALID_DEVICE)
{
V0(dev->Backend,lock)();
dev->Backend->lock();
aluHandleDisconnect(dev.get(), "Device update failure");
V0(dev->Backend,unlock)();
dev->Backend->unlock();
}
backlock.unlock();
@@ -3526,7 +3526,7 @@ ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context)
std::lock_guard<std::mutex> _{Device->BackendLock};
if(!ReleaseContext(ctx.get(), Device))
{
V0(Device->Backend,stop)();
Device->Backend->stop();
Device->Flags &= ~DEVICE_RUNNING;
}
}
@@ -3778,7 +3778,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
}
// Find a playback device to open
ALCenum err{V(device->Backend,open)(deviceName)};
ALCenum err{device->Backend->open(deviceName)};
if(err != ALC_NO_ERROR)
{
device = nullptr;
@@ -3869,7 +3869,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
ctx = next;
}
if((device->Flags&DEVICE_RUNNING))
V0(device->Backend,stop)();
device->Backend->stop();
device->Flags &= ~DEVICE_RUNNING;
backlock.unlock();
@@ -3929,7 +3929,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName,
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
device->Frequency, device->UpdateSize, device->NumUpdates
);
ALCenum err{V(device->Backend,open)(deviceName)};
ALCenum err{device->Backend->open(deviceName)};
if(err != ALC_NO_ERROR)
{
device = nullptr;
@@ -3978,7 +3978,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
{ std::lock_guard<std::mutex> _{device->BackendLock};
if((device->Flags&DEVICE_RUNNING))
V0(device->Backend,stop)();
device->Backend->stop();
device->Flags &= ~DEVICE_RUNNING;
}
@@ -4001,7 +4001,7 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
alcSetError(dev.get(), ALC_INVALID_DEVICE);
else if(!(dev->Flags&DEVICE_RUNNING))
{
if(V0(dev->Backend,start)())
if(dev->Backend->start())
dev->Flags |= DEVICE_RUNNING;
else
{
@@ -4020,7 +4020,7 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
{
std::lock_guard<std::mutex> _{dev->BackendLock};
if((dev->Flags&DEVICE_RUNNING))
V0(dev->Backend,stop)();
dev->Backend->stop();
dev->Flags &= ~DEVICE_RUNNING;
}
}
@@ -4036,8 +4036,9 @@ ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer,
ALCenum err{ALC_INVALID_VALUE};
{ std::lock_guard<std::mutex> _{dev->BackendLock};
if(samples >= 0 && V0(dev->Backend,availableSamples)() >= (ALCuint)samples)
err = V(dev->Backend,captureSamples)(buffer, samples);
BackendBase *backend{dev->Backend};
if(samples >= 0 && backend->availableSamples() >= (ALCuint)samples)
err = backend->captureSamples(buffer, samples);
}
if(err != ALC_NO_ERROR)
alcSetError(dev.get(), err);
@@ -4102,7 +4103,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
}
// Open the "backend"
V(device->Backend,open)("Loopback");
device->Backend->open("Loopback");
{
ALCdevice *head{DeviceList.load()};
@@ -4149,9 +4150,9 @@ FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, AL
alcSetError(dev.get(), ALC_INVALID_VALUE);
else
{
V0(dev->Backend,lock)();
dev->Backend->lock();
aluMixData(dev.get(), buffer, samples);
V0(dev->Backend,unlock)();
dev->Backend->unlock();
}
}
@@ -4173,7 +4174,7 @@ ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device)
{
std::lock_guard<std::mutex> _{dev->BackendLock};
if((dev->Flags&DEVICE_RUNNING))
V0(dev->Backend,stop)();
dev->Backend->stop();
dev->Flags &= ~DEVICE_RUNNING;
dev->Flags |= DEVICE_PAUSED;
}
@@ -4199,11 +4200,11 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device)
if(dev->ContextList.load() == nullptr)
return;
if(V0(dev->Backend,start)() == ALC_FALSE)
if(dev->Backend->start() == ALC_FALSE)
{
V0(dev->Backend,lock)();
dev->Backend->lock();
aluHandleDisconnect(dev.get(), "Device start failure");
V0(dev->Backend,unlock)();
dev->Backend->unlock();
alcSetError(dev.get(), ALC_INVALID_DEVICE);
return;
}
@@ -4261,7 +4262,7 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi
* the connected state so lost devices can attempt recover.
*/
if((dev->Flags&DEVICE_RUNNING))
V0(dev->Backend,stop)();
dev->Backend->stop();
dev->Flags &= ~DEVICE_RUNNING;
device->Connected.store(AL_TRUE);
@@ -4271,9 +4272,9 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi
alcSetError(dev.get(), err);
if(err == ALC_INVALID_DEVICE)
{
V0(dev->Backend,lock)();
dev->Backend->lock();
aluHandleDisconnect(dev.get(), "Device start failure");
V0(dev->Backend,unlock)();
dev->Backend->unlock();
}
return ALC_FALSE;
}
+182 -231
View File
@@ -422,46 +422,32 @@ int verify_state(snd_pcm_t *handle)
}
struct ALCplaybackAlsa final : public ALCbackend {
ALCplaybackAlsa(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCplaybackAlsa() override;
struct AlsaPlayback final : public BackendBase {
AlsaPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~AlsaPlayback() override;
int mixerProc();
int mixerNoMMapProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
ClockLatency getClockLatency() override;
snd_pcm_t *mPcmHandle{nullptr};
al::vector<char> mBuffer;
std::atomic<ALenum> mKillNow{AL_TRUE};
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "AlsaPlayback::"; }
DEF_NEWDEL(AlsaPlayback)
};
void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device);
void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self);
ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name);
ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self);
ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa);
void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device)
{
new (self) ALCplaybackAlsa{device};
SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self);
}
void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self)
{ self->~ALCplaybackAlsa(); }
ALCplaybackAlsa::~ALCplaybackAlsa()
AlsaPlayback::~AlsaPlayback()
{
if(mPcmHandle)
snd_pcm_close(mPcmHandle);
@@ -469,7 +455,7 @@ ALCplaybackAlsa::~ALCplaybackAlsa()
}
int ALCplaybackAlsa::mixerProc()
int AlsaPlayback::mixerProc()
{
SetRTPriority();
althrd_setname(MIXER_THREAD_NAME);
@@ -482,9 +468,9 @@ int ALCplaybackAlsa::mixerProc()
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
ALCplaybackAlsa_lock(this);
lock();
aluHandleDisconnect(mDevice, "Bad state: %s", snd_strerror(state));
ALCplaybackAlsa_unlock(this);
unlock();
break;
}
@@ -521,7 +507,7 @@ int ALCplaybackAlsa::mixerProc()
avail -= avail%update_size;
// it is possible that contiguous areas are smaller, thus we use a loop
ALCplaybackAlsa_lock(this);
lock();
while(avail > 0)
{
snd_pcm_uframes_t frames{static_cast<snd_pcm_uframes_t>(avail)};
@@ -548,13 +534,13 @@ int ALCplaybackAlsa::mixerProc()
avail -= frames;
}
ALCplaybackAlsa_unlock(this);
unlock();
}
return 0;
}
int ALCplaybackAlsa::mixerNoMMapProc()
int AlsaPlayback::mixerNoMMapProc()
{
SetRTPriority();
althrd_setname(MIXER_THREAD_NAME);
@@ -567,9 +553,9 @@ int ALCplaybackAlsa::mixerNoMMapProc()
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
ALCplaybackAlsa_lock(this);
lock();
aluHandleDisconnect(mDevice, "Bad state: %s", snd_strerror(state));
ALCplaybackAlsa_unlock(this);
unlock();
break;
}
@@ -603,7 +589,7 @@ int ALCplaybackAlsa::mixerNoMMapProc()
continue;
}
ALCplaybackAlsa_lock(this);
lock();
char *WritePtr{mBuffer.data()};
avail = snd_pcm_bytes_to_frames(mPcmHandle, mBuffer.size());
aluMixData(mDevice, WritePtr, avail);
@@ -637,14 +623,14 @@ int ALCplaybackAlsa::mixerNoMMapProc()
if(ret < 0) break;
}
}
ALCplaybackAlsa_unlock(this);
unlock();
}
return 0;
}
ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
ALCenum AlsaPlayback::open(const ALCchar *name)
{
const char *driver{};
if(name)
@@ -667,7 +653,7 @@ ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
}
TRACE("Opening device \"%s\"\n", driver);
int err{snd_pcm_open(&self->mPcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
if(err < 0)
{
ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(err));
@@ -677,18 +663,15 @@ ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
/* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
snd_config_update_free_global();
ALCdevice *device = self->mDevice;
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
ALCboolean AlsaPlayback::reset()
{
ALCdevice *device{self->mDevice};
snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
format = SND_PCM_FORMAT_S8;
@@ -713,11 +696,11 @@ ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
break;
}
bool allowmmap{!!GetConfigValueBool(device->DeviceName.c_str(), "alsa", "mmap", 1)};
ALuint periods{device->NumUpdates};
ALuint periodLen{static_cast<ALuint>(device->UpdateSize * U64(1000000) / device->Frequency)};
bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", 1)};
ALuint periods{mDevice->NumUpdates};
ALuint periodLen{static_cast<ALuint>(mDevice->UpdateSize * U64(1000000) / mDevice->Frequency)};
ALuint bufferLen{periodLen * periods};
ALuint rate{device->Frequency};
ALuint rate{mDevice->Frequency};
snd_pcm_uframes_t periodSizeInFrames;
snd_pcm_sw_params_t *sp{};
@@ -727,15 +710,15 @@ ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
int dir, err;
snd_pcm_hw_params_malloc(&hp);
#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
CHECK(snd_pcm_hw_params_any(self->mPcmHandle, hp));
CHECK(snd_pcm_hw_params_any(mPcmHandle, hp));
/* set interleaved access */
if(!allowmmap || snd_pcm_hw_params_set_access(self->mPcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
if(!allowmmap || snd_pcm_hw_params_set_access(mPcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
{
/* No mmap */
CHECK(snd_pcm_hw_params_set_access(self->mPcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
CHECK(snd_pcm_hw_params_set_access(mPcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
}
/* test and set format (implicitly sets sample bits) */
if(snd_pcm_hw_params_test_format(self->mPcmHandle, hp, format) < 0)
if(snd_pcm_hw_params_test_format(mPcmHandle, hp, format) < 0)
{
static const struct {
snd_pcm_format_t format;
@@ -753,16 +736,16 @@ ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
for(const auto &fmt : formatlist)
{
format = fmt.format;
if(snd_pcm_hw_params_test_format(self->mPcmHandle, hp, format) >= 0)
if(snd_pcm_hw_params_test_format(mPcmHandle, hp, format) >= 0)
{
device->FmtType = fmt.fmttype;
mDevice->FmtType = fmt.fmttype;
break;
}
}
}
CHECK(snd_pcm_hw_params_set_format(self->mPcmHandle, hp, format));
CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp, format));
/* test and set channels (implicitly sets frame bits) */
if(snd_pcm_hw_params_test_channels(self->mPcmHandle, hp, device->channelsFromFmt()) < 0)
if(snd_pcm_hw_params_test_channels(mPcmHandle, hp, mDevice->channelsFromFmt()) < 0)
{
static const DevFmtChannels channellist[] = {
DevFmtStereo,
@@ -774,33 +757,33 @@ ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
for(const auto &chan : channellist)
{
if(snd_pcm_hw_params_test_channels(self->mPcmHandle, hp, ChannelsFromDevFmt(chan, 0)) >= 0)
if(snd_pcm_hw_params_test_channels(mPcmHandle, hp, ChannelsFromDevFmt(chan, 0)) >= 0)
{
device->FmtChans = chan;
device->mAmbiOrder = 0;
mDevice->FmtChans = chan;
mDevice->mAmbiOrder = 0;
break;
}
}
}
CHECK(snd_pcm_hw_params_set_channels(self->mPcmHandle, hp, device->channelsFromFmt()));
CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp, mDevice->channelsFromFmt()));
/* set rate (implicitly constrains period/buffer parameters) */
if(!GetConfigValueBool(device->DeviceName.c_str(), "alsa", "allow-resampler", 0) ||
!(device->Flags&DEVICE_FREQUENCY_REQUEST))
if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", 0) ||
!(mDevice->Flags&DEVICE_FREQUENCY_REQUEST))
{
if(snd_pcm_hw_params_set_rate_resample(self->mPcmHandle, hp, 0) < 0)
if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp, 0) < 0)
ERR("Failed to disable ALSA resampler\n");
}
else if(snd_pcm_hw_params_set_rate_resample(self->mPcmHandle, hp, 1) < 0)
else if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp, 1) < 0)
ERR("Failed to enable ALSA resampler\n");
CHECK(snd_pcm_hw_params_set_rate_near(self->mPcmHandle, hp, &rate, nullptr));
CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp, &rate, nullptr));
/* set buffer time (implicitly constrains period/buffer parameters) */
if((err=snd_pcm_hw_params_set_buffer_time_near(self->mPcmHandle, hp, &bufferLen, nullptr)) < 0)
if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp, &bufferLen, nullptr)) < 0)
ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
/* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */
if((err=snd_pcm_hw_params_set_period_time_near(self->mPcmHandle, hp, &periodLen, nullptr)) < 0)
if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp, &periodLen, nullptr)) < 0)
ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
/* install and prepare hardware configuration */
CHECK(snd_pcm_hw_params(self->mPcmHandle, hp));
CHECK(snd_pcm_hw_params(mPcmHandle, hp));
/* retrieve configuration info */
CHECK(snd_pcm_hw_params_get_access(hp, &access));
@@ -812,19 +795,19 @@ ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
hp = nullptr;
snd_pcm_sw_params_malloc(&sp);
CHECK(snd_pcm_sw_params_current(self->mPcmHandle, sp));
CHECK(snd_pcm_sw_params_set_avail_min(self->mPcmHandle, sp, periodSizeInFrames));
CHECK(snd_pcm_sw_params_set_stop_threshold(self->mPcmHandle, sp, periodSizeInFrames*periods));
CHECK(snd_pcm_sw_params(self->mPcmHandle, sp));
CHECK(snd_pcm_sw_params_current(mPcmHandle, sp));
CHECK(snd_pcm_sw_params_set_avail_min(mPcmHandle, sp, periodSizeInFrames));
CHECK(snd_pcm_sw_params_set_stop_threshold(mPcmHandle, sp, periodSizeInFrames*periods));
CHECK(snd_pcm_sw_params(mPcmHandle, sp));
#undef CHECK
snd_pcm_sw_params_free(sp);
sp = nullptr;
device->NumUpdates = periods;
device->UpdateSize = periodSizeInFrames;
device->Frequency = rate;
mDevice->NumUpdates = periods;
mDevice->UpdateSize = periodSizeInFrames;
mDevice->Frequency = rate;
SetDefaultChannelOrder(device);
SetDefaultChannelOrder(mDevice);
return ALC_TRUE;
@@ -835,9 +818,8 @@ error:
return ALC_FALSE;
}
ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
ALCboolean AlsaPlayback::start()
{
ALCdevice *device{self->mDevice};
snd_pcm_hw_params_t *hp{};
snd_pcm_access_t access;
const char *funcerr;
@@ -845,7 +827,7 @@ ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
snd_pcm_hw_params_malloc(&hp);
#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
CHECK(snd_pcm_hw_params_current(self->mPcmHandle, hp));
CHECK(snd_pcm_hw_params_current(mPcmHandle, hp));
/* retrieve configuration info */
CHECK(snd_pcm_hw_params_get_access(hp, &access));
#undef CHECK
@@ -859,26 +841,26 @@ ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
snd_pcm_hw_params_free(hp);
hp = nullptr;
int (ALCplaybackAlsa::*thread_func)(){};
int (AlsaPlayback::*thread_func)(){};
if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
{
self->mBuffer.resize(snd_pcm_frames_to_bytes(self->mPcmHandle, device->UpdateSize));
thread_func = &ALCplaybackAlsa::mixerNoMMapProc;
mBuffer.resize(snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize));
thread_func = &AlsaPlayback::mixerNoMMapProc;
}
else
{
err = snd_pcm_prepare(self->mPcmHandle);
err = snd_pcm_prepare(mPcmHandle);
if(err < 0)
{
ERR("snd_pcm_prepare(data->mPcmHandle) failed: %s\n", snd_strerror(err));
return ALC_FALSE;
}
thread_func = &ALCplaybackAlsa::mixerProc;
thread_func = &AlsaPlayback::mixerProc;
}
try {
self->mKillNow.store(AL_FALSE, std::memory_order_release);
self->mThread = std::thread{std::mem_fn(thread_func), self};
mKillNow.store(AL_FALSE, std::memory_order_release);
mThread = std::thread{std::mem_fn(thread_func), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -886,45 +868,50 @@ ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
}
catch(...) {
}
self->mBuffer.clear();
mBuffer.clear();
return ALC_FALSE;
}
void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
void AlsaPlayback::stop()
{
if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !self->mThread.joinable())
if(mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !mThread.joinable())
return;
mThread.join();
self->mThread.join();
self->mBuffer.clear();
mBuffer.clear();
}
ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
ClockLatency AlsaPlayback::getClockLatency()
{
ALCdevice *device{self->mDevice};
ClockLatency ret;
ALCplaybackAlsa_lock(self);
ret.ClockTime = GetDeviceClockTime(device);
lock();
ret.ClockTime = GetDeviceClockTime(mDevice);
snd_pcm_sframes_t delay{};
int err{snd_pcm_delay(self->mPcmHandle, &delay)};
int err{snd_pcm_delay(mPcmHandle, &delay)};
if(err < 0)
{
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
delay = 0;
}
ret.Latency = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
ret.Latency /= device->Frequency;
ALCplaybackAlsa_unlock(self);
ret.Latency /= mDevice->Frequency;
unlock();
return ret;
}
struct ALCcaptureAlsa final : public ALCbackend {
ALCcaptureAlsa(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCcaptureAlsa() override;
struct AlsaCapture final : public BackendBase {
AlsaCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~AlsaCapture() override;
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
ClockLatency getClockLatency() override;
snd_pcm_t *mPcmHandle{nullptr};
@@ -934,34 +921,12 @@ struct ALCcaptureAlsa final : public ALCbackend {
RingBufferPtr mRing{nullptr};
snd_pcm_sframes_t mLastAvail{0};
static constexpr inline const char *CurrentPrefix() noexcept { return "AlsaCapture::"; }
DEF_NEWDEL(AlsaCapture)
};
void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device);
void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self);
ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name);
DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset)
ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa);
void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device)
{
new (self) ALCcaptureAlsa{device};
SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self);
}
void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self)
{ self->~ALCcaptureAlsa(); }
ALCcaptureAlsa::~ALCcaptureAlsa()
AlsaCapture::~AlsaCapture()
{
if(mPcmHandle)
snd_pcm_close(mPcmHandle);
@@ -969,9 +934,8 @@ ALCcaptureAlsa::~ALCcaptureAlsa()
}
ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
ALCenum AlsaCapture::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
const char *driver{};
if(name)
{
@@ -993,7 +957,7 @@ ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
}
TRACE("Opening device \"%s\"\n", driver);
int err{snd_pcm_open(&self->mPcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)};
int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)};
if(err < 0)
{
ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err));
@@ -1004,7 +968,7 @@ ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
snd_config_update_free_global();
snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
format = SND_PCM_FORMAT_S8;
@@ -1029,35 +993,35 @@ ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
break;
}
snd_pcm_uframes_t bufferSizeInFrames{maxu(device->UpdateSize*device->NumUpdates,
100*device->Frequency/1000)};
snd_pcm_uframes_t periodSizeInFrames{minu(bufferSizeInFrames, 25*device->Frequency/1000)};
snd_pcm_uframes_t bufferSizeInFrames{maxu(mDevice->UpdateSize*mDevice->NumUpdates,
100*mDevice->Frequency/1000)};
snd_pcm_uframes_t periodSizeInFrames{minu(bufferSizeInFrames, 25*mDevice->Frequency/1000)};
bool needring{false};
const char *funcerr{};
snd_pcm_hw_params_t *hp{};
snd_pcm_hw_params_malloc(&hp);
#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
CHECK(snd_pcm_hw_params_any(self->mPcmHandle, hp));
CHECK(snd_pcm_hw_params_any(mPcmHandle, hp));
/* set interleaved access */
CHECK(snd_pcm_hw_params_set_access(self->mPcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
CHECK(snd_pcm_hw_params_set_access(mPcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
/* set format (implicitly sets sample bits) */
CHECK(snd_pcm_hw_params_set_format(self->mPcmHandle, hp, format));
CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp, format));
/* set channels (implicitly sets frame bits) */
CHECK(snd_pcm_hw_params_set_channels(self->mPcmHandle, hp, device->channelsFromFmt()));
CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp, mDevice->channelsFromFmt()));
/* set rate (implicitly constrains period/buffer parameters) */
CHECK(snd_pcm_hw_params_set_rate(self->mPcmHandle, hp, device->Frequency, 0));
CHECK(snd_pcm_hw_params_set_rate(mPcmHandle, hp, mDevice->Frequency, 0));
/* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
if(snd_pcm_hw_params_set_buffer_size_min(self->mPcmHandle, hp, &bufferSizeInFrames) < 0)
if(snd_pcm_hw_params_set_buffer_size_min(mPcmHandle, hp, &bufferSizeInFrames) < 0)
{
TRACE("Buffer too large, using intermediate ring buffer\n");
needring = true;
CHECK(snd_pcm_hw_params_set_buffer_size_near(self->mPcmHandle, hp, &bufferSizeInFrames));
CHECK(snd_pcm_hw_params_set_buffer_size_near(mPcmHandle, hp, &bufferSizeInFrames));
}
/* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
CHECK(snd_pcm_hw_params_set_period_size_near(self->mPcmHandle, hp, &periodSizeInFrames, nullptr));
CHECK(snd_pcm_hw_params_set_period_size_near(mPcmHandle, hp, &periodSizeInFrames, nullptr));
/* install and prepare hardware configuration */
CHECK(snd_pcm_hw_params(self->mPcmHandle, hp));
CHECK(snd_pcm_hw_params(mPcmHandle, hp));
/* retrieve configuration info */
CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, nullptr));
#undef CHECK
@@ -1066,16 +1030,16 @@ ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
if(needring)
{
self->mRing = CreateRingBuffer(device->UpdateSize*device->NumUpdates,
device->frameSizeFromFmt(), false);
if(!self->mRing)
mRing = CreateRingBuffer(mDevice->UpdateSize*mDevice->NumUpdates,
mDevice->frameSizeFromFmt(), false);
if(!mRing)
{
ERR("ring buffer create failed\n");
goto error2;
}
}
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
@@ -1084,99 +1048,99 @@ error:
if(hp) snd_pcm_hw_params_free(hp);
error2:
self->mRing = nullptr;
snd_pcm_close(self->mPcmHandle);
self->mPcmHandle = nullptr;
mRing = nullptr;
snd_pcm_close(mPcmHandle);
mPcmHandle = nullptr;
return ALC_INVALID_VALUE;
}
ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
ALCboolean AlsaCapture::start()
{
int err{snd_pcm_prepare(self->mPcmHandle)};
int err{snd_pcm_prepare(mPcmHandle)};
if(err < 0)
ERR("prepare failed: %s\n", snd_strerror(err));
else
{
err = snd_pcm_start(self->mPcmHandle);
err = snd_pcm_start(mPcmHandle);
if(err < 0)
ERR("start failed: %s\n", snd_strerror(err));
}
if(err < 0)
{
aluHandleDisconnect(self->mDevice, "Capture state failure: %s", snd_strerror(err));
aluHandleDisconnect(mDevice, "Capture state failure: %s", snd_strerror(err));
return ALC_FALSE;
}
self->mDoCapture = true;
mDoCapture = true;
return ALC_TRUE;
}
void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
void AlsaCapture::stop()
{
/* OpenAL requires access to unread audio after stopping, but ALSA's
* snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
* available now so it'll be available later after the drop. */
ALCuint avail{ALCcaptureAlsa_availableSamples(self)};
if(!self->mRing && avail > 0)
* available now so it'll be available later after the drop.
*/
ALCuint avail{availableSamples()};
if(!mRing && avail > 0)
{
/* The ring buffer implicitly captures when checking availability.
* Direct access needs to explicitly capture it into temp storage. */
al::vector<char> temp(snd_pcm_frames_to_bytes(self->mPcmHandle, avail));
ALCcaptureAlsa_captureSamples(self, temp.data(), avail);
self->mBuffer = std::move(temp);
al::vector<char> temp(snd_pcm_frames_to_bytes(mPcmHandle, avail));
captureSamples(temp.data(), avail);
mBuffer = std::move(temp);
}
int err{snd_pcm_drop(self->mPcmHandle)};
int err{snd_pcm_drop(mPcmHandle)};
if(err < 0)
ERR("drop failed: %s\n", snd_strerror(err));
self->mDoCapture = false;
mDoCapture = false;
}
ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples)
ALCenum AlsaCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
{
ALCdevice *device{self->mDevice};
if(RingBuffer *ring{self->mRing.get()})
if(mRing)
{
ring->read(buffer, samples);
mRing->read(buffer, samples);
return ALC_NO_ERROR;
}
self->mLastAvail -= samples;
while(device->Connected.load(std::memory_order_acquire) && samples > 0)
mLastAvail -= samples;
while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0)
{
snd_pcm_sframes_t amt{0};
if(!self->mBuffer.empty())
if(!mBuffer.empty())
{
/* First get any data stored from the last stop */
amt = snd_pcm_bytes_to_frames(self->mPcmHandle, self->mBuffer.size());
amt = snd_pcm_bytes_to_frames(mPcmHandle, mBuffer.size());
if((snd_pcm_uframes_t)amt > samples) amt = samples;
amt = snd_pcm_frames_to_bytes(self->mPcmHandle, amt);
memcpy(buffer, self->mBuffer.data(), amt);
amt = snd_pcm_frames_to_bytes(mPcmHandle, amt);
memcpy(buffer, mBuffer.data(), amt);
self->mBuffer.erase(self->mBuffer.begin(), self->mBuffer.begin()+amt);
amt = snd_pcm_bytes_to_frames(self->mPcmHandle, amt);
mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt);
amt = snd_pcm_bytes_to_frames(mPcmHandle, amt);
}
else if(self->mDoCapture)
amt = snd_pcm_readi(self->mPcmHandle, buffer, samples);
else if(mDoCapture)
amt = snd_pcm_readi(mPcmHandle, buffer, samples);
if(amt < 0)
{
ERR("read error: %s\n", snd_strerror(amt));
if(amt == -EAGAIN)
continue;
if((amt=snd_pcm_recover(self->mPcmHandle, amt, 1)) >= 0)
if((amt=snd_pcm_recover(mPcmHandle, amt, 1)) >= 0)
{
amt = snd_pcm_start(self->mPcmHandle);
amt = snd_pcm_start(mPcmHandle);
if(amt >= 0)
amt = snd_pcm_avail_update(self->mPcmHandle);
amt = snd_pcm_avail_update(mPcmHandle);
}
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
aluHandleDisconnect(mDevice, "Capture recovery failure: %s", snd_strerror(amt));
break;
}
/* If the amount available is less than what's asked, we lost it
@@ -1190,100 +1154,96 @@ ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALC
samples -= amt;
}
if(samples > 0)
memset(buffer, ((device->FmtType == DevFmtUByte) ? 0x80 : 0),
snd_pcm_frames_to_bytes(self->mPcmHandle, samples));
memset(buffer, ((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0),
snd_pcm_frames_to_bytes(mPcmHandle, samples));
return ALC_NO_ERROR;
}
ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
ALCuint AlsaCapture::availableSamples()
{
ALCdevice *device{self->mDevice};
snd_pcm_sframes_t avail{0};
if(device->Connected.load(std::memory_order_acquire) && self->mDoCapture)
avail = snd_pcm_avail_update(self->mPcmHandle);
if(mDevice->Connected.load(std::memory_order_acquire) && mDoCapture)
avail = snd_pcm_avail_update(mPcmHandle);
if(avail < 0)
{
ERR("avail update failed: %s\n", snd_strerror(avail));
if((avail=snd_pcm_recover(self->mPcmHandle, avail, 1)) >= 0)
if((avail=snd_pcm_recover(mPcmHandle, avail, 1)) >= 0)
{
if(self->mDoCapture)
avail = snd_pcm_start(self->mPcmHandle);
if(mDoCapture)
avail = snd_pcm_start(mPcmHandle);
if(avail >= 0)
avail = snd_pcm_avail_update(self->mPcmHandle);
avail = snd_pcm_avail_update(mPcmHandle);
}
if(avail < 0)
{
ERR("restore error: %s\n", snd_strerror(avail));
aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(avail));
aluHandleDisconnect(mDevice, "Capture recovery failure: %s", snd_strerror(avail));
}
}
RingBuffer *ring{self->mRing.get()};
if(!ring)
if(!mRing)
{
if(avail < 0) avail = 0;
avail += snd_pcm_bytes_to_frames(self->mPcmHandle, self->mBuffer.size());
if(avail > self->mLastAvail) self->mLastAvail = avail;
return self->mLastAvail;
avail += snd_pcm_bytes_to_frames(mPcmHandle, mBuffer.size());
if(avail > mLastAvail) mLastAvail = avail;
return mLastAvail;
}
while(avail > 0)
{
auto vec = ring->getWriteVector();
auto vec = mRing->getWriteVector();
if(vec.first.len == 0) break;
snd_pcm_sframes_t amt{std::min<snd_pcm_sframes_t>(vec.first.len, avail)};
amt = snd_pcm_readi(self->mPcmHandle, vec.first.buf, amt);
amt = snd_pcm_readi(mPcmHandle, vec.first.buf, amt);
if(amt < 0)
{
ERR("read error: %s\n", snd_strerror(amt));
if(amt == -EAGAIN)
continue;
if((amt=snd_pcm_recover(self->mPcmHandle, amt, 1)) >= 0)
if((amt=snd_pcm_recover(mPcmHandle, amt, 1)) >= 0)
{
if(self->mDoCapture)
amt = snd_pcm_start(self->mPcmHandle);
if(mDoCapture)
amt = snd_pcm_start(mPcmHandle);
if(amt >= 0)
amt = snd_pcm_avail_update(self->mPcmHandle);
amt = snd_pcm_avail_update(mPcmHandle);
}
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
aluHandleDisconnect(mDevice, "Capture recovery failure: %s", snd_strerror(amt));
break;
}
avail = amt;
continue;
}
ring->writeAdvance(amt);
mRing->writeAdvance(amt);
avail -= amt;
}
return ring->readSpace();
return mRing->readSpace();
}
ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
ClockLatency AlsaCapture::getClockLatency()
{
ALCdevice *device{self->mDevice};
ClockLatency ret;
ALCcaptureAlsa_lock(self);
ret.ClockTime = GetDeviceClockTime(device);
lock();
ret.ClockTime = GetDeviceClockTime(mDevice);
snd_pcm_sframes_t delay{};
int err{snd_pcm_delay(self->mPcmHandle, &delay)};
int err{snd_pcm_delay(mPcmHandle, &delay)};
if(err < 0)
{
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
delay = 0;
}
ret.Latency = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
ret.Latency /= device->Frequency;
ALCcaptureAlsa_unlock(self);
ret.Latency /= mDevice->Frequency;
unlock();
return ret;
}
@@ -1332,21 +1292,12 @@ void AlsaBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *AlsaBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *AlsaBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCplaybackAlsa *backend;
NEW_OBJ(backend, ALCplaybackAlsa)(device);
return backend;
}
return new AlsaPlayback{device};
if(type == ALCbackend_Capture)
{
ALCcaptureAlsa *backend;
NEW_OBJ(backend, ALCcaptureAlsa)(device);
return backend;
}
return new AlsaCapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+24 -44
View File
@@ -12,80 +12,60 @@
void ALCdevice_Lock(ALCdevice *device)
{ V0(device->Backend,lock)(); }
{ device->Backend->lock(); }
void ALCdevice_Unlock(ALCdevice *device)
{ V0(device->Backend,unlock)(); }
{ device->Backend->unlock(); }
ClockLatency GetClockLatency(ALCdevice *device)
{
ClockLatency ret = V0(device->Backend,getClockLatency)();
BackendBase *backend{device->Backend};
ClockLatency ret{backend->getClockLatency()};
ret.Latency += device->FixedLatency;
return ret;
}
/* Base ALCbackend method implementations. */
ALCbackend::ALCbackend(ALCdevice *device) noexcept : mDevice{device}
/* BackendBase method implementations. */
BackendBase::BackendBase(ALCdevice *device) noexcept : mDevice{device}
{ }
ALCbackend::~ALCbackend()
BackendBase::~BackendBase()
{ }
ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self))
{
return ALC_FALSE;
}
ALCboolean BackendBase::reset()
{ return ALC_FALSE; }
ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples))
{
return ALC_INVALID_DEVICE;
}
ALCenum BackendBase::captureSamples(void* UNUSED(buffer), ALCuint UNUSED(samples))
{ return ALC_INVALID_DEVICE; }
ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
{
return 0;
}
ALCuint BackendBase::availableSamples()
{ return 0; }
ClockLatency ALCbackend_getClockLatency(ALCbackend *self)
ClockLatency BackendBase::getClockLatency()
{
ALCdevice *device = self->mDevice;
ALuint refcount;
ClockLatency ret;
ALuint refcount;
do {
while(((refcount=device->MixCount.load(std::memory_order_acquire))&1))
while(((refcount=mDevice->MixCount.load(std::memory_order_acquire))&1))
std::this_thread::yield();
ret.ClockTime = GetDeviceClockTime(device);
ret.ClockTime = GetDeviceClockTime(mDevice);
std::atomic_thread_fence(std::memory_order_acquire);
} while(refcount != device->MixCount.load(std::memory_order_relaxed));
} while(refcount != mDevice->MixCount.load(std::memory_order_relaxed));
/* NOTE: The device will generally have about all but one periods filled at
* any given time during playback. Without a more accurate measurement from
* the output, this is an okay approximation.
*/
ret.Latency = std::chrono::seconds{device->UpdateSize*maxi(device->NumUpdates-1, 0)};
ret.Latency /= device->Frequency;
ret.Latency = std::chrono::seconds{mDevice->UpdateSize*maxi(mDevice->NumUpdates-1, 0)};
ret.Latency /= mDevice->Frequency;
return ret;
}
void ALCbackend_lock(ALCbackend *self)
{
try {
self->mMutex.lock();
}
catch(...) {
std::terminate();
}
}
void BackendBase::lock() noexcept
{ mMutex.lock(); }
void ALCbackend_unlock(ALCbackend *self)
{
try {
self->mMutex.unlock();
}
catch(...) {
std::terminate();
}
}
void BackendBase::unlock() noexcept
{ mMutex.unlock(); }
+15 -64
View File
@@ -6,7 +6,6 @@
#include <mutex>
#include "alMain.h"
#include "polymorphism.h"
struct ClockLatency {
@@ -32,77 +31,29 @@ void ALCdevice_Unlock(ALCdevice *device);
ClockLatency GetClockLatency(ALCdevice *device);
struct BackendBase {
virtual ALCenum open(const ALCchar *name) = 0;
struct ALCbackendVtable;
virtual ALCboolean reset();
virtual ALCboolean start() = 0;
virtual void stop() = 0;
struct ALCbackend {
const ALCbackendVtable *vtbl;
virtual ALCenum captureSamples(void *buffer, ALCuint samples);
virtual ALCuint availableSamples();
virtual ClockLatency getClockLatency();
virtual void lock() noexcept;
virtual void unlock() noexcept;
ALCdevice *mDevice;
std::recursive_mutex mMutex;
ALCbackend(ALCdevice *device) noexcept;
virtual ~ALCbackend();
BackendBase(ALCdevice *device) noexcept;
virtual ~BackendBase();
};
ALCboolean ALCbackend_reset(ALCbackend *self);
ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
ALCuint ALCbackend_availableSamples(ALCbackend *self);
ClockLatency ALCbackend_getClockLatency(ALCbackend *self);
void ALCbackend_lock(ALCbackend *self);
void ALCbackend_unlock(ALCbackend *self);
struct ALCbackendVtable {
void (*const Destruct)(ALCbackend*);
ALCenum (*const open)(ALCbackend*, const ALCchar*);
ALCboolean (*const reset)(ALCbackend*);
ALCboolean (*const start)(ALCbackend*);
void (*const stop)(ALCbackend*);
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
ALCuint (*const availableSamples)(ALCbackend*);
ClockLatency (*const getClockLatency)(ALCbackend*);
void (*const lock)(ALCbackend*);
void (*const unlock)(ALCbackend*);
void (*const Delete)(void*);
};
#define DEFINE_ALCBACKEND_VTABLE(T) \
DECLARE_THUNK(T, ALCbackend, void, Destruct) \
DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
DECLARE_THUNK(T, ALCbackend, void, stop) \
DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency) \
DECLARE_THUNK(T, ALCbackend, void, lock) \
DECLARE_THUNK(T, ALCbackend, void, unlock) \
static void T##_ALCbackend_Delete(void *ptr) \
{ T##_Delete(static_cast<T*>(static_cast<ALCbackend*>(ptr))); } \
\
static const ALCbackendVtable T##_ALCbackend_vtable = { \
T##_ALCbackend_Destruct, \
\
T##_ALCbackend_open, \
T##_ALCbackend_reset, \
T##_ALCbackend_start, \
T##_ALCbackend_stop, \
T##_ALCbackend_captureSamples, \
T##_ALCbackend_availableSamples, \
T##_ALCbackend_getClockLatency, \
T##_ALCbackend_lock, \
T##_ALCbackend_unlock, \
\
T##_ALCbackend_Delete, \
}
enum ALCbackend_Type {
ALCbackend_Playback,
@@ -119,7 +70,7 @@ struct BackendFactory {
virtual void probe(DevProbe type, std::string *outnames) = 0;
virtual ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) = 0;
virtual BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) = 0;
};
#endif /* ALC_BACKENDS_BASE_H */
+121 -170
View File
@@ -30,6 +30,7 @@
#include "alu.h"
#include "ringbuffer.h"
#include "converter.h"
#include "backends/base.h"
#include <unistd.h>
#include <AudioUnit/AudioUnit.h>
@@ -41,9 +42,9 @@ namespace {
static const ALCchar ca_device[] = "CoreAudio Default";
struct ALCcoreAudioPlayback final : public ALCbackend {
ALCcoreAudioPlayback(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCcoreAudioPlayback() override;
struct CoreAudioPlayback final : public BackendBase {
CoreAudioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~CoreAudioPlayback() override;
static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
@@ -52,64 +53,47 @@ struct ALCcoreAudioPlayback final : public ALCbackend {
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData);
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
AudioUnit mAudioUnit;
ALuint mFrameSize{0u};
AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
static constexpr inline const char *CurrentPrefix() noexcept { return "CoreAudioPlayback::"; }
DEF_NEWDEL(CoreAudioPlayback)
};
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self);
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name);
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self);
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self);
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self);
static DECLARE_FORWARD2(ALCcoreAudioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioPlayback);
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device)
{
new (self) ALCcoreAudioPlayback{device};
SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
}
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
{ self->~ALCcoreAudioPlayback(); }
ALCcoreAudioPlayback::~ALCcoreAudioPlayback()
CoreAudioPlayback::~CoreAudioPlayback()
{
AudioUnitUninitialize(mAudioUnit);
AudioComponentInstanceDispose(mAudioUnit);
}
OSStatus ALCcoreAudioPlayback::MixerProcC(void *inRefCon,
OSStatus CoreAudioPlayback::MixerProcC(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
return static_cast<ALCcoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
inBusNumber, inNumberFrames, ioData);
}
OSStatus ALCcoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
const AudioTimeStamp* UNUSED(inTimeStamp), UInt32 UNUSED(inBusNumber),
UInt32 UNUSED(inNumberFrames), AudioBufferList *ioData)
{
ALCcoreAudioPlayback_lock(this);
lock();
aluMixData(mDevice, ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize/mFrameSize);
ALCcoreAudioPlayback_unlock(this);
unlock();
return noErr;
}
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name)
ALCenum CoreAudioPlayback::open(const ALCchar *name)
{
if(!name)
name = ca_device;
@@ -135,7 +119,7 @@ static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCch
return ALC_INVALID_VALUE;
}
OSStatus err{AudioComponentInstanceNew(comp, &self->mAudioUnit)};
OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
if(err != noErr)
{
ERR("AudioComponentInstanceNew failed\n");
@@ -143,32 +127,29 @@ static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCch
}
/* init and start the default audio unit... */
err = AudioUnitInitialize(self->mAudioUnit);
err = AudioUnitInitialize(mAudioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
AudioComponentInstanceDispose(self->mAudioUnit);
AudioComponentInstanceDispose(mAudioUnit);
return ALC_INVALID_VALUE;
}
ALCdevice *device{self->mDevice};
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
ALCboolean CoreAudioPlayback::reset()
{
ALCdevice *device{self->mDevice};
OSStatus err{AudioUnitUninitialize(self->mAudioUnit)};
OSStatus err{AudioUnitUninitialize(mAudioUnit)};
if(err != noErr)
ERR("-- AudioUnitUninitialize failed.\n");
/* retrieve default output unit's properties (output side) */
AudioStreamBasicDescription streamFormat{};
auto size = static_cast<UInt32>(sizeof(AudioStreamBasicDescription));
err = AudioUnitGetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, 0, &streamFormat, &size);
err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
0, &streamFormat, &size);
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
@@ -186,19 +167,19 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
#endif
/* set default output unit's input side to match output side */
err = AudioUnitSetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &streamFormat, size);
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
0, &streamFormat, size);
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
if(device->Frequency != streamFormat.mSampleRate)
if(mDevice->Frequency != streamFormat.mSampleRate)
{
device->NumUpdates = static_cast<ALuint>(
(ALuint64)device->NumUpdates*streamFormat.mSampleRate/device->Frequency);
device->Frequency = streamFormat.mSampleRate;
mDevice->NumUpdates = static_cast<ALuint>(
(ALuint64)mDevice->NumUpdates*streamFormat.mSampleRate/mDevice->Frequency);
mDevice->Frequency = streamFormat.mSampleRate;
}
/* FIXME: How to tell what channels are what in the output device, and how
@@ -206,53 +187,53 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
switch(streamFormat.mChannelsPerFrame)
{
case 1:
device->FmtChans = DevFmtMono;
mDevice->FmtChans = DevFmtMono;
break;
case 2:
device->FmtChans = DevFmtStereo;
mDevice->FmtChans = DevFmtStereo;
break;
case 4:
device->FmtChans = DevFmtQuad;
mDevice->FmtChans = DevFmtQuad;
break;
case 6:
device->FmtChans = DevFmtX51;
mDevice->FmtChans = DevFmtX51;
break;
case 7:
device->FmtChans = DevFmtX61;
mDevice->FmtChans = DevFmtX61;
break;
case 8:
device->FmtChans = DevFmtX71;
mDevice->FmtChans = DevFmtX71;
break;
default:
ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
device->FmtChans = DevFmtStereo;
mDevice->FmtChans = DevFmtStereo;
streamFormat.mChannelsPerFrame = 2;
break;
}
SetDefaultWFXChannelOrder(device);
SetDefaultWFXChannelOrder(mDevice);
/* use channel count and sample rate from the default output unit's current
* parameters, but reset everything else */
streamFormat.mFramesPerPacket = 1;
streamFormat.mFormatFlags = 0;
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtUByte:
device->FmtType = DevFmtByte;
mDevice->FmtType = DevFmtByte;
/* fall-through */
case DevFmtByte:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 8;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
mDevice->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 16;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
mDevice->FmtType = DevFmtInt;
/* fall-through */
case DevFmtInt:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
@@ -270,8 +251,8 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
kLinearPCMFormatFlagIsPacked;
err = AudioUnitSetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
0, &streamFormat, sizeof(AudioStreamBasicDescription));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -279,12 +260,12 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
}
/* setup callback */
self->mFrameSize = device->frameSizeFromFmt();
mFrameSize = mDevice->frameSizeFromFmt();
AURenderCallbackStruct input{};
input.inputProc = ALCcoreAudioPlayback::MixerProcC;
input.inputProcRefCon = self;
input.inputProc = CoreAudioPlayback::MixerProcC;
input.inputProcRefCon = this;
err = AudioUnitSetProperty(self->mAudioUnit, kAudioUnitProperty_SetRenderCallback,
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
@@ -293,7 +274,7 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
}
/* init the default audio unit... */
err = AudioUnitInitialize(self->mAudioUnit);
err = AudioUnitInitialize(mAudioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
@@ -303,9 +284,9 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
return ALC_TRUE;
}
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
ALCboolean CoreAudioPlayback::start()
{
OSStatus err{AudioOutputUnitStart(self->mAudioUnit)};
OSStatus err{AudioOutputUnitStart(mAudioUnit)};
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
@@ -314,17 +295,17 @@ static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
return ALC_TRUE;
}
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
void CoreAudioPlayback::stop()
{
OSStatus err{AudioOutputUnitStop(self->mAudioUnit)};
OSStatus err{AudioOutputUnitStop(mAudioUnit)};
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
struct ALCcoreAudioCapture final : public ALCbackend {
ALCcoreAudioCapture(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCcoreAudioCapture() override;
struct CoreAudioCapture final : public BackendBase {
CoreAudioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~CoreAudioCapture() override;
static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
@@ -333,6 +314,12 @@ struct ALCcoreAudioCapture final : public ALCbackend {
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList *ioData);
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(void *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
AudioUnit mAudioUnit{0};
ALuint mFrameSize{0u};
@@ -341,34 +328,12 @@ struct ALCcoreAudioCapture final : public ALCbackend {
SampleConverterPtr mConverter;
RingBufferPtr mRing{nullptr};
static constexpr inline const char *CurrentPrefix() noexcept { return "CoreAudioCapture::"; }
DEF_NEWDEL(CoreAudioCapture)
};
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self);
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name);
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self);
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self);
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self);
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture)
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
{
new (self) ALCcoreAudioCapture{device};
SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
}
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
{ self->~ALCcoreAudioCapture(); }
ALCcoreAudioCapture::~ALCcoreAudioCapture()
CoreAudioCapture::~CoreAudioCapture()
{
if(mAudioUnit)
AudioComponentInstanceDispose(mAudioUnit);
@@ -376,17 +341,17 @@ ALCcoreAudioCapture::~ALCcoreAudioCapture()
}
OSStatus ALCcoreAudioCapture::RecordProcC(void *inRefCon,
OSStatus CoreAudioCapture::RecordProcC(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
return static_cast<ALCcoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
inBusNumber, inNumberFrames, ioData);
}
OSStatus ALCcoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData)
OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
const AudioTimeStamp *inTimeStamp, UInt32 UNUSED(inBusNumber), UInt32 inNumberFrames,
AudioBufferList* UNUSED(ioData))
{
AudioUnitRenderActionFlags flags = 0;
union {
@@ -430,9 +395,8 @@ OSStatus ALCcoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFla
}
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name)
ALCenum CoreAudioCapture::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
AudioStreamBasicDescription requestedFormat; // The application requested format
AudioStreamBasicDescription hardwareFormat; // The hardware format
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
@@ -469,7 +433,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
}
// Open the component
err = AudioComponentInstanceNew(comp, &self->mAudioUnit);
err = AudioComponentInstanceNew(comp, &mAudioUnit);
if(err != noErr)
{
ERR("AudioComponentInstanceNew failed\n");
@@ -478,7 +442,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
// Turn off AudioUnit output
enableIO = 0;
err = AudioUnitSetProperty(self->mAudioUnit, kAudioOutputUnitProperty_EnableIO,
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
if(err != noErr)
{
@@ -488,7 +452,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
// Turn on AudioUnit input
enableIO = 1;
err = AudioUnitSetProperty(self->mAudioUnit, kAudioOutputUnitProperty_EnableIO,
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
if(err != noErr)
{
@@ -519,7 +483,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
}
// Track the input device
err = AudioUnitSetProperty(self->mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
if(err != noErr)
{
@@ -530,10 +494,10 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
#endif
// set capture callback
input.inputProc = ALCcoreAudioCapture::RecordProcC;
input.inputProcRefCon = self;
input.inputProc = CoreAudioCapture::RecordProcC;
input.inputProcRefCon = this;
err = AudioUnitSetProperty(self->mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
@@ -542,7 +506,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
}
// Initialize the device
err = AudioUnitInitialize(self->mAudioUnit);
err = AudioUnitInitialize(mAudioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
@@ -551,8 +515,8 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
// Get the hardware format
propertySize = sizeof(AudioStreamBasicDescription);
err = AudioUnitGetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1, &hardwareFormat, &propertySize);
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
@@ -560,7 +524,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
}
// Set up the requested format description
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtUByte:
requestedFormat.mBitsPerChannel = 8;
@@ -581,11 +545,11 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
ERR("%s samples not supported\n", DevFmtTypeString(mDevice->FmtType));
return ALC_INVALID_VALUE;
}
switch(device->FmtChans)
switch(mDevice->FmtChans)
{
case DevFmtMono:
requestedFormat.mChannelsPerFrame = 1;
@@ -600,20 +564,20 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
case DevFmtX61:
case DevFmtX71:
case DevFmtAmbi3D:
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
ERR("%s not supported\n", DevFmtChannelsString(mDevice->FmtChans));
return ALC_INVALID_VALUE;
}
requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
requestedFormat.mSampleRate = device->Frequency;
requestedFormat.mSampleRate = mDevice->Frequency;
requestedFormat.mFormatID = kAudioFormatLinearPCM;
requestedFormat.mReserved = 0;
requestedFormat.mFramesPerPacket = 1;
// save requested format description for later use
self->mFormat = requestedFormat;
self->mFrameSize = device->frameSizeFromFmt();
mFormat = requestedFormat;
mFrameSize = mDevice->frameSizeFromFmt();
// Use intermediate format for sample rate conversion (outputFormat)
// Set sample rate to the same as hardware for resampling later
@@ -622,8 +586,8 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
// The output format should be the requested format, but using the hardware sample rate
// This is because the AudioUnit will automatically scale other properties, except for sample rate
err = AudioUnitSetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1, (void*)&outputFormat, sizeof(outputFormat));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -631,9 +595,9 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
}
// Set the AudioUnit output format frame count
ALuint64 FrameCount64{device->UpdateSize};
FrameCount64 = (FrameCount64*outputFormat.mSampleRate + device->Frequency-1) /
device->Frequency;
ALuint64 FrameCount64{mDevice->UpdateSize};
FrameCount64 = (FrameCount64*outputFormat.mSampleRate + mDevice->Frequency-1) /
mDevice->Frequency;
FrameCount64 += MAX_RESAMPLE_PADDING*2;
if(FrameCount64 > std::numeric_limits<uint32_t>::max()/2)
{
@@ -642,7 +606,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
}
outputFrameCount = static_cast<uint32_t>(FrameCount64);
err = AudioUnitSetProperty(self->mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
if(err != noErr)
{
@@ -651,22 +615,22 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
}
// Set up sample converter if needed
if(outputFormat.mSampleRate != device->Frequency)
self->mConverter = CreateSampleConverter(device->FmtType, device->FmtType,
self->mFormat.mChannelsPerFrame, hardwareFormat.mSampleRate, device->Frequency,
if(outputFormat.mSampleRate != mDevice->Frequency)
mConverter = CreateSampleConverter(mDevice->FmtType, mDevice->FmtType,
mFormat.mChannelsPerFrame, hardwareFormat.mSampleRate, mDevice->Frequency,
BSinc24Resampler);
self->mRing = CreateRingBuffer(outputFrameCount, self->mFrameSize, false);
if(!self->mRing) return ALC_INVALID_VALUE;
mRing = CreateRingBuffer(outputFrameCount, mFrameSize, false);
if(!mRing) return ALC_INVALID_VALUE;
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
ALCboolean CoreAudioCapture::start()
{
OSStatus err{AudioOutputUnitStart(self->mAudioUnit)};
OSStatus err{AudioOutputUnitStart(mAudioUnit)};
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
@@ -675,48 +639,44 @@ static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
return ALC_TRUE;
}
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
void CoreAudioCapture::stop()
{
OSStatus err{AudioOutputUnitStop(self->mAudioUnit)};
OSStatus err{AudioOutputUnitStop(mAudioUnit)};
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
ALCenum CoreAudioCapture::captureSamples(void *buffer, ALCuint samples)
{
RingBuffer *ring{self->mRing.get()};
if(!self->mConverter)
if(!mConverter)
{
ring->read(buffer, samples);
mRing->read(buffer, samples);
return ALC_NO_ERROR;
}
auto rec_vec = ring->getReadVector();
auto rec_vec = mRing->getReadVector();
const void *src0{rec_vec.first.buf};
auto src0len = static_cast<ALsizei>(rec_vec.first.len);
auto got = static_cast<ALuint>(SampleConverterInput(self->mConverter.get(), &src0, &src0len,
auto got = static_cast<ALuint>(SampleConverterInput(mConverter.get(), &src0, &src0len,
buffer, samples));
size_t total_read{rec_vec.first.len - src0len};
if(got < samples && !src0len && rec_vec.second.len > 0)
{
const void *src1{rec_vec.second.buf};
auto src1len = static_cast<ALsizei>(rec_vec.second.len);
got += static_cast<ALuint>(SampleConverterInput(self->mConverter.get(), &src1, &src1len,
got += static_cast<ALuint>(SampleConverterInput(mConverter.get(), &src1, &src1len,
static_cast<char*>(buffer)+got, samples-got));
total_read += rec_vec.second.len - src1len;
}
ring->readAdvance(total_read);
mRing->readAdvance(total_read);
return ALC_NO_ERROR;
}
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
ALCuint CoreAudioCapture::availableSamples()
{
RingBuffer *ring{self->mRing.get()};
if(!self->mConverter) return ring->readSpace();
return SampleConverterAvailableOut(self->mConverter.get(), ring->readSpace());
if(!mConverter) return mRing->readSpace();
return SampleConverterAvailableOut(mConverter.get(), mRing->readSpace());
}
} // namespace
@@ -744,20 +704,11 @@ void CoreAudioBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *CoreAudioBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *CoreAudioBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCcoreAudioPlayback *backend;
NEW_OBJ(backend, ALCcoreAudioPlayback)(device);
return backend;
}
return new CoreAudioPlayback{device};
if(type == ALCbackend_Capture)
{
ALCcoreAudioCapture *backend;
NEW_OBJ(backend, ALCcoreAudioCapture)(device);
return backend;
}
return new CoreAudioCapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+171 -228
View File
@@ -184,12 +184,17 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUS
}
struct ALCdsoundPlayback final : public ALCbackend {
ALCdsoundPlayback(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCdsoundPlayback() override;
struct DSoundPlayback final : public BackendBase {
DSoundPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~DSoundPlayback() override;
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
IDirectSound *mDS{nullptr};
IDirectSoundBuffer *mPrimaryBuffer{nullptr};
IDirectSoundBuffer *mBuffer{nullptr};
@@ -198,34 +203,12 @@ struct ALCdsoundPlayback final : public ALCbackend {
std::atomic<ALenum> mKillNow{AL_TRUE};
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "DSoundPlayback::"; }
DEF_NEWDEL(DSoundPlayback)
};
void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device);
void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self);
ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name);
ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self);
ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self);
void ALCdsoundPlayback_stop(ALCdsoundPlayback *self);
DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock)
DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCdsoundPlayback);
void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device)
{
new (self) ALCdsoundPlayback{device};
SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self);
}
void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self)
{ self->~ALCdsoundPlayback(); }
ALCdsoundPlayback::~ALCdsoundPlayback()
DSoundPlayback::~DSoundPlayback()
{
if(mNotifies)
mNotifies->Release();
@@ -246,7 +229,7 @@ ALCdsoundPlayback::~ALCdsoundPlayback()
}
FORCE_ALIGN int ALCdsoundPlayback::mixerProc()
FORCE_ALIGN int DSoundPlayback::mixerProc()
{
SetRTPriority();
althrd_setname(MIXER_THREAD_NAME);
@@ -257,9 +240,9 @@ FORCE_ALIGN int ALCdsoundPlayback::mixerProc()
if(FAILED(err))
{
ERR("Failed to get buffer caps: 0x%lx\n", err);
ALCdsoundPlayback_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failure retrieving playback buffer info: 0x%lx", err);
ALCdsoundPlayback_unlock(this);
unlock();
return 1;
}
@@ -285,9 +268,9 @@ FORCE_ALIGN int ALCdsoundPlayback::mixerProc()
if(FAILED(err))
{
ERR("Failed to play buffer: 0x%lx\n", err);
ALCdsoundPlayback_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failure starting playback: 0x%lx", err);
ALCdsoundPlayback_unlock(this);
unlock();
return 1;
}
Playing = true;
@@ -319,25 +302,22 @@ FORCE_ALIGN int ALCdsoundPlayback::mixerProc()
}
}
// Successfully locked the output buffer
if(SUCCEEDED(err))
{
// If we have an active context, mix data directly into output buffer otherwise fill with silence
ALCdsoundPlayback_lock(this);
lock();
aluMixData(mDevice, WritePtr1, WriteCnt1/FrameSize);
if(WriteCnt2 > 0)
aluMixData(mDevice, WritePtr2, WriteCnt2/FrameSize);
ALCdsoundPlayback_unlock(this);
unlock();
// Unlock output buffer only when successfully locked
mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
}
else
{
ERR("Buffer lock error: %#lx\n", err);
ALCdsoundPlayback_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failed to lock output buffer: 0x%lx", err);
ALCdsoundPlayback_unlock(this);
unlock();
return 1;
}
@@ -349,10 +329,8 @@ FORCE_ALIGN int ALCdsoundPlayback::mixerProc()
return 0;
}
ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *deviceName)
ALCenum DSoundPlayback::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
HRESULT hr;
if(PlaybackDevices.empty())
{
@@ -366,16 +344,16 @@ ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *deviceNam
}
const GUID *guid{nullptr};
if(!deviceName && !PlaybackDevices.empty())
if(!name && !PlaybackDevices.empty())
{
deviceName = PlaybackDevices[0].name.c_str();
name = PlaybackDevices[0].name.c_str();
guid = &PlaybackDevices[0].guid;
}
else
{
auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
[deviceName](const DevMap &entry) -> bool
{ return entry.name == deviceName; }
[name](const DevMap &entry) -> bool
{ return entry.name == name; }
);
if(iter == PlaybackDevices.cend())
return ALC_INVALID_VALUE;
@@ -383,52 +361,50 @@ ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *deviceNam
}
hr = DS_OK;
self->mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if(!self->mNotifyEvent) hr = E_FAIL;
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if(!mNotifyEvent) hr = E_FAIL;
//DirectSound Init code
if(SUCCEEDED(hr))
hr = DirectSoundCreate(guid, &self->mDS, nullptr);
hr = DirectSoundCreate(guid, &mDS, nullptr);
if(SUCCEEDED(hr))
hr = self->mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
hr = mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
if(FAILED(hr))
{
ERR("Device init failed: 0x%08lx\n", hr);
return ALC_INVALID_VALUE;
}
device->DeviceName = deviceName;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
ALCboolean DSoundPlayback::reset()
{
ALCdevice *device{self->mDevice};
if(mNotifies)
mNotifies->Release();
mNotifies = nullptr;
if(mBuffer)
mBuffer->Release();
mBuffer = nullptr;
if(mPrimaryBuffer)
mPrimaryBuffer->Release();
mPrimaryBuffer = nullptr;
if(self->mNotifies)
self->mNotifies->Release();
self->mNotifies = nullptr;
if(self->mBuffer)
self->mBuffer->Release();
self->mBuffer = nullptr;
if(self->mPrimaryBuffer)
self->mPrimaryBuffer->Release();
self->mPrimaryBuffer = nullptr;
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
device->FmtType = DevFmtUByte;
mDevice->FmtType = DevFmtUByte;
break;
case DevFmtFloat:
if((device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
if((mDevice->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
break;
/* fall-through */
case DevFmtUShort:
device->FmtType = DevFmtShort;
mDevice->FmtType = DevFmtShort;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
mDevice->FmtType = DevFmtInt;
break;
case DevFmtUByte:
case DevFmtShort:
@@ -438,37 +414,37 @@ ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
WAVEFORMATEXTENSIBLE OutputType{};
DWORD speakers;
HRESULT hr{self->mDS->GetSpeakerConfig(&speakers)};
HRESULT hr{mDS->GetSpeakerConfig(&speakers)};
if(SUCCEEDED(hr))
{
speakers = DSSPEAKER_CONFIG(speakers);
if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
if(!(mDevice->Flags&DEVICE_CHANNELS_REQUEST))
{
if(speakers == DSSPEAKER_MONO)
device->FmtChans = DevFmtMono;
mDevice->FmtChans = DevFmtMono;
else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
device->FmtChans = DevFmtStereo;
mDevice->FmtChans = DevFmtStereo;
else if(speakers == DSSPEAKER_QUAD)
device->FmtChans = DevFmtQuad;
mDevice->FmtChans = DevFmtQuad;
else if(speakers == DSSPEAKER_5POINT1_SURROUND)
device->FmtChans = DevFmtX51;
mDevice->FmtChans = DevFmtX51;
else if(speakers == DSSPEAKER_5POINT1_BACK)
device->FmtChans = DevFmtX51Rear;
mDevice->FmtChans = DevFmtX51Rear;
else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
device->FmtChans = DevFmtX71;
mDevice->FmtChans = DevFmtX71;
else
ERR("Unknown system speaker config: 0x%lx\n", speakers);
}
device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
speakers == DSSPEAKER_HEADPHONE);
mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
speakers == DSSPEAKER_HEADPHONE);
switch(device->FmtChans)
switch(mDevice->FmtChans)
{
case DevFmtMono:
OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
break;
case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
mDevice->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
@@ -520,62 +496,62 @@ ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
retry_open:
hr = S_OK;
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
OutputType.Format.nChannels = device->channelsFromFmt();
OutputType.Format.wBitsPerSample = device->bytesFromFmt() * 8;
OutputType.Format.nChannels = mDevice->channelsFromFmt();
OutputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
OutputType.Format.nSamplesPerSec = device->Frequency;
OutputType.Format.nSamplesPerSec = mDevice->Frequency;
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
OutputType.Format.cbSize = 0;
}
if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
{
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
if(device->FmtType == DevFmtFloat)
if(mDevice->FmtType == DevFmtFloat)
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
if(self->mPrimaryBuffer)
self->mPrimaryBuffer->Release();
self->mPrimaryBuffer = nullptr;
if(mPrimaryBuffer)
mPrimaryBuffer->Release();
mPrimaryBuffer = nullptr;
}
else
{
if(SUCCEEDED(hr) && !self->mPrimaryBuffer)
if(SUCCEEDED(hr) && !mPrimaryBuffer)
{
DSBUFFERDESC DSBDescription{};
DSBDescription.dwSize = sizeof(DSBDescription);
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
hr = self->mDS->CreateSoundBuffer(&DSBDescription, &self->mPrimaryBuffer, nullptr);
hr = mDS->CreateSoundBuffer(&DSBDescription, &mPrimaryBuffer, nullptr);
}
if(SUCCEEDED(hr))
hr = self->mPrimaryBuffer->SetFormat(&OutputType.Format);
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
}
if(SUCCEEDED(hr))
{
if(device->NumUpdates > MAX_UPDATES)
if(mDevice->NumUpdates > MAX_UPDATES)
{
device->UpdateSize = (device->UpdateSize*device->NumUpdates +
MAX_UPDATES-1) / MAX_UPDATES;
device->NumUpdates = MAX_UPDATES;
mDevice->UpdateSize = (mDevice->UpdateSize*mDevice->NumUpdates + MAX_UPDATES-1) /
MAX_UPDATES;
mDevice->NumUpdates = MAX_UPDATES;
}
DSBUFFERDESC DSBDescription{};
DSBDescription.dwSize = sizeof(DSBDescription);
DSBDescription.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 |
DSBCAPS_GLOBALFOCUS;
DSBDescription.dwBufferBytes = device->UpdateSize * device->NumUpdates *
DSBDescription.dwBufferBytes = mDevice->UpdateSize * mDevice->NumUpdates *
OutputType.Format.nBlockAlign;
DSBDescription.lpwfxFormat = &OutputType.Format;
hr = self->mDS->CreateSoundBuffer(&DSBDescription, &self->mBuffer, nullptr);
if(FAILED(hr) && device->FmtType == DevFmtFloat)
hr = mDS->CreateSoundBuffer(&DSBDescription, &mBuffer, nullptr);
if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
{
device->FmtType = DevFmtShort;
mDevice->FmtType = DevFmtShort;
goto retry_open;
}
}
@@ -583,50 +559,50 @@ retry_open:
if(SUCCEEDED(hr))
{
void *ptr;
hr = self->mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
if(SUCCEEDED(hr))
{
auto Notifies = static_cast<IDirectSoundNotify*>(ptr);
self->mNotifies = Notifies;
mNotifies = Notifies;
device->NumUpdates = minu(device->NumUpdates, MAX_UPDATES);
mDevice->NumUpdates = minu(mDevice->NumUpdates, MAX_UPDATES);
std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots;
for(ALuint i{0};i < device->NumUpdates;++i)
for(ALuint i{0};i < mDevice->NumUpdates;++i)
{
nots[i].dwOffset = i * device->UpdateSize * OutputType.Format.nBlockAlign;
nots[i].hEventNotify = self->mNotifyEvent;
nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign;
nots[i].hEventNotify = mNotifyEvent;
}
if(Notifies->SetNotificationPositions(device->NumUpdates, nots.data()) != DS_OK)
if(Notifies->SetNotificationPositions(mDevice->NumUpdates, nots.data()) != DS_OK)
hr = E_FAIL;
}
}
if(FAILED(hr))
{
if(self->mNotifies)
self->mNotifies->Release();
self->mNotifies = nullptr;
if(self->mBuffer)
self->mBuffer->Release();
self->mBuffer = nullptr;
if(self->mPrimaryBuffer)
self->mPrimaryBuffer->Release();
self->mPrimaryBuffer = nullptr;
if(mNotifies)
mNotifies->Release();
mNotifies = nullptr;
if(mBuffer)
mBuffer->Release();
mBuffer = nullptr;
if(mPrimaryBuffer)
mPrimaryBuffer->Release();
mPrimaryBuffer = nullptr;
return ALC_FALSE;
}
ResetEvent(self->mNotifyEvent);
SetDefaultWFXChannelOrder(device);
ResetEvent(mNotifyEvent);
SetDefaultWFXChannelOrder(mDevice);
return ALC_TRUE;
}
ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self)
ALCboolean DSoundPlayback::start()
{
try {
self->mKillNow.store(AL_FALSE, std::memory_order_release);
self->mThread = std::thread(std::mem_fn(&ALCdsoundPlayback::mixerProc), self);
mKillNow.store(AL_FALSE, std::memory_order_release);
mThread = std::thread{std::mem_fn(&DSoundPlayback::mixerProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -637,20 +613,25 @@ ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self)
return ALC_FALSE;
}
void ALCdsoundPlayback_stop(ALCdsoundPlayback *self)
void DSoundPlayback::stop()
{
if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !self->mThread.joinable())
if(mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !mThread.joinable())
return;
mThread.join();
self->mThread.join();
self->mBuffer->Stop();
mBuffer->Stop();
}
struct ALCdsoundCapture final : public ALCbackend {
ALCdsoundCapture(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCdsoundCapture() override;
struct DSoundCapture final : public BackendBase {
DSoundCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~DSoundCapture() override;
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(void *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
IDirectSoundCapture *mDSC{nullptr};
IDirectSoundCaptureBuffer *mDSCbuffer{nullptr};
@@ -658,32 +639,12 @@ struct ALCdsoundCapture final : public ALCbackend {
DWORD mCursor{0u};
RingBufferPtr mRing;
static constexpr inline const char *CurrentPrefix() noexcept { return "DSoundCapture::"; }
DEF_NEWDEL(DSoundCapture)
};
void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
void ALCdsoundCapture_Destruct(ALCdsoundCapture *self);
ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name);
DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset)
ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self);
void ALCdsoundCapture_stop(ALCdsoundCapture *self);
ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples);
ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self);
DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock)
DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture)
DEFINE_ALCBACKEND_VTABLE(ALCdsoundCapture);
void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device)
{
new (self) ALCdsoundCapture{device};
SET_VTABLE2(ALCdsoundCapture, ALCbackend, self);
}
void ALCdsoundCapture_Destruct(ALCdsoundCapture *self)
{ self->~ALCdsoundCapture(); }
ALCdsoundCapture::~ALCdsoundCapture()
DSoundCapture::~DSoundCapture()
{
if(mDSCbuffer)
{
@@ -698,10 +659,8 @@ ALCdsoundCapture::~ALCdsoundCapture()
}
ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName)
ALCenum DSoundCapture::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
HRESULT hr;
if(CaptureDevices.empty())
{
@@ -715,28 +674,28 @@ ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName)
}
const GUID *guid{nullptr};
if(!deviceName && !CaptureDevices.empty())
if(!name && !CaptureDevices.empty())
{
deviceName = CaptureDevices[0].name.c_str();
name = CaptureDevices[0].name.c_str();
guid = &CaptureDevices[0].guid;
}
else
{
auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
[deviceName](const DevMap &entry) -> bool
{ return entry.name == deviceName; }
[name](const DevMap &entry) -> bool
{ return entry.name == name; }
);
if(iter == CaptureDevices.cend())
return ALC_INVALID_VALUE;
guid = &iter->guid;
}
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
WARN("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
return ALC_INVALID_ENUM;
case DevFmtUByte:
@@ -747,7 +706,7 @@ ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName)
}
WAVEFORMATEXTENSIBLE InputType{};
switch(device->FmtChans)
switch(mDevice->FmtChans)
{
case DevFmtMono:
InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
@@ -798,31 +757,31 @@ ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName)
SPEAKER_SIDE_RIGHT;
break;
case DevFmtAmbi3D:
WARN("%s capture not supported\n", DevFmtChannelsString(device->FmtChans));
WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
return ALC_INVALID_ENUM;
}
InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
InputType.Format.nChannels = device->channelsFromFmt();
InputType.Format.wBitsPerSample = device->bytesFromFmt() * 8;
InputType.Format.nChannels = mDevice->channelsFromFmt();
InputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
InputType.Format.nSamplesPerSec = device->Frequency;
InputType.Format.nSamplesPerSec = mDevice->Frequency;
InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
InputType.Format.cbSize = 0;
InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
if(device->FmtType == DevFmtFloat)
if(mDevice->FmtType == DevFmtFloat)
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
if(InputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
{
InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
}
ALuint samples{device->UpdateSize * device->NumUpdates};
samples = maxu(samples, 100 * device->Frequency / 1000);
ALuint samples{mDevice->UpdateSize * mDevice->NumUpdates};
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
DSCBUFFERDESC DSCBDescription{};
DSCBDescription.dwSize = sizeof(DSCBDescription);
@@ -831,107 +790,101 @@ ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName)
DSCBDescription.lpwfxFormat = &InputType.Format;
//DirectSoundCapture Init code
hr = DirectSoundCaptureCreate(guid, &self->mDSC, nullptr);
hr = DirectSoundCaptureCreate(guid, &mDSC, nullptr);
if(SUCCEEDED(hr))
self->mDSC->CreateCaptureBuffer(&DSCBDescription, &self->mDSCbuffer, nullptr);
mDSC->CreateCaptureBuffer(&DSCBDescription, &mDSCbuffer, nullptr);
if(SUCCEEDED(hr))
{
self->mRing = CreateRingBuffer(device->UpdateSize*device->NumUpdates,
mRing = CreateRingBuffer(mDevice->UpdateSize*mDevice->NumUpdates,
InputType.Format.nBlockAlign, false);
if(!self->mRing) hr = DSERR_OUTOFMEMORY;
if(!mRing) hr = DSERR_OUTOFMEMORY;
}
if(FAILED(hr))
{
ERR("Device init failed: 0x%08lx\n", hr);
self->mRing = nullptr;
if(self->mDSCbuffer)
self->mDSCbuffer->Release();
self->mDSCbuffer = nullptr;
if(self->mDSC)
self->mDSC->Release();
self->mDSC = nullptr;
mRing = nullptr;
if(mDSCbuffer)
mDSCbuffer->Release();
mDSCbuffer = nullptr;
if(mDSC)
mDSC->Release();
mDSC = nullptr;
return ALC_INVALID_VALUE;
}
self->mBufferBytes = DSCBDescription.dwBufferBytes;
SetDefaultWFXChannelOrder(device);
mBufferBytes = DSCBDescription.dwBufferBytes;
SetDefaultWFXChannelOrder(mDevice);
device->DeviceName = deviceName;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
ALCboolean DSoundCapture::start()
{
HRESULT hr{self->mDSCbuffer->Start(DSCBSTART_LOOPING)};
HRESULT hr{mDSCbuffer->Start(DSCBSTART_LOOPING)};
if(FAILED(hr))
{
ERR("start failed: 0x%08lx\n", hr);
aluHandleDisconnect(self->mDevice, "Failure starting capture: 0x%lx", hr);
aluHandleDisconnect(mDevice, "Failure starting capture: 0x%lx", hr);
return ALC_FALSE;
}
return ALC_TRUE;
}
void ALCdsoundCapture_stop(ALCdsoundCapture *self)
void DSoundCapture::stop()
{
HRESULT hr{self->mDSCbuffer->Stop()};
HRESULT hr{mDSCbuffer->Stop()};
if(FAILED(hr))
{
ERR("stop failed: 0x%08lx\n", hr);
aluHandleDisconnect(self->mDevice, "Failure stopping capture: 0x%lx", hr);
aluHandleDisconnect(mDevice, "Failure stopping capture: 0x%lx", hr);
}
}
ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples)
ALCenum DSoundCapture::captureSamples(void *buffer, ALCuint samples)
{
RingBuffer *ring{self->mRing.get()};
ring->read(buffer, samples);
mRing->read(buffer, samples);
return ALC_NO_ERROR;
}
ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
ALCuint DSoundCapture::availableSamples()
{
ALCdevice *device{self->mDevice};
RingBuffer *ring{self->mRing.get()};
if(!mDevice->Connected.load(std::memory_order_acquire))
return static_cast<ALCuint>(mRing->readSpace());
if(!device->Connected.load(std::memory_order_acquire))
return static_cast<ALCuint>(ring->readSpace());
ALsizei FrameSize{device->frameSizeFromFmt()};
DWORD BufferBytes{self->mBufferBytes};
DWORD LastCursor{self->mCursor};
ALsizei FrameSize{mDevice->frameSizeFromFmt()};
DWORD BufferBytes{mBufferBytes};
DWORD LastCursor{mCursor};
DWORD ReadCursor;
void *ReadPtr1, *ReadPtr2;
DWORD ReadCnt1, ReadCnt2;
HRESULT hr{self->mDSCbuffer->GetCurrentPosition(nullptr, &ReadCursor)};
HRESULT hr{mDSCbuffer->GetCurrentPosition(nullptr, &ReadCursor)};
if(SUCCEEDED(hr))
{
DWORD NumBytes{(ReadCursor-LastCursor + BufferBytes) % BufferBytes};
if(!NumBytes) return static_cast<ALCubyte>(ring->readSpace());
hr = self->mDSCbuffer->Lock(LastCursor, NumBytes, &ReadPtr1, &ReadCnt1,
&ReadPtr2, &ReadCnt2, 0);
if(!NumBytes) return static_cast<ALCubyte>(mRing->readSpace());
hr = mDSCbuffer->Lock(LastCursor, NumBytes, &ReadPtr1, &ReadCnt1, &ReadPtr2, &ReadCnt2, 0);
}
if(SUCCEEDED(hr))
{
ring->write(ReadPtr1, ReadCnt1/FrameSize);
mRing->write(ReadPtr1, ReadCnt1/FrameSize);
if(ReadPtr2 != nullptr && ReadCnt2 > 0)
ring->write(ReadPtr2, ReadCnt2/FrameSize);
hr = self->mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
self->mCursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
mRing->write(ReadPtr2, ReadCnt2/FrameSize);
hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
mCursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
}
if(FAILED(hr))
{
ERR("update failed: 0x%08lx\n", hr);
aluHandleDisconnect(device, "Failure retrieving capture data: 0x%lx", hr);
aluHandleDisconnect(mDevice, "Failure retrieving capture data: 0x%lx", hr);
}
return static_cast<ALCuint>(ring->readSpace());
return static_cast<ALCuint>(mRing->readSpace());
}
} // namespace
@@ -996,21 +949,11 @@ void DSoundBackendFactory::probe(DevProbe type, std::string *outnames)
CoUninitialize();
}
ALCbackend *DSoundBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *DSoundBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCdsoundPlayback *backend;
NEW_OBJ(backend, ALCdsoundPlayback)(device);
return backend;
}
return new DSoundPlayback{device};
if(type == ALCbackend_Capture)
{
ALCdsoundCapture *backend;
NEW_OBJ(backend, ALCdsoundCapture)(device);
return backend;
}
return new DSoundCapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+87 -112
View File
@@ -148,9 +148,9 @@ ALCboolean jack_load(void)
}
struct ALCjackPlayback final : public ALCbackend {
ALCjackPlayback(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCjackPlayback() override;
struct JackPlayback final : public BackendBase {
JackPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~JackPlayback() override;
static int bufferSizeNotifyC(jack_nframes_t numframes, void *arg);
int bufferSizeNotify(jack_nframes_t numframes);
@@ -160,6 +160,12 @@ struct ALCjackPlayback final : public ALCbackend {
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
ClockLatency getClockLatency() override;
jack_client_t *mClient{nullptr};
jack_port_t *mPort[MAX_OUTPUT_CHANNELS]{};
@@ -168,34 +174,12 @@ struct ALCjackPlayback final : public ALCbackend {
std::atomic<bool> mKillNow{true};
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "JackPlayback::"; }
DEF_NEWDEL(JackPlayback)
};
void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device);
void ALCjackPlayback_Destruct(ALCjackPlayback *self);
ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name);
ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self);
ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
void ALCjackPlayback_stop(ALCjackPlayback *self);
DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples)
ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self);
DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock)
DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback);
void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device)
{
new (self) ALCjackPlayback{device};
SET_VTABLE2(ALCjackPlayback, ALCbackend, self);
}
void ALCjackPlayback_Destruct(ALCjackPlayback *self)
{ self->~ALCjackPlayback(); }
ALCjackPlayback::~ALCjackPlayback()
JackPlayback::~JackPlayback()
{
if(!mClient)
return;
@@ -210,12 +194,12 @@ ALCjackPlayback::~ALCjackPlayback()
}
int ALCjackPlayback::bufferSizeNotifyC(jack_nframes_t numframes, void *arg)
{ return static_cast<ALCjackPlayback*>(arg)->bufferSizeNotify(numframes); }
int JackPlayback::bufferSizeNotifyC(jack_nframes_t numframes, void *arg)
{ return static_cast<JackPlayback*>(arg)->bufferSizeNotify(numframes); }
int ALCjackPlayback::bufferSizeNotify(jack_nframes_t numframes)
int JackPlayback::bufferSizeNotify(jack_nframes_t numframes)
{
ALCjackPlayback_lock(this);
lock();
mDevice->UpdateSize = numframes;
mDevice->NumUpdates = 2;
@@ -233,15 +217,15 @@ int ALCjackPlayback::bufferSizeNotify(jack_nframes_t numframes)
ERR("Failed to reallocate ringbuffer\n");
aluHandleDisconnect(mDevice, "Failed to reallocate %u-sample buffer", bufsize);
}
ALCjackPlayback_unlock(this);
unlock();
return 0;
}
int ALCjackPlayback::processC(jack_nframes_t numframes, void *arg)
{ return static_cast<ALCjackPlayback*>(arg)->process(numframes); }
int JackPlayback::processC(jack_nframes_t numframes, void *arg)
{ return static_cast<JackPlayback*>(arg)->process(numframes); }
int ALCjackPlayback::process(jack_nframes_t numframes)
int JackPlayback::process(jack_nframes_t numframes)
{
jack_default_audio_sample_t *out[MAX_OUTPUT_CHANNELS];
ALsizei numchans{0};
@@ -311,20 +295,20 @@ int ALCjackPlayback::process(jack_nframes_t numframes)
return 0;
}
int ALCjackPlayback::mixerProc()
int JackPlayback::mixerProc()
{
SetRTPriority();
althrd_setname(MIXER_THREAD_NAME);
ALCjackPlayback_lock(this);
lock();
while(!mKillNow.load(std::memory_order_acquire) &&
mDevice->Connected.load(std::memory_order_acquire))
{
if(mRing->writeSpace() < mDevice->UpdateSize)
{
ALCjackPlayback_unlock(this);
unlock();
mSem.wait();
ALCjackPlayback_lock(this);
lock();
continue;
}
@@ -340,13 +324,13 @@ int ALCjackPlayback::mixerProc()
aluMixData(mDevice, data.second.buf, len2);
mRing->writeAdvance(todo);
}
ALCjackPlayback_unlock(this);
unlock();
return 0;
}
ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
ALCenum JackPlayback::open(const ALCchar *name)
{
if(!name)
name = jackDevice;
@@ -355,8 +339,8 @@ ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
const char *client_name{"alsoft"};
jack_status_t status;
self->mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
if(self->mClient == nullptr)
mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
if(mClient == nullptr)
{
ERR("jack_client_open() failed, status = 0x%02x\n", status);
return ALC_INVALID_VALUE;
@@ -365,104 +349,102 @@ ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
TRACE("JACK server started\n");
if((status&JackNameNotUnique))
{
client_name = jack_get_client_name(self->mClient);
client_name = jack_get_client_name(mClient);
TRACE("Client name not unique, got `%s' instead\n", client_name);
}
jack_set_process_callback(self->mClient, &ALCjackPlayback::processC, self);
jack_set_buffer_size_callback(self->mClient, &ALCjackPlayback::bufferSizeNotifyC, self);
jack_set_process_callback(mClient, &JackPlayback::processC, this);
jack_set_buffer_size_callback(mClient, &JackPlayback::bufferSizeNotifyC, this);
ALCdevice *device{self->mDevice};
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
ALCboolean JackPlayback::reset()
{
std::for_each(std::begin(self->mPort), std::end(self->mPort),
[self](jack_port_t *port) -> void
{ if(port) jack_port_unregister(self->mClient, port); }
std::for_each(std::begin(mPort), std::end(mPort),
[this](jack_port_t *port) -> void
{ if(port) jack_port_unregister(mClient, port); }
);
std::fill(std::begin(self->mPort), std::end(self->mPort), nullptr);
std::fill(std::begin(mPort), std::end(mPort), nullptr);
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
* ready for when requested.
*/
ALCdevice *device{self->mDevice};
device->Frequency = jack_get_sample_rate(self->mClient);
device->UpdateSize = jack_get_buffer_size(self->mClient);
device->NumUpdates = 2;
mDevice->Frequency = jack_get_sample_rate(mClient);
mDevice->UpdateSize = jack_get_buffer_size(mClient);
mDevice->NumUpdates = 2;
ALuint bufsize{device->UpdateSize};
if(ConfigValueUInt(device->DeviceName.c_str(), "jack", "buffer-size", &bufsize))
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize;
ALuint bufsize{mDevice->UpdateSize};
if(ConfigValueUInt(mDevice->DeviceName.c_str(), "jack", "buffer-size", &bufsize))
bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
mDevice->NumUpdates = (bufsize+mDevice->UpdateSize) / mDevice->UpdateSize;
/* Force 32-bit float output. */
device->FmtType = DevFmtFloat;
mDevice->FmtType = DevFmtFloat;
ALsizei numchans{device->channelsFromFmt()};
auto ports_end = std::begin(self->mPort) + numchans;
auto bad_port = std::find_if_not(std::begin(self->mPort), ports_end,
[self](jack_port_t *&port) -> bool
ALsizei numchans{mDevice->channelsFromFmt()};
auto ports_end = std::begin(mPort) + numchans;
auto bad_port = std::find_if_not(std::begin(mPort), ports_end,
[this](jack_port_t *&port) -> bool
{
std::string name{"channel_" + std::to_string(&port - self->mPort + 1)};
port = jack_port_register(self->mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE,
std::string name{"channel_" + std::to_string(&port - mPort + 1)};
port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
return port != nullptr;
}
);
if(bad_port != ports_end)
{
ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(device->FmtChans));
if(bad_port == std::begin(self->mPort)) return ALC_FALSE;
ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(mDevice->FmtChans));
if(bad_port == std::begin(mPort)) return ALC_FALSE;
if(bad_port == std::begin(self->mPort)+1)
device->FmtChans = DevFmtMono;
if(bad_port == std::begin(mPort)+1)
mDevice->FmtChans = DevFmtMono;
else
{
ports_end = self->mPort+2;
ports_end = mPort+2;
while(bad_port != ports_end)
{
jack_port_unregister(self->mClient, *(--bad_port));
jack_port_unregister(mClient, *(--bad_port));
*bad_port = nullptr;
}
device->FmtChans = DevFmtStereo;
mDevice->FmtChans = DevFmtStereo;
}
numchans = std::distance(std::begin(self->mPort), bad_port);
numchans = std::distance(std::begin(mPort), bad_port);
}
self->mRing = nullptr;
self->mRing = CreateRingBuffer(bufsize, device->frameSizeFromFmt(), true);
if(!self->mRing)
mRing = nullptr;
mRing = CreateRingBuffer(bufsize, mDevice->frameSizeFromFmt(), true);
if(!mRing)
{
ERR("Failed to allocate ringbuffer\n");
return ALC_FALSE;
}
SetDefaultChannelOrder(device);
SetDefaultChannelOrder(mDevice);
return ALC_TRUE;
}
ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
ALCboolean JackPlayback::start()
{
if(jack_activate(self->mClient))
if(jack_activate(mClient))
{
ERR("Failed to activate client\n");
return ALC_FALSE;
}
const char **ports{jack_get_ports(self->mClient, nullptr, nullptr,
const char **ports{jack_get_ports(mClient, nullptr, nullptr,
JackPortIsPhysical|JackPortIsInput)};
if(ports == nullptr)
{
ERR("No physical playback ports found\n");
jack_deactivate(self->mClient);
jack_deactivate(mClient);
return ALC_FALSE;
}
std::mismatch(std::begin(self->mPort), std::end(self->mPort), ports,
[self](const jack_port_t *port, const char *pname) -> bool
std::mismatch(std::begin(mPort), std::end(mPort), ports,
[this](const jack_port_t *port, const char *pname) -> bool
{
if(!port) return false;
if(!pname)
@@ -470,7 +452,7 @@ ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
ERR("No physical playback port for \"%s\"\n", jack_port_name(port));
return false;
}
if(jack_connect(self->mClient, jack_port_name(port), pname))
if(jack_connect(mClient, jack_port_name(port), pname))
ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(port),
pname);
return true;
@@ -479,8 +461,8 @@ ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
jack_free(ports);
try {
self->mKillNow.store(false, std::memory_order_release);
self->mThread = std::thread(std::mem_fn(&ALCjackPlayback::mixerProc), self);
mKillNow.store(false, std::memory_order_release);
mThread = std::thread{std::mem_fn(&JackPlayback::mixerProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -488,33 +470,31 @@ ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
}
catch(...) {
}
jack_deactivate(self->mClient);
jack_deactivate(mClient);
return ALC_FALSE;
}
void ALCjackPlayback_stop(ALCjackPlayback *self)
void JackPlayback::stop()
{
if(self->mKillNow.exchange(true, std::memory_order_acq_rel) || !self->mThread.joinable())
if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
return;
self->mSem.post();
self->mThread.join();
mSem.post();
mThread.join();
jack_deactivate(self->mClient);
jack_deactivate(mClient);
}
ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
ClockLatency JackPlayback::getClockLatency()
{
ClockLatency ret;
ALCjackPlayback_lock(self);
ALCdevice *device{self->mDevice};
RingBuffer *ring{self->mRing.get()};
ret.ClockTime = GetDeviceClockTime(device);
ret.Latency = std::chrono::seconds{ring->readSpace()};
ret.Latency /= device->Frequency;
ALCjackPlayback_unlock(self);
lock();
ret.ClockTime = GetDeviceClockTime(mDevice);
ret.Latency = std::chrono::seconds{mRing->readSpace()};
ret.Latency /= mDevice->Frequency;
unlock();
return ret;
}
@@ -578,15 +558,10 @@ void JackBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *JackBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *JackBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCjackPlayback *backend;
NEW_OBJ(backend, ALCjackPlayback)(device);
return backend;
}
return new JackPlayback{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+19 -48
View File
@@ -28,59 +28,35 @@
namespace {
struct ALCloopback final : public ALCbackend {
ALCloopback(ALCdevice *device) noexcept : ALCbackend{device} { }
struct LoopbackBackend final : public BackendBase {
LoopbackBackend(ALCdevice *device) noexcept : BackendBase{device} { }
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
DEF_NEWDEL(LoopbackBackend)
};
void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
void ALCloopback_Destruct(ALCloopback *self);
ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
ALCboolean ALCloopback_reset(ALCloopback *self);
ALCboolean ALCloopback_start(ALCloopback *self);
void ALCloopback_stop(ALCloopback *self);
DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
DEFINE_ALCBACKEND_VTABLE(ALCloopback);
void ALCloopback_Construct(ALCloopback *self, ALCdevice *device)
ALCenum LoopbackBackend::open(const ALCchar *name)
{
new (self) ALCloopback{device};
SET_VTABLE2(ALCloopback, ALCbackend, self);
}
void ALCloopback_Destruct(ALCloopback *self)
{
self->~ALCloopback();
}
ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
{
ALCdevice *device{self->mDevice};
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCloopback_reset(ALCloopback *self)
ALCboolean LoopbackBackend::reset()
{
SetDefaultWFXChannelOrder(self->mDevice);
SetDefaultWFXChannelOrder(mDevice);
return ALC_TRUE;
}
ALCboolean ALCloopback_start(ALCloopback* UNUSED(self))
{
return ALC_TRUE;
}
ALCboolean LoopbackBackend::start()
{ return ALC_TRUE; }
void ALCloopback_stop(ALCloopback* UNUSED(self))
{
}
void LoopbackBackend::stop()
{ }
} // namespace
@@ -94,15 +70,10 @@ bool LoopbackBackendFactory::querySupport(ALCbackend_Type type)
void LoopbackBackendFactory::probe(DevProbe, std::string*)
{ }
ALCbackend *LoopbackBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *LoopbackBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Loopback)
{
ALCloopback *backend;
NEW_OBJ(backend, ALCloopback)(device);
return backend;
}
new LoopbackBackend{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+26 -50
View File
@@ -44,42 +44,24 @@ using std::chrono::nanoseconds;
constexpr ALCchar nullDevice[] = "No Output";
struct ALCnullBackend final : public ALCbackend {
ALCnullBackend(ALCdevice *device) noexcept : ALCbackend{device} { }
struct NullBackend final : public BackendBase {
NullBackend(ALCdevice *device) noexcept : BackendBase{device} { }
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
std::atomic<ALenum> mKillNow{AL_TRUE};
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "NullBackend::"; }
DEF_NEWDEL(NullBackend)
};
void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
void ALCnullBackend_Destruct(ALCnullBackend *self);
ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
ALCboolean ALCnullBackend_start(ALCnullBackend *self);
void ALCnullBackend_stop(ALCnullBackend *self);
DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
DEFINE_ALCBACKEND_VTABLE(ALCnullBackend);
void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
{
new (self) ALCnullBackend{device};
SET_VTABLE2(ALCnullBackend, ALCbackend, self);
}
void ALCnullBackend_Destruct(ALCnullBackend *self)
{ self->~ALCnullBackend(); }
int ALCnullBackend::mixerProc()
int NullBackend::mixerProc()
{
const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
@@ -102,9 +84,9 @@ int ALCnullBackend::mixerProc()
}
while(avail-done >= mDevice->UpdateSize)
{
ALCnullBackend_lock(this);
lock();
aluMixData(mDevice, nullptr, mDevice->UpdateSize);
ALCnullBackend_unlock(this);
unlock();
done += mDevice->UpdateSize;
}
@@ -125,30 +107,29 @@ int ALCnullBackend::mixerProc()
}
ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
ALCenum NullBackend::open(const ALCchar *name)
{
if(!name)
name = nullDevice;
else if(strcmp(name, nullDevice) != 0)
return ALC_INVALID_VALUE;
ALCdevice *device{self->mDevice};
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
ALCboolean NullBackend::reset()
{
SetDefaultWFXChannelOrder(self->mDevice);
SetDefaultWFXChannelOrder(mDevice);
return ALC_TRUE;
}
ALCboolean ALCnullBackend_start(ALCnullBackend *self)
ALCboolean NullBackend::start()
{
try {
self->mKillNow.store(AL_FALSE, std::memory_order_release);
self->mThread = std::thread(std::mem_fn(&ALCnullBackend::mixerProc), self);
mKillNow.store(AL_FALSE, std::memory_order_release);
mThread = std::thread{std::mem_fn(&NullBackend::mixerProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -159,11 +140,11 @@ ALCboolean ALCnullBackend_start(ALCnullBackend *self)
return ALC_FALSE;
}
void ALCnullBackend_stop(ALCnullBackend *self)
void NullBackend::stop()
{
if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !self->mThread.joinable())
if(mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !mThread.joinable())
return;
self->mThread.join();
mThread.join();
}
} // namespace
@@ -188,16 +169,11 @@ void NullBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *NullBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *NullBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCnullBackend *backend;
NEW_OBJ(backend, ALCnullBackend)(device);
return backend;
}
return NULL;
return new NullBackend{device};
return nullptr;
}
BackendFactory &NullBackendFactory::getFactory()
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+183 -257
View File
@@ -42,14 +42,15 @@
namespace {
/* Helper macros */
#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
static const ALCchar opensl_device[] = "OpenSL";
constexpr ALCchar opensl_device[] = "OpenSL";
static SLuint32 GetChannelMask(DevFmtChannels chans)
SLuint32 GetChannelMask(DevFmtChannels chans)
{
switch(chans)
{
@@ -78,7 +79,7 @@ static SLuint32 GetChannelMask(DevFmtChannels chans)
}
#ifdef SL_DATAFORMAT_PCM_EX
static SLuint32 GetTypeRepresentation(DevFmtType type)
SLuint32 GetTypeRepresentation(DevFmtType type)
{
switch(type)
{
@@ -97,7 +98,7 @@ static SLuint32 GetTypeRepresentation(DevFmtType type)
}
#endif
static const char *res_str(SLresult result)
const char *res_str(SLresult result)
{
switch(result)
{
@@ -132,20 +133,26 @@ static const char *res_str(SLresult result)
}
#define PRINTERR(x, s) do { \
if((x) != SL_RESULT_SUCCESS) \
if(UNLIKELY((x) != SL_RESULT_SUCCESS)) \
ERR("%s: %s\n", (s), res_str((x))); \
} while(0)
struct ALCopenslPlayback final : public ALCbackend {
ALCopenslPlayback(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCopenslPlayback() override;
struct OpenSLPlayback final : public BackendBase {
OpenSLPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~OpenSLPlayback() override;
static void processC(SLAndroidSimpleBufferQueueItf bq, void *context);
void process(SLAndroidSimpleBufferQueueItf bq);
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
ClockLatency getClockLatency() override;
/* engine interfaces */
SLObjectItf mEngineObj{nullptr};
SLEngineItf mEngine{nullptr};
@@ -163,34 +170,12 @@ struct ALCopenslPlayback final : public ALCbackend {
std::atomic<ALenum> mKillNow{AL_TRUE};
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "OpenSLPlayback::"; }
DEF_NEWDEL(OpenSLPlayback)
};
static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device);
static void ALCopenslPlayback_Destruct(ALCopenslPlayback *self);
static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name);
static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self);
static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self);
static void ALCopenslPlayback_stop(ALCopenslPlayback *self);
static DECLARE_FORWARD2(ALCopenslPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, ALCuint, availableSamples)
static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self);
static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCopenslPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCopenslPlayback);
static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device)
{
new (self) ALCopenslPlayback{device};
SET_VTABLE2(ALCopenslPlayback, ALCbackend, self);
}
static void ALCopenslPlayback_Destruct(ALCopenslPlayback* self)
{ self->~ALCopenslPlayback(); }
ALCopenslPlayback::~ALCopenslPlayback()
OpenSLPlayback::~OpenSLPlayback()
{
if(mBufferQueueObj)
VCALL0(mBufferQueueObj,Destroy)();
@@ -208,10 +193,10 @@ ALCopenslPlayback::~ALCopenslPlayback()
/* this callback handler is called every time a buffer finishes playing */
void ALCopenslPlayback::processC(SLAndroidSimpleBufferQueueItf bq, void *context)
{ static_cast<ALCopenslPlayback*>(context)->process(bq); }
void OpenSLPlayback::processC(SLAndroidSimpleBufferQueueItf bq, void *context)
{ static_cast<OpenSLPlayback*>(context)->process(bq); }
void ALCopenslPlayback::process(SLAndroidSimpleBufferQueueItf UNUSED(bq))
void OpenSLPlayback::process(SLAndroidSimpleBufferQueueItf UNUSED(bq))
{
/* A note on the ringbuffer usage: The buffer queue seems to hold on to the
* pointer passed to the Enqueue method, rather than copying the audio.
@@ -226,7 +211,7 @@ void ALCopenslPlayback::process(SLAndroidSimpleBufferQueueItf UNUSED(bq))
mSem.post();
}
int ALCopenslPlayback::mixerProc()
int OpenSLPlayback::mixerProc()
{
SetRTPriority();
althrd_setname(MIXER_THREAD_NAME);
@@ -242,7 +227,7 @@ int ALCopenslPlayback::mixerProc()
PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
}
ALCopenslPlayback_lock(this);
lock();
if(SL_RESULT_SUCCESS != result)
aluHandleDisconnect(mDevice, "Failed to get playback buffer: 0x%08x", result);
@@ -251,7 +236,7 @@ int ALCopenslPlayback::mixerProc()
{
if(mRing->writeSpace() == 0)
{
SLuint32 state = 0;
SLuint32 state{0};
result = VCALL(player,GetPlayState)(&state);
PRINTERR(result, "player->GetPlayState");
@@ -268,9 +253,9 @@ int ALCopenslPlayback::mixerProc()
if(mRing->writeSpace() == 0)
{
ALCopenslPlayback_unlock(this);
unlock();
mSem.wait();
ALCopenslPlayback_lock(this);
lock();
continue;
}
}
@@ -304,67 +289,63 @@ int ALCopenslPlayback::mixerProc()
data.first.buf += mDevice->UpdateSize*mFrameSize;
}
}
ALCopenslPlayback_unlock(this);
unlock();
return 0;
}
static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name)
ALCenum OpenSLPlayback::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
SLresult result;
if(!name)
name = opensl_device;
else if(strcmp(name, opensl_device) != 0)
return ALC_INVALID_VALUE;
// create engine
result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL);
SLresult result{slCreateEngine(&mEngineObj, 0, NULL, 0, NULL, NULL)};
PRINTERR(result, "slCreateEngine");
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE);
result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "engine->Realize");
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine);
result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
PRINTERR(result, "engine->GetInterface");
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(self->mEngine,CreateOutputMix)(&self->mOutputMix, 0, NULL, NULL);
result = VCALL(mEngine,CreateOutputMix)(&mOutputMix, 0, NULL, NULL);
PRINTERR(result, "engine->CreateOutputMix");
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(self->mOutputMix,Realize)(SL_BOOLEAN_FALSE);
result = VCALL(mOutputMix,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "outputMix->Realize");
}
if(SL_RESULT_SUCCESS != result)
{
if(self->mOutputMix != NULL)
VCALL0(self->mOutputMix,Destroy)();
self->mOutputMix = NULL;
if(mOutputMix)
VCALL0(mOutputMix,Destroy)();
mOutputMix = NULL;
if(self->mEngineObj != NULL)
VCALL0(self->mEngineObj,Destroy)();
self->mEngineObj = NULL;
self->mEngine = NULL;
if(mEngineObj)
VCALL0(mEngineObj,Destroy)();
mEngineObj = NULL;
mEngine = NULL;
return ALC_INVALID_VALUE;
}
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
ALCboolean OpenSLPlayback::reset()
{
ALCdevice *device{self->mDevice};
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
SLDataLocator_OutputMix loc_outmix;
SLDataSource audioSrc;
@@ -374,15 +355,15 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
SLboolean reqs[2];
SLresult result;
if(self->mBufferQueueObj != NULL)
VCALL0(self->mBufferQueueObj,Destroy)();
self->mBufferQueueObj = NULL;
if(mBufferQueueObj)
VCALL0(mBufferQueueObj,Destroy)();
mBufferQueueObj = NULL;
self->mRing = nullptr;
mRing = nullptr;
sampleRate = device->Frequency;
sampleRate = mDevice->Frequency;
#if 0
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
if(!(mDevice->Flags&DEVICE_FREQUENCY_REQUEST))
{
/* FIXME: Disabled until I figure out how to get the Context needed for
* the getSystemService call.
@@ -449,43 +430,43 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
}
#endif
if(sampleRate != device->Frequency)
if(sampleRate != mDevice->Frequency)
{
device->NumUpdates = (device->NumUpdates*sampleRate + (device->Frequency>>1)) /
device->Frequency;
device->NumUpdates = maxu(device->NumUpdates, 2);
device->Frequency = sampleRate;
mDevice->NumUpdates = (mDevice->NumUpdates*sampleRate + (mDevice->Frequency>>1)) /
mDevice->Frequency;
mDevice->NumUpdates = maxu(mDevice->NumUpdates, 2);
mDevice->Frequency = sampleRate;
}
device->FmtChans = DevFmtStereo;
device->FmtType = DevFmtShort;
mDevice->FmtChans = DevFmtStereo;
mDevice->FmtType = DevFmtShort;
SetDefaultWFXChannelOrder(device);
self->mFrameSize = device->frameSizeFromFmt();
SetDefaultWFXChannelOrder(mDevice);
mFrameSize = mDevice->frameSizeFromFmt();
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
loc_bufq.numBuffers = device->NumUpdates;
loc_bufq.numBuffers = mDevice->NumUpdates;
#ifdef SL_DATAFORMAT_PCM_EX
SLDataFormat_PCM_EX format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM_EX;
format_pcm.numChannels = device->channelsFromFmt();
format_pcm.sampleRate = device->Frequency * 1000;
format_pcm.bitsPerSample = device->bytesFromFmt() * 8;
format_pcm.numChannels = mDevice->channelsFromFmt();
format_pcm.sampleRate = mDevice->Frequency * 1000;
format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
format_pcm.containerSize = format_pcm.bitsPerSample;
format_pcm.channelMask = GetChannelMask(device->FmtChans);
format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
SL_BYTEORDER_BIGENDIAN;
format_pcm.representation = GetTypeRepresentation(device->FmtType);
format_pcm.representation = GetTypeRepresentation(mDevice->FmtType);
#else
SLDataFormat_PCM format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = device->channelsFromFmt();
format_pcm.samplesPerSec = device->Frequency * 1000;
format_pcm.bitsPerSample = device->bytesFromFmt() * 8;
format_pcm.numChannels = mDevice->channelsFromFmt();
format_pcm.samplesPerSec = mDevice->Frequency * 1000;
format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
format_pcm.containerSize = format_pcm.bitsPerSample;
format_pcm.channelMask = GetChannelMask(device->FmtChans);
format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
SL_BYTEORDER_BIGENDIAN;
#endif
@@ -494,7 +475,7 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
audioSrc.pFormat = &format_pcm;
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
loc_outmix.outputMix = self->mOutputMix;
loc_outmix.outputMix = mOutputMix;
audioSnk.pLocator = &loc_outmix;
audioSnk.pFormat = NULL;
@@ -504,22 +485,20 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
ids[1] = SL_IID_ANDROIDCONFIGURATION;
reqs[1] = SL_BOOLEAN_FALSE;
result = VCALL(self->mEngine,CreateAudioPlayer)(&self->mBufferQueueObj,
&audioSrc, &audioSnk, COUNTOF(ids), ids, reqs
);
result = VCALL(mEngine,CreateAudioPlayer)(&mBufferQueueObj, &audioSrc, &audioSnk, COUNTOF(ids),
ids, reqs);
PRINTERR(result, "engine->CreateAudioPlayer");
if(SL_RESULT_SUCCESS == result)
{
/* Set the stream type to "media" (games, music, etc), if possible. */
SLAndroidConfigurationItf config;
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDCONFIGURATION");
if(SL_RESULT_SUCCESS == result)
{
SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_STREAM_TYPE,
&streamType, sizeof(streamType)
);
result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_STREAM_TYPE, &streamType,
sizeof(streamType));
PRINTERR(result, "config->SetConfiguration");
}
@@ -528,26 +507,25 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(self->mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
result = VCALL(mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "bufferQueue->Realize");
}
if(SL_RESULT_SUCCESS == result)
{
self->mRing = CreateRingBuffer(device->NumUpdates,
self->mFrameSize*device->UpdateSize, true);
if(!self->mRing)
mRing = CreateRingBuffer(mDevice->NumUpdates, mFrameSize*mDevice->UpdateSize, true);
if(!mRing)
{
ERR("Out of memory allocating ring buffer %ux%u %u\n", device->UpdateSize,
device->NumUpdates, self->mFrameSize);
ERR("Out of memory allocating ring buffer %ux%u %u\n", mDevice->UpdateSize,
mDevice->NumUpdates, mFrameSize);
result = SL_RESULT_MEMORY_FAILURE;
}
}
if(SL_RESULT_SUCCESS != result)
{
if(self->mBufferQueueObj != NULL)
VCALL0(self->mBufferQueueObj,Destroy)();
self->mBufferQueueObj = NULL;
if(mBufferQueueObj)
VCALL0(mBufferQueueObj,Destroy)();
mBufferQueueObj = NULL;
return ALC_FALSE;
}
@@ -555,26 +533,24 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
return ALC_TRUE;
}
static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self)
ALCboolean OpenSLPlayback::start()
{
RingBuffer *ring{self->mRing.get()};
ring->reset();
mRing->reset();
SLAndroidSimpleBufferQueueItf bufferQueue;
SLresult result{VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&bufferQueue)};
PRINTERR(result, "bufferQueue->GetInterface");
if(SL_RESULT_SUCCESS != result)
return ALC_FALSE;
result = VCALL(bufferQueue,RegisterCallback)(&ALCopenslPlayback::processC, self);
result = VCALL(bufferQueue,RegisterCallback)(&OpenSLPlayback::processC, this);
PRINTERR(result, "bufferQueue->RegisterCallback");
if(SL_RESULT_SUCCESS != result) return ALC_FALSE;
try {
self->mKillNow.store(AL_FALSE);
self->mThread = std::thread(std::mem_fn(&ALCopenslPlayback::mixerProc), self);
mKillNow.store(AL_FALSE);
mThread = std::thread(std::mem_fn(&OpenSLPlayback::mixerProc), this);
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -585,20 +561,16 @@ static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self)
return ALC_FALSE;
}
static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
void OpenSLPlayback::stop()
{
SLAndroidSimpleBufferQueueItf bufferQueue;
SLPlayItf player;
SLresult result;
if(self->mKillNow.exchange(AL_TRUE) || !self->mThread.joinable())
if(mKillNow.exchange(AL_TRUE) || !mThread.joinable())
return;
self->mSem.post();
self->mThread.join();
mSem.post();
mThread.join();
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
SLPlayItf player;
SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player)};
PRINTERR(result, "bufferQueue->GetInterface");
if(SL_RESULT_SUCCESS == result)
{
@@ -606,8 +578,8 @@ static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
PRINTERR(result, "player->SetPlayState");
}
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&bufferQueue);
SLAndroidSimpleBufferQueueItf bufferQueue;
result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
PRINTERR(result, "bufferQueue->GetInterface");
if(SL_RESULT_SUCCESS == result)
{
@@ -630,29 +602,33 @@ static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
}
}
static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self)
ClockLatency OpenSLPlayback::getClockLatency()
{
ALCdevice *device{self->mDevice};
RingBuffer *ring{self->mRing.get()};
ClockLatency ret;
ALCopenslPlayback_lock(self);
ret.ClockTime = GetDeviceClockTime(device);
ret.Latency = std::chrono::seconds{ring->readSpace() * device->UpdateSize};
ret.Latency /= device->Frequency;
ALCopenslPlayback_unlock(self);
lock();
ret.ClockTime = GetDeviceClockTime(mDevice);
ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
ret.Latency /= mDevice->Frequency;
unlock();
return ret;
}
struct ALCopenslCapture final : public ALCbackend {
ALCopenslCapture(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCopenslCapture() override;
struct OpenSLCapture final : public BackendBase {
OpenSLCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~OpenSLCapture() override;
static void processC(SLAndroidSimpleBufferQueueItf bq, void *context);
void process(SLAndroidSimpleBufferQueueItf bq);
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(void *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
/* engine interfaces */
SLObjectItf mEngineObj{nullptr};
SLEngineItf mEngine;
@@ -664,33 +640,12 @@ struct ALCopenslCapture final : public ALCbackend {
ALCuint mSplOffset{0u};
ALsizei mFrameSize{0};
static constexpr inline const char *CurrentPrefix() noexcept { return "OpenSLCapture::"; }
DEF_NEWDEL(OpenSLCapture)
};
static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device);
static void ALCopenslCapture_Destruct(ALCopenslCapture *self);
static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name);
static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self);
static void ALCopenslCapture_stop(ALCopenslCapture *self);
static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self);
static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCopenslCapture)
DEFINE_ALCBACKEND_VTABLE(ALCopenslCapture);
static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device)
{
new (self) ALCopenslCapture{device};
SET_VTABLE2(ALCopenslCapture, ALCbackend, self);
}
static void ALCopenslCapture_Destruct(ALCopenslCapture *self)
{ self->~ALCopenslCapture(); }
ALCopenslCapture::~ALCopenslCapture()
OpenSLCapture::~OpenSLCapture()
{
if(mRecordObj)
VCALL0(mRecordObj,Destroy)();
@@ -703,19 +658,18 @@ ALCopenslCapture::~ALCopenslCapture()
}
void ALCopenslCapture::processC(SLAndroidSimpleBufferQueueItf bq, void *context)
{ static_cast<ALCopenslCapture*>(context)->process(bq); }
void OpenSLCapture::processC(SLAndroidSimpleBufferQueueItf bq, void *context)
{ static_cast<OpenSLCapture*>(context)->process(bq); }
void ALCopenslCapture::process(SLAndroidSimpleBufferQueueItf UNUSED(bq))
void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf UNUSED(bq))
{
/* A new chunk has been written into the ring buffer, advance it. */
mRing->writeAdvance(1);
}
static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name)
ALCenum OpenSLCapture::open(const ALCchar* name)
{
ALCdevice *device{self->mDevice};
SLDataLocator_AndroidSimpleBufferQueue loc_bq;
SLAndroidSimpleBufferQueueItf bufferQueue;
SLDataLocator_IODevice loc_dev;
@@ -728,32 +682,30 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
else if(strcmp(name, opensl_device) != 0)
return ALC_INVALID_VALUE;
result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL);
result = slCreateEngine(&mEngineObj, 0, NULL, 0, NULL, NULL);
PRINTERR(result, "slCreateEngine");
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE);
result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "engine->Realize");
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine);
result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
PRINTERR(result, "engine->GetInterface");
}
if(SL_RESULT_SUCCESS == result)
{
/* Ensure the total length is at least 100ms */
ALsizei length = maxi(device->NumUpdates * device->UpdateSize,
device->Frequency / 10);
ALsizei length{maxi(mDevice->NumUpdates*mDevice->UpdateSize, mDevice->Frequency/10)};
/* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
ALsizei update_len = clampi(device->NumUpdates*device->UpdateSize / 3,
device->Frequency / 100,
device->Frequency / 100 * 5);
ALsizei update_len{clampi(mDevice->NumUpdates*mDevice->UpdateSize / 3,
mDevice->Frequency/100, mDevice->Frequency/100*5)};
device->UpdateSize = update_len;
device->NumUpdates = (length+update_len-1) / update_len;
mDevice->UpdateSize = update_len;
mDevice->NumUpdates = (length+update_len-1) / update_len;
self->mFrameSize = device->frameSizeFromFmt();
mFrameSize = mDevice->frameSizeFromFmt();
}
loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
@@ -764,27 +716,27 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
audioSrc.pFormat = NULL;
loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
loc_bq.numBuffers = device->NumUpdates;
loc_bq.numBuffers = mDevice->NumUpdates;
#ifdef SL_DATAFORMAT_PCM_EX
SLDataFormat_PCM_EX format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM_EX;
format_pcm.numChannels = device->channelsFromFmt();
format_pcm.sampleRate = device->Frequency * 1000;
format_pcm.bitsPerSample = device->bytesFromFmt() * 8;
format_pcm.numChannels = mDevice->channelsFromFmt();
format_pcm.sampleRate = mDevice->Frequency * 1000;
format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
format_pcm.containerSize = format_pcm.bitsPerSample;
format_pcm.channelMask = GetChannelMask(device->FmtChans);
format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
SL_BYTEORDER_BIGENDIAN;
format_pcm.representation = GetTypeRepresentation(device->FmtType);
format_pcm.representation = GetTypeRepresentation(mDevice->FmtType);
#else
SLDataFormat_PCM format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = device->channelsFromFmt();
format_pcm.samplesPerSec = device->Frequency * 1000;
format_pcm.bitsPerSample = device->bytesFromFmt() * 8;
format_pcm.numChannels = mDevice->channelsFromFmt();
format_pcm.samplesPerSec = mDevice->Frequency * 1000;
format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
format_pcm.containerSize = format_pcm.bitsPerSample;
format_pcm.channelMask = GetChannelMask(device->FmtChans);
format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
SL_BYTEORDER_BIGENDIAN;
#endif
@@ -797,23 +749,21 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
const SLboolean reqs[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
result = VCALL(self->mEngine,CreateAudioRecorder)(&self->mRecordObj,
&audioSrc, &audioSnk, COUNTOF(ids), ids, reqs
);
result = VCALL(mEngine,CreateAudioRecorder)(&mRecordObj, &audioSrc, &audioSnk,
COUNTOF(ids), ids, reqs);
PRINTERR(result, "engine->CreateAudioRecorder");
}
if(SL_RESULT_SUCCESS == result)
{
/* Set the record preset to "generic", if possible. */
SLAndroidConfigurationItf config;
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
PRINTERR(result, "recordObj->GetInterface SL_IID_ANDROIDCONFIGURATION");
if(SL_RESULT_SUCCESS == result)
{
SLuint32 preset = SL_ANDROID_RECORDING_PRESET_GENERIC;
result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_RECORDING_PRESET,
&preset, sizeof(preset)
);
result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_RECORDING_PRESET, &preset,
sizeof(preset));
PRINTERR(result, "config->SetConfiguration");
}
@@ -822,31 +772,28 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(self->mRecordObj,Realize)(SL_BOOLEAN_FALSE);
result = VCALL(mRecordObj,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "recordObj->Realize");
}
if(SL_RESULT_SUCCESS == result)
{
self->mRing = CreateRingBuffer(device->NumUpdates,
device->UpdateSize*self->mFrameSize, false);
mRing = CreateRingBuffer(mDevice->NumUpdates, mDevice->UpdateSize*mFrameSize, false);
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&bufferQueue);
result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
PRINTERR(result, "recordObj->GetInterface");
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(bufferQueue,RegisterCallback)(&ALCopenslCapture::processC, self);
result = VCALL(bufferQueue,RegisterCallback)(&OpenSLCapture::processC, this);
PRINTERR(result, "bufferQueue->RegisterCallback");
}
if(SL_RESULT_SUCCESS == result)
{
RingBuffer *ring{self->mRing.get()};
ALsizei chunk_size = device->UpdateSize * self->mFrameSize;
ALsizei chunk_size = mDevice->UpdateSize * mFrameSize;
size_t i;
auto data = ring->getWriteVector();
auto data = mRing->getWriteVector();
for(i = 0;i < data.first.len && SL_RESULT_SUCCESS == result;i++)
{
result = VCALL(bufferQueue,Enqueue)(data.first.buf + chunk_size*i, chunk_size);
@@ -861,28 +808,26 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
if(SL_RESULT_SUCCESS != result)
{
if(self->mRecordObj != NULL)
VCALL0(self->mRecordObj,Destroy)();
self->mRecordObj = NULL;
if(mRecordObj)
VCALL0(mRecordObj,Destroy)();
mRecordObj = nullptr;
if(self->mEngineObj != NULL)
VCALL0(self->mEngineObj,Destroy)();
self->mEngineObj = NULL;
self->mEngine = NULL;
if(mEngineObj)
VCALL0(mEngineObj,Destroy)();
mEngineObj = nullptr;
mEngine = nullptr;
return ALC_INVALID_VALUE;
}
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self)
ALCboolean OpenSLCapture::start()
{
SLRecordItf record;
SLresult result;
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_RECORD, &record);
SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_RECORD, &record)};
PRINTERR(result, "recordObj->GetInterface");
if(SL_RESULT_SUCCESS == result)
@@ -893,21 +838,19 @@ static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self)
if(SL_RESULT_SUCCESS != result)
{
ALCopenslCapture_lock(self);
aluHandleDisconnect(self->mDevice, "Failed to start capture: 0x%08x", result);
ALCopenslCapture_unlock(self);
lock();
aluHandleDisconnect(mDevice, "Failed to start capture: 0x%08x", result);
unlock();
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCopenslCapture_stop(ALCopenslCapture *self)
void OpenSLCapture::stop()
{
SLRecordItf record;
SLresult result;
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_RECORD, &record);
SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_RECORD, &record)};
PRINTERR(result, "recordObj->GetInterface");
if(SL_RESULT_SUCCESS == result)
@@ -917,37 +860,33 @@ static void ALCopenslCapture_stop(ALCopenslCapture *self)
}
}
static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples)
ALCenum OpenSLCapture::captureSamples(void* buffer, ALCuint samples)
{
ALCdevice *device{self->mDevice};
RingBuffer *ring{self->mRing.get()};
ALsizei chunk_size = device->UpdateSize * self->mFrameSize;
ALsizei chunk_size = mDevice->UpdateSize * mFrameSize;
SLAndroidSimpleBufferQueueItf bufferQueue;
SLresult result;
ALCuint i;
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&bufferQueue);
result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
PRINTERR(result, "recordObj->GetInterface");
/* Read the desired samples from the ring buffer then advance its read
* pointer.
*/
auto data = ring->getReadVector();
auto data = mRing->getReadVector();
for(i = 0;i < samples;)
{
ALCuint rem = minu(samples - i, device->UpdateSize - self->mSplOffset);
memcpy((ALCbyte*)buffer + i*self->mFrameSize,
data.first.buf + self->mSplOffset*self->mFrameSize,
rem * self->mFrameSize);
ALCuint rem{minu(samples - i, mDevice->UpdateSize - mSplOffset)};
memcpy((ALCbyte*)buffer + i*mFrameSize, data.first.buf + mSplOffset*mFrameSize,
rem * mFrameSize);
self->mSplOffset += rem;
if(self->mSplOffset == device->UpdateSize)
mSplOffset += rem;
if(mSplOffset == mDevice->UpdateSize)
{
/* Finished a chunk, reset the offset and advance the read pointer. */
self->mSplOffset = 0;
mSplOffset = 0;
ring->readAdvance(1);
mRing->readAdvance(1);
result = VCALL(bufferQueue,Enqueue)(data.first.buf, chunk_size);
PRINTERR(result, "bufferQueue->Enqueue");
if(SL_RESULT_SUCCESS != result) break;
@@ -964,21 +903,17 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
if(SL_RESULT_SUCCESS != result)
{
ALCopenslCapture_lock(self);
aluHandleDisconnect(device, "Failed to update capture buffer: 0x%08x", result);
ALCopenslCapture_unlock(self);
lock();
aluHandleDisconnect(mDevice, "Failed to update capture buffer: 0x%08x", result);
unlock();
return ALC_INVALID_DEVICE;
}
return ALC_NO_ERROR;
}
static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self)
{
ALCdevice *device{self->mDevice};
RingBuffer *ring{self->mRing.get()};
return ring->readSpace() * device->UpdateSize;
}
ALCuint OpenSLCapture::availableSamples()
{ return mRing->readSpace()*mDevice->UpdateSize - mSplOffset; }
} // namespace
@@ -999,21 +934,12 @@ void OSLBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *OSLBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *OSLBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCopenslPlayback *backend;
NEW_OBJ(backend, ALCopenslPlayback)(device);
return backend;
}
return new OpenSLPlayback{device};
if(type == ALCbackend_Capture)
{
ALCopenslCapture *backend;
NEW_OBJ(backend, ALCopenslCapture)(device);
return backend;
}
return new OpenSLCapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+119 -168
View File
@@ -241,45 +241,29 @@ int log2i(ALCuint x)
}
struct ALCplaybackOSS final : public ALCbackend {
ALCplaybackOSS(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCplaybackOSS() override;
struct OSSPlayback final : public BackendBase {
OSSPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~OSSPlayback() override;
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
int mFd{-1};
al::vector<ALubyte> mMixData;
std::atomic<ALenum> mKillNow{AL_TRUE};
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "OSSPlayback::"; }
DEF_NEWDEL(OSSPlayback)
};
void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
void ALCplaybackOSS_Destruct(ALCplaybackOSS *self);
ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
void ALCplaybackOSS_stop(ALCplaybackOSS *self);
DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS);
void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
{
new (self) ALCplaybackOSS{device};
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
}
void ALCplaybackOSS_Destruct(ALCplaybackOSS *self)
{ self->~ALCplaybackOSS(); }
ALCplaybackOSS::~ALCplaybackOSS()
OSSPlayback::~OSSPlayback()
{
if(mFd != -1)
close(mFd);
@@ -287,14 +271,14 @@ ALCplaybackOSS::~ALCplaybackOSS()
}
int ALCplaybackOSS::mixerProc()
int OSSPlayback::mixerProc()
{
SetRTPriority();
althrd_setname(MIXER_THREAD_NAME);
const int frame_size{mDevice->frameSizeFromFmt()};
ALCplaybackOSS_lock(this);
lock();
while(!mKillNow.load(std::memory_order_acquire) &&
mDevice->Connected.load(std::memory_order_acquire))
{
@@ -302,9 +286,9 @@ int ALCplaybackOSS::mixerProc()
pollitem.fd = mFd;
pollitem.events = POLLOUT;
ALCplaybackOSS_unlock(this);
unlock();
int pret{poll(&pollitem, 1, 1000)};
ALCplaybackOSS_lock(this);
lock();
if(pret < 0)
{
if(errno == EINTR || errno == EAGAIN)
@@ -339,16 +323,14 @@ int ALCplaybackOSS::mixerProc()
write_ptr += wrote;
}
}
ALCplaybackOSS_unlock(this);
unlock();
return 0;
}
ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
ALCenum OSSPlayback::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
const char *devname{DefaultPlayback};
if(!name)
name = DefaultName;
@@ -366,20 +348,19 @@ ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
devname = iter->device_name.c_str();
}
self->mFd = open(devname, O_WRONLY);
if(self->mFd == -1)
mFd = ::open(devname, O_WRONLY);
if(mFd == -1)
{
ERR("Could not open %s: %s\n", devname, strerror(errno));
return ALC_INVALID_VALUE;
}
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
ALCboolean OSSPlayback::reset()
{
ALCdevice *device{self->mDevice};
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
@@ -390,7 +371,7 @@ ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
int ossSpeed;
const char *err;
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
ossFormat = AFMT_S8;
@@ -402,19 +383,19 @@ ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
device->FmtType = DevFmtShort;
mDevice->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
ossFormat = AFMT_S16_NE;
break;
}
periods = device->NumUpdates;
numChannels = device->channelsFromFmt();
ossSpeed = device->Frequency;
frameSize = numChannels * device->bytesFromFmt();
periods = mDevice->NumUpdates;
numChannels = mDevice->channelsFromFmt();
ossSpeed = mDevice->Frequency;
frameSize = numChannels * mDevice->bytesFromFmt();
/* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
log2FragmentSize = maxi(log2i(device->UpdateSize*frameSize), 4);
log2FragmentSize = maxi(log2i(mDevice->UpdateSize*frameSize), 4);
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define CHECKERR(func) if((func) < 0) { \
@@ -423,11 +404,11 @@ ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
}
/* Don't fail if SETFRAGMENT fails. We can handle just about anything
* that's reported back via GETOSPACE */
ioctl(self->mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
CHECKERR(ioctl(self->mFd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(self->mFd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(self->mFd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(self->mFd, SNDCTL_DSP_GETOSPACE, &info));
ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info));
if(0)
{
err:
@@ -436,38 +417,38 @@ ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
}
#undef CHECKERR
if(device->channelsFromFmt() != numChannels)
if(mDevice->channelsFromFmt() != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
numChannels);
return ALC_FALSE;
}
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
{
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType),
ossFormat);
return ALC_FALSE;
}
device->Frequency = ossSpeed;
device->UpdateSize = info.fragsize / frameSize;
device->NumUpdates = info.fragments;
mDevice->Frequency = ossSpeed;
mDevice->UpdateSize = info.fragsize / frameSize;
mDevice->NumUpdates = info.fragments;
SetDefaultChannelOrder(device);
SetDefaultChannelOrder(mDevice);
mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
return ALC_TRUE;
}
ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
ALCboolean OSSPlayback::start()
{
ALCdevice *device{self->mDevice};
try {
self->mMixData.resize(device->UpdateSize * device->frameSizeFromFmt());
self->mKillNow.store(AL_FALSE);
self->mThread = std::thread{std::mem_fn(&ALCplaybackOSS::mixerProc), self};
mKillNow.store(AL_FALSE);
mThread = std::thread{std::mem_fn(&OSSPlayback::mixerProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -478,58 +459,41 @@ ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
return ALC_FALSE;
}
void ALCplaybackOSS_stop(ALCplaybackOSS *self)
void OSSPlayback::stop()
{
if(self->mKillNow.exchange(AL_TRUE) || !self->mThread.joinable())
if(mKillNow.exchange(AL_TRUE) || !mThread.joinable())
return;
self->mThread.join();
mThread.join();
if(ioctl(self->mFd, SNDCTL_DSP_RESET) != 0)
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
ERR("Error resetting device: %s\n", strerror(errno));
self->mMixData.clear();
}
struct ALCcaptureOSS final : public ALCbackend {
ALCcaptureOSS(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCcaptureOSS() override;
struct OSScapture final : public BackendBase {
OSScapture(ALCdevice *device) noexcept : BackendBase{device} { }
~OSScapture() override;
int recordProc();
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
int mFd{-1};
RingBufferPtr mRing{nullptr};
std::atomic<ALenum> mKillNow{AL_TRUE};
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "OSScapture::"; }
DEF_NEWDEL(OSScapture)
};
void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
void ALCcaptureOSS_Destruct(ALCcaptureOSS *self);
ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
void ALCcaptureOSS_stop(ALCcaptureOSS *self);
ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS);
void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
{
new (self) ALCcaptureOSS{device};
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
}
void ALCcaptureOSS_Destruct(ALCcaptureOSS *self)
{ self->~ALCcaptureOSS(); }
ALCcaptureOSS::~ALCcaptureOSS()
OSScapture::~OSScapture()
{
if(mFd != -1)
close(mFd);
@@ -537,7 +501,7 @@ ALCcaptureOSS::~ALCcaptureOSS()
}
int ALCcaptureOSS::recordProc()
int OSScapture::recordProc()
{
SetRTPriority();
althrd_setname(RECORD_THREAD_NAME);
@@ -555,7 +519,9 @@ int ALCcaptureOSS::recordProc()
if(errno == EINTR || errno == EAGAIN)
continue;
ERR("poll failed: %s\n", strerror(errno));
lock();
aluHandleDisconnect(mDevice, "Failed to check capture samples: %s", strerror(errno));
unlock();
break;
}
else if(sret == 0)
@@ -571,10 +537,10 @@ int ALCcaptureOSS::recordProc()
if(amt < 0)
{
ERR("read failed: %s\n", strerror(errno));
ALCcaptureOSS_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failed reading capture samples: %s",
strerror(errno));
ALCcaptureOSS_unlock(this);
unlock();
break;
}
mRing->writeAdvance(amt/frame_size);
@@ -585,10 +551,8 @@ int ALCcaptureOSS::recordProc()
}
ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
ALCenum OSScapture::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
const char *devname{DefaultCapture};
if(!name)
name = DefaultName;
@@ -606,15 +570,15 @@ ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
devname = iter->device_name.c_str();
}
self->mFd = open(devname, O_RDONLY);
if(self->mFd == -1)
mFd = ::open(devname, O_RDONLY);
if(mFd == -1)
{
ERR("Could not open %s: %s\n", devname, strerror(errno));
return ALC_INVALID_VALUE;
}
int ossFormat{};
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
ossFormat = AFMT_S8;
@@ -629,15 +593,15 @@ ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
return ALC_INVALID_VALUE;
}
int periods{4};
int numChannels{device->channelsFromFmt()};
int frameSize{numChannels * device->bytesFromFmt()};
int ossSpeed{static_cast<int>(device->Frequency)};
int log2FragmentSize{log2i(device->UpdateSize * device->NumUpdates *
int numChannels{mDevice->channelsFromFmt()};
int frameSize{numChannels * mDevice->bytesFromFmt()};
int ossSpeed{static_cast<int>(mDevice->Frequency)};
int log2FragmentSize{log2i(mDevice->UpdateSize * mDevice->NumUpdates *
frameSize / periods)};
/* according to the OSS spec, 16 bytes are the minimum */
@@ -650,57 +614,58 @@ ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
err = #func; \
goto err; \
}
CHECKERR(ioctl(self->mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
CHECKERR(ioctl(self->mFd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(self->mFd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(self->mFd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(self->mFd, SNDCTL_DSP_GETISPACE, &info));
CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(mFd, SNDCTL_DSP_GETISPACE, &info));
if(0)
{
err:
ERR("%s failed: %s\n", err, strerror(errno));
close(self->mFd);
self->mFd = -1;
close(mFd);
mFd = -1;
return ALC_INVALID_VALUE;
}
#undef CHECKERR
if(device->channelsFromFmt() != numChannels)
if(mDevice->channelsFromFmt() != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
close(self->mFd);
self->mFd = -1;
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
numChannels);
close(mFd);
mFd = -1;
return ALC_INVALID_VALUE;
}
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
{
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
close(self->mFd);
self->mFd = -1;
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType), ossFormat);
close(mFd);
mFd = -1;
return ALC_INVALID_VALUE;
}
self->mRing = CreateRingBuffer(device->UpdateSize*device->NumUpdates, frameSize, false);
if(!self->mRing)
mRing = CreateRingBuffer(mDevice->UpdateSize*mDevice->NumUpdates, frameSize, false);
if(!mRing)
{
ERR("Ring buffer create failed\n");
close(self->mFd);
self->mFd = -1;
close(mFd);
mFd = -1;
return ALC_OUT_OF_MEMORY;
}
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
ALCboolean OSScapture::start()
{
try {
self->mKillNow.store(AL_FALSE);
self->mThread = std::thread{std::mem_fn(&ALCcaptureOSS::recordProc), self};
mKillNow.store(AL_FALSE);
mThread = std::thread{std::mem_fn(&OSScapture::recordProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -711,29 +676,24 @@ ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
return ALC_FALSE;
}
void ALCcaptureOSS_stop(ALCcaptureOSS *self)
void OSScapture::stop()
{
if(self->mKillNow.exchange(AL_TRUE) || !self->mThread.joinable())
if(mKillNow.exchange(AL_TRUE) || !mThread.joinable())
return;
mThread.join();
self->mThread.join();
if(ioctl(self->mFd, SNDCTL_DSP_RESET) != 0)
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
ERR("Error resetting device: %s\n", strerror(errno));
}
ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
ALCenum OSScapture::captureSamples(ALCvoid *buffer, ALCuint samples)
{
RingBuffer *ring{self->mRing.get()};
ring->read(buffer, samples);
mRing->read(buffer, samples);
return ALC_NO_ERROR;
}
ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
{
RingBuffer *ring{self->mRing.get()};
return ring->readSpace();
}
ALCuint OSScapture::availableSamples()
{ return mRing->readSpace(); }
} // namespace
@@ -791,20 +751,11 @@ void OSSBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *OSSBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *OSSBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCplaybackOSS *backend;
NEW_OBJ(backend, ALCplaybackOSS)(device);
return backend;
}
return new OSSPlayback{device};
if(type == ALCbackend_Capture)
{
ALCcaptureOSS *backend;
NEW_OBJ(backend, ALCcaptureOSS)(device);
return backend;
}
return new OSScapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+111 -172
View File
@@ -130,9 +130,9 @@ bool pa_load(void)
}
struct ALCportPlayback final : public ALCbackend {
ALCportPlayback(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCportPlayback() override;
struct PortPlayback final : public BackendBase {
PortPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~PortPlayback() override;
static int writeCallbackC(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
@@ -140,37 +140,20 @@ struct ALCportPlayback final : public ALCbackend {
int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags);
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
PaStream *mStream{nullptr};
PaStreamParameters mParams{};
ALuint mUpdateSize{0u};
static constexpr inline const char *CurrentPrefix() noexcept { return "PortPlayback::"; }
DEF_NEWDEL(PortPlayback)
};
void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device);
void ALCportPlayback_Destruct(ALCportPlayback *self);
ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name);
ALCboolean ALCportPlayback_reset(ALCportPlayback *self);
ALCboolean ALCportPlayback_start(ALCportPlayback *self);
void ALCportPlayback_stop(ALCportPlayback *self);
DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCportPlayback);
void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
{
new (self) ALCportPlayback{device};
SET_VTABLE2(ALCportPlayback, ALCbackend, self);
}
void ALCportPlayback_Destruct(ALCportPlayback *self)
{ self->~ALCportPlayback(); }
ALCportPlayback::~ALCportPlayback()
PortPlayback::~PortPlayback()
{
PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
if(err != paNoError)
@@ -179,132 +162,124 @@ ALCportPlayback::~ALCportPlayback()
}
int ALCportPlayback::writeCallbackC(const void *inputBuffer, void *outputBuffer,
int PortPlayback::writeCallbackC(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData)
{
return static_cast<ALCportPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
framesPerBuffer, timeInfo, statusFlags);
}
int ALCportPlayback::writeCallback(const void* UNUSED(inputBuffer), void *outputBuffer,
int PortPlayback::writeCallback(const void* UNUSED(inputBuffer), void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags))
{
ALCportPlayback_lock(this);
lock();
aluMixData(mDevice, outputBuffer, framesPerBuffer);
ALCportPlayback_unlock(this);
unlock();
return 0;
}
ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
ALCenum PortPlayback::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
PaError err;
if(!name)
name = pa_device;
else if(strcmp(name, pa_device) != 0)
return ALC_INVALID_VALUE;
self->mUpdateSize = device->UpdateSize;
mUpdateSize = mDevice->UpdateSize;
self->mParams.device = -1;
if(!ConfigValueInt(nullptr, "port", "device", &self->mParams.device) ||
self->mParams.device < 0)
self->mParams.device = Pa_GetDefaultOutputDevice();
self->mParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
(float)device->Frequency;
self->mParams.hostApiSpecificStreamInfo = nullptr;
mParams.device = -1;
if(!ConfigValueInt(nullptr, "port", "device", &mParams.device) || mParams.device < 0)
mParams.device = Pa_GetDefaultOutputDevice();
mParams.suggestedLatency = (mDevice->UpdateSize*mDevice->NumUpdates) /
(float)mDevice->Frequency;
mParams.hostApiSpecificStreamInfo = nullptr;
self->mParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
mParams.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
self->mParams.sampleFormat = paInt8;
mParams.sampleFormat = paInt8;
break;
case DevFmtUByte:
self->mParams.sampleFormat = paUInt8;
mParams.sampleFormat = paUInt8;
break;
case DevFmtUShort:
/* fall-through */
case DevFmtShort:
self->mParams.sampleFormat = paInt16;
mParams.sampleFormat = paInt16;
break;
case DevFmtUInt:
/* fall-through */
case DevFmtInt:
self->mParams.sampleFormat = paInt32;
mParams.sampleFormat = paInt32;
break;
case DevFmtFloat:
self->mParams.sampleFormat = paFloat32;
mParams.sampleFormat = paFloat32;
break;
}
retry_open:
err = Pa_OpenStream(&self->mStream, nullptr, &self->mParams,
device->Frequency, device->UpdateSize, paNoFlag,
&ALCportPlayback::writeCallbackC, self
);
PaError err{Pa_OpenStream(&mStream, nullptr, &mParams, mDevice->Frequency, mDevice->UpdateSize,
paNoFlag, &PortPlayback::writeCallbackC, this)};
if(err != paNoError)
{
if(self->mParams.sampleFormat == paFloat32)
if(mParams.sampleFormat == paFloat32)
{
self->mParams.sampleFormat = paInt16;
mParams.sampleFormat = paInt16;
goto retry_open;
}
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
return ALC_INVALID_VALUE;
}
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
ALCboolean PortPlayback::reset()
{
ALCdevice *device{self->mDevice};
const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
mDevice->Frequency = streamInfo->sampleRate;
mDevice->UpdateSize = mUpdateSize;
const PaStreamInfo *streamInfo{Pa_GetStreamInfo(self->mStream)};
device->Frequency = streamInfo->sampleRate;
device->UpdateSize = self->mUpdateSize;
if(self->mParams.sampleFormat == paInt8)
device->FmtType = DevFmtByte;
else if(self->mParams.sampleFormat == paUInt8)
device->FmtType = DevFmtUByte;
else if(self->mParams.sampleFormat == paInt16)
device->FmtType = DevFmtShort;
else if(self->mParams.sampleFormat == paInt32)
device->FmtType = DevFmtInt;
else if(self->mParams.sampleFormat == paFloat32)
device->FmtType = DevFmtFloat;
if(mParams.sampleFormat == paInt8)
mDevice->FmtType = DevFmtByte;
else if(mParams.sampleFormat == paUInt8)
mDevice->FmtType = DevFmtUByte;
else if(mParams.sampleFormat == paInt16)
mDevice->FmtType = DevFmtShort;
else if(mParams.sampleFormat == paInt32)
mDevice->FmtType = DevFmtInt;
else if(mParams.sampleFormat == paFloat32)
mDevice->FmtType = DevFmtFloat;
else
{
ERR("Unexpected sample format: 0x%lx\n", self->mParams.sampleFormat);
ERR("Unexpected sample format: 0x%lx\n", mParams.sampleFormat);
return ALC_FALSE;
}
if(self->mParams.channelCount == 2)
device->FmtChans = DevFmtStereo;
else if(self->mParams.channelCount == 1)
device->FmtChans = DevFmtMono;
if(mParams.channelCount == 2)
mDevice->FmtChans = DevFmtStereo;
else if(mParams.channelCount == 1)
mDevice->FmtChans = DevFmtMono;
else
{
ERR("Unexpected channel count: %u\n", self->mParams.channelCount);
ERR("Unexpected channel count: %u\n", mParams.channelCount);
return ALC_FALSE;
}
SetDefaultChannelOrder(device);
SetDefaultChannelOrder(mDevice);
return ALC_TRUE;
}
ALCboolean ALCportPlayback_start(ALCportPlayback *self)
ALCboolean PortPlayback::start()
{
PaError err{Pa_StartStream(self->mStream)};
PaError err{Pa_StartStream(mStream)};
if(err != paNoError)
{
ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
@@ -313,17 +288,17 @@ ALCboolean ALCportPlayback_start(ALCportPlayback *self)
return ALC_TRUE;
}
void ALCportPlayback_stop(ALCportPlayback *self)
void PortPlayback::stop()
{
PaError err{Pa_StopStream(self->mStream)};
PaError err{Pa_StopStream(mStream)};
if(err != paNoError)
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
}
struct ALCportCapture final : public ALCbackend {
ALCportCapture(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCportCapture() override;
struct PortCapture final : public BackendBase {
PortCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~PortCapture() override;
static int readCallbackC(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
@@ -331,38 +306,22 @@ struct ALCportCapture final : public ALCbackend {
int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags);
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
PaStream *mStream{nullptr};
PaStreamParameters mParams;
RingBufferPtr mRing{nullptr};
static constexpr inline const char *CurrentPrefix() noexcept { return "PortCapture::"; }
DEF_NEWDEL(PortCapture)
};
void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
void ALCportCapture_Destruct(ALCportCapture *self);
ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
ALCboolean ALCportCapture_start(ALCportCapture *self);
void ALCportCapture_stop(ALCportCapture *self);
ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
DEFINE_ALCBACKEND_VTABLE(ALCportCapture);
void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
{
new (self) ALCportCapture{device};
SET_VTABLE2(ALCportCapture, ALCbackend, self);
}
void ALCportCapture_Destruct(ALCportCapture *self)
{ self->~ALCportCapture(); }
ALCportCapture::~ALCportCapture()
PortCapture::~PortCapture()
{
PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
if(err != paNoError)
@@ -371,15 +330,15 @@ ALCportCapture::~ALCportCapture()
}
int ALCportCapture::readCallbackC(const void *inputBuffer, void *outputBuffer,
int PortCapture::readCallbackC(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void* userData)
{
return static_cast<ALCportCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
framesPerBuffer, timeInfo, statusFlags);
}
int ALCportCapture::readCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
int PortCapture::readCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags))
{
@@ -388,73 +347,66 @@ int ALCportCapture::readCallback(const void *inputBuffer, void *UNUSED(outputBuf
}
ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
ALCenum PortCapture::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
ALuint samples, frame_size;
PaError err;
if(!name)
name = pa_device;
else if(strcmp(name, pa_device) != 0)
return ALC_INVALID_VALUE;
samples = device->UpdateSize * device->NumUpdates;
samples = maxu(samples, 100 * device->Frequency / 1000);
frame_size = device->frameSizeFromFmt();
ALuint samples{mDevice->UpdateSize * mDevice->NumUpdates};
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
ALsizei frame_size{mDevice->frameSizeFromFmt()};
self->mRing = CreateRingBuffer(samples, frame_size, false);
if(!self->mRing) return ALC_INVALID_VALUE;
mRing = CreateRingBuffer(samples, frame_size, false);
if(!mRing) return ALC_INVALID_VALUE;
self->mParams.device = -1;
if(!ConfigValueInt(nullptr, "port", "capture", &self->mParams.device) ||
self->mParams.device < 0)
self->mParams.device = Pa_GetDefaultInputDevice();
self->mParams.suggestedLatency = 0.0f;
self->mParams.hostApiSpecificStreamInfo = nullptr;
mParams.device = -1;
if(!ConfigValueInt(nullptr, "port", "capture", &mParams.device) || mParams.device < 0)
mParams.device = Pa_GetDefaultInputDevice();
mParams.suggestedLatency = 0.0f;
mParams.hostApiSpecificStreamInfo = nullptr;
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
self->mParams.sampleFormat = paInt8;
mParams.sampleFormat = paInt8;
break;
case DevFmtUByte:
self->mParams.sampleFormat = paUInt8;
mParams.sampleFormat = paUInt8;
break;
case DevFmtShort:
self->mParams.sampleFormat = paInt16;
mParams.sampleFormat = paInt16;
break;
case DevFmtInt:
self->mParams.sampleFormat = paInt32;
mParams.sampleFormat = paInt32;
break;
case DevFmtFloat:
self->mParams.sampleFormat = paFloat32;
mParams.sampleFormat = paFloat32;
break;
case DevFmtUInt:
case DevFmtUShort:
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
ERR("%s samples not supported\n", DevFmtTypeString(mDevice->FmtType));
return ALC_INVALID_VALUE;
}
self->mParams.channelCount = device->channelsFromFmt();
mParams.channelCount = mDevice->channelsFromFmt();
err = Pa_OpenStream(&self->mStream, &self->mParams, nullptr,
device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
&ALCportCapture::readCallbackC, self
);
PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency,
paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, this)};
if(err != paNoError)
{
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
return ALC_INVALID_VALUE;
}
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean ALCportCapture_start(ALCportCapture *self)
ALCboolean PortCapture::start()
{
PaError err = Pa_StartStream(self->mStream);
PaError err{Pa_StartStream(mStream)};
if(err != paNoError)
{
ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
@@ -463,24 +415,20 @@ ALCboolean ALCportCapture_start(ALCportCapture *self)
return ALC_TRUE;
}
void ALCportCapture_stop(ALCportCapture *self)
void PortCapture::stop()
{
PaError err = Pa_StopStream(self->mStream);
PaError err{Pa_StopStream(mStream)};
if(err != paNoError)
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
}
ALCuint ALCportCapture_availableSamples(ALCportCapture *self)
{
RingBuffer *ring{self->mRing.get()};
return ring->readSpace();
}
ALCuint PortCapture::availableSamples()
{ return mRing->readSpace(); }
ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples)
ALCenum PortCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
{
RingBuffer *ring{self->mRing.get()};
ring->read(buffer, samples);
mRing->read(buffer, samples);
return ALC_NO_ERROR;
}
@@ -519,21 +467,12 @@ void PortBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *PortBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *PortBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCportPlayback *backend;
NEW_OBJ(backend, ALCportPlayback)(device);
return backend;
}
return new PortPlayback{device};
if(type == ALCbackend_Capture)
{
ALCportCapture *backend;
NEW_OBJ(backend, ALCportCapture)(device);
return backend;
}
return new PortCapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+209 -270
View File
@@ -729,8 +729,8 @@ void probeCaptureDevices(void)
}
struct PulsePlayback final : public ALCbackend {
PulsePlayback(ALCdevice *device) noexcept : ALCbackend{device} { }
struct PulsePlayback final : public BackendBase {
PulsePlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~PulsePlayback() override;
static void bufferAttrCallbackC(pa_stream *stream, void *pdata);
@@ -754,6 +754,14 @@ struct PulsePlayback final : public ALCbackend {
static void streamMovedCallbackC(pa_stream *stream, void *pdata);
void streamMovedCallback(pa_stream *stream);
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
ClockLatency getClockLatency() override;
void lock() noexcept override;
void unlock() noexcept override;
std::string mDeviceName;
pa_buffer_attr mAttr;
@@ -768,33 +776,9 @@ struct PulsePlayback final : public ALCbackend {
ALuint mFrameSize{0u};
static constexpr inline const char *CurrentPrefix() noexcept { return "PulsePlayback::"; }
DEF_NEWDEL(PulsePlayback)
};
void PulsePlayback_Construct(PulsePlayback *self, ALCdevice *device);
void PulsePlayback_Destruct(PulsePlayback *self);
ALCenum PulsePlayback_open(PulsePlayback *self, const ALCchar *name);
ALCboolean PulsePlayback_reset(PulsePlayback *self);
ALCboolean PulsePlayback_start(PulsePlayback *self);
void PulsePlayback_stop(PulsePlayback *self);
DECLARE_FORWARD2(PulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
DECLARE_FORWARD(PulsePlayback, ALCbackend, ALCuint, availableSamples)
ClockLatency PulsePlayback_getClockLatency(PulsePlayback *self);
void PulsePlayback_lock(PulsePlayback *self);
void PulsePlayback_unlock(PulsePlayback *self);
DECLARE_DEFAULT_ALLOCATORS(PulsePlayback)
DEFINE_ALCBACKEND_VTABLE(PulsePlayback);
void PulsePlayback_Construct(PulsePlayback *self, ALCdevice *device)
{
new (self) PulsePlayback{device};
SET_VTABLE2(PulsePlayback, ALCbackend, self);
}
void PulsePlayback_Destruct(PulsePlayback *self)
{ self->~PulsePlayback(); }
PulsePlayback::~PulsePlayback()
{
if(!mLoop)
@@ -960,7 +944,7 @@ void PulsePlayback::streamMovedCallback(pa_stream *stream)
}
ALCenum PulsePlayback_open(PulsePlayback *self, const ALCchar *name)
ALCenum PulsePlayback::open(const ALCchar *name)
{
const char *pulse_name{nullptr};
const char *dev_name{nullptr};
@@ -980,10 +964,10 @@ ALCenum PulsePlayback_open(PulsePlayback *self, const ALCchar *name)
dev_name = iter->name.c_str();
}
std::tie(self->mLoop, self->mContext) = pulse_open(&PulsePlayback::contextStateCallbackC, self);
if(!self->mLoop) return ALC_INVALID_VALUE;
std::tie(mLoop, mContext) = pulse_open(&PulsePlayback::contextStateCallbackC, this);
if(!mLoop) return ALC_INVALID_VALUE;
unique_palock palock{self->mLoop};
unique_palock palock{mLoop};
pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
PA_STREAM_FIX_CHANNELS};
@@ -1001,93 +985,89 @@ ALCenum PulsePlayback_open(PulsePlayback *self, const ALCchar *name)
pulse_name = getenv("ALSOFT_PULSE_DEFAULT");
if(pulse_name && !pulse_name[0]) pulse_name = nullptr;
}
self->mStream = pulse_connect_stream(pulse_name, self->mLoop, self->mContext, flags, nullptr,
&spec, nullptr, ALCbackend_Playback);
if(!self->mStream)
mStream = pulse_connect_stream(pulse_name, mLoop, mContext, flags, nullptr, &spec, nullptr,
ALCbackend_Playback);
if(!mStream)
{
palock = unique_palock{};
pulse_close(self->mLoop, self->mContext, self->mStream);
self->mLoop = nullptr;
self->mContext = nullptr;
pulse_close(mLoop, mContext, mStream);
mLoop = nullptr;
mContext = nullptr;
return ALC_INVALID_VALUE;
}
pa_stream_set_moved_callback(self->mStream, &PulsePlayback::streamMovedCallbackC, self);
self->mFrameSize = pa_frame_size(pa_stream_get_sample_spec(self->mStream));
pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
mFrameSize = pa_frame_size(pa_stream_get_sample_spec(mStream));
self->mDeviceName = pa_stream_get_device_name(self->mStream);
mDeviceName = pa_stream_get_device_name(mStream);
if(!dev_name)
{
pa_operation *op{pa_context_get_sink_info_by_name(self->mContext,
self->mDeviceName.c_str(), &PulsePlayback::sinkNameCallbackC, self)};
wait_for_operation(op, self->mLoop);
pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
&PulsePlayback::sinkNameCallbackC, this)};
wait_for_operation(op, mLoop);
}
else
{
ALCdevice *device{self->mDevice};
device->DeviceName = dev_name;
}
mDevice->DeviceName = dev_name;
return ALC_NO_ERROR;
}
ALCboolean PulsePlayback_reset(PulsePlayback *self)
ALCboolean PulsePlayback::reset()
{
unique_palock palock{self->mLoop};
unique_palock palock{mLoop};
if(self->mStream)
if(mStream)
{
pa_stream_set_state_callback(self->mStream, nullptr, nullptr);
pa_stream_set_moved_callback(self->mStream, nullptr, nullptr);
pa_stream_set_write_callback(self->mStream, nullptr, nullptr);
pa_stream_set_buffer_attr_callback(self->mStream, nullptr, nullptr);
pa_stream_disconnect(self->mStream);
pa_stream_unref(self->mStream);
self->mStream = nullptr;
pa_stream_set_state_callback(mStream, nullptr, nullptr);
pa_stream_set_moved_callback(mStream, nullptr, nullptr);
pa_stream_set_write_callback(mStream, nullptr, nullptr);
pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
pa_stream_disconnect(mStream);
pa_stream_unref(mStream);
mStream = nullptr;
}
pa_operation *op{pa_context_get_sink_info_by_name(self->mContext,
self->mDeviceName.c_str(), &PulsePlayback::sinkInfoCallbackC, self)};
wait_for_operation(op, self->mLoop);
pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
&PulsePlayback::sinkInfoCallbackC, this)};
wait_for_operation(op, mLoop);
ALCdevice *device{self->mDevice};
pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_AUTO_TIMING_UPDATE};
if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 0))
flags |= PA_STREAM_DONT_MOVE;
if(GetConfigValueBool(device->DeviceName.c_str(), "pulse", "adjust-latency", 0))
if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", 0))
flags |= PA_STREAM_ADJUST_LATENCY;
if(GetConfigValueBool(device->DeviceName.c_str(), "pulse", "fix-rate", 0) ||
!(device->Flags&DEVICE_FREQUENCY_REQUEST))
if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", 0) ||
!(mDevice->Flags&DEVICE_FREQUENCY_REQUEST))
flags |= PA_STREAM_FIX_RATE;
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
device->FmtType = DevFmtUByte;
mDevice->FmtType = DevFmtUByte;
/* fall-through */
case DevFmtUByte:
self->mSpec.format = PA_SAMPLE_U8;
mSpec.format = PA_SAMPLE_U8;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
mDevice->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
self->mSpec.format = PA_SAMPLE_S16NE;
mSpec.format = PA_SAMPLE_S16NE;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
mDevice->FmtType = DevFmtInt;
/* fall-through */
case DevFmtInt:
self->mSpec.format = PA_SAMPLE_S32NE;
mSpec.format = PA_SAMPLE_S32NE;
break;
case DevFmtFloat:
self->mSpec.format = PA_SAMPLE_FLOAT32NE;
mSpec.format = PA_SAMPLE_FLOAT32NE;
break;
}
self->mSpec.rate = device->Frequency;
self->mSpec.channels = device->channelsFromFmt();
mSpec.rate = mDevice->Frequency;
mSpec.channels = mDevice->channelsFromFmt();
if(pa_sample_spec_valid(&self->mSpec) == 0)
if(pa_sample_spec_valid(&mSpec) == 0)
{
ERR("Invalid sample format\n");
return ALC_FALSE;
@@ -1095,13 +1075,13 @@ ALCboolean PulsePlayback_reset(PulsePlayback *self)
const char *mapname{nullptr};
pa_channel_map chanmap;
switch(device->FmtChans)
switch(mDevice->FmtChans)
{
case DevFmtMono:
mapname = "mono";
break;
case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
mDevice->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
mapname = "front-left,front-right";
@@ -1124,54 +1104,52 @@ ALCboolean PulsePlayback_reset(PulsePlayback *self)
}
if(!pa_channel_map_parse(&chanmap, mapname))
{
ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans));
ERR("Failed to build channel map for %s\n", DevFmtChannelsString(mDevice->FmtChans));
return ALC_FALSE;
}
SetDefaultWFXChannelOrder(device);
SetDefaultWFXChannelOrder(mDevice);
size_t period_size{device->UpdateSize * pa_frame_size(&self->mSpec)};
self->mAttr.maxlength = -1;
self->mAttr.tlength = period_size * maxu(device->NumUpdates, 2);
self->mAttr.prebuf = 0;
self->mAttr.minreq = period_size;
self->mAttr.fragsize = -1;
size_t period_size{mDevice->UpdateSize * pa_frame_size(&mSpec)};
mAttr.maxlength = -1;
mAttr.tlength = period_size * maxu(mDevice->NumUpdates, 2);
mAttr.prebuf = 0;
mAttr.minreq = period_size;
mAttr.fragsize = -1;
self->mStream = pulse_connect_stream(self->mDeviceName.c_str(), self->mLoop, self->mContext,
flags, &self->mAttr, &self->mSpec, &chanmap, ALCbackend_Playback);
if(!self->mStream) return ALC_FALSE;
mStream = pulse_connect_stream(mDeviceName.c_str(), mLoop, mContext, flags, &mAttr, &mSpec,
&chanmap, ALCbackend_Playback);
if(!mStream) return ALC_FALSE;
pa_stream_set_state_callback(self->mStream, &PulsePlayback::streamStateCallbackC, self);
pa_stream_set_moved_callback(self->mStream, &PulsePlayback::streamMovedCallbackC, self);
pa_stream_set_state_callback(mStream, &PulsePlayback::streamStateCallbackC, this);
pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
self->mSpec = *(pa_stream_get_sample_spec(self->mStream));
self->mFrameSize = pa_frame_size(&self->mSpec);
mSpec = *(pa_stream_get_sample_spec(mStream));
mFrameSize = pa_frame_size(&mSpec);
if(device->Frequency != self->mSpec.rate)
if(mDevice->Frequency != mSpec.rate)
{
/* Server updated our playback rate, so modify the buffer attribs
* accordingly. */
device->NumUpdates = static_cast<ALuint>(clampd(
(ALdouble)self->mSpec.rate/device->Frequency*device->NumUpdates + 0.5, 2.0, 16.0));
mDevice->NumUpdates = static_cast<ALuint>(clampd(
(ALdouble)mSpec.rate/mDevice->Frequency*mDevice->NumUpdates + 0.5, 2.0, 16.0));
period_size = device->UpdateSize * self->mFrameSize;
self->mAttr.maxlength = -1;
self->mAttr.tlength = period_size * maxu(device->NumUpdates, 2);
self->mAttr.prebuf = 0;
self->mAttr.minreq = period_size;
period_size = mDevice->UpdateSize * mFrameSize;
mAttr.maxlength = -1;
mAttr.tlength = period_size * maxu(mDevice->NumUpdates, 2);
mAttr.prebuf = 0;
mAttr.minreq = period_size;
op = pa_stream_set_buffer_attr(self->mStream, &self->mAttr, stream_success_callback,
self->mLoop);
wait_for_operation(op, self->mLoop);
op = pa_stream_set_buffer_attr(mStream, &mAttr, stream_success_callback, mLoop);
wait_for_operation(op, mLoop);
device->Frequency = self->mSpec.rate;
mDevice->Frequency = mSpec.rate;
}
pa_stream_set_buffer_attr_callback(self->mStream, &PulsePlayback::bufferAttrCallbackC, self);
self->bufferAttrCallback(self->mStream);
pa_stream_set_buffer_attr_callback(mStream, &PulsePlayback::bufferAttrCallbackC, this);
bufferAttrCallback(mStream);
device->NumUpdates = clampu(
(self->mAttr.tlength + self->mAttr.minreq/2u) / self->mAttr.minreq, 2u, 16u);
device->UpdateSize = self->mAttr.minreq / self->mFrameSize;
mDevice->NumUpdates = clampu((mAttr.tlength + mAttr.minreq/2u) / mAttr.minreq, 2u, 16u);
mDevice->UpdateSize = mAttr.minreq / mFrameSize;
/* HACK: prebuf should be 0 as that's what we set it to. However on some
* systems it comes back as non-0, so we have to make sure the device will
@@ -1179,53 +1157,53 @@ ALCboolean PulsePlayback_reset(PulsePlayback *self)
* may have unintended consequences, but it's better than not starting at
* all.
*/
if(self->mAttr.prebuf != 0)
if(mAttr.prebuf != 0)
{
ALuint len{self->mAttr.prebuf / self->mFrameSize};
if(len <= device->UpdateSize*device->NumUpdates)
ALuint len{mAttr.prebuf / mFrameSize};
if(len <= mDevice->UpdateSize*mDevice->NumUpdates)
ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
len, self->mAttr.prebuf, device->UpdateSize*device->NumUpdates);
len, mAttr.prebuf, mDevice->UpdateSize*mDevice->NumUpdates);
else
{
ERR("Large prebuf, %u samples (%u bytes), increasing device from %u samples",
len, self->mAttr.prebuf, device->UpdateSize*device->NumUpdates);
device->NumUpdates = (len+device->UpdateSize-1) / device->UpdateSize;
len, mAttr.prebuf, mDevice->UpdateSize*mDevice->NumUpdates);
mDevice->NumUpdates = (len+mDevice->UpdateSize-1) / mDevice->UpdateSize;
}
}
return ALC_TRUE;
}
ALCboolean PulsePlayback_start(PulsePlayback *self)
ALCboolean PulsePlayback::start()
{
unique_palock palock{self->mLoop};
unique_palock palock{mLoop};
pa_stream_set_write_callback(self->mStream, &PulsePlayback::streamWriteCallbackC, self);
pa_operation *op{pa_stream_cork(self->mStream, 0, stream_success_callback, self->mLoop)};
wait_for_operation(op, self->mLoop);
pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, mLoop)};
wait_for_operation(op, mLoop);
return ALC_TRUE;
}
void PulsePlayback_stop(PulsePlayback *self)
void PulsePlayback::stop()
{
unique_palock palock{self->mLoop};
unique_palock palock{mLoop};
pa_stream_set_write_callback(self->mStream, nullptr, nullptr);
pa_operation *op{pa_stream_cork(self->mStream, 1, stream_success_callback, self->mLoop)};
wait_for_operation(op, self->mLoop);
pa_stream_set_write_callback(mStream, nullptr, nullptr);
pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, mLoop)};
wait_for_operation(op, mLoop);
}
ClockLatency PulsePlayback_getClockLatency(PulsePlayback *self)
ClockLatency PulsePlayback::getClockLatency()
{
ClockLatency ret;
pa_usec_t latency;
int neg, err;
{ palock_guard _{self->mLoop};
ret.ClockTime = GetDeviceClockTime(self->mDevice);
err = pa_stream_get_latency(self->mStream, &latency, &neg);
{ palock_guard _{mLoop};
ret.ClockTime = GetDeviceClockTime(mDevice);
err = pa_stream_get_latency(mStream, &latency, &neg);
}
if(UNLIKELY(err != 0))
@@ -1247,19 +1225,15 @@ ClockLatency PulsePlayback_getClockLatency(PulsePlayback *self)
}
void PulsePlayback_lock(PulsePlayback *self)
{
pa_threaded_mainloop_lock(self->mLoop);
}
void PulsePlayback::lock() noexcept
{ pa_threaded_mainloop_lock(mLoop); }
void PulsePlayback_unlock(PulsePlayback *self)
{
pa_threaded_mainloop_unlock(self->mLoop);
}
void PulsePlayback::unlock() noexcept
{ pa_threaded_mainloop_unlock(mLoop); }
struct PulseCapture final : public ALCbackend {
PulseCapture(ALCdevice *device) noexcept : ALCbackend{device} { }
struct PulseCapture final : public BackendBase {
PulseCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~PulseCapture() override;
static void contextStateCallbackC(pa_context *context, void *pdata);
@@ -1274,6 +1248,15 @@ struct PulseCapture final : public ALCbackend {
static void streamMovedCallbackC(pa_stream *stream, void *pdata);
void streamMovedCallback(pa_stream *stream);
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
ClockLatency getClockLatency() override;
void lock() noexcept override;
void unlock() noexcept override;
std::string mDeviceName;
const void *mCapStore{nullptr};
@@ -1291,33 +1274,9 @@ struct PulseCapture final : public ALCbackend {
pa_context *mContext{nullptr};
static constexpr inline const char *CurrentPrefix() noexcept { return "PulseCapture::"; }
DEF_NEWDEL(PulseCapture)
};
void PulseCapture_Construct(PulseCapture *self, ALCdevice *device);
void PulseCapture_Destruct(PulseCapture *self);
ALCenum PulseCapture_open(PulseCapture *self, const ALCchar *name);
DECLARE_FORWARD(PulseCapture, ALCbackend, ALCboolean, reset)
ALCboolean PulseCapture_start(PulseCapture *self);
void PulseCapture_stop(PulseCapture *self);
ALCenum PulseCapture_captureSamples(PulseCapture *self, ALCvoid *buffer, ALCuint samples);
ALCuint PulseCapture_availableSamples(PulseCapture *self);
ClockLatency PulseCapture_getClockLatency(PulseCapture *self);
void PulseCapture_lock(PulseCapture *self);
void PulseCapture_unlock(PulseCapture *self);
DECLARE_DEFAULT_ALLOCATORS(PulseCapture)
DEFINE_ALCBACKEND_VTABLE(PulseCapture);
void PulseCapture_Construct(PulseCapture *self, ALCdevice *device)
{
new (self) PulseCapture{device};
SET_VTABLE2(PulseCapture, ALCbackend, self);
}
void PulseCapture_Destruct(PulseCapture *self)
{ self->~PulseCapture(); }
PulseCapture::~PulseCapture()
{
if(!mLoop)
@@ -1377,11 +1336,9 @@ void PulseCapture::streamMovedCallback(pa_stream *stream)
}
ALCenum PulseCapture_open(PulseCapture *self, const ALCchar *name)
ALCenum PulseCapture::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
const char *pulse_name{nullptr};
if(name)
{
if(CaptureDevices.empty())
@@ -1394,38 +1351,38 @@ ALCenum PulseCapture_open(PulseCapture *self, const ALCchar *name)
if(iter == CaptureDevices.cend())
return ALC_INVALID_VALUE;
pulse_name = iter->device_name.c_str();
device->DeviceName = iter->name;
mDevice->DeviceName = iter->name;
}
std::tie(self->mLoop, self->mContext) = pulse_open(&PulseCapture::contextStateCallbackC, self);
if(!self->mLoop) return ALC_INVALID_VALUE;
std::tie(mLoop, mContext) = pulse_open(&PulseCapture::contextStateCallbackC, this);
if(!mLoop) return ALC_INVALID_VALUE;
unique_palock palock{self->mLoop};
unique_palock palock{mLoop};
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtUByte:
self->mSpec.format = PA_SAMPLE_U8;
mSpec.format = PA_SAMPLE_U8;
break;
case DevFmtShort:
self->mSpec.format = PA_SAMPLE_S16NE;
mSpec.format = PA_SAMPLE_S16NE;
break;
case DevFmtInt:
self->mSpec.format = PA_SAMPLE_S32NE;
mSpec.format = PA_SAMPLE_S32NE;
break;
case DevFmtFloat:
self->mSpec.format = PA_SAMPLE_FLOAT32NE;
mSpec.format = PA_SAMPLE_FLOAT32NE;
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
return ALC_INVALID_VALUE;
}
const char *mapname{nullptr};
pa_channel_map chanmap;
switch(device->FmtChans)
switch(mDevice->FmtChans)
{
case DevFmtMono:
mapname = "mono";
@@ -1449,166 +1406,161 @@ ALCenum PulseCapture_open(PulseCapture *self, const ALCchar *name)
mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
break;
case DevFmtAmbi3D:
ERR("%s capture samples not supported\n", DevFmtChannelsString(device->FmtChans));
ERR("%s capture samples not supported\n", DevFmtChannelsString(mDevice->FmtChans));
return ALC_INVALID_VALUE;
}
if(!pa_channel_map_parse(&chanmap, mapname))
{
ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans));
ERR("Failed to build channel map for %s\n", DevFmtChannelsString(mDevice->FmtChans));
return ALC_INVALID_VALUE;
}
self->mSpec.rate = device->Frequency;
self->mSpec.channels = device->channelsFromFmt();
mSpec.rate = mDevice->Frequency;
mSpec.channels = mDevice->channelsFromFmt();
if(pa_sample_spec_valid(&self->mSpec) == 0)
if(pa_sample_spec_valid(&mSpec) == 0)
{
ERR("Invalid sample format\n");
return ALC_INVALID_VALUE;
}
if(!pa_channel_map_init_auto(&chanmap, self->mSpec.channels, PA_CHANNEL_MAP_WAVEEX))
if(!pa_channel_map_init_auto(&chanmap, mSpec.channels, PA_CHANNEL_MAP_WAVEEX))
{
ERR("Couldn't build map for channel count (%d)!\n", self->mSpec.channels);
ERR("Couldn't build map for channel count (%d)!\n", mSpec.channels);
return ALC_INVALID_VALUE;
}
ALuint samples{device->UpdateSize * device->NumUpdates};
samples = maxu(samples, 100 * device->Frequency / 1000);
ALuint samples{mDevice->UpdateSize * mDevice->NumUpdates};
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
self->mAttr.minreq = -1;
self->mAttr.prebuf = -1;
self->mAttr.maxlength = samples * pa_frame_size(&self->mSpec);
self->mAttr.tlength = -1;
self->mAttr.fragsize = minu(samples, 50*device->Frequency/1000) *
pa_frame_size(&self->mSpec);
mAttr.minreq = -1;
mAttr.prebuf = -1;
mAttr.maxlength = samples * pa_frame_size(&mSpec);
mAttr.tlength = -1;
mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * pa_frame_size(&mSpec);
pa_stream_flags_t flags{PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY};
if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 0))
flags |= PA_STREAM_DONT_MOVE;
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
self->mStream = pulse_connect_stream(pulse_name, self->mLoop, self->mContext, flags,
&self->mAttr, &self->mSpec, &chanmap, ALCbackend_Capture);
if(!self->mStream) return ALC_INVALID_VALUE;
mStream = pulse_connect_stream(pulse_name, mLoop, mContext, flags, &mAttr, &mSpec, &chanmap,
ALCbackend_Capture);
if(!mStream) return ALC_INVALID_VALUE;
pa_stream_set_moved_callback(self->mStream, &PulseCapture::streamMovedCallbackC, self);
pa_stream_set_state_callback(self->mStream, &PulseCapture::streamStateCallbackC, self);
pa_stream_set_moved_callback(mStream, &PulseCapture::streamMovedCallbackC, this);
pa_stream_set_state_callback(mStream, &PulseCapture::streamStateCallbackC, this);
self->mDeviceName = pa_stream_get_device_name(self->mStream);
if(device->DeviceName.empty())
mDeviceName = pa_stream_get_device_name(mStream);
if(mDevice->DeviceName.empty())
{
pa_operation *op{pa_context_get_source_info_by_name(self->mContext,
self->mDeviceName.c_str(), &PulseCapture::sourceNameCallbackC, self
)};
wait_for_operation(op, self->mLoop);
pa_operation *op{pa_context_get_source_info_by_name(mContext, mDeviceName.c_str(),
&PulseCapture::sourceNameCallbackC, this)};
wait_for_operation(op, mLoop);
}
return ALC_NO_ERROR;
}
ALCboolean PulseCapture_start(PulseCapture *self)
ALCboolean PulseCapture::start()
{
palock_guard _{self->mLoop};
pa_operation *op{pa_stream_cork(self->mStream, 0, stream_success_callback, self->mLoop)};
wait_for_operation(op, self->mLoop);
palock_guard _{mLoop};
pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, mLoop)};
wait_for_operation(op, mLoop);
return ALC_TRUE;
}
void PulseCapture_stop(PulseCapture *self)
void PulseCapture::stop()
{
palock_guard _{self->mLoop};
pa_operation *op{pa_stream_cork(self->mStream, 1, stream_success_callback, self->mLoop)};
wait_for_operation(op, self->mLoop);
palock_guard _{mLoop};
pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, mLoop)};
wait_for_operation(op, mLoop);
}
ALCenum PulseCapture_captureSamples(PulseCapture *self, ALCvoid *buffer, ALCuint samples)
ALCenum PulseCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
{
ALCdevice *device{self->mDevice};
ALCuint todo{samples * static_cast<ALCuint>(pa_frame_size(&self->mSpec))};
ALCuint todo{samples * static_cast<ALCuint>(pa_frame_size(&mSpec))};
/* Capture is done in fragment-sized chunks, so we loop until we get all
* that's available */
self->mLastReadable -= todo;
unique_palock palock{self->mLoop};
mLastReadable -= todo;
unique_palock palock{mLoop};
while(todo > 0)
{
size_t rem{todo};
if(self->mCapLen == 0)
if(mCapLen == 0)
{
pa_stream_state_t state{pa_stream_get_state(self->mStream)};
pa_stream_state_t state{pa_stream_get_state(mStream)};
if(!PA_STREAM_IS_GOOD(state))
{
aluHandleDisconnect(device, "Bad capture state: %u", state);
aluHandleDisconnect(mDevice, "Bad capture state: %u", state);
return ALC_INVALID_DEVICE;
}
if(pa_stream_peek(self->mStream, &self->mCapStore, &self->mCapLen) < 0)
if(pa_stream_peek(mStream, &mCapStore, &mCapLen) < 0)
{
ERR("pa_stream_peek() failed: %s\n",
pa_strerror(pa_context_errno(self->mContext)));
aluHandleDisconnect(device, "Failed retrieving capture samples: %s",
pa_strerror(pa_context_errno(self->mContext)));
pa_strerror(pa_context_errno(mContext)));
aluHandleDisconnect(mDevice, "Failed retrieving capture samples: %s",
pa_strerror(pa_context_errno(mContext)));
return ALC_INVALID_DEVICE;
}
self->mCapRemain = self->mCapLen;
mCapRemain = mCapLen;
}
if(rem > self->mCapRemain)
rem = self->mCapRemain;
rem = minz(rem, mCapRemain);
memcpy(buffer, self->mCapStore, rem);
memcpy(buffer, mCapStore, rem);
buffer = (ALbyte*)buffer + rem;
todo -= rem;
self->mCapStore = (ALbyte*)self->mCapStore + rem;
self->mCapRemain -= rem;
if(self->mCapRemain == 0)
mCapStore = (ALbyte*)mCapStore + rem;
mCapRemain -= rem;
if(mCapRemain == 0)
{
pa_stream_drop(self->mStream);
self->mCapLen = 0;
pa_stream_drop(mStream);
mCapLen = 0;
}
}
palock.unlock();
if(todo > 0)
memset(buffer, ((device->FmtType==DevFmtUByte) ? 0x80 : 0), todo);
memset(buffer, ((mDevice->FmtType==DevFmtUByte) ? 0x80 : 0), todo);
return ALC_NO_ERROR;
}
ALCuint PulseCapture_availableSamples(PulseCapture *self)
ALCuint PulseCapture::availableSamples()
{
ALCdevice *device{self->mDevice};
size_t readable{self->mCapRemain};
size_t readable{mCapRemain};
if(device->Connected.load(std::memory_order_acquire))
if(mDevice->Connected.load(std::memory_order_acquire))
{
palock_guard _{self->mLoop};
size_t got{pa_stream_readable_size(self->mStream)};
palock_guard _{mLoop};
size_t got{pa_stream_readable_size(mStream)};
if(static_cast<ssize_t>(got) < 0)
{
ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
aluHandleDisconnect(device, "Failed getting readable size: %s", pa_strerror(got));
aluHandleDisconnect(mDevice, "Failed getting readable size: %s", pa_strerror(got));
}
else if(got > self->mCapLen)
readable += got - self->mCapLen;
else if(got > mCapLen)
readable += got - mCapLen;
}
if(self->mLastReadable < readable)
self->mLastReadable = readable;
return self->mLastReadable / pa_frame_size(&self->mSpec);
if(mLastReadable < readable)
mLastReadable = readable;
return mLastReadable / pa_frame_size(&mSpec);
}
ClockLatency PulseCapture_getClockLatency(PulseCapture *self)
ClockLatency PulseCapture::getClockLatency()
{
ClockLatency ret;
pa_usec_t latency;
int neg, err;
{ palock_guard _{self->mLoop};
ret.ClockTime = GetDeviceClockTime(self->mDevice);
err = pa_stream_get_latency(self->mStream, &latency, &neg);
{ palock_guard _{mLoop};
ret.ClockTime = GetDeviceClockTime(mDevice);
err = pa_stream_get_latency(mStream, &latency, &neg);
}
if(UNLIKELY(err != 0))
@@ -1625,15 +1577,11 @@ ClockLatency PulseCapture_getClockLatency(PulseCapture *self)
}
void PulseCapture_lock(PulseCapture *self)
{
pa_threaded_mainloop_lock(self->mLoop);
}
void PulseCapture::lock() noexcept
{ pa_threaded_mainloop_lock(mLoop); }
void PulseCapture_unlock(PulseCapture *self)
{
pa_threaded_mainloop_unlock(self->mLoop);
}
void PulseCapture::unlock() noexcept
{ pa_threaded_mainloop_unlock(mLoop); }
} // namespace
@@ -1720,21 +1668,12 @@ void PulseBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *PulseBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *PulseBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
PulsePlayback *backend;
NEW_OBJ(backend, PulsePlayback)(device);
return backend;
}
return new PulsePlayback{device};
if(type == ALCbackend_Capture)
{
PulseCapture *backend;
NEW_OBJ(backend, PulseCapture)(device);
return backend;
}
return new PulseCapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+48 -107
View File
@@ -170,26 +170,20 @@ void deviceList(int type, al::vector<DevMap> *devmap)
/* Wrappers to use an old-style backend with the new interface. */
struct PlaybackWrapper final : public ALCbackend {
PlaybackWrapper(ALCdevice *device) noexcept : ALCbackend{device} { }
struct PlaybackWrapper final : public BackendBase {
PlaybackWrapper(ALCdevice *device) noexcept : BackendBase{device} { }
~PlaybackWrapper() override;
std::unique_ptr<qsa_data> mExtraData;
};
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
static void PlaybackWrapper_Destruct(PlaybackWrapper *self);
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
static void PlaybackWrapper_stop(PlaybackWrapper *self);
static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
std::unique_ptr<qsa_data> mExtraData;
static constexpr inline const char *CurrentPrefix() noexcept { return "PlaybackWrapper::"; }
DEF_NEWDEL(PlaybackWrapper)
};
FORCE_ALIGN static int qsa_proc_playback(void *ptr)
@@ -213,7 +207,7 @@ FORCE_ALIGN static int qsa_proc_playback(void *ptr)
const ALint frame_size = device->frameSizeFromFmt();
PlaybackWrapper_lock(self);
self->lock();
while(!data->mKillNow.load(std::memory_order_acquire))
{
pollfd pollitem{};
@@ -221,9 +215,9 @@ FORCE_ALIGN static int qsa_proc_playback(void *ptr)
pollitem.events = POLLOUT;
/* Select also works like time slice to OS */
PlaybackWrapper_unlock(self);
self->unlock();
sret = poll(&pollitem, 1, 2000);
PlaybackWrapper_lock(self);
self->lock();
if(sret == -1)
{
if(errno == EINTR || errno == EAGAIN)
@@ -272,7 +266,7 @@ FORCE_ALIGN static int qsa_proc_playback(void *ptr)
}
}
}
PlaybackWrapper_unlock(self);
self->unlock();
return 0;
}
@@ -614,69 +608,45 @@ static void qsa_stop_playback(PlaybackWrapper *self)
}
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
{
new (self) PlaybackWrapper{device};
SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
}
static void PlaybackWrapper_Destruct(PlaybackWrapper *self)
{ self->~PlaybackWrapper(); }
PlaybackWrapper::~PlaybackWrapper()
{
if(mExtraData)
qsa_close_playback(this);
}
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
{
return qsa_open_playback(self, name);
}
ALCenum PlaybackWrapper::open(const ALCchar *name)
{ return qsa_open_playback(this, name); }
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
{
return qsa_reset_playback(self);
}
ALCboolean PlaybackWrapper::reset()
{ return qsa_reset_playback(this); }
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
{
return qsa_start_playback(self);
}
static void PlaybackWrapper_stop(PlaybackWrapper *self)
{
qsa_stop_playback(self);
}
ALCboolean PlaybackWrapper::start()
{ return qsa_start_playback(this); }
void PlaybackWrapper::stop()
{ qsa_stop_playback(this); }
/***********/
/* Capture */
/***********/
struct CaptureWrapper final : public ALCbackend {
CaptureWrapper(ALCdevice *device) noexcept : ALCbackend{device} { }
struct CaptureWrapper final : public BackendBase {
CaptureWrapper(ALCdevice *device) noexcept : BackendBase{device} { }
~CaptureWrapper() override;
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(void *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
std::unique_ptr<qsa_data> mExtraData;
static constexpr inline const char *CurrentPrefix() noexcept { return "CaptureWrapper::"; }
DEF_NEWDEL(CaptureWrapper)
};
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
static void CaptureWrapper_Destruct(CaptureWrapper *self);
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
static void CaptureWrapper_stop(CaptureWrapper *self);
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
{
ALCdevice *device = self->mDevice;
@@ -917,46 +887,26 @@ static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuin
}
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
{
new (self) CaptureWrapper{device};
SET_VTABLE2(CaptureWrapper, ALCbackend, self);
}
static void CaptureWrapper_Destruct(CaptureWrapper *self)
{ self->~CaptureWrapper(); }
CaptureWrapper::~CaptureWrapper()
{
if(mExtraData)
qsa_close_capture(this);
}
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
{
return qsa_open_capture(self, name);
}
ALCenum CaptureWrapper::open(const ALCchar *name)
{ return qsa_open_capture(this, name); }
static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
{
qsa_start_capture(self);
return ALC_TRUE;
}
ALCboolean CaptureWrapper::start()
{ qsa_start_capture(this); return ALC_TRUE; }
static void CaptureWrapper_stop(CaptureWrapper *self)
{
qsa_stop_capture(self);
}
void CaptureWrapper::stop()
{ qsa_stop_capture(this); }
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
{
return qsa_capture_samples(self, buffer, samples);
}
ALCenum CaptureWrapper::captureSamples(void *buffer, ALCuint samples)
{ return qsa_capture_samples(this, buffer, samples); }
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
{
return qsa_available_samples(self);
}
ALCuint CaptureWrapper::availableSamples()
{ return qsa_available_samples(this); }
} // namespace
@@ -1002,22 +952,13 @@ void QSABackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *QSABackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *QSABackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
PlaybackWrapper *backend;
NEW_OBJ(backend, PlaybackWrapper)(device);
return backend;
}
return new PlaybackWrapper{device};
if(type == ALCbackend_Capture)
{
CaptureWrapper *backend;
NEW_OBJ(backend, CaptureWrapper)(device);
return backend;
}
return NULL;
return new CaptureWrapper{device};
return nullptr;
}
BackendFactory &QSABackendFactory::getFactory()
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+65 -93
View File
@@ -43,13 +43,20 @@ namespace {
constexpr ALCchar defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
struct ALCsdl2Backend final : public ALCbackend {
ALCsdl2Backend(ALCdevice *device) noexcept : ALCbackend{device} { }
~ALCsdl2Backend() override;
struct Sdl2Backend final : public BackendBase {
Sdl2Backend(ALCdevice *device) noexcept : BackendBase{device} { }
~Sdl2Backend() override;
static void audioCallbackC(void *ptr, Uint8 *stream, int len);
void audioCallback(Uint8 *stream, int len);
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
void lock() noexcept override;
void unlock() noexcept override;
SDL_AudioDeviceID mDeviceID{0u};
ALsizei mFrameSize{0};
@@ -57,55 +64,32 @@ struct ALCsdl2Backend final : public ALCbackend {
DevFmtChannels mFmtChans{};
DevFmtType mFmtType{};
ALuint mUpdateSize{0u};
static constexpr inline const char *CurrentPrefix() noexcept { return "ALCsdl2Playback::"; }
DEF_NEWDEL(Sdl2Backend)
};
void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device);
void ALCsdl2Backend_Destruct(ALCsdl2Backend *self);
ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name);
ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self);
ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self);
void ALCsdl2Backend_stop(ALCsdl2Backend *self);
DECLARE_FORWARD2(ALCsdl2Backend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ClockLatency, getClockLatency)
void ALCsdl2Backend_lock(ALCsdl2Backend *self);
void ALCsdl2Backend_unlock(ALCsdl2Backend *self);
DECLARE_DEFAULT_ALLOCATORS(ALCsdl2Backend)
DEFINE_ALCBACKEND_VTABLE(ALCsdl2Backend);
void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device)
{
new (self) ALCsdl2Backend{device};
SET_VTABLE2(ALCsdl2Backend, ALCbackend, self);
}
void ALCsdl2Backend_Destruct(ALCsdl2Backend *self)
{ self->~ALCsdl2Backend(); }
ALCsdl2Backend::~ALCsdl2Backend()
Sdl2Backend::~Sdl2Backend()
{
if(mDeviceID)
SDL_CloseAudioDevice(mDeviceID);
mDeviceID = 0;
}
void ALCsdl2Backend::audioCallbackC(void *ptr, Uint8 *stream, int len)
{ static_cast<ALCsdl2Backend*>(ptr)->audioCallback(stream, len); }
void Sdl2Backend::audioCallbackC(void *ptr, Uint8 *stream, int len)
{ static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); }
void ALCsdl2Backend::audioCallback(Uint8 *stream, int len)
void Sdl2Backend::audioCallback(Uint8 *stream, int len)
{
assert((len % mFrameSize) == 0);
aluMixData(mDevice, stream, len / mFrameSize);
}
ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name)
ALCenum Sdl2Backend::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
SDL_AudioSpec want{}, have{};
want.freq = device->Frequency;
switch(device->FmtType)
want.freq = mDevice->Frequency;
switch(mDevice->FmtType)
{
case DevFmtUByte: want.format = AUDIO_U8; break;
case DevFmtByte: want.format = AUDIO_S8; break;
@@ -115,35 +99,35 @@ ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name)
case DevFmtInt: want.format = AUDIO_S32SYS; break;
case DevFmtFloat: want.format = AUDIO_F32; break;
}
want.channels = (device->FmtChans == DevFmtMono) ? 1 : 2;
want.samples = device->UpdateSize;
want.callback = &ALCsdl2Backend::audioCallbackC;
want.userdata = self;
want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
want.samples = mDevice->UpdateSize;
want.callback = &Sdl2Backend::audioCallbackC;
want.userdata = this;
/* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
* necessarily the first in the list.
*/
if(!name || strcmp(name, defaultDeviceName) == 0)
self->mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
SDL_AUDIO_ALLOW_ANY_CHANGE);
mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
SDL_AUDIO_ALLOW_ANY_CHANGE);
else
{
const size_t prefix_len = strlen(DEVNAME_PREFIX);
if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
self->mDeviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
SDL_AUDIO_ALLOW_ANY_CHANGE);
mDeviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
SDL_AUDIO_ALLOW_ANY_CHANGE);
else
self->mDeviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
SDL_AUDIO_ALLOW_ANY_CHANGE);
mDeviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
SDL_AUDIO_ALLOW_ANY_CHANGE);
}
if(self->mDeviceID == 0)
if(mDeviceID == 0)
return ALC_INVALID_VALUE;
device->Frequency = have.freq;
mDevice->Frequency = have.freq;
if(have.channels == 1)
device->FmtChans = DevFmtMono;
mDevice->FmtChans = DevFmtMono;
else if(have.channels == 2)
device->FmtChans = DevFmtStereo;
mDevice->FmtChans = DevFmtStereo;
else
{
ERR("Got unhandled SDL channel count: %d\n", (int)have.channels);
@@ -151,61 +135,54 @@ ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name)
}
switch(have.format)
{
case AUDIO_U8: device->FmtType = DevFmtUByte; break;
case AUDIO_S8: device->FmtType = DevFmtByte; break;
case AUDIO_U16SYS: device->FmtType = DevFmtUShort; break;
case AUDIO_S16SYS: device->FmtType = DevFmtShort; break;
case AUDIO_S32SYS: device->FmtType = DevFmtInt; break;
case AUDIO_F32SYS: device->FmtType = DevFmtFloat; break;
case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break;
case AUDIO_S8: mDevice->FmtType = DevFmtByte; break;
case AUDIO_U16SYS: mDevice->FmtType = DevFmtUShort; break;
case AUDIO_S16SYS: mDevice->FmtType = DevFmtShort; break;
case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break;
case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break;
default:
ERR("Got unsupported SDL format: 0x%04x\n", have.format);
return ALC_INVALID_VALUE;
}
device->UpdateSize = have.samples;
device->NumUpdates = 2; /* SDL always (tries to) use two periods. */
mDevice->UpdateSize = have.samples;
mDevice->NumUpdates = 2; /* SDL always (tries to) use two periods. */
self->mFrameSize = device->frameSizeFromFmt();
self->mFrequency = device->Frequency;
self->mFmtChans = device->FmtChans;
self->mFmtType = device->FmtType;
self->mUpdateSize = device->UpdateSize;
mFrameSize = mDevice->frameSizeFromFmt();
mFrequency = mDevice->Frequency;
mFmtChans = mDevice->FmtChans;
mFmtType = mDevice->FmtType;
mUpdateSize = mDevice->UpdateSize;
device->DeviceName = name ? name : defaultDeviceName;
mDevice->DeviceName = name ? name : defaultDeviceName;
return ALC_NO_ERROR;
}
ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self)
ALCboolean Sdl2Backend::reset()
{
ALCdevice *device{self->mDevice};
device->Frequency = self->mFrequency;
device->FmtChans = self->mFmtChans;
device->FmtType = self->mFmtType;
device->UpdateSize = self->mUpdateSize;
device->NumUpdates = 2;
SetDefaultWFXChannelOrder(device);
mDevice->Frequency = mFrequency;
mDevice->FmtChans = mFmtChans;
mDevice->FmtType = mFmtType;
mDevice->UpdateSize = mUpdateSize;
mDevice->NumUpdates = 2;
SetDefaultWFXChannelOrder(mDevice);
return ALC_TRUE;
}
ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self)
ALCboolean Sdl2Backend::start()
{
SDL_PauseAudioDevice(self->mDeviceID, 0);
SDL_PauseAudioDevice(mDeviceID, 0);
return ALC_TRUE;
}
void ALCsdl2Backend_stop(ALCsdl2Backend *self)
{
SDL_PauseAudioDevice(self->mDeviceID, 1);
}
void Sdl2Backend::stop()
{ SDL_PauseAudioDevice(mDeviceID, 1); }
void ALCsdl2Backend_lock(ALCsdl2Backend *self)
{
SDL_LockAudioDevice(self->mDeviceID);
}
void Sdl2Backend::lock() noexcept
{ SDL_LockAudioDevice(mDeviceID); }
void ALCsdl2Backend_unlock(ALCsdl2Backend *self)
{
SDL_UnlockAudioDevice(self->mDeviceID);
}
void Sdl2Backend::unlock() noexcept
{ SDL_UnlockAudioDevice(mDeviceID); }
} // namespace
@@ -248,14 +225,9 @@ void SDL2BackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *SDL2BackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *SDL2BackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCsdl2Backend *backend;
NEW_OBJ(backend, ALCsdl2Backend)(device);
return backend;
}
return new Sdl2Backend{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+102 -156
View File
@@ -42,12 +42,17 @@ namespace {
static const ALCchar sndio_device[] = "SndIO Default";
struct SndioPlayback final : public ALCbackend {
SndioPlayback(ALCdevice *device) noexcept : ALCbackend{device} { }
struct SndioPlayback final : public BackendBase {
SndioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~SndioPlayback() override;
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
sio_hdl *mSndHandle{nullptr};
al::vector<ALubyte> mBuffer;
@@ -56,32 +61,9 @@ struct SndioPlayback final : public ALCbackend {
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "SndioPlayback::"; }
DEF_NEWDEL(SndioPlayback)
};
void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device);
void SndioPlayback_Destruct(SndioPlayback *self);
ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name);
ALCboolean SndioPlayback_reset(SndioPlayback *self);
ALCboolean SndioPlayback_start(SndioPlayback *self);
void SndioPlayback_stop(SndioPlayback *self);
DECLARE_FORWARD2(SndioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
DECLARE_FORWARD(SndioPlayback, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(SndioPlayback, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(SndioPlayback, ALCbackend, void, lock)
DECLARE_FORWARD(SndioPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(SndioPlayback)
DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
{
new (self) SndioPlayback{device};
SET_VTABLE2(SndioPlayback, ALCbackend, self);
}
void SndioPlayback_Destruct(SndioPlayback *self)
{ self->~SndioPlayback(); }
SndioPlayback::~SndioPlayback()
{
if(mSndHandle)
@@ -102,18 +84,18 @@ int SndioPlayback::mixerProc()
auto WritePtr = static_cast<ALubyte*>(mBuffer.data());
size_t len{mBuffer.size()};
SndioPlayback_lock(this);
lock();
aluMixData(mDevice, WritePtr, len/frameSize);
SndioPlayback_unlock(this);
unlock();
while(len > 0 && !mKillNow.load(std::memory_order_acquire))
{
size_t wrote{sio_write(mSndHandle, WritePtr, len)};
if(wrote == 0)
{
ERR("sio_write failed\n");
SndioPlayback_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failed to write playback samples");
SndioPlayback_unlock(this);
unlock();
break;
}
@@ -126,37 +108,33 @@ int SndioPlayback::mixerProc()
}
ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
ALCenum SndioPlayback::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
if(!name)
name = sndio_device;
else if(strcmp(name, sndio_device) != 0)
return ALC_INVALID_VALUE;
self->mSndHandle = sio_open(nullptr, SIO_PLAY, 0);
if(self->mSndHandle == nullptr)
mSndHandle = sio_open(nullptr, SIO_PLAY, 0);
if(mSndHandle == nullptr)
{
ERR("Could not open device\n");
return ALC_INVALID_VALUE;
}
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean SndioPlayback_reset(SndioPlayback *self)
ALCboolean SndioPlayback::reset()
{
ALCdevice *device{self->mDevice};
sio_par par;
sio_initpar(&par);
par.rate = device->Frequency;
par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
par.rate = mDevice->Frequency;
par.pchan = ((mDevice->FmtChans != DevFmtMono) ? 2 : 1);
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
par.bits = 8;
@@ -186,11 +164,11 @@ ALCboolean SndioPlayback_reset(SndioPlayback *self)
}
par.le = SIO_LE_NATIVE;
par.round = device->UpdateSize;
par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
if(!par.appbufsz) par.appbufsz = device->UpdateSize;
par.round = mDevice->UpdateSize;
par.appbufsz = mDevice->UpdateSize * (mDevice->NumUpdates-1);
if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
if(!sio_setpar(self->mSndHandle, &par) || !sio_getpar(self->mSndHandle, &par))
if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
{
ERR("Failed to set device parameters\n");
return ALC_FALSE;
@@ -202,49 +180,49 @@ ALCboolean SndioPlayback_reset(SndioPlayback *self)
return ALC_FALSE;
}
device->Frequency = par.rate;
device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
mDevice->Frequency = par.rate;
mDevice->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
if(par.bits == 8 && par.sig == 1)
device->FmtType = DevFmtByte;
mDevice->FmtType = DevFmtByte;
else if(par.bits == 8 && par.sig == 0)
device->FmtType = DevFmtUByte;
mDevice->FmtType = DevFmtUByte;
else if(par.bits == 16 && par.sig == 1)
device->FmtType = DevFmtShort;
mDevice->FmtType = DevFmtShort;
else if(par.bits == 16 && par.sig == 0)
device->FmtType = DevFmtUShort;
mDevice->FmtType = DevFmtUShort;
else if(par.bits == 32 && par.sig == 1)
device->FmtType = DevFmtInt;
mDevice->FmtType = DevFmtInt;
else if(par.bits == 32 && par.sig == 0)
device->FmtType = DevFmtUInt;
mDevice->FmtType = DevFmtUInt;
else
{
ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
return ALC_FALSE;
}
SetDefaultChannelOrder(device);
SetDefaultChannelOrder(mDevice);
device->UpdateSize = par.round;
device->NumUpdates = (par.bufsz/par.round) + 1;
mDevice->UpdateSize = par.round;
mDevice->NumUpdates = (par.bufsz/par.round) + 1;
self->mBuffer.resize(device->UpdateSize * device->frameSizeFromFmt());
std::fill(self->mBuffer.begin(), self->mBuffer.end(), 0);
mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
std::fill(mBuffer.begin(), mBuffer.end(), 0);
return ALC_TRUE;
}
ALCboolean SndioPlayback_start(SndioPlayback *self)
ALCboolean SndioPlayback::start()
{
if(!sio_start(self->mSndHandle))
if(!sio_start(mSndHandle))
{
ERR("Error starting playback\n");
return ALC_FALSE;
}
try {
self->mKillNow.store(false, std::memory_order_release);
self->mThread = std::thread{std::mem_fn(&SndioPlayback::mixerProc), self};
mKillNow.store(false, std::memory_order_release);
mThread = std::thread{std::mem_fn(&SndioPlayback::mixerProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -252,27 +230,33 @@ ALCboolean SndioPlayback_start(SndioPlayback *self)
}
catch(...) {
}
sio_stop(self->mSndHandle);
sio_stop(mSndHandle);
return ALC_FALSE;
}
void SndioPlayback_stop(SndioPlayback *self)
void SndioPlayback::stop()
{
if(self->mKillNow.exchange(true, std::memory_order_acq_rel) || !self->mThread.joinable())
if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
return;
self->mThread.join();
mThread.join();
if(!sio_stop(self->mSndHandle))
if(!sio_stop(mSndHandle))
ERR("Error stopping device\n");
}
struct SndioCapture final : public ALCbackend {
SndioCapture(ALCdevice *device) noexcept : ALCbackend{device} { }
struct SndioCapture final : public BackendBase {
SndioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~SndioCapture() override;
int recordProc();
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(void *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
sio_hdl *mSndHandle{nullptr};
RingBufferPtr mRing;
@@ -281,32 +265,9 @@ struct SndioCapture final : public ALCbackend {
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "SndioCapture::"; }
DEF_NEWDEL(SndioCapture)
};
void SndioCapture_Construct(SndioCapture *self, ALCdevice *device);
void SndioCapture_Destruct(SndioCapture *self);
ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name);
DECLARE_FORWARD(SndioCapture, ALCbackend, ALCboolean, reset)
ALCboolean SndioCapture_start(SndioCapture *self);
void SndioCapture_stop(SndioCapture *self);
ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples);
ALCuint SndioCapture_availableSamples(SndioCapture *self);
DECLARE_FORWARD(SndioCapture, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(SndioCapture, ALCbackend, void, lock)
DECLARE_FORWARD(SndioCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(SndioCapture)
DEFINE_ALCBACKEND_VTABLE(SndioCapture);
void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
{
new (self) SndioCapture{device};
SET_VTABLE2(SndioCapture, ALCbackend, self);
}
void SndioCapture_Destruct(SndioCapture *self)
{ self->~SndioCapture(); }
SndioCapture::~SndioCapture()
{
if(mSndHandle)
@@ -346,9 +307,9 @@ int SndioCapture::recordProc()
size_t got{sio_read(mSndHandle, data.first.buf, minz(todo-total, data.first.len))};
if(!got)
{
SndioCapture_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failed to read capture samples");
SndioCapture_unlock(this);
unlock();
break;
}
@@ -363,26 +324,24 @@ int SndioCapture::recordProc()
}
ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
ALCenum SndioCapture::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
sio_par par;
if(!name)
name = sndio_device;
else if(strcmp(name, sndio_device) != 0)
return ALC_INVALID_VALUE;
self->mSndHandle = sio_open(nullptr, SIO_REC, 0);
if(self->mSndHandle == nullptr)
mSndHandle = sio_open(nullptr, SIO_REC, 0);
if(mSndHandle == nullptr)
{
ERR("Could not open device\n");
return ALC_INVALID_VALUE;
}
sio_par par;
sio_initpar(&par);
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
par.bps = 1;
@@ -409,23 +368,23 @@ ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
par.sig = 0;
break;
case DevFmtFloat:
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
return ALC_INVALID_VALUE;
}
par.bits = par.bps * 8;
par.le = SIO_LE_NATIVE;
par.msb = SIO_LE_NATIVE ? 0 : 1;
par.rchan = device->channelsFromFmt();
par.rate = device->Frequency;
par.rchan = mDevice->channelsFromFmt();
par.rate = mDevice->Frequency;
par.appbufsz = maxu(device->UpdateSize*device->NumUpdates, (device->Frequency+9)/10);
par.round = clampu(par.appbufsz/device->NumUpdates, (device->Frequency+99)/100,
(device->Frequency+19)/20);
par.appbufsz = maxu(mDevice->UpdateSize*mDevice->NumUpdates, (mDevice->Frequency+9)/10);
par.round = clampu(par.appbufsz/mDevice->NumUpdates, (mDevice->Frequency+99)/100,
(mDevice->Frequency+19)/20);
device->UpdateSize = par.round;
device->NumUpdates = maxu(par.appbufsz/par.round, 1);
mDevice->UpdateSize = par.round;
mDevice->NumUpdates = maxu(par.appbufsz/par.round, 1);
if(!sio_setpar(self->mSndHandle, &par) || !sio_getpar(self->mSndHandle, &par))
if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
{
ERR("Failed to set device parameters\n");
return ALC_INVALID_VALUE;
@@ -437,46 +396,46 @@ ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
return ALC_INVALID_VALUE;
}
if(!((device->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
(device->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
(device->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
(device->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
(device->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
(device->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
device->channelsFromFmt() != (ALsizei)par.rchan ||
device->Frequency != par.rate)
if(!((mDevice->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
(mDevice->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
(mDevice->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
(mDevice->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
(mDevice->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
(mDevice->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
mDevice->channelsFromFmt() != (ALsizei)par.rchan ||
mDevice->Frequency != par.rate)
{
ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
DevFmtTypeString(device->FmtType), DevFmtChannelsString(device->FmtChans),
device->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
mDevice->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
return ALC_INVALID_VALUE;
}
self->mRing = CreateRingBuffer(device->UpdateSize*device->NumUpdates, par.bps*par.rchan, false);
if(!self->mRing)
mRing = CreateRingBuffer(mDevice->UpdateSize*mDevice->NumUpdates, par.bps*par.rchan, false);
if(!mRing)
{
ERR("Failed to allocate %u-byte ringbuffer\n",
device->UpdateSize*device->NumUpdates*par.bps*par.rchan);
mDevice->UpdateSize*mDevice->NumUpdates*par.bps*par.rchan);
return ALC_OUT_OF_MEMORY;
}
SetDefaultChannelOrder(device);
SetDefaultChannelOrder(mDevice);
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean SndioCapture_start(SndioCapture *self)
ALCboolean SndioCapture::start()
{
if(!sio_start(self->mSndHandle))
if(!sio_start(mSndHandle))
{
ERR("Error starting playback\n");
return ALC_FALSE;
}
try {
self->mKillNow.store(false, std::memory_order_release);
self->mThread = std::thread{std::mem_fn(&SndioCapture::recordProc), self};
mKillNow.store(false, std::memory_order_release);
mThread = std::thread{std::mem_fn(&SndioCapture::recordProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -484,32 +443,28 @@ ALCboolean SndioCapture_start(SndioCapture *self)
}
catch(...) {
}
sio_stop(self->mSndHandle);
sio_stop(mSndHandle);
return ALC_FALSE;
}
void SndioCapture_stop(SndioCapture *self)
void SndioCapture::stop()
{
if(self->mKillNow.exchange(true, std::memory_order_acq_rel) || !self->mThread.joinable())
if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
return;
self->mThread.join();
mThread.join();
if(!sio_stop(self->mSndHandle))
if(!sio_stop(mSndHandle))
ERR("Error stopping device\n");
}
ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples)
ALCenum SndioCapture::captureSamples(void *buffer, ALCuint samples)
{
RingBuffer *ring{self->mRing.get()};
ring->read(buffer, samples);
mRing->read(buffer, samples);
return ALC_NO_ERROR;
}
ALCuint SndioCapture_availableSamples(SndioCapture *self)
{
RingBuffer *ring{self->mRing.get()};
return ring->readSpace();
}
ALCuint SndioCapture::availableSamples()
{ return mRing->readSpace(); }
} // namespace
@@ -537,20 +492,11 @@ void SndIOBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *SndIOBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *SndIOBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
SndioPlayback *backend;
NEW_OBJ(backend, SndioPlayback)(device);
return backend;
}
return new SndioPlayback{device};
if(type == ALCbackend_Capture)
{
SndioCapture *backend;
NEW_OBJ(backend, SndioCapture)(device);
return backend;
}
return new SndioCapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+47 -77
View File
@@ -54,12 +54,17 @@ constexpr ALCchar solaris_device[] = "Solaris Default";
const char *solaris_driver = "/dev/audio";
struct SolarisBackend final : public ALCbackend {
SolarisBackend(ALCdevice *device) noexcept : ALCbackend{device} { }
struct SolarisBackend final : public BackendBase {
SolarisBackend(ALCdevice *device) noexcept : BackendBase{device} { }
~SolarisBackend() override;
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
int mFd{-1};
al::vector<ALubyte> mBuffer;
@@ -68,32 +73,9 @@ struct SolarisBackend final : public ALCbackend {
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "SolarisBackend::"; }
DEF_NEWDEL(SolarisBackend)
};
void SolarisBackend_Construct(SolarisBackend *self, ALCdevice *device);
void SolarisBackend_Destruct(SolarisBackend *self);
ALCenum SolarisBackend_open(SolarisBackend *self, const ALCchar *name);
ALCboolean SolarisBackend_reset(SolarisBackend *self);
ALCboolean SolarisBackend_start(SolarisBackend *self);
void SolarisBackend_stop(SolarisBackend *self);
DECLARE_FORWARD2(SolarisBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
DECLARE_FORWARD(SolarisBackend, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(SolarisBackend, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(SolarisBackend, ALCbackend, void, lock)
DECLARE_FORWARD(SolarisBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(SolarisBackend)
DEFINE_ALCBACKEND_VTABLE(SolarisBackend);
void SolarisBackend_Construct(SolarisBackend *self, ALCdevice *device)
{
new (self) SolarisBackend{device};
SET_VTABLE2(SolarisBackend, ALCbackend, self);
}
void SolarisBackend_Destruct(SolarisBackend *self)
{ self->~SolarisBackend(); }
SolarisBackend::~SolarisBackend()
{
if(mFd != -1)
@@ -108,7 +90,7 @@ int SolarisBackend::mixerProc()
const int frame_size{mDevice->frameSizeFromFmt()};
SolarisBackend_lock(this);
lock();
while(!mKillNow.load(std::memory_order_acquire) &&
mDevice->Connected.load(std::memory_order_acquire))
{
@@ -116,9 +98,9 @@ int SolarisBackend::mixerProc()
pollitem.fd = mFd;
pollitem.events = POLLOUT;
SolarisBackend_unlock(this);
unlock();
int pret{poll(&pollitem, 1, 1000)};
SolarisBackend_lock(this);
lock();
if(pret < 0)
{
if(errno == EINTR || errno == EAGAIN)
@@ -154,49 +136,43 @@ int SolarisBackend::mixerProc()
write_ptr += wrote;
}
}
SolarisBackend_unlock(this);
unlock();
return 0;
}
ALCenum SolarisBackend_open(SolarisBackend *self, const ALCchar *name)
ALCenum SolarisBackend::open(const ALCchar *name)
{
if(!name)
name = solaris_device;
else if(strcmp(name, solaris_device) != 0)
return ALC_INVALID_VALUE;
self->mFd = open(solaris_driver, O_WRONLY);
if(self->mFd == -1)
mFd = ::open(solaris_driver, O_WRONLY);
if(mFd == -1)
{
ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
return ALC_INVALID_VALUE;
}
ALCdevice *device{self->mDevice};
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean SolarisBackend_reset(SolarisBackend *self)
ALCboolean SolarisBackend::reset()
{
ALCdevice *device{self->mDevice};
audio_info_t info;
ALsizei frameSize;
ALsizei numChannels;
AUDIO_INITINFO(&info);
info.play.sample_rate = device->Frequency;
info.play.sample_rate = mDevice->Frequency;
if(device->FmtChans != DevFmtMono)
device->FmtChans = DevFmtStereo;
numChannels = device->channelsFromFmt();
if(mDevice->FmtChans != DevFmtMono)
mDevice->FmtChans = DevFmtStereo;
ALsizei numChannels{mDevice->channelsFromFmt()};
info.play.channels = numChannels;
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
info.play.precision = 8;
@@ -210,7 +186,7 @@ ALCboolean SolarisBackend_reset(SolarisBackend *self)
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
device->FmtType = DevFmtShort;
mDevice->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
info.play.precision = 16;
@@ -218,48 +194,48 @@ ALCboolean SolarisBackend_reset(SolarisBackend *self)
break;
}
frameSize = numChannels * device->bytesFromFmt();
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
ALsizei frameSize{numChannels * mDevice->bytesFromFmt()};
info.play.buffer_size = mDevice->UpdateSize*mDevice->NumUpdates * frameSize;
if(ioctl(self->mFd, AUDIO_SETINFO, &info) < 0)
if(ioctl(mFd, AUDIO_SETINFO, &info) < 0)
{
ERR("ioctl failed: %s\n", strerror(errno));
return ALC_FALSE;
}
if(device->channelsFromFmt() != (ALsizei)info.play.channels)
if(mDevice->channelsFromFmt() != (ALsizei)info.play.channels)
{
ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans),
ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
info.play.channels);
return ALC_FALSE;
}
if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && device->FmtType == DevFmtUByte) ||
(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtByte) ||
(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtShort) ||
(info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtInt)))
if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && mDevice->FmtType == DevFmtUByte) ||
(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtByte) ||
(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtShort) ||
(info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtInt)))
{
ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(device->FmtType),
ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(mDevice->FmtType),
info.play.precision, info.play.encoding);
return ALC_FALSE;
}
device->Frequency = info.play.sample_rate;
device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
mDevice->Frequency = info.play.sample_rate;
mDevice->UpdateSize = (info.play.buffer_size/mDevice->NumUpdates) + 1;
SetDefaultChannelOrder(device);
SetDefaultChannelOrder(mDevice);
self->mBuffer.resize(device->UpdateSize * device->frameSizeFromFmt());
std::fill(self->mBuffer.begin(), self->mBuffer.end(), 0);
mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
std::fill(mBuffer.begin(), mBuffer.end(), 0);
return ALC_TRUE;
}
ALCboolean SolarisBackend_start(SolarisBackend *self)
ALCboolean SolarisBackend::start()
{
try {
self->mKillNow.store(false, std::memory_order_release);
self->mThread = std::thread{std::mem_fn(&SolarisBackend::mixerProc), self};
mKillNow.store(false, std::memory_order_release);
mThread = std::thread{std::mem_fn(&SolarisBackend::mixerProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -270,14 +246,13 @@ ALCboolean SolarisBackend_start(SolarisBackend *self)
return ALC_FALSE;
}
void SolarisBackend_stop(SolarisBackend *self)
void SolarisBackend::stop()
{
if(self->mKillNow.exchange(true, std::memory_order_acq_rel) || !self->mThread.joinable())
if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
return;
mThread.join();
self->mThread.join();
if(ioctl(self->mFd, AUDIO_DRAIN) < 0)
if(ioctl(mFd, AUDIO_DRAIN) < 0)
ERR("Error draining device: %s\n", strerror(errno));
}
@@ -317,14 +292,9 @@ void SolarisBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *SolarisBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *SolarisBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
SolarisBackend *backend;
NEW_OBJ(backend, SolarisBackend)(device);
return backend;
}
return new SolarisBackend{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+103 -159
View File
@@ -494,19 +494,25 @@ DWORD WasapiProxy::messageHandler(void *ptr)
}
struct WasapiPlayback final : public ALCbackend, WasapiProxy {
WasapiPlayback(ALCdevice *device) noexcept : ALCbackend{device} { }
struct WasapiPlayback final : public BackendBase, WasapiProxy {
WasapiPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~WasapiPlayback() override;
int mixerProc();
ALCenum open(const ALCchar *name) override;
HRESULT openProxy() override;
void closeProxy() override;
ALCboolean reset() override;
HRESULT resetProxy() override;
ALCboolean start() override;
HRESULT startProxy() override;
void stop() override;
void stopProxy() override;
ClockLatency getClockLatency() override;
std::wstring mDevId;
IMMDevice *mMMDev{nullptr};
@@ -522,32 +528,9 @@ struct WasapiPlayback final : public ALCbackend, WasapiProxy {
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "WasapiPlayback::"; }
DEF_NEWDEL(WasapiPlayback)
};
void WasapiPlayback_Construct(WasapiPlayback *self, ALCdevice *device);
void WasapiPlayback_Destruct(WasapiPlayback *self);
ALCenum WasapiPlayback_open(WasapiPlayback *self, const ALCchar *name);
ALCboolean WasapiPlayback_reset(WasapiPlayback *self);
ALCboolean WasapiPlayback_start(WasapiPlayback *self);
void WasapiPlayback_stop(WasapiPlayback *self);
DECLARE_FORWARD2(WasapiPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
DECLARE_FORWARD(WasapiPlayback, ALCbackend, ALCuint, availableSamples)
ClockLatency WasapiPlayback_getClockLatency(WasapiPlayback *self);
DECLARE_FORWARD(WasapiPlayback, ALCbackend, void, lock)
DECLARE_FORWARD(WasapiPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(WasapiPlayback)
DEFINE_ALCBACKEND_VTABLE(WasapiPlayback);
void WasapiPlayback_Construct(WasapiPlayback *self, ALCdevice *device)
{
new (self) WasapiPlayback{device};
SET_VTABLE2(WasapiPlayback, ALCbackend, self);
}
void WasapiPlayback_Destruct(WasapiPlayback *self)
{ self->~WasapiPlayback(); }
WasapiPlayback::~WasapiPlayback()
{
if(mMsgEvent)
@@ -576,9 +559,9 @@ FORCE_ALIGN int WasapiPlayback::mixerProc()
if(FAILED(hr))
{
ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
WasapiPlayback_lock(this);
lock();
aluHandleDisconnect(mDevice, "COM init failed: 0x%08lx", hr);
WasapiPlayback_unlock(this);
unlock();
return 1;
}
@@ -594,9 +577,9 @@ FORCE_ALIGN int WasapiPlayback::mixerProc()
if(FAILED(hr))
{
ERR("Failed to get padding: 0x%08lx\n", hr);
WasapiPlayback_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failed to retrieve buffer padding: 0x%08lx", hr);
WasapiPlayback_unlock(this);
unlock();
break;
}
mPadding.store(written, std::memory_order_relaxed);
@@ -615,18 +598,18 @@ FORCE_ALIGN int WasapiPlayback::mixerProc()
hr = mRender->GetBuffer(len, &buffer);
if(SUCCEEDED(hr))
{
WasapiPlayback_lock(this);
lock();
aluMixData(mDevice, buffer, len);
mPadding.store(written + len, std::memory_order_relaxed);
WasapiPlayback_unlock(this);
unlock();
hr = mRender->ReleaseBuffer(len, 0);
}
if(FAILED(hr))
{
ERR("Failed to buffer data: 0x%08lx\n", hr);
WasapiPlayback_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failed to send playback samples: 0x%08lx", hr);
WasapiPlayback_unlock(this);
unlock();
break;
}
}
@@ -676,13 +659,13 @@ ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
return ALC_TRUE;
}
ALCenum WasapiPlayback_open(WasapiPlayback *self, const ALCchar *deviceName)
ALCenum WasapiPlayback::open(const ALCchar *name)
{
HRESULT hr = S_OK;
HRESULT hr{S_OK};
self->mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
self->mMsgEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if(self->mNotifyEvent == nullptr || self->mMsgEvent == nullptr)
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
mMsgEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if(mNotifyEvent == nullptr || mMsgEvent == nullptr)
{
ERR("Failed to create message events: %lu\n", GetLastError());
hr = E_FAIL;
@@ -690,35 +673,34 @@ ALCenum WasapiPlayback_open(WasapiPlayback *self, const ALCchar *deviceName)
if(SUCCEEDED(hr))
{
if(deviceName)
if(name)
{
if(PlaybackDevices.empty())
{
ThreadRequest req = { self->mMsgEvent, 0 };
ThreadRequest req = { mMsgEvent, 0 };
if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
(void)WaitForResponse(&req);
}
hr = E_FAIL;
auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
[deviceName](const DevMap &entry) -> bool
{ return entry.name == deviceName || entry.endpoint_guid == deviceName; }
[name](const DevMap &entry) -> bool
{ return entry.name == name || entry.endpoint_guid == name; }
);
if(iter == PlaybackDevices.cend())
{
std::wstring wname{utf8_to_wstr(deviceName)};
std::wstring wname{utf8_to_wstr(name)};
iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
[&wname](const DevMap &entry) -> bool
{ return entry.devid == wname; }
);
}
if(iter == PlaybackDevices.cend())
WARN("Failed to find device name matching \"%s\"\n", deviceName);
WARN("Failed to find device name matching \"%s\"\n", name);
else
{
ALCdevice *device{self->mDevice};
self->mDevId = iter->devid;
device->DeviceName = iter->name;
mDevId = iter->devid;
mDevice->DeviceName = iter->name;
hr = S_OK;
}
}
@@ -726,8 +708,8 @@ ALCenum WasapiPlayback_open(WasapiPlayback *self, const ALCchar *deviceName)
if(SUCCEEDED(hr))
{
ThreadRequest req{ self->mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(self);
ThreadRequest req{ mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(this);
hr = E_FAIL;
if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)proxy))
@@ -738,14 +720,14 @@ ALCenum WasapiPlayback_open(WasapiPlayback *self, const ALCchar *deviceName)
if(FAILED(hr))
{
if(self->mNotifyEvent != nullptr)
CloseHandle(self->mNotifyEvent);
self->mNotifyEvent = nullptr;
if(self->mMsgEvent != nullptr)
CloseHandle(self->mMsgEvent);
self->mMsgEvent = nullptr;
if(mNotifyEvent != nullptr)
CloseHandle(mNotifyEvent);
mNotifyEvent = nullptr;
if(mMsgEvent != nullptr)
CloseHandle(mMsgEvent);
mMsgEvent = nullptr;
self->mDevId.clear();
mDevId.clear();
ERR("Device init failed: 0x%08lx\n", hr);
return ALC_INVALID_VALUE;
@@ -798,12 +780,11 @@ void WasapiPlayback::closeProxy()
}
ALCboolean WasapiPlayback_reset(WasapiPlayback *self)
ALCboolean WasapiPlayback::reset()
{
ThreadRequest req{ self->mMsgEvent, 0 };
ThreadRequest req{ mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(this);
HRESULT hr{E_FAIL};
auto proxy = static_cast<WasapiProxy*>(self);
if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)proxy))
hr = WaitForResponse(&req);
@@ -1066,12 +1047,11 @@ HRESULT WasapiPlayback::resetProxy()
}
ALCboolean WasapiPlayback_start(WasapiPlayback *self)
ALCboolean WasapiPlayback::start()
{
ThreadRequest req{ self->mMsgEvent, 0 };
ThreadRequest req{ mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(this);
HRESULT hr{E_FAIL};
auto proxy = static_cast<WasapiProxy*>(self);
if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)proxy))
hr = WaitForResponse(&req);
@@ -1113,10 +1093,10 @@ HRESULT WasapiPlayback::startProxy()
}
void WasapiPlayback_stop(WasapiPlayback *self)
void WasapiPlayback::stop()
{
ThreadRequest req{ self->mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(self);
ThreadRequest req{ mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(this);
if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)proxy))
(void)WaitForResponse(&req);
}
@@ -1135,34 +1115,39 @@ void WasapiPlayback::stopProxy()
}
ClockLatency WasapiPlayback_getClockLatency(WasapiPlayback *self)
ClockLatency WasapiPlayback::getClockLatency()
{
ClockLatency ret;
WasapiPlayback_lock(self);
ALCdevice *device{self->mDevice};
ret.ClockTime = GetDeviceClockTime(device);
ret.Latency = std::chrono::seconds{self->mPadding.load(std::memory_order_relaxed)};
ret.Latency /= device->Frequency;
WasapiPlayback_unlock(self);
lock();
ret.ClockTime = GetDeviceClockTime(mDevice);
ret.Latency = std::chrono::seconds{mPadding.load(std::memory_order_relaxed)};
ret.Latency /= mDevice->Frequency;
unlock();
return ret;
}
struct WasapiCapture final : public ALCbackend, WasapiProxy {
WasapiCapture(ALCdevice *device) noexcept : ALCbackend{device} { }
struct WasapiCapture final : public BackendBase, WasapiProxy {
WasapiCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~WasapiCapture() override;
int recordProc();
ALCenum open(const ALCchar *name) override;
HRESULT openProxy() override;
void closeProxy() override;
HRESULT resetProxy() override;
ALCboolean start() override;
HRESULT startProxy() override;
void stop() override;
void stopProxy() override;
ALCenum captureSamples(void *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
std::wstring mDevId;
IMMDevice *mMMDev{nullptr};
@@ -1180,33 +1165,9 @@ struct WasapiCapture final : public ALCbackend, WasapiProxy {
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "WasapiCapture::"; }
DEF_NEWDEL(WasapiCapture)
};
void WasapiCapture_Construct(WasapiCapture *self, ALCdevice *device);
void WasapiCapture_Destruct(WasapiCapture *self);
ALCenum WasapiCapture_open(WasapiCapture *self, const ALCchar *name);
DECLARE_FORWARD(WasapiCapture, ALCbackend, ALCboolean, reset)
ALCboolean WasapiCapture_start(WasapiCapture *self);
void WasapiCapture_stop(WasapiCapture *self);
ALCenum WasapiCapture_captureSamples(WasapiCapture *self, ALCvoid *buffer, ALCuint samples);
ALuint WasapiCapture_availableSamples(WasapiCapture *self);
DECLARE_FORWARD(WasapiCapture, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(WasapiCapture, ALCbackend, void, lock)
DECLARE_FORWARD(WasapiCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(WasapiCapture)
DEFINE_ALCBACKEND_VTABLE(WasapiCapture);
void WasapiCapture_Construct(WasapiCapture *self, ALCdevice *device)
{
new (self) WasapiCapture{device};
SET_VTABLE2(WasapiCapture, ALCbackend, self);
}
void WasapiCapture_Destruct(WasapiCapture *self)
{ self->~WasapiCapture(); }
WasapiCapture::~WasapiCapture()
{
if(mMsgEvent)
@@ -1232,9 +1193,9 @@ FORCE_ALIGN int WasapiCapture::recordProc()
if(FAILED(hr))
{
ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
WasapiCapture_lock(this);
lock();
aluHandleDisconnect(mDevice, "COM init failed: 0x%08lx", hr);
WasapiCapture_unlock(this);
unlock();
return 1;
}
@@ -1306,9 +1267,9 @@ FORCE_ALIGN int WasapiCapture::recordProc()
if(FAILED(hr))
{
WasapiCapture_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failed to capture samples: 0x%08lx", hr);
WasapiCapture_unlock(this);
unlock();
break;
}
@@ -1322,13 +1283,13 @@ FORCE_ALIGN int WasapiCapture::recordProc()
}
ALCenum WasapiCapture_open(WasapiCapture *self, const ALCchar *deviceName)
ALCenum WasapiCapture::open(const ALCchar *name)
{
HRESULT hr{S_OK};
self->mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
self->mMsgEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if(self->mNotifyEvent == nullptr || self->mMsgEvent == nullptr)
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
mMsgEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if(mNotifyEvent == nullptr || mMsgEvent == nullptr)
{
ERR("Failed to create message events: %lu\n", GetLastError());
hr = E_FAIL;
@@ -1336,35 +1297,34 @@ ALCenum WasapiCapture_open(WasapiCapture *self, const ALCchar *deviceName)
if(SUCCEEDED(hr))
{
if(deviceName)
if(name)
{
if(CaptureDevices.empty())
{
ThreadRequest req{ self->mMsgEvent, 0 };
ThreadRequest req{ mMsgEvent, 0 };
if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, CAPTURE_DEVICE_PROBE))
(void)WaitForResponse(&req);
}
hr = E_FAIL;
auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
[deviceName](const DevMap &entry) -> bool
{ return entry.name == deviceName || entry.endpoint_guid == deviceName; }
[name](const DevMap &entry) -> bool
{ return entry.name == name || entry.endpoint_guid == name; }
);
if(iter == CaptureDevices.cend())
{
std::wstring wname{utf8_to_wstr(deviceName)};
std::wstring wname{utf8_to_wstr(name)};
iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
[&wname](const DevMap &entry) -> bool
{ return entry.devid == wname; }
);
}
if(iter == CaptureDevices.cend())
WARN("Failed to find device name matching \"%s\"\n", deviceName);
WARN("Failed to find device name matching \"%s\"\n", name);
else
{
ALCdevice *device{self->mDevice};
self->mDevId = iter->devid;
device->DeviceName = iter->name;
mDevId = iter->devid;
mDevice->DeviceName = iter->name;
hr = S_OK;
}
}
@@ -1372,10 +1332,9 @@ ALCenum WasapiCapture_open(WasapiCapture *self, const ALCchar *deviceName)
if(SUCCEEDED(hr))
{
ThreadRequest req{ self->mMsgEvent, 0 };
ThreadRequest req{ mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(this);
hr = E_FAIL;
auto proxy = static_cast<WasapiProxy*>(self);
if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)proxy))
hr = WaitForResponse(&req);
else
@@ -1384,24 +1343,23 @@ ALCenum WasapiCapture_open(WasapiCapture *self, const ALCchar *deviceName)
if(FAILED(hr))
{
if(self->mNotifyEvent != nullptr)
CloseHandle(self->mNotifyEvent);
self->mNotifyEvent = nullptr;
if(self->mMsgEvent != nullptr)
CloseHandle(self->mMsgEvent);
self->mMsgEvent = nullptr;
if(mNotifyEvent != nullptr)
CloseHandle(mNotifyEvent);
mNotifyEvent = nullptr;
if(mMsgEvent != nullptr)
CloseHandle(mMsgEvent);
mMsgEvent = nullptr;
self->mDevId.clear();
mDevId.clear();
ERR("Device init failed: 0x%08lx\n", hr);
return ALC_INVALID_VALUE;
}
else
{
ThreadRequest req{ self->mMsgEvent, 0 };
ThreadRequest req{ mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(this);
hr = E_FAIL;
auto proxy = static_cast<WasapiProxy*>(self);
if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)proxy))
hr = WaitForResponse(&req);
else
@@ -1694,12 +1652,11 @@ HRESULT WasapiCapture::resetProxy()
}
ALCboolean WasapiCapture_start(WasapiCapture *self)
ALCboolean WasapiCapture::start()
{
ThreadRequest req{ self->mMsgEvent, 0 };
ThreadRequest req{ mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(this);
HRESULT hr{E_FAIL};
auto proxy = static_cast<WasapiProxy*>(self);
if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)proxy))
hr = WaitForResponse(&req);
@@ -1744,10 +1701,10 @@ HRESULT WasapiCapture::startProxy()
}
void WasapiCapture_stop(WasapiCapture *self)
void WasapiCapture::stop()
{
ThreadRequest req{ self->mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(self);
ThreadRequest req{ mMsgEvent, 0 };
auto proxy = static_cast<WasapiProxy*>(this);
if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)proxy))
(void)WaitForResponse(&req);
}
@@ -1767,16 +1724,12 @@ void WasapiCapture::stopProxy()
}
ALuint WasapiCapture_availableSamples(WasapiCapture *self)
{
RingBuffer *ring{self->mRing.get()};
return (ALuint)ring->readSpace();
}
ALCuint WasapiCapture::availableSamples()
{ return (ALCuint)mRing->readSpace(); }
ALCenum WasapiCapture_captureSamples(WasapiCapture *self, ALCvoid *buffer, ALCuint samples)
ALCenum WasapiCapture::captureSamples(void *buffer, ALCuint samples)
{
RingBuffer *ring{self->mRing.get()};
ring->read(buffer, samples);
mRing->read(buffer, samples);
return ALC_NO_ERROR;
}
@@ -1857,21 +1810,12 @@ void WasapiBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *WasapiBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *WasapiBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
WasapiPlayback *backend;
NEW_OBJ(backend, WasapiPlayback)(device);
return backend;
}
return new WasapiPlayback{device};
if(type == ALCbackend_Capture)
{
WasapiCapture *backend;
NEW_OBJ(backend, WasapiCapture)(device);
return backend;
}
return new WasapiCapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+69 -95
View File
@@ -78,12 +78,17 @@ void fwrite32le(ALuint val, FILE *f)
}
struct WaveBackend final : public ALCbackend {
WaveBackend(ALCdevice *device) noexcept : ALCbackend{device} { }
struct WaveBackend final : public BackendBase {
WaveBackend(ALCdevice *device) noexcept : BackendBase{device} { }
~WaveBackend() override;
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
FILE *mFile{nullptr};
long mDataStart{-1};
@@ -93,32 +98,9 @@ struct WaveBackend final : public ALCbackend {
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "WaveBackend::"; }
DEF_NEWDEL(WaveBackend)
};
void WaveBackend_Construct(WaveBackend *self, ALCdevice *device);
void WaveBackend_Destruct(WaveBackend *self);
ALCenum WaveBackend_open(WaveBackend *self, const ALCchar *name);
ALCboolean WaveBackend_reset(WaveBackend *self);
ALCboolean WaveBackend_start(WaveBackend *self);
void WaveBackend_stop(WaveBackend *self);
DECLARE_FORWARD2(WaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
DECLARE_FORWARD(WaveBackend, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(WaveBackend, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(WaveBackend, ALCbackend, void, lock)
DECLARE_FORWARD(WaveBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(WaveBackend)
DEFINE_ALCBACKEND_VTABLE(WaveBackend);
void WaveBackend_Construct(WaveBackend *self, ALCdevice *device)
{
new (self) WaveBackend{device};
SET_VTABLE2(WaveBackend, ALCbackend, self);
}
void WaveBackend_Destruct(WaveBackend *self)
{ self->~WaveBackend(); }
WaveBackend::~WaveBackend()
{
if(mFile)
@@ -151,9 +133,9 @@ int WaveBackend::mixerProc()
}
while(avail-done >= mDevice->UpdateSize)
{
WaveBackend_lock(this);
lock();
aluMixData(mDevice, mBuffer.data(), mDevice->UpdateSize);
WaveBackend_unlock(this);
unlock();
done += mDevice->UpdateSize;
if(!IS_LITTLE_ENDIAN)
@@ -189,9 +171,9 @@ int WaveBackend::mixerProc()
if(ferror(mFile))
{
ERR("Error writing to file\n");
WaveBackend_lock(this);
lock();
aluHandleDisconnect(mDevice, "Failed to write playback samples");
WaveBackend_unlock(this);
unlock();
break;
}
}
@@ -212,8 +194,7 @@ int WaveBackend::mixerProc()
return 0;
}
ALCenum WaveBackend_open(WaveBackend *self, const ALCchar *name)
ALCenum WaveBackend::open(const ALCchar *name)
{
const char *fname{GetConfigValue(nullptr, "wave", "file", "")};
if(!fname[0]) return ALC_INVALID_VALUE;
@@ -226,49 +207,47 @@ ALCenum WaveBackend_open(WaveBackend *self, const ALCchar *name)
#ifdef _WIN32
{
std::wstring wname = utf8_to_wstr(fname);
self->mFile = _wfopen(wname.c_str(), L"wb");
mFile = _wfopen(wname.c_str(), L"wb");
}
#else
self->mFile = fopen(fname, "wb");
mFile = fopen(fname, "wb");
#endif
if(!self->mFile)
if(!mFile)
{
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
return ALC_INVALID_VALUE;
}
ALCdevice *device{self->mDevice};
device->DeviceName = name;
mDevice->DeviceName = name;
return ALC_NO_ERROR;
}
ALCboolean WaveBackend_reset(WaveBackend *self)
ALCboolean WaveBackend::reset()
{
ALCdevice *device{self->mDevice};
ALuint channels=0, bits=0, chanmask=0;
int isbformat = 0;
size_t val;
fseek(self->mFile, 0, SEEK_SET);
clearerr(self->mFile);
fseek(mFile, 0, SEEK_SET);
clearerr(mFile);
if(GetConfigValueBool(nullptr, "wave", "bformat", 0))
{
device->FmtChans = DevFmtAmbi3D;
device->mAmbiOrder = 1;
mDevice->FmtChans = DevFmtAmbi3D;
mDevice->mAmbiOrder = 1;
}
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtByte:
device->FmtType = DevFmtUByte;
mDevice->FmtType = DevFmtUByte;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
mDevice->FmtType = DevFmtShort;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
mDevice->FmtType = DevFmtInt;
break;
case DevFmtUByte:
case DevFmtShort:
@@ -276,7 +255,7 @@ ALCboolean WaveBackend_reset(WaveBackend *self)
case DevFmtFloat:
break;
}
switch(device->FmtChans)
switch(mDevice->FmtChans)
{
case DevFmtMono: chanmask = 0x04; break;
case DevFmtStereo: chanmask = 0x01 | 0x02; break;
@@ -287,71 +266,71 @@ ALCboolean WaveBackend_reset(WaveBackend *self)
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
case DevFmtAmbi3D:
/* .amb output requires FuMa */
device->mAmbiOrder = mini(device->mAmbiOrder, 3);
device->mAmbiLayout = AmbiLayout::FuMa;
device->mAmbiScale = AmbiNorm::FuMa;
mDevice->mAmbiOrder = mini(mDevice->mAmbiOrder, 3);
mDevice->mAmbiLayout = AmbiLayout::FuMa;
mDevice->mAmbiScale = AmbiNorm::FuMa;
isbformat = 1;
chanmask = 0;
break;
}
bits = device->bytesFromFmt() * 8;
channels = device->channelsFromFmt();
bits = mDevice->bytesFromFmt() * 8;
channels = mDevice->channelsFromFmt();
fputs("RIFF", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
fputs("RIFF", mFile);
fwrite32le(0xFFFFFFFF, mFile); // 'RIFF' header len; filled in at close
fputs("WAVE", self->mFile);
fputs("WAVE", mFile);
fputs("fmt ", self->mFile);
fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
fputs("fmt ", mFile);
fwrite32le(40, mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
// 16-bit val, format type id (extensible: 0xFFFE)
fwrite16le(0xFFFE, self->mFile);
fwrite16le(0xFFFE, mFile);
// 16-bit val, channel count
fwrite16le(channels, self->mFile);
fwrite16le(channels, mFile);
// 32-bit val, frequency
fwrite32le(device->Frequency, self->mFile);
fwrite32le(mDevice->Frequency, mFile);
// 32-bit val, bytes per second
fwrite32le(device->Frequency * channels * bits / 8, self->mFile);
fwrite32le(mDevice->Frequency * channels * bits / 8, mFile);
// 16-bit val, frame size
fwrite16le(channels * bits / 8, self->mFile);
fwrite16le(channels * bits / 8, mFile);
// 16-bit val, bits per sample
fwrite16le(bits, self->mFile);
fwrite16le(bits, mFile);
// 16-bit val, extra byte count
fwrite16le(22, self->mFile);
fwrite16le(22, mFile);
// 16-bit val, valid bits per sample
fwrite16le(bits, self->mFile);
fwrite16le(bits, mFile);
// 32-bit val, channel mask
fwrite32le(chanmask, self->mFile);
fwrite32le(chanmask, mFile);
// 16 byte GUID, sub-type format
val = fwrite((device->FmtType == DevFmtFloat) ?
val = fwrite((mDevice->FmtType == DevFmtFloat) ?
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, mFile);
(void)val;
fputs("data", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
fputs("data", mFile);
fwrite32le(0xFFFFFFFF, mFile); // 'data' header len; filled in at close
if(ferror(self->mFile))
if(ferror(mFile))
{
ERR("Error writing header: %s\n", strerror(errno));
return ALC_FALSE;
}
self->mDataStart = ftell(self->mFile);
mDataStart = ftell(mFile);
SetDefaultWFXChannelOrder(device);
SetDefaultWFXChannelOrder(mDevice);
const ALuint bufsize{device->frameSizeFromFmt() * device->UpdateSize};
self->mBuffer.resize(bufsize);
const ALuint bufsize{mDevice->frameSizeFromFmt() * mDevice->UpdateSize};
mBuffer.resize(bufsize);
return ALC_TRUE;
}
ALCboolean WaveBackend_start(WaveBackend *self)
ALCboolean WaveBackend::start()
{
try {
self->mKillNow.store(AL_FALSE, std::memory_order_release);
self->mThread = std::thread{std::mem_fn(&WaveBackend::mixerProc), self};
mKillNow.store(AL_FALSE, std::memory_order_release);
mThread = std::thread{std::mem_fn(&WaveBackend::mixerProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -362,20 +341,20 @@ ALCboolean WaveBackend_start(WaveBackend *self)
return ALC_FALSE;
}
void WaveBackend_stop(WaveBackend *self)
void WaveBackend::stop()
{
if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !self->mThread.joinable())
if(mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !mThread.joinable())
return;
self->mThread.join();
mThread.join();
long size{ftell(self->mFile)};
long size{ftell(mFile)};
if(size > 0)
{
long dataLen{size - self->mDataStart};
if(fseek(self->mFile, self->mDataStart-4, SEEK_SET) == 0)
fwrite32le(dataLen, self->mFile); // 'data' header len
if(fseek(self->mFile, 4, SEEK_SET) == 0)
fwrite32le(size-8, self->mFile); // 'WAVE' header len
long dataLen{size - mDataStart};
if(fseek(mFile, mDataStart-4, SEEK_SET) == 0)
fwrite32le(dataLen, mFile); // 'data' header len
if(fseek(mFile, 4, SEEK_SET) == 0)
fwrite32le(size-8, mFile); // 'WAVE' header len
}
}
@@ -401,15 +380,10 @@ void WaveBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *WaveBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *WaveBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
WaveBackend *backend;
NEW_OBJ(backend, WaveBackend)(device);
return backend;
}
return new WaveBackend{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
+151 -214
View File
@@ -120,8 +120,8 @@ void ProbeCaptureDevices(void)
}
struct WinMMPlayback final : public ALCbackend {
WinMMPlayback(ALCdevice *device) noexcept : ALCbackend{device} { }
struct WinMMPlayback final : public BackendBase {
WinMMPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~WinMMPlayback() override;
static void CALLBACK waveOutProcC(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
@@ -129,6 +129,11 @@ struct WinMMPlayback final : public ALCbackend {
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
std::atomic<ALuint> mWritable{0u};
al::semaphore mSem;
int mIdx{0};
@@ -142,32 +147,9 @@ struct WinMMPlayback final : public ALCbackend {
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "WinMMPlayback::"; }
DEF_NEWDEL(WinMMPlayback)
};
void WinMMPlayback_Construct(WinMMPlayback *self, ALCdevice *device);
void WinMMPlayback_Destruct(WinMMPlayback *self);
ALCenum WinMMPlayback_open(WinMMPlayback *self, const ALCchar *name);
ALCboolean WinMMPlayback_reset(WinMMPlayback *self);
ALCboolean WinMMPlayback_start(WinMMPlayback *self);
void WinMMPlayback_stop(WinMMPlayback *self);
DECLARE_FORWARD2(WinMMPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
DECLARE_FORWARD(WinMMPlayback, ALCbackend, ALCuint, availableSamples)
DECLARE_FORWARD(WinMMPlayback, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(WinMMPlayback, ALCbackend, void, lock)
DECLARE_FORWARD(WinMMPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(WinMMPlayback)
DEFINE_ALCBACKEND_VTABLE(WinMMPlayback);
void WinMMPlayback_Construct(WinMMPlayback *self, ALCdevice *device)
{
new (self) WinMMPlayback{device};
SET_VTABLE2(WinMMPlayback, ALCbackend, self);
}
void WinMMPlayback_Destruct(WinMMPlayback *self)
{ self->~WinMMPlayback(); }
WinMMPlayback::~WinMMPlayback()
{
if(mOutHdl)
@@ -200,16 +182,16 @@ FORCE_ALIGN int WinMMPlayback::mixerProc()
SetRTPriority();
althrd_setname(MIXER_THREAD_NAME);
WinMMPlayback_lock(this);
lock();
while(!mKillNow.load(std::memory_order_acquire) &&
mDevice->Connected.load(std::memory_order_acquire))
{
ALsizei todo = mWritable.load(std::memory_order_acquire);
if(todo < 1)
{
WinMMPlayback_unlock(this);
unlock();
mSem.wait();
WinMMPlayback_lock(this);
lock();
continue;
}
@@ -224,146 +206,137 @@ FORCE_ALIGN int WinMMPlayback::mixerProc()
} while(--todo);
mIdx = widx;
}
WinMMPlayback_unlock(this);
unlock();
return 0;
}
ALCenum WinMMPlayback_open(WinMMPlayback *self, const ALCchar *deviceName)
ALCenum WinMMPlayback::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
if(PlaybackDevices.empty())
ProbePlaybackDevices();
// Find the Device ID matching the deviceName if valid
auto iter = deviceName ?
std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), deviceName) :
auto iter = name ?
std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) :
PlaybackDevices.cbegin();
if(iter == PlaybackDevices.cend()) return ALC_INVALID_VALUE;
auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
retry_open:
self->mFormat = WAVEFORMATEX{};
if(device->FmtType == DevFmtFloat)
mFormat = WAVEFORMATEX{};
if(mDevice->FmtType == DevFmtFloat)
{
self->mFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
self->mFormat.wBitsPerSample = 32;
mFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
mFormat.wBitsPerSample = 32;
}
else
{
self->mFormat.wFormatTag = WAVE_FORMAT_PCM;
if(device->FmtType == DevFmtUByte || device->FmtType == DevFmtByte)
self->mFormat.wBitsPerSample = 8;
mFormat.wFormatTag = WAVE_FORMAT_PCM;
if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtByte)
mFormat.wBitsPerSample = 8;
else
self->mFormat.wBitsPerSample = 16;
mFormat.wBitsPerSample = 16;
}
self->mFormat.nChannels = ((device->FmtChans == DevFmtMono) ? 1 : 2);
self->mFormat.nBlockAlign = self->mFormat.wBitsPerSample *
self->mFormat.nChannels / 8;
self->mFormat.nSamplesPerSec = device->Frequency;
self->mFormat.nAvgBytesPerSec = self->mFormat.nSamplesPerSec *
self->mFormat.nBlockAlign;
self->mFormat.cbSize = 0;
mFormat.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
mFormat.nSamplesPerSec = mDevice->Frequency;
mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
mFormat.cbSize = 0;
MMRESULT res{waveOutOpen(&self->mOutHdl, DeviceID, &self->mFormat,
(DWORD_PTR)&WinMMPlayback::waveOutProcC, (DWORD_PTR)self, CALLBACK_FUNCTION)};
MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMPlayback::waveOutProcC,
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
if(res != MMSYSERR_NOERROR)
{
if(device->FmtType == DevFmtFloat)
if(mDevice->FmtType == DevFmtFloat)
{
device->FmtType = DevFmtShort;
mDevice->FmtType = DevFmtShort;
goto retry_open;
}
ERR("waveOutOpen failed: %u\n", res);
return ALC_INVALID_VALUE;
}
device->DeviceName = PlaybackDevices[DeviceID];
mDevice->DeviceName = PlaybackDevices[DeviceID];
return ALC_NO_ERROR;
}
ALCboolean WinMMPlayback_reset(WinMMPlayback *self)
ALCboolean WinMMPlayback::reset()
{
ALCdevice *device{self->mDevice};
mDevice->UpdateSize = static_cast<ALuint>(
(ALuint64)mDevice->UpdateSize * mFormat.nSamplesPerSec / mDevice->Frequency);
mDevice->UpdateSize = (mDevice->UpdateSize*mDevice->NumUpdates + 3) / 4;
mDevice->NumUpdates = 4;
mDevice->Frequency = mFormat.nSamplesPerSec;
device->UpdateSize = static_cast<ALuint>(
(ALuint64)device->UpdateSize * self->mFormat.nSamplesPerSec / device->Frequency);
device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
device->NumUpdates = 4;
device->Frequency = self->mFormat.nSamplesPerSec;
if(self->mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
if(self->mFormat.wBitsPerSample == 32)
device->FmtType = DevFmtFloat;
if(mFormat.wBitsPerSample == 32)
mDevice->FmtType = DevFmtFloat;
else
{
ERR("Unhandled IEEE float sample depth: %d\n", self->mFormat.wBitsPerSample);
ERR("Unhandled IEEE float sample depth: %d\n", mFormat.wBitsPerSample);
return ALC_FALSE;
}
}
else if(self->mFormat.wFormatTag == WAVE_FORMAT_PCM)
else if(mFormat.wFormatTag == WAVE_FORMAT_PCM)
{
if(self->mFormat.wBitsPerSample == 16)
device->FmtType = DevFmtShort;
else if(self->mFormat.wBitsPerSample == 8)
device->FmtType = DevFmtUByte;
if(mFormat.wBitsPerSample == 16)
mDevice->FmtType = DevFmtShort;
else if(mFormat.wBitsPerSample == 8)
mDevice->FmtType = DevFmtUByte;
else
{
ERR("Unhandled PCM sample depth: %d\n", self->mFormat.wBitsPerSample);
ERR("Unhandled PCM sample depth: %d\n", mFormat.wBitsPerSample);
return ALC_FALSE;
}
}
else
{
ERR("Unhandled format tag: 0x%04x\n", self->mFormat.wFormatTag);
ERR("Unhandled format tag: 0x%04x\n", mFormat.wFormatTag);
return ALC_FALSE;
}
if(self->mFormat.nChannels == 2)
device->FmtChans = DevFmtStereo;
else if(self->mFormat.nChannels == 1)
device->FmtChans = DevFmtMono;
if(mFormat.nChannels == 2)
mDevice->FmtChans = DevFmtStereo;
else if(mFormat.nChannels == 1)
mDevice->FmtChans = DevFmtMono;
else
{
ERR("Unhandled channel count: %d\n", self->mFormat.nChannels);
ERR("Unhandled channel count: %d\n", mFormat.nChannels);
return ALC_FALSE;
}
SetDefaultWFXChannelOrder(device);
SetDefaultWFXChannelOrder(mDevice);
ALuint BufferSize{device->UpdateSize * device->frameSizeFromFmt()};
ALuint BufferSize{mDevice->UpdateSize * mDevice->frameSizeFromFmt()};
al_free(self->mWaveBuffer[0].lpData);
self->mWaveBuffer[0] = WAVEHDR{};
self->mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16,
BufferSize * self->mWaveBuffer.size()));
self->mWaveBuffer[0].dwBufferLength = BufferSize;
for(size_t i{1};i < self->mWaveBuffer.size();i++)
al_free(mWaveBuffer[0].lpData);
mWaveBuffer[0] = WAVEHDR{};
mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
mWaveBuffer[0].dwBufferLength = BufferSize;
for(size_t i{1};i < mWaveBuffer.size();i++)
{
self->mWaveBuffer[i] = WAVEHDR{};
self->mWaveBuffer[i].lpData = self->mWaveBuffer[i-1].lpData +
self->mWaveBuffer[i-1].dwBufferLength;
self->mWaveBuffer[i].dwBufferLength = BufferSize;
mWaveBuffer[i] = WAVEHDR{};
mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
mWaveBuffer[i].dwBufferLength = BufferSize;
}
self->mIdx = 0;
mIdx = 0;
return ALC_TRUE;
}
ALCboolean WinMMPlayback_start(WinMMPlayback *self)
ALCboolean WinMMPlayback::start()
{
try {
std::for_each(self->mWaveBuffer.begin(), self->mWaveBuffer.end(),
[self](WAVEHDR &waveHdr) -> void
{ waveOutPrepareHeader(self->mOutHdl, &waveHdr, static_cast<UINT>(sizeof(WAVEHDR))); }
std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
[this](WAVEHDR &waveHdr) -> void
{ waveOutPrepareHeader(mOutHdl, &waveHdr, static_cast<UINT>(sizeof(WAVEHDR))); }
);
self->mWritable.store(static_cast<ALuint>(self->mWaveBuffer.size()),
std::memory_order_release);
mWritable.store(static_cast<ALuint>(mWaveBuffer.size()), std::memory_order_release);
self->mKillNow.store(AL_FALSE, std::memory_order_release);
self->mThread = std::thread{std::mem_fn(&WinMMPlayback::mixerProc), self};
mKillNow.store(AL_FALSE, std::memory_order_release);
mThread = std::thread{std::mem_fn(&WinMMPlayback::mixerProc), this};
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -374,24 +347,24 @@ ALCboolean WinMMPlayback_start(WinMMPlayback *self)
return ALC_FALSE;
}
void WinMMPlayback_stop(WinMMPlayback *self)
void WinMMPlayback::stop()
{
if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !self->mThread.joinable())
if(mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !mThread.joinable())
return;
self->mThread.join();
mThread.join();
while(self->mWritable.load(std::memory_order_acquire) < self->mWaveBuffer.size())
self->mSem.wait();
std::for_each(self->mWaveBuffer.begin(), self->mWaveBuffer.end(),
[self](WAVEHDR &waveHdr) -> void
{ waveOutUnprepareHeader(self->mOutHdl, &waveHdr, sizeof(WAVEHDR)); }
while(mWritable.load(std::memory_order_acquire) < mWaveBuffer.size())
mSem.wait();
std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
[this](WAVEHDR &waveHdr) -> void
{ waveOutUnprepareHeader(mOutHdl, &waveHdr, sizeof(WAVEHDR)); }
);
self->mWritable.store(0, std::memory_order_release);
mWritable.store(0, std::memory_order_release);
}
struct WinMMCapture final : public ALCbackend {
WinMMCapture(ALCdevice *device) noexcept : ALCbackend{device} { }
struct WinMMCapture final : public BackendBase {
WinMMCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~WinMMCapture() override;
static void CALLBACK waveInProcC(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
@@ -399,6 +372,12 @@ struct WinMMCapture final : public ALCbackend {
int captureProc();
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(void *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
std::atomic<ALuint> mReadable{0u};
al::semaphore mSem;
int mIdx{0};
@@ -414,32 +393,9 @@ struct WinMMCapture final : public ALCbackend {
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "WinMMCapture::"; }
DEF_NEWDEL(WinMMCapture)
};
void WinMMCapture_Construct(WinMMCapture *self, ALCdevice *device);
void WinMMCapture_Destruct(WinMMCapture *self);
ALCenum WinMMCapture_open(WinMMCapture *self, const ALCchar *deviceName);
DECLARE_FORWARD(WinMMCapture, ALCbackend, ALCboolean, reset)
ALCboolean WinMMCapture_start(WinMMCapture *self);
void WinMMCapture_stop(WinMMCapture *self);
ALCenum WinMMCapture_captureSamples(WinMMCapture *self, ALCvoid *buffer, ALCuint samples);
ALCuint WinMMCapture_availableSamples(WinMMCapture *self);
DECLARE_FORWARD(WinMMCapture, ALCbackend, ClockLatency, getClockLatency)
DECLARE_FORWARD(WinMMCapture, ALCbackend, void, lock)
DECLARE_FORWARD(WinMMCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(WinMMCapture)
DEFINE_ALCBACKEND_VTABLE(WinMMCapture);
void WinMMCapture_Construct(WinMMCapture *self, ALCdevice *device)
{
new (self) WinMMCapture{device};
SET_VTABLE2(WinMMCapture, ALCbackend, self);
}
void WinMMCapture_Destruct(WinMMCapture *self)
{ self->~WinMMCapture(); }
WinMMCapture::~WinMMCapture()
{
// Close the Wave device
@@ -471,16 +427,16 @@ int WinMMCapture::captureProc()
{
althrd_setname(RECORD_THREAD_NAME);
WinMMCapture_lock(this);
lock();
while(!mKillNow.load(std::memory_order_acquire) &&
mDevice->Connected.load(std::memory_order_acquire))
{
ALuint todo{mReadable.load(std::memory_order_acquire)};
if(todo < 1)
{
WinMMCapture_unlock(this);
unlock();
mSem.wait();
WinMMCapture_lock(this);
lock();
continue;
}
@@ -495,27 +451,25 @@ int WinMMCapture::captureProc()
} while(--todo);
mIdx = widx;
}
WinMMCapture_unlock(this);
unlock();
return 0;
}
ALCenum WinMMCapture_open(WinMMCapture *self, const ALCchar *deviceName)
ALCenum WinMMCapture::open(const ALCchar *name)
{
ALCdevice *device{self->mDevice};
if(CaptureDevices.empty())
ProbeCaptureDevices();
// Find the Device ID matching the deviceName if valid
auto iter = deviceName ?
std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), deviceName) :
auto iter = name ?
std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) :
CaptureDevices.cbegin();
if(iter == CaptureDevices.cend()) return ALC_INVALID_VALUE;
auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
switch(device->FmtChans)
switch(mDevice->FmtChans)
{
case DevFmtMono:
case DevFmtStereo:
@@ -530,7 +484,7 @@ ALCenum WinMMCapture_open(WinMMCapture *self, const ALCchar *deviceName)
return ALC_INVALID_ENUM;
}
switch(device->FmtType)
switch(mDevice->FmtType)
{
case DevFmtUByte:
case DevFmtShort:
@@ -544,21 +498,18 @@ ALCenum WinMMCapture_open(WinMMCapture *self, const ALCchar *deviceName)
return ALC_INVALID_ENUM;
}
self->mFormat = WAVEFORMATEX{};
self->mFormat.wFormatTag = (device->FmtType == DevFmtFloat) ?
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
self->mFormat.nChannels = device->channelsFromFmt();
self->mFormat.wBitsPerSample = device->bytesFromFmt() * 8;
self->mFormat.nBlockAlign = self->mFormat.wBitsPerSample *
self->mFormat.nChannels / 8;
self->mFormat.nSamplesPerSec = device->Frequency;
self->mFormat.nAvgBytesPerSec = self->mFormat.nSamplesPerSec *
self->mFormat.nBlockAlign;
self->mFormat.cbSize = 0;
mFormat = WAVEFORMATEX{};
mFormat.wFormatTag = (mDevice->FmtType == DevFmtFloat) ?
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
mFormat.nChannels = mDevice->channelsFromFmt();
mFormat.wBitsPerSample = mDevice->bytesFromFmt() * 8;
mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
mFormat.nSamplesPerSec = mDevice->Frequency;
mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
mFormat.cbSize = 0;
MMRESULT res{waveInOpen(&self->mInHdl, DeviceID, &self->mFormat,
(DWORD_PTR)&WinMMCapture::waveInProcC, (DWORD_PTR)self, CALLBACK_FUNCTION
)};
MMRESULT res{waveInOpen(&mInHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMCapture::waveInProcC,
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
if(res != MMSYSERR_NOERROR)
{
ERR("waveInOpen failed: %u\n", res);
@@ -566,47 +517,46 @@ ALCenum WinMMCapture_open(WinMMCapture *self, const ALCchar *deviceName)
}
// Ensure each buffer is 50ms each
DWORD BufferSize{self->mFormat.nAvgBytesPerSec / 20u};
BufferSize -= (BufferSize % self->mFormat.nBlockAlign);
DWORD BufferSize{mFormat.nAvgBytesPerSec / 20u};
BufferSize -= (BufferSize % mFormat.nBlockAlign);
// Allocate circular memory buffer for the captured audio
// Make sure circular buffer is at least 100ms in size
ALuint CapturedDataSize{device->UpdateSize*device->NumUpdates};
ALuint CapturedDataSize{mDevice->UpdateSize*mDevice->NumUpdates};
CapturedDataSize = static_cast<ALuint>(
std::max<size_t>(CapturedDataSize, BufferSize*self->mWaveBuffer.size()));
std::max<size_t>(CapturedDataSize, BufferSize*mWaveBuffer.size()));
self->mRing = CreateRingBuffer(CapturedDataSize, self->mFormat.nBlockAlign, false);
if(!self->mRing) return ALC_INVALID_VALUE;
mRing = CreateRingBuffer(CapturedDataSize, mFormat.nBlockAlign, false);
if(!mRing) return ALC_INVALID_VALUE;
al_free(self->mWaveBuffer[0].lpData);
self->mWaveBuffer[0] = WAVEHDR{};
self->mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize*4));
self->mWaveBuffer[0].dwBufferLength = BufferSize;
for(size_t i{1};i < self->mWaveBuffer.size();++i)
al_free(mWaveBuffer[0].lpData);
mWaveBuffer[0] = WAVEHDR{};
mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize*4));
mWaveBuffer[0].dwBufferLength = BufferSize;
for(size_t i{1};i < mWaveBuffer.size();++i)
{
self->mWaveBuffer[i] = WAVEHDR{};
self->mWaveBuffer[i].lpData = self->mWaveBuffer[i-1].lpData +
self->mWaveBuffer[i-1].dwBufferLength;
self->mWaveBuffer[i].dwBufferLength = self->mWaveBuffer[i-1].dwBufferLength;
mWaveBuffer[i] = WAVEHDR{};
mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength;
}
device->DeviceName = CaptureDevices[DeviceID];
mDevice->DeviceName = CaptureDevices[DeviceID];
return ALC_NO_ERROR;
}
ALCboolean WinMMCapture_start(WinMMCapture *self)
ALCboolean WinMMCapture::start()
{
try {
for(size_t i{0};i < self->mWaveBuffer.size();++i)
for(size_t i{0};i < mWaveBuffer.size();++i)
{
waveInPrepareHeader(self->mInHdl, &self->mWaveBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(self->mInHdl, &self->mWaveBuffer[i], sizeof(WAVEHDR));
waveInPrepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
}
self->mKillNow.store(AL_FALSE, std::memory_order_release);
self->mThread = std::thread{std::mem_fn(&WinMMCapture::captureProc), self};
mKillNow.store(AL_FALSE, std::memory_order_release);
mThread = std::thread{std::mem_fn(&WinMMCapture::captureProc), this};
waveInStart(self->mInHdl);
waveInStart(mInHdl);
return ALC_TRUE;
}
catch(std::exception& e) {
@@ -617,37 +567,33 @@ ALCboolean WinMMCapture_start(WinMMCapture *self)
return ALC_FALSE;
}
void WinMMCapture_stop(WinMMCapture *self)
void WinMMCapture::stop()
{
waveInStop(self->mInHdl);
waveInStop(mInHdl);
self->mKillNow.store(AL_TRUE, std::memory_order_release);
if(self->mThread.joinable())
mKillNow.store(AL_TRUE, std::memory_order_release);
if(mThread.joinable())
{
self->mSem.post();
self->mThread.join();
mSem.post();
mThread.join();
}
waveInReset(self->mInHdl);
for(size_t i{0};i < self->mWaveBuffer.size();++i)
waveInUnprepareHeader(self->mInHdl, &self->mWaveBuffer[i], sizeof(WAVEHDR));
waveInReset(mInHdl);
for(size_t i{0};i < mWaveBuffer.size();++i)
waveInUnprepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
self->mReadable.store(0, std::memory_order_release);
self->mIdx = 0;
mReadable.store(0, std::memory_order_release);
mIdx = 0;
}
ALCenum WinMMCapture_captureSamples(WinMMCapture *self, ALCvoid *buffer, ALCuint samples)
ALCenum WinMMCapture::captureSamples(void *buffer, ALCuint samples)
{
RingBuffer *ring{self->mRing.get()};
ring->read(buffer, samples);
mRing->read(buffer, samples);
return ALC_NO_ERROR;
}
ALCuint WinMMCapture_availableSamples(WinMMCapture *self)
{
RingBuffer *ring{self->mRing.get()};
return (ALCuint)ring->readSpace();
}
ALCuint WinMMCapture::availableSamples()
{ return (ALCuint)mRing->readSpace(); }
} // namespace
@@ -688,21 +634,12 @@ void WinMMBackendFactory::probe(DevProbe type, std::string *outnames)
}
}
ALCbackend *WinMMBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
BackendBase *WinMMBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
WinMMPlayback *backend;
NEW_OBJ(backend, WinMMPlayback)(device);
return backend;
}
return new WinMMPlayback{device};
if(type == ALCbackend_Capture)
{
WinMMCapture *backend;
NEW_OBJ(backend, WinMMCapture)(device);
return backend;
}
return new WinMMCapture{device};
return nullptr;
}
+1 -1
View File
@@ -12,7 +12,7 @@ public:
void probe(DevProbe type, std::string *outnames) override;
ALCbackend *createBackend(ALCdevice *device, ALCbackend_Type type) override;
BackendBase *createBackend(ALCdevice *device, ALCbackend_Type type) override;
static BackendFactory &getFactory();
};
-90
View File
@@ -1,90 +0,0 @@
#ifndef POLYMORPHISM_H
#define POLYMORPHISM_H
/* Defines method forwards, which call the given parent's (T2's) implementation. */
#define DECLARE_FORWARD(T1, T2, rettype, func) \
rettype T1##_##func(T1 *obj) \
{ return T2##_##func(static_cast<T2*>(obj)); }
#define DECLARE_FORWARD1(T1, T2, rettype, func, argtype1) \
rettype T1##_##func(T1 *obj, argtype1 a) \
{ return T2##_##func(static_cast<T2*>(obj), a); }
#define DECLARE_FORWARD2(T1, T2, rettype, func, argtype1, argtype2) \
rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b) \
{ return T2##_##func(static_cast<T2*>(obj), a, b); }
#define DECLARE_FORWARD3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b, argtype3 c) \
{ return T2##_##func(static_cast<T2*>(obj), a, b, c); }
/* Defines method thunks, functions that call to the child's method. */
#define DECLARE_THUNK(T1, T2, rettype, func) \
static rettype T1##_##T2##_##func(T2 *obj) \
{ return T1##_##func(static_cast<T1*>(obj)); }
#define DECLARE_THUNK1(T1, T2, rettype, func, argtype1) \
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a) \
{ return T1##_##func(static_cast<T1*>(obj), a); }
#define DECLARE_THUNK2(T1, T2, rettype, func, argtype1, argtype2) \
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b) \
{ return T1##_##func(static_cast<T1*>(obj), a, b); }
#define DECLARE_THUNK3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \
{ return T1##_##func(static_cast<T1*>(obj), a, b, c); }
#define DECLARE_THUNK4(T1, T2, rettype, func, argtype1, argtype2, argtype3, argtype4) \
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c, argtype4 d) \
{ return T1##_##func(static_cast<T1*>(obj), a, b, c, d); }
/* Defines the default functions used to (de)allocate a polymorphic object. */
#define DECLARE_DEFAULT_ALLOCATORS(T) \
static void* T##_New(size_t size) { return al_calloc(16, size); } \
static void T##_Delete(void *ptr) { al_free(ptr); }
/* Helper to extract an argument list for virtual method calls. */
#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
/* Call a "virtual" method on an object, with arguments. */
#define V(obj, func) ((obj)->vtbl->func((obj), EXTRACT_VCALL_ARGS
/* Call a "virtual" method on an object, with no arguments. */
#define V0(obj, func) ((obj)->vtbl->func((obj) EXTRACT_VCALL_ARGS
/* Helper to extract an argument list for NEW_OBJ calls. */
#define EXTRACT_NEW_ARGS(...) __VA_ARGS__); \
} \
} while(0)
/* Allocate and construct an object, with arguments. */
#define NEW_OBJ(_res, T) do { \
_res = (T*)T##_New(sizeof(T)); \
if(_res) \
{ \
T##_Construct(_res, EXTRACT_NEW_ARGS
/* Allocate and construct an object, with no arguments. */
#define NEW_OBJ0(_res, T) do { \
_res = (T*)T##_New(sizeof(T)); \
if(_res) \
{ \
T##_Construct(_res EXTRACT_NEW_ARGS
/* Destructs and deallocate an object. */
#define DELETE_OBJ(obj) do { \
if((obj) != NULL) \
{ \
V0((obj),Destruct)(); \
V0((obj),Delete)(); \
} \
} while(0)
/* Helper to get a type's vtable thunk for a child type. */
#define GET_VTABLE2(T1, T2) (&(T1##_##T2##_vtable))
/* Helper to set an object's vtable thunk for a child type. Used when constructing an object. */
#define SET_VTABLE2(T1, T2, obj) (static_cast<T2*>(obj)->vtbl = GET_VTABLE2(T1, T2))
#endif /* POLYMORPHISM_H */
-1
View File
@@ -773,7 +773,6 @@ SET(ALC_OBJS
Alc/bformatdec.cpp
Alc/bformatdec.h
Alc/panning.cpp
Alc/polymorphism.h
Alc/mixvoice.cpp
Alc/mixer/defs.h
Alc/mixer/mixer_c.cpp
+2 -2
View File
@@ -236,7 +236,7 @@ struct EnumeratedHrtf;
struct DirectHrtfState;
struct FrontStablizer;
struct Compressor;
struct ALCbackend;
struct BackendBase;
struct ALbuffer;
struct ALeffect;
struct ALfilter;
@@ -786,7 +786,7 @@ struct ALCdevice_struct {
std::atomic<ALCcontext*> ContextList{nullptr};
std::mutex BackendLock;
ALCbackend *Backend{nullptr};
BackendBase *Backend{nullptr};
std::atomic<ALCdevice*> next{nullptr};