Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
  3.  * It is copyright by its individual contributors, as recorded in the
  4.  * project's Git history.  See COPYING.txt at the top level for license
  5.  * terms and a link to the Git history.
  6.  */
  7. /*
  8.  *
  9.  * SDL CD Audio functions
  10.  *
  11.  *
  12.  */
  13.  
  14. #include <algorithm>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17.  
  18. #include <SDL.h>
  19.  
  20. #ifdef __linux__
  21. #include <sys/ioctl.h>
  22. #include <linux/cdrom.h>
  23. #endif
  24.  
  25. #include "pstypes.h"
  26. #include "dxxerror.h"
  27. #include "args.h"
  28. #include "rbaudio.h"
  29. #include "console.h"
  30. #include "timer.h"
  31. #include "partial_range.h"
  32. #include "compiler-range_for.h"
  33.  
  34. #define DXX_CHECK_CD_INDRIVE_0(S)       \
  35.         static_assert(!CD_INDRIVE(S), #S)
  36. #define DXX_CHECK_CD_INDRIVE_1(S)       \
  37.         static_assert(CD_INDRIVE(S), #S)
  38. #undef CD_INDRIVE
  39. #define CD_INDRIVE(s)   (static_cast<int>(s) > 0)
  40. DXX_CHECK_CD_INDRIVE_0(CD_ERROR);
  41. DXX_CHECK_CD_INDRIVE_0(CD_TRAYEMPTY);
  42. DXX_CHECK_CD_INDRIVE_1(CD_STOPPED);
  43. DXX_CHECK_CD_INDRIVE_1(CD_PLAYING);
  44. DXX_CHECK_CD_INDRIVE_1(CD_PAUSED);
  45.  
  46. namespace dcx {
  47.  
  48. #define REDBOOK_VOLUME_SCALE 255
  49.  
  50. static SDL_CD *s_cd = NULL;
  51. static int initialised = 0;
  52.  
  53. void RBAExit()
  54. {
  55.         if (s_cd)
  56.         {
  57.                 SDL_CDStop(s_cd);
  58.                 SDL_CDClose(s_cd);
  59.         }
  60. }
  61.  
  62. void RBAInit()
  63. {
  64.         int num_cds;
  65.         if (initialised) return;
  66.  
  67.         if (SDL_Init(SDL_INIT_CDROM) < 0)
  68.         {
  69.                 Warning("RBAudio: SDL library initialisation failed: %s.",SDL_GetError());
  70.                 return;
  71.         }
  72.  
  73.         num_cds = SDL_CDNumDrives();
  74.         if (num_cds < 1)
  75.         {
  76.                 con_puts(CON_NORMAL, "RBAudio: No cdrom drives found!");
  77. #if defined(__APPLE__) || defined(macintosh)
  78.                 SDL_QuitSubSystem(SDL_INIT_CDROM);      // necessary for rescanning CDROMs
  79. #endif
  80.                 return;
  81.         }
  82.        
  83.         for (int i = 0; i < num_cds; i++)
  84.         {
  85.                 if (s_cd)
  86.                         SDL_CDClose(s_cd);
  87.                 s_cd = SDL_CDOpen(i);
  88.                
  89.                 if (s_cd && CD_INDRIVE(SDL_CDStatus(s_cd)))
  90.                 {
  91.                         const auto &&r = partial_const_range(s_cd->track, static_cast<unsigned>(s_cd->numtracks));
  92.                         if (std::find_if(r.begin(), r.end(), [](const SDL_CDtrack &t){ return t.type == SDL_AUDIO_TRACK; }) != r.end())
  93.                         {
  94.                                 initialised = 1;
  95.                                 RBAList();
  96.                                 return; // we've found an audio CD
  97.                         }
  98.                 }
  99.                 else if (s_cd == NULL)
  100.                         Warning("RBAudio: Could not open cdrom %i for redbook audio:%s\n", i, SDL_GetError());
  101.         }
  102.        
  103.         {
  104.                 con_puts(CON_NORMAL, "RBAudio: No audio CDs found");
  105.                 if (s_cd)       // if there's no audio CD, say that there's no redbook and hence play MIDI instead
  106.                 {
  107.                         SDL_CDClose(s_cd);
  108.                         s_cd = NULL;
  109.                 }
  110. #if defined(__APPLE__) || defined(macintosh)
  111.                 SDL_QuitSubSystem(SDL_INIT_CDROM);      // necessary for rescanning CDROMs
  112. #endif
  113.                 return;
  114.         }
  115. }
  116.  
  117. int RBAEnabled()
  118. {
  119.         return initialised;
  120. }
  121.  
  122. int RBAPlayTrack(int a)
  123. {
  124.         if (!s_cd) return -1;
  125.  
  126.         if (CD_INDRIVE(SDL_CDStatus(s_cd)) ) {
  127.                 if (SDL_CDPlayTracks(s_cd, a-1, 0, 0, 0) == 0)
  128.                 {
  129.                         con_printf(CON_VERBOSE, "RBAudio: Playing track %i", a);
  130.                         return a;
  131.                 }
  132.         }
  133.         return -1;
  134. }
  135.  
  136. static void (*redbook_finished_hook)() = NULL;
  137.  
  138. void RBAStop()
  139. {
  140.         if (!s_cd) return;
  141.         SDL_CDStop(s_cd);
  142.         redbook_finished_hook = NULL;   // no calling the finished hook - stopped means stopped here
  143.         con_puts(CON_VERBOSE, "RBAudio: Playback stopped");
  144. }
  145.  
  146. void RBAEjectDisk()
  147. {
  148.         if (!s_cd) return;
  149.         SDL_CDEject(s_cd);      // play nothing until it tries to load a song
  150. #if defined(__APPLE__) || defined(macintosh)
  151.         SDL_QuitSubSystem(SDL_INIT_CDROM);      // necessary for rescanning CDROMs
  152. #endif
  153.         initialised = 0;
  154. }
  155.  
  156. #ifdef __linux__
  157. void RBASetVolume(int volume)
  158. {
  159.         int cdfile, level;
  160.         struct cdrom_volctrl volctrl;
  161.  
  162.         if (!s_cd) return;
  163.  
  164.         cdfile = s_cd->id;
  165.         level = volume*REDBOOK_VOLUME_SCALE/8;
  166.  
  167.         if ((level<0) || (level>REDBOOK_VOLUME_SCALE)) {
  168.                 con_printf(CON_CRITICAL, "RBAudio: illegal volume value (allowed values 0-%i)",REDBOOK_VOLUME_SCALE);
  169.                 return;
  170.         }
  171.  
  172.         volctrl.channel0
  173.                 = volctrl.channel1
  174.                 = volctrl.channel2
  175.                 = volctrl.channel3
  176.                 = level;
  177.         if ( ioctl(cdfile, CDROMVOLCTRL, &volctrl) == -1 ) {
  178.                 con_puts(CON_CRITICAL, "RBAudio: CDROMVOLCTRL ioctl failed");
  179.                 return;
  180.         }
  181. }
  182. #endif
  183.  
  184. void RBAPause()
  185. {
  186.         if (!s_cd) return;
  187.         SDL_CDPause(s_cd);
  188.         con_puts(CON_VERBOSE, "RBAudio: Playback paused");
  189. }
  190.  
  191. int RBAResume()
  192. {
  193.         if (!s_cd) return -1;
  194.         SDL_CDResume(s_cd);
  195.         con_puts(CON_VERBOSE, "RBAudio: Playback resumed");
  196.         return 1;
  197. }
  198.  
  199. int RBAPauseResume()
  200. {
  201.         if (!s_cd) return 0;
  202.  
  203.         if (SDL_CDStatus(s_cd) == CD_PLAYING)
  204.         {
  205.                 SDL_CDPause(s_cd);
  206.                 con_puts(CON_VERBOSE, "RBAudio: Toggle Playback pause");
  207.         }
  208.         else if (SDL_CDStatus(s_cd) == CD_PAUSED)
  209.         {
  210.                 SDL_CDResume(s_cd);
  211.                 con_puts(CON_VERBOSE, "RBAudio: Toggle Playback resume");
  212.         }
  213.         else
  214.                 return 0;
  215.  
  216.         return 1;
  217. }
  218.  
  219. int RBAGetNumberOfTracks()
  220. {
  221.         if (!s_cd) return -1;
  222.         SDL_CDStatus(s_cd);
  223.         con_printf(CON_VERBOSE, "RBAudio: Found %i tracks on CD", s_cd->numtracks);
  224.         return s_cd->numtracks;
  225. }
  226.  
  227. // check if we need to call the 'finished' hook
  228. // needs to go in all event loops
  229. // a real hook would be ideal, but is currently unsupported by SDL Audio CD
  230. void RBACheckFinishedHook()
  231. {
  232.         static fix64 last_check_time = 0;
  233.        
  234.         if (!s_cd) return;
  235.  
  236.         if ((timer_query() - last_check_time) >= F2_0)
  237.         {
  238.                 if ((SDL_CDStatus(s_cd) == CD_STOPPED) && redbook_finished_hook)
  239.                 {
  240.                         con_puts(CON_VERBOSE, "RBAudio: Playback done, calling finished-hook");
  241.                         redbook_finished_hook();
  242.                 }
  243.                 last_check_time = timer_query();
  244.         }
  245. }
  246.  
  247. // plays tracks first through last, inclusive
  248. int RBAPlayTracks(int first, int last, void (*hook_finished)(void))
  249. {
  250.         if (!s_cd)
  251.                 return 0;
  252.  
  253.         if (CD_INDRIVE(SDL_CDStatus(s_cd)))
  254.         {
  255.                 redbook_finished_hook = hook_finished;
  256.                 con_printf(CON_VERBOSE, "RBAudio: Playing tracks %i to %i", first, last);
  257.                 return SDL_CDPlayTracks(s_cd, first - 1, 0, last - first + 1, 0) == 0;
  258.         }
  259.         return 0;
  260. }
  261.  
  262. // return the track number currently playing.  Useful if RBAPlayTracks()
  263. // is called.  Returns 0 if no track playing, else track number
  264. int RBAGetTrackNum()
  265. {
  266.         if (!s_cd)
  267.                 return 0;
  268.  
  269.         if (SDL_CDStatus(s_cd) != CD_PLAYING)
  270.                 return 0;
  271.  
  272.         return s_cd->cur_track + 1;
  273. }
  274.  
  275. int RBAPeekPlayStatus()
  276. {
  277.         if (!s_cd)
  278.                 return 0;
  279.  
  280.         if (SDL_CDStatus(s_cd) == CD_PLAYING)
  281.                 return 1;
  282.         else if (SDL_CDStatus(s_cd) == CD_PAUSED)       // hack so it doesn't keep restarting paused music
  283.                 return -1;
  284.        
  285.         return 0;
  286. }
  287.  
  288. static int cddb_sum(int n)
  289. {
  290.         int ret;
  291.  
  292.         /* For backward compatibility this algorithm must not change */
  293.  
  294.         ret = 0;
  295.  
  296.         while (n > 0) {
  297.                 ret = ret + (n % 10);
  298.                 n = n / 10;
  299.         }
  300.  
  301.         return (ret);
  302. }
  303.  
  304.  
  305. unsigned long RBAGetDiscID()
  306. {
  307.         int i, t = 0, n = 0;
  308.  
  309.         if (!s_cd)
  310.                 return 0;
  311.  
  312.         /* For backward compatibility this algorithm must not change */
  313.  
  314.         i = 0;
  315.  
  316.         while (i < s_cd->numtracks) {
  317.                 n += cddb_sum(s_cd->track[i].offset / CD_FPS);
  318.                 i++;
  319.         }
  320.  
  321.         t = (s_cd->track[s_cd->numtracks].offset / CD_FPS) -
  322.             (s_cd->track[0].offset / CD_FPS);
  323.  
  324.         return ((n % 0xff) << 24 | t << 8 | s_cd->numtracks);
  325. }
  326.  
  327. void RBAList(void)
  328. {
  329.  
  330.         if (!s_cd)
  331.                 return;
  332.  
  333.         range_for (auto &i, partial_const_range(s_cd->track, static_cast<unsigned>(s_cd->numtracks)))
  334.                 con_printf(CON_VERBOSE, "RBAudio: CD track %d, type %s, length %d, offset %d", i.id, (i.type == SDL_AUDIO_TRACK) ? "audio" : "data", i.length, i.offset);
  335. }
  336.  
  337. }
  338.