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.  * This code handles HMP files. It can:
  9.  * - Open/read/close HMP files
  10.  * - Play HMP via Windows MIDI
  11.  * - Convert HMP to MIDI for further use
  12.  * Based on work of Arne de Bruijn and the JFFEE project
  13.  */
  14. #include <stdexcept>
  15. #include <string.h>
  16. #include <stdlib.h>
  17. #include <stdio.h>
  18.  
  19. #include "hmp.h"
  20. #include "u_mem.h"
  21. #include "console.h"
  22. #include "timer.h"
  23. #include "serial.h"
  24.  
  25. #include "dxxsconf.h"
  26. #include "dsx-ns.h"
  27. #include "compiler-range_for.h"
  28. #include "d_range.h"
  29. #include "partial_range.h"
  30. #include <memory>
  31.  
  32. namespace dcx {
  33.  
  34. #define MIDIINT(x)      (words_bigendian ? (x) : (SWAPINT(x)))
  35. #define MIDISHORT(x)    (words_bigendian ? (x) : (SWAPSHORT(x)))
  36.  
  37. #ifdef _WIN32
  38. static int midi_volume;
  39. static int channel_volume[16];
  40. static void hmp_stop(hmp_file *hmp);
  41. #endif
  42.  
  43. // READ/OPEN/CLOSE HMP
  44.  
  45. hmp_file::~hmp_file()
  46. {
  47. #ifdef _WIN32
  48.         hmp_stop(this);
  49. #endif
  50. }
  51.  
  52. std::unique_ptr<hmp_file> hmp_open(const char *filename) {
  53.         int data, tempo;
  54.         auto fp = PHYSFSX_openReadBuffered(filename);
  55.  
  56.         if (!fp)
  57.                 return NULL;
  58.  
  59.         std::unique_ptr<hmp_file> hmp(new hmp_file{});
  60.         char buf[8];
  61.         if ((PHYSFS_read(fp, buf, 1, 8) != 8) || (memcmp(buf, "HMIMIDIP", 8)))
  62.         {
  63.                 return NULL;
  64.         }
  65.  
  66.         if (PHYSFSX_fseek(fp, 0x30, SEEK_SET))
  67.         {
  68.                 return NULL;
  69.         }
  70.  
  71.         unsigned num_tracks;
  72.         if (PHYSFS_read(fp, &num_tracks, 4, 1) != 1)
  73.         {
  74.                 return NULL;
  75.         }
  76.  
  77.         if ((num_tracks < 1) || (num_tracks > HMP_TRACKS))
  78.         {
  79.                 return NULL;
  80.         }
  81.         hmp->num_trks = num_tracks;
  82.  
  83.         if (PHYSFSX_fseek(fp, 0x38, SEEK_SET))
  84.         {
  85.                 return NULL;
  86.         }
  87.         if (PHYSFS_read(fp, &tempo, 4, 1) != 1)
  88.         {
  89.                 return NULL;
  90.         }
  91.         hmp->tempo = INTEL_INT(tempo);
  92.  
  93.         if (PHYSFSX_fseek(fp, 0x308, SEEK_SET))
  94.         {
  95.                 return NULL;
  96.         }
  97.  
  98.         range_for (auto &i, partial_range(hmp->trks, num_tracks))
  99.         {
  100.                 if ((PHYSFSX_fseek(fp, 4, SEEK_CUR)) || (PHYSFS_read(fp, &data, 4, 1) != 1))
  101.                 {
  102.                         return NULL;
  103.                 }
  104.  
  105.                 data -= 12;
  106.                 i.len = data;
  107.                 i.data = std::make_unique<uint8_t[]>(data);
  108.                 /* finally, read track data */
  109.                 if ((PHYSFSX_fseek(fp, 4, SEEK_CUR)) || (PHYSFS_read(fp, i.data.get(), data, 1) != 1))
  110.                 {
  111.                         return NULL;
  112.                 }
  113.                 i.loop_set = 0;
  114.         }
  115.         hmp->filesize = PHYSFS_fileLength(fp);
  116.         return hmp;
  117. }
  118.  
  119. #ifdef _WIN32
  120. // PLAY HMP AS MIDI
  121.  
  122. void hmp_stop(hmp_file *hmp)
  123. {
  124.         MIDIHDR *mhdr;
  125.         if (!hmp->stop) {
  126.                 hmp->stop = 1;
  127.                 //PumpMessages();
  128.                 midiStreamStop(hmp->hmidi);
  129.                 while (hmp->bufs_in_mm)
  130.                         timer_delay(1);
  131.         }
  132.         while ((mhdr = hmp->evbuf)) {
  133.                 midiOutUnprepareHeader(reinterpret_cast<HMIDIOUT>(hmp->hmidi), mhdr, sizeof(MIDIHDR));
  134.                 hmp->evbuf = mhdr->lpNext;
  135.                 d_free(mhdr);
  136.         }
  137.  
  138.         if (hmp->hmidi) {
  139.                 midiStreamClose(hmp->hmidi);
  140.                 hmp->hmidi = NULL;
  141.         }
  142. }
  143.  
  144. /*
  145.  * read a HMI type variable length number
  146.  */
  147. static int get_var_num_hmi(unsigned char *data, int datalen, unsigned long *value) {
  148.         unsigned char *p;
  149.         unsigned long v = 0;
  150.         int shift = 0;
  151.  
  152.         p = data;
  153.         while ((datalen > 0) && !(*p & 0x80)) {
  154.                 v += *(p++) << shift;
  155.                 shift += 7;
  156.                 datalen --;
  157.         }
  158.         if (!datalen)
  159.                 return 0;
  160.         v += (*(p++) & 0x7f) << shift;
  161.         if (value) *value = v;
  162.         return p - data;
  163. }
  164.  
  165. /*
  166.  * read a MIDI type variable length number
  167.  */
  168. static int get_var_num(unsigned char *data, int datalen,
  169.         unsigned *value) {
  170.         unsigned char *orgdata = data;
  171.         unsigned long v = 0;
  172.  
  173.         while ((datalen > 0) && (*data & 0x80))
  174.                 v = (v << 7) + (*(data++) & 0x7f);
  175.         if (!datalen)
  176.                 return 0;
  177.         v = (v << 7) + *(data++);
  178.         if (value) *value = v;
  179.         return data - orgdata;
  180. }
  181.  
  182. static int get_event(hmp_file *hmp, event *ev) {
  183.         static const std::array<int, 7> cmdlen{{3,3,3,3,2,2,3}};
  184.         unsigned long got;
  185.         unsigned long mindelta, delta;
  186.         int ev_num;
  187.         hmp_track *fndtrk = nullptr;
  188.  
  189.         mindelta = INT_MAX;
  190.         range_for (auto &rtrk, partial_range(hmp->trks, static_cast<unsigned>(hmp->num_trks)))
  191.         {
  192.                 const auto trk = &rtrk;
  193.                 if (!trk->left || (hmp->loop_start && hmp->looping && !trk->loop_set))
  194.                         continue;
  195.                 if (!(got = get_var_num_hmi(trk->cur, trk->left, &delta)))
  196.                         return HMP_INVALID_FILE;
  197.                 if (trk->left > got + 2 && *(trk->cur + got) == 0xff
  198.                         && *(trk->cur + got + 1) == 0x2f) {/* end of track */
  199.                         trk->left = 0;
  200.                         continue;
  201.                 }
  202.  
  203.                 if (hmp->loop_start && hmp->looping)
  204.                         if (trk->cur == trk->loop)
  205.                                 delta = trk->loop_start - hmp->loop_start;
  206.  
  207.         delta += trk->cur_time - hmp->cur_time;
  208.                 if (delta < mindelta) {
  209.                         mindelta = delta;
  210.                         fndtrk = trk;
  211.                 }
  212.         }
  213.         const auto trk = fndtrk;
  214.         if (!trk)
  215.                         return HMP_EOF;
  216.  
  217.         got = get_var_num_hmi(trk->cur, trk->left, &delta);
  218.  
  219.         if (hmp->loop_start && hmp->looping)
  220.                 if (trk->cur == trk->loop)
  221.                         delta = trk->loop_start - hmp->loop_start;
  222.  
  223.         trk->cur_time += delta;
  224.  
  225.         if (!hmp->loop_start && *(trk->cur + got) >> 4 == MIDI_CONTROL_CHANGE && *(trk->cur + got + 1) == HMP_LOOP_START)
  226.         {
  227.             hmp->loop_start = trk->cur_time;
  228.             if ((hmp->filesize == 86949) && (trk->cur_time == 29)) // special ugly HACK HACK HACK for Descent2-version of descent.hmp where loop at this point causes instruments not being reset properly. No track supporting HMP loop I know of meets the requirements for the condition below and even if so - it only disabled the HMP loop feature.
  229.             {
  230.                 hmp->loop_start = 0;
  231.             }
  232.  
  233.         }
  234.  
  235.         if (!hmp->loop_end && *(trk->cur + got) >> 4 == MIDI_CONTROL_CHANGE && *(trk->cur + got + 1) == HMP_LOOP_END)
  236.                 hmp->loop_end = trk->cur_time;
  237.  
  238.         if (hmp->loop_start && !trk->loop_set && trk->cur_time >= hmp->loop_start)
  239.         {
  240.                 trk->loop_start = trk->cur_time;
  241.                 trk->loop = trk->cur;
  242.                 trk->len = trk->left;
  243.                 trk->loop_set = 1;
  244.         }
  245.  
  246.         if (hmp->loop_end && trk->cur_time > hmp->loop_end)
  247.                 return HMP_EOF;
  248.  
  249.         ev->delta = trk->cur_time - hmp->cur_time;
  250.         hmp->cur_time = trk->cur_time;
  251.  
  252.         if ((trk->left -= got) < 3)
  253.                 return HMP_INVALID_FILE;
  254.         trk->cur += got;
  255.         /*memset(ev, 0, sizeof(*ev));*/ev->datalen = 0;
  256.         ev->msg[0] = ev_num = *(trk->cur++);
  257.         trk->left--;
  258.         if (ev_num < 0x80)
  259.                 return HMP_INVALID_FILE; /* invalid command */
  260.         if (ev_num < 0xf0) {
  261.                 ev->msg[1] = *(trk->cur++);
  262.                 trk->left--;
  263.                 if (cmdlen[((ev_num) >> 4) - 8] == 3) {
  264.                         ev->msg[2] = *(trk->cur++);
  265.                         trk->left--;
  266.  
  267.                         if (ev->msg[0] >> 4 == MIDI_CONTROL_CHANGE && ev->msg[1] == MIDI_VOLUME)
  268.                         {
  269.                                 channel_volume[ev->msg[0] & 0xf] = ev->msg[2];
  270.  
  271.                                 ev->msg[2] = ev->msg[2] * midi_volume / MIDI_VOLUME_SCALE;
  272.                         }
  273.                 }
  274.         } else if (ev_num == 0xff) {
  275.                 ev->msg[1] = *(trk->cur++);
  276.                 trk->left--;
  277.                 if (!(got = get_var_num(ev->data = trk->cur,
  278.                         trk->left, &ev->datalen)))
  279.                         return HMP_INVALID_FILE;
  280.                 trk->cur += ev->datalen;
  281.                 if (trk->left <= ev->datalen)
  282.                         return HMP_INVALID_FILE;
  283.                 trk->left -= ev->datalen;
  284.         } else /* sysex -> error */
  285.             return HMP_INVALID_FILE;
  286.         return 0;
  287. }
  288.  
  289. static int fill_buffer(hmp_file *hmp) {
  290.         MIDIHDR *mhdr = hmp->evbuf;
  291.         unsigned int *p = reinterpret_cast<unsigned int *>(mhdr->lpData + mhdr->dwBytesRecorded);
  292.         unsigned int *pend = reinterpret_cast<unsigned int *>(mhdr->lpData + mhdr->dwBufferLength);
  293.         unsigned int i;
  294.         event ev{};
  295.  
  296.         while (p + 4 <= pend) {
  297.                 if (hmp->pending_size) {
  298.                         i = (p - pend) * 4;
  299.                         if (i > hmp->pending_size)
  300.                                 i = hmp->pending_size;
  301.                         *(p++) = hmp->pending_event | i;
  302.                         *(p++) = 0;
  303.                         memcpy(reinterpret_cast<uint8_t *>(p), hmp->pending, i);
  304.                         hmp->pending_size -= i;
  305.                         p += (i + 3) / 4;
  306.                 } else {
  307.                         if ((i = get_event(hmp, &ev))) {
  308.                                 mhdr->dwBytesRecorded = (reinterpret_cast<uint8_t *>(p)) - (reinterpret_cast<uint8_t *>(mhdr->lpData));
  309.                                 return i;
  310.                         }
  311.                         if (ev.datalen) {
  312.                                 hmp->pending_size = ev.datalen;
  313.                                 hmp->pending = ev.data;
  314.                                 hmp->pending_event = ev.msg[0] << 24;
  315.                         } else {
  316.                                 *(p++) = ev.delta;
  317.                                 *(p++) = 0;
  318.                                 *(p++) = (static_cast<DWORD>(MEVT_SHORTMSG) << 24) |
  319.                                         static_cast<DWORD>(ev.msg[0]) |
  320.                                         (static_cast<DWORD>(ev.msg[1]) << 8) |
  321.                                         (static_cast<DWORD>(ev.msg[2]) << 16);
  322.                         }
  323.                 }
  324.         }
  325.         mhdr->dwBytesRecorded = (reinterpret_cast<uint8_t *>(p)) - (reinterpret_cast<uint8_t *>(mhdr->lpData));
  326.         return 0;
  327. }
  328.  
  329. static int setup_buffers(hmp_file *hmp) {
  330.         MIDIHDR *buf, *lastbuf;
  331.  
  332.         lastbuf = NULL;
  333.         for (int i = 0; i < HMP_BUFFERS; i++) {
  334.                 if (!(buf = reinterpret_cast<MIDIHDR *>(d_malloc(HMP_BUFSIZE + sizeof(MIDIHDR)))))
  335.                         return HMP_OUT_OF_MEM;
  336.                 memset(buf, 0, sizeof(MIDIHDR));
  337.                 buf->lpData = reinterpret_cast<char *>(buf) + sizeof(MIDIHDR);
  338.                 buf->dwBufferLength = HMP_BUFSIZE;
  339.                 buf->dwUser = reinterpret_cast<uintptr_t>(hmp);
  340.                 buf->lpNext = lastbuf;
  341.                 lastbuf = buf;
  342.         }
  343.         hmp->evbuf = lastbuf;
  344.         return 0;
  345. }
  346.  
  347. static void reset_tracks(struct hmp_file *hmp)
  348. {
  349.         if (hmp->num_trks > 0)
  350.         range_for (auto &i, partial_range(hmp->trks, static_cast<unsigned>(hmp->num_trks)))
  351.         {
  352.                 if (i.loop_set)
  353.                         i.cur = i.loop;
  354.                 else
  355.                         i.cur = i.data.get();
  356.                 i.left = i.len;
  357.                 i.cur_time = 0;
  358.         }
  359.         hmp->cur_time = 0;
  360. }
  361.  
  362. static void _stdcall midi_callback(HMIDISTRM, UINT uMsg, DWORD, DWORD_PTR dw1, DWORD)
  363. {
  364.         hmp_file *hmp;
  365.         int rc;
  366.  
  367.         if (uMsg != MOM_DONE)
  368.                 return;
  369.  
  370.         const auto mhdr = reinterpret_cast<MIDIHDR *>(dw1);
  371.         mhdr->dwBytesRecorded = 0;
  372.         hmp = reinterpret_cast<hmp_file *>(mhdr->dwUser);
  373.         mhdr->lpNext = hmp->evbuf;
  374.         hmp->evbuf = mhdr;
  375.         hmp->bufs_in_mm--;
  376.  
  377.         if (!hmp->stop) {
  378.                 while (fill_buffer(hmp) == HMP_EOF) {
  379.                         if (hmp->bLoop)
  380.                                 hmp->stop = 0;
  381.                         else
  382.                                 hmp->stop = 1;
  383.  
  384.                         hmp->looping = 1;
  385.  
  386.                         reset_tracks(hmp);
  387.                 }
  388.                 if ((rc = midiStreamOut(hmp->hmidi, hmp->evbuf,
  389.                         sizeof(MIDIHDR))) != MMSYSERR_NOERROR) {
  390.                         /* ??? */
  391.                 } else {
  392.                         hmp->evbuf = hmp->evbuf->lpNext;
  393.                         hmp->bufs_in_mm++;
  394.                 }
  395.         }
  396.  
  397.  
  398. }
  399.  
  400. static void setup_tempo(hmp_file *hmp, unsigned long tempo) {
  401.         MIDIHDR *mhdr = hmp->evbuf;
  402.         unsigned int *p = reinterpret_cast<unsigned int *>(mhdr->lpData + mhdr->dwBytesRecorded);
  403.         *(p++) = 0;
  404.         *(p++) = 0;
  405.         *(p++) = ((static_cast<DWORD>(MEVT_TEMPO))<<24) | tempo;
  406.         mhdr->dwBytesRecorded += 12;
  407. }
  408.  
  409. void hmp_setvolume(hmp_file *hmp, int volume)
  410. {
  411.         if (hmp)
  412.                 range_for (const int channel, xrange(16u))
  413.                         midiOutShortMsg(reinterpret_cast<HMIDIOUT>(hmp->hmidi), static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_VOLUME << 8 | (channel_volume[channel] * volume / MIDI_VOLUME_SCALE) << 16));
  414.  
  415.         midi_volume = volume;
  416. }
  417.  
  418. int hmp_play(hmp_file *hmp, int bLoop)
  419. {
  420.         int rc;
  421.         MIDIPROPTIMEDIV mptd;
  422.  
  423.         hmp->bLoop = bLoop;
  424.         hmp->loop_start = 0;
  425.         hmp->loop_end = 0;
  426.         hmp->looping = 0;
  427.         hmp->devid = MIDI_MAPPER;
  428.  
  429.         if ((rc = setup_buffers(hmp)))
  430.                 return rc;
  431.         if ((midiStreamOpen(&hmp->hmidi, &hmp->devid,1, static_cast<DWORD>(reinterpret_cast<uintptr_t>(&midi_callback)), 0, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
  432.         {
  433.                 hmp->hmidi = NULL;
  434.                 return HMP_MM_ERR;
  435.         }
  436.         mptd.cbStruct  = sizeof(mptd);
  437.         mptd.dwTimeDiv = hmp->tempo;
  438.         if ((midiStreamProperty(hmp->hmidi, reinterpret_cast<BYTE *>(&mptd), MIDIPROP_SET|MIDIPROP_TIMEDIV)) != MMSYSERR_NOERROR)
  439.         {
  440.                 return HMP_MM_ERR;
  441.         }
  442.  
  443.         reset_tracks(hmp);
  444.         setup_tempo(hmp, 0x0f4240);
  445.  
  446.         hmp->stop = 0;
  447.         while (hmp->evbuf) {
  448.                 if ((rc = fill_buffer(hmp))) {
  449.                         if (rc == HMP_EOF) {
  450.                                 reset_tracks(hmp);
  451.                                 continue;
  452.                         } else
  453.                                 return rc;
  454.                 }
  455.                 if ((rc = midiOutPrepareHeader(reinterpret_cast<HMIDIOUT>(hmp->hmidi), hmp->evbuf,
  456.                         sizeof(MIDIHDR))) != MMSYSERR_NOERROR) {
  457.                         return HMP_MM_ERR;
  458.                 }
  459.                 if ((rc = midiStreamOut(hmp->hmidi, hmp->evbuf,
  460.                         sizeof(MIDIHDR))) != MMSYSERR_NOERROR) {
  461.                         return HMP_MM_ERR;
  462.                 }
  463.                 hmp->evbuf = hmp->evbuf->lpNext;
  464.                 hmp->bufs_in_mm++;
  465.         }
  466.         midiStreamRestart(hmp->hmidi);
  467.         return 0;
  468. }
  469.  
  470. void hmp_pause(hmp_file *hmp)
  471. {
  472.         if (hmp)
  473.                 midiStreamPause(hmp->hmidi);
  474. }
  475.  
  476. void hmp_resume(hmp_file *hmp)
  477. {
  478.         if (hmp)
  479.                 midiStreamRestart(hmp->hmidi);
  480. }
  481.  
  482. void hmp_reset()
  483. {
  484.         HMIDIOUT hmidi;
  485.         MIDIHDR mhdr;
  486.         MMRESULT rval;
  487.         char GS_Reset[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7 };
  488.  
  489.  
  490.         if ((rval = midiOutOpen(&hmidi, MIDI_MAPPER, 0, 0, 0)) != MMSYSERR_NOERROR)
  491.         {
  492.                 switch (rval)
  493.                 {
  494.                         case MIDIERR_NODEVICE:
  495.                                 con_printf(CON_DEBUG, "midiOutOpen Error: no MIDI port was found.");
  496.                                 break;
  497.                         case MMSYSERR_ALLOCATED:
  498.                                 con_printf(CON_DEBUG, "midiOutOpen Error: specified resource is already allocated.");
  499.                                 break;
  500.                         case MMSYSERR_BADDEVICEID:
  501.                                 con_printf(CON_DEBUG, "midiOutOpen Error: specified device identifier is out of range.");
  502.                                 break;
  503.                         case MMSYSERR_INVALPARAM:
  504.                                 con_printf(CON_DEBUG, "midiOutOpen Error: specified pointer or structure is invalid.");
  505.                                 break;
  506.                         case MMSYSERR_NOMEM:
  507.                                 con_printf(CON_DEBUG, "midiOutOpen Error: unable to allocate or lock memory.");
  508.                                 break;
  509.                         default:
  510.                                 con_printf(CON_DEBUG, "midiOutOpen Error code %i",rval);
  511.                                 break;
  512.                 }
  513.                 return;
  514.         }
  515.  
  516.         range_for (const int channel, xrange(16u))
  517.         {
  518.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_ALL_SOUNDS_OFF << 8 | 0 << 16));
  519.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_RESET_ALL_CONTROLLERS << 8 | 0 << 16));
  520.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_ALL_NOTES_OFF << 8 | 0 << 16));
  521.  
  522.                 channel_volume[channel] = 100;
  523.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_PANPOT << 8 | 64 << 16));
  524.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_REVERB << 8 | 40 << 16));
  525.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_CHORUS << 8 | 0 << 16));
  526.  
  527.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_BANK_SELECT_MSB << 8 | 0 << 16));
  528.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_BANK_SELECT_LSB << 8 | 0 << 16));
  529.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_PROGRAM_CHANGE << 4 | 0 << 8));
  530.         }
  531.  
  532.         mhdr.lpData = GS_Reset;
  533.         mhdr.dwBufferLength = sizeof(GS_Reset);
  534.         mhdr.dwFlags = 0;
  535.         if ((rval = midiOutPrepareHeader(hmidi, &mhdr, sizeof(MIDIHDR))) == MMSYSERR_NOERROR)
  536.         {
  537.                 if ((rval = midiOutLongMsg(hmidi, &mhdr, sizeof(MIDIHDR))) == MMSYSERR_NOERROR)
  538.                 {
  539.                         fix64 wait_done = timer_query();
  540.                         while (!(mhdr.dwFlags & MHDR_DONE))
  541.                         {
  542.                                 auto timer = timer_update();
  543.                                 if (timer >= wait_done + F1_0)
  544.                                 {
  545.                                         con_printf(CON_DEBUG, "hmp_reset: Timeout waiting for MHDR_DONE");
  546.                                         break;
  547.                                 }
  548.                         }
  549.                 }
  550.                 else
  551.                 {
  552.                         switch (rval)
  553.                         {
  554.                                 case MIDIERR_NOTREADY:
  555.                                         con_printf(CON_DEBUG, "midiOutLongMsg Error: the hardware is busy with other data.");
  556.                                         break;
  557.                                 case MIDIERR_UNPREPARED:
  558.                                         con_printf(CON_DEBUG, "midiOutLongMsg Error: the buffer pointed to by lpMidiOutHdr has not been prepared.");
  559.                                         break;
  560.                                 case MMSYSERR_INVALHANDLE:
  561.                                         con_printf(CON_DEBUG, "midiOutLongMsg Error: the specified device handle is invalid.");
  562.                                         break;
  563.                                 case MMSYSERR_INVALPARAM:
  564.                                         con_printf(CON_DEBUG, "midiOutLongMsg Error: the specified pointer or structure is invalid.");
  565.                                         break;
  566.                                 default:
  567.                                         con_printf(CON_DEBUG, "midiOutLongMsg Error code %i",rval);
  568.                                         break;
  569.                         }
  570.                 }
  571.                 midiOutUnprepareHeader(hmidi, &mhdr, sizeof(MIDIHDR));
  572.  
  573.                 timer_delay(F1_0/20);
  574.         }
  575.         else
  576.         {
  577.                 switch (rval)
  578.                 {
  579.                         case MMSYSERR_INVALHANDLE:
  580.                                 con_printf(CON_DEBUG, "midiOutPrepareHeader Error: The specified device handle is invalid.");
  581.                                 break;
  582.                         case MMSYSERR_INVALPARAM:
  583.                                 con_printf(CON_DEBUG, "midiOutPrepareHeader Error: The specified address is invalid or the given stream buffer is greater than 64K.");
  584.                                 break;
  585.                         case MMSYSERR_NOMEM:
  586.                                 con_printf(CON_DEBUG, "midiOutPrepareHeader Error: The system is unable to allocate or lock memory.");
  587.                                 break;
  588.                         default:
  589.                                 con_printf(CON_DEBUG, "midiOutPrepareHeader Error code %i",rval);
  590.                                 break;
  591.                 }
  592.         }
  593.  
  594.         range_for (const int channel, xrange(16u))
  595.                 midiOutShortMsg(hmidi, static_cast<DWORD>(channel | MIDI_CONTROL_CHANGE << 4 | MIDI_VOLUME << 8 | (100 * midi_volume / MIDI_VOLUME_SCALE) << 16));
  596.         midiOutClose(hmidi);
  597. }
  598. #endif
  599.  
  600. // CONVERSION FROM HMP TO MIDI
  601.  
  602. static void hmptrk2mid(ubyte* data, int size, std::vector<uint8_t> &midbuf)
  603. {
  604.         uint8_t *dptr = data;
  605.         ubyte lc1 = 0,last_com = 0;
  606.         uint d;
  607.         int n1;
  608.  
  609.         while (data < dptr + size)
  610.         {
  611.                 if (data[0] & 0x80) {
  612.                         ubyte b = (data[0] & 0x7F);
  613.                         midbuf.emplace_back(b);
  614.                 }
  615.                 else {
  616.                         d = (data[0] & 0x7F);
  617.                         n1 = 0;
  618.                         while ((data[n1] & 0x80) == 0) {
  619.                                 n1++;
  620.                                 d += (data[n1] & 0x7F) << (n1 * 7);
  621.                                 }
  622.                         n1 = 1;
  623.                         while ((data[n1] & 0x80) == 0) {
  624.                                 n1++;
  625.                                 if (n1 == 4)
  626.                                         throw std::runtime_error("bad HMP");
  627.                                 }
  628.                         for(int n2 = 0; n2 <= n1; n2++) {
  629.                                 ubyte b = (data[n1 - n2] & 0x7F);
  630.  
  631.                                 if (n2 != n1)
  632.                                         b |= 0x80;
  633.                                 midbuf.emplace_back(b);
  634.                                 }
  635.                         data += n1;
  636.                 }
  637.                 data++;
  638.                 if (*data == 0xFF) { //meta?
  639.                         midbuf.insert(midbuf.end(), data, data + 3 + data[2]);
  640.                         if (data[1] == 0x2F)
  641.                                 break;
  642.                 }
  643.                 else {
  644.                         lc1=data[0] ;
  645.                         if ((lc1&0x80) == 0)
  646.                                 throw std::runtime_error("bad HMP");
  647.                         switch (lc1 & 0xF0) {
  648.                                 case 0x80:
  649.                                 case 0x90:
  650.                                 case 0xA0:
  651.                                 case 0xB0:
  652.                                 case 0xE0:
  653.                                         if (lc1 != last_com)
  654.                                         {
  655.                                                 midbuf.emplace_back(lc1);
  656.                                         }
  657.                                         midbuf.insert(midbuf.end(), data + 1, data + 3);
  658.                                         data += 3;
  659.                                         break;
  660.                                 case 0xC0:
  661.                                 case 0xD0:
  662.                                         if (lc1 != last_com)
  663.                                         {
  664.                                                 midbuf.emplace_back(lc1);
  665.                                         }
  666.                                         midbuf.emplace_back(data[1]);
  667.                                         data += 2;
  668.                                         break;
  669.                                 default:
  670.                                         throw std::runtime_error("bad HMP");
  671.                                 }
  672.                         last_com = lc1;
  673.                 }
  674.         }
  675. }
  676.  
  677. struct be_bytebuffer_t : serial::writer::bytebuffer_t
  678. {
  679.         be_bytebuffer_t(pointer u) : bytebuffer_t(u) {}
  680.         static uint16_t endian() { return big_endian; }
  681. };
  682.  
  683. const std::array<uint8_t, 10> magic_header{{
  684.         'M', 'T', 'h', 'd',
  685.         0, 0, 0, 6,
  686.         0, 1,
  687. }};
  688. const std::array<uint8_t, 19> tempo{{'M','T','r','k',0,0,0,11,0,0xFF,0x51,0x03,0x18,0x80,0x00,0,0xFF,0x2F,0}};
  689. const std::array<uint8_t, 8> track_header{{'M', 'T', 'r', 'k', 0, 0, 0, 0}};
  690.  
  691. struct midhdr
  692. {
  693.         int16_t num_trks;
  694.         int16_t time_div;
  695.         midhdr(hmp_file *hmp) :
  696.                 num_trks(hmp->num_trks), time_div(hmp->tempo*1.6)
  697.         {
  698.         }
  699. };
  700.  
  701. DEFINE_SERIAL_CONST_UDT_TO_MESSAGE(midhdr, m, (magic_header, m.num_trks, m.time_div, tempo));
  702.  
  703. void hmp2mid(const char *hmp_name, std::vector<uint8_t> &midbuf)
  704. {
  705.         std::unique_ptr<hmp_file> hmp = hmp_open(hmp_name);
  706.         if (!hmp)
  707.                 return;
  708.  
  709.         const midhdr mh(hmp.get());
  710.         // write MIDI-header
  711.         midbuf.resize(serial::message_type<decltype(mh)>::maximum_size);
  712.         be_bytebuffer_t bb(&midbuf[0]);
  713.         serial::process_buffer(bb, mh);
  714.  
  715.         // tracks
  716.         for (int i = 1; i < hmp->num_trks; i++)
  717.         {
  718.                 midbuf.insert(midbuf.end(), track_header.begin(), track_header.end());
  719.                 auto size_before = midbuf.size();
  720.                 auto midtrklenpos = midbuf.size() - 4;
  721.                 hmptrk2mid(hmp->trks[i].data.get(), hmp->trks[i].len, midbuf);
  722.                 auto size_after = midbuf.size();
  723.                 be_bytebuffer_t bbmi(&midbuf[midtrklenpos]);
  724.                 serial::process_buffer(bbmi, static_cast<int32_t>(size_after - size_before));
  725.         }
  726. }
  727.  
  728. }
  729.