|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
audio: audio output interface
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
copyright ?-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 Michael Hipp
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include <errno.h>
|
|
Packit |
c32a2d |
#include "mpg123app.h"
|
|
Packit |
c32a2d |
#include "audio.h"
|
|
Packit |
c32a2d |
#include "out123.h"
|
|
Packit |
c32a2d |
#include "common.h"
|
|
Packit |
c32a2d |
#include "sysutil.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifdef HAVE_SYS_WAIT_H
|
|
Packit |
c32a2d |
#include <sys/wait.h>
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
mpg123_string* audio_enclist(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int i;
|
|
Packit |
c32a2d |
mpg123_string *list;
|
|
Packit |
c32a2d |
size_t enc_count = 0;
|
|
Packit |
c32a2d |
const int *enc_codes = NULL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Only the encodings supported by libmpg123 build
|
|
Packit |
c32a2d |
Those returned by out123_enc_list() are a superset. */
|
|
Packit |
c32a2d |
mpg123_encodings(&enc_codes, &enc_count);
|
|
Packit |
c32a2d |
if((list = malloc(sizeof(*list))))
|
|
Packit |
c32a2d |
mpg123_init_string(list);
|
|
Packit |
c32a2d |
/* Further calls to mpg123 string lib are hardened against NULL. */
|
|
Packit |
c32a2d |
for(i=0;i
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(i>0)
|
|
Packit |
c32a2d |
mpg123_add_string(list, " ");
|
|
Packit |
c32a2d |
mpg123_add_string(list, out123_enc_name(enc_codes[i]));
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
return list;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void capline(mpg123_handle *mh, long rate)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int enci;
|
|
Packit |
c32a2d |
const int *encs;
|
|
Packit |
c32a2d |
size_t num_encs;
|
|
Packit |
c32a2d |
mpg123_encodings(&encs, &num_encs);
|
|
Packit |
c32a2d |
fprintf(stderr," %5ld |", pitch_rate(rate));
|
|
Packit |
c32a2d |
for(enci=0; enci
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
switch(mpg123_format_support(mh, rate, encs[enci]))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
case MPG123_MONO: fprintf(stderr, " M |"); break;
|
|
Packit |
c32a2d |
case MPG123_STEREO: fprintf(stderr, " S |"); break;
|
|
Packit |
c32a2d |
case MPG123_MONO|MPG123_STEREO: fprintf(stderr, " M/S |"); break;
|
|
Packit |
c32a2d |
default: fprintf(stderr, " |");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
fprintf(stderr, "\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void print_capabilities(out123_handle *ao, mpg123_handle *mh)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int r,e;
|
|
Packit |
c32a2d |
const long *rates;
|
|
Packit |
c32a2d |
size_t num_rates;
|
|
Packit |
c32a2d |
const int *encs;
|
|
Packit |
c32a2d |
size_t num_encs;
|
|
Packit |
c32a2d |
char *name;
|
|
Packit |
c32a2d |
char *dev;
|
|
Packit |
c32a2d |
out123_driver_info(ao, &name, &dev;;
|
|
Packit |
c32a2d |
mpg123_rates(&rates, &num_rates);
|
|
Packit |
c32a2d |
mpg123_encodings(&encs, &num_encs);
|
|
Packit |
c32a2d |
fprintf(stderr,"\nAudio driver: %s\nAudio device: %s\nAudio capabilities:\n(matrix of [S]tereo or [M]ono support for sample format and rate in Hz)\n |", name, dev);
|
|
Packit |
c32a2d |
for(e=0;e
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char *encname = out123_enc_name(encs[e]);
|
|
Packit |
c32a2d |
fprintf(stderr," %5s |", encname ? encname : "???");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
fprintf(stderr,"\n ------|");
|
|
Packit |
c32a2d |
for(e=0;e
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
fprintf(stderr, "\n");
|
|
Packit |
c32a2d |
for(r=0; r
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(param.force_rate) capline(mh, param.force_rate);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
fprintf(stderr,"\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Quick-shot paired table setup with remembering search in it.
|
|
Packit |
c32a2d |
this is for storing pairs of output sampling rate and decoding
|
|
Packit |
c32a2d |
sampling rate. */
|
|
Packit |
c32a2d |
struct ratepair { long a; long b; };
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
long brate(struct ratepair *table, long arate, int count, int *last)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int i = 0;
|
|
Packit |
c32a2d |
int j;
|
|
Packit |
c32a2d |
for(j=0; j<2; ++j)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
i = i ? 0 : *last;
|
|
Packit |
c32a2d |
for(; i
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
*last = i;
|
|
Packit |
c32a2d |
return table[i].b;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* This uses the currently opened audio device, queries its caps.
|
|
Packit |
c32a2d |
In case of buffered playback, this works _once_ by querying the buffer for the caps before entering the main loop. */
|
|
Packit |
c32a2d |
void audio_capabilities(out123_handle *ao, mpg123_handle *mh)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int force_fmt = 0;
|
|
Packit |
c32a2d |
size_t ri;
|
|
Packit |
c32a2d |
/* Pitching introduces a difference between decoder rate and playback rate. */
|
|
Packit |
c32a2d |
long decode_rate;
|
|
Packit |
c32a2d |
const long *rates;
|
|
Packit |
c32a2d |
long *outrates;
|
|
Packit |
c32a2d |
struct ratepair *unpitch;
|
|
Packit |
c32a2d |
struct mpg123_fmt *outfmts = NULL;
|
|
Packit |
c32a2d |
int fmtcount;
|
|
Packit |
c32a2d |
size_t num_rates, rlimit;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
debug("audio_capabilities");
|
|
Packit |
c32a2d |
mpg123_rates(&rates, &num_rates);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
mpg123_format_none(mh); /* Start with nothing. */
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(param.force_encoding != NULL)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!param.quiet)
|
|
Packit |
c32a2d |
fprintf(stderr, "Note: forcing output encoding %s\n", param.force_encoding);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
force_fmt = out123_enc_byname(param.force_encoding);
|
|
Packit |
c32a2d |
if(!force_fmt)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
error1("Failed to find an encoding to match requested \"%s\"!\n"
|
|
Packit |
c32a2d |
, param.force_encoding);
|
|
Packit |
c32a2d |
return; /* No capabilities at all... */
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else if(param.verbose > 2)
|
|
Packit |
c32a2d |
fprintf(stderr, "Note: forcing encoding code 0x%x (%s)\n"
|
|
Packit |
c32a2d |
, force_fmt, out123_enc_name(force_fmt));
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Lots of preparation of rate lists. */
|
|
Packit |
c32a2d |
rlimit = param.force_rate > 0 ? num_rates+1 : num_rates;
|
|
Packit |
c32a2d |
outrates = malloc(sizeof(*rates)*rlimit);
|
|
Packit |
c32a2d |
unpitch = malloc(sizeof(*unpitch)*rlimit);
|
|
Packit |
c32a2d |
if(!outrates || !unpitch)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!param.quiet)
|
|
Packit |
c32a2d |
error("DOOM");
|
|
Packit |
c32a2d |
return;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
for(ri = 0; ri
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
decode_rate = ri < num_rates ? rates[ri] : param.force_rate;
|
|
Packit |
c32a2d |
outrates[ri] = pitch_rate(decode_rate);
|
|
Packit |
c32a2d |
unpitch[ri].a = outrates[ri];
|
|
Packit |
c32a2d |
unpitch[ri].b = decode_rate;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Actually query formats possible with given rates. */
|
|
Packit |
c32a2d |
fmtcount = out123_formats(ao, outrates, rlimit, 1, 2, &outfmts);
|
|
Packit |
c32a2d |
free(outrates);
|
|
Packit |
c32a2d |
if(fmtcount > 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int fi;
|
|
Packit |
c32a2d |
int unpitch_i = 0;
|
|
Packit |
c32a2d |
if(param.verbose > 1 && outfmts[0].encoding > 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char *encname = out123_enc_name(outfmts[0].encoding);
|
|
Packit |
c32a2d |
fprintf(stderr, "Note: default format %li Hz, %i channels, %s\n"
|
|
Packit |
c32a2d |
, outfmts[0].rate, outfmts[0].channels
|
|
Packit |
c32a2d |
, encname ? encname : "???" );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
for(fi=1; fi
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int fmts = outfmts[fi].encoding;
|
|
Packit |
c32a2d |
if(param.verbose > 2)
|
|
Packit |
c32a2d |
fprintf( stderr
|
|
Packit |
c32a2d |
, "Note: output support for %li Hz, %i channels: 0x%x\n"
|
|
Packit |
c32a2d |
, outfmts[fi].rate, outfmts[fi].channels, outfmts[fi].encoding );
|
|
Packit |
c32a2d |
if(force_fmt)
|
|
Packit |
c32a2d |
{ /* Filter for forced encoding. */
|
|
Packit |
c32a2d |
if((fmts & force_fmt) == force_fmt)
|
|
Packit |
c32a2d |
fmts = force_fmt;
|
|
Packit |
c32a2d |
else /* Nothing else! */
|
|
Packit |
c32a2d |
fmts = 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
mpg123_format( mh
|
|
Packit |
c32a2d |
, brate(unpitch, outfmts[fi].rate, rlimit, &unpitch_i)
|
|
Packit |
c32a2d |
, outfmts[fi].channels, fmts );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
free(outfmts);
|
|
Packit |
c32a2d |
free(unpitch);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(param.verbose > 1) print_capabilities(ao, mh);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
int set_pitch(mpg123_handle *fr, out123_handle *ao, double new_pitch)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
double old_pitch = param.pitch;
|
|
Packit |
c32a2d |
long rate;
|
|
Packit |
c32a2d |
int channels, format;
|
|
Packit |
c32a2d |
int smode = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Be safe, check support. */
|
|
Packit |
c32a2d |
if(mpg123_getformat(fr, &rate, &channels, &format) != MPG123_OK)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* We might just not have a track handy. */
|
|
Packit |
c32a2d |
error("There is no current audio format, cannot apply pitch. This might get fixed in future.");
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
param.pitch = new_pitch;
|
|
Packit |
c32a2d |
if(param.pitch < -0.99) param.pitch = -0.99;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(channels == 1) smode = MPG123_MONO;
|
|
Packit |
c32a2d |
if(channels == 2) smode = MPG123_STEREO;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
out123_stop(ao);
|
|
Packit |
c32a2d |
/* Remember: This takes param.pitch into account. */
|
|
Packit |
c32a2d |
audio_capabilities(ao, fr);
|
|
Packit |
c32a2d |
if(!(mpg123_format_support(fr, rate, format) & smode))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Note: When using --pitch command line parameter, you can go higher
|
|
Packit |
c32a2d |
because a lower decoder sample rate is automagically chosen.
|
|
Packit |
c32a2d |
Here, we'd need to switch decoder rate during track... good? */
|
|
Packit |
c32a2d |
error("Reached a hardware limit there with pitch!");
|
|
Packit |
c32a2d |
param.pitch = old_pitch;
|
|
Packit |
c32a2d |
audio_capabilities(ao, fr);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
return out123_start(ao, pitch_rate(rate), channels, format);
|
|
Packit |
c32a2d |
}
|