Mercurial > audlegacy-plugins
view src/modplug/load_mid.cxx @ 2292:61548eb4f676
first changes to make sndfile compatible with VFS
author | mf0102 <0102@gmx.at> |
---|---|
date | Thu, 10 Jan 2008 19:49:26 +0100 |
parents | 3673c7ec4ea2 |
children |
line wrap: on
line source
/* * This program is free software; you can redistribute it and modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the license or (at your * option) any later version. * * Authors: Olivier Lapicque <olivierl@jps.net> */ ////////////////////////////////////////////// // MIDI loader // ////////////////////////////////////////////// #include "stdafx.h" #include "sndfile.h" #define MIDI_DRUMCHANNEL 10 #define MIDI_MAXTRACKS 64 UINT gnMidiImportSpeed = 3; UINT gnMidiPatternLen = 128; #pragma pack(1) typedef struct MIDIFILEHEADER { CHAR id[4]; // "MThd" = 0x6468544D DWORD len; // 6 WORD w1; // 1? WORD wTrks; // 2? WORD wDivision; // F0 } MIDIFILEHEADER; typedef struct MIDITRACKHEADER { CHAR id[4]; // "MTrk" = 0x6B72544D DWORD len; } MIDITRACKHEADER; static LONG midivolumetolinear(UINT nMidiVolume) { return (nMidiVolume * nMidiVolume << 16) / (127*127); } ////////////////////////////////////////////////////////////////////// // Midi Loader Internal Structures #define CHNSTATE_NOTEOFFPENDING 0x0001 // MOD Channel State description (current volume, panning, etc...) typedef struct MODCHANNELSTATE { DWORD flags; // Channel Flags WORD idlecount; WORD pitchsrc, pitchdest; // Pitch Bend (current position/new position) BYTE parent; // Midi Channel parent BYTE pan; // Channel Panning 0-255 BYTE note; // Note On # (0=available) } MODCHANNELSTATE; // MIDI Channel State (Midi Channels 0-15) typedef struct MIDICHANNELSTATE { DWORD flags; // Channel Flags WORD pitchbend; // Pitch Bend Amount (14-bits unsigned) BYTE note_on[128]; // If note=on -> MOD channel # + 1 (0 if note=off) BYTE program; // Channel Midi Program WORD bank; // 0-16383 // -- Controllers --------- function ---------- CC# --- range --- init (midi) --- BYTE pan; // Channel Panning CC10 [0-255] 128 (64) BYTE expression; // Channel Expression CC11 0-128 128 (127) BYTE volume; // Channel Volume CC7 0-128 80 (100) BYTE modulation; // Modulation CC1 0-127 0 BYTE pitchbendrange;// Pitch Bend Range 64 } MIDICHANNELSTATE; typedef struct MIDITRACK { LPCBYTE ptracks, ptrmax; DWORD status; LONG nexteventtime; } MIDITRACK; #pragma pack() LPCSTR szMidiGroupNames[17] = { "Piano", "Chromatic Percussion", "Organ", "Guitar", "Bass", "Strings", "Ensemble", "Brass", "Reed", "Pipe", "Synth Lead", "Synth Pad", "Synth Effects", "Ethnic", "Percussive", "Sound Effects", "Percussions" }; LPCSTR szMidiProgramNames[128] = { // 1-8: Piano "Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano", "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavi", // 9-16: Chromatic Percussion "Celesta", "Glockenspiel", "Music Box", "Vibraphone", "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", // 17-24: Organ "Drawbar Organ", "Percussive Organ", "Rock Organ", "Church Organ", "Reed Organ", "Accordion", "Harmonica", "Tango Accordion", // 25-32: Guitar "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", "Electric Guitar (jazz)", "Electric Guitar (clean)", "Electric Guitar (muted)", "Overdriven Guitar", "Distortion Guitar", "Guitar harmonics", // 33-40 Bass "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", // 41-48 Strings "Violin", "Viola", "Cello", "Contrabass", "Tremolo Strings", "Pizzicato Strings", "Orchestral Harp", "Timpani", // 49-56 Ensemble "String Ensemble 1", "String Ensemble 2", "SynthStrings 1", "SynthStrings 2", "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", // 57-64 Brass "Trumpet", "Trombone", "Tuba", "Muted Trumpet", "French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2", // 65-72 Reed "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax", "Oboe", "English Horn", "Bassoon", "Clarinet", // 73-80 Pipe "Piccolo", "Flute", "Recorder", "Pan Flute", "Blown Bottle", "Shakuhachi", "Whistle", "Ocarina", // 81-88 Synth Lead "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", "Lead 4 (chiff)", "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8 (bass + lead)", // 89-96 Synth Pad "Pad 1 (new age)", "Pad 2 (warm)", "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", "Pad 7 (halo)", "Pad 8 (sweep)", // 97-104 Synth Effects "FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", "FX 8 (sci-fi)", // 105-112 Ethnic "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bag pipe", "Fiddle", "Shanai", // 113-120 Percussive "Tinkle Bell", "Agogo", "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", // 121-128 Sound Effects "Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", "Applause", "Gunshot" }; // Notes 25-85 LPCSTR szMidiPercussionNames[61] = { "Seq Click", "Brush Tap", "Brush Swirl", "Brush Slap", "Brush Swirl W/Attack", "Snare Roll", "Castanet", "Snare Lo", "Sticks", "Bass Drum Lo", "Open Rim Shot", "Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", "Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi Hat", "High Floor Tom", "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat", "Low-Mid Tom", "Hi Mid Tom", "Crash Cymbal 1", "High Tom", "Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine", "Splash Cymbal", "Cowbell", "Crash Cymbal 2", "Vibraslap", "Ride Cymbal 2", "Hi Bongo", "Low Bongo", "Mute Hi Conga", "Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale", "High Agogo", "Low Agogo", "Cabasa", "Maracas", "Short Whistle", "Long Whistle", "Short Guiro", "Long Guiro", "Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica", "Open Cuica", "Mute Triangle", "Open Triangle", "Shaker", "Jingle Bell", "Bell Tree", }; const WORD kMidiChannelPriority[16] = { 0xFFFE, 0xFFFC, 0xFFF8, 0xFFF0, 0xFFE0, 0xFFC0, 0xFF80, 0xFF00, 0xFE00, 0xFDFF, 0xF800, 0xF000, 0xE000, 0xC000, 0x8000, 0x0000, }; /////////////////////////////////////////////////////////////////////////// // Helper functions static LONG getmidilong(LPCBYTE &p, LPCBYTE pmax) //---------------------------------------------------------- { DWORD n; UINT a; a = (p < pmax) ? *(p++) : 0; n = 0; while (a&0x80) { n = (n<<7)|(a&0x7F); a = (p < pmax) ? *(p++) : 0; } return (n<<7)|(LONG)a; } // Returns MOD tempo and tick multiplier static int ConvertMidiTempo(int tempo_us, int *pTickMultiplier) //------------------------------------------------------------- { int nBestModTempo = 120; int nBestError = 1000000; // 1s int nBestMultiplier = 1; int nSpeed = gnMidiImportSpeed; for (int nModTempo=110; nModTempo<=240; nModTempo++) { int tick_us = (2500000) / nModTempo; int nFactor = (tick_us+tempo_us/2) / tempo_us; if (!nFactor) nFactor = 1; int nError = tick_us - tempo_us * nFactor; if (nError < 0) nError = -nError; if (nError < nBestError) { nBestError = nError; nBestModTempo = nModTempo; nBestMultiplier = nFactor; } if ((!nError) || ((nError<=1) && (nFactor==64))) break; } *pTickMultiplier = nBestMultiplier * nSpeed; return nBestModTempo; } //////////////////////////////////////////////////////////////////////////////// // Maps a midi instrument - returns the instrument number in the file UINT CSoundFile::MapMidiInstrument(DWORD dwBankProgram, UINT nChannel, UINT nNote) //-------------------------------------------------------------------------------- { INSTRUMENTHEADER *penv; UINT nProgram = dwBankProgram & 0x7F; UINT nBank = dwBankProgram >> 7; nNote &= 0x7F; if (nNote >= 120) return 0; for (UINT i=1; i<=m_nInstruments; i++) if (Headers[i]) { INSTRUMENTHEADER *p = Headers[i]; // Drum Kit ? if (nChannel == MIDI_DRUMCHANNEL) { if (nNote == p->nMidiDrumKey) return i; } else // Melodic Instrument { if (nProgram == p->nMidiProgram) return i; } } if ((m_nInstruments + 1 >= MAX_INSTRUMENTS) || (m_nSamples + 1 >= MAX_SAMPLES)) return 0; penv = new INSTRUMENTHEADER; if (!penv) return 0; memset(penv, 0, sizeof(INSTRUMENTHEADER)); m_nSamples++; m_nInstruments++; Headers[m_nInstruments] = penv; penv->wMidiBank = nBank; penv->nMidiProgram = nProgram; penv->nMidiChannel = nChannel; if (nChannel == MIDI_DRUMCHANNEL) penv->nMidiDrumKey = nNote; penv->nGlobalVol = 128; penv->nFadeOut = 1024; penv->nPan = 128; penv->nPPC = 5*12; penv->nNNA = NNA_NOTEOFF; penv->nDCT = (nChannel == MIDI_DRUMCHANNEL) ? DCT_SAMPLE : DCT_NOTE; penv->nDNA = DNA_NOTEFADE; for (UINT j=0; j<120; j++) { int mapnote = j+1; if (nChannel == MIDI_DRUMCHANNEL) { mapnote = 61; /*mapnote = 61 + j - nNote; if (mapnote < 1) mapnote = 1; if (mapnote > 120) mapnote = 120;*/ } penv->Keyboard[j] = m_nSamples; penv->NoteMap[j] = (BYTE)mapnote; } penv->dwFlags |= ENV_VOLUME; if (nChannel != MIDI_DRUMCHANNEL) penv->dwFlags |= ENV_VOLSUSTAIN; penv->VolEnv.nNodes=4; penv->VolEnv.Ticks[0]=0; penv->VolEnv.Values[0] = 64; penv->VolEnv.Ticks[1] = 10; penv->VolEnv.Values[1] = 64; penv->VolEnv.Ticks[2] = 15; penv->VolEnv.Values[2] = 48; penv->VolEnv.Ticks[3] = 20; penv->VolEnv.Values[3] = 0; penv->VolEnv.nSustainStart=1; penv->VolEnv.nSustainEnd=1; // Sample Ins[m_nSamples].nPan = 128; Ins[m_nSamples].nVolume = 256; Ins[m_nSamples].nGlobalVol = 64; if (nChannel != MIDI_DRUMCHANNEL) { // GM Midi Name strcpy((char*)penv->name, (char*)szMidiProgramNames[nProgram]); strcpy((char*)m_szNames[m_nSamples], (char*)szMidiProgramNames[nProgram]); } else { strcpy((char*)penv->name, "Percussions"); if ((nNote >= 24) && (nNote <= 84)) strcpy((char*)m_szNames[m_nSamples], (char*)szMidiPercussionNames[nNote-24]); else strcpy((char*)m_szNames[m_nSamples], "Percussions"); } return m_nInstruments; } ///////////////////////////////////////////////////////////////// // Loader Status #define MIDIGLOBAL_SONGENDED 0x0001 #define MIDIGLOBAL_FROZEN 0x0002 #define MIDIGLOBAL_UPDATETEMPO 0x0004 #define MIDIGLOBAL_UPDATEMASTERVOL 0x0008 // Midi Globals #define MIDIGLOBAL_GMSYSTEMON 0x0100 #define MIDIGLOBAL_XGSYSTEMON 0x0200 BOOL CSoundFile::ReadMID(const BYTE *lpStream, DWORD dwMemLength) //--------------------------------------------------------------- { const MIDIFILEHEADER *pmfh = (const MIDIFILEHEADER *)lpStream; const MIDITRACKHEADER *pmth; MODCHANNELSTATE chnstate[MAX_BASECHANNELS]; MIDICHANNELSTATE midichstate[16]; MIDITRACK miditracks[MIDI_MAXTRACKS]; DWORD dwMemPos, dwGlobalFlags, tracks, tempo; UINT row, pat, midimastervol; short int division; int midi_clock, nTempoUsec, nPPQN, nTickMultiplier; // Fix import parameters if (gnMidiImportSpeed < 2) gnMidiImportSpeed = 2; if (gnMidiImportSpeed > 6) gnMidiImportSpeed = 6; if (gnMidiPatternLen < 64) gnMidiPatternLen = 64; if (gnMidiPatternLen > 256) gnMidiPatternLen = 256; // Detect RMI files if ((dwMemLength > 12) && (memcmp(lpStream, "RIFF",4) == 0) && (memcmp(lpStream, "RMID",4) == 0)) { lpStream += 12; dwMemLength -= 12; while (dwMemLength > 8) { char *id = (char*)lpStream; DWORD len = *(DWORD *)(lpStream+4); lpStream += 8; dwMemLength -= 8; if ((memcmp(id, "data",4) == 0) && (len < dwMemLength)) { dwMemLength = len; pmfh = (const MIDIFILEHEADER *)lpStream; break; } if (len >= dwMemLength) return FALSE; lpStream += len; dwMemLength -= len; } } // MIDI File Header if ((dwMemLength < sizeof(MIDIFILEHEADER)+8) || (memcmp(pmfh->id, "MThd",4) != 0)) return FALSE; dwMemPos = 8 + bswapBE32(pmfh->len); if (dwMemPos >= dwMemLength - 8) return FALSE; pmth = (MIDITRACKHEADER *)(lpStream+dwMemPos); tracks = bswapBE16(pmfh->wTrks); if ((!tracks) || (memcmp(pmth->id, "MTrk", 4) != 0)) return FALSE; if (tracks > MIDI_MAXTRACKS) tracks = MIDI_MAXTRACKS; // Reading File... m_nType = MOD_TYPE_MID; m_nChannels = 32; m_nSamples = 0; m_nInstruments = 0; m_dwSongFlags |= (SONG_LINEARSLIDES | SONG_INSTRUMENTMODE); m_szNames[0][0] = 0; // MIDI->MOD Tempo Conversion division = bswapBE16(pmfh->wDivision); if (division < 0) { int nFrames = -(division>>8); int nSubFrames = (division & 0xff); nPPQN = nFrames * nSubFrames / 2; if (!nPPQN) nPPQN = 1; } else { nPPQN = (division) ? division : 96; } nTempoUsec = 500000 / nPPQN; tempo = ConvertMidiTempo(nTempoUsec, &nTickMultiplier); m_nDefaultTempo = tempo; m_nDefaultSpeed = gnMidiImportSpeed; m_nDefaultGlobalVolume = 256; midimastervol = m_nDefaultGlobalVolume; // Initializing memset(Order, 0xFF, sizeof(Order)); memset(chnstate, 0, sizeof(chnstate)); memset(miditracks, 0, sizeof(miditracks)); memset(midichstate, 0, sizeof(midichstate)); // Initializing Patterns Order[0] = 0; for (UINT ipat=0; ipat<MAX_PATTERNS; ipat++) PatternSize[ipat] = gnMidiPatternLen; // Initializing Channels for (UINT ics=0; ics<MAX_BASECHANNELS; ics++) { // Channel settings ChnSettings[ics].nPan = 128; ChnSettings[ics].nVolume = 64; ChnSettings[ics].dwFlags = 0; // Channels state chnstate[ics].pan = 128; chnstate[ics].pitchsrc = 0x2000; chnstate[ics].pitchdest = 0x2000; } // Initializing Track Pointers for (UINT itrk=0; itrk<tracks; itrk++) { miditracks[itrk].nexteventtime = -1; miditracks[itrk].status = 0x2F; pmth = (MIDITRACKHEADER *)(lpStream+dwMemPos); if (dwMemPos + 8 >= dwMemLength) break; DWORD len = bswapBE32(pmth->len); if ((memcmp(pmth->id, "MTrk", 4) == 0) && (dwMemPos + 8 + len <= dwMemLength)) { // Initializing midi tracks miditracks[itrk].ptracks = lpStream+dwMemPos+8; miditracks[itrk].ptrmax = miditracks[itrk].ptracks + len; miditracks[itrk].nexteventtime = getmidilong(miditracks[itrk].ptracks, miditracks[itrk].ptrmax); } dwMemPos += 8 + len; } // Initializing midi channels state for (UINT imidi=0; imidi<16; imidi++) { midichstate[imidi].pan = 128; // middle midichstate[imidi].expression = 128; // no attenuation midichstate[imidi].volume = 80; // GM specs defaults to 100 midichstate[imidi].pitchbend = 0x2000; // Pitch Bend Amount midichstate[imidi].pitchbendrange = 64; // Pitch Bend Range: +/- 2 semitones } //////////////////////////////////////////////////////////////////////////// // Main Midi Sequencer Loop pat = 0; row = 0; midi_clock = 0; dwGlobalFlags = MIDIGLOBAL_UPDATETEMPO | MIDIGLOBAL_FROZEN; do { // Allocate current pattern if not allocated yet if (!Patterns[pat]) { Patterns[pat] = AllocatePattern(PatternSize[pat], m_nChannels); if (!Patterns[pat]) break; } dwGlobalFlags |= MIDIGLOBAL_SONGENDED; MODCOMMAND *m = Patterns[pat] + row * m_nChannels; // Parse Tracks for (UINT trk=0; trk<tracks; trk++) if (miditracks[trk].ptracks) { MIDITRACK *ptrk = &miditracks[trk]; dwGlobalFlags &= ~MIDIGLOBAL_SONGENDED; while ((ptrk->ptracks) && (ptrk->nexteventtime >= 0) && (midi_clock+(nTickMultiplier>>2) >= ptrk->nexteventtime)) { if (ptrk->ptracks[0] & 0x80) ptrk->status = *(ptrk->ptracks++); switch(ptrk->status) { ///////////////////////////////////////////////////////////////////// // End Of Track case 0x2F: // End Of Song case 0xFC: ptrk->ptracks = NULL; break; ///////////////////////////////////////////////////////////////////// // SYSEX messages case 0xF0: case 0xF7: { LONG len = getmidilong(ptrk->ptracks, ptrk->ptrmax); if ((len > 1) && (ptrk->ptracks + len <ptrk->ptrmax) && (ptrk->ptracks[len-1] == 0xF7)) { DWORD dwSysEx1 = 0, dwSysEx2 = 0; if (len >= 4) dwSysEx1 = (*((DWORD *)(ptrk->ptracks))) & 0x7F7F7F7F; if (len >= 8) dwSysEx2 = (*((DWORD *)(ptrk->ptracks+4))) & 0x7F7F7F7F; // GM System On if ((len == 5) && (dwSysEx1 == 0x01097F7E)) { dwGlobalFlags |= MIDIGLOBAL_GMSYSTEMON; } else // XG System On if ((len == 8) && ((dwSysEx1 & 0xFFFFF0FF) == 0x004c1043) && (dwSysEx2 == 0x77007e00)) { dwGlobalFlags |= MIDIGLOBAL_XGSYSTEMON; } else // Midi Master Volume if ((len == 7) && (dwSysEx1 == 0x01047F7F)) { midimastervol = midivolumetolinear(ptrk->ptracks[5] & 0x7F) >> 8; if (midimastervol < 16) midimastervol = 16; dwGlobalFlags |= MIDIGLOBAL_UPDATEMASTERVOL; } } ptrk->ptracks += len; } break; ////////////////////////////////////////////////////////////////////// // META-events: FF.code.len.data[len] case 0xFF: { UINT i = *(ptrk->ptracks++); LONG len = getmidilong(ptrk->ptracks, ptrk->ptrmax); if (ptrk->ptracks+len > ptrk->ptrmax) { // EOF ptrk->ptracks = NULL; } else switch(i) { // FF.01 [text]: Song Information case 0x01: if (!len) break; if ((len < 32) && (!m_szNames[0][0])) { memcpy(m_szNames[0], ptrk->ptracks, len); m_szNames[0][len] = 0; } else if ((!m_lpszSongComments) && (ptrk->ptracks[0]) && (ptrk->ptracks[0] < 0x7F)) { m_lpszSongComments = new char [len+1]; if (m_lpszSongComments) { memcpy(m_lpszSongComments, ptrk->ptracks, len); m_lpszSongComments[len] = 0; } } break; // FF.02 [text]: Song Copyright case 0x02: if (!len) break; if ((!m_lpszSongComments) && (ptrk->ptracks[0]) && (ptrk->ptracks[0] < 0x7F) && (len > 7)) { m_lpszSongComments = new char [len+1]; if (m_lpszSongComments) { memcpy(m_lpszSongComments, ptrk->ptracks, len); m_lpszSongComments[len] = 0; } } break; // FF.03: Sequence Name case 0x03: // FF.06: Sequence Text (->Pattern names) case 0x06: if ((len > 1) && (!trk)) { UINT k = (len < 32) ? len : 31; CHAR s[32]; memcpy(s, ptrk->ptracks, k); s[k] = 0; if ((!strnicmp((char*)s, "Copyri", 6)) || (!s[0])) break; if (i == 0x03) { if (!m_szNames[0][0]) strcpy((char*)m_szNames[0], (char*)s); } } break; // FF.07: Cue Point (marker) // FF.20: Channel Prefix // FF.2F: End of Track case 0x2F: ptrk->status = 0x2F; ptrk->ptracks = NULL; break; // FF.51 [tttttt]: Set Tempo case 0x51: { LONG l = ptrk->ptracks[0]; l = (l << 8) | ptrk->ptracks[1]; l = (l << 8) | ptrk->ptracks[2]; if (l <= 0) break; nTempoUsec = l / nPPQN; if (nTempoUsec < 100) nTempoUsec = 100; tempo = ConvertMidiTempo(nTempoUsec, &nTickMultiplier); dwGlobalFlags |= MIDIGLOBAL_UPDATETEMPO; } break; // FF.58: Time Signature // FF.7F: Sequencer-Specific } if (ptrk->ptracks) ptrk->ptracks += len; } break; ////////////////////////////////////////////////////////////////////////// // Regular Voice Events default: { UINT midich = (ptrk->status & 0x0F)+1; UINT midist = ptrk->status & 0xF0; MIDICHANNELSTATE *pmidich = &midichstate[midich-1]; UINT note, velocity; switch(midist) { ////////////////////////////////// // Note Off: 80.note.velocity case 0x80: // Note On: 90.note.velocity case 0x90: note = ptrk->ptracks[0] & 0x7F; velocity = (midist == 0x90) ? (ptrk->ptracks[1] & 0x7F) : 0; ptrk->ptracks += 2; // Note On: 90.note.velocity if (velocity) { // Start counting rows dwGlobalFlags &= ~MIDIGLOBAL_FROZEN; // if the note is already playing, we reuse this channel UINT nchn = pmidich->note_on[note]; if ((nchn) && (chnstate[nchn-1].parent != midich)) nchn = 0; // or else, we look for an available child channel if (!nchn) { for (UINT i=0; i<m_nChannels; i++) if (chnstate[i].parent == midich) { if ((!chnstate[i].note) && ((!m[i].note) || (m[i].note & 0x80))) { // found an available channel nchn = i+1; break; } } } // still nothing? in this case, we try to allocate a new mod channel if (!nchn) { for (UINT i=0; i<m_nChannels; i++) if (!chnstate[i].parent) { nchn = i+1; chnstate[i].parent = midich; break; } } // still not? we have to steal a voice from another channel // We found our channel: let's do the note on if (nchn) { pmidich->note_on[note] = nchn; nchn--; chnstate[nchn].pitchsrc = pmidich->pitchbend; chnstate[nchn].pitchdest = pmidich->pitchbend; chnstate[nchn].flags &= ~CHNSTATE_NOTEOFFPENDING; chnstate[nchn].idlecount = 0; chnstate[nchn].note = note+1; int realnote = note; if (midich != 10) { realnote += (((int)pmidich->pitchbend - 0x2000) * pmidich->pitchbendrange) / (0x2000*32); if (realnote < 0) realnote = 0; if (realnote > 119) realnote = 119; } m[nchn].note = realnote+1; m[nchn].instr = MapMidiInstrument(pmidich->program + ((UINT)pmidich->bank << 7), midich, note); m[nchn].volcmd = VOLCMD_VOLUME; LONG vol = midivolumetolinear(velocity) >> 8; vol = (vol * (LONG)pmidich->volume * (LONG)pmidich->expression) >> 13; if (vol > 256) vol = 256; if (vol < 4) vol = 4; m[nchn].vol = (BYTE)(vol>>2); // Channel Panning if ((!m[nchn].command) && (pmidich->pan != chnstate[nchn].pan)) { chnstate[nchn].pan = pmidich->pan; m[nchn].param = pmidich->pan; m[nchn].command = CMD_PANNING8; } } } else // Note Off; 90.note.00 if (!(dwGlobalFlags & MIDIGLOBAL_FROZEN)) { UINT nchn = pmidich->note_on[note]; if (nchn) { nchn--; chnstate[nchn].flags |= CHNSTATE_NOTEOFFPENDING; chnstate[nchn].note = 0; pmidich->note_on[note] = 0; } else { for (UINT i=0; i<m_nChannels; i++) { if ((chnstate[i].parent == midich) && (chnstate[i].note == note+1)) { chnstate[i].note = 0; chnstate[i].flags |= CHNSTATE_NOTEOFFPENDING; } } } } break; /////////////////////////////////// // A0.xx.yy: Aftertouch case 0xA0: { ptrk->ptracks += 2; } break; /////////////////////////////////// // B0: Control Change case 0xB0: { UINT controller = ptrk->ptracks[0]; UINT value = ptrk->ptracks[1] & 0x7F; ptrk->ptracks += 2; switch(controller) { // Bn.00.xx: Bank Select MSB (GS) case 0x00: pmidich->bank &= 0x7F; pmidich->bank |= (value << 7); break; // Bn.01.xx: Modulation Depth case 0x01: pmidich->pitchbendrange = value; break; // Bn.07.xx: Volume case 0x07: pmidich->volume = (BYTE)(midivolumetolinear(value) >> 9); break; // Bn.0B.xx: Expression case 0x0B: pmidich->expression = (BYTE)(midivolumetolinear(value) >> 9); break; // Bn.0A.xx: Pan case 0x0A: pmidich->pan = value * 2; break; // Bn.20.xx: Bank Select LSB (GS) case 0x20: pmidich->bank &= (0x7F << 7); pmidich->bank |= value; break; // Bn.79.00: Reset All Controllers (GM) case 0x79: pmidich->modulation = 0; pmidich->expression = 128; pmidich->pitchbend = 0x2000; pmidich->pitchbendrange = 64; // Should also reset pedals (40h-43h), NRP, RPN, aftertouch break; // Bn.78.00: All Sound Off (GS) // Bn.7B.00: All Notes Off (GM) case 0x78: case 0x7B: if (value == 0x00) { // All Notes Off for (UINT k=0; k<m_nChannels; k++) { if (chnstate[k].note) { chnstate[k].flags |= CHNSTATE_NOTEOFFPENDING; chnstate[k].note = 0; } } } break; //////////////////////////////////// // Controller List // // Bn.02.xx: Breath Control // Bn.04.xx: Foot Pedal // Bn.05.xx: Portamento Time (Glissando Time) // Bn.06.xx: Data Entry MSB // Bn.08.xx: Balance // Bn.10-13.xx: GP Control #1-#4 // Bn.20-3F.xx: Data LSB for controllers 0-31 // Bn.26.xx: Data Entry LSB // Bn.40.xx: Hold Pedal #1 // Bn.41.xx: Portamento (GS) // Bn.42.xx: Sostenuto (GS) // Bn.43.xx: Soft Pedal (GS) // Bn.44.xx: Legato Pedal // Bn.45.xx: Hold Pedal #2 // Bn.46.xx: Sound Variation // Bn.47.xx: Sound Timbre // Bn.48.xx: Sound Release Time // Bn.49.xx: Sound Attack Time // Bn.4A.xx: Sound Brightness // Bn.4B-4F.xx: Sound Control #6-#10 // Bn.50-53.xx: GP Control #5-#8 // Bn.54.xx: Portamento Control (GS) // Bn.5B.xx: Reverb Level (GS) // Bn.5C.xx: Tremolo Depth // Bn.5D.xx: Chorus Level (GS) // Bn.5E.xx: Celeste Depth // Bn.5F.xx: Phaser Depth // Bn.60.xx: Data Increment // Bn.61.xx: Data Decrement // Bn.62.xx: Non-RPN Parameter LSB (GS) // Bn.63.xx: Non-RPN Parameter MSB (GS) // Bn.64.xx: RPN Parameter LSB (GM) // Bn.65.xx: RPN Parameter MSB (GM) // Bn.7A.00: Local On/Off // Bn.7C.00: Omni Mode Off // Bn.7D.00: Omni Mode On // Bn.7E.mm: Mono Mode On // Bn.7F.00: Poly Mode On } } break; //////////////////////////////// // C0.pp: Program Change case 0xC0: { pmidich->program = ptrk->ptracks[0] & 0x7F; ptrk->ptracks++; } break; //////////////////////////////// // D0: Channel Aftertouch (Polyphonic Key Pressure) case 0xD0: { ptrk->ptracks++; } break; //////////////////////////////// // E0: Pitch Bend case 0xE0: { pmidich->pitchbend = (WORD)(((UINT)ptrk->ptracks[1] << 7) + (ptrk->ptracks[0] & 0x7F)); for (UINT i=0; i<128; i++) if (pmidich->note_on[i]) { UINT nchn = pmidich->note_on[i]-1; if (chnstate[nchn].parent == midich) { chnstate[nchn].pitchdest = pmidich->pitchbend; } } ptrk->ptracks+=2; } break; ////////////////////////////////////// // F0 & Unsupported commands: skip it default: ptrk->ptracks++; } }} // switch+default // Process to next event if (ptrk->ptracks) { ptrk->nexteventtime += getmidilong(ptrk->ptracks, ptrk->ptrmax); } if (ptrk->ptracks >= ptrk->ptrmax) ptrk->ptracks = NULL; } // End reached? if (ptrk->ptracks >= ptrk->ptrmax) ptrk->ptracks = NULL; } //////////////////////////////////////////////////////////////////// // Move to next row if (!(dwGlobalFlags & MIDIGLOBAL_FROZEN)) { // Check MOD channels status for (UINT ichn=0; ichn<m_nChannels; ichn++) { // Pending Global Effects ? if (!m[ichn].command) { if ((chnstate[ichn].pitchsrc != chnstate[ichn].pitchdest) && (chnstate[ichn].parent)) { int newpitch = chnstate[ichn].pitchdest; int pitchbendrange = midichstate[chnstate[ichn].parent-1].pitchbendrange; // +/- 256 for +/- pitch bend range int slideamount = (newpitch - (int)chnstate[ichn].pitchsrc) / (int)32; if (slideamount) { const int ppdiv = (16 * 128 * (gnMidiImportSpeed-1)); newpitch = (int)chnstate[ichn].pitchsrc + slideamount; if (slideamount < 0) { int param = (-slideamount * pitchbendrange + ppdiv/2) / ppdiv; if (param >= 0x80) param = 0x80; if (param > 0) { m[ichn].param = (BYTE)param; m[ichn].command = CMD_PORTAMENTODOWN; } } else { int param = (slideamount * pitchbendrange + ppdiv/2) / ppdiv; if (param >= 0x80) param = 0x80; if (param > 0) { m[ichn].param = (BYTE)param; m[ichn].command = CMD_PORTAMENTOUP; } } } chnstate[ichn].pitchsrc = (WORD)newpitch; } else if (dwGlobalFlags & MIDIGLOBAL_UPDATETEMPO) { m[ichn].command = CMD_TEMPO; m[ichn].param = (BYTE)tempo; dwGlobalFlags &= ~MIDIGLOBAL_UPDATETEMPO; } else if (dwGlobalFlags & MIDIGLOBAL_UPDATEMASTERVOL) { m[ichn].command = CMD_GLOBALVOLUME; m[ichn].param = midimastervol >> 1; // 0-128 dwGlobalFlags &= ~MIDIGLOBAL_UPDATEMASTERVOL; } } // Check pending noteoff events for m[ichn] if (!m[ichn].note) { if (chnstate[ichn].flags & CHNSTATE_NOTEOFFPENDING) { chnstate[ichn].flags &= ~CHNSTATE_NOTEOFFPENDING; m[ichn].note = 0xFF; } // Check State of channel chnstate[ichn].idlecount++; if ((chnstate[ichn].note) && (chnstate[ichn].idlecount >= 50)) { chnstate[ichn].note = 0; m[ichn].note = 0xFF; // only if not drum channel ? } else if (chnstate[ichn].idlecount >= 500) // 20secs of inactivity { chnstate[ichn].idlecount = 0; chnstate[ichn].parent = 0; } } } if ((++row) >= PatternSize[pat]) { pat++; if (pat >= MAX_PATTERNS-1) break; Order[pat] = pat; Order[pat+1] = 0xFF; row = 0; } } // Increase midi clock midi_clock += nTickMultiplier; } while (!(dwGlobalFlags & MIDIGLOBAL_SONGENDED)); return TRUE; }