home *** CD-ROM | disk | FTP | other *** search
/ Collection of Hack-Phreak Scene Programs / cleanhpvac.zip / cleanhpvac / PTMID3.ZIP / PTMIDSAV.C < prev    next >
C/C++ Source or Header  |  1994-07-03  |  15KB  |  475 lines

  1. /*
  2.  * ptmidsav.c: MOD-format saver module for ptmid. Takes a structure
  3.  * representing a tune and creates a file which contains that tune.
  4.  *
  5.  * Author: Andrew Scott  (c)opyright 1994
  6.  *
  7.  * Date: 17/11/1993 ver 0.0
  8.  *       8/1/1994   ver 0.1
  9.  *       11/2/1994  ver 0.2
  10.  */
  11.  
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <math.h>
  16. #include "ptmid.h"
  17. #include "samples.h"
  18.  
  19. /**
  20.  ** The midiperiod array allows quick conversion between MIDI note
  21.  ** values and Protracker note values. All "out-of-bounds" values
  22.  ** are given closest legal value.
  23.  **/
  24. unsigned midiperiod[128] = {
  25.   0,   1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
  26.   1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
  27.   1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
  28.   1712,1616,1525,1440,1357,1281,1209,1141,1077,1017,961, 907,
  29.   856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
  30.   428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
  31.   214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
  32.   107, 101, 95,  90,  85,  80,  76,  71,  67,  64,  60,  57,
  33.   57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,
  34.   57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,
  35.   57,  57,  57,  57,  57,  57,  57,  57
  36.   };
  37.  
  38. /*
  39.  * PutblanksPfile: Outputs as many null characters to a given file as
  40.  * is desired (max. 131). Inelegant solution.
  41.  */
  42. void PutblanksPfile(FILE *pfile, int cch)
  43. {
  44.     if (cch)
  45.         fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  46.             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  47.             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  48.             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  49.             "\0\0", 1, cch, pfile);
  50. }
  51.  
  52. /*
  53.  * PutPfileSz: Outputs a string to a given file and pads it to the correct
  54.  * length by outputting null characters if necessary.
  55.  */
  56. void PutPfileSz(FILE *pfile, Sz szT, int cch)
  57. {
  58.     while (*szT && cch) {
  59.         putc(*(szT++), pfile);
  60.         cch--;
  61.     }
  62.     PutblanksPfile(pfile, cch);
  63. }
  64.  
  65. /*
  66.  * WritePfile: Writes division information (sample, pitch, effect) to
  67.  * given file in the format specified by wModfmt.
  68.  *
  69.  * date: 2/7/1994 - added multi-format support
  70.  *       3/7/1994 - now aborts on write error
  71.  */
  72. void WritePfile(FILE *pfile, unsigned bSam, unsigned wPit, unsigned wEff)
  73. {
  74.   static int cNote = -1, irgchPos, Buffsiz;
  75.   static char *pchBuff;
  76.  
  77.   if (-1 == cNote) {
  78.     irgchPos = 0;
  79.     if (1 == wModfmt)
  80.       pchBuff = (char *) malloc(cNote = Buffsiz = 4 * wMaxchan * DIVSPERPAT);
  81.     else
  82.       pchBuff = (char *) malloc(cNote = Buffsiz = 3 * wMaxchan * DIVSPERPAT);
  83.   }
  84.   switch (wModfmt) {
  85.     case 1:
  86.       wPit = midiperiod[wPit];
  87.       pchBuff[0 + irgchPos] = (bSam & 0xF0) | (wPit >> 8);
  88.       pchBuff[1 + irgchPos] = wPit & 0xFF;
  89.       pchBuff[2 + irgchPos] = ((bSam & 0x0F) << 4) | (wEff >> 8);
  90.       pchBuff[3 + irgchPos] = wEff & 0xFF;
  91.       cNote -= 4;
  92.       irgchPos += 4;
  93.       break;
  94.     case 2:
  95.       if (0 < wPit)
  96.         wPit = wPit - 36;
  97.       pchBuff[0 + irgchPos] = ((bSam & 0x30) >> 4) | (wPit << 2);
  98.       pchBuff[1 + irgchPos] = ((bSam & 0x0F) << 4) | (wEff >> 8);
  99.       pchBuff[2 + irgchPos] = wEff & 0xFF;
  100.       cNote -= 3;
  101.       if ((irgchPos += DIVSPERPAT * 3) >= Buffsiz)
  102.         irgchPos -= Buffsiz - 3;
  103.       break;
  104.   }
  105.   if (0 == cNote) {
  106.     if (0 == fwrite(pchBuff, Buffsiz, 1, pfile)) {
  107.       ERROR;
  108.       exit(1);
  109.     }
  110.     irgchPos = 0;
  111.     cNote = Buffsiz;
  112.   }
  113. }
  114.  
  115. /*
  116.  * PeiNextPtune: Given a pointer to a tune, will start using it if not
  117.  * already using one. Will return next event in quanta, else NULL.
  118.  * If it gets to the end of the tune, will set flag to false and will
  119.  * wait to be given a new tune.
  120.  */
  121. EI *PeiNextPtune(Tune *ptuneMain, int *pf)
  122. {
  123.     static Tune *ptune = NULL;
  124.     static unsigned long quant;
  125.     EI *pei;
  126.  
  127.     if (NULL == ptune) { /** If first time called **/
  128.         if (NULL == ptuneMain) /** If no tune given, quit **/
  129.             return NULL;
  130.         *pf = 1;
  131.         quant = 0;
  132.         ptune = ptuneMain; /** Initialize tune pointer to start of tune **/
  133.     } else /** Else **/
  134.         quant++; /** Advance along tune 1 quantize fraction **/
  135.     if (quant < ptune->count) /** If haven't reached next event **/
  136.         return NULL; /** return nothing-happening **/
  137.  
  138.     pei = ptune->pei; /** Otherwise note next event **/
  139.     if ((ptune = ptune->ptune) == NULL) /** If no more events **/
  140.         *pf = 0; /** register end of tune **/
  141.     return pei; /** Return that event **/
  142. }
  143.  
  144. /*
  145.  * Convqpm: Converts a given qpm number into a double tempo thingy.
  146.  */
  147. void Convqpm(unsigned qpm, int rgbTempo[2], int ticks)
  148. {
  149.     if (792 / ticks <= qpm && 6144 / ticks - 1 >= qpm) {
  150.         rgbTempo[0] = ticks; /** If can use current ticks/div, do so **/
  151.         rgbTempo[1] = qpm * ticks / 24;
  152.     } else if (33 > qpm) /** Else if qpm is very small **/
  153.         if (26 > qpm) { /** approximate it **/
  154.             rgbTempo[0] = 31;
  155.             rgbTempo[1] = 33;
  156.         } else {
  157.             rgbTempo[0] = 31;
  158.             rgbTempo[1] = qpm * 31 / 24;
  159.         }
  160.     else if (6144 <= qpm) { /** Else if qpm is very big **/
  161.         rgbTempo[0] = 1; /** approximate it too **/
  162.         rgbTempo[1] = 255;
  163.     } else { /** Else look for closest fraction **/
  164.         int j, k, kMax;
  165.         double ratio, junk;
  166.  
  167.         ratio = qpm / 24.0;
  168.         j = k = 791 / qpm + 1; /** I hope these constraints are Ok **/
  169.         kMax = 6143 / qpm;
  170.         while (k++ < kMax)
  171.             if (fabs(modf(ratio * k, &junk) - 0.5) >
  172.              fabs(modf(ratio * j, &junk) - 0.5))
  173.                 j = k;
  174.         rgbTempo[0] = j;
  175.         rgbTempo[1] = j * ratio + 0.5;
  176.     }
  177. }
  178.  
  179. /*
  180.  * PutpatternsPtunePfile: Given an output file and a tune, will output the
  181.  * tune as standard Protracker patterns. wMaxchan determines number of
  182.  * channels per division. wPatmax determines maximum number of patterns
  183.  * to write before terminating.
  184.  *
  185.  * date: 3/7/1994 - quiet samples now play sample 0 rather than sample 31
  186.  */
  187. int PutpatternsPtunePfile(Tune *ptune, FILE *pfile)
  188. {
  189.   int iT, iT2, wPat = 0, cDiv, ipw, ipwMax, fGoing;
  190.   unsigned *pwLen, pwNote[3 * MAXCHANS], rgbTempo[2] = {6,125};
  191.   unsigned wNewtempo = 120;
  192.     unsigned long cDev = 0;
  193.     EI *pei;
  194.  
  195.   pwLen = (unsigned *) calloc(MAXCHANS, sizeof(unsigned));
  196.   pei = PeiNextPtune(ptune, &fGoing); /** Get first event **/
  197.   ipw = wMaxchan;
  198.   ipwMax = wMaxchan * 3;
  199.     for (wPat = 0; fGoing; wPat++) { /** Loop until told to stop **/
  200.         if (wPat == wPatmax) { /** If pattern limit reached, stop **/
  201.             Error("Warning -- Pattern limit %d reached. Aborting!", wPatmax);
  202.             break;
  203.         }
  204.         for (cDiv = 64; cDiv--; ) { /** For each division in a pattern **/
  205.             memset(pwNote, 0, ipwMax * sizeof(unsigned)); /** Clear next notes **/
  206.  
  207.       for (iT = wMaxchan; iT--; ) /** With any currently playing notes **/
  208.                 if (pwLen[iT])
  209.                     if (0 == --pwLen[iT]) { /** Check if just stopped **/
  210.             pwNote[iT2 = iT * 3] = 0; /** Yes.. store quiet command **/
  211.                         pwNote[iT2 + 2] = 0xC00;
  212.                     }
  213.  
  214.             if (fGoing) { /** If still going in the tune **/
  215.         for (; NULL != pei; pei = pei->pei) /** For each event at this position **/
  216.           if (-1 != pei->pitch) { /** If note-event **/
  217.             if (-2 != pei->pitch) { /** which is valid **/
  218.  
  219.               iT = ipwMax - 1; /*** Find channel to stick note ***/
  220.               for (; 0 < iT && 0xC00 != pwNote[iT]; iT -= 3);
  221.               if (0 > iT) {
  222.                 for (iT = ipw; iT--; )
  223.                   if (0 == pwLen[iT])
  224.                     break;
  225.                 if (0 > iT) {
  226.                   for (iT = wMaxchan; iT-- > ipw; )
  227.                     if (0 == pwLen[iT])
  228.                       break;
  229.                   if (0 > iT) {
  230.                     iT2 = (unsigned) pei->effect / 2;
  231.                     for (iT = wMaxchan; iT--; )
  232.                       if (iT2 <= pwLen[iT])
  233.                         break;
  234.                     if (0 > iT) {
  235.                       cDev++;
  236.                       continue;
  237.                     }
  238.                   }
  239.                 }
  240.                 if (--ipw < 0)
  241.                   ipw = wMaxchan - 1;
  242.                 iT = iT * 3 + 2;
  243.               }
  244.  
  245.               pwNote[iT - 2] = pei->inst + 1; /*** Store note ***/
  246.               if (fStats && 0 != pwNote[iT - 1])
  247.                 cDev++;
  248.               pwNote[iT - 1] = pei->pitch;
  249.               if (pei->vol != rgmsDecided[pei->inst].bDefvol)
  250.                 pwNote[iT] = 0xC00 + pei->vol;
  251.               else
  252.                 pwNote[iT] = 0;
  253.               pwLen[iT / 3] = (unsigned) pei->effect;
  254.             }
  255.  
  256.                     } else /** Else store new tempo **/
  257.             wNewtempo = (unsigned) pei->effect;
  258.                 pei = PeiNextPtune(NULL, &fGoing);
  259.             }
  260.  
  261.             if (0 != wNewtempo) { /** If need to set a tempo this division **/
  262.                 int rgbNew[2];
  263.  
  264.                 Convqpm(wNewtempo, rgbNew, rgbTempo[0]); /** Find protracker equivalent **/
  265.                 if (rgbNew[0] != rgbTempo[0] || rgbNew[1] != rgbTempo[1]) {
  266.                     for (iT = ipwMax - 1; iT > 0; iT -= 3) /** Find a channel for it **/
  267.                         if (0 == pwNote[iT])
  268.                             break;
  269.                     if (iT < 0) { /** If no channel.. damn. Have to replace something **/
  270.                         unsigned bMin = -1, bTest;
  271.  
  272.                         for (iT2 = ipwMax - 1; iT2 > 0; iT2 -= 3) {
  273.                             bTest = abs((pwNote[iT2] & 0xFF) - rgmsDecided[pwNote[iT2 - 2] - 1].bDefvol);
  274.                             if (bTest < bMin) {
  275.                                 bMin = bTest;
  276.                                 iT = iT2; /** Find best thing to replace **/
  277.                             }
  278.                         }
  279.                     }
  280.  
  281.                     if (rgbNew[0] != rgbTempo[0])
  282.                         if (rgbNew[1] != rgbTempo[1]) { /** If need two places **/
  283.                             pwNote[iT] = 0xFFF;
  284.                             for (iT2 = ipwMax - 1; iT2 > 0; iT2 -= 3) /** Find a channel **/
  285.                                 if (0 == pwNote[iT2])
  286.                                     break;
  287.                             if (iT2 < 0) { /** No channels.. use different new tempo **/
  288.                                 iT2 = rgbNew[1] * rgbTempo[0] / rgbNew[0];
  289.                                 if (255 < iT2)
  290.                                     if (280 < iT2)
  291.                                         iT2 = 0;
  292.                                     else
  293.                                         iT2 = 255;
  294.                                 else if (33 > iT2)
  295.                                     if (30 > iT2)
  296.                                         iT2 = 33;
  297.                                     else
  298.                                         iT2 = 0;
  299.                                 if (0 != iT2) { /** If we can allow for ~10% bpm variance **/
  300.                                     pwNote[iT] = 0xF00 + iT2; /** then use it **/
  301.                                     rgbTempo[1] = iT2;
  302.                                 } else { /** Else try to find a ticks value **/
  303.                                     iT2 = rgbNew[0] * rgbTempo[1] / rgbNew[1];
  304.                                     if (0 == iT2)
  305.                                         iT2 = 1;
  306.                                     else if (32 < iT2)
  307.                                         iT2 = 32;
  308.                                     pwNote[iT] = 0xF00 + iT2; /** and use it **/
  309.                                     rgbTempo[0] = iT2;
  310.                                 }
  311.                             } else {
  312.                                 pwNote[iT] = 0xF00 + rgbNew[0]; /** Store 2 value tempo **/
  313.                                 rgbTempo[0] = rgbNew[0];
  314.                                 pwNote[iT2] = 0xF00 + rgbNew[1];
  315.                                 rgbTempo[1] = rgbNew[1];
  316.                             }
  317.                         } else {
  318.                             pwNote[iT] = 0xF00 + rgbNew[0]; /** Store just ticks/div **/
  319.                             rgbTempo[0] = rgbNew[0];
  320.                         }
  321.                     else {
  322.                         pwNote[iT] = 0xF00 + rgbNew[1]; /** Store just bpm **/
  323.                         rgbTempo[1] = rgbNew[1];
  324.                     }
  325.                 }
  326.                 wNewtempo = 0;
  327.             }
  328.  
  329.             for (iT = ipwMax - 1; iT > 0; iT -= 3) /** Write division to file **/
  330.                 WritePfile(pfile, pwNote[iT - 2], pwNote[iT - 1], pwNote[iT]);
  331.         }
  332.     }
  333.     if (fStats)
  334.         printf(" Number of dropped notes: %lu\n", cDev);
  335.   free(pwLen);
  336.     return wPat; /** Return number of patterns written **/
  337. }
  338.  
  339. /*
  340.  * SavePtunePfile: Given an output file and a tune, will output the tune to
  341.  * the file using module format specified by wModfmt.
  342.  *
  343.  * date: 30/6/1994 - added call to LenOutPfileFn
  344.  *       2/7/1994 - added MTM saving
  345.  */
  346. void SavePtunePfile(Tune *ptune, FILE *pfile)
  347. {
  348.   int iT, cPatterns, cSamps;
  349.   unsigned rgwSampsiz[MAXSAMPS], wT;
  350.   long Patternpos;
  351.   SI *psi;
  352.  
  353.   if (2 == wModfmt)
  354.     fwrite("MTM\20", 1, 4, pfile);
  355.   PutPfileSz(pfile, szTitle, 20); /** Write title **/
  356.   if (2 == wModfmt)
  357.     PutblanksPfile(pfile, 6);
  358.   for (cSamps = 0; cSamps < MAXSAMPS; cSamps++)
  359.     if (NULL == rgmsDecided[cSamps].psi)
  360.       break;
  361.   if (2 == wModfmt) {
  362.     putc(cSamps, pfile);
  363.     putc(0, pfile);
  364.     putc(DIVSPERPAT, pfile);
  365.     putc(wMaxchan, pfile);
  366.     fwrite("\0\17\0\17\0\17\0\17\0\17\0\17\0\17\0\17\0\17\0\17\0\17\0\17\0\17"
  367.       "\0\17\0\17\0\17", 1, 32, pfile); /** Write pan positions **/
  368.   }
  369.   for (iT = 0; iT < cSamps; iT++) { /** Write sample info **/
  370.         PutPfileSz(pfile, rgmsDecided[iT].szName, 22);
  371.     psi = rgmsDecided[iT].psi;
  372.     switch (wModfmt) {
  373.       case 1:
  374.         putc(0, pfile);
  375.         putc(0, pfile);
  376.         putc(psi->bFinetune & 0x0F, pfile);
  377.         putc(rgmsDecided[iT].bDefvol, pfile);
  378.         putc(psi->wLppos >> 8, pfile);
  379.         putc(psi->wLppos & 0xFF, pfile);
  380.         putc(psi->wLplen >> 8, pfile);
  381.         putc(psi->wLplen & 0xFF, pfile);
  382.         break;
  383.       case 2:
  384.         PutblanksPfile(pfile, 4);
  385.         wT = psi->wLppos;
  386.         putc((wT & 0x007F) << 1, pfile);
  387.         putc((wT & 0x7F80) >> 7, pfile);
  388.         putc((wT > 0x7FFF) ? 1 : 0, pfile);
  389.         putc(0, pfile);
  390.         wT += psi->wLplen;
  391.         putc((wT & 0x007F) << 1, pfile);
  392.         putc((wT & 0x7F80) >> 7, pfile);
  393.         putc((wT > 0x7FFF) ? 1 : 0, pfile);
  394.         putc(0, pfile);
  395.         putc(psi->bFinetune, pfile);
  396.         putc(rgmsDecided[iT].bDefvol, pfile);
  397.         putc(0, pfile);
  398.         break;
  399.     }
  400.     }
  401.   if (1 == wModfmt)
  402.     for (iT = cSamps; iT < MAXSAMPS; iT++) { /** Write any blank samples **/
  403.       PutPfileSz(pfile, rgmsDecided[iT].szName, 22);
  404.       PutblanksPfile(pfile, 8);
  405.     }
  406.     Patternpos = ftell(pfile); /** Store start of pattern # table **/
  407.   if (1 == wModfmt) {
  408.     PutblanksPfile(pfile, 130); /** Write it as blanks **/
  409.     fwrite(szId, 4, 1, pfile); /** Write protracker id **/
  410.   } else
  411.     PutblanksPfile(pfile, 128);
  412.  
  413.     cPatterns = PutpatternsPtunePfile(ptune, pfile); /** Write patterns **/
  414.  
  415.   if (2 == wModfmt) {
  416.     int wT2;
  417.  
  418.     iT = 1;
  419.     for (wT = cPatterns; wT--; )
  420.       for (wT2 = 0; wT2 < 32; wT2++)
  421.         if (wT2 < wMaxchan) {
  422.           putc(iT & 0xFF, pfile);
  423.           putc(iT >> 8, pfile);
  424.           iT++;
  425.         } else {
  426.           putc(0, pfile);
  427.           putc(0, pfile);
  428.         }
  429.  
  430.     fseek(pfile, 24, SEEK_SET);
  431.     wT = cPatterns * wMaxchan;
  432.     putc(wT & 0xFF, pfile);
  433.     putc(wT >> 8, pfile);
  434.     putc(cPatterns - 1, pfile);
  435.     putc(cPatterns - 1, pfile);
  436.   }
  437.  
  438.     fseek(pfile, Patternpos, SEEK_SET); /** Go back to pattern # table **/
  439.   if (1 == wModfmt) {
  440.     putc(cPatterns, pfile);
  441.     putc(127, pfile);
  442.   }
  443.     for (iT = 0; iT < cPatterns; ) /** Write pattern numbers **/
  444.         putc(iT++, pfile);
  445.  
  446.     if (!fQuiet)
  447.         printf("Writing %d samples: ", cSamps);
  448.     fseek(pfile, 0, SEEK_END);
  449.     for (iT = 0; iT < cSamps; iT++) { /** Write samples **/
  450.     psi = rgmsDecided[iT].psi;
  451.     rgwSampsiz[iT] = (unsigned) (LenOutPfileFn(pfile, psi->fnSample,
  452.       1 == wModfmt) / 2);
  453.         if (!fQuiet)
  454.             printf(".");
  455.     }
  456.     if (!fQuiet)
  457.         printf("\n");
  458.  
  459.   for (iT = cSamps; iT--; ) /** Write samples' lengths **/
  460.     switch (wModfmt) {
  461.       case 1:
  462.         fseek(pfile, 42 + 30 * iT, SEEK_SET);
  463.         putc(rgwSampsiz[iT] >> 8, pfile);
  464.         putc(rgwSampsiz[iT] & 0xFF, pfile);
  465.         break;
  466.       case 2:
  467.         fseek(pfile, 88 + 37 * iT, SEEK_SET);
  468.         wT = rgwSampsiz[iT];
  469.         putc((wT & 0x007F) << 1, pfile);
  470.         putc((wT & 0x7F80) >> 7, pfile);
  471.         putc((wT > 0x7FFF) ? 1 : 0, pfile);
  472.         break;
  473.     }
  474. }
  475.