You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
567 lines
13 KiB
567 lines
13 KiB
/**************************************************************************
|
|
|
|
track.cc - class track, which has a midi file track and its events
|
|
This file is part of LibKMid 0.9.5
|
|
Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
|
|
LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
|
|
|
|
***************************************************************************/
|
|
|
|
#include "track.h"
|
|
#include <stdlib.h>
|
|
#include "sndcard.h"
|
|
#include "midispec.h"
|
|
#include "midfile.h"
|
|
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#endif
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
#define T2MS(ticks) (((double)ticks)*(double)60000L)/((double)tempoToMetronomeTempo(tempo)*(double)tPCN)
|
|
|
|
#define MS2T(ms) (((ms)*(double)tempoToMetronomeTempo(tempo)*(double)tPCN)/((double)60000L))
|
|
|
|
#define PEDANTIC_TRACK
|
|
#define CHANGETEMPO_ONLY_IN_TRACK0
|
|
//#define TRACKDEBUG
|
|
//#define TRACKDEBUG2
|
|
|
|
MidiTrack::MidiTrack(FILE *file,int tpcn,int Id)
|
|
{
|
|
id=Id;
|
|
tPCN=tpcn;
|
|
currentpos=0;
|
|
size=0;
|
|
data=0L;
|
|
tempo=1000000;
|
|
if (feof(file))
|
|
{
|
|
clear();
|
|
return;
|
|
};
|
|
size=readLong(file);
|
|
#ifdef TRACKDEBUG
|
|
printf("Track %d : Size %ld\n",id,size);
|
|
#endif
|
|
data=new uchar[size];
|
|
if (data==NULL)
|
|
{
|
|
perror("track: Not enough memory ?");
|
|
exit(-1);
|
|
}
|
|
ulong rsize=0;
|
|
if ((rsize=fread(data,1,size,file))!=size)
|
|
{
|
|
fprintf(stderr,"track (%d): File is corrupt : Couldn't load track (%ld!=%ld) !!\n", id, rsize, size);
|
|
size=rsize;
|
|
};
|
|
/*
|
|
ptrdata=data;
|
|
current_ticks=0;
|
|
delta_ticks=readVariableLengthValue();
|
|
wait_ticks=delta_ticks;
|
|
endoftrack=0;
|
|
*/
|
|
init();
|
|
}
|
|
|
|
MidiTrack::~MidiTrack()
|
|
{
|
|
delete data;
|
|
endoftrack=1;
|
|
currentpos=0;
|
|
size=0;
|
|
}
|
|
|
|
int MidiTrack::power2to(int i)
|
|
{
|
|
return 1<<i;
|
|
}
|
|
|
|
ulong MidiTrack::readVariableLengthValue(void)
|
|
{
|
|
ulong dticks=0;
|
|
|
|
while ((*ptrdata) & 0x80)
|
|
{
|
|
#ifdef PEDANTIC_TRACK
|
|
if (currentpos>=size)
|
|
{
|
|
endoftrack=1;
|
|
fprintf(stderr, "track (%d) : EndofTrack found by accident !\n",id);
|
|
delta_ticks = wait_ticks = ~0;
|
|
time_at_next_event=10000 * 60000L;
|
|
return 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
dticks=(dticks << 7) | (*ptrdata) & 0x7F;
|
|
ptrdata++;currentpos++;
|
|
}
|
|
|
|
}
|
|
dticks=((dticks << 7) | (*ptrdata) & 0x7F);
|
|
ptrdata++;currentpos++;
|
|
|
|
#ifdef PEDANTIC_TRACK
|
|
|
|
if (currentpos>=size)
|
|
{
|
|
endoftrack=1;
|
|
fprintf(stderr,"track (%d): EndofTrack found by accident 2 !\n",id);
|
|
dticks=0;
|
|
delta_ticks = wait_ticks = ~0;
|
|
time_at_next_event=10000 * 60000L;
|
|
return 0;
|
|
}
|
|
#endif
|
|
#ifdef TRACKDEBUG
|
|
printfdebug("track(%d): DTICKS : %ld\n",id,dticks);
|
|
usleep(10);
|
|
#endif
|
|
return dticks;
|
|
}
|
|
|
|
int MidiTrack::ticksPassed (ulong ticks)
|
|
{
|
|
if (endoftrack==1) return 0;
|
|
if (ticks>wait_ticks)
|
|
{
|
|
printfdebug("track (%d): ERROR : TICKS PASSED > WAIT TICKS\n", id);
|
|
return 1;
|
|
}
|
|
wait_ticks-=ticks;
|
|
return 0;
|
|
}
|
|
|
|
int MidiTrack::msPassed (ulong ms)
|
|
{
|
|
if (endoftrack==1) return 0;
|
|
current_time+=ms;
|
|
//fprintf(stderr, "old + %ld = CURR %g ", ms,current_time);
|
|
if ( current_time>time_at_next_event )
|
|
{
|
|
fprintf(stderr, "track (%d): ERROR : MS PASSED > WAIT MS\n", id);
|
|
return 1;
|
|
}
|
|
#ifdef TRACKDEBUG
|
|
if (current_time==time_at_next_event) printfdebug("track(%d): _OK_",id);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int MidiTrack::currentMs(double ms)
|
|
{
|
|
if (endoftrack==1) return 0;
|
|
current_time=ms;
|
|
//printfdebug("CURR %g",current_time);
|
|
#ifdef PEDANTIC_TRACK
|
|
if (current_time>time_at_next_event)
|
|
{
|
|
fprintf(stderr,"track(%d): ERROR : MS PASSED > WAIT MS\n", id);
|
|
exit(-1);
|
|
return 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void MidiTrack::readEvent(MidiEvent *ev)
|
|
{
|
|
int i,j;
|
|
if (endoftrack==1)
|
|
{
|
|
ev->command=0;
|
|
return;
|
|
}
|
|
/*
|
|
printfdebug("...... %d\n",id);
|
|
printfdebug("current : %g , tane : %g\n",current_time,time_at_next_event);
|
|
printfdebug("......\n");
|
|
*/
|
|
int skip_event=0;
|
|
current_time=time_at_next_event;
|
|
if (((*ptrdata)&0x80)!=0)
|
|
{
|
|
ev->command=(*ptrdata);
|
|
ptrdata++;currentpos++;
|
|
lastcommand=ev->command;
|
|
}
|
|
else
|
|
{
|
|
ev->command=lastcommand;
|
|
}
|
|
|
|
#ifdef PEDANTIC_TRACK
|
|
if (currentpos>=size)
|
|
{
|
|
endoftrack=1;
|
|
delta_ticks = wait_ticks = ~0;
|
|
time_at_next_event=10000 * 60000L;
|
|
ev->command=MIDI_SYSTEM_PREFIX;
|
|
ev->chn=0xF;
|
|
ev->d1=ME_END_OF_TRACK;
|
|
fprintf(stderr, "track (%d): EndofTrack found by accident 3\n",id);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
ev->chn=ev->command & 0xF;
|
|
ev->command=ev->command & 0xF0;
|
|
switch (ev->command)
|
|
{
|
|
case (MIDI_NOTEON) :
|
|
ev->note = *ptrdata;ptrdata++;currentpos++;
|
|
ev->vel = *ptrdata;ptrdata++;currentpos++;
|
|
if (ev->vel==0)
|
|
note[ev->chn][ev->note]=FALSE;
|
|
else
|
|
note[ev->chn][ev->note]=TRUE;
|
|
|
|
#ifdef TRACKDEBUG2
|
|
if (ev->chn==6) {
|
|
if (ev->vel==0) printfdebug("Note Onf\n");
|
|
else printfdebug("Note On\n");
|
|
};
|
|
#endif
|
|
break;
|
|
case (MIDI_NOTEOFF) :
|
|
#ifdef TRACKDEBUG2
|
|
if (ev->chn==6) printfdebug("Note Off\n");
|
|
#endif
|
|
ev->note = *ptrdata;ptrdata++;currentpos++;
|
|
ev->vel = *ptrdata;ptrdata++;currentpos++;
|
|
note[ev->chn][ev->note]=FALSE;
|
|
|
|
break;
|
|
case (MIDI_KEY_PRESSURE) :
|
|
#ifdef TRACKDEBUG2
|
|
if (ev->chn==6) printfdebug ("Key press\n");
|
|
#endif
|
|
ev->note = *ptrdata;ptrdata++;currentpos++;
|
|
ev->vel = *ptrdata;ptrdata++;currentpos++;
|
|
break;
|
|
case (MIDI_PGM_CHANGE) :
|
|
#ifdef TRACKDEBUG2
|
|
if (ev->chn==6) printfdebug ("Pgm\n");
|
|
#endif
|
|
ev->patch = *ptrdata;ptrdata++;currentpos++;
|
|
break;
|
|
case (MIDI_CHN_PRESSURE) :
|
|
#ifdef TRACKDEBUG2
|
|
if (ev->chn==6) printfdebug ("Chn press\n");
|
|
#endif
|
|
ev->vel = *ptrdata;ptrdata++;currentpos++;
|
|
break;
|
|
case (MIDI_PITCH_BEND) :
|
|
#ifdef TRACKDEBUG2
|
|
if (ev->chn==6) printfdebug ("Pitch\n");
|
|
#endif
|
|
ev->d1 = *ptrdata;ptrdata++;currentpos++;
|
|
ev->d2 = *ptrdata;ptrdata++;currentpos++;
|
|
break;
|
|
case (MIDI_CTL_CHANGE) :
|
|
#ifdef TRACKDEBUG2
|
|
if (ev->chn==6) printfdebug (stderr, "Ctl\n");
|
|
#endif
|
|
ev->ctl = *ptrdata;ptrdata++; currentpos++;
|
|
ev->d1 = *ptrdata;ptrdata++;currentpos++;
|
|
/*
|
|
switch (ev->ctl)
|
|
{
|
|
case (96) : printfdebug("RPN Increment\n");break;
|
|
case (97) : printfdebug("RPN Decrement\n");break;
|
|
case (98) : printfdebug("nRPN 98 %d\n",ev->d1);break;
|
|
case (99) : printfdebug("nRPN 99 %d\n",ev->d1);break;
|
|
case (100) : printfdebug("RPN 100 %d\n",ev->d1);break;
|
|
case (101) : printfdebug("RPN 101 %d\n",ev->d1);break;
|
|
};
|
|
*/
|
|
break;
|
|
|
|
case (MIDI_SYSTEM_PREFIX) :
|
|
#ifdef TRACKDEBUG2
|
|
if (ev->chn==6) printfdebug ("Sys Prefix\n");
|
|
#endif
|
|
switch ((ev->command|ev->chn))
|
|
{
|
|
case (0xF0) :
|
|
case (0xF7) :
|
|
ev->length=readVariableLengthValue();
|
|
#ifdef PEDANTIC_TRACK
|
|
if (endoftrack)
|
|
{
|
|
ev->command=MIDI_SYSTEM_PREFIX;
|
|
ev->chn=0xF;
|
|
ev->d1=ME_END_OF_TRACK;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ev->data=ptrdata;
|
|
ptrdata+=ev->length;currentpos+=ev->length;
|
|
}
|
|
break;
|
|
case (0xFE):
|
|
case (0xF8):
|
|
// printfdebug("Active sensing\n");
|
|
break;
|
|
case (META_EVENT) :
|
|
ev->d1=*ptrdata;ptrdata++;currentpos++;
|
|
switch (ev->d1)
|
|
{
|
|
case (ME_END_OF_TRACK) :
|
|
i=0;
|
|
j=0;
|
|
while ((i<16)&&(note[i][j]==FALSE))
|
|
{
|
|
j++;
|
|
if (j==128) { j=0; i++; };
|
|
}
|
|
if (i<16) // that is, if there is any key still pressed
|
|
{
|
|
ptrdata--;currentpos--;
|
|
ev->chn=i;
|
|
ev->command=MIDI_NOTEOFF;
|
|
ev->note = j;
|
|
ev->vel = 0;
|
|
note[ev->chn][ev->note]=FALSE;
|
|
fprintf(stderr,"Note Off(simulated)\n");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
endoftrack=1;
|
|
delta_ticks = wait_ticks = ~0;
|
|
time_at_next_event=10000 * 60000L;
|
|
#ifdef TRACKDEBUG
|
|
printfdebug("EndofTrack %d event\n",id);
|
|
#endif
|
|
}
|
|
break;
|
|
case (ME_SET_TEMPO):
|
|
ev->length=readVariableLengthValue();
|
|
#ifdef PEDANTIC_TRACK
|
|
if (endoftrack)
|
|
{
|
|
ev->command=MIDI_SYSTEM_PREFIX;
|
|
ev->chn=0xF;
|
|
ev->d1=ME_END_OF_TRACK;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ev->data=ptrdata;
|
|
ptrdata+=ev->length;currentpos+=ev->length;
|
|
// tempo=((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2]));
|
|
// ticks_from_previous_tempochange=0;
|
|
// time_at_previous_tempochange=current_time;
|
|
#ifdef TRACKDEBUG
|
|
printfdebug("Track %d : Set Tempo : %ld\n",id,tempo);
|
|
#endif
|
|
#ifdef CHANGETEMPO_ONLY_IN_TRACK0
|
|
if (id!=0) skip_event=1;
|
|
#endif
|
|
}
|
|
break;
|
|
case (ME_TIME_SIGNATURE) :
|
|
ev->length=*ptrdata;ptrdata++;currentpos++;
|
|
ev->d2=*ptrdata;ptrdata++;currentpos++;
|
|
ev->d3=power2to(*ptrdata);ptrdata++;currentpos++;
|
|
ev->d4=*ptrdata;ptrdata++;currentpos++;
|
|
ev->d5=*ptrdata;ptrdata++;currentpos++;
|
|
#ifdef TRACKDEBUG
|
|
printfdebug("TIME SIGNATURE :\n");
|
|
printfdebug("%d\n",ev->d2);
|
|
printfdebug("---- %d metronome , %d number of 32nd notes per quarter note\n",ev->d4,ev->d5);
|
|
printfdebug("%d\n",ev->d3);
|
|
#endif
|
|
break;
|
|
case (ME_TRACK_SEQ_NUMBER) :
|
|
case (ME_TEXT) :
|
|
case (ME_COPYRIGHT) :
|
|
case (ME_SEQ_OR_TRACK_NAME) :
|
|
case (ME_TRACK_INSTR_NAME) :
|
|
case (ME_LYRIC) :
|
|
case (ME_MARKER) :
|
|
case (ME_CUE_POINT) :
|
|
case (ME_CHANNEL_PREFIX) :
|
|
case (ME_MIDI_PORT) :
|
|
case (ME_SMPTE_OFFSET) :
|
|
case (ME_KEY_SIGNATURE) :
|
|
ev->length=readVariableLengthValue();
|
|
#ifdef PEDANTIC_TRACK
|
|
if (endoftrack)
|
|
{
|
|
ev->command=MIDI_SYSTEM_PREFIX;
|
|
ev->chn=0xF;
|
|
ev->d1=ME_END_OF_TRACK;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ev->data=ptrdata;
|
|
ptrdata+=ev->length;currentpos+=ev->length;
|
|
}
|
|
break;
|
|
default:
|
|
#ifdef GENERAL_DEBUG_MESSAGES
|
|
fprintf(stderr,"track (%d) : Default handler for meta event " \
|
|
"0x%x\n", id, ev->d1);
|
|
#endif
|
|
ev->length=readVariableLengthValue();
|
|
#ifdef PEDANTIC_TRACK
|
|
if (endoftrack)
|
|
{
|
|
ev->command=MIDI_SYSTEM_PREFIX;
|
|
ev->chn=0xF;
|
|
ev->d1=ME_END_OF_TRACK;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ev->data=ptrdata;
|
|
ptrdata+=ev->length;currentpos+=ev->length;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
default :
|
|
fprintf(stderr,"track (%d): Default handler for system event 0x%x\n",
|
|
id, (ev->command|ev->chn));
|
|
break;
|
|
}
|
|
break;
|
|
default :
|
|
fprintf(stderr,"track (%d): Default handler for event 0x%x\n",
|
|
id, (ev->command|ev->chn));
|
|
break;
|
|
}
|
|
#ifdef PEDANTIC_TRACK
|
|
if (currentpos>=size)
|
|
{
|
|
endoftrack=1;
|
|
delta_ticks = wait_ticks = ~0;
|
|
time_at_next_event=10000 * 60000L;
|
|
printfdebug("track (%d): EndofTrack reached\n",id);
|
|
}
|
|
#endif
|
|
if (endoftrack==0)
|
|
{
|
|
current_ticks+=delta_ticks;
|
|
delta_ticks=readVariableLengthValue();
|
|
#ifdef PEDANTIC_TRACK
|
|
if (endoftrack)
|
|
{
|
|
ev->command=MIDI_SYSTEM_PREFIX;
|
|
ev->chn=0xF;
|
|
ev->d1=ME_END_OF_TRACK;
|
|
return;
|
|
}
|
|
#endif
|
|
ticks_from_previous_tempochange+=delta_ticks;
|
|
|
|
time_at_next_event=T2MS(ticks_from_previous_tempochange)+time_at_previous_tempochange;
|
|
/*
|
|
printf("tane2 : %g, ticks : %g, delta_ticks %ld, tempo : %ld\n",
|
|
time_at_next_event,ticks_from_previous_tempochange,delta_ticks,tempo);
|
|
printf("timeatprevtc %g , curr %g\n",time_at_previous_tempochange,current_time);
|
|
*/
|
|
wait_ticks=delta_ticks;
|
|
|
|
}
|
|
if (skip_event) readEvent(ev);
|
|
}
|
|
|
|
|
|
void MidiTrack::clear(void)
|
|
{
|
|
endoftrack=1;
|
|
ptrdata=data;
|
|
current_ticks=0;
|
|
currentpos=0;
|
|
|
|
for (int i=0;i<16;i++)
|
|
for (int j=0;j<128;j++)
|
|
note[i][j]=FALSE;
|
|
|
|
delta_ticks = wait_ticks = ~0;
|
|
time_at_previous_tempochange=0;
|
|
current_time=0;
|
|
ticks_from_previous_tempochange=0;
|
|
tempo=1000000;
|
|
time_at_next_event=10000 * 60000L;
|
|
|
|
}
|
|
|
|
|
|
void MidiTrack::init(void)
|
|
{
|
|
if (data==0L) { clear(); return; };
|
|
endoftrack=0;
|
|
ptrdata=data;
|
|
current_ticks=0;
|
|
currentpos=0;
|
|
|
|
for (int i=0;i<16;i++)
|
|
for (int j=0;j<128;j++)
|
|
note[i][j]=FALSE;
|
|
|
|
delta_ticks=readVariableLengthValue();
|
|
if (endoftrack) return;
|
|
wait_ticks=delta_ticks;
|
|
|
|
|
|
time_at_previous_tempochange=0;
|
|
current_time=0;
|
|
ticks_from_previous_tempochange=wait_ticks;
|
|
tempo=1000000;
|
|
time_at_next_event=T2MS(delta_ticks);
|
|
//printf("tane1 : %g\n",time_at_next_event);
|
|
}
|
|
|
|
void MidiTrack::changeTempo(ulong t)
|
|
{
|
|
if (endoftrack==1) return;
|
|
if (tempo==t) return;
|
|
double ticks;
|
|
time_at_previous_tempochange=current_time;
|
|
ticks=MS2T(time_at_next_event-current_time);
|
|
tempo=t;
|
|
time_at_next_event=T2MS(ticks)+current_time;
|
|
ticks_from_previous_tempochange=ticks;
|
|
|
|
}
|
|
|
|
/*
|
|
double MidiTrack::absMsOfNextEvent (void)
|
|
{
|
|
//printf("%d : %g\n",id,time_at_next_event);
|
|
return time_at_next_event;
|
|
}
|
|
*/
|
|
|
|
#undef T2MS
|
|
#undef MS2T
|