diff options
Diffstat (limited to 'libkmid/track.cc')
-rw-r--r-- | libkmid/track.cc | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/libkmid/track.cc b/libkmid/track.cc new file mode 100644 index 000000000..f936906e7 --- /dev/null +++ b/libkmid/track.cc @@ -0,0 +1,566 @@ +/************************************************************************** + + 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 ((note[i][j]==FALSE)&&(i<16)) + { + 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 |