Files
AC_Pro/MidiTrack.cpp
T
2026-06-13 00:05:19 +08:00

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;
}
// ---------------------------------------------------------------------------