/*
* Copyright 2017 Yann Weber
*
* This file is part of SndPipe.
*
* SndPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* SndPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SndPipe. If not, see .
*/
#include
#include
#include
#include
#ifdef SDL2
#include
#else
#include
#pragma GCC error "Not supported yet :'("
#endif
#define SNDPIPE_LONG_OPT {\
{"freq", required_argument, 0, 'f'},\
{"signed", no_argument, 0, 'S'},\
{"bit-size", required_argument, 0, 'B'},\
{"endianness", required_argument, 0, 'E'},\
{"float", no_argument, 0, 'F'},\
{"channels", required_argument, 0, 'c'},\
{"samples", required_argument, 0, 's'},\
{"output", required_argument, 0, 'o'},\
{"no-sound", no_argument, 0, 'N'},\
{"verbose", no_argument, 0, 'v'},\
{"help", no_argument, 0, 'h'},\
{0, 0, 0, 0}\
}
#define SNDPIPE_SHORT_OPT "f:SB:E:Fc:s:o:Nvh"
#define SNDPIPE_OPT_HELP {\
{"Input frequencie (default is 48000)", "FREQ"},\
{"If given signed numbers are expected (default for 32 bits)", NULL},\
{"Number of bits per number. One of : 32, 16 and 8 (integers only)",\
"BIT_COUNT"},\
{"Number endianess. Accepted values are : LSB, MSB and SYS (system's \
native byte order)", "BYTEORDER"},\
{"If given, floating point numbers are excepted instead of intergers\
(32 bits only)",\
NULL},\
{"Channels count. 1 for mono, 2 for stereo, ...", "COUNT"},\
{"Samples per audioframe. Default value is 4096", "INT"},\
{"Output filename. STDOUT if '-' given", "FILENAME"},\
{"Don't play sound on soundcard using SDL", NULL},\
{"Display informations on stderr when given", NULL},\
{"Print this help and exit", NULL},\
{"", NULL}\
}
#define SNDPIPE_UNSIGNED 0
#define SNDPIPE_SIGNED 1
#define SNDPIPE_ESYS 0
#define SNDPIPE_ELSB 2
#define SNDPIPE_EMSB 4
#define SNDPIPE_BSZ_8 0
#define SNDPIPE_BSZ_16 8
#define SNDPIPE_BSZ_32 16
#define SNDPIPE_INT32 0
#define SNDPIPE_FLOAT32 32
#define SNDPIPE_SDL_FMT {\
AUDIO_U8,\
AUDIO_S8,\
0,\
0,\
0,\
0,\
0,\
0,\
AUDIO_U16SYS,\
AUDIO_S16SYS,\
AUDIO_U16LSB,\
AUDIO_S16LSB,\
AUDIO_U16MSB,\
AUDIO_S16MSB,\
0,\
0,\
AUDIO_S32SYS,\
AUDIO_F32SYS,\
AUDIO_S32LSB,\
AUDIO_F32LSB,\
AUDIO_S32MSB,\
AUDIO_F32MSB\
}
static int VERBOSITY = 0;
static int SILENT = 0;
void usage()
{
static struct option opts[] = SNDPIPE_LONG_OPT;
static char *help[][2] = SNDPIPE_OPT_HELP;
size_t i;
printf("Usage : generator | sndpipe [OPTIONS]\n");
printf("or : sndpipe [OPTIONS] < file\n");
printf("\t\n");
printf("Options list\n");
i=0;
while(opts[i].name)
{
printf("\t-%c, --%s", opts[i].val, opts[i].name);
if(opts[i].has_arg == required_argument)
{
printf("=%s\n",
help[i][1]?help[i][1]:"ARG");
}
else if(opts[i].has_arg == optional_argument)
{
printf("[=%s]\n",
help[i][1]?help[i][1]:"ARG");
}
else
{
printf("\n");
}
printf("\t\t%s\n\n", help[i][0]);
i++;
}
}
void audio_cllbck(void *udata, Uint8 *stream, int len)
{
int ret;
while(len)
{
if((ret = read(0, stream, len)) == -1)
{
perror("Error reading stdin");
exit(1);
}
if (ret == 0)
{
fprintf(stderr, "Stdin EOF... Exiting...\n");
*(int*)udata = 0;
break;
}
len -= ret;
}
return;
}
int main(int argc, char **argv)
{
static struct option long_opts[] = SNDPIPE_LONG_OPT;
static SDL_AudioFormat opt_fmt[] = SNDPIPE_SDL_FMT;
SDL_AudioSpec want, have;
SDL_AudioDeviceID dev;
int running, c, opt_i, ret, fmt_idx;
short err;
SDL_memset(&want, 0, sizeof(want));
want.freq = 48000;
want.format = AUDIO_U8;
want.channels = 2;
want.samples = 4096;
want.callback = audio_cllbck;
want.userdata = &running;
fmt_idx = 0;
while(1)
{
c = getopt_long(argc, argv, SNDPIPE_SHORT_OPT, long_opts, &opt_i);
if(c == -1)
{
break;
}
switch(c)
{
case 'v':
VERBOSITY++;
break;
case 'f':
want.freq = atoi(optarg);
break;
case 'S':
fmt_idx |= SNDPIPE_SIGNED;
break;
case 'B':
switch(ret = atoi(optarg))
{
case 8:
fmt_idx |= SNDPIPE_BSZ_8;
break;
case 16:
fmt_idx |= SNDPIPE_BSZ_16;
break;
case 32:
fmt_idx |= SNDPIPE_BSZ_32;
break;
default:
fprintf(stderr, "\
Invalid bits count %d\n", ret);
err = 1;
break;
}
break;
case 'E':
if( !strncmp("LSB", optarg, 4)\
|| !strncmp("lsb", optarg, 4))
{
fmt_idx |= SNDPIPE_ELSB;
}
else if(!strncmp("MSB", optarg, 4)\
|| !strncmp("msb", optarg, 4))
{
fmt_idx |= SNDPIPE_EMSB;
}
else if(!strncmp("SYS", optarg, 4)\
|| !strncmp("sys", optarg, 4))
{
fmt_idx |= SNDPIPE_ESYS;
}
else
{
fprintf(stderr, "\
Invalid endianness '%s'\n", optarg);
err = 1;
}
break;
case 'F':
fmt_idx |= SNDPIPE_FLOAT32;
break;
case 'c':
want.channels = atoi(optarg);
if(want.channels < 1)
{
fprintf(stderr, "Invalid channel count \
%d\n", want.channels);
err = 1;
}
break;
case 's':
want.samples = atoi(optarg);
break;
case 'o':
fprintf(stderr, "\
Ouptut file not implemented yet :'(\n");
break;
case 'N':
SILENT = 1;
break;
case 'h':
usage();
exit(0);
default:
/* ?? */
err = 1;
}
}
if(err)
{
usage();
exit(1);
}
if((fmt_idx & (SNDPIPE_BSZ_32 | SNDPIPE_SIGNED))\
== (SNDPIPE_BSZ_32| SNDPIPE_SIGNED))
{
fmt_idx &= 0xFF - SNDPIPE_SIGNED;
}
if(fmt_idx & SNDPIPE_FLOAT32)
{
if(!(fmt_idx & SNDPIPE_BSZ_32))
{
fprintf(stderr, "Floats only supported for 32 bits.\n\n");
usage();
exit(1);
}
fmt_idx &= 0xFF - SNDPIPE_FLOAT32;
fmt_idx |= 1; //correcting index for float32
}
if(fmt_idx & (SNDPIPE_ELSB | SNDPIPE_EMSB) \
&& !(fmt_idx & (SNDPIPE_BSZ_16 | SNDPIPE_BSZ_32)))
{
fprintf(stderr,
"Specifying endianness is not supported for 8-bit\n\n");
usage();
exit(1);
}
if(fmt_idx > 21)
{
fprintf(stderr, "Fatal error : invalid format\n\n");
exit(2);
}
if(err)
{
fprintf(stderr, "\n");
exit(1);
}
want.format = opt_fmt[fmt_idx];
if(!SILENT)
{
if(SDL_Init(SDL_INIT_AUDIO ) <0 )
{
fprintf(stderr,
"Unable to initialize SDL : '%s'\n", SDL_GetError());
return 1;
}
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
if(dev == 0)
{
fprintf(stderr,
"Failed to open an audio device : '%s'\n",
SDL_GetError());
return 1;
}
}
if(!SILENT)
{
running = 1;
SDL_PauseAudioDevice(dev, 0); /* playing */
while(running)
{
SDL_Delay(1);
}
SDL_Quit();
}
}