mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-27 04:50:28 +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
|
@ -16,6 +16,7 @@ TOOL_VIZ_BINS = \
|
|||
$(TOOL_VIZ_COMS:%=%.dbg)
|
||||
|
||||
TOOL_VIZ_DIRECTDEPS = \
|
||||
DSP_AUDIO \
|
||||
DSP_CORE \
|
||||
DSP_MPEG \
|
||||
DSP_SCALE \
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/audio/cosmoaudio/cosmoaudio.h"
|
||||
#include "dsp/core/core.h"
|
||||
#include "dsp/core/half.h"
|
||||
#include "dsp/core/illumination.h"
|
||||
|
@ -142,8 +143,6 @@ Effects Shortcuts:\n\
|
|||
CTRL-G {Unsharp,Sharp}\n\
|
||||
\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\
|
||||
COLUMNS=𝑥 sets width [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\
|
||||
Mono (known as DejaVu Sans Mono in the open source community), Menlo,\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"
|
||||
|
||||
#define CTRL(C) ((C) ^ 0100)
|
||||
|
@ -226,16 +220,6 @@ struct FrameBuffer {
|
|||
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[] = {
|
||||
{"BT.601", &kBt601Primaries},
|
||||
{"BT.709", &kBt709Primaries},
|
||||
|
@ -256,6 +240,7 @@ static const struct NamedVector kLightings[] = {
|
|||
static plm_t *plm_;
|
||||
static float gamma_;
|
||||
static int volscale_;
|
||||
struct CosmoAudio *ca_;
|
||||
static enum Blur blur_;
|
||||
static enum Sharp sharp_;
|
||||
static jmp_buf jb_, jbi_;
|
||||
|
@ -263,32 +248,27 @@ static double pary_, parx_;
|
|||
static struct TtyIdent ti_;
|
||||
static struct YCbCr *ycbcr_;
|
||||
static bool emboss_, sobel_;
|
||||
static volatile int playpid_;
|
||||
static const char *patharg_;
|
||||
static struct winsize wsize_;
|
||||
static float hue_, sat_, lit_;
|
||||
static volatile bool resized_;
|
||||
static void *xtcodes_, *audio_;
|
||||
static struct FrameBuffer fb0_;
|
||||
static unsigned chans_, srate_;
|
||||
static volatile bool ignoresigs_;
|
||||
static size_t dh_, dw_, framecount_;
|
||||
static struct FrameCountRing fcring_;
|
||||
static volatile bool resized_, piped_;
|
||||
static int lumakernel_, chromakernel_;
|
||||
static openspeaker_f tryspeakerfns_[4];
|
||||
static int primaries_, lighting_, swing_;
|
||||
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 Graphic graphic_[2], *g1_, *g2_;
|
||||
static struct timespec deadline_, dura_, starttime_;
|
||||
static bool yes_, stats_, dither_, ttymode_, istango_;
|
||||
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 int homerow_, lastrow_, playfd_, infd_, outfd_, speakerfails_;
|
||||
static char status_[7][200], logpath_[PATH_MAX], fifopath_[PATH_MAX],
|
||||
chansstr_[32], sratestr_[32];
|
||||
static char status_[7][200], logpath_[PATH_MAX], chansstr_[32], sratestr_[32];
|
||||
|
||||
static void OnCtrlC(void) {
|
||||
longjmp(jb_, 1);
|
||||
|
@ -298,18 +278,6 @@ static void OnResize(void) {
|
|||
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) {
|
||||
return timespec_sub(deadline_, timespec_real());
|
||||
}
|
||||
|
@ -349,32 +317,9 @@ static int GetLighting(const char *s) {
|
|||
return GetNamedVector(kLightings, ARRAYLEN(kLightings), s);
|
||||
}
|
||||
|
||||
static bool CloseSpeaker(void) {
|
||||
int rc, wstatus;
|
||||
rc = 0;
|
||||
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 CloseSpeaker(void) {
|
||||
if (ca_)
|
||||
cosmoaudio_close(ca_);
|
||||
}
|
||||
|
||||
static void ResizeVtFrame(struct VtFrame *f, size_t yn, size_t xn) {
|
||||
|
@ -473,106 +418,14 @@ static void DimensionDisplay(void) {
|
|||
} 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) {
|
||||
size_t i;
|
||||
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;
|
||||
return cosmoaudio_open(&ca_, srate_, chans_) == COSMOAUDIO_SUCCESS;
|
||||
}
|
||||
|
||||
static void OnAudio(plm_t *mpeg, plm_samples_t *samples, void *user) {
|
||||
if (playfd_ != -1) {
|
||||
DEBUGF("OnAudio() [grace=%,ldns]", timespec_tonanos(GetGraceTime()));
|
||||
CHECK_EQ(2, chans_);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ca_)
|
||||
return;
|
||||
cosmoaudio_write(ca_, samples->interleaved, samples->count);
|
||||
}
|
||||
|
||||
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) {
|
||||
size_t yn, xn;
|
||||
playfd_ = -1;
|
||||
INFOF("%s(%`'s)", "OpenVideo", patharg_);
|
||||
CHECK_NOTNULL((plm_ = plm_create_with_filename(patharg_)));
|
||||
swing_ = 219;
|
||||
|
@ -1336,14 +1188,8 @@ static void RestoreTty(void) {
|
|||
}
|
||||
|
||||
static void HandleSignals(void) {
|
||||
if (piped_) {
|
||||
WARNF("SIGPIPE");
|
||||
CloseSpeaker();
|
||||
piped_ = false;
|
||||
}
|
||||
if (resized_) {
|
||||
if (resized_)
|
||||
RefreshDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintVideo(void) {
|
||||
|
@ -1393,17 +1239,6 @@ static bool AskUserYesOrNoQuestion(const char *prompt) {
|
|||
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) {
|
||||
tinyprint(fd, "Usage: ", program_invocation_name, USAGE, NULL);
|
||||
exit(rc);
|
||||
|
@ -1442,8 +1277,6 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
static void OnExit(void) {
|
||||
if (playpid_)
|
||||
kill(playpid_, SIGTERM), sched_yield();
|
||||
if (plm_)
|
||||
plm_destroy(plm_), plm_ = NULL;
|
||||
YCbCrFree(&ycbcr_);
|
||||
|
@ -1460,11 +1293,6 @@ static void OnExit(void) {
|
|||
CloseSpeaker();
|
||||
}
|
||||
|
||||
static void MakeLatencyLittleLessBad(void) {
|
||||
LOGIFNEG1(sys_mlockall(MCL_CURRENT));
|
||||
LOGIFNEG1(nice(-5));
|
||||
}
|
||||
|
||||
static void PickDefaults(void) {
|
||||
/*
|
||||
* 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_FSCREENINFO 0x4602
|
||||
|
||||
|
@ -1582,7 +1403,6 @@ static void TryToOpenFrameBuffer(void) {
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
sigset_t wut;
|
||||
const char *s;
|
||||
ShowCrashReports();
|
||||
gamma_ = 2.4;
|
||||
volscale_ -= 2;
|
||||
|
@ -1599,17 +1419,6 @@ int main(int argc, char *argv[]) {
|
|||
if (optind == argc)
|
||||
PrintUsage(EX_USAGE, STDERR_FILENO);
|
||||
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;
|
||||
outfd_ = STDOUT_FILENO;
|
||||
if (!setjmp(jb_)) {
|
||||
|
@ -1617,8 +1426,6 @@ int main(int argc, char *argv[]) {
|
|||
xsigaction(SIGHUP, OnCtrlC, 0, 0, NULL);
|
||||
xsigaction(SIGTERM, OnCtrlC, 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)
|
||||
ttymode_ = true;
|
||||
__cxa_atexit((void *)OnExit, NULL, NULL);
|
||||
|
@ -1629,10 +1436,7 @@ int main(int argc, char *argv[]) {
|
|||
infd_ = -1;
|
||||
}
|
||||
/* CHECK_NE(-1, fcntl(outfd_, F_SETFL, O_NONBLOCK)); */
|
||||
if (CanPlayAudio())
|
||||
MakeLatencyLittleLessBad();
|
||||
TryToOpenFrameBuffer();
|
||||
RenounceSpecialPrivileges();
|
||||
if (t2 > t1)
|
||||
longjmp(jb_, 1);
|
||||
OpenVideo();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue