playwave.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /*
  2. PLAYWAVE: A test application for the SDL mixer library.
  3. Copyright (C) 1997-2013 Sam Lantinga <[email protected]>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. /* $Id$ */
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #ifdef unix
  23. #include <unistd.h>
  24. #endif
  25. #include "SDL.h"
  26. #include "SDL_mixer.h"
  27. #ifdef HAVE_SIGNAL_H
  28. #include <signal.h>
  29. #endif
  30. /*
  31. * rcg06132001 various mixer tests. Define the ones you want.
  32. */
  33. /*#define TEST_MIX_DECODERS*/
  34. /*#define TEST_MIX_VERSIONS*/
  35. /*#define TEST_MIX_CHANNELFINISHED*/
  36. /*#define TEST_MIX_PANNING*/
  37. /*#define TEST_MIX_DISTANCE*/
  38. /*#define TEST_MIX_POSITION*/
  39. #if (defined TEST_MIX_POSITION)
  40. #if (defined TEST_MIX_PANNING)
  41. #error TEST_MIX_POSITION interferes with TEST_MIX_PANNING.
  42. #endif
  43. #if (defined TEST_MIX_DISTANCE)
  44. #error TEST_MIX_POSITION interferes with TEST_MIX_DISTANCE.
  45. #endif
  46. #endif
  47. /* rcg06192001 for debugging purposes. */
  48. static void output_test_warnings(void)
  49. {
  50. #if (defined TEST_MIX_CHANNELFINISHED)
  51. fprintf(stderr, "Warning: TEST_MIX_CHANNELFINISHED is enabled in this binary...\n");
  52. #endif
  53. #if (defined TEST_MIX_PANNING)
  54. fprintf(stderr, "Warning: TEST_MIX_PANNING is enabled in this binary...\n");
  55. #endif
  56. #if (defined TEST_MIX_VERSIONS)
  57. fprintf(stderr, "Warning: TEST_MIX_VERSIONS is enabled in this binary...\n");
  58. #endif
  59. #if (defined TEST_MIX_DISTANCE)
  60. fprintf(stderr, "Warning: TEST_MIX_DISTANCE is enabled in this binary...\n");
  61. #endif
  62. #if (defined TEST_MIX_POSITION)
  63. fprintf(stderr, "Warning: TEST_MIX_POSITION is enabled in this binary...\n");
  64. #endif
  65. }
  66. static int audio_open = 0;
  67. static Mix_Chunk *wave = NULL;
  68. /* rcg06042009 Report available decoders. */
  69. #if (defined TEST_MIX_DECODERS)
  70. static void report_decoders(void)
  71. {
  72. int i, total;
  73. printf("Supported decoders...\n");
  74. total = Mix_GetNumChunkDecoders();
  75. for (i = 0; i < total; i++) {
  76. fprintf(stderr, " - chunk decoder: %s\n", Mix_GetChunkDecoder(i));
  77. }
  78. total = Mix_GetNumMusicDecoders();
  79. for (i = 0; i < total; i++) {
  80. fprintf(stderr, " - music decoder: %s\n", Mix_GetMusicDecoder(i));
  81. }
  82. }
  83. #endif
  84. /* rcg06192001 Check new Mixer version API. */
  85. #if (defined TEST_MIX_VERSIONS)
  86. static void output_versions(const char *libname, const SDL_version *compiled,
  87. const SDL_version *linked)
  88. {
  89. fprintf(stderr,
  90. "This program was compiled against %s %d.%d.%d,\n"
  91. " and is dynamically linked to %d.%d.%d.\n", libname,
  92. compiled->major, compiled->minor, compiled->patch,
  93. linked->major, linked->minor, linked->patch);
  94. }
  95. static void test_versions(void)
  96. {
  97. SDL_version compiled;
  98. const SDL_version *linked;
  99. SDL_VERSION(&compiled);
  100. linked = SDL_Linked_Version();
  101. output_versions("SDL", &compiled, linked);
  102. SDL_MIXER_VERSION(&compiled);
  103. linked = Mix_Linked_Version();
  104. output_versions("SDL_mixer", &compiled, linked);
  105. }
  106. #endif
  107. #ifdef TEST_MIX_CHANNELFINISHED /* rcg06072001 */
  108. static volatile int channel_is_done = 0;
  109. static void channel_complete_callback(int chan)
  110. {
  111. Mix_Chunk *done_chunk = Mix_GetChunk(chan);
  112. fprintf(stderr, "We were just alerted that Mixer channel #%d is done.\n", chan);
  113. fprintf(stderr, "Channel's chunk pointer is (%p).\n", done_chunk);
  114. fprintf(stderr, " Which %s correct.\n", (wave == done_chunk) ? "is" : "is NOT");
  115. channel_is_done = 1;
  116. }
  117. #endif
  118. /* rcg06192001 abstract this out for testing purposes. */
  119. static int still_playing(void)
  120. {
  121. #ifdef TEST_MIX_CHANNELFINISHED
  122. return(!channel_is_done);
  123. #else
  124. return(Mix_Playing(0));
  125. #endif
  126. }
  127. #if (defined TEST_MIX_PANNING)
  128. static void do_panning_update(void)
  129. {
  130. static Uint8 leftvol = 128;
  131. static Uint8 rightvol = 128;
  132. static Uint8 leftincr = -1;
  133. static Uint8 rightincr = 1;
  134. static int panningok = 1;
  135. static Uint32 next_panning_update = 0;
  136. if ((panningok) && (SDL_GetTicks() >= next_panning_update)) {
  137. panningok = Mix_SetPanning(0, leftvol, rightvol);
  138. if (!panningok) {
  139. fprintf(stderr, "Mix_SetPanning(0, %d, %d) failed!\n",
  140. (int) leftvol, (int) rightvol);
  141. fprintf(stderr, "Reason: [%s].\n", Mix_GetError());
  142. }
  143. if ((leftvol == 255) || (leftvol == 0)) {
  144. if (leftvol == 255)
  145. printf("All the way in the left speaker.\n");
  146. leftincr *= -1;
  147. }
  148. if ((rightvol == 255) || (rightvol == 0)) {
  149. if (rightvol == 255)
  150. printf("All the way in the right speaker.\n");
  151. rightincr *= -1;
  152. }
  153. leftvol += leftincr;
  154. rightvol += rightincr;
  155. next_panning_update = SDL_GetTicks() + 10;
  156. }
  157. }
  158. #endif
  159. #if (defined TEST_MIX_DISTANCE)
  160. static void do_distance_update(void)
  161. {
  162. static Uint8 distance = 1;
  163. static Uint8 distincr = 1;
  164. static int distanceok = 1;
  165. static Uint32 next_distance_update = 0;
  166. if ((distanceok) && (SDL_GetTicks() >= next_distance_update)) {
  167. distanceok = Mix_SetDistance(0, distance);
  168. if (!distanceok) {
  169. fprintf(stderr, "Mix_SetDistance(0, %d) failed!\n", (int) distance);
  170. fprintf(stderr, "Reason: [%s].\n", Mix_GetError());
  171. }
  172. if (distance == 0) {
  173. printf("Distance at nearest point.\n");
  174. distincr *= -1;
  175. }
  176. else if (distance == 255) {
  177. printf("Distance at furthest point.\n");
  178. distincr *= -1;
  179. }
  180. distance += distincr;
  181. next_distance_update = SDL_GetTicks() + 15;
  182. }
  183. }
  184. #endif
  185. #if (defined TEST_MIX_POSITION)
  186. static void do_position_update(void)
  187. {
  188. static Sint16 distance = 1;
  189. static Sint8 distincr = 1;
  190. static Uint16 angle = 0;
  191. static Sint8 angleincr = 1;
  192. static int positionok = 1;
  193. static Uint32 next_position_update = 0;
  194. if ((positionok) && (SDL_GetTicks() >= next_position_update)) {
  195. positionok = Mix_SetPosition(0, angle, distance);
  196. if (!positionok) {
  197. fprintf(stderr, "Mix_SetPosition(0, %d, %d) failed!\n",
  198. (int) angle, (int) distance);
  199. fprintf(stderr, "Reason: [%s].\n", Mix_GetError());
  200. }
  201. if (angle == 0) {
  202. printf("Due north; now rotating clockwise...\n");
  203. angleincr = 1;
  204. }
  205. else if (angle == 360) {
  206. printf("Due north; now rotating counter-clockwise...\n");
  207. angleincr = -1;
  208. }
  209. distance += distincr;
  210. if (distance < 0) {
  211. distance = 0;
  212. distincr = 3;
  213. printf("Distance is very, very near. Stepping away by threes...\n");
  214. } else if (distance > 255) {
  215. distance = 255;
  216. distincr = -3;
  217. printf("Distance is very, very far. Stepping towards by threes...\n");
  218. }
  219. angle += angleincr;
  220. next_position_update = SDL_GetTicks() + 30;
  221. }
  222. }
  223. #endif
  224. static void CleanUp(int exitcode)
  225. {
  226. if ( wave ) {
  227. Mix_FreeChunk(wave);
  228. wave = NULL;
  229. }
  230. if ( audio_open ) {
  231. Mix_CloseAudio();
  232. audio_open = 0;
  233. }
  234. SDL_Quit();
  235. exit(exitcode);
  236. }
  237. static void Usage(char *argv0)
  238. {
  239. fprintf(stderr, "Usage: %s [-8] [-r rate] [-c channels] [-f] [-F] [-l] [-m] <wavefile>\n", argv0);
  240. }
  241. /*
  242. * rcg06182001 This is sick, but cool.
  243. *
  244. * Actually, it's meant to be an example of how to manipulate a voice
  245. * without having to use the mixer effects API. This is more processing
  246. * up front, but no extra during the mixing process. Also, in a case like
  247. * this, when you need to touch the whole sample at once, it's the only
  248. * option you've got. And, with the effects API, you are altering a copy of
  249. * the original sample for each playback, and thus, your changes aren't
  250. * permanent; here, you've got a reversed sample, and that's that until
  251. * you either reverse it again, or reload it.
  252. */
  253. static void flip_sample(Mix_Chunk *wave)
  254. {
  255. Uint16 format;
  256. int channels, i, incr;
  257. Uint8 *start = wave->abuf;
  258. Uint8 *end = wave->abuf + wave->alen;
  259. Mix_QuerySpec(NULL, &format, &channels);
  260. incr = (format & 0xFF) * channels;
  261. end -= incr;
  262. switch (incr) {
  263. case 8:
  264. for (i = wave->alen / 2; i >= 0; i -= 1) {
  265. Uint8 tmp = *start;
  266. *start = *end;
  267. *end = tmp;
  268. start++;
  269. end--;
  270. }
  271. break;
  272. case 16:
  273. for (i = wave->alen / 2; i >= 0; i -= 2) {
  274. Uint16 tmp = *start;
  275. *((Uint16 *) start) = *((Uint16 *) end);
  276. *((Uint16 *) end) = tmp;
  277. start += 2;
  278. end -= 2;
  279. }
  280. break;
  281. case 32:
  282. for (i = wave->alen / 2; i >= 0; i -= 4) {
  283. Uint32 tmp = *start;
  284. *((Uint32 *) start) = *((Uint32 *) end);
  285. *((Uint32 *) end) = tmp;
  286. start += 4;
  287. end -= 4;
  288. }
  289. break;
  290. default:
  291. fprintf(stderr, "Unhandled format in sample flipping.\n");
  292. return;
  293. }
  294. }
  295. int main(int argc, char *argv[])
  296. {
  297. int audio_rate;
  298. Uint16 audio_format;
  299. int audio_channels;
  300. int loops = 0;
  301. int i;
  302. int reverse_stereo = 0;
  303. int reverse_sample = 0;
  304. #ifdef HAVE_SETBUF
  305. setbuf(stdout, NULL); /* rcg06132001 for debugging purposes. */
  306. setbuf(stderr, NULL); /* rcg06192001 for debugging purposes, too. */
  307. #endif
  308. output_test_warnings();
  309. /* Initialize variables */
  310. audio_rate = MIX_DEFAULT_FREQUENCY;
  311. audio_format = MIX_DEFAULT_FORMAT;
  312. audio_channels = 2;
  313. /* Check command line usage */
  314. for ( i=1; argv[i] && (*argv[i] == '-'); ++i ) {
  315. if ( (strcmp(argv[i], "-r") == 0) && argv[i+1] ) {
  316. ++i;
  317. audio_rate = atoi(argv[i]);
  318. } else
  319. if ( strcmp(argv[i], "-m") == 0 ) {
  320. audio_channels = 1;
  321. } else
  322. if ( (strcmp(argv[i], "-c") == 0) && argv[i+1] ) {
  323. ++i;
  324. audio_channels = atoi(argv[i]);
  325. } else
  326. if ( strcmp(argv[i], "-l") == 0 ) {
  327. loops = -1;
  328. } else
  329. if ( strcmp(argv[i], "-8") == 0 ) {
  330. audio_format = AUDIO_U8;
  331. } else
  332. if ( strcmp(argv[i], "-f") == 0 ) { /* rcg06122001 flip stereo */
  333. reverse_stereo = 1;
  334. } else
  335. if ( strcmp(argv[i], "-F") == 0 ) { /* rcg06172001 flip sample */
  336. reverse_sample = 1;
  337. } else {
  338. Usage(argv[0]);
  339. return(1);
  340. }
  341. }
  342. if ( ! argv[i] ) {
  343. Usage(argv[0]);
  344. return(1);
  345. }
  346. /* Initialize the SDL library */
  347. if ( SDL_Init(SDL_INIT_AUDIO) < 0 ) {
  348. fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
  349. return(255);
  350. }
  351. #ifdef HAVE_SIGNAL_H
  352. signal(SIGINT, CleanUp);
  353. signal(SIGTERM, CleanUp);
  354. #endif
  355. /* Open the audio device */
  356. if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, 4096) < 0) {
  357. fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
  358. CleanUp(2);
  359. } else {
  360. Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
  361. printf("Opened audio at %d Hz %d bit %s", audio_rate,
  362. (audio_format&0xFF),
  363. (audio_channels > 2) ? "surround" :
  364. (audio_channels > 1) ? "stereo" : "mono");
  365. if ( loops ) {
  366. printf(" (looping)\n");
  367. } else {
  368. putchar('\n');
  369. }
  370. }
  371. audio_open = 1;
  372. #if (defined TEST_MIX_VERSIONS)
  373. test_versions();
  374. #endif
  375. #if (defined TEST_MIX_DECODERS)
  376. report_decoders();
  377. #endif
  378. /* Load the requested wave file */
  379. wave = Mix_LoadWAV(argv[i]);
  380. if ( wave == NULL ) {
  381. fprintf(stderr, "Couldn't load %s: %s\n",
  382. argv[i], SDL_GetError());
  383. CleanUp(2);
  384. }
  385. if (reverse_sample) {
  386. flip_sample(wave);
  387. }
  388. #ifdef TEST_MIX_CHANNELFINISHED /* rcg06072001 */
  389. Mix_ChannelFinished(channel_complete_callback);
  390. #endif
  391. if ( (!Mix_SetReverseStereo(MIX_CHANNEL_POST, reverse_stereo)) &&
  392. (reverse_stereo) )
  393. {
  394. printf("Failed to set up reverse stereo effect!\n");
  395. printf("Reason: [%s].\n", Mix_GetError());
  396. }
  397. /* Play and then exit */
  398. Mix_PlayChannel(0, wave, loops);
  399. while (still_playing()) {
  400. #if (defined TEST_MIX_PANNING) /* rcg06132001 */
  401. do_panning_update();
  402. #endif
  403. #if (defined TEST_MIX_DISTANCE) /* rcg06192001 */
  404. do_distance_update();
  405. #endif
  406. #if (defined TEST_MIX_POSITION) /* rcg06202001 */
  407. do_position_update();
  408. #endif
  409. SDL_Delay(1);
  410. } /* while still_playing() loop... */
  411. CleanUp(0);
  412. /* Not reached, but fixes compiler warnings */
  413. return 0;
  414. }
  415. /* end of playwave.c ... */