Pipe stdin to soundcard using SDL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sndpipe.c 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /*
  2. * Copyright 2017 Yann Weber
  3. *
  4. * This file is part of SndPipe.
  5. *
  6. * SndPipe is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * any later version.
  10. *
  11. * SndPipe is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with SndPipe. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <unistd.h>
  20. #include <stdio.h>
  21. #include <getopt.h>
  22. #include <errno.h>
  23. #ifdef SDL2
  24. #include <SDL2/SDL.h>
  25. #else
  26. #include <SDL/SDL.h>
  27. #pragma GCC error "Not supported yet :'("
  28. #endif
  29. #define SNDPIPE_LONG_OPT {\
  30. {"freq", required_argument, 0, 'f'},\
  31. {"signed", no_argument, 0, 'S'},\
  32. {"bit-size", required_argument, 0, 'B'},\
  33. {"endianness", required_argument, 0, 'E'},\
  34. {"float", no_argument, 0, 'F'},\
  35. {"channels", required_argument, 0, 'c'},\
  36. {"samples", required_argument, 0, 's'},\
  37. {"output", required_argument, 0, 'o'},\
  38. {"no-sound", no_argument, 0, 'N'},\
  39. {"verbose", no_argument, 0, 'v'},\
  40. {"help", no_argument, 0, 'h'},\
  41. {0, 0, 0, 0}\
  42. }
  43. #define SNDPIPE_SHORT_OPT "f:SB:E:Fc:s:o:Nvh"
  44. #define SNDPIPE_OPT_HELP {\
  45. {"Input frequencie (default is 48000)", "FREQ"},\
  46. {"If given signed numbers are expected (default for 32 bits)", NULL},\
  47. {"Number of bits per number. One of : 32, 16 and 8 (integers only)",\
  48. "BIT_COUNT"},\
  49. {"Number endianess. Accepted values are : LSB, MSB and SYS (system's \
  50. native byte order)", "BYTEORDER"},\
  51. {"If given, floating point numbers are excepted instead of intergers\
  52. (32 bits only)",\
  53. NULL},\
  54. {"Channels count. 1 for mono, 2 for stereo, ...", "COUNT"},\
  55. {"Samples per audioframe. Default value is 4096", "INT"},\
  56. {"Output filename. STDOUT if '-' given", "FILENAME"},\
  57. {"Don't play sound on soundcard using SDL", NULL},\
  58. {"Display informations on stderr when given", NULL},\
  59. {"Print this help and exit", NULL},\
  60. {"", NULL}\
  61. }
  62. #define SNDPIPE_UNSIGNED 0
  63. #define SNDPIPE_SIGNED 1
  64. #define SNDPIPE_ESYS 0
  65. #define SNDPIPE_ELSB 2
  66. #define SNDPIPE_EMSB 4
  67. #define SNDPIPE_BSZ_8 0
  68. #define SNDPIPE_BSZ_16 8
  69. #define SNDPIPE_BSZ_32 16
  70. #define SNDPIPE_INT32 0
  71. #define SNDPIPE_FLOAT32 32
  72. #define SNDPIPE_SDL_FMT {\
  73. AUDIO_U8,\
  74. AUDIO_S8,\
  75. 0,\
  76. 0,\
  77. 0,\
  78. 0,\
  79. 0,\
  80. 0,\
  81. AUDIO_U16SYS,\
  82. AUDIO_S16SYS,\
  83. AUDIO_U16LSB,\
  84. AUDIO_S16LSB,\
  85. AUDIO_U16MSB,\
  86. AUDIO_S16MSB,\
  87. 0,\
  88. 0,\
  89. AUDIO_S32SYS,\
  90. AUDIO_F32SYS,\
  91. AUDIO_S32LSB,\
  92. AUDIO_F32LSB,\
  93. AUDIO_S32MSB,\
  94. AUDIO_F32MSB\
  95. }
  96. static int VERBOSITY = 0;
  97. static int SILENT = 0;
  98. void usage()
  99. {
  100. static struct option opts[] = SNDPIPE_LONG_OPT;
  101. static char *help[][2] = SNDPIPE_OPT_HELP;
  102. size_t i;
  103. printf("Usage : generator | sndpipe [OPTIONS]\n");
  104. printf("or : sndpipe [OPTIONS] < file\n");
  105. printf("\t\n");
  106. printf("Options list\n");
  107. i=0;
  108. while(opts[i].name)
  109. {
  110. printf("\t-%c, --%s", opts[i].val, opts[i].name);
  111. if(opts[i].has_arg == required_argument)
  112. {
  113. printf("=%s\n",
  114. help[i][1]?help[i][1]:"ARG");
  115. }
  116. else if(opts[i].has_arg == optional_argument)
  117. {
  118. printf("[=%s]\n",
  119. help[i][1]?help[i][1]:"ARG");
  120. }
  121. else
  122. {
  123. printf("\n");
  124. }
  125. printf("\t\t%s\n\n", help[i][0]);
  126. i++;
  127. }
  128. }
  129. void audio_cllbck(void *udata, Uint8 *stream, int len)
  130. {
  131. int ret;
  132. while(len)
  133. {
  134. if((ret = read(0, stream, len)) == -1)
  135. {
  136. perror("Error reading stdin");
  137. exit(1);
  138. }
  139. if (ret == 0)
  140. {
  141. fprintf(stderr, "Stdin EOF... Exiting...\n");
  142. *(int*)udata = 0;
  143. break;
  144. }
  145. len -= ret;
  146. }
  147. return;
  148. }
  149. int main(int argc, char **argv)
  150. {
  151. static struct option long_opts[] = SNDPIPE_LONG_OPT;
  152. static SDL_AudioFormat opt_fmt[] = SNDPIPE_SDL_FMT;
  153. SDL_AudioSpec want, have;
  154. SDL_AudioDeviceID dev;
  155. int running, c, opt_i, ret, fmt_idx;
  156. short err;
  157. SDL_memset(&want, 0, sizeof(want));
  158. want.freq = 48000;
  159. want.format = AUDIO_U8;
  160. want.channels = 2;
  161. want.samples = 4096;
  162. want.callback = audio_cllbck;
  163. want.userdata = &running;
  164. fmt_idx = 0;
  165. while(1)
  166. {
  167. c = getopt_long(argc, argv, SNDPIPE_SHORT_OPT, long_opts, &opt_i);
  168. if(c == -1)
  169. {
  170. break;
  171. }
  172. switch(c)
  173. {
  174. case 'v':
  175. VERBOSITY++;
  176. break;
  177. case 'f':
  178. want.freq = atoi(optarg);
  179. break;
  180. case 'S':
  181. fmt_idx |= SNDPIPE_SIGNED;
  182. break;
  183. case 'B':
  184. switch(ret = atoi(optarg))
  185. {
  186. case 8:
  187. fmt_idx |= SNDPIPE_BSZ_8;
  188. break;
  189. case 16:
  190. fmt_idx |= SNDPIPE_BSZ_16;
  191. break;
  192. case 32:
  193. fmt_idx |= SNDPIPE_BSZ_32;
  194. break;
  195. default:
  196. fprintf(stderr, "\
  197. Invalid bits count %d\n", ret);
  198. err = 1;
  199. break;
  200. }
  201. break;
  202. case 'E':
  203. if( !strncmp("LSB", optarg, 4)\
  204. || !strncmp("lsb", optarg, 4))
  205. {
  206. fmt_idx |= SNDPIPE_ELSB;
  207. }
  208. else if(!strncmp("MSB", optarg, 4)\
  209. || !strncmp("msb", optarg, 4))
  210. {
  211. fmt_idx |= SNDPIPE_EMSB;
  212. }
  213. else if(!strncmp("SYS", optarg, 4)\
  214. || !strncmp("sys", optarg, 4))
  215. {
  216. fmt_idx |= SNDPIPE_ESYS;
  217. }
  218. else
  219. {
  220. fprintf(stderr, "\
  221. Invalid endianness '%s'\n", optarg);
  222. err = 1;
  223. }
  224. break;
  225. case 'F':
  226. fmt_idx |= SNDPIPE_FLOAT32;
  227. break;
  228. case 'c':
  229. want.channels = atoi(optarg);
  230. if(want.channels < 1)
  231. {
  232. fprintf(stderr, "Invalid channel count \
  233. %d\n", want.channels);
  234. err = 1;
  235. }
  236. break;
  237. case 's':
  238. want.samples = atoi(optarg);
  239. break;
  240. case 'o':
  241. fprintf(stderr, "\
  242. Ouptut file not implemented yet :'(\n");
  243. break;
  244. case 'N':
  245. SILENT = 1;
  246. break;
  247. case 'h':
  248. usage();
  249. exit(0);
  250. default:
  251. /* ?? */
  252. err = 1;
  253. }
  254. }
  255. if(err)
  256. {
  257. usage();
  258. exit(1);
  259. }
  260. if((fmt_idx & (SNDPIPE_BSZ_32 | SNDPIPE_SIGNED))\
  261. == (SNDPIPE_BSZ_32| SNDPIPE_SIGNED))
  262. {
  263. fmt_idx &= 0xFF - SNDPIPE_SIGNED;
  264. }
  265. if(fmt_idx & SNDPIPE_FLOAT32)
  266. {
  267. if(!(fmt_idx & SNDPIPE_BSZ_32))
  268. {
  269. fprintf(stderr, "Floats only supported for 32 bits.\n\n");
  270. usage();
  271. exit(1);
  272. }
  273. fmt_idx &= 0xFF - SNDPIPE_FLOAT32;
  274. fmt_idx |= 1; //correcting index for float32
  275. }
  276. if(fmt_idx & (SNDPIPE_ELSB | SNDPIPE_EMSB) \
  277. && !(fmt_idx & (SNDPIPE_BSZ_16 | SNDPIPE_BSZ_32)))
  278. {
  279. fprintf(stderr,
  280. "Specifying endianness is not supported for 8-bit\n\n");
  281. usage();
  282. exit(1);
  283. }
  284. if(fmt_idx > 21)
  285. {
  286. fprintf(stderr, "Fatal error : invalid format\n\n");
  287. exit(2);
  288. }
  289. if(err)
  290. {
  291. fprintf(stderr, "\n");
  292. exit(1);
  293. }
  294. want.format = opt_fmt[fmt_idx];
  295. if(!SILENT)
  296. {
  297. if(SDL_Init(SDL_INIT_AUDIO ) <0 )
  298. {
  299. fprintf(stderr,
  300. "Unable to initialize SDL : '%s'\n", SDL_GetError());
  301. return 1;
  302. }
  303. dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
  304. if(dev == 0)
  305. {
  306. fprintf(stderr,
  307. "Failed to open an audio device : '%s'\n",
  308. SDL_GetError());
  309. return 1;
  310. }
  311. }
  312. if(!SILENT)
  313. {
  314. running = 1;
  315. SDL_PauseAudioDevice(dev, 0); /* playing */
  316. while(running)
  317. {
  318. SDL_Delay(1);
  319. }
  320. SDL_Quit();
  321. }
  322. }