📄 mcimidi.c
字号:
*/static DWORD MIDI_mciReadMTrk(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt){ DWORD toberead; FOURCC fourcc; if (mmioRead(wmm->hFile, (HPSTR)&fourcc, (long)sizeof(FOURCC)) != (long)sizeof(FOURCC)) { return MCIERR_INVALID_FILE; } if (fourcc != mmioFOURCC('M', 'T', 'r', 'k')) { WARN("Can't synchronize on 'MTrk' !\n"); return MCIERR_INVALID_FILE; } if (MIDI_mciReadLong(wmm, &toberead) != 0) { return MCIERR_INVALID_FILE; } mmt->dwFirst = mmioSeek(wmm->hFile, 0, SEEK_CUR); /* >= 0 */ mmt->dwLast = mmt->dwFirst + toberead; /* compute # of pulses in this track */ mmt->dwIndex = mmt->dwFirst; mmt->dwEventPulse = 0; while (MIDI_mciReadNextEvent(wmm, mmt) == 0 && LOWORD(mmt->dwEventData) != 0x2FFF) { char buf[1024]; WORD len; mmt->dwIndex += mmt->wEventLength; switch (LOWORD(mmt->dwEventData)) { case 0x02FF: case 0x03FF: /* position after meta data header */ mmioSeek(wmm->hFile, mmt->dwIndex + HIWORD(mmt->dwEventData), SEEK_SET); len = mmt->wEventLength - HIWORD(mmt->dwEventData); if (len >= sizeof(buf)) { WARN("Buffer for text is too small (%d bytes, when %u are needed)\n", sizeof(buf) - 1, len); len = sizeof(buf) - 1; } if (mmioRead(wmm->hFile, (HPSTR)buf, len) == len) { buf[len] = 0; /* end string in case */ switch (HIBYTE(LOWORD(mmt->dwEventData))) { case 0x02: if (wmm->lpstrCopyright) { WARN("Two copyright notices (%s|%s)\n", wmm->lpstrCopyright, buf); } else { wmm->lpstrCopyright = HeapAlloc( GetProcessHeap(), 0, strlen(buf)+1 ); strcpy( wmm->lpstrCopyright, buf ); } break; case 0x03: if (wmm->lpstrName) { WARN("Two names (%s|%s)\n", wmm->lpstrName, buf); } else { wmm->lpstrName = HeapAlloc( GetProcessHeap(), 0, strlen(buf)+1 ); strcpy( wmm->lpstrName, buf ); } break; } } break; } } mmt->dwLength = mmt->dwEventPulse; TRACE("Track %u has %lu bytes and %lu pulses\n", mmt->wTrackNr, toberead, mmt->dwLength); /* reset track data */ mmt->wStatus = 1; /* ok, playing */ mmt->dwIndex = mmt->dwFirst; mmt->dwEventPulse = 0; if (mmioSeek(wmm->hFile, 0, SEEK_CUR) != mmt->dwLast) { WARN("Ouch, out of sync seek=%lu track=%lu\n", mmioSeek(wmm->hFile, 0, SEEK_CUR), mmt->dwLast); /* position at end of this track, to be ready to read next track */ mmioSeek(wmm->hFile, mmt->dwLast, SEEK_SET); } return 0;}/************************************************************************** * MIDI_mciReadMThd [internal] */static DWORD MIDI_mciReadMThd(WINE_MCIMIDI* wmm, DWORD dwOffset){ DWORD toberead; FOURCC fourcc; WORD nt; TRACE("(%p, %08lX);\n", wmm, dwOffset); if (mmioSeek(wmm->hFile, dwOffset, SEEK_SET) != dwOffset) { WARN("Can't seek at %08lX begin of 'MThd' \n", dwOffset); return MCIERR_INVALID_FILE; } if (mmioRead(wmm->hFile, (HPSTR)&fourcc, (long) sizeof(FOURCC)) != (long) sizeof(FOURCC)) return MCIERR_INVALID_FILE; if (fourcc != mmioFOURCC('M', 'T', 'h', 'd')) { WARN("Can't synchronize on 'MThd' !\n"); return MCIERR_INVALID_FILE; } if (MIDI_mciReadLong(wmm, &toberead) != 0 || toberead < 3 * sizeof(WORD)) return MCIERR_INVALID_FILE; if (MIDI_mciReadWord(wmm, &wmm->wFormat) != 0 || MIDI_mciReadWord(wmm, &wmm->nTracks) != 0 || MIDI_mciReadWord(wmm, &wmm->nDivision) != 0) { return MCIERR_INVALID_FILE; } TRACE("toberead=0x%08lX, wFormat=0x%04X nTracks=0x%04X nDivision=0x%04X\n", toberead, wmm->wFormat, wmm->nTracks, wmm->nDivision); /* MS doc says that the MIDI MCI time format must be put by default to the format * stored in the MIDI file... */ if (wmm->nDivision > 0x8000) { /* eric.pouech@lemel.fr 98/11 * In did not check this very code (pulses are expressed as SMPTE sub-frames). * In about 40 MB of MIDI files I have, none was SMPTE based... * I'm just wondering if this is widely used :-). So, if someone has one of * these files, I'd like to know about. */ FIXME("Handling SMPTE time in MIDI files has not been tested\n" "Please report to comp.emulators.ms-windows.wine with MIDI file !\n"); switch (HIBYTE(wmm->nDivision)) { case 0xE8: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_24; break; /* -24 */ case 0xE7: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_25; break; /* -25 */ case 0xE3: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30DROP; break; /* -29 */ /* is the MCI constant correct ? */ case 0xE2: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30; break; /* -30 */ default: WARN("Unsupported number of frames %d\n", -(char)HIBYTE(wmm->nDivision)); return MCIERR_INVALID_FILE; } switch (LOBYTE(wmm->nDivision)) { case 4: /* MIDI Time Code */ case 8: case 10: case 80: /* SMPTE bit resolution */ case 100: default: WARN("Unsupported number of sub-frames %d\n", LOBYTE(wmm->nDivision)); return MCIERR_INVALID_FILE; } } else if (wmm->nDivision == 0) { WARN("Number of division is 0, can't support that !!\n"); return MCIERR_INVALID_FILE; } else { wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS; } switch (wmm->wFormat) { case 0: if (wmm->nTracks != 1) { WARN("Got type 0 file whose number of track is not 1. Setting it to 1\n"); wmm->nTracks = 1; } break; case 1: case 2: break; default: WARN("Handling MIDI files which format = %d is not (yet) supported\n" "Please report with MIDI file !\n", wmm->wFormat); return MCIERR_INVALID_FILE; } if (wmm->nTracks & 0x8000) { /* this shouldn't be a problem... */ WARN("Ouch !! Implementation limitation to 32k tracks per MIDI file is overflowed\n"); wmm->nTracks = 0x7FFF; } if ((wmm->tracks = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_MIDITRACK) * wmm->nTracks)) == NULL) { return MCIERR_OUT_OF_MEMORY; } toberead -= 3 * sizeof(WORD); if (toberead > 0) { TRACE("Size of MThd > 6, skipping %ld extra bytes\n", toberead); mmioSeek(wmm->hFile, toberead, SEEK_CUR); } for (nt = 0; nt < wmm->nTracks; nt++) { wmm->tracks[nt].wTrackNr = nt; if (MIDI_mciReadMTrk(wmm, &wmm->tracks[nt]) != 0) { WARN("Can't read 'MTrk' header \n"); return MCIERR_INVALID_FILE; } } wmm->dwTempo = 500000; return 0;}/************************************************************************** * MIDI_ConvertPulseToMS [internal] */static DWORD MIDI_ConvertPulseToMS(WINE_MCIMIDI* wmm, DWORD pulse){ DWORD ret = 0; /* FIXME: this function may return false values since the tempo (wmm->dwTempo) * may change during file playing */ if (wmm->nDivision == 0) { FIXME("Shouldn't happen. wmm->nDivision = 0\n"); } else if (wmm->nDivision > 0x8000) { /* SMPTE, unchecked FIXME? */ int nf = -(char)HIBYTE(wmm->nDivision); /* number of frames */ int nsf = LOBYTE(wmm->nDivision); /* number of sub-frames */ ret = (pulse * 1000) / (nf * nsf); } else { ret = (DWORD)((double)pulse * ((double)wmm->dwTempo / 1000) / (double)wmm->nDivision); } /* TRACE("pulse=%lu tempo=%lu division=%u=0x%04x => ms=%lu\n", pulse, wmm->dwTempo, wmm->nDivision, wmm->nDivision, ret); */ return ret;}#define TIME_MS_IN_ONE_HOUR (60*60*1000)#define TIME_MS_IN_ONE_MINUTE (60*1000)#define TIME_MS_IN_ONE_SECOND (1000)/************************************************************************** * MIDI_ConvertTimeFormatToMS [internal] */static DWORD MIDI_ConvertTimeFormatToMS(WINE_MCIMIDI* wmm, DWORD val){ DWORD ret = 0; switch (wmm->dwMciTimeFormat) { case MCI_FORMAT_MILLISECONDS: ret = val; break; case MCI_FORMAT_SMPTE_24: ret = (HIBYTE(HIWORD(val)) * 125) / 3 + LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND + HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR; break; case MCI_FORMAT_SMPTE_25: ret = HIBYTE(HIWORD(val)) * 40 + LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND + HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR; break; case MCI_FORMAT_SMPTE_30: ret = (HIBYTE(HIWORD(val)) * 100) / 3 + LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND + HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR; break; default: WARN("Bad time format %lu!\n", wmm->dwMciTimeFormat); } /* TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmm->dwMciTimeFormat, ret); */ return ret;}/************************************************************************** * MIDI_ConvertMSToTimeFormat [internal] */static DWORD MIDI_ConvertMSToTimeFormat(WINE_MCIMIDI* wmm, DWORD _val){ DWORD ret = 0, val = _val; DWORD h, m, s, f; switch (wmm->dwMciTimeFormat) { case MCI_FORMAT_MILLISECONDS: ret = val; break; case MCI_FORMAT_SMPTE_24: case MCI_FORMAT_SMPTE_25: case MCI_FORMAT_SMPTE_30: h = val / TIME_MS_IN_ONE_HOUR; m = (val -= h * TIME_MS_IN_ONE_HOUR) / TIME_MS_IN_ONE_MINUTE; s = (val -= m * TIME_MS_IN_ONE_MINUTE) / TIME_MS_IN_ONE_SECOND; switch (wmm->dwMciTimeFormat) { case MCI_FORMAT_SMPTE_24: /* one frame is 1000/24 val long, 1000/24 == 125/3 */ f = (val * 3) / 125; val -= (f * 125) / 3; break; case MCI_FORMAT_SMPTE_25: /* one frame is 1000/25 ms long, 1000/25 == 40 */ f = val / 40; val -= f * 40; break; case MCI_FORMAT_SMPTE_30: /* one frame is 1000/30 ms long, 1000/30 == 100/3 */ f = (val * 3) / 100; val -= (f * 100) / 3; break; default: FIXME("There must be some bad bad programmer\n"); f = 0; } /* val contains the number of ms which cannot make a complete frame */ /* FIXME: is this correct ? programs seem to be happy with that */ ret = (f << 24) | (s << 16) | (m << 8) | (h << 0); break; default: WARN("Bad time format %lu!\n", wmm->dwMciTimeFormat); } /* TRACE("val=%lu [tf=%lu] => ret=%lu=0x%08lx\n", _val, wmm->dwMciTimeFormat, ret, ret); */ return ret;}/************************************************************************** * MIDI_GetMThdLengthMS [internal] */static DWORD MIDI_GetMThdLengthMS(WINE_MCIMIDI* wmm){ WORD nt; DWORD ret = 0; for (nt = 0; nt < wmm->nTracks; nt++) { if (wmm->wFormat == 2) { ret += wmm->tracks[nt].dwLength; } else if (wmm->tracks[nt].dwLength > ret) { ret = wmm->tracks[nt].dwLength; } } /* FIXME: this is wrong if there is a tempo change inside the file */ return MIDI_ConvertPulseToMS(wmm, ret);}/************************************************************************** * MIDI_mciOpen [internal] */static DWORD MIDI_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSA lpParms){ DWORD dwRet = 0; DWORD dwDeviceID; WINE_MCIMIDI* wmm = (WINE_MCIMIDI*)mciGetDriverData(wDevID); TRACE("(%04x, %08lX, %p)\n", wDevID, dwFlags, lpParms); if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID; if (dwFlags & MCI_OPEN_SHAREABLE) return MCIERR_HARDWARE; if (wmm->nUseCount > 0) { /* The driver is already opened on this channel * MIDI sequencer cannot be shared */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -