Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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
}