mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Get printvideo audio working on Windows and MacOS
This commit is contained in:
parent
07fde68d52
commit
5d3b91d8b9
18 changed files with 93470 additions and 212 deletions
2
Makefile
2
Makefile
|
@ -278,6 +278,7 @@ include libc/x/BUILD.mk # │
|
||||||
include dsp/scale/BUILD.mk # │
|
include dsp/scale/BUILD.mk # │
|
||||||
include dsp/mpeg/BUILD.mk # │
|
include dsp/mpeg/BUILD.mk # │
|
||||||
include dsp/tty/BUILD.mk # │
|
include dsp/tty/BUILD.mk # │
|
||||||
|
include dsp/audio/BUILD.mk # │
|
||||||
include dsp/BUILD.mk # │
|
include dsp/BUILD.mk # │
|
||||||
include third_party/stb/BUILD.mk # │
|
include third_party/stb/BUILD.mk # │
|
||||||
include third_party/mbedtls/BUILD.mk # │
|
include third_party/mbedtls/BUILD.mk # │
|
||||||
|
@ -439,6 +440,7 @@ COSMOPOLITAN_OBJECTS = \
|
||||||
THIRD_PARTY_OPENMP \
|
THIRD_PARTY_OPENMP \
|
||||||
TOOL_ARGS \
|
TOOL_ARGS \
|
||||||
NET_HTTP \
|
NET_HTTP \
|
||||||
|
DSP_AUDIO \
|
||||||
LIBC_SOCK \
|
LIBC_SOCK \
|
||||||
LIBC_NT_WS2_32 \
|
LIBC_NT_WS2_32 \
|
||||||
LIBC_NT_IPHLPAPI \
|
LIBC_NT_IPHLPAPI \
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
|
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
|
||||||
|
|
||||||
.PHONY: o/$(MODE)/dsp
|
.PHONY: o/$(MODE)/dsp
|
||||||
o/$(MODE)/dsp: o/$(MODE)/dsp/core \
|
o/$(MODE)/dsp: o/$(MODE)/dsp/audio \
|
||||||
|
o/$(MODE)/dsp/core \
|
||||||
o/$(MODE)/dsp/mpeg \
|
o/$(MODE)/dsp/mpeg \
|
||||||
o/$(MODE)/dsp/scale \
|
o/$(MODE)/dsp/scale \
|
||||||
o/$(MODE)/dsp/tty
|
o/$(MODE)/dsp/tty
|
||||||
|
|
56
dsp/audio/BUILD.mk
Normal file
56
dsp/audio/BUILD.mk
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||||
|
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
|
||||||
|
|
||||||
|
PKGS += DSP_AUDIO
|
||||||
|
|
||||||
|
DSP_AUDIO_ARTIFACTS += DSP_AUDIO_A
|
||||||
|
DSP_AUDIO = $(DSP_AUDIO_A_DEPS) $(DSP_AUDIO_A)
|
||||||
|
DSP_AUDIO_A = o/$(MODE)/dsp/audio/audio.a
|
||||||
|
DSP_AUDIO_A_FILES := $(wildcard dsp/audio/*)
|
||||||
|
DSP_AUDIO_A_HDRS = $(filter %.h,$(DSP_AUDIO_A_FILES)) dsp/audio/cosmoaudio/cosmoaudio.h
|
||||||
|
DSP_AUDIO_A_SRCS = $(filter %.c,$(DSP_AUDIO_A_FILES))
|
||||||
|
|
||||||
|
DSP_AUDIO_A_DATA = \
|
||||||
|
dsp/audio/cosmoaudio/miniaudio.h \
|
||||||
|
dsp/audio/cosmoaudio/cosmoaudio.c \
|
||||||
|
dsp/audio/cosmoaudio/cosmoaudio.h \
|
||||||
|
dsp/audio/cosmoaudio/cosmoaudio.dll \
|
||||||
|
|
||||||
|
DSP_AUDIO_A_OBJS = \
|
||||||
|
$(DSP_AUDIO_A_SRCS:%.c=o/$(MODE)/%.o) \
|
||||||
|
$(DSP_AUDIO_A_DATA:%=o/$(MODE)/%.zip.o) \
|
||||||
|
|
||||||
|
DSP_AUDIO_A_CHECKS = \
|
||||||
|
$(DSP_AUDIO_A).pkg \
|
||||||
|
$(DSP_AUDIO_A_HDRS:%=o/$(MODE)/%.ok)
|
||||||
|
|
||||||
|
DSP_AUDIO_A_DIRECTDEPS = \
|
||||||
|
LIBC_CALLS \
|
||||||
|
LIBC_DLOPEN \
|
||||||
|
LIBC_INTRIN \
|
||||||
|
LIBC_NEXGEN32E \
|
||||||
|
LIBC_STR \
|
||||||
|
LIBC_SYSV \
|
||||||
|
LIBC_PROC \
|
||||||
|
LIBC_THREAD \
|
||||||
|
|
||||||
|
DSP_AUDIO_A_DEPS := \
|
||||||
|
$(call uniq,$(foreach x,$(DSP_AUDIO_A_DIRECTDEPS),$($(x))))
|
||||||
|
|
||||||
|
$(DSP_AUDIO_A): dsp/audio/ \
|
||||||
|
$(DSP_AUDIO_A).pkg \
|
||||||
|
$(DSP_AUDIO_A_OBJS)
|
||||||
|
|
||||||
|
$(DSP_AUDIO_A).pkg: \
|
||||||
|
$(DSP_AUDIO_A_OBJS) \
|
||||||
|
$(foreach x,$(DSP_AUDIO_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||||
|
|
||||||
|
DSP_AUDIO_LIBS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)))
|
||||||
|
DSP_AUDIO_SRCS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)_SRCS))
|
||||||
|
DSP_AUDIO_HDRS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)_HDRS))
|
||||||
|
DSP_AUDIO_CHECKS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)_CHECKS))
|
||||||
|
DSP_AUDIO_OBJS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)_OBJS))
|
||||||
|
$(DSP_AUDIO_OBJS): $(BUILD_FILES) dsp/audio/BUILD.mk
|
||||||
|
|
||||||
|
.PHONY: o/$(MODE)/dsp/audio
|
||||||
|
o/$(MODE)/dsp/audio: $(DSP_AUDIO_CHECKS)
|
353
dsp/audio/audio.c
Normal file
353
dsp/audio/audio.c
Normal file
|
@ -0,0 +1,353 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2024 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "dsp/audio/cosmoaudio/cosmoaudio.h"
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/struct/stat.h"
|
||||||
|
#include "libc/calls/struct/timespec.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/dlopen/dlfcn.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
|
#include "libc/proc/posix_spawn.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/sysv/consts/o.h"
|
||||||
|
#include "libc/temp.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
__static_yoink("dsp/audio/cosmoaudio/miniaudio.h");
|
||||||
|
__static_yoink("dsp/audio/cosmoaudio/cosmoaudio.h");
|
||||||
|
__static_yoink("dsp/audio/cosmoaudio/cosmoaudio.c");
|
||||||
|
__static_yoink("dsp/audio/cosmoaudio/cosmoaudio.dll");
|
||||||
|
|
||||||
|
static const struct Source {
|
||||||
|
const char *zip;
|
||||||
|
const char *name;
|
||||||
|
} srcs[] = {
|
||||||
|
{"/zip/dsp/audio/cosmoaudio/miniaudio.h", "miniaudio.h"},
|
||||||
|
{"/zip/dsp/audio/cosmoaudio/cosmoaudio.h", "cosmoaudio.h"},
|
||||||
|
{"/zip/dsp/audio/cosmoaudio/cosmoaudio.c", "cosmoaudio.c"}, // must last
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
pthread_once_t once;
|
||||||
|
typeof(cosmoaudio_open) *open;
|
||||||
|
typeof(cosmoaudio_close) *close;
|
||||||
|
typeof(cosmoaudio_write) *write;
|
||||||
|
typeof(cosmoaudio_read) *read;
|
||||||
|
} g_audio;
|
||||||
|
|
||||||
|
static const char *get_tmp_dir(void) {
|
||||||
|
const char *tmpdir;
|
||||||
|
if (!(tmpdir = getenv("TMPDIR")) || !*tmpdir)
|
||||||
|
if (!(tmpdir = getenv("HOME")) || !*tmpdir)
|
||||||
|
tmpdir = ".";
|
||||||
|
return tmpdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_app_dir(char *path, size_t size) {
|
||||||
|
strlcpy(path, get_tmp_dir(), size);
|
||||||
|
strlcat(path, "/.cosmo/", size);
|
||||||
|
if (makedirs(path, 0755))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_dso_path(char *path, size_t size) {
|
||||||
|
if (!get_app_dir(path, size))
|
||||||
|
return false;
|
||||||
|
strlcat(path, "cosmoaudio", size);
|
||||||
|
if (IsWindows()) {
|
||||||
|
strlcat(path, ".dll", size);
|
||||||
|
} else if (IsXnu()) {
|
||||||
|
strlcat(path, ".dylib", size);
|
||||||
|
} else {
|
||||||
|
strlcat(path, ".so", size);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_file_newer_than_time(const char *path, const char *other) {
|
||||||
|
struct stat st1, st2;
|
||||||
|
if (stat(path, &st1))
|
||||||
|
// PATH should always exist when calling this function
|
||||||
|
return -1;
|
||||||
|
if (stat(other, &st2)) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
// PATH should replace OTHER because OTHER doesn't exist yet
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// some other error happened, so we can't do anything
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// PATH should replace OTHER if PATH was modified more recently
|
||||||
|
return timespec_cmp(st1.st_mtim, st2.st_mtim) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_file_newer_than_bytes(const char *path, const char *other) {
|
||||||
|
int other_fd;
|
||||||
|
if ((other_fd = open(other, O_RDONLY | O_CLOEXEC)) == -1) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int path_fd;
|
||||||
|
if ((path_fd = open(path, O_RDONLY | O_CLOEXEC)) == -1) {
|
||||||
|
close(other_fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int res;
|
||||||
|
long i = 0;
|
||||||
|
for (;;) {
|
||||||
|
char path_buf[512];
|
||||||
|
ssize_t path_rc = pread(path_fd, path_buf, sizeof(path_buf), i);
|
||||||
|
if (path_rc == -1) {
|
||||||
|
res = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char other_buf[512];
|
||||||
|
ssize_t other_rc = pread(other_fd, other_buf, sizeof(other_buf), i);
|
||||||
|
if (other_rc == -1) {
|
||||||
|
res = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!path_rc || !other_rc) {
|
||||||
|
if (!path_rc && !other_rc)
|
||||||
|
res = false;
|
||||||
|
else
|
||||||
|
res = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size_t size = path_rc;
|
||||||
|
if (other_rc < path_rc)
|
||||||
|
size = other_rc;
|
||||||
|
if (memcmp(path_buf, other_buf, size)) {
|
||||||
|
res = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += size;
|
||||||
|
}
|
||||||
|
if (close(path_fd))
|
||||||
|
res = -1;
|
||||||
|
if (close(other_fd))
|
||||||
|
res = -1;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_file_newer_than(const char *path, const char *other) {
|
||||||
|
if (startswith(path, "/zip/"))
|
||||||
|
// to keep builds deterministic, embedded zip files always have
|
||||||
|
// the same timestamp from back in 2022 when it was implemented
|
||||||
|
return is_file_newer_than_bytes(path, other);
|
||||||
|
else
|
||||||
|
return is_file_newer_than_time(path, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool extract(const char *zip, const char *to) {
|
||||||
|
int fdin, fdout;
|
||||||
|
char stage[PATH_MAX];
|
||||||
|
strlcpy(stage, to, sizeof(stage));
|
||||||
|
if (strlcat(stage, ".XXXXXX", sizeof(stage)) >= sizeof(stage)) {
|
||||||
|
errno = ENAMETOOLONG;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((fdout = mkostemp(stage, O_CLOEXEC)) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((fdin = open(zip, O_RDONLY | O_CLOEXEC)) == -1) {
|
||||||
|
close(fdout);
|
||||||
|
unlink(stage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (copyfd(fdin, fdout, -1) == -1) {
|
||||||
|
close(fdin);
|
||||||
|
close(fdout);
|
||||||
|
unlink(stage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (close(fdout)) {
|
||||||
|
close(fdin);
|
||||||
|
unlink(stage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (close(fdin)) {
|
||||||
|
unlink(stage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rename(stage, to)) {
|
||||||
|
unlink(stage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool deploy(const char *dso) {
|
||||||
|
switch (is_file_newer_than("/zip/dsp/audio/cosmoaudio/cosmoaudio.dll", dso)) {
|
||||||
|
case 0:
|
||||||
|
return true;
|
||||||
|
case 1:
|
||||||
|
return extract("/zip/dsp/audio/cosmoaudio/cosmoaudio.dll", dso);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool build(const char *dso) {
|
||||||
|
|
||||||
|
// extract source code
|
||||||
|
char src[PATH_MAX];
|
||||||
|
bool needs_rebuild = false;
|
||||||
|
for (int i = 0; i < sizeof(srcs) / sizeof(*srcs); ++i) {
|
||||||
|
get_app_dir(src, PATH_MAX);
|
||||||
|
strlcat(src, srcs[i].name, sizeof(src));
|
||||||
|
switch (is_file_newer_than(srcs[i].zip, src)) {
|
||||||
|
case -1:
|
||||||
|
return false;
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
needs_rebuild = true;
|
||||||
|
if (!extract(srcs[i].zip, src))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine if we need to build
|
||||||
|
if (!needs_rebuild) {
|
||||||
|
switch (is_file_newer_than(src, dso)) {
|
||||||
|
case -1:
|
||||||
|
return false;
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
needs_rebuild = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile dynamic shared object
|
||||||
|
if (needs_rebuild) {
|
||||||
|
int fd;
|
||||||
|
char tmpdso[PATH_MAX];
|
||||||
|
strlcpy(tmpdso, dso, sizeof(tmpdso));
|
||||||
|
strlcat(tmpdso, ".XXXXXX", sizeof(tmpdso));
|
||||||
|
if ((fd = mkostemp(tmpdso, O_CLOEXEC)) != -1) {
|
||||||
|
close(fd);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char *args[] = {
|
||||||
|
"cc", //
|
||||||
|
"-I.", //
|
||||||
|
"-O2", //
|
||||||
|
"-fPIC", //
|
||||||
|
"-shared", //
|
||||||
|
"-pthread", //
|
||||||
|
"-DNDEBUG", //
|
||||||
|
IsAarch64() ? "-ffixed-x28" : "-DIGNORE1", //
|
||||||
|
src, //
|
||||||
|
"-o", //
|
||||||
|
tmpdso, //
|
||||||
|
"-ldl", //
|
||||||
|
"-lm", //
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
int pid, ws;
|
||||||
|
errno_t err = posix_spawnp(&pid, "cc", NULL, NULL, args, environ);
|
||||||
|
if (err)
|
||||||
|
return false;
|
||||||
|
while (waitpid(pid, &ws, 0) == -1) {
|
||||||
|
if (errno != EINTR)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ws)
|
||||||
|
return false;
|
||||||
|
if (rename(tmpdso, dso))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cosmoaudio_setup(void) {
|
||||||
|
void *handle;
|
||||||
|
if (!(handle = cosmo_dlopen("cosmoaudio.so", RTLD_LOCAL))) {
|
||||||
|
if (issetugid())
|
||||||
|
return;
|
||||||
|
char dso[PATH_MAX];
|
||||||
|
if (!get_dso_path(dso, sizeof(dso)))
|
||||||
|
return;
|
||||||
|
if (IsWindows())
|
||||||
|
if (deploy(dso))
|
||||||
|
if ((handle = cosmo_dlopen(dso, RTLD_LOCAL)))
|
||||||
|
goto WeAreGood;
|
||||||
|
if (!build(dso))
|
||||||
|
return;
|
||||||
|
if (!(handle = cosmo_dlopen(dso, RTLD_LOCAL)))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WeAreGood:
|
||||||
|
g_audio.open = cosmo_dlsym(handle, "cosmoaudio_open");
|
||||||
|
g_audio.close = cosmo_dlsym(handle, "cosmoaudio_close");
|
||||||
|
g_audio.write = cosmo_dlsym(handle, "cosmoaudio_write");
|
||||||
|
g_audio.read = cosmo_dlsym(handle, "cosmoaudio_read");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cosmoaudio_init(void) {
|
||||||
|
pthread_once(&g_audio.once, cosmoaudio_setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
COSMOAUDIO_ABI int cosmoaudio_open(struct CosmoAudio **cap, int sampleRate,
|
||||||
|
int channels) {
|
||||||
|
cosmoaudio_init();
|
||||||
|
if (!g_audio.open)
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
return g_audio.open(cap, sampleRate, channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
COSMOAUDIO_ABI int cosmoaudio_close(struct CosmoAudio *ca) {
|
||||||
|
cosmoaudio_init();
|
||||||
|
if (!g_audio.close)
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
return g_audio.close(ca);
|
||||||
|
}
|
||||||
|
|
||||||
|
COSMOAUDIO_ABI int cosmoaudio_write(struct CosmoAudio *ca, const float *data,
|
||||||
|
int frames) {
|
||||||
|
cosmoaudio_init();
|
||||||
|
if (!g_audio.write)
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
return g_audio.write(ca, data, frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
COSMOAUDIO_ABI int cosmoaudio_read(struct CosmoAudio *ca, float *data,
|
||||||
|
int frames) {
|
||||||
|
cosmoaudio_init();
|
||||||
|
if (!g_audio.read)
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
return g_audio.read(ca, data, frames);
|
||||||
|
}
|
3
dsp/audio/cosmoaudio/.gitignore
vendored
Normal file
3
dsp/audio/cosmoaudio/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*.o
|
||||||
|
/Debug
|
||||||
|
/Release
|
86
dsp/audio/cosmoaudio/Makefile.msvc
Normal file
86
dsp/audio/cosmoaudio/Makefile.msvc
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
# Makefile for MSVC x64 Command Line Developer Tools
|
||||||
|
#
|
||||||
|
# nmake /f Makefile.msvc check
|
||||||
|
# nmake /f Makefile.msvc MODE=debug check
|
||||||
|
#
|
||||||
|
|
||||||
|
# Compiler and linker
|
||||||
|
CC=cl
|
||||||
|
LINK=link
|
||||||
|
|
||||||
|
# Build mode (can be overridden from command line)
|
||||||
|
!IFNDEF MODE
|
||||||
|
MODE=release
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Library dependencies.
|
||||||
|
TEST_LIBS=OneCore.lib
|
||||||
|
|
||||||
|
# Compiler flags
|
||||||
|
CFLAGS_COMMON=/nologo /W4 /Gy /EHsc
|
||||||
|
CFLAGS_DEBUG=/Od /Zi /MDd /D_DEBUG
|
||||||
|
CFLAGS_RELEASE=/O2 /MD /DNDEBUG
|
||||||
|
|
||||||
|
!IF "$(MODE)"=="debug"
|
||||||
|
CFLAGS=$(CFLAGS_COMMON) $(CFLAGS_DEBUG)
|
||||||
|
LDFLAGS=/DEBUG
|
||||||
|
OUT_DIR=Debug
|
||||||
|
!ELSE
|
||||||
|
CFLAGS=$(CFLAGS_COMMON) $(CFLAGS_RELEASE) /GL
|
||||||
|
LDFLAGS=/RELEASE /OPT:REF /OPT:ICF /LTCG /INCREMENTAL:NO
|
||||||
|
OUT_DIR=Release
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Additional flags for DLL
|
||||||
|
DLL_CFLAGS=$(CFLAGS) /D_USRDLL /D_WINDLL
|
||||||
|
|
||||||
|
# Linker flags
|
||||||
|
LDFLAGS=/NOLOGO /SUBSYSTEM:CONSOLE $(LDFLAGS)
|
||||||
|
|
||||||
|
# Output file names
|
||||||
|
DLL_TARGET=$(OUT_DIR)\cosmoaudio.dll
|
||||||
|
TEST_TARGET=$(OUT_DIR)\test.exe
|
||||||
|
|
||||||
|
# Source files
|
||||||
|
DLL_SOURCES=cosmoaudio.c
|
||||||
|
TEST_SOURCES=test.c
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
DLL_OBJECTS=$(OUT_DIR)\cosmoaudio.obj
|
||||||
|
TEST_OBJECTS=$(OUT_DIR)\test.obj
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
all: $(OUT_DIR) $(DLL_TARGET) $(TEST_TARGET)
|
||||||
|
|
||||||
|
# Create output directory
|
||||||
|
$(OUT_DIR):
|
||||||
|
if not exist $(OUT_DIR) mkdir $(OUT_DIR)
|
||||||
|
|
||||||
|
# Rule to build the DLL
|
||||||
|
$(DLL_TARGET): $(OUT_DIR) $(DLL_OBJECTS)
|
||||||
|
$(LINK) /DLL $(LDFLAGS) /OUT:$(DLL_TARGET) $(DLL_OBJECTS)
|
||||||
|
|
||||||
|
# Rule to build the test program
|
||||||
|
$(TEST_TARGET): $(OUT_DIR) $(TEST_OBJECTS) $(DLL_TARGET)
|
||||||
|
$(LINK) $(LDFLAGS) /OUT:$(TEST_TARGET) $(TEST_OBJECTS) $(DLL_TARGET:.dll=.lib) $(TEST_LIBS)
|
||||||
|
|
||||||
|
# Rules to compile .c files to .obj files with header dependencies
|
||||||
|
{.}.c{$(OUT_DIR)}.obj:
|
||||||
|
$(CC) $(DLL_CFLAGS) /c /Fo$(OUT_DIR)\ $<
|
||||||
|
|
||||||
|
$(OUT_DIR)\test.obj: $(OUT_DIR) test.c cosmoaudio.h
|
||||||
|
$(CC) $(CFLAGS) /c /Fo$(OUT_DIR)\ test.c
|
||||||
|
|
||||||
|
$(OUT_DIR)\cosmoaudio.obj: $(OUT_DIR) cosmoaudio.c miniaudio.h cosmoaudio.h
|
||||||
|
$(CC) $(DLL_CFLAGS) /c /Fo$(OUT_DIR)\ cosmoaudio.c
|
||||||
|
|
||||||
|
# Clean target
|
||||||
|
clean:
|
||||||
|
if exist $(OUT_DIR) rmdir /s /q $(OUT_DIR)
|
||||||
|
|
||||||
|
# Run tests (now called 'check')
|
||||||
|
check: $(TEST_TARGET)
|
||||||
|
$(TEST_TARGET)
|
||||||
|
|
||||||
|
# Phony targets
|
||||||
|
.PHONY: all clean check
|
251
dsp/audio/cosmoaudio/cosmoaudio.c
Normal file
251
dsp/audio/cosmoaudio/cosmoaudio.c
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
#define COSMOAUDIO_BUILD
|
||||||
|
#include "cosmoaudio.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MA_STATIC
|
||||||
|
#define MA_NO_DECODING
|
||||||
|
#define MA_NO_ENCODING
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define MA_DR_MP3_NO_STDIO
|
||||||
|
#endif
|
||||||
|
#define MINIAUDIO_IMPLEMENTATION
|
||||||
|
#include "miniaudio.h"
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define LOG(...) fprintf(stderr, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define LOG(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct CosmoAudio {
|
||||||
|
ma_device device;
|
||||||
|
ma_pcm_rb input;
|
||||||
|
ma_pcm_rb output;
|
||||||
|
ma_uint32 sampleRate;
|
||||||
|
ma_uint32 channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int read_ring_buffer(ma_pcm_rb* rb, float* pOutput, ma_uint32 frameCount,
|
||||||
|
ma_uint32 channels) {
|
||||||
|
ma_result result;
|
||||||
|
ma_uint32 framesRead;
|
||||||
|
ma_uint32 framesToRead;
|
||||||
|
for (framesRead = 0; framesRead < frameCount; framesRead += framesToRead) {
|
||||||
|
framesToRead = frameCount - framesRead;
|
||||||
|
void* pMappedBuffer;
|
||||||
|
result = ma_pcm_rb_acquire_read(rb, &framesToRead, &pMappedBuffer);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
LOG("ma_pcm_rb_acquire_read failed: %s\n", ma_result_description(result));
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
}
|
||||||
|
if (!framesToRead)
|
||||||
|
break;
|
||||||
|
memcpy(pOutput + framesRead * channels, pMappedBuffer,
|
||||||
|
framesToRead * channels * sizeof(float));
|
||||||
|
result = ma_pcm_rb_commit_read(rb, framesToRead);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
if (result == MA_AT_END)
|
||||||
|
break;
|
||||||
|
LOG("ma_pcm_rb_commit_read failed: %s\n", ma_result_description(result));
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return framesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_ring_buffer(ma_pcm_rb* rb, const float* pInput,
|
||||||
|
ma_uint32 frameCount, ma_uint32 channels) {
|
||||||
|
ma_result result;
|
||||||
|
ma_uint32 framesWritten;
|
||||||
|
ma_uint32 framesToWrite;
|
||||||
|
for (framesWritten = 0; framesWritten < frameCount;
|
||||||
|
framesWritten += framesToWrite) {
|
||||||
|
framesToWrite = frameCount - framesWritten;
|
||||||
|
void* pMappedBuffer;
|
||||||
|
result = ma_pcm_rb_acquire_write(rb, &framesToWrite, &pMappedBuffer);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
LOG("ma_pcm_rb_acquire_write failed: %s\n",
|
||||||
|
ma_result_description(result));
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
}
|
||||||
|
if (!framesToWrite)
|
||||||
|
break;
|
||||||
|
memcpy(pMappedBuffer, pInput + framesWritten * channels,
|
||||||
|
framesToWrite * channels * sizeof(float));
|
||||||
|
result = ma_pcm_rb_commit_write(rb, framesToWrite);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
if (result == MA_AT_END)
|
||||||
|
break;
|
||||||
|
LOG("ma_pcm_rb_commit_write failed: %s\n", ma_result_description(result));
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return framesWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_callback_f32(ma_device* pDevice, float* pOutput,
|
||||||
|
const float* pInput, ma_uint32 frameCount) {
|
||||||
|
struct CosmoAudio* ca = (struct CosmoAudio*)pDevice->pUserData;
|
||||||
|
read_ring_buffer(&ca->output, pOutput, frameCount, ca->channels);
|
||||||
|
write_ring_buffer(&ca->input, pInput, frameCount, ca->channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
|
||||||
|
ma_uint32 frameCount) {
|
||||||
|
data_callback_f32(pDevice, (float*)pOutput, (const float*)pInput, frameCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens access to speaker and microphone.
|
||||||
|
*
|
||||||
|
* @param cap will receive pointer to allocated CosmoAudio object on success,
|
||||||
|
* which must be freed by caller with cosmoaudio_close()
|
||||||
|
* @param sampleRate is sample rate in Hz, e.g. 44100
|
||||||
|
* @param channels is number of channels (1 for mono, 2 for stereo)
|
||||||
|
* @return 0 on success, or negative error code on failure
|
||||||
|
*/
|
||||||
|
COSMOAUDIO_ABI int cosmoaudio_open(struct CosmoAudio** cap, int sampleRate,
|
||||||
|
int channels) {
|
||||||
|
|
||||||
|
// Allocate cosmo audio object.
|
||||||
|
struct CosmoAudio* ca;
|
||||||
|
if (!(ca = (struct CosmoAudio*)malloc(sizeof(struct CosmoAudio))))
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
ca->channels = channels;
|
||||||
|
ca->sampleRate = sampleRate;
|
||||||
|
|
||||||
|
// Initialize device.
|
||||||
|
ma_result result;
|
||||||
|
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_duplex);
|
||||||
|
deviceConfig.sampleRate = sampleRate;
|
||||||
|
deviceConfig.capture.channels = channels;
|
||||||
|
deviceConfig.capture.format = ma_format_f32;
|
||||||
|
deviceConfig.capture.shareMode = ma_share_mode_shared;
|
||||||
|
deviceConfig.playback.channels = channels;
|
||||||
|
deviceConfig.playback.format = ma_format_f32;
|
||||||
|
deviceConfig.dataCallback = data_callback;
|
||||||
|
deviceConfig.pUserData = ca;
|
||||||
|
result = ma_device_init(NULL, &deviceConfig, &ca->device);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
free(ca);
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the speaker ring buffer.
|
||||||
|
result = ma_pcm_rb_init(ma_format_f32, channels,
|
||||||
|
ca->device.playback.internalPeriodSizeInFrames * 10,
|
||||||
|
NULL, NULL, &ca->output);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
ma_device_uninit(&ca->device);
|
||||||
|
free(ca);
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
}
|
||||||
|
ma_pcm_rb_set_sample_rate(&ca->output, sampleRate);
|
||||||
|
|
||||||
|
// Initialize the microphone ring buffer.
|
||||||
|
result = ma_pcm_rb_init(ma_format_f32, channels,
|
||||||
|
ca->device.capture.internalPeriodSizeInFrames * 10,
|
||||||
|
NULL, NULL, &ca->input);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
ma_pcm_rb_uninit(&ca->output);
|
||||||
|
ma_device_uninit(&ca->device);
|
||||||
|
free(ca);
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
}
|
||||||
|
ma_pcm_rb_set_sample_rate(&ca->output, sampleRate);
|
||||||
|
|
||||||
|
// Start audio playback.
|
||||||
|
if (ma_device_start(&ca->device) != MA_SUCCESS) {
|
||||||
|
ma_pcm_rb_uninit(&ca->input);
|
||||||
|
ma_pcm_rb_uninit(&ca->output);
|
||||||
|
ma_device_uninit(&ca->device);
|
||||||
|
free(ca);
|
||||||
|
return COSMOAUDIO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
*cap = ca;
|
||||||
|
return COSMOAUDIO_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes audio device and frees all associated resources.
|
||||||
|
*
|
||||||
|
* @param ca is CosmoAudio object returned earlier by cosmoaudio_open()
|
||||||
|
* @return 0 on success, or negative error code on failure
|
||||||
|
*/
|
||||||
|
COSMOAUDIO_ABI int cosmoaudio_close(struct CosmoAudio* ca) {
|
||||||
|
ma_device_uninit(&ca->device);
|
||||||
|
ma_pcm_rb_uninit(&ca->output);
|
||||||
|
free(ca);
|
||||||
|
return COSMOAUDIO_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes raw audio data to speaker.
|
||||||
|
*
|
||||||
|
* The data is written to a ring buffer in real-time, which is then
|
||||||
|
* played back very soon on the audio device. This has tolerence for
|
||||||
|
* a certain amount of buffering, but expects that this function is
|
||||||
|
* repeatedly called at a regular time interval. The caller should
|
||||||
|
* have its own sleep loop for this purpose.
|
||||||
|
*
|
||||||
|
* @param ca is CosmoAudio object returned earlier by cosmoaudio_open()
|
||||||
|
* @param data is pointer to raw audio samples, expected to be in the range
|
||||||
|
* -1.0 to 1.0, where channels are interleaved
|
||||||
|
* @param frames is the number of frames (i.e. number of samples divided by
|
||||||
|
* number of channels) from `data` to write to audio device
|
||||||
|
* @return number of frames written, or negative error code on failure
|
||||||
|
*/
|
||||||
|
COSMOAUDIO_ABI int cosmoaudio_write(struct CosmoAudio* ca, const float* data,
|
||||||
|
int frames) {
|
||||||
|
return write_ring_buffer(&ca->output, data, frames, ca->channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads raw audio data from microphone.
|
||||||
|
*
|
||||||
|
* The data is read from a ring buffer in real-time, which is then
|
||||||
|
* played back very soon on the audio device. This has tolerence for
|
||||||
|
* a certain amount of buffering, but expects that this function is
|
||||||
|
* repeatedly called at a regular time interval. The caller should
|
||||||
|
* have its own sleep loop for this purpose.
|
||||||
|
*
|
||||||
|
* @param ca is CosmoAudio object returned earlier by cosmoaudio_open()
|
||||||
|
* @param data is pointer to raw audio samples, expected to be in the range
|
||||||
|
* -1.0 to 1.0, where channels are interleaved
|
||||||
|
* @param frames is the number of frames (i.e. number of samples divided by
|
||||||
|
* number of channels) from `data` to read from microphone
|
||||||
|
* @return number of frames read, or negative error code on failure
|
||||||
|
*/
|
||||||
|
COSMOAUDIO_ABI int cosmoaudio_read(struct CosmoAudio* ca, float* data,
|
||||||
|
int frames) {
|
||||||
|
int read;
|
||||||
|
for (int i = 0; i < frames; i += read) {
|
||||||
|
int remaining = frames - i;
|
||||||
|
read = read_ring_buffer(&ca->input, data + i * ca->channels, remaining,
|
||||||
|
ca->channels);
|
||||||
|
if (read < 0)
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <Windows.h>
|
||||||
|
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
|
||||||
|
LPVOID lpReserved) {
|
||||||
|
switch (ul_reason_for_call) {
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
case DLL_THREAD_ATTACH:
|
||||||
|
case DLL_THREAD_DETACH:
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
(void)hModule;
|
||||||
|
(void)lpReserved;
|
||||||
|
(void)ul_reason_for_call;
|
||||||
|
}
|
||||||
|
#endif
|
BIN
dsp/audio/cosmoaudio/cosmoaudio.dll
Normal file
BIN
dsp/audio/cosmoaudio/cosmoaudio.dll
Normal file
Binary file not shown.
41
dsp/audio/cosmoaudio/cosmoaudio.h
Normal file
41
dsp/audio/cosmoaudio/cosmoaudio.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef COSMOAUDIO_H_
|
||||||
|
#define COSMOAUDIO_H_
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define COSMOAUDIO_ABI
|
||||||
|
#ifdef COSMOAUDIO_BUILD
|
||||||
|
#define COSMOAUDIO_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define COSMOAUDIO_API __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define COSMOAUDIO_API
|
||||||
|
#ifdef __x86_64__
|
||||||
|
#define COSMOAUDIO_ABI __attribute__((__ms_abi__))
|
||||||
|
#else
|
||||||
|
#define COSMOAUDIO_ABI
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define COSMOAUDIO_SUCCESS 0
|
||||||
|
#define COSMOAUDIO_ERROR -1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct CosmoAudio;
|
||||||
|
|
||||||
|
COSMOAUDIO_API int cosmoaudio_open(struct CosmoAudio **, int,
|
||||||
|
int) COSMOAUDIO_ABI;
|
||||||
|
COSMOAUDIO_API int cosmoaudio_close(struct CosmoAudio *) COSMOAUDIO_ABI;
|
||||||
|
COSMOAUDIO_API int cosmoaudio_write(struct CosmoAudio *, const float *,
|
||||||
|
int) COSMOAUDIO_ABI;
|
||||||
|
COSMOAUDIO_API int cosmoaudio_read(struct CosmoAudio *, float *,
|
||||||
|
int) COSMOAUDIO_ABI;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* COSMOAUDIO_H_ */
|
0
dsp/audio/cosmoaudio/miniaudio.c
Normal file
0
dsp/audio/cosmoaudio/miniaudio.c
Normal file
92621
dsp/audio/cosmoaudio/miniaudio.h
Normal file
92621
dsp/audio/cosmoaudio/miniaudio.h
Normal file
File diff suppressed because it is too large
Load diff
36
dsp/audio/cosmoaudio/test.c
Normal file
36
dsp/audio/cosmoaudio/test.c
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "cosmoaudio.h"
|
||||||
|
|
||||||
|
#ifndef M_PIf
|
||||||
|
#define M_PIf 3.14159265358979323846f
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int hz = 44100;
|
||||||
|
int channels = 2;
|
||||||
|
struct CosmoAudio *ca;
|
||||||
|
if (cosmoaudio_open(&ca, hz, channels) != COSMOAUDIO_SUCCESS) {
|
||||||
|
fprintf(stderr, "%s: failed to open audio\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = 1000;
|
||||||
|
int sample = 0;
|
||||||
|
float *buf = (float *)malloc(sizeof(float) * channels * n);
|
||||||
|
for (;;) {
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
float freq = 440;
|
||||||
|
float t = (float)sample++ / hz;
|
||||||
|
if (sample == hz)
|
||||||
|
sample = 0;
|
||||||
|
buf[i * channels] = sinf(freq * 2.f * M_PIf * t);
|
||||||
|
buf[i * channels + 1] = sinf(freq * 2.f * M_PIf * t);
|
||||||
|
}
|
||||||
|
cosmoaudio_write(ca, buf, 128);
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ EXAMPLES_BINS = \
|
||||||
|
|
||||||
EXAMPLES_DIRECTDEPS = \
|
EXAMPLES_DIRECTDEPS = \
|
||||||
CTL \
|
CTL \
|
||||||
|
DSP_AUDIO \
|
||||||
DSP_CORE \
|
DSP_CORE \
|
||||||
DSP_SCALE \
|
DSP_SCALE \
|
||||||
DSP_TTY \
|
DSP_TTY \
|
||||||
|
|
|
@ -18,6 +18,7 @@ libc/isystem/byteswap.h \
|
||||||
libc/isystem/clzerointrin.h \
|
libc/isystem/clzerointrin.h \
|
||||||
libc/isystem/complex.h \
|
libc/isystem/complex.h \
|
||||||
libc/isystem/cosmo.h \
|
libc/isystem/cosmo.h \
|
||||||
|
libc/isystem/cosmoaudio.h \
|
||||||
libc/isystem/cpio.h \
|
libc/isystem/cpio.h \
|
||||||
libc/isystem/cpuid.h \
|
libc/isystem/cpuid.h \
|
||||||
libc/isystem/crypt.h \
|
libc/isystem/crypt.h \
|
||||||
|
|
|
@ -171,7 +171,7 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
// some programs like bash like to poll([stdin], 1, -1) so let's
|
// some programs like bash like to poll([stdin], 1, -1) so let's
|
||||||
// avoid busy looping in such cases. we could generalize this to
|
// avoid busy looping in such cases. we could generalize this to
|
||||||
// always avoid busy loops, but we'd need poll to launch threads
|
// always avoid busy loops, but we'd need poll to launch threads
|
||||||
if (pn == 1 && sn == 0 && (pipefds[i].events & POLLRDNORM_)) {
|
if (0 && pn == 1 && sn == 0 && (pipefds[i].events & POLLRDNORM_)) {
|
||||||
int err = errno;
|
int err = errno;
|
||||||
switch (CountConsoleInputBytesBlocking(waitfor, sigmask)) {
|
switch (CountConsoleInputBytesBlocking(waitfor, sigmask)) {
|
||||||
case -1:
|
case -1:
|
||||||
|
|
1
libc/isystem/cosmoaudio.h
Normal file
1
libc/isystem/cosmoaudio.h
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include "dsp/audio/cosmoaudio/cosmoaudio.h"
|
|
@ -16,6 +16,7 @@ TOOL_VIZ_BINS = \
|
||||||
$(TOOL_VIZ_COMS:%=%.dbg)
|
$(TOOL_VIZ_COMS:%=%.dbg)
|
||||||
|
|
||||||
TOOL_VIZ_DIRECTDEPS = \
|
TOOL_VIZ_DIRECTDEPS = \
|
||||||
|
DSP_AUDIO \
|
||||||
DSP_CORE \
|
DSP_CORE \
|
||||||
DSP_MPEG \
|
DSP_MPEG \
|
||||||
DSP_SCALE \
|
DSP_SCALE \
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "dsp/audio/cosmoaudio/cosmoaudio.h"
|
||||||
#include "dsp/core/core.h"
|
#include "dsp/core/core.h"
|
||||||
#include "dsp/core/half.h"
|
#include "dsp/core/half.h"
|
||||||
#include "dsp/core/illumination.h"
|
#include "dsp/core/illumination.h"
|
||||||
|
@ -142,8 +143,6 @@ Effects Shortcuts:\n\
|
||||||
CTRL-G {Unsharp,Sharp}\n\
|
CTRL-G {Unsharp,Sharp}\n\
|
||||||
\n\
|
\n\
|
||||||
Environment Variables:\n\
|
Environment Variables:\n\
|
||||||
SOX overrides location of SoX executable\n\
|
|
||||||
FFPLAY overrides location of FFmpeg ffplay executable\n\
|
|
||||||
ROWS=𝑦 sets height [inarticulate mode]\n\
|
ROWS=𝑦 sets height [inarticulate mode]\n\
|
||||||
COLUMNS=𝑥 sets width [inarticulate mode]\n\
|
COLUMNS=𝑥 sets width [inarticulate mode]\n\
|
||||||
TERM=dumb inarticulate mode\n\
|
TERM=dumb inarticulate mode\n\
|
||||||
|
@ -158,11 +157,6 @@ in a different format, then it's fast and easy to convert them:\n\
|
||||||
The terminal fonts we recommend are PragmataPro, Bitstream Vera Sans\n\
|
The terminal fonts we recommend are PragmataPro, Bitstream Vera Sans\n\
|
||||||
Mono (known as DejaVu Sans Mono in the open source community), Menlo,\n\
|
Mono (known as DejaVu Sans Mono in the open source community), Menlo,\n\
|
||||||
and Lucida Console.\n\
|
and Lucida Console.\n\
|
||||||
\n\
|
|
||||||
On Linux, playing audio requires either `sox` or `ffplay` being on\n\
|
|
||||||
the $PATH. Kitty is the fastest terminal. Alacritty also has a fast\n\
|
|
||||||
display. GNOME Terminal and xterm both work well in 256-color or ANSI\n\
|
|
||||||
mode.\n\
|
|
||||||
\n"
|
\n"
|
||||||
|
|
||||||
#define CTRL(C) ((C) ^ 0100)
|
#define CTRL(C) ((C) ^ 0100)
|
||||||
|
@ -226,16 +220,6 @@ struct FrameBuffer {
|
||||||
struct FrameBufferVirtualScreenInfo vscreen;
|
struct FrameBufferVirtualScreenInfo vscreen;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct itimerval kTimerDisarm = {
|
|
||||||
{0, 0},
|
|
||||||
{0, 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct itimerval kTimerHalfSecondSingleShot = {
|
|
||||||
{0, 0},
|
|
||||||
{0, 500000},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct NamedVector kPrimaries[] = {
|
static const struct NamedVector kPrimaries[] = {
|
||||||
{"BT.601", &kBt601Primaries},
|
{"BT.601", &kBt601Primaries},
|
||||||
{"BT.709", &kBt709Primaries},
|
{"BT.709", &kBt709Primaries},
|
||||||
|
@ -256,6 +240,7 @@ static const struct NamedVector kLightings[] = {
|
||||||
static plm_t *plm_;
|
static plm_t *plm_;
|
||||||
static float gamma_;
|
static float gamma_;
|
||||||
static int volscale_;
|
static int volscale_;
|
||||||
|
struct CosmoAudio *ca_;
|
||||||
static enum Blur blur_;
|
static enum Blur blur_;
|
||||||
static enum Sharp sharp_;
|
static enum Sharp sharp_;
|
||||||
static jmp_buf jb_, jbi_;
|
static jmp_buf jb_, jbi_;
|
||||||
|
@ -263,32 +248,27 @@ static double pary_, parx_;
|
||||||
static struct TtyIdent ti_;
|
static struct TtyIdent ti_;
|
||||||
static struct YCbCr *ycbcr_;
|
static struct YCbCr *ycbcr_;
|
||||||
static bool emboss_, sobel_;
|
static bool emboss_, sobel_;
|
||||||
static volatile int playpid_;
|
static const char *patharg_;
|
||||||
static struct winsize wsize_;
|
static struct winsize wsize_;
|
||||||
static float hue_, sat_, lit_;
|
static float hue_, sat_, lit_;
|
||||||
|
static volatile bool resized_;
|
||||||
static void *xtcodes_, *audio_;
|
static void *xtcodes_, *audio_;
|
||||||
static struct FrameBuffer fb0_;
|
static struct FrameBuffer fb0_;
|
||||||
static unsigned chans_, srate_;
|
static unsigned chans_, srate_;
|
||||||
static volatile bool ignoresigs_;
|
static volatile bool ignoresigs_;
|
||||||
static size_t dh_, dw_, framecount_;
|
static size_t dh_, dw_, framecount_;
|
||||||
static struct FrameCountRing fcring_;
|
static struct FrameCountRing fcring_;
|
||||||
static volatile bool resized_, piped_;
|
|
||||||
static int lumakernel_, chromakernel_;
|
static int lumakernel_, chromakernel_;
|
||||||
static openspeaker_f tryspeakerfns_[4];
|
|
||||||
static int primaries_, lighting_, swing_;
|
static int primaries_, lighting_, swing_;
|
||||||
static uint64_t t1, t2, t3, t4, t5, t6, t8;
|
static uint64_t t1, t2, t3, t4, t5, t6, t8;
|
||||||
static const char *sox_, *ffplay_, *patharg_;
|
static int homerow_, lastrow_, infd_, outfd_;
|
||||||
static struct VtFrame vtframe_[2], *f1_, *f2_;
|
static struct VtFrame vtframe_[2], *f1_, *f2_;
|
||||||
static struct Graphic graphic_[2], *g1_, *g2_;
|
static struct Graphic graphic_[2], *g1_, *g2_;
|
||||||
static struct timespec deadline_, dura_, starttime_;
|
static struct timespec deadline_, dura_, starttime_;
|
||||||
static bool yes_, stats_, dither_, ttymode_, istango_;
|
static bool yes_, stats_, dither_, ttymode_, istango_;
|
||||||
static struct timespec decode_start_, f1_start_, f2_start_;
|
static struct timespec decode_start_, f1_start_, f2_start_;
|
||||||
static int16_t pcm_[PLM_AUDIO_SAMPLES_PER_FRAME * 2 / 8][8];
|
|
||||||
static int16_t pcmscale_[PLM_AUDIO_SAMPLES_PER_FRAME * 2 / 8][8];
|
|
||||||
static bool fullclear_, historyclear_, tuned_, yonly_, gotvideo_;
|
static bool fullclear_, historyclear_, tuned_, yonly_, gotvideo_;
|
||||||
static int homerow_, lastrow_, playfd_, infd_, outfd_, speakerfails_;
|
static char status_[7][200], logpath_[PATH_MAX], chansstr_[32], sratestr_[32];
|
||||||
static char status_[7][200], logpath_[PATH_MAX], fifopath_[PATH_MAX],
|
|
||||||
chansstr_[32], sratestr_[32];
|
|
||||||
|
|
||||||
static void OnCtrlC(void) {
|
static void OnCtrlC(void) {
|
||||||
longjmp(jb_, 1);
|
longjmp(jb_, 1);
|
||||||
|
@ -298,18 +278,6 @@ static void OnResize(void) {
|
||||||
resized_ = true;
|
resized_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnSigPipe(void) {
|
|
||||||
piped_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnSigChld(void) {
|
|
||||||
playpid_ = 0, piped_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void StrikeDownCrapware(int sig) {
|
|
||||||
kill(playpid_, SIGKILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct timespec GetGraceTime(void) {
|
static struct timespec GetGraceTime(void) {
|
||||||
return timespec_sub(deadline_, timespec_real());
|
return timespec_sub(deadline_, timespec_real());
|
||||||
}
|
}
|
||||||
|
@ -349,32 +317,9 @@ static int GetLighting(const char *s) {
|
||||||
return GetNamedVector(kLightings, ARRAYLEN(kLightings), s);
|
return GetNamedVector(kLightings, ARRAYLEN(kLightings), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CloseSpeaker(void) {
|
static void CloseSpeaker(void) {
|
||||||
int rc, wstatus;
|
if (ca_)
|
||||||
rc = 0;
|
cosmoaudio_close(ca_);
|
||||||
pthread_yield();
|
|
||||||
if (playfd_) {
|
|
||||||
rc |= close(playfd_);
|
|
||||||
playfd_ = -1;
|
|
||||||
}
|
|
||||||
if (playpid_) {
|
|
||||||
kill(playpid_, SIGTERM);
|
|
||||||
xsigaction(SIGALRM, StrikeDownCrapware, SA_RESETHAND, 0, 0);
|
|
||||||
setitimer(ITIMER_REAL, &kTimerHalfSecondSingleShot, NULL);
|
|
||||||
while (playpid_) {
|
|
||||||
if (waitpid(playpid_, &wstatus, 0) != -1) {
|
|
||||||
rc |= WEXITSTATUS(wstatus);
|
|
||||||
} else if (errno == EINTR) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
rc = -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
playpid_ = 0;
|
|
||||||
setitimer(ITIMER_REAL, &kTimerDisarm, NULL);
|
|
||||||
}
|
|
||||||
return !!rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ResizeVtFrame(struct VtFrame *f, size_t yn, size_t xn) {
|
static void ResizeVtFrame(struct VtFrame *f, size_t yn, size_t xn) {
|
||||||
|
@ -473,106 +418,14 @@ static void DimensionDisplay(void) {
|
||||||
} while (resized_);
|
} while (resized_);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int WriteAudio(int fd, const void *data, size_t size, int deadlinems) {
|
|
||||||
ssize_t rc;
|
|
||||||
const char *p;
|
|
||||||
size_t wrote, n;
|
|
||||||
p = data;
|
|
||||||
n = size;
|
|
||||||
do {
|
|
||||||
TryAgain:
|
|
||||||
if ((rc = write(fd, p, n)) != -1) {
|
|
||||||
wrote = rc;
|
|
||||||
p += wrote;
|
|
||||||
n -= wrote;
|
|
||||||
} else if (errno == EINTR) {
|
|
||||||
goto TryAgain;
|
|
||||||
} else if (errno == EAGAIN) {
|
|
||||||
if (poll((struct pollfd[]){{fd, POLLOUT}}, 1, deadlinems) == 0) {
|
|
||||||
return etimedout();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} while (n);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool TrySpeaker(const char *prog, char *const *args) {
|
|
||||||
int pipefds[2];
|
|
||||||
CHECK_NE(-1, pipe2(pipefds, O_CLOEXEC));
|
|
||||||
if (!(playpid_ = fork())) {
|
|
||||||
dup2(pipefds[0], 0);
|
|
||||||
dup2(fileno(__log_file), 1);
|
|
||||||
dup2(fileno(__log_file), 2);
|
|
||||||
close(fileno(__log_file));
|
|
||||||
execv(prog, args);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
playfd_ = pipefds[1];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool TrySox(void) {
|
|
||||||
return TrySpeaker(sox_, ARGZ("play", "-q", "-c", chansstr_, "-traw",
|
|
||||||
"-esigned", "-b16", "-r", sratestr_, "-"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool TryFfplay(void) {
|
|
||||||
return TrySpeaker(ffplay_, ARGZ("ffplay", "-nodisp", "-loglevel", "quiet",
|
|
||||||
"-fflags", "nobuffer", "-ac", chansstr_,
|
|
||||||
"-ar", sratestr_, "-f", "s16le", "pipe:"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool OpenSpeaker(void) {
|
static bool OpenSpeaker(void) {
|
||||||
size_t i;
|
return cosmoaudio_open(&ca_, srate_, chans_) == COSMOAUDIO_SUCCESS;
|
||||||
static bool once, count;
|
|
||||||
if (!once) {
|
|
||||||
once = true;
|
|
||||||
i = 0;
|
|
||||||
if (ffplay_)
|
|
||||||
tryspeakerfns_[i++] = TryFfplay;
|
|
||||||
if (sox_)
|
|
||||||
tryspeakerfns_[i++] = TrySox;
|
|
||||||
}
|
|
||||||
snprintf(fifopath_, sizeof(fifopath_), "%s%s.%d.%d.wav", __get_tmpdir(),
|
|
||||||
firstnonnull(program_invocation_short_name, "unknown"), getpid(),
|
|
||||||
count);
|
|
||||||
for (i = 0; i < ARRAYLEN(tryspeakerfns_); ++i) {
|
|
||||||
if (tryspeakerfns_[i]) {
|
|
||||||
if (++speakerfails_ <= 2 && tryspeakerfns_[i]()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
speakerfails_ = 0;
|
|
||||||
tryspeakerfns_[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnAudio(plm_t *mpeg, plm_samples_t *samples, void *user) {
|
static void OnAudio(plm_t *mpeg, plm_samples_t *samples, void *user) {
|
||||||
if (playfd_ != -1) {
|
if (!ca_)
|
||||||
DEBUGF("OnAudio() [grace=%,ldns]", timespec_tonanos(GetGraceTime()));
|
return;
|
||||||
CHECK_EQ(2, chans_);
|
cosmoaudio_write(ca_, samples->interleaved, samples->count);
|
||||||
CHECK_EQ(ARRAYLEN(pcm_) * 8, samples->count * chans_);
|
|
||||||
float2short(ARRAYLEN(pcm_), pcm_, (void *)samples->interleaved);
|
|
||||||
scalevolume(ARRAYLEN(pcm_), pcm_, volscale_);
|
|
||||||
sad16x8n(ARRAYLEN(pcm_), pcm_, pcmscale_);
|
|
||||||
DEBUGF("transcoded audio");
|
|
||||||
TryAgain:
|
|
||||||
if (WriteAudio(playfd_, pcm_, sizeof(pcm_), 1000) != -1) {
|
|
||||||
DEBUGF("WriteAudio(%d, %zu) ok [grace=%,ldns]", playfd_,
|
|
||||||
samples->count * 2, timespec_tonanos(GetGraceTime()));
|
|
||||||
} else {
|
|
||||||
WARNF("WriteAudio(%d, %zu) failed: %s", playfd_, samples->count * 2,
|
|
||||||
strerror(errno));
|
|
||||||
CloseSpeaker();
|
|
||||||
if (OpenSpeaker()) {
|
|
||||||
goto TryAgain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DescribeAlgorithms(char *p) {
|
static void DescribeAlgorithms(char *p) {
|
||||||
|
@ -885,7 +738,6 @@ static void OnVideo(plm_t *mpeg, plm_frame_t *pf, void *user) {
|
||||||
|
|
||||||
static void OpenVideo(void) {
|
static void OpenVideo(void) {
|
||||||
size_t yn, xn;
|
size_t yn, xn;
|
||||||
playfd_ = -1;
|
|
||||||
INFOF("%s(%`'s)", "OpenVideo", patharg_);
|
INFOF("%s(%`'s)", "OpenVideo", patharg_);
|
||||||
CHECK_NOTNULL((plm_ = plm_create_with_filename(patharg_)));
|
CHECK_NOTNULL((plm_ = plm_create_with_filename(patharg_)));
|
||||||
swing_ = 219;
|
swing_ = 219;
|
||||||
|
@ -1336,14 +1188,8 @@ static void RestoreTty(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HandleSignals(void) {
|
static void HandleSignals(void) {
|
||||||
if (piped_) {
|
if (resized_)
|
||||||
WARNF("SIGPIPE");
|
|
||||||
CloseSpeaker();
|
|
||||||
piped_ = false;
|
|
||||||
}
|
|
||||||
if (resized_) {
|
|
||||||
RefreshDisplay();
|
RefreshDisplay();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintVideo(void) {
|
static void PrintVideo(void) {
|
||||||
|
@ -1393,17 +1239,6 @@ static bool AskUserYesOrNoQuestion(const char *prompt) {
|
||||||
return c == 'y' || c == 'Y';
|
return c == 'y' || c == 'Y';
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CanPlayAudio(void) {
|
|
||||||
if (ffplay_ || sox_) {
|
|
||||||
return true;
|
|
||||||
} else if (AskUserYesOrNoQuestion(
|
|
||||||
"ffplay not found; continue without audio?")) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
longjmp(jb_, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PrintUsage(int rc, int fd) {
|
static void PrintUsage(int rc, int fd) {
|
||||||
tinyprint(fd, "Usage: ", program_invocation_name, USAGE, NULL);
|
tinyprint(fd, "Usage: ", program_invocation_name, USAGE, NULL);
|
||||||
exit(rc);
|
exit(rc);
|
||||||
|
@ -1442,8 +1277,6 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnExit(void) {
|
static void OnExit(void) {
|
||||||
if (playpid_)
|
|
||||||
kill(playpid_, SIGTERM), sched_yield();
|
|
||||||
if (plm_)
|
if (plm_)
|
||||||
plm_destroy(plm_), plm_ = NULL;
|
plm_destroy(plm_), plm_ = NULL;
|
||||||
YCbCrFree(&ycbcr_);
|
YCbCrFree(&ycbcr_);
|
||||||
|
@ -1460,11 +1293,6 @@ static void OnExit(void) {
|
||||||
CloseSpeaker();
|
CloseSpeaker();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MakeLatencyLittleLessBad(void) {
|
|
||||||
LOGIFNEG1(sys_mlockall(MCL_CURRENT));
|
|
||||||
LOGIFNEG1(nice(-5));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PickDefaults(void) {
|
static void PickDefaults(void) {
|
||||||
/*
|
/*
|
||||||
* Direct color ain't true color -- it just means xterm does the
|
* Direct color ain't true color -- it just means xterm does the
|
||||||
|
@ -1478,13 +1306,6 @@ static void PickDefaults(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RenounceSpecialPrivileges(void) {
|
|
||||||
if (issetugid()) {
|
|
||||||
setegid(getgid());
|
|
||||||
seteuid(getuid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define FBIOGET_VSCREENINFO 0x4600
|
#define FBIOGET_VSCREENINFO 0x4600
|
||||||
#define FBIOGET_FSCREENINFO 0x4602
|
#define FBIOGET_FSCREENINFO 0x4602
|
||||||
|
|
||||||
|
@ -1582,7 +1403,6 @@ static void TryToOpenFrameBuffer(void) {
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
sigset_t wut;
|
sigset_t wut;
|
||||||
const char *s;
|
|
||||||
ShowCrashReports();
|
ShowCrashReports();
|
||||||
gamma_ = 2.4;
|
gamma_ = 2.4;
|
||||||
volscale_ -= 2;
|
volscale_ -= 2;
|
||||||
|
@ -1599,17 +1419,6 @@ int main(int argc, char *argv[]) {
|
||||||
if (optind == argc)
|
if (optind == argc)
|
||||||
PrintUsage(EX_USAGE, STDERR_FILENO);
|
PrintUsage(EX_USAGE, STDERR_FILENO);
|
||||||
patharg_ = argv[optind];
|
patharg_ = argv[optind];
|
||||||
s = commandvenv("SOX", "sox");
|
|
||||||
sox_ = s ? strdup(s) : 0;
|
|
||||||
s = commandvenv("FFPLAY", "ffplay");
|
|
||||||
ffplay_ = s ? strdup(s) : 0;
|
|
||||||
if (!sox_ && !ffplay_) {
|
|
||||||
fprintf(stderr, "please install either the "
|
|
||||||
"`play` (sox) or "
|
|
||||||
"`ffplay` (ffmpeg) "
|
|
||||||
"commands, so printvideo can play audio\n");
|
|
||||||
usleep(10000);
|
|
||||||
}
|
|
||||||
infd_ = STDIN_FILENO;
|
infd_ = STDIN_FILENO;
|
||||||
outfd_ = STDOUT_FILENO;
|
outfd_ = STDOUT_FILENO;
|
||||||
if (!setjmp(jb_)) {
|
if (!setjmp(jb_)) {
|
||||||
|
@ -1617,8 +1426,6 @@ int main(int argc, char *argv[]) {
|
||||||
xsigaction(SIGHUP, OnCtrlC, 0, 0, NULL);
|
xsigaction(SIGHUP, OnCtrlC, 0, 0, NULL);
|
||||||
xsigaction(SIGTERM, OnCtrlC, 0, 0, NULL);
|
xsigaction(SIGTERM, OnCtrlC, 0, 0, NULL);
|
||||||
xsigaction(SIGWINCH, OnResize, 0, 0, NULL);
|
xsigaction(SIGWINCH, OnResize, 0, 0, NULL);
|
||||||
xsigaction(SIGCHLD, OnSigChld, 0, 0, NULL);
|
|
||||||
xsigaction(SIGPIPE, OnSigPipe, 0, 0, NULL);
|
|
||||||
if (ttyraw(kTtyLfToCrLf) != -1)
|
if (ttyraw(kTtyLfToCrLf) != -1)
|
||||||
ttymode_ = true;
|
ttymode_ = true;
|
||||||
__cxa_atexit((void *)OnExit, NULL, NULL);
|
__cxa_atexit((void *)OnExit, NULL, NULL);
|
||||||
|
@ -1629,10 +1436,7 @@ int main(int argc, char *argv[]) {
|
||||||
infd_ = -1;
|
infd_ = -1;
|
||||||
}
|
}
|
||||||
/* CHECK_NE(-1, fcntl(outfd_, F_SETFL, O_NONBLOCK)); */
|
/* CHECK_NE(-1, fcntl(outfd_, F_SETFL, O_NONBLOCK)); */
|
||||||
if (CanPlayAudio())
|
|
||||||
MakeLatencyLittleLessBad();
|
|
||||||
TryToOpenFrameBuffer();
|
TryToOpenFrameBuffer();
|
||||||
RenounceSpecialPrivileges();
|
|
||||||
if (t2 > t1)
|
if (t2 > t1)
|
||||||
longjmp(jb_, 1);
|
longjmp(jb_, 1);
|
||||||
OpenVideo();
|
OpenVideo();
|
||||||
|
|
Loading…
Reference in a new issue