early-access version 2291

This commit is contained in:
pineappleEA
2021-12-08 07:33:31 +01:00
parent 02705a572c
commit 58d4c17ba4
61 changed files with 9377 additions and 4730 deletions
+386 -306
View File
@@ -10,25 +10,25 @@
* accompanying file LICENSE for details.
*/
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include "cubeb_mixer.h"
#include "cubeb_strings.h"
#include "cubeb-internal.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/types.h>
#include <unistd.h>
/* Supported well by most hardware. */
#ifndef OSS_PREFER_RATE
@@ -55,25 +55,25 @@
#define ENV_AUDIO_DEVICE "AUDIO_DEVICE"
#ifndef OSS_MAX_CHANNELS
# if defined(__FreeBSD__) || defined(__DragonFly__)
#if defined(__FreeBSD__) || defined(__DragonFly__)
/*
* The current maximum number of channels supported
* on FreeBSD is 8.
*
* Reference: FreeBSD 12.1-RELEASE
*/
# define OSS_MAX_CHANNELS (8)
# elif defined(__sun__)
#define OSS_MAX_CHANNELS (8)
#elif defined(__sun__)
/*
* The current maximum number of channels supported
* on Illumos is 16.
*
* Reference: PSARC 2008/318
*/
# define OSS_MAX_CHANNELS (16)
# else
# define OSS_MAX_CHANNELS (2)
# endif
#define OSS_MAX_CHANNELS (16)
#else
#define OSS_MAX_CHANNELS (2)
#endif
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
@@ -89,13 +89,16 @@ struct cubeb {
/* Our intern string store */
pthread_mutex_t mutex; /* protects devid_strs */
cubeb_strings *devid_strs;
cubeb_strings * devid_strs;
};
struct oss_stream {
oss_devnode_t name;
int fd;
void * buf;
unsigned int nfr; /* Number of frames allocated */
unsigned int nfrags;
unsigned int bufframes;
struct stream_info {
int channels;
@@ -112,29 +115,26 @@ struct cubeb_stream {
struct cubeb * context;
void * user_ptr;
pthread_t thread;
bool doorbell; /* (m) */
bool doorbell; /* (m) */
pthread_cond_t doorbell_cv; /* (m) */
pthread_cond_t stopped_cv; /* (m) */
pthread_cond_t stopped_cv; /* (m) */
pthread_mutex_t mtx; /* Members protected by this should be marked (m) */
bool thread_created; /* (m) */
bool running; /* (m) */
bool destroying; /* (m) */
cubeb_state state; /* (m) */
bool running; /* (m) */
bool destroying; /* (m) */
cubeb_state state; /* (m) */
float volume /* (m) */;
struct oss_stream play;
struct oss_stream record;
cubeb_data_callback data_cb;
cubeb_state_callback state_cb;
uint64_t frames_written /* (m) */;
unsigned int nfr; /* Number of frames allocated */
unsigned int nfrags;
unsigned int bufframes;
};
static char const *
oss_cubeb_devid_intern(cubeb *context, char const * devid)
oss_cubeb_devid_intern(cubeb * context, char const * devid)
{
char const *is;
char const * is;
pthread_mutex_lock(&context->mutex);
is = cubeb_strings_intern(context->devid_strs, devid);
pthread_mutex_unlock(&context->mutex);
@@ -142,7 +142,8 @@ oss_cubeb_devid_intern(cubeb *context, char const * devid)
}
int
oss_init(cubeb **context, char const *context_name) {
oss_init(cubeb ** context, char const * context_name)
{
cubeb * c;
(void)context_name;
@@ -211,7 +212,7 @@ oss_get_min_latency(cubeb * context, cubeb_stream_params params,
}
static void
oss_free_cubeb_device_info_strings(cubeb_device_info *cdi)
oss_free_cubeb_device_info_strings(cubeb_device_info * cdi)
{
free((char *)cdi->device_id);
free((char *)cdi->friendly_name);
@@ -230,8 +231,8 @@ oss_free_cubeb_device_info_strings(cubeb_device_info *cdi)
* Return 0 if OK, otherwise 1.
*/
static int
oss_probe_open(const char *dsppath, cubeb_device_type type,
int *fdp, oss_audioinfo *resai)
oss_probe_open(const char * dsppath, cubeb_device_type type, int * fdp,
oss_audioinfo * resai)
{
oss_audioinfo ai;
int error;
@@ -258,81 +259,81 @@ oss_probe_open(const char *dsppath, cubeb_device_type type,
struct sndstat_info {
oss_devnode_t devname;
const char *desc;
const char * desc;
cubeb_device_type type;
int preferred;
};
static int
oss_sndstat_line_parse(char *line, int is_ud, struct sndstat_info *sinfo)
oss_sndstat_line_parse(char * line, int is_ud, struct sndstat_info * sinfo)
{
char *matchptr = line, *n = NULL;
struct sndstat_info res;
char *matchptr = line, *n = NULL;
struct sndstat_info res;
memset(&res, 0, sizeof(res));
memset(&res, 0, sizeof(res));
n = strchr(matchptr, ':');
if (n == NULL)
n = strchr(matchptr, ':');
if (n == NULL)
goto fail;
if (is_ud == 0) {
unsigned int devunit;
if (sscanf(matchptr, "pcm%u: ", &devunit) < 1)
goto fail;
if (is_ud == 0) {
unsigned int devunit;
if (sscanf(matchptr, "pcm%u: ", &devunit) < 1)
goto fail;
if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1)
goto fail;
} else {
if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/")))
goto fail;
strlcpy(res.devname, "/dev/", sizeof(res.devname));
strncat(res.devname, matchptr, n - matchptr);
}
matchptr = n + 1;
n = strchr(matchptr, '<');
if (n == NULL)
if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1)
goto fail;
matchptr = n + 1;
n = strrchr(matchptr, '>');
if (n == NULL)
} else {
if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/")))
goto fail;
*n = 0;
res.desc = matchptr;
matchptr = n + 1;
n = strchr(matchptr, '(');
if (n == NULL)
goto fail;
matchptr = n + 1;
n = strrchr(matchptr, ')');
if (n == NULL)
goto fail;
*n = 0;
if (!isdigit(matchptr[0])) {
if (strstr(matchptr, "play") != NULL)
res.type |= CUBEB_DEVICE_TYPE_OUTPUT;
if (strstr(matchptr, "rec") != NULL)
res.type |= CUBEB_DEVICE_TYPE_INPUT;
} else {
int p, r;
if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2)
goto fail;
if (p > 0)
res.type |= CUBEB_DEVICE_TYPE_OUTPUT;
if (r > 0)
res.type |= CUBEB_DEVICE_TYPE_INPUT;
}
matchptr = n + 1;
if (strstr(matchptr, "default") != NULL)
res.preferred = 1;
strlcpy(res.devname, "/dev/", sizeof(res.devname));
strncat(res.devname, matchptr, n - matchptr);
}
matchptr = n + 1;
*sinfo = res;
return 0;
n = strchr(matchptr, '<');
if (n == NULL)
goto fail;
matchptr = n + 1;
n = strrchr(matchptr, '>');
if (n == NULL)
goto fail;
*n = 0;
res.desc = matchptr;
matchptr = n + 1;
n = strchr(matchptr, '(');
if (n == NULL)
goto fail;
matchptr = n + 1;
n = strrchr(matchptr, ')');
if (n == NULL)
goto fail;
*n = 0;
if (!isdigit(matchptr[0])) {
if (strstr(matchptr, "play") != NULL)
res.type |= CUBEB_DEVICE_TYPE_OUTPUT;
if (strstr(matchptr, "rec") != NULL)
res.type |= CUBEB_DEVICE_TYPE_INPUT;
} else {
int p, r;
if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2)
goto fail;
if (p > 0)
res.type |= CUBEB_DEVICE_TYPE_OUTPUT;
if (r > 0)
res.type |= CUBEB_DEVICE_TYPE_INPUT;
}
matchptr = n + 1;
if (strstr(matchptr, "default") != NULL)
res.preferred = 1;
*sinfo = res;
return 0;
fail:
return 1;
return 1;
}
/*
@@ -344,10 +345,10 @@ static int
oss_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
cubeb_device_info *devinfop = NULL;
char *line = NULL;
cubeb_device_info * devinfop = NULL;
char * line = NULL;
size_t linecap = 0;
FILE *sndstatfp = NULL;
FILE * sndstatfp = NULL;
int collection_cnt = 0;
int is_ud = 0;
int skipall = 0;
@@ -360,7 +361,7 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
if (sndstatfp == NULL)
goto fail;
while (getline(&line, &linecap, sndstatfp) > 0) {
const char *devid = NULL;
const char * devid = NULL;
struct sndstat_info sinfo;
oss_audioinfo ai;
@@ -373,7 +374,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
skipall = 0;
continue;
}
if (!strncmp(line, SNDSTAT_USER_BEGIN_STR, strlen(SNDSTAT_USER_BEGIN_STR))) {
if (!strncmp(line, SNDSTAT_USER_BEGIN_STR,
strlen(SNDSTAT_USER_BEGIN_STR))) {
is_ud = 1;
skipall = 0;
continue;
@@ -433,8 +435,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
collection_cnt++;
void *newp = reallocarray(devinfop, collection_cnt + 1,
sizeof(cubeb_device_info));
void * newp =
reallocarray(devinfop, collection_cnt + 1, sizeof(cubeb_device_info));
if (newp == NULL)
goto fail;
devinfop = newp;
@@ -464,7 +466,7 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
{
oss_sysinfo si;
int error, i;
cubeb_device_info *devinfop = NULL;
cubeb_device_info * devinfop = NULL;
int collection_cnt = 0;
int mixer_fd = -1;
@@ -476,7 +478,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
error = ioctl(mixer_fd, SNDCTL_SYSINFO, &si);
if (error) {
LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno);
LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d",
OSS_DEFAULT_MIXER, errno);
goto fail;
}
@@ -487,8 +490,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
collection->count = 0;
for (i = 0; i < si.numaudios; i++) {
oss_audioinfo ai;
cubeb_device_info cdi = { 0 };
const char *devid = NULL;
cubeb_device_info cdi = {0};
const char * devid = NULL;
ai.dev = i;
error = ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai);
@@ -574,24 +577,24 @@ static unsigned int
oss_chn_from_cubeb(cubeb_channel chn)
{
switch (chn) {
case CHANNEL_FRONT_LEFT:
return CHID_L;
case CHANNEL_FRONT_RIGHT:
return CHID_R;
case CHANNEL_FRONT_CENTER:
return CHID_C;
case CHANNEL_LOW_FREQUENCY:
return CHID_LFE;
case CHANNEL_BACK_LEFT:
return CHID_LR;
case CHANNEL_BACK_RIGHT:
return CHID_RR;
case CHANNEL_SIDE_LEFT:
return CHID_LS;
case CHANNEL_SIDE_RIGHT:
return CHID_RS;
default:
return CHID_UNDEF;
case CHANNEL_FRONT_LEFT:
return CHID_L;
case CHANNEL_FRONT_RIGHT:
return CHID_R;
case CHANNEL_FRONT_CENTER:
return CHID_C;
case CHANNEL_LOW_FREQUENCY:
return CHID_LFE;
case CHANNEL_BACK_LEFT:
return CHID_LR;
case CHANNEL_BACK_RIGHT:
return CHID_RR;
case CHANNEL_SIDE_LEFT:
return CHID_LS;
case CHANNEL_SIDE_RIGHT:
return CHID_RS;
default:
return CHID_UNDEF;
}
}
@@ -648,7 +651,8 @@ oss_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params,
return CUBEB_ERROR;
}
/* Mono layout is an exception */
if (params->layout != CUBEB_LAYOUT_UNDEFINED && params->layout != CUBEB_LAYOUT_MONO) {
if (params->layout != CUBEB_LAYOUT_UNDEFINED &&
params->layout != CUBEB_LAYOUT_MONO) {
chnorder = oss_cubeb_layout_to_chnorder(params->layout);
if (ioctl(fd, SNDCTL_DSP_SET_CHNORDER, &chnorder) == -1)
LOG("Non-fatal error %d occured when setting channel order.", errno);
@@ -741,73 +745,191 @@ oss_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol)
}
}
static int
oss_get_rec_frames(cubeb_stream * s, unsigned int nframes)
{
size_t rem = nframes * s->record.frame_size;
size_t read_ofs = 0;
while (rem > 0) {
ssize_t n;
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, rem)) <
0) {
if (errno == EINTR)
continue;
return CUBEB_ERROR;
}
read_ofs += n;
rem -= n;
}
return 0;
}
static int
oss_put_play_frames(cubeb_stream * s, unsigned int nframes)
{
size_t rem = nframes * s->play.frame_size;
size_t write_ofs = 0;
while (rem > 0) {
ssize_t n;
if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, rem)) < 0) {
if (errno == EINTR)
continue;
return CUBEB_ERROR;
}
pthread_mutex_lock(&s->mtx);
s->frames_written += n / s->play.frame_size;
pthread_mutex_unlock(&s->mtx);
write_ofs += n;
rem -= n;
}
return 0;
}
static int
oss_wait_fds_for_space(cubeb_stream * s, long * nfrp)
{
audio_buf_info bi;
struct pollfd pfds[2];
long nfr, tnfr;
int i;
assert(s->play.fd != -1 || s->record.fd != -1);
pfds[0].events = POLLOUT | POLLHUP;
pfds[0].revents = 0;
pfds[0].fd = s->play.fd;
pfds[1].events = POLLIN | POLLHUP;
pfds[1].revents = 0;
pfds[1].fd = s->record.fd;
retry:
nfr = LONG_MAX;
if (poll(pfds, 2, 1000) == -1) {
return CUBEB_ERROR;
}
for (i = 0; i < 2; i++) {
if (pfds[i].revents & POLLHUP) {
return CUBEB_ERROR;
}
}
if (s->play.fd != -1) {
if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) {
return CUBEB_STATE_ERROR;
}
tnfr = bi.bytes / s->play.frame_size;
if (tnfr <= 0) {
/* too little space - stop polling record, if any */
pfds[0].fd = s->play.fd;
pfds[1].fd = -1;
goto retry;
} else if (tnfr > (long)s->play.bufframes) {
/* too many frames available - limit */
tnfr = (long)s->play.bufframes;
}
if (nfr > tnfr) {
nfr = tnfr;
}
}
if (s->record.fd != -1) {
if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi) == -1) {
return CUBEB_STATE_ERROR;
}
tnfr = bi.bytes / s->record.frame_size;
if (tnfr <= 0) {
/* too little space - stop polling playback, if any */
pfds[0].fd = -1;
pfds[1].fd = s->record.fd;
goto retry;
} else if (tnfr > (long)s->record.bufframes) {
/* too many frames available - limit */
tnfr = (long)s->record.bufframes;
}
if (nfr > tnfr) {
nfr = tnfr;
}
}
*nfrp = nfr;
return 0;
}
/* 1 - Stopped by cubeb_stream_stop, otherwise 0 */
static int
oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
oss_audio_loop(cubeb_stream * s, cubeb_state * new_state)
{
cubeb_state state = CUBEB_STATE_STOPPED;
int trig = 0;
int drain = 0;
struct pollfd pfds[2];
unsigned int ppending, rpending;
int trig = 0, drain = 0;
const bool play_on = s->play.fd != -1, record_on = s->record.fd != -1;
long nfr = 0;
pfds[0].fd = s->play.fd;
pfds[0].events = POLLOUT;
pfds[1].fd = s->record.fd;
pfds[1].events = POLLIN;
ppending = 0;
rpending = s->bufframes;
if (s->record.fd != -1) {
if (record_on) {
if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) {
LOG("Error %d occured when setting trigger on record fd", errno);
state = CUBEB_STATE_ERROR;
goto breakdown;
}
trig |= PCM_ENABLE_INPUT;
if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) {
memset(s->record.buf, 0, s->record.bufframes * s->record.frame_size);
if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) {
LOG("Error %d occured when setting trigger on record fd", errno);
state = CUBEB_STATE_ERROR;
goto breakdown;
}
memset(s->record.buf, 0, s->bufframes * s->record.frame_size);
}
if (!play_on && !record_on) {
/*
* Stop here if the stream is not play & record stream,
* play-only stream or record-only stream
*/
goto breakdown;
}
while (1) {
long nfr = 0;
pthread_mutex_lock(&s->mtx);
if (!s->running || s->destroying) {
pthread_mutex_unlock(&s->mtx);
break;
}
pthread_mutex_unlock(&s->mtx);
if (s->play.fd == -1 && s->record.fd == -1) {
/*
* Stop here if the stream is not play & record stream,
* play-only stream or record-only stream
*/
goto breakdown;
}
while ((s->bufframes - ppending) >= s->nfr && rpending >= s->nfr) {
long n = ((s->bufframes - ppending) < rpending) ? s->bufframes - ppending : rpending;
char *rptr = NULL, *pptr = NULL;
if (s->record.fd != -1)
rptr = (char *)s->record.buf;
if (s->play.fd != -1)
pptr = (char *)s->play.buf + ppending * s->play.frame_size;
if (s->record.fd != -1 && s->record.floating) {
oss_linear32_to_float(s->record.buf, s->record.info.channels * n);
long got = 0;
if (nfr > 0) {
if (record_on) {
if (oss_get_rec_frames(s, nfr) == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
if (s->record.floating) {
oss_linear32_to_float(s->record.buf, s->record.info.channels * nfr);
}
}
nfr = s->data_cb(s, s->user_ptr, rptr, pptr, n);
if (nfr == CUBEB_ERROR) {
got = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, nfr);
if (got == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
if (pptr) {
if (got < nfr) {
if (s->play.fd != -1) {
drain = 1;
} else {
/*
* This is a record-only stream and number of frames
* returned from data_cb() is smaller than number
* of frames required to read. Stop here.
*/
state = CUBEB_STATE_STOPPED;
goto breakdown;
}
}
if (got > 0 && play_on) {
float vol;
pthread_mutex_lock(&s->mtx);
@@ -815,102 +937,24 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
pthread_mutex_unlock(&s->mtx);
if (s->play.floating) {
oss_float_to_linear32(pptr, s->play.info.channels * nfr, vol);
oss_float_to_linear32(s->play.buf, s->play.info.channels * got, vol);
} else {
oss_linear16_set_vol((int16_t *)pptr, s->play.info.channels * nfr, vol);
oss_linear16_set_vol((int16_t *)s->play.buf,
s->play.info.channels * got, vol);
}
}
if (pptr) {
ppending += nfr;
assert(ppending <= s->bufframes);
}
if (rptr) {
assert(rpending >= nfr);
rpending -= nfr;
memmove(rptr, rptr + nfr * s->record.frame_size,
(s->bufframes - nfr) * s->record.frame_size);
}
if (nfr < n) {
if (s->play.fd != -1) {
drain = 1;
break;
} else {
/*
* This is a record-only stream and number of frames
* returned from data_cb() is smaller than number
* of frames required to read. Stop here.
*/
state = CUBEB_STATE_STOPPED;
goto breakdown;
}
}
}
ssize_t n, frames;
int nfds;
pfds[0].revents = 0;
pfds[1].revents = 0;
nfds = poll(pfds, 2, 1000);
if (nfds == -1) {
if (errno == EINTR)
continue;
LOG("Error %d occured when polling playback and record fd", errno);
state = CUBEB_STATE_ERROR;
goto breakdown;
} else if (nfds == 0)
continue;
if ((pfds[0].revents & (POLLERR | POLLHUP)) ||
(pfds[1].revents & (POLLERR | POLLHUP))) {
LOG("Error occured on playback, record fds");
state = CUBEB_STATE_ERROR;
goto breakdown;
}
if (pfds[0].revents) {
while (ppending > 0) {
size_t bytes = ppending * s->play.frame_size;
if ((n = write(s->play.fd, (uint8_t *)s->play.buf, bytes)) < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN) {
if (drain)
continue;
break;
}
if (oss_put_play_frames(s, got) == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
frames = n / s->play.frame_size;
pthread_mutex_lock(&s->mtx);
s->frames_written += frames;
pthread_mutex_unlock(&s->mtx);
ppending -= frames;
memmove(s->play.buf, (uint8_t *)s->play.buf + n,
(s->bufframes - frames) * s->play.frame_size);
}
if (drain) {
state = CUBEB_STATE_DRAINED;
goto breakdown;
}
}
if (pfds[1].revents) {
while (s->bufframes - rpending > 0) {
size_t bytes = (s->bufframes - rpending) * s->record.frame_size;
size_t read_ofs = rpending * s->record.frame_size;
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, bytes)) < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN)
break;
state = CUBEB_STATE_ERROR;
goto breakdown;
}
frames = n / s->record.frame_size;
rpending += frames;
}
}
if (drain) {
state = CUBEB_STATE_DRAINED;
if (oss_wait_fds_for_space(s, &nfr) != 0) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
}
@@ -926,9 +970,9 @@ breakdown:
}
static void *
oss_io_routine(void *arg)
oss_io_routine(void * arg)
{
cubeb_stream *s = arg;
cubeb_stream * s = arg;
cubeb_state new_state;
int stopped;
@@ -969,9 +1013,10 @@ static inline int
oss_calc_frag_shift(unsigned int frames, unsigned int frame_size)
{
int n = 4;
int blksize = (frames * frame_size + OSS_NFRAGS - 1) / OSS_NFRAGS;
while ((1 << n) < blksize)
int blksize = frames * frame_size;
while ((1 << n) < blksize) {
n++;
}
return n;
}
@@ -982,22 +1027,17 @@ oss_get_frag_params(unsigned int shift)
}
static int
oss_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
oss_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
unsigned int latency_frames, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
int ret = CUBEB_OK;
unsigned int playnfr = 0, recnfr = 0;
cubeb_stream *s = NULL;
const char *defdsp;
cubeb_stream * s = NULL;
const char * defdsp;
if (!(defdsp = getenv(ENV_AUDIO_DEVICE)) || *defdsp == '\0')
defdsp = OSS_DEFAULT_DEVICE;
@@ -1009,7 +1049,6 @@ oss_stream_init(cubeb * context,
}
s->state = CUBEB_STATE_STOPPED;
s->record.fd = s->play.fd = -1;
s->nfr = latency_frames;
if (input_device != NULL) {
strlcpy(s->record.name, input_device, sizeof(s->record.name));
} else {
@@ -1030,26 +1069,35 @@ oss_stream_init(cubeb * context,
nb_channels = cubeb_channel_layout_nb_channels(input_stream_params->layout);
if (input_stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
nb_channels != input_stream_params->channels) {
LOG("input_stream_params->layout does not match input_stream_params->channels");
LOG("input_stream_params->layout does not match "
"input_stream_params->channels");
ret = CUBEB_ERROR_INVALID_PARAMETER;
goto error;
}
if (s->record.fd == -1) {
if ((s->record.fd = open(s->record.name, O_RDONLY | O_NONBLOCK)) == -1) {
LOG("Audio device \"%s\" could not be opened as read-only",
s->record.name);
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) {
LOG("Audio device \"%s\" could not be opened as read-only",
s->record.name);
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
if ((ret = oss_copy_params(s->record.fd, s, input_stream_params,
&s->record.info)) != CUBEB_OK) {
LOG("Setting record params failed");
goto error;
}
s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
s->record.frame_size = s->record.info.channels * (s->record.info.precision / 8);
recnfr = (1 << oss_calc_frag_shift(s->nfr, s->record.frame_size)) / s->record.frame_size;
s->record.floating =
(input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
s->record.frame_size =
s->record.info.channels * (s->record.info.precision / 8);
s->record.nfrags = OSS_NFRAGS;
s->record.nfr = latency_frames / OSS_NFRAGS;
s->record.bufframes = s->record.nfrags * s->record.nfr;
uint32_t minnfr;
oss_get_min_latency(context, *input_stream_params, &minnfr);
if (s->record.nfr < minnfr) {
s->record.nfr = minnfr;
s->record.nfrags = latency_frames / minnfr;
}
}
if (output_stream_params != NULL) {
unsigned int nb_channels;
@@ -1058,20 +1106,20 @@ oss_stream_init(cubeb * context,
ret = CUBEB_ERROR_NOT_SUPPORTED;
goto error;
}
nb_channels = cubeb_channel_layout_nb_channels(output_stream_params->layout);
nb_channels =
cubeb_channel_layout_nb_channels(output_stream_params->layout);
if (output_stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
nb_channels != output_stream_params->channels) {
LOG("output_stream_params->layout does not match output_stream_params->channels");
LOG("output_stream_params->layout does not match "
"output_stream_params->channels");
ret = CUBEB_ERROR_INVALID_PARAMETER;
goto error;
}
if (s->play.fd == -1) {
if ((s->play.fd = open(s->play.name, O_WRONLY | O_NONBLOCK)) == -1) {
LOG("Audio device \"%s\" could not be opened as write-only",
s->play.name);
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) {
LOG("Audio device \"%s\" could not be opened as write-only",
s->play.name);
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
if ((ret = oss_copy_params(s->play.fd, s, output_stream_params,
&s->play.info)) != CUBEB_OK) {
@@ -1080,23 +1128,55 @@ oss_stream_init(cubeb * context,
}
s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8);
playnfr = (1 << oss_calc_frag_shift(s->nfr, s->play.frame_size)) / s->play.frame_size;
s->play.nfrags = OSS_NFRAGS;
s->play.nfr = latency_frames / OSS_NFRAGS;
uint32_t minnfr;
oss_get_min_latency(context, *output_stream_params, &minnfr);
if (s->play.nfr < minnfr) {
s->play.nfr = minnfr;
s->play.nfrags = latency_frames / minnfr;
}
s->play.bufframes = s->play.nfrags * s->play.nfr;
}
/* Use the largest nframes among playing and recording streams */
s->nfr = (playnfr > recnfr) ? playnfr : recnfr;
s->nfrags = OSS_NFRAGS;
s->bufframes = s->nfr * s->nfrags;
if (s->play.fd != -1) {
int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->play.frame_size));
if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
int frag = oss_get_frag_params(
oss_calc_frag_shift(s->play.nfr, s->play.frame_size));
if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
frag);
audio_buf_info bi;
if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi))
LOG("Failed to get play fd's buffer info.");
else {
s->play.nfr = bi.fragsize / s->play.frame_size;
s->play.nfrags = bi.fragments;
s->play.bufframes = s->play.nfr * s->play.nfrags;
}
int lw = s->play.frame_size;
if (ioctl(s->play.fd, SNDCTL_DSP_LOW_WATER, &lw))
LOG("Audio device \"%s\" (play) could not set trigger threshold",
s->play.name);
}
if (s->record.fd != -1) {
int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->record.frame_size));
int frag = oss_get_frag_params(
oss_calc_frag_shift(s->record.nfr, s->record.frame_size));
if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
frag);
audio_buf_info bi;
if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi))
LOG("Failed to get record fd's buffer info.");
else {
s->record.nfr = bi.fragsize / s->record.frame_size;
s->record.nfrags = bi.fragments;
s->record.bufframes = s->record.nfr * s->record.nfrags;
}
int lw = s->record.frame_size;
if (ioctl(s->record.fd, SNDCTL_DSP_LOW_WATER, &lw))
LOG("Audio device \"%s\" (record) could not set trigger threshold",
s->record.name);
}
s->context = context;
s->volume = 1.0;
@@ -1119,13 +1199,14 @@ oss_stream_init(cubeb * context,
s->doorbell = false;
if (s->play.fd != -1) {
if ((s->play.buf = calloc(s->bufframes, s->play.frame_size)) == NULL) {
if ((s->play.buf = calloc(s->play.bufframes, s->play.frame_size)) == NULL) {
ret = CUBEB_ERROR;
goto error;
}
}
if (s->record.fd != -1) {
if ((s->record.buf = calloc(s->bufframes, s->record.frame_size)) == NULL) {
if ((s->record.buf = calloc(s->record.bufframes, s->record.frame_size)) ==
NULL) {
ret = CUBEB_ERROR;
goto error;
}
@@ -1219,10 +1300,10 @@ oss_get_current_device(cubeb_stream * stream, cubeb_device ** const device)
if (*device == NULL) {
return CUBEB_ERROR;
}
(*device)->input_name = stream->record.fd != -1 ?
strdup(stream->record.name) : NULL;
(*device)->output_name = stream->play.fd != -1 ?
strdup(stream->play.name) : NULL;
(*device)->input_name =
stream->record.fd != -1 ? strdup(stream->record.name) : NULL;
(*device)->output_name =
stream->play.fd != -1 ? strdup(stream->play.name) : NULL;
return CUBEB_OK;
}
@@ -1249,7 +1330,6 @@ static struct cubeb_ops const oss_ops = {
.stream_destroy = oss_stream_destroy,
.stream_start = oss_stream_start,
.stream_stop = oss_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = oss_stream_get_position,
.stream_get_latency = oss_stream_get_latency,
.stream_get_input_latency = NULL,