/* * 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(); } }