home *** CD-ROM | disk | FTP | other *** search
/ Large Pack of OldSkool DOS MOD Trackers / goattracker_2.69.zip / src / gsound.c < prev    next >
C/C++ Source or Header  |  2009-12-20  |  12KB  |  501 lines

  1. //
  2. // GOATTRACKER sound routines
  3. //
  4.  
  5. #define GSOUND_C
  6.  
  7. #ifdef __WIN32__
  8. #include <windows.h>
  9. #endif
  10.  
  11. #include "goattrk2.h"
  12.  
  13. // General / reSID output
  14. int playspeed;
  15. int usehardsid = 0;
  16. int usecatweasel = 0;
  17. int initted = 0;
  18. int firsttimeinit = 1;
  19. unsigned framerate = PALFRAMERATE;
  20. Sint16 *buffer = NULL;
  21. FILE *writehandle = NULL;
  22.  
  23. void sound_playrout(void);
  24. void sound_mixer(Sint32 *dest, unsigned samples);
  25. Uint32 sound_timer(Uint32 interval);
  26.  
  27. #ifdef __WIN32__
  28.  
  29. // Win32 HardSID output
  30. typedef void (CALLBACK* lpWriteToHardSID)(Uint8 DeviceID, Uint8 SID_reg, Uint8 Data);
  31. typedef Uint8 (CALLBACK* lpReadFromHardSID)(Uint8 DeviceID, Uint8 SID_reg);
  32. typedef void (CALLBACK* lpInitHardSID_Mapper)(void);
  33. typedef void (CALLBACK* lpMuteHardSID_Line)(int Mute);
  34. typedef void (CALLBACK* lpHardSID_Delay)(Uint8 DeviceID, Uint16 Cycles);
  35. typedef void (CALLBACK* lpHardSID_Write)(Uint8 DeviceID, Uint16 Cycles, Uint8 SID_reg, Uint8 Data);
  36. typedef void (CALLBACK* lpHardSID_Flush)(Uint8 DeviceID);
  37. typedef void (CALLBACK* lpHardSID_SoftFlush)(Uint8 DeviceID);
  38. lpWriteToHardSID WriteToHardSID = NULL;
  39. lpReadFromHardSID ReadFromHardSID = NULL;
  40. lpInitHardSID_Mapper InitHardSID_Mapper = NULL;
  41. lpMuteHardSID_Line MuteHardSID_Line = NULL;
  42. lpHardSID_Delay HardSID_Delay = NULL;
  43. lpHardSID_Write HardSID_Write = NULL;
  44. lpHardSID_Flush HardSID_Flush = NULL;
  45. lpHardSID_SoftFlush HardSID_SoftFlush = NULL;
  46. HINSTANCE hardsiddll = 0;
  47. int dll_initialized = FALSE;
  48. // Cycle-exact HardSID support
  49. int cycleexacthardsid = FALSE;
  50. SDL_Thread* playerthread = NULL;
  51. SDL_mutex* flushmutex = NULL;
  52. volatile int runplayerthread = FALSE;
  53. volatile int flushplayerthread = FALSE;
  54. volatile int suspendplayroutine = FALSE;
  55. int sound_thread(void *userdata);
  56.  
  57. void InitHardDLL(void);
  58.  
  59. // Win32 CatWeasel MK3 PCI output
  60. #define SID_SID_PEEK_POKE   CTL_CODE(FILE_DEVICE_SOUND,0x0800UL + 1,METHOD_BUFFERED,FILE_ANY_ACCESS)
  61. HANDLE catweaselhandle;
  62.  
  63. #else
  64.  
  65. // Unix HardSID & CatWeasel output
  66. int hardsidfd = -1;
  67. int catweaselfd = -1;
  68.  
  69. #endif
  70.  
  71. int sound_init(unsigned b, unsigned mr, unsigned writer, unsigned hardsid, unsigned m, unsigned ntsc, unsigned multiplier, unsigned catweasel, unsigned interpolate, unsigned customclockrate)
  72. {
  73.   int c;
  74.  
  75.   #ifdef __WIN32__
  76.   if (!flushmutex)
  77.       flushmutex = SDL_CreateMutex();
  78.   #endif
  79.  
  80.   sound_uninit();
  81.  
  82.   if (multiplier)
  83.   {
  84.     if (ntsc)
  85.     {
  86.       framerate = NTSCFRAMERATE * multiplier;
  87.       snd_bpmtempo = 150 * multiplier;
  88.     }
  89.     else
  90.     {
  91.       framerate = PALFRAMERATE * multiplier;
  92.       snd_bpmtempo = 125 * multiplier;
  93.     }
  94.   }
  95.   else
  96.   {
  97.     if (ntsc)
  98.     {
  99.       framerate = NTSCFRAMERATE / 2;
  100.       snd_bpmtempo = 150 / 2;
  101.     }
  102.     else
  103.     {
  104.       framerate = PALFRAMERATE / 2;
  105.       snd_bpmtempo = 125 / 2;
  106.     }
  107.   }
  108.  
  109.   if (hardsid)
  110.   {
  111.     #ifdef __WIN32__
  112.     InitHardDLL();
  113.     if (dll_initialized)
  114.     {
  115.       usehardsid = hardsid;
  116.       for (c = 0; c < NUMSIDREGS; c++)
  117.       {
  118.         sidreg[c] = 0;
  119.         WriteToHardSID(usehardsid-1, c, 0x00);
  120.       }
  121.       MuteHardSID_Line(FALSE);
  122.     }
  123.     else return 0;
  124.     if (!cycleexacthardsid)
  125.     {
  126.       SDL_SetTimer(1000 / framerate, sound_timer);
  127.     }
  128.     else
  129.     {
  130.       runplayerthread = TRUE;
  131.       playerthread = SDL_CreateThread(sound_thread, NULL);
  132.       if (!playerthread) return 0;
  133.     }
  134.     #else
  135.     char filename[80];
  136.     sprintf(filename, "/dev/sid%d", hardsid-1);
  137.     hardsidfd = open(filename, O_WRONLY, S_IREAD|S_IWRITE);
  138.     if (hardsidfd >= 0)
  139.     {
  140.       usehardsid = hardsid;
  141.       for (c = 0; c < NUMSIDREGS; c++)
  142.       {
  143.         Uint32 dataword = c << 8;
  144.         write(hardsidfd, &dataword, 4);
  145.       }
  146.     }
  147.     else return 0;
  148.     SDL_SetTimer(1000 / framerate, sound_timer);
  149.     #endif
  150.  
  151.     goto SOUNDOK;
  152.   }
  153.  
  154.   if (catweasel)
  155.   {
  156.     #ifdef __WIN32__
  157.     catweaselhandle = CreateFile("\\\\.\\SID6581_1", GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, 0L,
  158.       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0L);
  159.     if (catweaselhandle == INVALID_HANDLE_VALUE)
  160.       return 0;
  161.     #else
  162.     catweaselfd = open("/dev/sid", O_WRONLY);
  163.     if (catweaselfd < 0)
  164.       catweaselfd = open("/dev/misc/sid", O_WRONLY);
  165.     if (catweaselfd < 0)
  166.       return 0;
  167.     #ifndef __amigaos__
  168.     if (ntsc)
  169.       ioctl(catweaselfd, CWSID_IOCTL_NTSC);
  170.     else
  171.       ioctl(catweaselfd, CWSID_IOCTL_PAL);
  172.     #endif
  173.     #endif
  174.  
  175.     usecatweasel = 1;
  176.     SDL_SetTimer(1000 / framerate, sound_timer);
  177.     goto SOUNDOK;
  178.   }
  179.  
  180.   if (!buffer) buffer = malloc(MIXBUFFERSIZE * sizeof(Sint16));
  181.   if (!buffer) return 0;
  182.  
  183.   if (writer)
  184.     writehandle = fopen("sidaudio.raw", "wb");
  185.  
  186.   playspeed = mr;
  187.   if (playspeed < MINMIXRATE) playspeed = MINMIXRATE;
  188.   if (playspeed > MAXMIXRATE) playspeed = MAXMIXRATE;
  189.   if (b < MINBUF) b = MINBUF;
  190.   if (b > MAXBUF) b = MAXBUF;
  191.  
  192.   if (firsttimeinit)
  193.   {
  194.     if (!snd_init(mr, SIXTEENBIT|MONO, b, 1, 0)) return 0;
  195.     firsttimeinit = 0;
  196.   }
  197.   playspeed = snd_mixrate;
  198.   sid_init(playspeed, m, ntsc, interpolate & 1, customclockrate, interpolate >> 1);
  199.  
  200.   snd_player = &sound_playrout;
  201.   snd_setcustommixer(sound_mixer);
  202.  
  203.   SOUNDOK:
  204.   initted = 1;
  205.   return 1;
  206. }
  207.  
  208. void sound_uninit(void)
  209. {
  210.   int c;
  211.  
  212.   if (!initted) return;
  213.   initted = 0;
  214.  
  215.   // Apparently a delay is needed to make sure the sound timer thread is
  216.   // not mixing stuff anymore, and we can safely delete related structures
  217.   SDL_Delay(50);
  218.  
  219.   if (usehardsid || usecatweasel)
  220.   {
  221.     #ifdef __WIN32__
  222.     if (!playerthread)
  223.     {
  224.       SDL_SetTimer(0, NULL);
  225.     }
  226.     else
  227.     {
  228.       runplayerthread = FALSE;
  229.       SDL_WaitThread(playerthread, NULL);
  230.       playerthread = NULL;
  231.     }
  232.     #else
  233.     SDL_SetTimer(0, NULL);
  234.     #endif
  235.   }
  236.   else
  237.   {
  238.       snd_setcustommixer(NULL);
  239.     snd_player = NULL;
  240.   }
  241.  
  242.   if (writehandle)
  243.   {
  244.     fclose(writehandle);
  245.     writehandle = NULL;
  246.   }
  247.   
  248.   if (buffer)
  249.   {
  250.     free(buffer);
  251.     buffer = NULL;
  252.   }
  253.  
  254.   if (usehardsid)
  255.   {
  256.     #ifdef __WIN32__
  257.     for (c = 0; c < NUMSIDREGS; c++)
  258.     {
  259.       WriteToHardSID(usehardsid-1, c, 0x00);
  260.     }
  261.     MuteHardSID_Line(TRUE);
  262.     #else
  263.     if (hardsidfd >= 0)
  264.     {
  265.       for (c = 0; c < NUMSIDREGS; c++)
  266.       {
  267.         Uint32 dataword = c << 8;
  268.         write(hardsidfd, &dataword, 4);
  269.       }
  270.       close(hardsidfd);
  271.       hardsidfd = -1;
  272.     }
  273.     #endif
  274.   }
  275.  
  276.   if (usecatweasel)
  277.   {
  278.     #ifdef __WIN32__
  279.     DWORD w;
  280.     unsigned char buf[NUMSIDREGS * 2];
  281.     for (w = 0; w < NUMSIDREGS; w++)
  282.     {
  283.       buf[w*2] = 0x18 - w;
  284.       buf[w*2+1] = 0;
  285.     }
  286.     DeviceIoControl(catweaselhandle, SID_SID_PEEK_POKE, buf, sizeof(buf), 0L, 0UL, &w, 0L);
  287.     CloseHandle(catweaselhandle);
  288.     catweaselhandle = INVALID_HANDLE_VALUE;
  289.     #else
  290.     if (catweaselfd >= 0)
  291.     {
  292.       unsigned char buf[NUMSIDREGS];
  293.       memset(buf, 0, sizeof(buf));
  294.       lseek(catweaselfd, 0, SEEK_SET);
  295.       write(catweaselfd, buf, sizeof(buf));
  296.       close(catweaselfd);
  297.       catweaselfd = -1;
  298.     }
  299.     #endif
  300.   }
  301. }
  302.  
  303. void sound_suspend(void)
  304. {
  305.   #ifdef __WIN32__
  306.   SDL_LockMutex(flushmutex);
  307.   suspendplayroutine = TRUE;
  308.   SDL_UnlockMutex(flushmutex);
  309.   #endif
  310. }
  311.  
  312. void sound_flush(void)
  313. {
  314.   #ifdef __WIN32__
  315.   SDL_LockMutex(flushmutex);
  316.   flushplayerthread = TRUE;
  317.   SDL_UnlockMutex(flushmutex);
  318.   #endif
  319. }
  320.  
  321. Uint32 sound_timer(Uint32 interval)
  322. {
  323.   if (!initted) return interval;
  324.   sound_playrout();
  325.   return interval;
  326. }
  327.  
  328. #ifdef __WIN32__
  329. int sound_thread(void *userdata)
  330. {
  331.   unsigned long flush_cycles_interactive = hardsidbufinteractive * 1000; /* 0 = flush off for interactive mode*/
  332.   unsigned long flush_cycles_playback = hardsidbufplayback * 1000; /* 0 = flush off for playback mode*/
  333.   unsigned long cycles_after_flush = 0;
  334.   boolean interactive;
  335.  
  336.   while (runplayerthread)
  337.   {
  338.     unsigned cycles = 1000000 / framerate; // HardSID should be clocked at 1MHz
  339.     int c;
  340.  
  341.     if (flush_cycles_interactive > 0 || flush_cycles_playback > 0)
  342.     {
  343.       cycles_after_flush += cycles;
  344.     }
  345.  
  346.     // Do flush if starting playback, stopping playback, starting an interactive note etc.
  347.     if (flushplayerthread)
  348.     {
  349.       SDL_LockMutex(flushmutex);
  350.       if (HardSID_Flush)
  351.       {
  352.         HardSID_Flush(usehardsid-1);
  353.       }
  354.       // Can clear player suspend now (if set)
  355.       suspendplayroutine = FALSE;
  356.       flushplayerthread = FALSE;
  357.       SDL_UnlockMutex(flushmutex);
  358.  
  359.       SDL_Delay(0);
  360.     }
  361.  
  362.     if (!suspendplayroutine) playroutine();
  363.  
  364.     interactive = !(boolean)recordmode /* jam mode */ || !(boolean)isplaying();
  365.  
  366.     for (c = 0; c < NUMSIDREGS; c++)
  367.     {
  368.       unsigned o = sid_getorder(c);
  369.  
  370.         // Extra delay before loading the waveform (and mt_chngate,x)
  371.         if ((o == 4) || (o == 11) || (o == 18))
  372.         {
  373.         HardSID_Write(usehardsid-1, SIDWRITEDELAY+SIDWAVEDELAY, o, sidreg[o]);
  374.           cycles -= SIDWRITEDELAY+SIDWAVEDELAY;
  375.       }
  376.         else
  377.       {
  378.             HardSID_Write(usehardsid-1, SIDWRITEDELAY, o, sidreg[o]);
  379.             cycles -= SIDWRITEDELAY;
  380.         }
  381.     }
  382.  
  383.     // Now wait the rest of frame
  384.     while (cycles)
  385.     {
  386.       unsigned runnow = cycles;
  387.       if (runnow > 65535) runnow = 65535;
  388.       HardSID_Delay(usehardsid-1, runnow);
  389.       cycles -= runnow;
  390.     }
  391.  
  392.       if ((flush_cycles_interactive>0 && interactive && cycles_after_flush>=flush_cycles_interactive) ||
  393.           (flush_cycles_playback>0 && !interactive && cycles_after_flush>=flush_cycles_playback))
  394.     {
  395.       if (HardSID_SoftFlush)
  396.         HardSID_SoftFlush(usehardsid-1);
  397.           cycles_after_flush = 0;
  398.     }
  399.   }
  400.  
  401.   unsigned r;
  402.  
  403.   for (r = 0; r < NUMSIDREGS; r++)
  404.   {
  405.     HardSID_Write(usehardsid-1, SIDWRITEDELAY, r, 0);
  406.   }
  407.   if (HardSID_SoftFlush)
  408.     HardSID_SoftFlush(usehardsid-1);
  409.  
  410.   return 0;
  411. }
  412. #endif
  413.  
  414. void sound_playrout(void)
  415. {
  416.   int c;
  417.  
  418.   playroutine();
  419.   if (usehardsid)
  420.   {
  421.     #ifdef __WIN32__
  422.     for (c = 0; c < NUMSIDREGS; c++)
  423.     {
  424.       unsigned o = sid_getorder(c);
  425.       WriteToHardSID(usehardsid-1, o, sidreg[o]);
  426.     }
  427.     #else
  428.     for (c = 0; c < NUMSIDREGS; c++)
  429.     {
  430.       unsigned o = sid_getorder(c);
  431.       Uint32 dataword = (o << 8) | sidreg[o];
  432.       write(hardsidfd, &dataword, 4);
  433.     }
  434.     #endif
  435.   }
  436.   else if (usecatweasel)
  437.   {
  438.     #ifdef __WIN32__
  439.     DWORD w;
  440.     unsigned char buf[NUMSIDREGS * 2];
  441.  
  442.     for(w = 0; w < NUMSIDREGS; w++)
  443.     {
  444.       unsigned o = sid_getorder(w);
  445.  
  446.       buf[w*2] = o;
  447.       buf[w*2+1] = sidreg[o];
  448.     }
  449.     DeviceIoControl(catweaselhandle, SID_SID_PEEK_POKE, buf, sizeof(buf), 0L, 0UL, &w, 0L);
  450.     #else
  451.     for (c = 0; c < NUMSIDREGS; c++)
  452.     {
  453.       unsigned o = sid_getorder(c);
  454.  
  455.       lseek(catweaselfd, o, SEEK_SET);
  456.       write(catweaselfd, &sidreg[o], 1);
  457.     }
  458.     #endif
  459.   }
  460. }
  461.  
  462. void sound_mixer(Sint32 *dest, unsigned samples)
  463. {
  464.   int c;
  465.  
  466.   if (!initted) return;
  467.   if (samples > MIXBUFFERSIZE) return;
  468.   if (!buffer) return;
  469.  
  470.   sid_fillbuffer(buffer, samples);
  471.   if (writehandle)
  472.     fwrite(buffer, samples * sizeof(Uint16), 1, writehandle);
  473.  
  474.   for (c = 0; c < samples; c++)
  475.     dest[c] = buffer[c];
  476. }
  477.  
  478. #ifdef __WIN32__
  479. void InitHardDLL()
  480. {
  481.   if (!(hardsiddll=LoadLibrary("HARDSID.DLL"))) return;
  482.  
  483.   WriteToHardSID = (lpWriteToHardSID) GetProcAddress(hardsiddll, "WriteToHardSID");
  484.   ReadFromHardSID = (lpReadFromHardSID) GetProcAddress(hardsiddll, "ReadFromHardSID");
  485.   InitHardSID_Mapper = (lpInitHardSID_Mapper) GetProcAddress(hardsiddll, "InitHardSID_Mapper");
  486.   MuteHardSID_Line = (lpMuteHardSID_Line) GetProcAddress(hardsiddll, "MuteHardSID_Line");
  487.  
  488.   if (!WriteToHardSID) return;
  489.  
  490.   // Try to get cycle-exact interface
  491.   HardSID_Delay = (lpHardSID_Delay) GetProcAddress(hardsiddll, "HardSID_Delay");
  492.   HardSID_Write = (lpHardSID_Write) GetProcAddress(hardsiddll, "HardSID_Write");
  493.   HardSID_Flush = (lpHardSID_Flush) GetProcAddress(hardsiddll, "HardSID_Flush");
  494.   HardSID_SoftFlush = (lpHardSID_SoftFlush) GetProcAddress(hardsiddll, "HardSID_SoftFlush");
  495.   if ((HardSID_Delay) && (HardSID_Write)) cycleexacthardsid = TRUE;
  496.  
  497.   InitHardSID_Mapper();
  498.   dll_initialized = TRUE;
  499. }
  500. #endif
  501.