Convert the backends to use proper inheritence
This commit is contained in:
+31
-30
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user