2007-11-13 18:02:18 -08:00
|
|
|
/**
|
|
|
|
* OpenAL cross platform audio library
|
|
|
|
* Copyright (C) 1999-2007 by authors.
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the
|
2014-08-18 14:11:03 +02:00
|
|
|
* Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2007-11-13 18:02:18 -08:00
|
|
|
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
|
|
|
*/
|
|
|
|
|
2008-01-16 14:09:04 -08:00
|
|
|
#include "config.h"
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
#include "backends/oss.h"
|
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/types.h>
|
2015-12-11 09:24:40 -08:00
|
|
|
#include <sys/time.h>
|
2007-11-13 18:02:18 -08:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
2016-01-21 02:08:45 -08:00
|
|
|
#include <string.h>
|
2007-11-13 18:02:18 -08:00
|
|
|
#include <memory.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <math.h>
|
2012-09-14 02:14:29 -07:00
|
|
|
|
2018-11-14 20:49:08 -08:00
|
|
|
#include <atomic>
|
|
|
|
#include <thread>
|
2018-11-14 19:16:50 -08:00
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
#include <algorithm>
|
|
|
|
|
2007-11-13 18:02:18 -08:00
|
|
|
#include "alMain.h"
|
2012-09-14 02:14:29 -07:00
|
|
|
#include "alu.h"
|
2018-01-11 07:56:54 -08:00
|
|
|
#include "alconfig.h"
|
2018-01-11 09:16:28 -08:00
|
|
|
#include "ringbuffer.h"
|
2013-10-28 12:05:33 -07:00
|
|
|
#include "compat.h"
|
2007-11-13 18:02:18 -08:00
|
|
|
|
|
|
|
#include <sys/soundcard.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The OSS documentation talks about SOUND_MIXER_READ, but the header
|
|
|
|
* only contains MIXER_READ. Play safe. Same for WRITE.
|
|
|
|
*/
|
|
|
|
#ifndef SOUND_MIXER_READ
|
|
|
|
#define SOUND_MIXER_READ MIXER_READ
|
|
|
|
#endif
|
|
|
|
#ifndef SOUND_MIXER_WRITE
|
|
|
|
#define SOUND_MIXER_WRITE MIXER_WRITE
|
|
|
|
#endif
|
|
|
|
|
2016-01-21 01:05:29 -08:00
|
|
|
#if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
|
2015-12-17 19:20:08 -06:00
|
|
|
#define ALC_OSS_COMPAT
|
|
|
|
#endif
|
|
|
|
#ifndef SNDCTL_AUDIOINFO
|
|
|
|
#define ALC_OSS_COMPAT
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FreeBSD strongly discourages the use of specific devices,
|
|
|
|
* such as those returned in oss_audioinfo.devnode
|
|
|
|
*/
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
#define ALC_OSS_DEVNODE_TRUC
|
|
|
|
#endif
|
|
|
|
|
2018-11-13 23:23:46 -08:00
|
|
|
namespace {
|
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
constexpr char DefaultName[] = "OSS Default";
|
|
|
|
const char *DefaultPlayback{"/dev/dsp"};
|
|
|
|
const char *DefaultCapture{"/dev/dsp"};
|
2015-12-17 19:20:08 -06:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
struct DevMap {
|
|
|
|
std::string name;
|
|
|
|
std::string device_name;
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
template<typename StrT0, typename StrT1>
|
|
|
|
DevMap(StrT0&& name_, StrT1&& devname_)
|
|
|
|
: name{std::forward<StrT0>(name_)}, device_name{std::forward<StrT1>(devname_)}
|
|
|
|
{ }
|
2015-12-17 19:20:08 -06:00
|
|
|
};
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-24 16:58:49 -08:00
|
|
|
bool checkName(const al::vector<DevMap> &list, const std::string &name)
|
2018-11-14 19:16:50 -08:00
|
|
|
{
|
|
|
|
return std::find_if(list.cbegin(), list.cend(),
|
|
|
|
[&name](const DevMap &entry) -> bool
|
|
|
|
{ return entry.name == name; }
|
|
|
|
) != list.cend();
|
|
|
|
}
|
|
|
|
|
2018-11-24 16:58:49 -08:00
|
|
|
al::vector<DevMap> PlaybackDevices;
|
|
|
|
al::vector<DevMap> CaptureDevices;
|
2018-11-14 19:16:50 -08:00
|
|
|
|
|
|
|
|
2016-01-21 01:05:29 -08:00
|
|
|
#ifdef ALC_OSS_COMPAT
|
2015-12-17 19:20:08 -06:00
|
|
|
|
2016-12-21 10:54:19 -08:00
|
|
|
#define DSP_CAP_OUTPUT 0x00020000
|
|
|
|
#define DSP_CAP_INPUT 0x00010000
|
2018-11-24 16:58:49 -08:00
|
|
|
void ALCossListPopulate(al::vector<DevMap> *devlist, int type)
|
2015-12-17 19:20:08 -06:00
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
devlist->emplace_back(DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback);
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2018-11-24 16:58:49 -08:00
|
|
|
void ALCossListAppend(al::vector<DevMap> *list, const char *handle, size_t hlen, const char *path, size_t plen)
|
2016-01-21 01:05:29 -08:00
|
|
|
{
|
2015-12-17 19:20:08 -06:00
|
|
|
#ifdef ALC_OSS_DEVNODE_TRUC
|
2018-11-14 19:16:50 -08:00
|
|
|
for(size_t i{0};i < plen;i++)
|
2016-01-21 01:05:29 -08:00
|
|
|
{
|
|
|
|
if(path[i] == '.')
|
2015-12-17 19:20:08 -06:00
|
|
|
{
|
2016-01-21 01:05:29 -08:00
|
|
|
if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
|
2015-12-17 19:20:08 -06:00
|
|
|
hlen = hlen + i - plen;
|
|
|
|
plen = i;
|
|
|
|
}
|
2016-01-21 01:05:29 -08:00
|
|
|
}
|
2015-12-17 19:20:08 -06:00
|
|
|
#endif
|
2016-01-21 01:05:29 -08:00
|
|
|
if(handle[0] == '\0')
|
|
|
|
{
|
2015-12-17 19:20:08 -06:00
|
|
|
handle = path;
|
|
|
|
hlen = plen;
|
|
|
|
}
|
2016-01-21 01:05:29 -08:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
std::string basename{handle, hlen};
|
|
|
|
basename.erase(std::find(basename.begin(), basename.end(), '\0'), basename.end());
|
|
|
|
std::string devname{path, plen};
|
|
|
|
devname.erase(std::find(devname.begin(), devname.end(), '\0'), devname.end());
|
|
|
|
|
|
|
|
auto iter = std::find_if(list->cbegin(), list->cend(),
|
|
|
|
[&devname](const DevMap &entry) -> bool
|
|
|
|
{ return entry.device_name == devname; }
|
|
|
|
);
|
|
|
|
if(iter != list->cend())
|
|
|
|
return;
|
|
|
|
|
|
|
|
int count{1};
|
|
|
|
std::string newname{basename};
|
|
|
|
while(checkName(PlaybackDevices, newname))
|
2016-01-21 01:05:29 -08:00
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
newname = basename;
|
|
|
|
newname += " #";
|
|
|
|
newname += std::to_string(++count);
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
2016-01-21 01:05:29 -08:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
list->emplace_back(std::move(newname), std::move(devname));
|
|
|
|
const DevMap &entry = list->back();
|
2016-01-21 01:05:29 -08:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
|
|
|
|
2018-11-24 16:58:49 -08:00
|
|
|
void ALCossListPopulate(al::vector<DevMap> *devlist, int type_flag)
|
2015-12-17 19:20:08 -06:00
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
int fd{open("/dev/mixer", O_RDONLY)};
|
|
|
|
if(fd < 0)
|
2015-12-17 19:20:08 -06:00
|
|
|
{
|
2017-07-23 16:43:39 -07:00
|
|
|
TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
|
2018-11-14 19:16:50 -08:00
|
|
|
goto done;
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
2018-11-14 19:16:50 -08:00
|
|
|
|
|
|
|
struct oss_sysinfo si;
|
2016-01-21 01:05:29 -08:00
|
|
|
if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
|
2015-12-17 19:20:08 -06:00
|
|
|
{
|
2017-07-23 16:43:39 -07:00
|
|
|
TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
|
2016-01-21 01:05:29 -08:00
|
|
|
goto done;
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
2016-01-21 01:05:29 -08:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
for(int i{0};i < si.numaudios;i++)
|
|
|
|
{
|
|
|
|
struct oss_audioinfo ai;
|
2015-12-17 19:20:08 -06:00
|
|
|
ai.dev = i;
|
2016-01-21 01:05:29 -08:00
|
|
|
if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
|
2015-12-17 19:20:08 -06:00
|
|
|
{
|
2016-01-21 01:05:29 -08:00
|
|
|
ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
|
2015-12-17 19:20:08 -06:00
|
|
|
continue;
|
|
|
|
}
|
2018-11-14 19:16:50 -08:00
|
|
|
if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
|
2016-01-21 02:08:45 -08:00
|
|
|
continue;
|
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
const char *handle;
|
|
|
|
size_t len;
|
2016-01-21 02:08:45 -08:00
|
|
|
if(ai.handle[0] != '\0')
|
2015-12-17 19:20:08 -06:00
|
|
|
{
|
2016-01-21 01:05:29 -08:00
|
|
|
len = strnlen(ai.handle, sizeof(ai.handle));
|
|
|
|
handle = ai.handle;
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-21 01:05:29 -08:00
|
|
|
len = strnlen(ai.name, sizeof(ai.name));
|
|
|
|
handle = ai.name;
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
2018-11-14 19:16:50 -08:00
|
|
|
|
|
|
|
ALCossListAppend(devlist, handle, len, ai.devnode,
|
|
|
|
strnlen(ai.devnode, sizeof(ai.devnode)));
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
2016-01-21 01:05:29 -08:00
|
|
|
|
|
|
|
done:
|
2018-11-14 19:16:50 -08:00
|
|
|
if(fd >= 0)
|
|
|
|
close(fd);
|
|
|
|
fd = -1;
|
|
|
|
|
|
|
|
const char *defdev{(type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback};
|
|
|
|
auto iter = std::find_if(devlist->cbegin(), devlist->cend(),
|
|
|
|
[defdev](const DevMap &entry) -> bool
|
|
|
|
{ return entry.device_name == defdev; }
|
|
|
|
);
|
|
|
|
if(iter == devlist->cend())
|
|
|
|
devlist->insert(devlist->begin(), DevMap{DefaultName, defdev});
|
|
|
|
else
|
2015-12-17 19:20:08 -06:00
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
DevMap entry{std::move(*iter)};
|
|
|
|
devlist->erase(iter);
|
|
|
|
devlist->insert(devlist->begin(), std::move(entry));
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
2018-11-14 19:16:50 -08:00
|
|
|
devlist->shrink_to_fit();
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
#endif
|
|
|
|
|
2018-11-13 23:23:46 -08:00
|
|
|
int log2i(ALCuint x)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
|
|
|
int y = 0;
|
|
|
|
while (x > 1)
|
|
|
|
{
|
|
|
|
x >>= 1;
|
|
|
|
y++;
|
|
|
|
}
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
2018-11-13 23:23:46 -08:00
|
|
|
|
2018-11-12 23:49:11 -08:00
|
|
|
struct ALCplaybackOSS final : public ALCbackend {
|
2018-11-14 20:49:08 -08:00
|
|
|
int fd{-1};
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-26 17:31:04 -08:00
|
|
|
al::vector<ALubyte> mMixData;
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-26 17:31:04 -08:00
|
|
|
std::atomic<ALenum> mKillNow{AL_TRUE};
|
|
|
|
std::thread mThread;
|
2018-11-12 23:49:11 -08:00
|
|
|
};
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
int ALCplaybackOSS_mixerProc(ALCplaybackOSS *self);
|
|
|
|
|
|
|
|
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)
|
2014-03-22 00:28:55 -07:00
|
|
|
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
|
2013-11-02 15:42:45 -07:00
|
|
|
DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS);
|
|
|
|
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
|
2018-11-12 23:49:11 -08:00
|
|
|
{
|
|
|
|
new (self) ALCplaybackOSS{};
|
|
|
|
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
|
|
|
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
|
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
void ALCplaybackOSS_Destruct(ALCplaybackOSS *self)
|
2018-11-12 23:49:11 -08:00
|
|
|
{
|
|
|
|
if(self->fd != -1)
|
|
|
|
close(self->fd);
|
|
|
|
self->fd = -1;
|
|
|
|
|
|
|
|
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
|
|
|
self->~ALCplaybackOSS();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
int ALCplaybackOSS_mixerProc(ALCplaybackOSS *self)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2013-11-02 15:42:45 -07:00
|
|
|
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
2017-02-18 15:58:15 -08:00
|
|
|
struct timeval timeout;
|
|
|
|
ALubyte *write_ptr;
|
|
|
|
ALint frame_size;
|
|
|
|
ALint to_write;
|
2009-10-19 00:33:00 -07:00
|
|
|
ssize_t wrote;
|
2017-02-18 15:58:15 -08:00
|
|
|
fd_set wfds;
|
|
|
|
int sret;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2010-05-12 07:27:12 -07:00
|
|
|
SetRTPriority();
|
2018-11-17 06:07:04 -08:00
|
|
|
althrd_setname(MIXER_THREAD_NAME);
|
2009-11-29 23:02:21 -08:00
|
|
|
|
2018-12-19 05:57:36 -08:00
|
|
|
frame_size = device->frameSizeFromFmt();
|
2009-09-15 18:19:00 -07:00
|
|
|
|
2017-02-18 15:58:15 -08:00
|
|
|
ALCplaybackOSS_lock(self);
|
2018-11-26 17:31:04 -08:00
|
|
|
while(!self->mKillNow.load(std::memory_order_acquire) &&
|
2018-11-26 18:07:52 -08:00
|
|
|
device->Connected.load(std::memory_order_acquire))
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2017-02-18 15:58:15 -08:00
|
|
|
FD_ZERO(&wfds);
|
|
|
|
FD_SET(self->fd, &wfds);
|
|
|
|
timeout.tv_sec = 1;
|
|
|
|
timeout.tv_usec = 0;
|
|
|
|
|
|
|
|
ALCplaybackOSS_unlock(self);
|
2018-11-12 23:49:11 -08:00
|
|
|
sret = select(self->fd+1, nullptr, &wfds, nullptr, &timeout);
|
2017-02-18 15:58:15 -08:00
|
|
|
ALCplaybackOSS_lock(self);
|
|
|
|
if(sret < 0)
|
|
|
|
{
|
|
|
|
if(errno == EINTR)
|
|
|
|
continue;
|
|
|
|
ERR("select failed: %s\n", strerror(errno));
|
2018-02-03 13:54:42 -08:00
|
|
|
aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
|
2017-02-18 15:58:15 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if(sret == 0)
|
|
|
|
{
|
|
|
|
WARN("select timeout\n");
|
|
|
|
continue;
|
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-26 17:31:04 -08:00
|
|
|
write_ptr = self->mMixData.data();
|
|
|
|
to_write = self->mMixData.size();
|
2017-02-18 15:58:15 -08:00
|
|
|
aluMixData(device, write_ptr, to_write/frame_size);
|
2018-11-26 17:31:04 -08:00
|
|
|
while(to_write > 0 && !self->mKillNow.load())
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2017-02-18 15:58:15 -08:00
|
|
|
wrote = write(self->fd, write_ptr, to_write);
|
2009-05-25 12:12:37 -07:00
|
|
|
if(wrote < 0)
|
|
|
|
{
|
2017-02-18 15:58:15 -08:00
|
|
|
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
|
|
|
continue;
|
|
|
|
ERR("write failed: %s\n", strerror(errno));
|
2018-02-03 13:54:42 -08:00
|
|
|
aluHandleDisconnect(device, "Failed writing playback samples: %s",
|
|
|
|
strerror(errno));
|
2017-02-18 15:58:15 -08:00
|
|
|
break;
|
2009-05-25 12:12:37 -07:00
|
|
|
}
|
|
|
|
|
2017-02-18 15:58:15 -08:00
|
|
|
to_write -= wrote;
|
|
|
|
write_ptr += wrote;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
}
|
2017-02-18 15:58:15 -08:00
|
|
|
ALCplaybackOSS_unlock(self);
|
2007-11-13 18:02:18 -08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-16 19:34:52 -08:00
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2013-11-02 15:42:45 -07:00
|
|
|
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
const char *devname{DefaultPlayback};
|
|
|
|
if(!name)
|
|
|
|
name = DefaultName;
|
2015-12-17 19:20:08 -06:00
|
|
|
else
|
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
if(PlaybackDevices.empty())
|
|
|
|
ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
|
|
|
|
|
|
|
|
auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
|
|
|
|
[&name](const DevMap &entry) -> bool
|
|
|
|
{ return entry.name == name; }
|
|
|
|
);
|
|
|
|
if(iter == PlaybackDevices.cend())
|
2015-12-17 19:20:08 -06:00
|
|
|
return ALC_INVALID_VALUE;
|
2018-11-14 19:16:50 -08:00
|
|
|
devname = iter->device_name.c_str();
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
self->fd = open(devname, O_WRONLY);
|
2013-11-02 15:42:45 -07:00
|
|
|
if(self->fd == -1)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
ERR("Could not open %s: %s\n", devname, strerror(errno));
|
2011-08-24 14:24:48 -07:00
|
|
|
return ALC_INVALID_VALUE;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2018-11-18 18:45:45 -08:00
|
|
|
device->DeviceName = name;
|
2011-08-24 14:24:48 -07:00
|
|
|
return ALC_NO_ERROR;
|
2009-08-13 12:28:46 -07:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
|
2009-08-13 12:28:46 -07:00
|
|
|
{
|
2013-11-02 15:42:45 -07:00
|
|
|
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
2009-08-13 12:28:46 -07:00
|
|
|
int numFragmentsLogSize;
|
|
|
|
int log2FragmentSize;
|
|
|
|
unsigned int periods;
|
|
|
|
audio_buf_info info;
|
|
|
|
ALuint frameSize;
|
|
|
|
int numChannels;
|
|
|
|
int ossFormat;
|
|
|
|
int ossSpeed;
|
2018-11-12 23:49:11 -08:00
|
|
|
const char *err;
|
2009-08-13 12:28:46 -07:00
|
|
|
|
2010-12-04 19:50:00 -08:00
|
|
|
switch(device->FmtType)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtByte:
|
|
|
|
ossFormat = AFMT_S8;
|
|
|
|
break;
|
|
|
|
case DevFmtUByte:
|
2007-11-13 18:02:18 -08:00
|
|
|
ossFormat = AFMT_U8;
|
|
|
|
break;
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtUShort:
|
2012-02-14 11:44:57 -08:00
|
|
|
case DevFmtInt:
|
|
|
|
case DevFmtUInt:
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtFloat:
|
|
|
|
device->FmtType = DevFmtShort;
|
2009-08-15 13:20:35 -07:00
|
|
|
/* fall-through */
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtShort:
|
2007-11-13 18:02:18 -08:00
|
|
|
ossFormat = AFMT_S16_NE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-09-16 23:29:32 -07:00
|
|
|
periods = device->NumUpdates;
|
2018-12-19 05:57:36 -08:00
|
|
|
numChannels = device->channelsFromFmt();
|
2007-11-13 18:02:18 -08:00
|
|
|
ossSpeed = device->Frequency;
|
2018-12-19 05:57:36 -08:00
|
|
|
frameSize = numChannels * device->bytesFromFmt();
|
2017-02-22 15:44:47 -08:00
|
|
|
/* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
|
|
|
|
log2FragmentSize = maxi(log2i(device->UpdateSize*frameSize), 4);
|
2007-11-13 18:02:18 -08:00
|
|
|
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
|
|
|
|
|
2010-12-09 23:32:47 -08:00
|
|
|
#define CHECKERR(func) if((func) < 0) { \
|
|
|
|
err = #func; \
|
|
|
|
goto err; \
|
|
|
|
}
|
2009-10-20 12:28:51 -07:00
|
|
|
/* Don't fail if SETFRAGMENT fails. We can handle just about anything
|
|
|
|
* that's reported back via GETOSPACE */
|
2013-11-02 15:42:45 -07:00
|
|
|
ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
|
|
|
|
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
|
|
|
|
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
|
|
|
|
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
|
|
|
|
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info));
|
2010-12-09 23:32:47 -08:00
|
|
|
if(0)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2010-12-09 23:32:47 -08:00
|
|
|
err:
|
2011-07-13 01:43:00 -07:00
|
|
|
ERR("%s failed: %s\n", err, strerror(errno));
|
2007-11-13 18:02:18 -08:00
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
2010-12-09 23:32:47 -08:00
|
|
|
#undef CHECKERR
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2018-12-19 05:57:36 -08:00
|
|
|
if(device->channelsFromFmt() != numChannels)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2011-07-13 01:43:00 -07:00
|
|
|
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
|
2008-01-01 06:25:00 -08:00
|
|
|
return ALC_FALSE;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2010-12-04 19:50:00 -08:00
|
|
|
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
|
|
|
|
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
|
|
|
|
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2011-07-13 01:43:00 -07:00
|
|
|
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
|
2007-11-13 18:02:18 -08:00
|
|
|
return ALC_FALSE;
|
|
|
|
}
|
|
|
|
|
2012-01-17 16:38:58 -08:00
|
|
|
device->Frequency = ossSpeed;
|
2009-08-13 12:28:46 -07:00
|
|
|
device->UpdateSize = info.fragsize / frameSize;
|
2017-02-22 15:44:47 -08:00
|
|
|
device->NumUpdates = info.fragments;
|
2009-08-13 12:28:46 -07:00
|
|
|
|
2009-12-02 04:03:51 -08:00
|
|
|
SetDefaultChannelOrder(device);
|
|
|
|
|
2012-03-05 07:11:09 -08:00
|
|
|
return ALC_TRUE;
|
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
|
2012-03-05 07:11:09 -08:00
|
|
|
{
|
2013-11-02 15:42:45 -07:00
|
|
|
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
2012-03-05 07:11:09 -08:00
|
|
|
|
2018-11-14 20:49:08 -08:00
|
|
|
try {
|
2018-12-19 05:57:36 -08:00
|
|
|
self->mMixData.resize(device->UpdateSize * device->frameSizeFromFmt());
|
2012-03-05 07:11:09 -08:00
|
|
|
|
2018-11-26 17:31:04 -08:00
|
|
|
self->mKillNow.store(AL_FALSE);
|
|
|
|
self->mThread = std::thread(ALCplaybackOSS_mixerProc, self);
|
2018-11-14 20:49:08 -08:00
|
|
|
return ALC_TRUE;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2018-11-14 20:49:08 -08:00
|
|
|
catch(std::exception& e) {
|
|
|
|
ERR("Could not create playback thread: %s\n", e.what());
|
|
|
|
}
|
|
|
|
catch(...) {
|
|
|
|
}
|
|
|
|
return ALC_FALSE;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
void ALCplaybackOSS_stop(ALCplaybackOSS *self)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-26 17:31:04 -08:00
|
|
|
if(self->mKillNow.exchange(AL_TRUE) || !self->mThread.joinable())
|
2009-08-13 12:28:46 -07:00
|
|
|
return;
|
2018-11-26 17:31:04 -08:00
|
|
|
self->mThread.join();
|
2007-11-13 18:02:18 -08:00
|
|
|
|
2013-11-02 15:42:45 -07:00
|
|
|
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
|
2011-07-13 01:43:00 -07:00
|
|
|
ERR("Error resetting device: %s\n", strerror(errno));
|
2009-10-20 12:28:51 -07:00
|
|
|
|
2018-11-26 17:31:04 -08:00
|
|
|
self->mMixData.clear();
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-12 23:49:11 -08:00
|
|
|
struct ALCcaptureOSS final : public ALCbackend {
|
2018-11-14 20:49:08 -08:00
|
|
|
int fd{-1};
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-12-22 11:38:38 -08:00
|
|
|
RingBufferPtr mRing{nullptr};
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-26 17:31:04 -08:00
|
|
|
std::atomic<ALenum> mKillNow{AL_TRUE};
|
|
|
|
std::thread mThread;
|
2018-11-12 23:49:11 -08:00
|
|
|
};
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
int ALCcaptureOSS_recordProc(ALCcaptureOSS *self);
|
|
|
|
|
|
|
|
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)
|
2014-03-22 00:28:55 -07:00
|
|
|
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
|
2013-11-02 15:42:45 -07:00
|
|
|
DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS);
|
|
|
|
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
|
2018-11-12 23:49:11 -08:00
|
|
|
{
|
|
|
|
new (self) ALCcaptureOSS{};
|
|
|
|
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
|
|
|
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
|
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
void ALCcaptureOSS_Destruct(ALCcaptureOSS *self)
|
2018-11-12 23:49:11 -08:00
|
|
|
{
|
|
|
|
if(self->fd != -1)
|
|
|
|
close(self->fd);
|
|
|
|
self->fd = -1;
|
|
|
|
|
|
|
|
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
|
|
|
self->~ALCcaptureOSS();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
int ALCcaptureOSS_recordProc(ALCcaptureOSS *self)
|
2013-11-02 15:42:45 -07:00
|
|
|
{
|
|
|
|
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
2017-02-18 15:58:15 -08:00
|
|
|
struct timeval timeout;
|
|
|
|
int frame_size;
|
|
|
|
fd_set rfds;
|
2016-03-29 23:48:36 -07:00
|
|
|
ssize_t amt;
|
2017-02-18 15:58:15 -08:00
|
|
|
int sret;
|
2013-11-02 15:42:45 -07:00
|
|
|
|
|
|
|
SetRTPriority();
|
2018-11-17 06:07:04 -08:00
|
|
|
althrd_setname(RECORD_THREAD_NAME);
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-12-19 05:57:36 -08:00
|
|
|
frame_size = device->frameSizeFromFmt();
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-26 17:31:04 -08:00
|
|
|
while(!self->mKillNow.load())
|
2013-11-02 15:42:45 -07:00
|
|
|
{
|
2017-02-18 15:58:15 -08:00
|
|
|
FD_ZERO(&rfds);
|
|
|
|
FD_SET(self->fd, &rfds);
|
|
|
|
timeout.tv_sec = 1;
|
|
|
|
timeout.tv_usec = 0;
|
|
|
|
|
2018-11-12 23:49:11 -08:00
|
|
|
sret = select(self->fd+1, &rfds, nullptr, nullptr, &timeout);
|
2017-02-18 15:58:15 -08:00
|
|
|
if(sret < 0)
|
2013-11-02 15:42:45 -07:00
|
|
|
{
|
2017-02-18 15:58:15 -08:00
|
|
|
if(errno == EINTR)
|
|
|
|
continue;
|
|
|
|
ERR("select failed: %s\n", strerror(errno));
|
2018-02-03 13:54:42 -08:00
|
|
|
aluHandleDisconnect(device, "Failed to check capture samples: %s", strerror(errno));
|
2017-02-18 15:58:15 -08:00
|
|
|
break;
|
2013-11-02 15:42:45 -07:00
|
|
|
}
|
2017-02-18 15:58:15 -08:00
|
|
|
else if(sret == 0)
|
2013-11-02 15:42:45 -07:00
|
|
|
{
|
2017-02-18 15:58:15 -08:00
|
|
|
WARN("select timeout\n");
|
2013-11-02 15:42:45 -07:00
|
|
|
continue;
|
|
|
|
}
|
2017-02-18 15:58:15 -08:00
|
|
|
|
2018-12-22 11:38:38 -08:00
|
|
|
auto vec = ll_ringbuffer_get_write_vector(self->mRing.get());
|
2018-11-19 04:46:49 -08:00
|
|
|
if(vec.first.len > 0)
|
2017-02-18 15:58:15 -08:00
|
|
|
{
|
2018-11-19 04:46:49 -08:00
|
|
|
amt = read(self->fd, vec.first.buf, vec.first.len*frame_size);
|
2017-02-18 15:58:15 -08:00
|
|
|
if(amt < 0)
|
|
|
|
{
|
|
|
|
ERR("read failed: %s\n", strerror(errno));
|
|
|
|
ALCcaptureOSS_lock(self);
|
2018-02-03 13:54:42 -08:00
|
|
|
aluHandleDisconnect(device, "Failed reading capture samples: %s", strerror(errno));
|
2017-02-18 15:58:15 -08:00
|
|
|
ALCcaptureOSS_unlock(self);
|
|
|
|
break;
|
|
|
|
}
|
2018-12-22 11:38:38 -08:00
|
|
|
ll_ringbuffer_write_advance(self->mRing.get(), amt/frame_size);
|
2017-02-18 15:58:15 -08:00
|
|
|
}
|
2013-11-02 15:42:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2013-11-02 15:42:45 -07:00
|
|
|
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
2007-12-16 19:34:52 -08:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
const char *devname{DefaultCapture};
|
|
|
|
if(!name)
|
|
|
|
name = DefaultName;
|
2015-12-17 19:20:08 -06:00
|
|
|
else
|
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
if(CaptureDevices.empty())
|
|
|
|
ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
|
|
|
|
|
|
|
|
auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
|
|
|
|
[&name](const DevMap &entry) -> bool
|
|
|
|
{ return entry.name == name; }
|
|
|
|
);
|
|
|
|
if(iter == CaptureDevices.cend())
|
2015-12-17 19:20:08 -06:00
|
|
|
return ALC_INVALID_VALUE;
|
2018-11-14 19:16:50 -08:00
|
|
|
devname = iter->device_name.c_str();
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
2007-12-16 19:34:52 -08:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
self->fd = open(devname, O_RDONLY);
|
2013-11-02 15:42:45 -07:00
|
|
|
if(self->fd == -1)
|
2007-12-16 19:34:52 -08:00
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
ERR("Could not open %s: %s\n", devname, strerror(errno));
|
2011-08-24 14:44:15 -07:00
|
|
|
return ALC_INVALID_VALUE;
|
2007-12-16 19:34:52 -08:00
|
|
|
}
|
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
int ossFormat{};
|
2010-12-04 19:50:00 -08:00
|
|
|
switch(device->FmtType)
|
2007-12-16 19:34:52 -08:00
|
|
|
{
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtByte:
|
|
|
|
ossFormat = AFMT_S8;
|
|
|
|
break;
|
|
|
|
case DevFmtUByte:
|
2007-12-16 19:34:52 -08:00
|
|
|
ossFormat = AFMT_U8;
|
|
|
|
break;
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtShort:
|
2007-12-16 19:34:52 -08:00
|
|
|
ossFormat = AFMT_S16_NE;
|
|
|
|
break;
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtUShort:
|
2012-02-14 11:44:57 -08:00
|
|
|
case DevFmtInt:
|
|
|
|
case DevFmtUInt:
|
2010-12-04 19:50:00 -08:00
|
|
|
case DevFmtFloat:
|
2012-02-29 21:45:44 -08:00
|
|
|
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
|
2011-08-24 14:44:15 -07:00
|
|
|
return ALC_INVALID_VALUE;
|
2007-12-16 19:34:52 -08:00
|
|
|
}
|
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
int periods{4};
|
2018-12-19 05:57:36 -08:00
|
|
|
int numChannels{device->channelsFromFmt()};
|
|
|
|
int frameSize{numChannels * device->bytesFromFmt()};
|
2018-11-14 19:16:50 -08:00
|
|
|
int ossSpeed{static_cast<int>(device->Frequency)};
|
|
|
|
int log2FragmentSize{log2i(device->UpdateSize * device->NumUpdates *
|
|
|
|
frameSize / periods)};
|
2007-12-16 19:34:52 -08:00
|
|
|
|
|
|
|
/* according to the OSS spec, 16 bytes are the minimum */
|
2018-11-14 19:16:50 -08:00
|
|
|
log2FragmentSize = std::max(log2FragmentSize, 4);
|
|
|
|
int numFragmentsLogSize{(periods << 16) | log2FragmentSize};
|
2007-12-16 19:34:52 -08:00
|
|
|
|
2018-11-14 19:16:50 -08:00
|
|
|
audio_buf_info info;
|
|
|
|
const char *err;
|
2010-12-09 23:32:47 -08:00
|
|
|
#define CHECKERR(func) if((func) < 0) { \
|
|
|
|
err = #func; \
|
|
|
|
goto err; \
|
|
|
|
}
|
2013-11-02 15:42:45 -07:00
|
|
|
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
|
|
|
|
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
|
|
|
|
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
|
|
|
|
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
|
|
|
|
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info));
|
2010-12-09 23:32:47 -08:00
|
|
|
if(0)
|
2007-12-16 19:34:52 -08:00
|
|
|
{
|
2010-12-09 23:32:47 -08:00
|
|
|
err:
|
2011-07-13 01:43:00 -07:00
|
|
|
ERR("%s failed: %s\n", err, strerror(errno));
|
2013-11-02 15:42:45 -07:00
|
|
|
close(self->fd);
|
|
|
|
self->fd = -1;
|
2011-08-24 14:44:15 -07:00
|
|
|
return ALC_INVALID_VALUE;
|
2007-12-16 19:34:52 -08:00
|
|
|
}
|
2010-12-09 23:32:47 -08:00
|
|
|
#undef CHECKERR
|
2007-12-16 19:34:52 -08:00
|
|
|
|
2018-12-19 05:57:36 -08:00
|
|
|
if(device->channelsFromFmt() != numChannels)
|
2007-12-16 19:34:52 -08:00
|
|
|
{
|
2011-07-13 01:43:00 -07:00
|
|
|
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
|
2013-11-02 15:42:45 -07:00
|
|
|
close(self->fd);
|
|
|
|
self->fd = -1;
|
2011-08-24 14:44:15 -07:00
|
|
|
return ALC_INVALID_VALUE;
|
2007-12-16 19:34:52 -08:00
|
|
|
}
|
|
|
|
|
2010-12-04 19:50:00 -08:00
|
|
|
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
|
|
|
|
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
|
|
|
|
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
|
2007-12-16 19:34:52 -08:00
|
|
|
{
|
2011-07-13 01:43:00 -07:00
|
|
|
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
|
2013-11-02 15:42:45 -07:00
|
|
|
close(self->fd);
|
|
|
|
self->fd = -1;
|
2011-08-24 14:44:15 -07:00
|
|
|
return ALC_INVALID_VALUE;
|
2007-12-16 19:34:52 -08:00
|
|
|
}
|
|
|
|
|
2018-12-22 11:38:38 -08:00
|
|
|
self->mRing.reset(ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, frameSize,
|
|
|
|
false));
|
2018-11-26 17:31:04 -08:00
|
|
|
if(!self->mRing)
|
2007-12-16 19:34:52 -08:00
|
|
|
{
|
2011-07-13 01:43:00 -07:00
|
|
|
ERR("Ring buffer create failed\n");
|
2013-11-02 15:42:45 -07:00
|
|
|
close(self->fd);
|
|
|
|
self->fd = -1;
|
2011-08-24 14:44:15 -07:00
|
|
|
return ALC_OUT_OF_MEMORY;
|
2007-12-16 19:34:52 -08:00
|
|
|
}
|
|
|
|
|
2018-11-18 18:45:45 -08:00
|
|
|
device->DeviceName = name;
|
2011-08-24 14:44:15 -07:00
|
|
|
return ALC_NO_ERROR;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-14 20:49:08 -08:00
|
|
|
try {
|
2018-11-26 17:31:04 -08:00
|
|
|
self->mKillNow.store(AL_FALSE);
|
|
|
|
self->mThread = std::thread(ALCcaptureOSS_recordProc, self);
|
2018-11-14 20:49:08 -08:00
|
|
|
return ALC_TRUE;
|
|
|
|
}
|
|
|
|
catch(std::exception& e) {
|
|
|
|
ERR("Could not create record thread: %s\n", e.what());
|
|
|
|
}
|
|
|
|
catch(...) {
|
|
|
|
}
|
|
|
|
return ALC_FALSE;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
void ALCcaptureOSS_stop(ALCcaptureOSS *self)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-26 17:31:04 -08:00
|
|
|
if(self->mKillNow.exchange(AL_TRUE) || !self->mThread.joinable())
|
2017-02-18 15:58:15 -08:00
|
|
|
return;
|
|
|
|
|
2018-11-26 17:31:04 -08:00
|
|
|
self->mThread.join();
|
2017-02-18 15:58:15 -08:00
|
|
|
|
|
|
|
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
|
|
|
|
ERR("Error resetting device: %s\n", strerror(errno));
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-12-22 11:38:38 -08:00
|
|
|
ll_ringbuffer_read(self->mRing.get(), static_cast<char*>(buffer), samples);
|
2011-09-18 20:27:34 -07:00
|
|
|
return ALC_NO_ERROR;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
|
2013-11-02 15:42:45 -07:00
|
|
|
{
|
2018-12-22 11:38:38 -08:00
|
|
|
return ll_ringbuffer_read_space(self->mRing.get());
|
2013-11-02 15:42:45 -07:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
} // namespace
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-12 23:49:11 -08:00
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
BackendFactory &OSSBackendFactory::getFactory()
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-15 23:02:26 -08:00
|
|
|
static OSSBackendFactory factory{};
|
|
|
|
return factory;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
bool OSSBackendFactory::init()
|
2007-11-13 18:02:18 -08:00
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
ConfigValueStr(nullptr, "oss", "device", &DefaultPlayback);
|
|
|
|
ConfigValueStr(nullptr, "oss", "capture", &DefaultCapture);
|
2012-02-29 21:45:44 -08:00
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
return true;
|
2007-11-13 18:02:18 -08:00
|
|
|
}
|
2009-08-26 23:45:00 -07:00
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
void OSSBackendFactory::deinit()
|
2015-12-17 19:20:08 -06:00
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
PlaybackDevices.clear();
|
|
|
|
CaptureDevices.clear();
|
2015-12-17 19:20:08 -06:00
|
|
|
}
|
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
bool OSSBackendFactory::querySupport(ALCbackend_Type type)
|
|
|
|
{ return (type == ALCbackend_Playback || type == ALCbackend_Capture); }
|
2015-12-17 19:20:08 -06:00
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
void OSSBackendFactory::probe(enum DevProbe type, std::string *outnames)
|
2009-08-27 06:09:33 -07:00
|
|
|
{
|
2018-11-14 19:16:50 -08:00
|
|
|
auto add_device = [outnames](const DevMap &entry) -> void
|
|
|
|
{
|
|
|
|
#ifdef HAVE_STAT
|
|
|
|
struct stat buf;
|
|
|
|
if(stat(entry.device_name.c_str(), &buf) == 0)
|
|
|
|
#endif
|
|
|
|
{
|
2018-11-15 04:24:33 -08:00
|
|
|
/* Includes null char. */
|
|
|
|
outnames->append(entry.name.c_str(), entry.name.length()+1);
|
2018-11-14 19:16:50 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-06-14 04:02:58 -07:00
|
|
|
switch(type)
|
2010-01-09 06:40:50 -08:00
|
|
|
{
|
2011-06-14 04:02:58 -07:00
|
|
|
case ALL_DEVICE_PROBE:
|
2018-11-14 19:16:50 -08:00
|
|
|
PlaybackDevices.clear();
|
|
|
|
ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
|
|
|
|
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
2016-12-01 18:26:18 -08:00
|
|
|
break;
|
2011-06-14 04:02:58 -07:00
|
|
|
|
|
|
|
case CAPTURE_DEVICE_PROBE:
|
2018-11-14 19:16:50 -08:00
|
|
|
CaptureDevices.clear();
|
|
|
|
ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
|
|
|
|
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
2018-09-07 22:02:37 -07:00
|
|
|
break;
|
|
|
|
}
|
2009-08-27 06:09:33 -07:00
|
|
|
}
|
2013-11-02 15:42:45 -07:00
|
|
|
|
2018-11-15 23:02:26 -08:00
|
|
|
ALCbackend *OSSBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
|
2013-11-02 15:42:45 -07:00
|
|
|
{
|
|
|
|
if(type == ALCbackend_Playback)
|
|
|
|
{
|
|
|
|
ALCplaybackOSS *backend;
|
2015-05-18 16:39:44 -07:00
|
|
|
NEW_OBJ(backend, ALCplaybackOSS)(device);
|
2018-11-12 23:49:11 -08:00
|
|
|
if(!backend) return nullptr;
|
2013-11-02 15:42:45 -07:00
|
|
|
return STATIC_CAST(ALCbackend, backend);
|
|
|
|
}
|
|
|
|
if(type == ALCbackend_Capture)
|
|
|
|
{
|
|
|
|
ALCcaptureOSS *backend;
|
2015-05-18 16:39:44 -07:00
|
|
|
NEW_OBJ(backend, ALCcaptureOSS)(device);
|
2018-11-12 23:49:11 -08:00
|
|
|
if(!backend) return nullptr;
|
2013-11-02 15:42:45 -07:00
|
|
|
return STATIC_CAST(ALCbackend, backend);
|
|
|
|
}
|
|
|
|
|
2018-11-12 23:49:11 -08:00
|
|
|
return nullptr;
|
2013-11-02 15:42:45 -07:00
|
|
|
}
|