📄 mfi.c
字号:
if (length > infoLength) break; infoLength -= length; if (type == BE_FCC(0x7469746C /*titl*/)) { char *title; if (length == 0) return NULL; if ((title = malloc(length + 1)) == NULL) break; if (tf_read(title, length, 1, tf) != 1) { free(title); break; } title[length] = '\0'; return title; } else if (length != 0 && tf_seek(tf, length, SEEK_CUR) == -1) break; } return NULL;}typedef struct LastNoteInfo { int on, off, note, velocity;} LastNoteInfo;#define NO_LAST_NOTE_INFO -1#define LASTNOTEINFO_HAS_DATA(lni) ((lni).on != NO_LAST_NOTE_INFO)#define SEND_LASTNOTEINFO(lni, ch) if (LASTNOTEINFO_HAS_DATA((lni)[ch])) SendLastNoteInfo(lni, ch);#define SEND_AND_CLEAR_LASTNOTEINFO(lni, ch) if (LASTNOTEINFO_HAS_DATA((lni)[ch])) { SendLastNoteInfo(lni, ch); (lni)[ch].on = NO_LAST_NOTE_INFO; }inline void StoreLastNoteInfo(LastNoteInfo *info, int channel, int time, int duration, int note, int velocity){ info[channel].on = time; info[channel].off = time + duration; info[channel].note = note; info[channel].velocity = velocity;}inline void SendLastNoteInfo(const LastNoteInfo *info, int channel){ NOTE_BUF_EV_DEBUGSTR(channel, info[channel].on, note_name[info[channel].note % 12], info[channel].note / 12, info[channel].velocity, info[channel].off); MIDIEVENT(info[channel].on, ME_NOTEON, channel, info[channel].note, info[channel].velocity); MIDIEVENT(info[channel].off, ME_NOTEOFF, channel, info[channel].note, 0);}#define CHECK_AND_READ_FROM_FILE(ptr, readLen) do { \ if ((length) < (readLen) || tf_read(ptr, readLen, 1, tf) != 1) { \ ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "Odd track length."); \ return 1; \ } \ length -= readLen; \ } while(0)static int read_mfi_track(int trackNo, int length, int mfiVersion, int noteType, int extStDLength, timidity_file *tf){ uint8 data[5]; int i, pos, note, velocity, dataLength; uint8 instruments[MAX_CHANNELS]; int channelMap[4]; LastNoteInfo lastNotes[MAX_CHANNELS]; readmidi_set_track(trackNo, 1); pos = 0; velocity = 0x7F; for(i = 0; i < 4; i++) channelMap[i] = (trackNo * 4) + i; dataLength = (noteType == 1) ? 4 : 3; data[3] = 0; /* initialize for debugging purpose */ for(i = 0; i < MAX_CHANNELS; i++) lastNotes[i].on = NO_LAST_NOTE_INFO; while (length > 0) { CHECK_AND_READ_FROM_FILE(data, dataLength); pos += data[0]; if (data[1] != 0xFF) /* note */ { int channel; channel = channelMap[data[1] >> 6]; note = 0x48 - 0x1B + (data[1] & 0x3F); if (dataLength >= 4) { velocity = ((data[3] & 0xFC) >> 1) | (data[3] >> 7); /* abcdefgh -> 0abcdefa */ if (data[3] & 0x2) /* sign */ data[3] |= ~0x01; else data[3] &= 0x01; note += ((int8)data[3]) * 12; /* octave shift */ } if (LASTNOTEINFO_HAS_DATA(lastNotes[channel])) { if (lastNotes[channel].off <= pos || note != lastNotes[channel].note) { SendLastNoteInfo(lastNotes, channel); StoreLastNoteInfo(lastNotes, channel, pos, data[2], note, velocity); } #if 0 else if (note != lastNotes[channel].note) /* possible slur */ { if (lastNotes[channel].on == pos) /* may be a chord */ { SendLastNoteInfo(lastNotes, channel); StoreLastNoteInfo(lastNotes, channel, pos, data[2], note, velocity); } else /* slur */ { /* not implemented */ } } #endif else /* tie, what if the velocity isn't the same? :-) */ lastNotes[channel].off = pos + data[2]; } else StoreLastNoteInfo(lastNotes, channel, pos, data[2], note, velocity); NOTE_EVENT_DEBUGSTR(channel, note_name[note % 12], note / 12, velocity, data[2]); } else /* controls */ { if (dataLength == 3) CHECK_AND_READ_FROM_FILE(&data[3], 1); if ((data[2] & 0xF0) == 0xC0) /* tempo */ { int timebase, tempo; timebase = data[2] & 0xF; if ((timebase & 0x7) == 0x7) ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Undefined tempo timebase."); else { if (timebase & 0x8) timebase = 15 << (timebase & 0x7); else timebase = 6 << (timebase & 0x7); tempo = 48 * (1000000 * 60 / data[3] / timebase); MIDIEVENT(pos, ME_TEMPO, tempo & 0xFF, (tempo >> 16) & 0xFF, (tempo >> 8) & 0xFF); } } else if ((data[2] & 0xF0) == 0xF0) /* extended controls */ { int extLength, channel; uint8 extData[512]; CHECK_AND_READ_FROM_FILE(&data[4], 1); extLength = (data[3] << 8) | data[4]; if (extLength <= sizeof extData) { CHECK_AND_READ_FROM_FILE(extData, extLength); switch(data[2] & 0xF) { /* case 0x0: /-* modify envelope (MFi1) */ case 0x1: /* vibrato (MFi1) */ if (mfiVersion == 1 && extData[0] == 0x01 && extData[1] & 0x01) { channel = channelMap[extData[1] >> 5]; MIDIEVENT(pos, ME_MODULATION_WHEEL, channel, (extData[2] & 0xC0) ? 64 : 0, 0) } break; /*case 0xF: /-* system exclusive (MFi2 or MFi3) */ default: EX_UNKNOWN_EXT_DATA_DEBUGSTR(extLength); } } else { if (tf_seek(tf, extLength, SEEK_CUR) == -1) return 1; EX_UNKNOWN_EXT_DATA_DEBUGSTR(extLength); length -= extLength; } } else { int part, channel, value; #define GET_PART_AND_CHANNEL(p, c) (p) = data[3] >> 6; (c) = channelMap[p] switch(data[2]) { case 0xB0: /* master volume */ value = MAPBITS2(data[3] & 0x7F, 7, 16); MIDIEVENT(pos, ME_MASTER_VOLUME, 0, value & 0xFF, value >> 8); EX_NCDATA_DEBUGSTR1("Master Volume", "%d", value); break; case 0xBA: /* set drum channel flag */ channel = (data[3] >> 3) & 0xF; value = data[3] & 0x1; MIDIEVENT(pos, ME_DRUMPART, channel, value, 0); EX_DATA_DEBUGSTR1("Drum Flag", channel, "%d", value); break; case 0xD0: /* music begin/end */ /* ignored */ break; /* case 0xDD: /-* loop begin/end */ case 0xDE: /* nop */ break; case 0xDF: /* end-of-track */ if (length != 0) { ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "Premature end-of-track (%d)", length); length = 0; } break; case 0xE0: /* program change */ GET_PART_AND_CHANNEL(part, channel); SEND_AND_CLEAR_LASTNOTEINFO(lastNotes, channel); /*MIDIEVENT(pos, ME_DRUMPART, channel, 0, 0);*/ instruments[channel] = (instruments[channel] & 0x40) | (data[3] & 0x3F); MIDIEVENT(pos, ME_PROGRAM, channel, instruments[channel], 0); EX_DATA_DEBUGSTR1("Program Change", channel, "%d", instruments[channel]); break; case 0xE1: /* pre program change */ GET_PART_AND_CHANNEL(part, channel); instruments[channel] = (data[3] & 0x1) << 6; EX_DATA_DEBUGSTR1("Pre Program Change", channel, "%d", instruments[channel]); break; case 0xE2: /* volume */ GET_PART_AND_CHANNEL(part, channel); value = MAPBITS(data[3] & 0x3F, 6, 7); MIDIEVENT(pos, ME_MAINVOLUME, channel, value, 0); EX_DATA_DEBUGSTR1("Volume", channel, "%d", value); break; case 0xE3: /* pan */ GET_PART_AND_CHANNEL(part, channel); value = MAPBITS(data[3] & 0x3F, 6, 7); MIDIEVENT(pos, ME_PAN, channel, value, 0); EX_DATA_DEBUGSTR1("Pan", channel, "%d", value); break; case 0xE4: /* pitch bend (MFi3) */ if (mfiVersion >= 3) { GET_PART_AND_CHANNEL(part, channel); value = MAPBITS2(data[3] & 0x3F, 6, 14); MIDIEVENT(pos, ME_PITCHWHEEL, channel, value & 0x7F, value >> 7); EX_DATA_DEBUGSTR1("Pitch Bend", channel, "%d", value); } break; case 0xE5: /* map part to channel */ part = data[3] >> 6; SEND_AND_CLEAR_LASTNOTEINFO(lastNotes, channelMap[part]); channelMap[part] = data[3] & 0xF; SEND_AND_CLEAR_LASTNOTEINFO(lastNotes, channelMap[part]); EX_DATA_DEBUGSTR1("Map Channel", part, "%d", channelMap[part]); break; case 0xE6: /* expression */ GET_PART_AND_CHANNEL(part, channel); value = data[3] & 0x3F; if (value & 0x20) value = 64 - (((value ^ 0x3F) + 1) << 1); else if (value & 0x10) value = 64 + 1 + (value << 1); else value = 64 + (value << 1); MIDIEVENT(pos, ME_EXPRESSION, channel, value, 0); EX_DATA_DEBUGSTR1("Expression", channel, "%d", value); break; case 0xE7: /* pitch bend range (MFi3) */ if (mfiVersion >= 3) { GET_PART_AND_CHANNEL(part, channel); value = data[3] & 0x3F; MIDIEVENT(pos, ME_RPN_MSB, channel, 0, 0); MIDIEVENT(pos, ME_RPN_LSB, channel, 0, 0); MIDIEVENT(pos, ME_DATA_ENTRY_MSB, channel, value, 0); EX_DATA_DEBUGSTR1("Pitch Bend Range", channel, "%d", value); } break; case 0xEA: /* vibrato (MFi3) */ if (mfiVersion >= 3) { GET_PART_AND_CHANNEL(part, channel); value = MAPBITS(data[3] & 0x3F, 6, 7); MIDIEVENT(pos, ME_MODULATION_WHEEL, channel, value, 0); EX_DATA_DEBUGSTR1("Vibrato", channel, "%d", value); } break; default: EX_UNKNOWN_DATA_DEBUGSTR(); } } } } for(i = 0; i < MAX_CHANNELS; i++) { SEND_LASTNOTEINFO(lastNotes, i); } return 0;}static int tf_read_beint16(int *value, timidity_file *tf){ uint16 value_; if (tf_read(&value_, 2, 1, tf) != 1) return 0; *value = BE_SHORT(value_); return 1;}static int tf_read_beint32(int *value, timidity_file *tf){ uint32 value_; if (tf_read(&value_, 4, 1, tf) != 1) return 0; *value = BE_LONG(value_); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -