Example #1
0
struct _mdi *_WM_ParseNewXmi(uint8_t *xmi_data, uint32_t xmi_size) {
    struct _mdi *xmi_mdi = NULL;
    uint32_t xmi_tmpdata = 0;
    uint8_t xmi_formcnt = 0;
    uint32_t xmi_catlen = 0;
    uint32_t xmi_subformlen = 0;
    uint32_t i = 0;
    uint32_t j = 0;

    uint32_t xmi_evntlen = 0;
    uint32_t xmi_divisions = 60;
    uint32_t xmi_tempo = 500000;
    uint32_t xmi_sample_count = 0;
    float xmi_sample_count_f = 0;
    float xmi_sample_remainder = 0;
    float xmi_samples_per_delta_f = 0;
    uint8_t xmi_ch = 0;
    uint8_t xmi_note = 0;
    uint32_t *xmi_notelen = NULL;

    uint32_t setup_ret = 0;
    uint32_t xmi_delta = 0;
    uint32_t xmi_lowestdelta = 0;

    uint32_t xmi_evnt_cnt = 0;


    if (memcmp(xmi_data,"FORM",4)) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);
        return NULL;
    }

    xmi_data += 4;
    xmi_size -= 4;

    // bytes until next entry
    xmi_tmpdata = *xmi_data++ << 24;
    xmi_tmpdata |= *xmi_data++ << 16;
    xmi_tmpdata |= *xmi_data++ << 8;
    xmi_tmpdata |= *xmi_data++;
    xmi_size -= 4;

    if (memcmp(xmi_data,"XDIRINFO",8)) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);
        return NULL;
    }
    xmi_data += 8;
    xmi_size -= 8;

    /*
     0x00 0x00 0x00 0x02 at this point are unknown
     so skip over them
     */
    xmi_data += 4;
    xmi_size -= 4;

    // number of forms contained after this point
    xmi_formcnt = *xmi_data++;
    if (xmi_formcnt == 0) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);
        return NULL;
    }
    xmi_size--;

    /*
     at this stage unsure if remaining data in
     this section means anything
     */
    xmi_tmpdata -= 13;
    xmi_data += xmi_tmpdata;
    xmi_size -= xmi_tmpdata;

    /* FIXME: Check: may not even need to process CAT information */
    if (memcmp(xmi_data,"CAT ",4)) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);
        return NULL;
    }
    xmi_data += 4;
    xmi_size -= 4;

    xmi_catlen = *xmi_data++ << 24;
    xmi_catlen |= *xmi_data++ << 16;
    xmi_catlen |= *xmi_data++ << 8;
    xmi_catlen |= *xmi_data++;
    xmi_size -= 4;

    WMIDI_UNUSED(xmi_catlen);

    if (memcmp(xmi_data,"XMID",4)) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);
        return NULL;
    }
    xmi_data += 4;
    xmi_size -= 4;

    xmi_mdi = _WM_initMDI();
    _WM_midi_setup_divisions(xmi_mdi, xmi_divisions);
    _WM_midi_setup_tempo(xmi_mdi, xmi_tempo);

    xmi_samples_per_delta_f = _WM_GetSamplesPerTick(xmi_divisions, xmi_tempo);

    xmi_notelen = (uint32_t *) malloc(sizeof(uint32_t) * 16 * 128);
    memset(xmi_notelen, 0, (sizeof(uint32_t) * 16 * 128));

    for (i = 0; i < xmi_formcnt; i++) {
        if (memcmp(xmi_data,"FORM",4)) {
            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);
            goto _xmi_end;
        }
        xmi_data += 4;
        xmi_size -= 4;

        xmi_subformlen = *xmi_data++ << 24;
        xmi_subformlen |= *xmi_data++ << 16;
        xmi_subformlen |= *xmi_data++ << 8;
        xmi_subformlen |= *xmi_data++;
        xmi_size -= 4;

        if (memcmp(xmi_data,"XMID",4)) {
            _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);
            goto _xmi_end;
        }
        xmi_data += 4;
        xmi_size -= 4;
        xmi_subformlen -= 4;

        // Process Subform
        do {
            if (!memcmp(xmi_data,"TIMB",4)) {
                // Holds patch information
                // FIXME: May not be needed for playback as EVNT seems to
                //        hold patch events
                xmi_data += 4;

                xmi_tmpdata = *xmi_data++ << 24;
                xmi_tmpdata |= *xmi_data++ << 16;
                xmi_tmpdata |= *xmi_data++ << 8;
                xmi_tmpdata |= *xmi_data++;
                xmi_data += xmi_tmpdata;
                xmi_size -= (8 + xmi_tmpdata);
                xmi_subformlen -= (8 + xmi_tmpdata);

            } else if (!memcmp(xmi_data,"RBRN",4)) {
                // Unknown what this is
                // FIXME: May not be needed for playback
                xmi_data += 4;

                xmi_tmpdata = *xmi_data++ << 24;
                xmi_tmpdata |= *xmi_data++ << 16;
                xmi_tmpdata |= *xmi_data++ << 8;
                xmi_tmpdata |= *xmi_data++;
                xmi_data += xmi_tmpdata;
                xmi_size -= (8 + xmi_tmpdata);
                xmi_subformlen -= (8 + xmi_tmpdata);

            } else if (!memcmp(xmi_data,"EVNT",4)) {
                // EVNT is where all the MIDI music information is stored
                xmi_data += 4;

                xmi_evnt_cnt++;

                xmi_evntlen = *xmi_data++ << 24;
                xmi_evntlen |= *xmi_data++ << 16;
                xmi_evntlen |= *xmi_data++ << 8;
                xmi_evntlen |= *xmi_data++;
                xmi_size -= 8;
                xmi_subformlen -= 8;

                do {
                    if (*xmi_data < 0x80) {
                        xmi_delta = 0;
                        if (*xmi_data > 0x7f) {
                            while (*xmi_data > 0x7f) {
                                xmi_delta = (xmi_delta << 7) | (*xmi_data++ & 0x7f);
                                xmi_size--;
                                xmi_evntlen--;
                                xmi_subformlen--;
                            }
                        }
                        xmi_delta = (xmi_delta << 7) | (*xmi_data++ & 0x7f);
                        xmi_size--;
                        xmi_evntlen--;
                        xmi_subformlen--;

                        do {
                            // determine delta till next event
                            if ((xmi_lowestdelta != 0) && (xmi_lowestdelta <= xmi_delta)) {
                                xmi_tmpdata = xmi_lowestdelta;
                            } else {
                                xmi_tmpdata = xmi_delta;
                            }

                            xmi_sample_count_f= (((float) xmi_tmpdata * xmi_samples_per_delta_f) + xmi_sample_remainder);

                            xmi_sample_count = (uint32_t) xmi_sample_count_f;
                            xmi_sample_remainder = xmi_sample_count_f - (float) xmi_sample_count;

                            xmi_mdi->events[xmi_mdi->event_count - 1].samples_to_next += xmi_sample_count;
                            xmi_mdi->extra_info.approx_total_samples += xmi_sample_count;

                            xmi_lowestdelta = 0;

                            // scan through on notes
                            for (j = 0; j < (16*128); j++) {
                                // only want notes that are on
                                if (xmi_notelen[j] == 0) continue;

                                // remove delta to next event from on notes
                                xmi_notelen[j] -= xmi_tmpdata;

                                // Check if we need to turn note off
                                if (xmi_notelen[j] == 0) {
                                    xmi_ch = j / 128;
                                    xmi_note = j - (xmi_ch * 128);
                                    _WM_midi_setup_noteoff(xmi_mdi, xmi_ch, xmi_note, 0);
                                } else {
                                    // otherwise work out new lowest delta
                                    if ((xmi_lowestdelta == 0) || (xmi_lowestdelta > xmi_notelen[j])) {
                                        xmi_lowestdelta = xmi_notelen[j];
                                    }
                                }
                            }
                            xmi_delta -= xmi_tmpdata;
                        } while (xmi_delta);

                    } else {
                        if ((xmi_data[0] == 0xff) && (xmi_data[1] == 0x51) && (xmi_data[2] == 0x03)) {
                            // Ignore tempo events
                            setup_ret = 6;
                            goto _XMI_Next_Event;
                        }
                        if ((setup_ret = _WM_SetupMidiEvent(xmi_mdi,xmi_data, xmi_size, 0)) == 0) {
                            goto _xmi_end;
                        }

                        if ((*xmi_data & 0xf0) == 0x90) {
                            // Note on has extra data stating note length
                            xmi_ch = *xmi_data & 0x0f;
                            xmi_note = xmi_data[1];
                            xmi_data += setup_ret;
                            xmi_size -= setup_ret;
                            xmi_evntlen -= setup_ret;
                            xmi_subformlen -= setup_ret;

                            xmi_tmpdata = 0;

                            if (*xmi_data > 0x7f) {
                                while (*xmi_data > 0x7f) {
                                    xmi_tmpdata = (xmi_tmpdata << 7) | (*xmi_data++ & 0x7f);
                                    xmi_size--;
                                    xmi_evntlen--;
                                    xmi_subformlen--;
                                }
                            }
                            xmi_tmpdata = (xmi_tmpdata << 7) | (*xmi_data++ & 0x7f);
                            xmi_size--;
                            xmi_evntlen--;
                            xmi_subformlen--;

                            // store length
                            xmi_notelen[128 * xmi_ch + xmi_note] = xmi_tmpdata;
                            if ((xmi_tmpdata > 0) && ((xmi_lowestdelta == 0) || (xmi_tmpdata < xmi_lowestdelta))) {
                                xmi_lowestdelta = xmi_tmpdata;
                            }

                        } else {
                        _XMI_Next_Event:
                            xmi_data += setup_ret;
                            xmi_size -= setup_ret;
                            xmi_evntlen -= setup_ret;
                            xmi_subformlen -= setup_ret;
                        }
                    }

                } while (xmi_evntlen);

            } else {
                _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);
                goto _xmi_end;
            }

        } while (xmi_subformlen);
    }

    // Finalise mdi structure
    if ((xmi_mdi->reverb = _WM_init_reverb(_WM_SampleRate, _WM_reverb_room_width, _WM_reverb_room_length, _WM_reverb_listen_posx, _WM_reverb_listen_posy)) == NULL) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to init reverb", 0);
        goto _xmi_end;
    }
    xmi_mdi->extra_info.current_sample = 0;
    xmi_mdi->current_event = &xmi_mdi->events[0];
    xmi_mdi->samples_to_mix = 0;
    xmi_mdi->note = NULL;
    /* More than 1 event form in XMI means treat as type 2 */
    if (xmi_evnt_cnt > 1) {
        xmi_mdi->is_type2 = 1;
    }
    _WM_ResetToStart(xmi_mdi);

_xmi_end:
    if (xmi_notelen) free(xmi_notelen);
    if (xmi_mdi->reverb) return (xmi_mdi);
    _WM_freeMDI(xmi_mdi);
    return NULL;
}
Example #2
0
/*
 Turns hmp file data into an event stream
 */
struct _mdi *
_WM_ParseNewHmi(uint8_t *hmi_data, uint32_t hmi_size) {
    uint32_t hmi_tmp = 0;
    uint8_t *hmi_base = hmi_data;
    uint16_t hmi_bpm = 0;
    uint16_t hmi_division = 0;

//  uint32_t hmi_duration_secs = 0;
    uint32_t hmi_track_cnt = 0;
    uint32_t *hmi_track_offset = NULL;
    uint32_t i = 0;
    uint32_t j = 0;
    uint8_t *hmi_addr = NULL;
    uint32_t *hmi_track_header_length = NULL;
    struct _mdi *hmi_mdi = NULL;
    uint32_t tempo_f = 5000000.0;
    uint32_t *hmi_track_end = NULL;
    uint8_t hmi_tracks_ended = 0;
    uint8_t *hmi_running_event = NULL;
    uint32_t setup_ret = 0;
    uint32_t *hmi_delta = NULL;

    uint32_t smallest_delta = 0;
    uint32_t subtract_delta = 0;

    uint32_t sample_count = 0;
    float sample_count_f = 0;
    float sample_remainder = 0;

    float samples_per_delta_f = 0.0;

    struct _note {
        uint32_t length;
        uint8_t channel;
    } *note;

    //FIXME: This needs to be used for sanity check.
    UNUSED(hmi_size);

    if (memcmp(hmi_data, "HMI-MIDISONG061595", 18)) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_NOT_HMI, NULL, 0);
        return NULL;
    }

    //FIXME: Unsure if this is correct but it seems to be the only offset that plays the files at what appears to be the right speed.
    hmi_bpm = hmi_data[212];

    hmi_division = 60;

    hmi_track_cnt = hmi_data[228];

    hmi_mdi = _WM_initMDI();

    _WM_midi_setup_divisions(hmi_mdi, hmi_division);

    if ((_WM_MixerOptions & WM_MO_ROUNDTEMPO)) {
        tempo_f = (float) (60000000 / hmi_bpm) + 0.5f;
    } else {
        tempo_f = (float) (60000000 / hmi_bpm);
    }
    samples_per_delta_f = _WM_GetSamplesPerTick(hmi_division, (uint32_t)tempo_f);

    _WM_midi_setup_tempo(hmi_mdi, (uint32_t)tempo_f);

    hmi_track_offset = (uint32_t *)malloc(sizeof(uint32_t) * hmi_track_cnt);
    hmi_track_header_length = malloc(sizeof(uint32_t) * hmi_track_cnt);
    hmi_track_end = malloc(sizeof(uint32_t) * hmi_track_cnt);
    hmi_delta = malloc(sizeof(uint32_t) * hmi_track_cnt);
    note = malloc(sizeof(struct _note) * 128 * hmi_track_cnt);
    hmi_running_event = malloc(sizeof(uint8_t) * 128 * hmi_track_cnt);

    hmi_data += 370;

    smallest_delta = 0xffffffff;

    if (hmi_size < (370 + (hmi_track_cnt * 17))) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_NOT_HMI, "file too short", 0);
        goto _hmi_end;
    }

    hmi_track_offset[0] = *hmi_data; // To keep Xcode happy

    for (i = 0; i < hmi_track_cnt; i++) {
        hmi_track_offset[i] = *hmi_data++;
        hmi_track_offset[i] += (*hmi_data++ << 8);
        hmi_track_offset[i] += (*hmi_data++ << 16);
        hmi_track_offset[i] += (*hmi_data++ << 24);

        if (hmi_size < (hmi_track_offset[i] + 0x5a + 4)) {
            _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_NOT_HMI, "file too short", 0);
            goto _hmi_end;
        }

        hmi_addr = hmi_base + hmi_track_offset[i];

        if (memcmp(hmi_addr, "HMI-MIDITRACK", 13)) {
            _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_NOT_HMI, NULL, 0);
            goto _hmi_end;
        }

        hmi_track_header_length[i] = hmi_addr[0x57];
        hmi_track_header_length[i] += (hmi_addr[0x58] << 8);
        hmi_track_header_length[i] += (hmi_addr[0x59] << 16);
        hmi_track_header_length[i] += (hmi_addr[0x5a] << 24);

        hmi_addr += hmi_track_header_length[i];
        hmi_track_offset[i] += hmi_track_header_length[i];

        // Get tracks initial delta and set its samples_till_next;
        hmi_delta[i] = 0;
        if (*hmi_addr > 0x7f) {
            do {
                hmi_delta[i] = (hmi_delta[i] << 7) + (*hmi_addr & 0x7f);
                hmi_addr++;
                hmi_track_offset[i]++;
            } while (*hmi_addr > 0x7f);
        }
        hmi_delta[i] = (hmi_delta[i] << 7) + (*hmi_addr & 0x7f);
        hmi_track_offset[i]++;
        hmi_addr++;

        // Find smallest delta to work with
        if (hmi_delta[i] < smallest_delta) {
            smallest_delta = hmi_delta[i];
        }

        hmi_track_end[i] = 0;
        hmi_running_event[i] = 0;

        for (j = 0; j < 128; j++) {
            hmi_tmp = (128 * i) + j;
            note[hmi_tmp].length = 0;
            note[hmi_tmp].channel = 0;
        }
    }

    subtract_delta = smallest_delta;
    sample_count_f= (((float) smallest_delta * samples_per_delta_f) + sample_remainder);

    sample_count = (uint32_t) sample_count_f;
    sample_remainder = sample_count_f - (float) sample_count;

    hmi_mdi->events[hmi_mdi->event_count - 1].samples_to_next += sample_count;
    hmi_mdi->extra_info.approx_total_samples += sample_count;

    while (hmi_tracks_ended < hmi_track_cnt) {
        smallest_delta = 0;
        for (i = 0; i < hmi_track_cnt; i++) {
            if (hmi_track_end[i]) continue;

            // first check to see if any active notes need turning off.
            for (j = 0; j < 128; j++) {
                hmi_tmp = (128 * i) + j;
                if (note[hmi_tmp].length) {
                    note[hmi_tmp].length -= subtract_delta;
                    if (note[hmi_tmp].length) {
                        if ((!smallest_delta) || (smallest_delta > note[hmi_tmp].length)) {
                            smallest_delta = note[hmi_tmp].length;
                        }
                    } else {
                        _WM_midi_setup_noteoff(hmi_mdi, note[hmi_tmp].channel, j, 0);
                    }
                }
            }

            if (hmi_delta[i]) {
                hmi_delta[i] -= subtract_delta;
                if (hmi_delta[i]) {
                    if ((!smallest_delta) || (smallest_delta > hmi_delta[i])) {
                        smallest_delta = hmi_delta[i];
                    }
                    continue;
                }
            }

            do {
                hmi_data = hmi_base + hmi_track_offset[i];
                hmi_delta[i] = 0;

                if (hmi_data[0] == 0xfe) {
                    // HMI only event of some sort.
                    if (hmi_data[1] == 0x10) {
                        hmi_tmp = (hmi_data[4] + 5);
                        hmi_data += hmi_tmp;
                        hmi_track_offset[i] += hmi_tmp;
                    } else if (hmi_data[1] == 0x15) {
                        hmi_data += 4;
                        hmi_track_offset[i] += 4;
                    }
                    hmi_data += 4;
                    hmi_track_offset[i] += 4;
                } else {
                    if ((setup_ret = _WM_SetupMidiEvent(hmi_mdi,hmi_data,hmi_running_event[i])) == 0) {
                        _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_CORUPT, "(missing event)", 0);
                        goto _hmi_end;
                    }
                    if ((hmi_data[0] == 0xff) && (hmi_data[1] == 0x2f) && (hmi_data[2] == 0x00)) {
                        hmi_track_end[i] = 1;
                        hmi_tracks_ended++;
                        for(j = 0; j < 128; j++) {
                            hmi_tmp = (128 * i) + j;
                            if (note[hmi_tmp].length) {
                                _WM_midi_setup_noteoff(hmi_mdi, note[hmi_tmp].channel, j, 0);
                                note[hmi_tmp].length = 0;
                            }
                        }
                        goto _hmi_next_track;
                    }
                    // Running event
                    // 0xff does not alter running event
                    if ((*hmi_data == 0xF0) || (*hmi_data == 0xF7)) {
                        // Sysex resets running event data
                        hmi_running_event[i] = 0;
                    } else if (*hmi_data < 0xF0) {
                        // MIDI events 0x80 to 0xEF set running event
                        if (*hmi_data >= 0x80) {
                            hmi_running_event[i] = *hmi_data;
                        }
                    }
                    if ((hmi_running_event[i] & 0xf0) == 0x90) {
                        // note on has extra data to specify how long the note is.
                        if (*hmi_data > 127) {
                            hmi_tmp = hmi_data[1];
                        } else {
                            hmi_tmp = *hmi_data;
                        }
                        hmi_tmp += (i * 128);

                        note[hmi_tmp].channel = hmi_running_event[i] & 0xf;

                        hmi_data += setup_ret;
                        hmi_track_offset[i] += setup_ret;

                        note[hmi_tmp].length = 0;
                        if (*hmi_data > 0x7f) {
                            do {
                                note[hmi_tmp].length = (note[hmi_tmp].length << 7) | (*hmi_data & 0x7F);
                                hmi_data++;
                                hmi_track_offset[i]++;
                            } while (*hmi_data > 0x7F);
                        }
                        note[hmi_tmp].length = (note[hmi_tmp].length << 7) | (*hmi_data & 0x7F);
                        hmi_data++;
                        hmi_track_offset[i]++;

                        if (note[hmi_tmp].length) {
                            if ((!smallest_delta) || (smallest_delta > note[hmi_tmp].length)) {
                                smallest_delta = note[hmi_tmp].length;
                            }
                        } else {
                            _WM_midi_setup_noteoff(hmi_mdi, note[hmi_tmp].channel, j, 0);
                        }

                    } else {
                        hmi_data += setup_ret;
                        hmi_track_offset[i] += setup_ret;
                    }
                }

                // get track delta
                // hmi_delta[i] = 0; // set at start of loop
                if (*hmi_data > 0x7f) {
                    do {
                        hmi_delta[i] = (hmi_delta[i] << 7) | (*hmi_data & 0x7F);
                        hmi_data++;
                        hmi_track_offset[i]++;
                    } while (*hmi_data > 0x7F);
                }
                hmi_delta[i] = (hmi_delta[i] << 7) | (*hmi_data & 0x7F);
                hmi_data++;
                hmi_track_offset[i]++;
            } while (!hmi_delta[i]);
            if ((!smallest_delta) || (smallest_delta > hmi_delta[i])) {
                smallest_delta = hmi_delta[i];
            }

_hmi_next_track:
            hmi_tmp = 0;
            UNUSED(hmi_tmp);
        }

        // convert smallest delta to samples till next
        subtract_delta = smallest_delta;
        sample_count_f= (((float) smallest_delta * samples_per_delta_f) + sample_remainder);

        sample_count = (uint32_t) sample_count_f;
        sample_remainder = sample_count_f - (float) sample_count;

        hmi_mdi->events[hmi_mdi->event_count - 1].samples_to_next += sample_count;
        hmi_mdi->extra_info.approx_total_samples += sample_count;
    }

    if ((hmi_mdi->reverb = _WM_init_reverb(_WM_SampleRate, _WM_reverb_room_width, _WM_reverb_room_length, _WM_reverb_listen_posx, _WM_reverb_listen_posy)) == NULL) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_MEM, "to init reverb", 0);
        goto _hmi_end;
    }

    hmi_mdi->extra_info.current_sample = 0;
    hmi_mdi->current_event = &hmi_mdi->events[0];
    hmi_mdi->samples_to_mix = 0;
    hmi_mdi->note = NULL;

    _WM_ResetToStart(hmi_mdi);

_hmi_end:
    free(hmi_track_offset);
    free(hmi_track_header_length);
    free(hmi_track_end);
    free(hmi_delta);
    free(note);
    free(hmi_running_event);

    if (hmi_mdi->reverb) return (hmi_mdi);
    _WM_freeMDI(hmi_mdi);
    return 0;
}
Example #3
0
/*
 Turns hmp file data into an event stream
 */
struct _mdi *
_WM_ParseNewHmp(uint8_t *hmp_data, uint32_t hmp_size) {
    uint8_t is_hmp2 = 0;
    uint32_t zero_cnt = 0;
    uint32_t i = 0;
    uint32_t hmp_file_length = 0;
    uint32_t hmp_chunks = 0;
    uint32_t hmp_divisions = 0;
    uint32_t hmp_unknown = 0;
    uint32_t hmp_bpm = 0;
    uint32_t hmp_song_time = 0;
    struct _mdi *hmp_mdi;
    uint8_t **hmp_chunk;
    uint32_t *chunk_length;
    uint32_t *chunk_ofs;
    uint32_t *chunk_delta;
    uint8_t *chunk_end;
    uint32_t chunk_num = 0;
    uint32_t hmp_track = 0;
//  uint32_t j = 0;
    uint32_t smallest_delta = 0;
    uint32_t subtract_delta = 0;
//  uint32_t chunks_finished = 0;
    uint32_t end_of_chunks = 0;
    uint32_t var_len_shift = 0;

    float tempo_f = 500000.0;
    float samples_per_delta_f = 0.0;

//  uint8_t hmp_event = 0;
//  uint8_t hmp_channel = 0;

    uint32_t sample_count = 0;
    float sample_count_f = 0;
    float sample_remainder = 0;

    if (memcmp(hmp_data, "HMIMIDIP", 8)) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_NOT_HMP, NULL, 0);
        return NULL;
    }
    hmp_data += 8;
    hmp_size -= 8;

    if (!memcmp(hmp_data, "013195", 6)) {
        hmp_data += 6;
        hmp_size -= 6;
        is_hmp2 = 1;
    }

    // should be a bunch of \0's
    if (is_hmp2) {
        zero_cnt = 18;
    } else {
        zero_cnt = 24;
    }
    for (i = 0; i < zero_cnt; i++) {
        if (hmp_data[i] != 0) {
            _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_NOT_HMP, NULL, 0);
            return NULL;
        }
    }
    hmp_data += zero_cnt;
    hmp_size -= zero_cnt;

    hmp_file_length = *hmp_data++;
    hmp_file_length += (*hmp_data++ << 8);
    hmp_file_length += (*hmp_data++ << 16);
    hmp_file_length += (*hmp_data++ << 24);
    hmp_size -= 4;

    UNUSED(hmp_file_length);

    // Next 12 bytes are normally \0 so skipping over them
    hmp_data += 12;
    hmp_size -= 12;

    hmp_chunks = *hmp_data++;
    hmp_chunks += (*hmp_data++ << 8);
    hmp_chunks += (*hmp_data++ << 16);
    hmp_chunks += (*hmp_data++ << 24);
    hmp_size -= 4;

    // Still decyphering what this is
    hmp_unknown = *hmp_data++;
    hmp_unknown += (*hmp_data++ << 8);
    hmp_unknown += (*hmp_data++ << 16);
    hmp_unknown += (*hmp_data++ << 24);
    hmp_size -= 4;

    UNUSED(hmp_unknown);

    // Defaulting: experimenting has found this to be the ideal value
    hmp_divisions = 60;

    // Beats per minute
    hmp_bpm = *hmp_data++;
    hmp_bpm += (*hmp_data++ << 8);
    hmp_bpm += (*hmp_data++ << 16);
    hmp_bpm += (*hmp_data++ << 24);
    hmp_size -= 4;

    /* Slow but needed for accuracy */
    if ((_WM_MixerOptions & WM_MO_ROUNDTEMPO)) {
        tempo_f = (float) (60000000 / hmp_bpm) + 0.5f;
    } else {
        tempo_f = (float) (60000000 / hmp_bpm);
    }

    samples_per_delta_f = _WM_GetSamplesPerTick(hmp_divisions, tempo_f);

    //DEBUG
    //fprintf(stderr, "DEBUG: Samples Per Delta Tick: %f\r\n",samples_per_delta_f);

    // FIXME: This value is incorrect
    hmp_song_time = *hmp_data++;
    hmp_song_time += (*hmp_data++ << 8);
    hmp_song_time += (*hmp_data++ << 16);
    hmp_song_time += (*hmp_data++ << 24);
    hmp_size -= 4;

    // DEBUG
    //fprintf(stderr,"DEBUG: ??DIVISIONS??: %u, BPM: %u, ??SONG TIME??: %u:%.2u\r\n",hmp_divisions, hmp_bpm, (hmp_song_time / 60), (hmp_song_time % 60));

    UNUSED(hmp_song_time);

    if (is_hmp2) {
        hmp_data += 840;
        hmp_size -= 840;
    } else {
        hmp_data += 712;
        hmp_size -= 712;
    }

    hmp_mdi = _WM_initMDI();

    _WM_midi_setup_divisions(hmp_mdi, hmp_divisions);
    _WM_midi_setup_tempo(hmp_mdi, (uint32_t)tempo_f);

    hmp_chunk = malloc(sizeof(uint8_t *) * hmp_chunks);
    chunk_length = malloc(sizeof(uint32_t) * hmp_chunks);
    chunk_delta = malloc(sizeof(uint32_t) * hmp_chunks);
    chunk_ofs = malloc(sizeof(uint32_t) * hmp_chunks);
    chunk_end = malloc(sizeof(uint8_t) * hmp_chunks);

    smallest_delta = 0xffffffff;
    // store chunk info for use, and check chunk lengths
    for (i = 0; i < hmp_chunks; i++) {
        hmp_chunk[i] = hmp_data;
        chunk_ofs[i] = 0;

        chunk_num = *hmp_data++;
        chunk_num += (*hmp_data++ << 8);
        chunk_num += (*hmp_data++ << 16);
        chunk_num += (*hmp_data++ << 24);
        chunk_ofs[i] += 4;

        UNUSED(chunk_num);

        chunk_length[i] = *hmp_data++;
        chunk_length[i] += (*hmp_data++ << 8);
        chunk_length[i] += (*hmp_data++ << 16);
        chunk_length[i] += (*hmp_data++ << 24);
        chunk_ofs[i] += 4;

        if (chunk_length[i] > hmp_size) {
            _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_NOT_HMP, "file too short", 0);
            goto _hmp_end;
        }
        hmp_size -= chunk_length[i];

        hmp_track = *hmp_data++;
        hmp_track += (*hmp_data++ << 8);
        hmp_track += (*hmp_data++ << 16);
        hmp_track += (*hmp_data++ << 24);
        chunk_ofs[i] += 4;

        UNUSED(hmp_track);

        // Start of Midi Data
        chunk_delta[i] = 0;
        var_len_shift = 0;
        if (*hmp_data < 0x80) {
            do {
                chunk_delta[i] = chunk_delta[i] | ((*hmp_data++ & 0x7F) << var_len_shift);
                var_len_shift += 7;
                chunk_ofs[i]++;
            } while (*hmp_data < 0x80);
        }
        chunk_delta[i] = chunk_delta[i] | ((*hmp_data++ & 0x7F) << var_len_shift);
        chunk_ofs[i]++;

        if (chunk_delta[i] < smallest_delta) {
            smallest_delta = chunk_delta[i];
        }

        // goto start of next chunk
        hmp_data = hmp_chunk[i] + chunk_length[i];
        hmp_chunk[i] += chunk_ofs[i]++;
        chunk_end[i] = 0;
    }

    subtract_delta = smallest_delta;
    sample_count_f = (((float) smallest_delta * samples_per_delta_f) + sample_remainder);

    sample_count = (uint32_t) sample_count_f;
    sample_remainder = sample_count_f - (float) sample_count;

    hmp_mdi->events[hmp_mdi->event_count - 1].samples_to_next += sample_count;
    hmp_mdi->extra_info.approx_total_samples += sample_count;

    while (end_of_chunks < hmp_chunks) {
        smallest_delta = 0;

        // DEBUG
        // fprintf(stderr,"DEBUG: Delta Ticks: %u\r\n",subtract_delta);

        for (i = 0; i < hmp_chunks; i++) {
            if (chunk_end[i])
                continue;

            if (chunk_delta[i]) {
                chunk_delta[i] -= subtract_delta;
                if (chunk_delta[i]) {
                    if ((!smallest_delta)
                        || (smallest_delta > chunk_delta[i])) {
                        smallest_delta = chunk_delta[i];
                    }
                    continue;
                }
            }
            do {
                if (((hmp_chunk[i][0] & 0xf0) == 0xb0 ) && ((hmp_chunk[i][1] == 110) || (hmp_chunk[i][1] == 111)) && (hmp_chunk[i][2] > 0x7f)) {
                    // Reserved for loop markers
                    // TODO: still deciding what to do about these
                    hmp_chunk[i] += 3;
                } else {
                    uint32_t setup_ret = 0;

                    if ((setup_ret = _WM_SetupMidiEvent(hmp_mdi, hmp_chunk[i], 0)) == 0) {
                        _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_CORUPT, "(missing event)", 0);
                        goto _hmp_end;
                    }

                    if ((hmp_chunk[i][0] == 0xff) && (hmp_chunk[i][1] == 0x2f) && (hmp_chunk[i][2] == 0x00)) {
                        /* End of Chunk */
                        end_of_chunks++;
                        chunk_end[i] = 1;
                        hmp_chunk[i] += 3;
                        goto NEXT_CHUNK;
                    } else if ((hmp_chunk[i][0] == 0xff) && (hmp_chunk[i][1] == 0x51) && (hmp_chunk[i][2] == 0x03)) {
                        /* Tempo */
                        tempo_f = (float)((hmp_chunk[i][3] << 16) + (hmp_chunk[i][4] << 8)+ hmp_chunk[i][5]);
                        if (tempo_f == 0.0)
                            tempo_f = 500000.0;

                        // DEBUG
                        fprintf(stderr,"DEBUG: Tempo change %f\r\n", tempo_f);
                    }
                    hmp_chunk[i] += setup_ret;
                }
                var_len_shift = 0;
                chunk_delta[i] = 0;
                if (*hmp_chunk[i] < 0x80) {
                    do {
                        chunk_delta[i] = chunk_delta[i] + ((*hmp_chunk[i] & 0x7F) << var_len_shift);
                        var_len_shift += 7;
                        hmp_chunk[i]++;
                    } while (*hmp_chunk[i] < 0x80);
                }
                chunk_delta[i] = chunk_delta[i] + ((*hmp_chunk[i] & 0x7F) << var_len_shift);
                hmp_chunk[i]++;
            } while (!chunk_delta[i]);

            if ((!smallest_delta) || (smallest_delta > chunk_delta[i])) {
                smallest_delta = chunk_delta[i];
            }
        NEXT_CHUNK: continue;
        }

        subtract_delta = smallest_delta;
        sample_count_f= (((float) smallest_delta * samples_per_delta_f) + sample_remainder);

        sample_count = (uint32_t) sample_count_f;
        sample_remainder = sample_count_f - (float) sample_count;

        hmp_mdi->events[hmp_mdi->event_count - 1].samples_to_next += sample_count;
        hmp_mdi->extra_info.approx_total_samples += sample_count;

        // DEBUG
        // fprintf(stderr,"DEBUG: Sample Count %u\r\n",sample_count);
    }

    if ((hmp_mdi->reverb = _WM_init_reverb(_WM_SampleRate, _WM_reverb_room_width, _WM_reverb_room_length, _WM_reverb_listen_posx, _WM_reverb_listen_posy)) == NULL) {
        _WM_GLOBAL_ERROR(__FUNCTION__, __FILE__, __LINE__, WM_ERR_MEM, "to init reverb", 0);
        goto _hmp_end;
    }

    hmp_mdi->extra_info.current_sample = 0;
    hmp_mdi->current_event = &hmp_mdi->events[0];
    hmp_mdi->samples_to_mix = 0;
    hmp_mdi->note = NULL;

    _WM_ResetToStart(hmp_mdi);

_hmp_end:
    free(hmp_chunk);
    free(chunk_length);
    free(chunk_delta);
    free(chunk_ofs);
    free(chunk_end);
    if (hmp_mdi->reverb) return (hmp_mdi);
    _WM_freeMDI(hmp_mdi);
    return NULL;
}