572 lines
14 KiB
C++
572 lines
14 KiB
C++
//---------------------------------------------------------------------------
|
|
#include <vcl.h>
|
|
#pragma hdrstop
|
|
|
|
#include "GlobalUnit.h"
|
|
#include "Compose.h"
|
|
#include "MidiTrack.h"
|
|
//---------------------------------------------------------------------------
|
|
#pragma package(smart_init)
|
|
|
|
/***************************************************************************************/
|
|
// 全局变量
|
|
/***************************************************************************************/
|
|
TMidi g_Midi; //MIDI轨道对象
|
|
TMidiTrackList& g_aMidiTracks = g_Midi.MidiTrackList(); //MIDI轨道列表
|
|
int g_iSelMidiTrack = -1; //当前处理的MIDI轨道
|
|
|
|
/***************************************************************************************/
|
|
// Midi类函数
|
|
void TMidi::ClearMidiTracks()
|
|
{
|
|
ClearMidiTrackNotes();
|
|
|
|
while( !m_lstMidiTracks.empty() ){
|
|
delete m_lstMidiTracks.back();
|
|
m_lstMidiTracks.pop_back();
|
|
}
|
|
|
|
while( !m_aChnoUsed.empty() ){
|
|
delete m_aChnoUsed.back();
|
|
m_aChnoUsed.pop_back();
|
|
}
|
|
|
|
m_nMidiFile = 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::ClearMidiTrackNotes()
|
|
{
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
TMidiTrack* pTrack = m_lstMidiTracks[i];
|
|
|
|
while( !pTrack->aNotes.empty() ){
|
|
delete pTrack->aNotes.back();
|
|
pTrack->aNotes.pop_back();
|
|
}
|
|
|
|
pTrack->ptMin = 0xFF;
|
|
pTrack->ptMax = 0;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TMidiTrack* TMidi::FindMidiTrack( BYTE file, BYTE chno )
|
|
{
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
if( m_lstMidiTracks[i]->file==file && m_lstMidiTracks[i]->chno==chno ){
|
|
return m_lstMidiTracks[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int TMidi::GetMidiTrackIndex( BYTE file, BYTE chno )
|
|
{
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
if( m_lstMidiTracks[i]->file==file
|
|
&& m_lstMidiTracks[i]->chno==chno ){
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::SortMidiTracks()
|
|
{
|
|
/* ClearMidiTracks();
|
|
|
|
if( !g_pUseStyle )
|
|
return;
|
|
|
|
TStyleItem* pStyleItem = g_pUseStyle;
|
|
*/
|
|
for( int i=0; i<g_Opus.StageCount(); i++ ){
|
|
TOpusStage* pStage = g_lstOpusStages[i];
|
|
|
|
for( int j=0; j<pStage->aTracks.size(); j++ ){
|
|
TTrack* pTrk = pStage->aTracks[j];
|
|
|
|
// BYTE ch = ( pTrk->iBank==0xFF || pTrk->iBank<0x80 ) ? pTrk->iChanNo
|
|
// : pTrk->iChanSpec;
|
|
|
|
TMidiTrack* pMT = GetValidMidiTrack( i, pTrk->iFile, pTrk->iChanSpec,
|
|
pTrk->iBank, pTrk->iProg );
|
|
|
|
if( pTrk->iChanSpec!=pMT->chno || pTrk->iFile!=pMT->file ){
|
|
pTrk->iFile = pMT->file;
|
|
pTrk->iChanSpec = pMT->chno;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
for( int k=0; k<m_lstMidiTracks.size(); k++ ){
|
|
TMidiTrack* pChannel = m_lstMidiTracks[k];
|
|
|
|
DebugPrint( "\nchan %d bank=%d chno=%d prog=%d\n", k, pChannel->bank, pChannel->chno, pChannel->prog );
|
|
AnsiString strStages = " stages:";
|
|
for( int m=0; m<pChannel->aStages.size(); m++ ){
|
|
strStages += " ";
|
|
strStages += IntToStr( pChannel->aStages[m] );
|
|
}
|
|
|
|
DebugPrint( "%s\n", strStages.c_str() );
|
|
}
|
|
|
|
for( int m=0; m<m_aChnoUsed.size(); m++ ){
|
|
AnsiString strStages = "\nbank=";
|
|
strStages += IntToStr( m_aChnoUsed[m]->bank );
|
|
|
|
strStages += " used:";
|
|
|
|
for( int n=0; n<16; n++ ){
|
|
if( m_aChnoUsed[m]->chno & (1<<n) ){
|
|
strStages += " ";
|
|
strStages += IntToStr( n );
|
|
}
|
|
}
|
|
|
|
strStages += "\n";
|
|
|
|
DebugPrint( "%s\n", strStages.c_str() );
|
|
}
|
|
#endif
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TChnoUsed* TMidi::GetChnoUsed( BYTE file, BYTE bank )
|
|
{
|
|
TChnoUsed* pCU = NULL;
|
|
for( int i=0; i<m_aChnoUsed.size(); i++ ){
|
|
TChnoUsed* pChUs = m_aChnoUsed[i];
|
|
|
|
if( pChUs->file==file && pChUs->bank==bank ){
|
|
pCU = pChUs;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !pCU ){
|
|
pCU = new TChnoUsed( bank, file );
|
|
AddChnoUsed( pCU );
|
|
}
|
|
|
|
return pCU;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
BYTE TMidi::GetValidFile( BYTE bank, BYTE chno )
|
|
{
|
|
WORD ch = ( 1<<chno );
|
|
TChnoUsed* pCU = NULL;
|
|
for( int i=0; i<m_aChnoUsed.size(); i++ ){
|
|
TChnoUsed* pChUs = m_aChnoUsed[i];
|
|
|
|
if( pChUs->bank==bank && !(pChUs->chno & ch) ){
|
|
pCU = pChUs;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !pCU ){
|
|
pCU = new TChnoUsed( bank, m_nMidiFile++ );
|
|
AddChnoUsed( pCU );
|
|
}
|
|
|
|
pCU->chno |= ch;
|
|
return pCU->file;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TMidiTrack* TMidi::GetValidMidiTrack( int iStage, BYTE file, BYTE chno, BYTE bank, BYTE prog )
|
|
{
|
|
vector<int> aStages;
|
|
aStages.push_back( iStage );
|
|
|
|
return GetValidMidiTrack( aStages, file, chno, bank, prog );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TMidiTrack* TMidi::GetValidMidiTrack( vector<int>& lstStages, BYTE file, BYTE chno, BYTE bank, BYTE prog )
|
|
{
|
|
TMidiTrack* pMidiTrack = NULL;
|
|
vector<TMidiTrack*> aValidTracks;
|
|
bool bVSTi = bank!=0xFF && bank>=0x80;
|
|
|
|
if( file!=0xFF ){
|
|
TMidiTrack* pMT = FindMidiTrack( file, chno );
|
|
|
|
if( !pMT ){
|
|
pMidiTrack = new TMidiTrack( bank, chno, prog, file );
|
|
AddMidiTrack( pMidiTrack );
|
|
|
|
goto EXIT_LINE;
|
|
}
|
|
else if( pMT->bank==bank && (!bVSTi && ( pMT->chno==9 && chno==9
|
|
|| pMT->chno!=9 && chno!=9
|
|
&& pMT->prog==prog ) //GM的鼓通道或相同bank和prog
|
|
|| bVSTi && pMT->chno==chno ) //VSTi的通道相同
|
|
&& pMT->ValidStages( lstStages ) ){
|
|
pMidiTrack = pMT;
|
|
|
|
goto EXIT_LINE;
|
|
}
|
|
}
|
|
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
TMidiTrack* pMT = m_lstMidiTracks[i];
|
|
|
|
if( bank==pMT->bank && (!bVSTi && ( pMT->chno==9 && chno==9
|
|
|| pMT->chno!=9 && chno!=9
|
|
&& pMT->prog==prog ) //GM的鼓通道或相同bank和prog
|
|
|| bVSTi && pMT->chno==chno ) //VSTi的通道相同
|
|
&& pMT->ValidStages( lstStages ) ){
|
|
pMidiTrack = pMT;
|
|
|
|
goto EXIT_LINE;
|
|
}
|
|
}
|
|
|
|
//未找到有效MidiTrack,新建
|
|
for( int i=0; i<m_aChnoUsed.size(); i++ ){
|
|
if( m_aChnoUsed[i]->bank==bank ){
|
|
TChnoUsed* pCU = m_aChnoUsed[i];
|
|
|
|
if( bVSTi && ( pCU->chno & (1<<chno) )) //VSTi不重设chno
|
|
continue;
|
|
|
|
if( chno==9 && !(pCU->chno & 0x200) || chno!=9 && (pCU->chno & 0xFDFF)!=0xFDFF ){
|
|
if( pCU->chno & (1<<chno) ){
|
|
for( int n=0; n<16; n++ ){
|
|
if( n!=9 && !(pCU->chno & (1<<n)) ){
|
|
chno = n;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pMidiTrack = new TMidiTrack( bank, chno, prog, pCU->file );
|
|
AddMidiTrack( pMidiTrack );
|
|
|
|
goto EXIT_LINE;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXIT_LINE:
|
|
if( !pMidiTrack ){
|
|
pMidiTrack = new TMidiTrack( bank, chno, prog, m_nMidiFile++ );
|
|
AddMidiTrack( pMidiTrack );
|
|
}
|
|
|
|
for( int i=0; i<lstStages.size(); i++ ){
|
|
pMidiTrack->AddStage( lstStages[i] );
|
|
}
|
|
|
|
return pMidiTrack;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::AddMidiTrack( TMidiTrack* pCh, int iInd )
|
|
{
|
|
if( iInd>=0 && iInd<m_lstMidiTracks.size() ){
|
|
m_lstMidiTracks.insert( m_lstMidiTracks.begin()+iInd, pCh );
|
|
}
|
|
else{
|
|
m_lstMidiTracks.push_back( pCh );
|
|
}
|
|
|
|
if( m_nMidiFile<=pCh->file )
|
|
m_nMidiFile = pCh->file+1;
|
|
|
|
TChnoUsed* pCU = GetChnoUsed( pCh->file, pCh->bank );
|
|
pCU->chno |= ( 1 << pCh->chno );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::AddChnoUsed( TChnoUsed* pCU )
|
|
{
|
|
int m = 0;
|
|
for( ; m<m_aChnoUsed.size(); m++ ){
|
|
if( (char)m_aChnoUsed[m]->bank > (char)pCU->bank ){
|
|
m_aChnoUsed.insert( m_aChnoUsed.begin()+m, pCU );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( m==m_aChnoUsed.size() )
|
|
m_aChnoUsed.push_back( pCU );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::DeleteMidiTrack( TMidiTrack* pCh )
|
|
{
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
if( pCh==m_lstMidiTracks[i] ){
|
|
TChnoUsed* pCU = GetChnoUsed( pCh->file, pCh->bank );
|
|
pCU->chno &= ~( 1 << pCh->chno );
|
|
if( pCU->chno==0 )
|
|
DeleteChnoUsed( pCU );
|
|
|
|
delete pCh;
|
|
m_lstMidiTracks.erase( m_lstMidiTracks.begin() + i );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::DeleteChnoUsed( TChnoUsed* pCU )
|
|
{
|
|
for( int i=0; i<m_aChnoUsed.size(); i++ ){
|
|
if( pCU==m_aChnoUsed[i] ){
|
|
delete pCU;
|
|
m_aChnoUsed.erase( m_aChnoUsed.begin() + i );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::CutStage( int iStage )
|
|
{
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
TMidiTrack* pMT = m_lstMidiTracks[i];
|
|
|
|
for( int j=pMT->aStages.size()-1; j>=0; j-- ){
|
|
if( pMT->aStages[j]>iStage )
|
|
pMT->aStages[j]++;
|
|
else if( pMT->aStages[j]==iStage ){
|
|
pMT->aStages.insert( pMT->aStages.begin()+j+1, iStage+1 );
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::InsertStage( int iStage )
|
|
{
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
TMidiTrack* pMT = m_lstMidiTracks[i];
|
|
|
|
for( int j=pMT->aStages.size()-1; j>=0; j-- ){
|
|
if( pMT->aStages[j]>=iStage )
|
|
pMT->aStages[j]++;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::RemoveStage( int iStage, bool bSort )
|
|
{
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
TMidiTrack* pMT = m_lstMidiTracks[i];
|
|
|
|
for( int j=pMT->aStages.size()-1; j>=0; j-- ){
|
|
if( pMT->aStages[j]>iStage ){
|
|
if( bSort )
|
|
pMT->aStages[j]--;
|
|
}
|
|
else if( pMT->aStages[j]==iStage )
|
|
pMT->aStages.erase( pMT->aStages.begin()+j );
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int TMidi::ClearEmptyTrack()
|
|
{
|
|
int iFirstEmpty = -1;
|
|
|
|
for( int i=m_lstMidiTracks.size()-1; i>=0; i-- ){
|
|
if( m_lstMidiTracks[i]->aStages.empty() ){
|
|
TMidiTrack* pMT = m_lstMidiTracks[i];
|
|
TChnoUsed* pCU = GetChnoUsed( pMT->file, pMT->bank );
|
|
pCU->chno &= ~( 1 << pMT->chno );
|
|
if( pCU->chno==0 )
|
|
DeleteChnoUsed( pCU );
|
|
|
|
delete pMT;
|
|
m_lstMidiTracks.erase( m_lstMidiTracks.begin() + i );
|
|
|
|
iFirstEmpty = i;
|
|
}
|
|
}
|
|
|
|
return iFirstEmpty;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::ChangMidiChno( int iMT, BYTE fiNew, BYTE chNew )
|
|
{
|
|
TMidiTrack* pMT = m_lstMidiTracks[iMT];
|
|
|
|
for( int i=0; i<pMT->aStages.size(); i++ ){
|
|
TOpusStage* pStage = g_lstOpusStages[pMT->aStages[i]];
|
|
for( int j=0; j<pStage->aTracks.size(); j++ ){
|
|
TTrack* pTrack = pStage->aTracks[j];
|
|
if( pTrack->iFile==pMT->file && pTrack->iChanSpec==pMT->chno ){
|
|
pTrack->iFile = fiNew;
|
|
pTrack->iChanSpec = chNew;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TChnoUsed* pCU = g_Midi.GetChnoUsed( pMT->file, pMT->bank );
|
|
pCU->chno &= ~( 1 << pMT->chno );
|
|
pMT->file = fiNew;
|
|
pMT->chno = chNew;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::ChangeNotesVolume( int iMT, int iSBar, int iEBar, BYTE volume )
|
|
{
|
|
TMidiTrack* pMT = m_lstMidiTracks[iMT];
|
|
int iTickS = iSBar * POINTS_PER_BAR,
|
|
iTickE = iEBar * POINTS_PER_BAR;
|
|
|
|
for( int i=0; i<pMT->aNotes.size(); i++ ){
|
|
TMidiNote* pNote = pMT->aNotes[i];
|
|
if( pNote->tkOn < pNote->tkOff && pNote->tkOn < iTickE && pNote->tkOff > iTickS ){
|
|
pNote->volume = volume;
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TColor u_clrNote[64];
|
|
void TMidi::InitNoteColor()
|
|
{
|
|
int clrSeed[2][3] = {{52,101,100},{162,180,180}};
|
|
|
|
int r0 = clrSeed[0][0],
|
|
r1 = clrSeed[1][0],
|
|
g0 = clrSeed[0][1],
|
|
g1 = clrSeed[1][1],
|
|
b0 = clrSeed[0][2],
|
|
b1 = clrSeed[1][2];
|
|
double dR = (r1-r0) / 64.0,
|
|
dG = (g1-g0) / 64.0,
|
|
dB = (b1-b0) / 64.0;
|
|
|
|
u_clrNote[63] = RGB( r0, g0, b0 );
|
|
|
|
for (int i=62; i>0; i--){
|
|
r0 += dR;
|
|
g0 += dG;
|
|
b0 += dB;
|
|
|
|
u_clrNote[i] = RGB( r0, g0, b0 );
|
|
}
|
|
|
|
u_clrNote[0] = RGB( r1, g1, b1 );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TColor TMidi::GetNoteColor( int vol )
|
|
{
|
|
return u_clrNote[vol>>1];
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::GetSpecBanks( vector<BYTE>& lstBank )
|
|
{
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
TMidiTrack* pMT = m_lstMidiTracks[i];
|
|
if( pMT->bank!=0xFF ){
|
|
bool bAdd = true;
|
|
|
|
for( int b=0; b<lstBank.size(); b++ ){
|
|
if( pMT->bank==lstBank[b] ){
|
|
bAdd = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bAdd )
|
|
lstBank.push_back( pMT->bank );
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::SetSpecBanks( BANKMAPLIST& lstMap )
|
|
{
|
|
for( int i=0; i<m_lstMidiTracks.size(); i++ ){
|
|
TMidiTrack* pMT = m_lstMidiTracks[i];
|
|
if( pMT->bank!=0xFF ){
|
|
for( int b=0; b<lstMap.size(); b++ ){
|
|
TBankMap* pMap = lstMap[b];
|
|
|
|
if( pMT->bank==pMap->iBankOld ){
|
|
pMT->bank = pMap->iBankNew;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TMidi::WriteProj( FILE* pFile )
|
|
{
|
|
int num = m_lstMidiTracks.size();
|
|
fwrite( &num, sizeof(int), 1, pFile );
|
|
|
|
for( int i=0; i<num; i++ ){
|
|
TMidiTrack* pMT = m_lstMidiTracks[i];
|
|
|
|
fwrite( &pMT->state, sizeof(BYTE), 1, pFile );
|
|
fwrite( &pMT->volume, sizeof(BYTE), 1, pFile );
|
|
fwrite( &pMT->pan, sizeof(char), 1, pFile );
|
|
fwrite( &pMT->gain, sizeof(char), 1, pFile );
|
|
|
|
fwrite( &pMT->bank, sizeof(BYTE), 1, pFile );
|
|
fwrite( &pMT->chno, sizeof(BYTE), 1, pFile );
|
|
fwrite( &pMT->prog, sizeof(BYTE), 1, pFile );
|
|
fwrite( &pMT->file, sizeof(BYTE), 1, pFile );
|
|
}
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
|
|
int TMidi::ReadProj( FILE* pFile, DWORD dwVersion )
|
|
{
|
|
int num = 0;
|
|
fread( &num, sizeof(int), 1, pFile );
|
|
|
|
for( int i=0; i<num; i++ ){
|
|
TMidiTrack* pMT = new TMidiTrack;
|
|
|
|
fread( &pMT->state, sizeof(BYTE), 1, pFile );
|
|
fread( &pMT->volume, sizeof(BYTE), 1, pFile );
|
|
fread( &pMT->pan, sizeof(char), 1, pFile );
|
|
fread( &pMT->gain, sizeof(char), 1, pFile );
|
|
|
|
fread( &pMT->bank, sizeof(BYTE), 1, pFile );
|
|
fread( &pMT->chno, sizeof(BYTE), 1, pFile );
|
|
fread( &pMT->prog, sizeof(BYTE), 1, pFile );
|
|
fread( &pMT->file, sizeof(BYTE), 1, pFile );
|
|
|
|
AddMidiTrack( pMT );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
|