|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
out123: simple program to stream data to an audio output device
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
copyright 1995-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
|
|
Packit |
c32a2d |
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
|
Packit |
c32a2d |
initially written by Thomas Orgis (extracted from mpg123.c)
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
This is a stripped down mpg123 that only uses libout123 to write standard input
|
|
Packit |
c32a2d |
to an audio device.
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
TODO: Add basic parsing of WAV headers to be able to pipe in WAV files, especially
|
|
Packit |
c32a2d |
from something like mpg123 -w -.
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#define ME "out123"
|
|
Packit |
c32a2d |
#include "config.h"
|
|
Packit |
c32a2d |
#include "compat.h"
|
|
Packit |
c32a2d |
#if WIN32
|
|
Packit |
c32a2d |
#include "win32_support.h"
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#include "out123.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifdef HAVE_SYS_WAIT_H
|
|
Packit |
c32a2d |
#include <sys/wait.h>
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#ifdef HAVE_SYS_RESOURCE_H
|
|
Packit |
c32a2d |
#include <sys/resource.h>
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include <errno.h>
|
|
Packit |
c32a2d |
#include <string.h>
|
|
Packit |
c32a2d |
#include <time.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifdef HAVE_SCHED_H
|
|
Packit |
c32a2d |
#include <sched.h>
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "sysutil.h"
|
|
Packit |
c32a2d |
#include "getlopt.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "waves.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* be paranoid about setpriority support */
|
|
Packit |
c32a2d |
#ifndef PRIO_PROCESS
|
|
Packit |
c32a2d |
#undef HAVE_SETPRIORITY
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int intflag = FALSE;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void usage(int err);
|
|
Packit |
c32a2d |
static void want_usage(char* arg);
|
|
Packit |
c32a2d |
static void long_usage(int err);
|
|
Packit |
c32a2d |
static void want_long_usage(char* arg);
|
|
Packit |
c32a2d |
static void print_title(FILE* o);
|
|
Packit |
c32a2d |
static void give_version(char* arg);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int verbose = 0;
|
|
Packit |
c32a2d |
static int quiet = FALSE;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static FILE* input = NULL;
|
|
Packit |
c32a2d |
static char *encoding_name = NULL;
|
|
Packit |
c32a2d |
static int encoding = MPG123_ENC_SIGNED_16;
|
|
Packit |
c32a2d |
static int channels = 2;
|
|
Packit |
c32a2d |
static long rate = 44100;
|
|
Packit |
c32a2d |
static char *driver = NULL;
|
|
Packit |
c32a2d |
static char *device = NULL;
|
|
Packit |
c32a2d |
size_t buffer_kb = 0;
|
|
Packit |
c32a2d |
static int realtime = FALSE;
|
|
Packit |
c32a2d |
#ifdef HAVE_WINDOWS_H
|
|
Packit |
c32a2d |
static int w32_priority = 0;
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
static int aggressive = FALSE;
|
|
Packit |
c32a2d |
static double preload = 0.2;
|
|
Packit |
c32a2d |
static long outflags = 0;
|
|
Packit |
c32a2d |
static long gain = -1;
|
|
Packit |
c32a2d |
static const char *name = NULL; /* Let the out123 library choose "out123". */
|
|
Packit |
c32a2d |
static double device_buffer; /* output device buffer */
|
|
Packit |
c32a2d |
long timelimit = -1;
|
|
Packit |
c32a2d |
off_t offset = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
char *wave_patterns = NULL;
|
|
Packit |
c32a2d |
char *wave_freqs = NULL;
|
|
Packit |
c32a2d |
char *wave_phases = NULL;
|
|
Packit |
c32a2d |
/* Default to around 2 MiB memory for the table. */
|
|
Packit |
c32a2d |
long wave_limit = 300000;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
size_t pcmblock = 1152; /* samples (pcm frames) we treat en bloc */
|
|
Packit |
c32a2d |
/* To be set after settling format. */
|
|
Packit |
c32a2d |
size_t pcmframe = 0;
|
|
Packit |
c32a2d |
unsigned char *audio = NULL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Option to play some oscillatory test signals. */
|
|
Packit |
c32a2d |
struct wave_table *waver = NULL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
out123_handle *ao = NULL;
|
|
Packit |
c32a2d |
char *cmd_name = NULL;
|
|
Packit |
c32a2d |
/* ThOr: pointers are not TRUE or FALSE */
|
|
Packit |
c32a2d |
char *equalfile = NULL;
|
|
Packit |
c32a2d |
int fresh = TRUE;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
size_t bufferblock = 4096;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
int OutputDescriptor;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
char *fullprogname = NULL; /* Copy of argv[0]. */
|
|
Packit |
c32a2d |
char *binpath; /* Path to myself. */
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* File-global storage of command line arguments.
|
|
Packit |
c32a2d |
They may be needed for cleanup after charset conversion. */
|
|
Packit |
c32a2d |
static char **argv = NULL;
|
|
Packit |
c32a2d |
static int argc = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Drain output device/buffer, but still give the option to interrupt things. */
|
|
Packit |
c32a2d |
static void controlled_drain(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int framesize;
|
|
Packit |
c32a2d |
long rate;
|
|
Packit |
c32a2d |
size_t drain_block;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(intflag || !out123_buffered(ao))
|
|
Packit |
c32a2d |
return;
|
|
Packit |
c32a2d |
if(out123_getformat(ao, &rate, NULL, NULL, &framesize))
|
|
Packit |
c32a2d |
return;
|
|
Packit |
c32a2d |
drain_block = 1024*framesize;
|
|
Packit |
c32a2d |
if(!quiet)
|
|
Packit |
c32a2d |
fprintf( stderr
|
|
Packit |
c32a2d |
, "\n"ME": draining buffer of %.1f s (you may interrupt)\n"
|
|
Packit |
c32a2d |
, (double)out123_buffered(ao)/framesize/rate );
|
|
Packit |
c32a2d |
do {
|
|
Packit |
c32a2d |
out123_ndrain(ao, drain_block);
|
|
Packit |
c32a2d |
} while(!intflag && out123_buffered(ao));
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void safe_exit(int code)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char *dummy, *dammy;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(!code)
|
|
Packit |
c32a2d |
controlled_drain();
|
|
Packit |
c32a2d |
if(intflag || code)
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
out123_del(ao);
|
|
Packit |
c32a2d |
#ifdef WANT_WIN32_UNICODE
|
|
Packit |
c32a2d |
win32_cmdline_free(argc, argv); /* This handles the premature argv == NULL, too. */
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
/* It's ugly... but let's just fix this still-reachable memory chunk of static char*. */
|
|
Packit |
c32a2d |
split_dir_file("", &dummy, &dammy);
|
|
Packit |
c32a2d |
if(fullprogname) free(fullprogname);
|
|
Packit |
c32a2d |
if(audio) free(audio);
|
|
Packit |
c32a2d |
wave_table_del(waver);
|
|
Packit |
c32a2d |
exit(code);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void check_fatal_output(int code)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(code)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!quiet)
|
|
Packit |
c32a2d |
error2( "out123 error %i: %s"
|
|
Packit |
c32a2d |
, out123_errcode(ao), out123_strerror(ao) );
|
|
Packit |
c32a2d |
safe_exit(code);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_output_module( char *arg )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
unsigned int i;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Search for a colon and set the device if found */
|
|
Packit |
c32a2d |
for(i=0; i< strlen( arg ); i++) {
|
|
Packit |
c32a2d |
if (arg[i] == ':') {
|
|
Packit |
c32a2d |
arg[i] = 0;
|
|
Packit |
c32a2d |
device = &arg[i+1];
|
|
Packit |
c32a2d |
debug1("Setting output device: %s", device);
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Set the output module */
|
|
Packit |
c32a2d |
driver = arg;
|
|
Packit |
c32a2d |
debug1("Setting output module: %s", driver );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_output_flag(int flag)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(outflags <= 0) outflags = flag;
|
|
Packit |
c32a2d |
else outflags |= flag;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_output_h(char *a)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
set_output_flag(OUT123_HEADPHONES);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_output_s(char *a)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
set_output_flag(OUT123_INTERNAL_SPEAKER);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_output_l(char *a)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
set_output_flag(OUT123_LINE_OUT);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_output(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* If single letter, it's the legacy output switch for AIX/HP/Sun.
|
|
Packit |
c32a2d |
If longer, it's module[:device] . If zero length, it's rubbish. */
|
|
Packit |
c32a2d |
if(strlen(arg) <= 1) switch(arg[0])
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
case 'h': set_output_h(arg); break;
|
|
Packit |
c32a2d |
case 's': set_output_s(arg); break;
|
|
Packit |
c32a2d |
case 'l': set_output_l(arg); break;
|
|
Packit |
c32a2d |
default:
|
|
Packit |
c32a2d |
error1("\"%s\" is no valid output", arg);
|
|
Packit |
c32a2d |
safe_exit(1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else set_output_module(arg);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_verbose (char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
verbose++;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_quiet (char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
verbose=0;
|
|
Packit |
c32a2d |
quiet=TRUE;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_out_wav(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
driver = "wav";
|
|
Packit |
c32a2d |
device = arg;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void set_out_cdr(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
driver = "cdr";
|
|
Packit |
c32a2d |
device = arg;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void set_out_au(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
driver = "au";
|
|
Packit |
c32a2d |
device = arg;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void set_out_test(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
driver = "test";
|
|
Packit |
c32a2d |
device = NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_out_file(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
driver = "raw";
|
|
Packit |
c32a2d |
device = arg;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_out_stdout(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
driver = "raw";
|
|
Packit |
c32a2d |
device = NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void set_out_stdout1(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
driver = "raw";
|
|
Packit |
c32a2d |
device = NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#if !defined (HAVE_SCHED_SETSCHEDULER) && !defined (HAVE_WINDOWS_H)
|
|
Packit |
c32a2d |
static void realtime_not_compiled(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fprintf(stderr, ME": Option '-T / --realtime' not compiled into this binary.\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void list_output_modules(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char **names = NULL;
|
|
Packit |
c32a2d |
char **descr = NULL;
|
|
Packit |
c32a2d |
int count = -1;
|
|
Packit |
c32a2d |
out123_handle *lao;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if((lao=out123_new()))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
out123_param_string(lao, OUT123_BINDIR, binpath);
|
|
Packit |
c32a2d |
out123_param_int(lao, OUT123_VERBOSE, verbose);
|
|
Packit |
c32a2d |
if(quiet)
|
|
Packit |
c32a2d |
out123_param_int(lao, OUT123_FLAGS, OUT123_QUIET);
|
|
Packit |
c32a2d |
if((count=out123_drivers(lao, &names, &descr)) >= 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int i;
|
|
Packit |
c32a2d |
for(i=0; i
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
printf( "%-15s\t%s\n", names[i], descr[i] );
|
|
Packit |
c32a2d |
free(names[i]);
|
|
Packit |
c32a2d |
free(descr[i]);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
free(names);
|
|
Packit |
c32a2d |
free(descr);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
out123_del(lao);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else if(!quiet)
|
|
Packit |
c32a2d |
error("Failed to create an out123 handle.");
|
|
Packit |
c32a2d |
exit(count >= 0 ? 0 : 1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void list_encodings(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int i;
|
|
Packit |
c32a2d |
int enc_count = 0;
|
|
Packit |
c32a2d |
int *enc_codes = NULL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
enc_count = out123_enc_list(&enc_codes);
|
|
Packit |
c32a2d |
/* All of the returned encodings have to have proper names!
|
|
Packit |
c32a2d |
It is a libout123 bug if not, and it should be quickly caught. */
|
|
Packit |
c32a2d |
for(i=0;i
|
|
Packit |
c32a2d |
printf( "%s:\t%s\n"
|
|
Packit |
c32a2d |
, out123_enc_name(enc_codes[i]), out123_enc_longname(enc_codes[i]) );
|
|
Packit |
c32a2d |
free(enc_codes);
|
|
Packit |
c32a2d |
exit(0);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int getencs(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int encs = 0;
|
|
Packit |
c32a2d |
out123_handle *lao;
|
|
Packit |
c32a2d |
if(verbose)
|
|
Packit |
c32a2d |
fprintf( stderr
|
|
Packit |
c32a2d |
, ME": getting supported encodings for %li Hz, %i channels\n"
|
|
Packit |
c32a2d |
, rate, channels );
|
|
Packit |
c32a2d |
if((lao=out123_new()))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
out123_param_int(lao, OUT123_VERBOSE, verbose);
|
|
Packit |
c32a2d |
if(quiet)
|
|
Packit |
c32a2d |
out123_param_int(lao, OUT123_FLAGS, OUT123_QUIET);
|
|
Packit |
c32a2d |
if(!out123_open(lao, driver, device))
|
|
Packit |
c32a2d |
encs = out123_encodings(lao, rate, channels);
|
|
Packit |
c32a2d |
else if(!quiet)
|
|
Packit |
c32a2d |
error1("cannot open driver: %s", out123_strerror(lao));
|
|
Packit |
c32a2d |
out123_del(lao);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else if(!quiet)
|
|
Packit |
c32a2d |
error("Failed to create an out123 handle.");
|
|
Packit |
c32a2d |
return encs;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void test_format(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int encs;
|
|
Packit |
c32a2d |
encs = getencs();
|
|
Packit |
c32a2d |
exit((encs & encoding) ? 0 : -1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void test_encodings(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int encs;
|
|
Packit |
c32a2d |
encs = getencs();
|
|
Packit |
c32a2d |
printf("%i\n", encs);
|
|
Packit |
c32a2d |
exit(!encs);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void query_format(char *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
out123_handle *lao;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(verbose)
|
|
Packit |
c32a2d |
fprintf(stderr, ME": querying default format\n");
|
|
Packit |
c32a2d |
if((lao=out123_new()))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
out123_param_int(lao, OUT123_VERBOSE, verbose);
|
|
Packit |
c32a2d |
if(quiet)
|
|
Packit |
c32a2d |
out123_param_int(lao, OUT123_FLAGS, OUT123_QUIET);
|
|
Packit |
c32a2d |
if(!out123_open(lao, driver, device))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
struct mpg123_fmt *fmts = NULL;
|
|
Packit |
c32a2d |
int count;
|
|
Packit |
c32a2d |
count = out123_formats(lao, NULL, 0, 0, 0, &fmts);
|
|
Packit |
c32a2d |
if(count > 0 && fmts[0].encoding > 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char *encname = out123_enc_name(fmts[0].encoding);
|
|
Packit |
c32a2d |
printf( "--rate %li --channels %i --encoding %s\n"
|
|
Packit |
c32a2d |
, fmts[0].rate, fmts[0].channels
|
|
Packit |
c32a2d |
, encname ? encname : "???" );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(verbose)
|
|
Packit |
c32a2d |
fprintf(stderr, ME": no default format found\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
free(fmts);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else if(!quiet)
|
|
Packit |
c32a2d |
error1("cannot open driver: %s", out123_strerror(lao));
|
|
Packit |
c32a2d |
out123_del(lao);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else if(!quiet)
|
|
Packit |
c32a2d |
error("Failed to create an out123 handle.");
|
|
Packit |
c32a2d |
exit(0);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Please note: GLO_NUM expects point to LONG! */
|
|
Packit |
c32a2d |
/* ThOr:
|
|
Packit |
c32a2d |
* Yeah, and despite that numerous addresses to int variables were
|
|
Packit |
c32a2d |
passed.
|
|
Packit |
c32a2d |
* That's not good on my Alpha machine with int=32bit and long=64bit!
|
|
Packit |
c32a2d |
* Introduced GLO_INT and GLO_LONG as different bits to make that clear.
|
|
Packit |
c32a2d |
* GLO_NUM no longer exists.
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
topt opts[] = {
|
|
Packit |
c32a2d |
{'t', "test", GLO_INT, set_out_test, NULL, 0},
|
|
Packit |
c32a2d |
{'s', "stdout", GLO_INT, set_out_stdout, NULL, 0},
|
|
Packit |
c32a2d |
{'S', "STDOUT", GLO_INT, set_out_stdout1, NULL, 0},
|
|
Packit |
c32a2d |
{'O', "outfile", GLO_ARG | GLO_CHAR, set_out_file, NULL, 0},
|
|
Packit |
c32a2d |
{'v', "verbose", 0, set_verbose, 0, 0},
|
|
Packit |
c32a2d |
{'q', "quiet", 0, set_quiet, 0, 0},
|
|
Packit |
c32a2d |
{'m', "mono", GLO_INT, 0, &channels, 1},
|
|
Packit |
c32a2d |
{0, "stereo", GLO_INT, 0, &channels, 2},
|
|
Packit |
c32a2d |
{'c', "channels", GLO_ARG | GLO_INT, 0, &channels, 0},
|
|
Packit |
c32a2d |
{'r', "rate", GLO_ARG | GLO_LONG, 0, &rate, 0},
|
|
Packit |
c32a2d |
{0, "headphones", 0, set_output_h, 0,0},
|
|
Packit |
c32a2d |
{0, "speaker", 0, set_output_s, 0,0},
|
|
Packit |
c32a2d |
{0, "lineout", 0, set_output_l, 0,0},
|
|
Packit |
c32a2d |
{'o', "output", GLO_ARG | GLO_CHAR, set_output, 0, 0},
|
|
Packit |
c32a2d |
{0, "list-modules",0, list_output_modules, NULL, 0},
|
|
Packit |
c32a2d |
{'a', "audiodevice", GLO_ARG | GLO_CHAR, 0, &device, 0},
|
|
Packit |
c32a2d |
#ifndef NOXFERMEM
|
|
Packit |
c32a2d |
{'b', "buffer", GLO_ARG | GLO_LONG, 0, &buffer_kb, 0},
|
|
Packit |
c32a2d |
{0, "preload", GLO_ARG|GLO_DOUBLE, 0, &preload, 0},
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#ifdef HAVE_SETPRIORITY
|
|
Packit |
c32a2d |
{0, "aggressive", GLO_INT, 0, &aggressive, 2},
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#if defined (HAVE_SCHED_SETSCHEDULER) || defined (HAVE_WINDOWS_H)
|
|
Packit |
c32a2d |
/* check why this should be a long variable instead of int! */
|
|
Packit |
c32a2d |
{'T', "realtime", GLO_INT, 0, &realtime, TRUE },
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
{'T', "realtime", 0, realtime_not_compiled, 0, 0 },
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#ifdef HAVE_WINDOWS_H
|
|
Packit |
c32a2d |
{0, "priority", GLO_ARG | GLO_INT, 0, &w32_priority, 0},
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
{'w', "wav", GLO_ARG | GLO_CHAR, set_out_wav, 0, 0 },
|
|
Packit |
c32a2d |
{0, "cdr", GLO_ARG | GLO_CHAR, set_out_cdr, 0, 0 },
|
|
Packit |
c32a2d |
{0, "au", GLO_ARG | GLO_CHAR, set_out_au, 0, 0 },
|
|
Packit |
c32a2d |
{'?', "help", 0, want_usage, 0, 0 },
|
|
Packit |
c32a2d |
{0 , "longhelp" , 0, want_long_usage, 0, 0 },
|
|
Packit |
c32a2d |
{0 , "version" , 0, give_version, 0, 0 },
|
|
Packit |
c32a2d |
{'e', "encoding", GLO_ARG|GLO_CHAR, 0, &encoding_name, 0},
|
|
Packit |
c32a2d |
{0, "list-encodings", 0, list_encodings, 0, 0 },
|
|
Packit |
c32a2d |
{0, "test-format", 0, test_format, 0, 0 },
|
|
Packit |
c32a2d |
{0, "test-encodings", 0, test_encodings, 0, 0},
|
|
Packit |
c32a2d |
{0, "query-format", 0, query_format, 0, 0},
|
|
Packit |
c32a2d |
{0, "name", GLO_ARG|GLO_CHAR, 0, &name, 0},
|
|
Packit |
c32a2d |
{0, "devbuffer", GLO_ARG|GLO_DOUBLE, 0, &device_buffer, 0},
|
|
Packit |
c32a2d |
{0, "timelimit", GLO_ARG|GLO_LONG, 0, &timelimit, 0},
|
|
Packit |
c32a2d |
{0, "wave-pat", GLO_ARG|GLO_CHAR, 0, &wave_patterns, 0},
|
|
Packit |
c32a2d |
{0, "wave-freq", GLO_ARG|GLO_CHAR, 0, &wave_freqs, 0},
|
|
Packit |
c32a2d |
{0, "wave-phase", GLO_ARG|GLO_CHAR, 0, &wave_phases, 0},
|
|
Packit |
c32a2d |
{0, "wave-limit", GLO_ARG|GLO_LONG, 0, &wave_limit, 0},
|
|
Packit |
c32a2d |
{0, 0, 0, 0, 0, 0}
|
|
Packit |
c32a2d |
};
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* An strtok() that also returns empty tokens on multiple separators. */
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static size_t mytok_count(const char *choppy)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t count = 0;
|
|
Packit |
c32a2d |
if(choppy)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
count = 1;
|
|
Packit |
c32a2d |
do {
|
|
Packit |
c32a2d |
if(*choppy == ',')
|
|
Packit |
c32a2d |
++count;
|
|
Packit |
c32a2d |
} while(*(++choppy));
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
return count;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static char *mytok(char **choppy)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char *tok;
|
|
Packit |
c32a2d |
if(!*choppy)
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
tok = *choppy;
|
|
Packit |
c32a2d |
while(**choppy && **choppy != ',')
|
|
Packit |
c32a2d |
++(*choppy);
|
|
Packit |
c32a2d |
/* Another token follows if we found a separator. */
|
|
Packit |
c32a2d |
if(**choppy == ',')
|
|
Packit |
c32a2d |
*(*choppy)++ = 0;
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
*choppy = NULL; /* Nothing left. */
|
|
Packit |
c32a2d |
return tok;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void setup_wavegen(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t count = 0;
|
|
Packit |
c32a2d |
size_t i;
|
|
Packit |
c32a2d |
double *freq = NULL;
|
|
Packit |
c32a2d |
double *phase = NULL;
|
|
Packit |
c32a2d |
const char **pat = NULL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(wave_freqs)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char *tok;
|
|
Packit |
c32a2d |
char *next;
|
|
Packit |
c32a2d |
count = mytok_count(wave_freqs);
|
|
Packit |
c32a2d |
freq = malloc(sizeof(double)*count);
|
|
Packit |
c32a2d |
if(!freq){ error("OOM!"); safe_exit(1); }
|
|
Packit |
c32a2d |
next = wave_freqs;
|
|
Packit |
c32a2d |
for(i=0; i
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
tok = mytok(&next;;
|
|
Packit |
c32a2d |
if(tok && *tok)
|
|
Packit |
c32a2d |
freq[i] = atof(tok);
|
|
Packit |
c32a2d |
else if(i)
|
|
Packit |
c32a2d |
freq[i] = freq[i-1];
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
freq[i] = 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else return;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(count && wave_patterns)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char *tok;
|
|
Packit |
c32a2d |
char *next = wave_patterns;
|
|
Packit |
c32a2d |
pat = malloc(sizeof(char*)*count);
|
|
Packit |
c32a2d |
if(!pat){ error("OOM!"); safe_exit(1); }
|
|
Packit |
c32a2d |
for(i=0; i
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
tok = mytok(&next;;
|
|
Packit |
c32a2d |
if((tok && *tok) || i==0)
|
|
Packit |
c32a2d |
pat[i] = tok;
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
pat[i] = pat[i-1];
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(count && wave_phases)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char *tok;
|
|
Packit |
c32a2d |
char *next = wave_phases;
|
|
Packit |
c32a2d |
phase = malloc(sizeof(double)*count);
|
|
Packit |
c32a2d |
if(!phase){ error("OOM!"); safe_exit(1); }
|
|
Packit |
c32a2d |
for(i=0; i
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
tok = mytok(&next;;
|
|
Packit |
c32a2d |
if(tok && *tok)
|
|
Packit |
c32a2d |
phase[i] = atof(tok);
|
|
Packit |
c32a2d |
else if(i)
|
|
Packit |
c32a2d |
phase[i] = phase[i-1];
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
phase[i] = 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
waver = wave_table_new( rate, channels, encoding, count, freq, pat, phase
|
|
Packit |
c32a2d |
, (size_t)wave_limit );
|
|
Packit |
c32a2d |
if(!waver)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
error("Cannot set up wave generator.");
|
|
Packit |
c32a2d |
safe_exit(132);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(verbose)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fprintf(stderr, "wave table of %" SIZE_P " samples\n", waver->samples);
|
|
Packit |
c32a2d |
/* TODO: There is a crash here! pat being optimised away ... */
|
|
Packit |
c32a2d |
for(i=0; i
|
|
Packit |
c32a2d |
fprintf( stderr, "wave %" SIZE_P ": %s @ %g Hz (%g Hz) p %g\n"
|
|
Packit |
c32a2d |
, i
|
|
Packit |
c32a2d |
, (pat && pat[i]) ? pat[i] : wave_pattern_default
|
|
Packit |
c32a2d |
, freq[i], waver->freq[i]
|
|
Packit |
c32a2d |
, phase ? phase[i] : 0 );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(phase)
|
|
Packit |
c32a2d |
free(phase);
|
|
Packit |
c32a2d |
if(pat)
|
|
Packit |
c32a2d |
free(pat);
|
|
Packit |
c32a2d |
if(freq)
|
|
Packit |
c32a2d |
free(freq);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* return 1 on success, 0 on failure */
|
|
Packit |
c32a2d |
int play_frame(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t got_samples;
|
|
Packit |
c32a2d |
size_t get_samples = pcmblock;
|
|
Packit |
c32a2d |
if(timelimit >= 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(offset >= timelimit)
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
else if(timelimit < offset+get_samples)
|
|
Packit |
c32a2d |
get_samples = (off_t)timelimit-offset;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(waver)
|
|
Packit |
c32a2d |
got_samples = wave_table_extract(waver, audio, get_samples);
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
got_samples = fread(audio, pcmframe, get_samples, input);
|
|
Packit |
c32a2d |
/* Play what is there to play (starting with second decode_frame call!) */
|
|
Packit |
c32a2d |
if(got_samples)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t got_bytes = pcmframe * got_samples;
|
|
Packit |
c32a2d |
if(out123_play(ao, audio, got_bytes) < (int)got_bytes)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!quiet)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
error2( "out123 error %i: %s"
|
|
Packit |
c32a2d |
, out123_errcode(ao), out123_strerror(ao) );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
safe_exit(133);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
offset += got_samples;
|
|
Packit |
c32a2d |
return 1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#if !defined(WIN32) && !defined(GENERIC)
|
|
Packit |
c32a2d |
static void catch_interrupt(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
intflag = TRUE;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
int main(int sys_argc, char ** sys_argv)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int result;
|
|
Packit |
c32a2d |
#if defined(WIN32)
|
|
Packit |
c32a2d |
_setmode(STDIN_FILENO, _O_BINARY);
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#if defined (WANT_WIN32_UNICODE)
|
|
Packit |
c32a2d |
if(win32_cmdline_utf8(&argc, &argv) != 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
error("Cannot convert command line to UTF8!");
|
|
Packit |
c32a2d |
safe_exit(76);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
argv = sys_argv;
|
|
Packit |
c32a2d |
argc = sys_argc;
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(!(fullprogname = compat_strdup(argv[0])))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
error("OOM"); /* Out Of Memory. Don't waste bytes on that error. */
|
|
Packit |
c32a2d |
safe_exit(1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Extract binary and path, take stuff before/after last / or \ . */
|
|
Packit |
c32a2d |
if( (cmd_name = strrchr(fullprogname, '/'))
|
|
Packit |
c32a2d |
|| (cmd_name = strrchr(fullprogname, '\\')))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* There is some explicit path. */
|
|
Packit |
c32a2d |
cmd_name[0] = 0; /* End byte for path. */
|
|
Packit |
c32a2d |
cmd_name++;
|
|
Packit |
c32a2d |
binpath = fullprogname;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
cmd_name = fullprogname; /* No path separators there. */
|
|
Packit |
c32a2d |
binpath = NULL; /* No path at all. */
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Get default flags. */
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
out123_handle *paro = out123_new();
|
|
Packit |
c32a2d |
out123_getparam_int(paro, OUT123_FLAGS, &outflags);
|
|
Packit |
c32a2d |
out123_del(paro);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifdef OS2
|
|
Packit |
c32a2d |
_wildcard(&argc,&argv);
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
while ((result = getlopt(argc, argv, opts)))
|
|
Packit |
c32a2d |
switch (result) {
|
|
Packit |
c32a2d |
case GLO_UNKNOWN:
|
|
Packit |
c32a2d |
fprintf (stderr, ME": invalid argument: %s\n", loptarg);
|
|
Packit |
c32a2d |
usage(1);
|
|
Packit |
c32a2d |
case GLO_NOARG:
|
|
Packit |
c32a2d |
fprintf (stderr, ME": missing argument for parameter: %s\n", loptarg);
|
|
Packit |
c32a2d |
usage(1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(quiet)
|
|
Packit |
c32a2d |
verbose = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Ensure cleanup before we cause too much mess. */
|
|
Packit |
c32a2d |
#if !defined(WIN32) && !defined(GENERIC)
|
|
Packit |
c32a2d |
catchsignal(SIGINT, catch_interrupt);
|
|
Packit |
c32a2d |
catchsignal(SIGTERM, catch_interrupt);
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
ao = out123_new();
|
|
Packit |
c32a2d |
if(!ao){ error("Failed to allocate output."); exit(1); }
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if
|
|
Packit |
c32a2d |
( 0
|
|
Packit |
c32a2d |
|| out123_param_int(ao, OUT123_FLAGS, outflags)
|
|
Packit |
c32a2d |
|| out123_param_float(ao, OUT123_PRELOAD, preload)
|
|
Packit |
c32a2d |
|| out123_param_int(ao, OUT123_GAIN, gain)
|
|
Packit |
c32a2d |
|| out123_param_int(ao, OUT123_VERBOSE, verbose)
|
|
Packit |
c32a2d |
|| out123_param_string(ao, OUT123_NAME, name)
|
|
Packit |
c32a2d |
|| out123_param_string(ao, OUT123_BINDIR, binpath)
|
|
Packit |
c32a2d |
|| out123_param_float(ao, OUT123_DEVICEBUFFER, device_buffer)
|
|
Packit |
c32a2d |
)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
error("Error setting output parameters. Do you need a usage reminder?");
|
|
Packit |
c32a2d |
usage(1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifdef HAVE_SETPRIORITY
|
|
Packit |
c32a2d |
if(aggressive) { /* tst */
|
|
Packit |
c32a2d |
int mypid = getpid();
|
|
Packit |
c32a2d |
if(!quiet) fprintf(stderr, ME": Aggressively trying to increase priority.\n");
|
|
Packit |
c32a2d |
if(setpriority(PRIO_PROCESS,mypid,-20))
|
|
Packit |
c32a2d |
error("Failed to aggressively increase priority.\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#if defined (HAVE_SCHED_SETSCHEDULER) && !defined (__CYGWIN__) && !defined (HAVE_WINDOWS_H)
|
|
Packit |
c32a2d |
/* Cygwin --realtime seems to fail when accessing network, using win32 set priority instead */
|
|
Packit |
c32a2d |
/* MinGW may have pthread installed, we prefer win32API */
|
|
Packit |
c32a2d |
if(realtime)
|
|
Packit |
c32a2d |
{ /* Get real-time priority */
|
|
Packit |
c32a2d |
struct sched_param sp;
|
|
Packit |
c32a2d |
if(!quiet) fprintf(stderr, ME": Getting real-time priority\n");
|
|
Packit |
c32a2d |
memset(&sp, 0, sizeof(struct sched_param));
|
|
Packit |
c32a2d |
sp.sched_priority = sched_get_priority_min(SCHED_FIFO);
|
|
Packit |
c32a2d |
if (sched_setscheduler(0, SCHED_RR, &sp) == -1)
|
|
Packit |
c32a2d |
error("Can't get realtime priority\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* make sure not Cygwin, it doesn't need it */
|
|
Packit |
c32a2d |
#if defined(WIN32) && defined(HAVE_WINDOWS_H)
|
|
Packit |
c32a2d |
/* argument "3" is equivalent to realtime priority class */
|
|
Packit |
c32a2d |
win32_set_priority(realtime ? 3 : w32_priority);
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(encoding_name)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
encoding = out123_enc_byname(encoding_name);
|
|
Packit |
c32a2d |
if(!encoding)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
error1("Unknown encoding '%s' given!\n", encoding_name);
|
|
Packit |
c32a2d |
safe_exit(1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
pcmframe = out123_encsize(encoding)*channels;
|
|
Packit |
c32a2d |
bufferblock = pcmblock*pcmframe;
|
|
Packit |
c32a2d |
audio = (unsigned char*) malloc(bufferblock);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
check_fatal_output(out123_set_buffer(ao, buffer_kb*1024));
|
|
Packit |
c32a2d |
/* This needs bufferblock set! */
|
|
Packit |
c32a2d |
check_fatal_output(out123_open(ao, driver, device));
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(verbose)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
long props = 0;
|
|
Packit |
c32a2d |
const char *encname;
|
|
Packit |
c32a2d |
char *realname = NULL;
|
|
Packit |
c32a2d |
encname = out123_enc_name(encoding);
|
|
Packit |
c32a2d |
fprintf(stderr, ME": format: %li Hz, %i channels, %s\n"
|
|
Packit |
c32a2d |
, rate, channels, encname ? encname : "???" );
|
|
Packit |
c32a2d |
out123_getparam_string(ao, OUT123_NAME, &realname);
|
|
Packit |
c32a2d |
if(realname)
|
|
Packit |
c32a2d |
fprintf(stderr, ME": output real name: %s\n", realname);
|
|
Packit |
c32a2d |
out123_getparam_int(ao, OUT123_PROPFLAGS, &props;;
|
|
Packit |
c32a2d |
if(props & OUT123_PROP_LIVE)
|
|
Packit |
c32a2d |
fprintf(stderr, ME": This is a live sink.\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
check_fatal_output(out123_start(ao, rate, channels, encoding));
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
input = stdin;
|
|
Packit |
c32a2d |
if(wave_freqs)
|
|
Packit |
c32a2d |
setup_wavegen();
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
while(play_frame() && !intflag)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* be happy */
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(intflag) /* Make it quick! */
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!quiet)
|
|
Packit |
c32a2d |
fprintf(stderr, ME": Interrupted. Dropping the ball.\n");
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
safe_exit(0); /* That closes output and restores terminal, too. */
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static char* output_enclist(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int i;
|
|
Packit |
c32a2d |
char *list = NULL;
|
|
Packit |
c32a2d |
char *pos;
|
|
Packit |
c32a2d |
size_t len = 0;
|
|
Packit |
c32a2d |
int enc_count = 0;
|
|
Packit |
c32a2d |
int *enc_codes = NULL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
enc_count = out123_enc_list(&enc_codes);
|
|
Packit |
c32a2d |
for(i=0;i
|
|
Packit |
c32a2d |
len += strlen(out123_enc_name(enc_codes[i]));
|
|
Packit |
c32a2d |
len += enc_count;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if((pos = list = malloc(len))) for(i=0;i
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char *name = out123_enc_name(enc_codes[i]);
|
|
Packit |
c32a2d |
if(i>0)
|
|
Packit |
c32a2d |
*(pos++) = ' ';
|
|
Packit |
c32a2d |
strcpy(pos, name);
|
|
Packit |
c32a2d |
pos+=strlen(name);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
free(enc_codes);
|
|
Packit |
c32a2d |
return list;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void print_title(FILE *o)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fprintf(o, "Simple audio output with raw PCM input\n");
|
|
Packit |
c32a2d |
fprintf(o, "\tversion %s; derived from mpg123 by Michael Hipp and others\n", PACKAGE_VERSION);
|
|
Packit |
c32a2d |
fprintf(o, "\tfree software (LGPL) without any warranty but with best wishes\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void usage(int err) /* print syntax & exit */
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
FILE* o = stdout;
|
|
Packit |
c32a2d |
if(err)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
o = stderr;
|
|
Packit |
c32a2d |
fprintf(o, ME": You made some mistake in program usage... let me briefly remind you:\n\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
print_title(o);
|
|
Packit |
c32a2d |
fprintf(o,"\nusage: %s [option(s)] [file(s) | URL(s) | -]\n", cmd_name);
|
|
Packit |
c32a2d |
fprintf(o,"supported options [defaults in brackets]:\n");
|
|
Packit |
c32a2d |
fprintf(o," -v increase verbosity level -q quiet (only print errors)\n");
|
|
Packit |
c32a2d |
fprintf(o," -t testmode (no output) -s write to stdout\n");
|
|
Packit |
c32a2d |
fprintf(o," -w f write output as WAV file\n");
|
|
Packit |
c32a2d |
fprintf(o," -b n output buffer: n Kbytes [0] \n");
|
|
Packit |
c32a2d |
fprintf(o," -r n set samplerate [44100]\n");
|
|
Packit |
c32a2d |
fprintf(o," -o m select output module -a d set audio device\n");
|
|
Packit |
c32a2d |
fprintf(o," -m single-channel (mono) instead of stereo\n");
|
|
Packit |
c32a2d |
#ifdef HAVE_SCHED_SETSCHEDULER
|
|
Packit |
c32a2d |
fprintf(o," -T get realtime priority\n");
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
fprintf(o," -? this help --version print name + version\n");
|
|
Packit |
c32a2d |
fprintf(o,"See the manpage out123(1) or call %s with --longhelp for more parameters and information.\n", cmd_name);
|
|
Packit |
c32a2d |
safe_exit(err);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void want_usage(char* arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
usage(0);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void long_usage(int err)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char *enclist;
|
|
Packit |
c32a2d |
FILE* o = stdout;
|
|
Packit |
c32a2d |
if(err)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
o = stderr;
|
|
Packit |
c32a2d |
fprintf(o, "You made some mistake in program usage... let me remind you:\n\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
enclist = output_enclist();
|
|
Packit |
c32a2d |
print_title(o);
|
|
Packit |
c32a2d |
fprintf(o,"\nusage: %s [option(s)] [file(s) | URL(s) | -]\n", cmd_name);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
fprintf(o," --name <n> set instance name (p.ex. JACK client)\n");
|
|
Packit |
c32a2d |
fprintf(o," -o <o> --output <o> select audio output module\n");
|
|
Packit |
c32a2d |
fprintf(o," --list-modules list the available modules\n");
|
|
Packit |
c32a2d |
fprintf(o," -a <d> --audiodevice <d> select audio device (for files, empty or - is stdout)\n");
|
|
Packit |
c32a2d |
fprintf(o," -s --stdout write raw audio to stdout (-o raw -a -)\n");
|
|
Packit |
c32a2d |
fprintf(o," -S --STDOUT play AND output stream (not implemented yet)\n");
|
|
Packit |
c32a2d |
fprintf(o," -O <f> --output <f> raw output to given file (-o raw -a <f>)\n");
|
|
Packit |
c32a2d |
fprintf(o," -w <f> --wav <f> write samples as WAV file in <f> (-o wav -a <f>)\n");
|
|
Packit |
c32a2d |
fprintf(o," --au <f> write samples as Sun AU file in <f> (-o au -a <f>)\n");
|
|
Packit |
c32a2d |
fprintf(o," --cdr <f> write samples as raw CD audio file in <f> (-o cdr -a <f>)\n");
|
|
Packit |
c32a2d |
fprintf(o," -r <r> --rate <r> set the audio output rate in Hz (default 44100)\n");
|
|
Packit |
c32a2d |
fprintf(o," -c <n> --channels <n> set channel count to <n>\n");
|
|
Packit |
c32a2d |
fprintf(o," -e <c> --encoding <c> set output encoding (%s)\n"
|
|
Packit |
c32a2d |
, enclist != NULL ? enclist : "OOM!");
|
|
Packit |
c32a2d |
fprintf(o," -m --mono set channel count to 1\n");
|
|
Packit |
c32a2d |
fprintf(o," --stereo set channel count to 2 (default)\n");
|
|
Packit |
c32a2d |
fprintf(o," --list-encodings list of encoding short and long names\n");
|
|
Packit |
c32a2d |
fprintf(o," --test-format return 0 if configued audio format is supported\n");
|
|
Packit |
c32a2d |
fprintf(o," --test-encodings print out possible encodings with given channels/rate\n");
|
|
Packit |
c32a2d |
fprintf(o," --query-format print out default format for given device, if any\n");
|
|
Packit |
c32a2d |
fprintf(o," -o h --headphones (aix/hp/sun) output on headphones\n");
|
|
Packit |
c32a2d |
fprintf(o," -o s --speaker (aix/hp/sun) output on speaker\n");
|
|
Packit |
c32a2d |
fprintf(o," -o l --lineout (aix/hp/sun) output to lineout\n");
|
|
Packit |
c32a2d |
#ifndef NOXFERMEM
|
|
Packit |
c32a2d |
fprintf(o," -b <n> --buffer <n> set play buffer (\"output cache\")\n");
|
|
Packit |
c32a2d |
fprintf(o," --preload <value> fraction of buffer to fill before playback\n");
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
fprintf(o," --devbuffer <s> set device buffer in seconds; <= 0 means default\n");
|
|
Packit |
c32a2d |
fprintf(o," --timelimit <s> set time limit in PCM samples if >= 0\n");
|
|
Packit |
c32a2d |
fprintf(o," --wave-freq <f> set wave generator frequency or list of those\n");
|
|
Packit |
c32a2d |
fprintf(o," with comma separation for enabling a generated\n");
|
|
Packit |
c32a2d |
fprintf(o," test signal instead of standard input,\n");
|
|
Packit |
c32a2d |
fprintf(o," empty value repeating the previous\n");
|
|
Packit |
c32a2d |
fprintf(o," --wave-pat set wave pattern(s) (out of those:\n");
|
|
Packit |
c32a2d |
fprintf(o," %s),\n", wave_pattern_list);
|
|
Packit |
c32a2d |
fprintf(o," empty value repeating the previous\n");
|
|
Packit |
c32a2d |
fprintf(o," --wave-phase set wave phase shift(s), negative values\n");
|
|
Packit |
c32a2d |
fprintf(o," inverting the pattern in time and\n");
|
|
Packit |
c32a2d |
fprintf(o," empty value repeating the previous\n");
|
|
Packit |
c32a2d |
fprintf(o," --wave-limit <l> soft limit on wave table size\n");
|
|
Packit |
c32a2d |
fprintf(o," -t --test no output, just read and discard data (-o test)\n");
|
|
Packit |
c32a2d |
fprintf(o," -v[*] --verbose increase verboselevel\n");
|
|
Packit |
c32a2d |
#ifdef HAVE_SETPRIORITY
|
|
Packit |
c32a2d |
fprintf(o," --aggressive tries to get higher priority (nice)\n");
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#if defined (HAVE_SCHED_SETSCHEDULER) || defined (HAVE_WINDOWS_H)
|
|
Packit |
c32a2d |
fprintf(o," -T --realtime tries to get realtime priority\n");
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
#ifdef HAVE_WINDOWS_H
|
|
Packit |
c32a2d |
fprintf(o," --priority <n> use specified process priority\n");
|
|
Packit |
c32a2d |
fprintf(o," accepts -2 to 3 as integer arguments\n");
|
|
Packit |
c32a2d |
fprintf(o," -2 as idle, 0 as normal and 3 as realtime.\n");
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
fprintf(o," -? --help give compact help\n");
|
|
Packit |
c32a2d |
fprintf(o," --longhelp give this long help listing\n");
|
|
Packit |
c32a2d |
fprintf(o," --version give name / version string\n");
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
fprintf(o,"\nSee the manpage out123(1) for more information.\n");
|
|
Packit |
c32a2d |
free(enclist);
|
|
Packit |
c32a2d |
safe_exit(err);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void want_long_usage(char* arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
long_usage(0);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void give_version(char* arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fprintf(stdout, "out123 "PACKAGE_VERSION"\n");
|
|
Packit |
c32a2d |
safe_exit(0);
|
|
Packit |
c32a2d |
}
|