|
|
|
/**************************************************************************
|
|
|
|
|
|
|
|
player.cc - class MidiPlayer. Plays a set of tracks
|
|
|
|
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.
|
|
|
|
|
|
|
|
$Id$
|
|
|
|
|
|
|
|
Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
|
|
|
|
|
|
|
|
***************************************************************************/
|
|
|
|
#include "player.h"
|
|
|
|
#include "sndcard.h"
|
|
|
|
#include "midispec.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include "midistat.h"
|
|
|
|
#include "mt32togm.h"
|
|
|
|
|
|
|
|
//#define PLAYERDEBUG
|
|
|
|
//#define GENERAL_DEBUG_MESSAGES
|
|
|
|
|
|
|
|
#define T2MS(ticks) (((double)ticks)*(double)60000L)/((double)tempoToMetronomeTempo(tempo)*(double)info->ticksPerCuarterNote)
|
|
|
|
|
|
|
|
#define MS2T(ms) (((ms)*(double)tempoToMetronomeTempo(tempo)*(double)info->ticksPerCuarterNote)/((double)60000L))
|
|
|
|
|
|
|
|
#define REMOVEDUPSTRINGS
|
|
|
|
|
|
|
|
MidiPlayer::MidiPlayer(DeviceManager *midi_,PlayerController *pctl)
|
|
|
|
{
|
|
|
|
midi=midi_;
|
|
|
|
info=NULL;
|
|
|
|
tracks=NULL;
|
|
|
|
songLoaded=0;
|
|
|
|
ctl=pctl;
|
|
|
|
spev=NULL;
|
|
|
|
na=NULL;
|
|
|
|
parsesong=true;
|
|
|
|
generatebeats=false;
|
|
|
|
}
|
|
|
|
|
|
|
|
MidiPlayer::~MidiPlayer()
|
|
|
|
{
|
|
|
|
removeSpecialEvents();
|
|
|
|
removeSong();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MidiPlayer::removeSong(void)
|
|
|
|
{
|
|
|
|
if ((songLoaded)&&(tracks!=NULL))
|
|
|
|
{
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("Removing song from memory\n");
|
|
|
|
#endif
|
|
|
|
int i=0;
|
|
|
|
while (i<info->ntracks)
|
|
|
|
{
|
|
|
|
if (tracks[i]!=NULL) delete tracks[i];
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
delete tracks;
|
|
|
|
tracks=NULL;
|
|
|
|
if (info!=NULL)
|
|
|
|
{
|
|
|
|
delete info;
|
|
|
|
info=NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
songLoaded=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MidiPlayer::loadSong(const char *filename)
|
|
|
|
{
|
|
|
|
removeSong();
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("Loading Song : %s\n",filename);
|
|
|
|
#endif
|
|
|
|
info=new MidiFileInfo;
|
|
|
|
int ok;
|
|
|
|
tracks=readMidiFile(filename,info,ok);
|
|
|
|
if (ok<0) return ok;
|
|
|
|
if (tracks==NULL) return -4;
|
|
|
|
|
|
|
|
parseInfoData(info,tracks,ctl->ratioTempo);
|
|
|
|
|
|
|
|
if (parsesong)
|
|
|
|
{
|
|
|
|
parseSpecialEvents();
|
|
|
|
if (generatebeats) generateBeats();
|
|
|
|
}
|
|
|
|
|
|
|
|
songLoaded=1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MidiPlayer::insertBeat(SpecialEvent *ev,ulong ms,int num,int den)
|
|
|
|
{
|
|
|
|
SpecialEvent *beat=new SpecialEvent;
|
|
|
|
beat->next=ev->next;
|
|
|
|
ev->next=beat;
|
|
|
|
beat->id=1;
|
|
|
|
beat->type=7;
|
|
|
|
beat->absmilliseconds=ms;
|
|
|
|
beat->num=num;
|
|
|
|
beat->den=den;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MidiPlayer::generateBeats(void)
|
|
|
|
{
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("player::Generating Beats...\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (spev==NULL) return;
|
|
|
|
SpecialEvent *ev=spev;
|
|
|
|
SpecialEvent *nextev=ev->next;
|
|
|
|
ulong tempo=(ulong)(500000 * ctl->ratioTempo);
|
|
|
|
int i=1;
|
|
|
|
int num=4;
|
|
|
|
int den=4;
|
|
|
|
// ulong beatstep=((double)tempo*4/(den*1000));
|
|
|
|
// ulong beatstep=T2MS(info->ticksPerCuarterNote*(4/den));
|
|
|
|
double ticksleft=(((double)info->ticksPerCuarterNote*4)/den);
|
|
|
|
|
|
|
|
double beatstep=T2MS(ticksleft);
|
|
|
|
double nextbeatms=0;
|
|
|
|
double lastbeatms=0;
|
|
|
|
double measurems=0;
|
|
|
|
|
|
|
|
while (nextev!=NULL)
|
|
|
|
{
|
|
|
|
switch (ev->type)
|
|
|
|
{
|
|
|
|
case (0): // End of list
|
|
|
|
{
|
|
|
|
};break;
|
|
|
|
case (1): // Text
|
|
|
|
case (2): // Lyrics
|
|
|
|
{
|
|
|
|
};break;
|
|
|
|
case (3): // Change Tempo
|
|
|
|
{
|
|
|
|
lastbeatms=ev->absmilliseconds;
|
|
|
|
ticksleft=MS2T(nextbeatms-lastbeatms);
|
|
|
|
tempo=ev->tempo;
|
|
|
|
nextbeatms=lastbeatms+T2MS(ticksleft);
|
|
|
|
// printf("Change at %lu to %d\n",ev->absmilliseconds,ev->tempo);
|
|
|
|
// beatstep=((double)tempo*4/(den*1000));
|
|
|
|
beatstep=T2MS(((static_cast<double>(info->ticksPerCuarterNote)*4)/den));
|
|
|
|
};break;
|
|
|
|
case (6): // Change number of beats per measure
|
|
|
|
{
|
|
|
|
num=ev->num;
|
|
|
|
i=1;
|
|
|
|
den=ev->den;
|
|
|
|
// printf("Change at %lu to %d/%d\n",ev->absmilliseconds,num,den);
|
|
|
|
// beatstep=((double)tempo*4/(den*1000));
|
|
|
|
// beatstep=T2MS(info->ticksPerCuarterNote*(4/den));
|
|
|
|
beatstep=T2MS((((double)info->ticksPerCuarterNote*4)/den));
|
|
|
|
nextbeatms=ev->absmilliseconds;
|
|
|
|
};break;
|
|
|
|
};
|
|
|
|
if (nextev->absmilliseconds>nextbeatms)
|
|
|
|
{
|
|
|
|
//printf("Adding %d,%d\n",num,tot);
|
|
|
|
//printf("beat at %g , %d/%d\n",nextbeatms,i,num);
|
|
|
|
//printf(" %ld %d\n",nextev->absmilliseconds,nextev->type);
|
|
|
|
if (i == 1) {
|
|
|
|
measurems=nextbeatms;
|
|
|
|
}
|
|
|
|
insertBeat(ev, static_cast<unsigned long>(nextbeatms), i++, num);
|
|
|
|
if (i > num) {
|
|
|
|
i=1;
|
|
|
|
}
|
|
|
|
lastbeatms=nextbeatms;
|
|
|
|
nextbeatms+=beatstep;
|
|
|
|
// nextbeatms=measurems+beatstep*i;
|
|
|
|
|
|
|
|
ticksleft = ( (static_cast<double>(info->ticksPerCuarterNote)*4) / den);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ev=ev->next;
|
|
|
|
nextev=ev->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ev==NULL doesn't indicate the end of the song, so continue generating beats */
|
|
|
|
|
|
|
|
if (ev!=NULL)
|
|
|
|
{
|
|
|
|
if (ev->type==0)
|
|
|
|
{
|
|
|
|
ev=spev;
|
|
|
|
/* Looking if ev->next is NULL is not needed because
|
|
|
|
we are sure that a ev->type == 0 exists, we just have
|
|
|
|
to assure that the first spev is not the only one */
|
|
|
|
if (ev->next!=NULL)
|
|
|
|
while (ev->next->type!=0) ev=ev->next;
|
|
|
|
}
|
|
|
|
while (nextbeatms<info->millisecsTotal)
|
|
|
|
{
|
|
|
|
// printf("beat2 at %g , %d/%d\n",nextbeatms,i,num);
|
|
|
|
if (i==1) measurems=nextbeatms;
|
|
|
|
insertBeat(ev, static_cast<unsigned long>(nextbeatms), i++, num);
|
|
|
|
if (i>num) i=1;
|
|
|
|
nextbeatms+=beatstep;
|
|
|
|
ev=ev->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Regenerate IDs */
|
|
|
|
|
|
|
|
ev=spev;
|
|
|
|
i=1;
|
|
|
|
while (ev!=NULL)
|
|
|
|
{
|
|
|
|
ev->id=i++;
|
|
|
|
ev=ev->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("player::Beats Generated\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void MidiPlayer::removeSpecialEvents(void)
|
|
|
|
{
|
|
|
|
SpecialEvent * ev=spev;
|
|
|
|
while (spev!=NULL)
|
|
|
|
{
|
|
|
|
ev=spev->next;
|
|
|
|
delete spev;
|
|
|
|
spev=ev;
|
|
|
|
}
|
|
|
|
delete na;
|
|
|
|
na=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MidiPlayer::parseSpecialEvents(void)
|
|
|
|
{
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("player::Parsing...\n");
|
|
|
|
#endif
|
|
|
|
removeSpecialEvents();
|
|
|
|
spev=new SpecialEvent;
|
|
|
|
if (spev==NULL) return;
|
|
|
|
SpecialEvent *pspev=spev;
|
|
|
|
pspev->type=0;
|
|
|
|
pspev->ticks=0;
|
|
|
|
if (na) delete na;
|
|
|
|
na=new NoteArray();
|
|
|
|
if (!na) { delete spev; spev=0L; return; };
|
|
|
|
int trk;
|
|
|
|
int minTrk;
|
|
|
|
double minTime=0;
|
|
|
|
double maxTime;
|
|
|
|
ulong tempo=(ulong)(500000 * (ctl->ratioTempo));
|
|
|
|
ulong firsttempo=0;
|
|
|
|
for (int i=0;i<info->ntracks;i++)
|
|
|
|
{
|
|
|
|
tracks[i]->init();
|
|
|
|
tracks[i]->changeTempo(tempo);
|
|
|
|
}
|
|
|
|
MidiEvent *ev=new MidiEvent;
|
|
|
|
//ulong mspass;
|
|
|
|
double prevms=0;
|
|
|
|
int spev_id=1;
|
|
|
|
int j;
|
|
|
|
int parsing=1;
|
|
|
|
#ifdef REMOVEDUPSTRINGS
|
|
|
|
char lasttext[1024];
|
|
|
|
ulong lasttexttime=0;
|
|
|
|
lasttext[0]=0;
|
|
|
|
int lasttexttype=0;
|
|
|
|
#endif
|
|
|
|
while (parsing)
|
|
|
|
{
|
|
|
|
prevms=minTime;
|
|
|
|
trk=0;
|
|
|
|
minTrk=0;
|
|
|
|
maxTime=minTime + 2 * 60000L;
|
|
|
|
minTime=maxTime;
|
|
|
|
parsing=0;
|
|
|
|
while (trk<info->ntracks)
|
|
|
|
{
|
|
|
|
if (tracks[trk]->absMsOfNextEvent()<minTime)
|
|
|
|
{
|
|
|
|
minTrk=trk;
|
|
|
|
minTime=tracks[minTrk]->absMsOfNextEvent();
|
|
|
|
parsing=1;
|
|
|
|
}
|
|
|
|
trk++;
|
|
|
|
}
|
|
|
|
// if ((minTime==maxTime))
|
|
|
|
if (parsing==0)
|
|
|
|
{
|
|
|
|
// parsing=0;
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("END of parsing\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// mspass=(ulong)(minTime-prevms);
|
|
|
|
trk=0;
|
|
|
|
while (trk<info->ntracks)
|
|
|
|
{
|
|
|
|
tracks[trk]->currentMs(minTime);
|
|
|
|
trk++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
trk=minTrk;
|
|
|
|
tracks[trk]->readEvent(ev);
|
|
|
|
switch (ev->command)
|
|
|
|
{
|
|
|
|
case (MIDI_NOTEON) :
|
|
|
|
if (ev->vel==0) na->add((ulong)minTime,ev->chn,0, ev->note);
|
|
|
|
else na->add((ulong)minTime,ev->chn,1,ev->note);
|
|
|
|
break;
|
|
|
|
case (MIDI_NOTEOFF) :
|
|
|
|
na->add((ulong)minTime,ev->chn,0, ev->note);
|
|
|
|
break;
|
|
|
|
case (MIDI_PGM_CHANGE) :
|
|
|
|
na->add((ulong)minTime,ev->chn, 2,ev->patch);
|
|
|
|
break;
|
|
|
|
case (MIDI_SYSTEM_PREFIX) :
|
|
|
|
{
|
|
|
|
if ((ev->command|ev->chn)==META_EVENT)
|
|
|
|
{
|
|
|
|
switch (ev->d1)
|
|
|
|
{
|
|
|
|
case (1) :
|
|
|
|
case (5) :
|
|
|
|
{
|
|
|
|
if (pspev!=NULL)
|
|
|
|
{
|
|
|
|
pspev->absmilliseconds=(ulong)minTime;
|
|
|
|
pspev->type=ev->d1;
|
|
|
|
pspev->id=spev_id++;
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("ev->length %ld\n",ev->length);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
strncpy(pspev->text,(char *)ev->data,
|
|
|
|
(ev->length>= sizeof(lasttext))? sizeof(lasttext)-1 : (ev->length) );
|
|
|
|
pspev->text[(ev->length>= sizeof(lasttext))? sizeof(lasttext)-1:(ev->length)]=0;
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("(%s)(%s)\n",pspev->text,lasttext);
|
|
|
|
#endif
|
|
|
|
#ifdef REMOVEDUPSTRINGS
|
|
|
|
if ((strcmp(pspev->text,lasttext)!=0)||(pspev->absmilliseconds!=lasttexttime)||(pspev->type!=lasttexttype))
|
|
|
|
{
|
|
|
|
lasttexttime=pspev->absmilliseconds;
|
|
|
|
lasttexttype=pspev->type;
|
|
|
|
strncpy(lasttext, pspev->text, 1024);
|
|
|
|
lasttext[sizeof(lasttext)-1] = 0;
|
|
|
|
#endif
|
|
|
|
pspev->next=new SpecialEvent;
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
if (pspev->next==NULL) printf("pspev->next=NULL\n");
|
|
|
|
#endif
|
|
|
|
pspev=pspev->next;
|
|
|
|
#ifdef REMOVEDUPSTRINGS
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case (ME_SET_TEMPO) :
|
|
|
|
{
|
|
|
|
if (pspev!=NULL)
|
|
|
|
{
|
|
|
|
pspev->absmilliseconds=(ulong)minTime;
|
|
|
|
pspev->type=3;
|
|
|
|
pspev->id=spev_id++;
|
|
|
|
tempo=(ulong)(((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2])) * ctl->ratioTempo);
|
|
|
|
pspev->tempo=tempo;
|
|
|
|
if (firsttempo==0) firsttempo=tempo;
|
|
|
|
for (j=0;j<info->ntracks;j++)
|
|
|
|
{
|
|
|
|
tracks[j]->changeTempo(tempo);
|
|
|
|
}
|
|
|
|
pspev->next=new SpecialEvent;
|
|
|
|
pspev=pspev->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case (ME_TIME_SIGNATURE) :
|
|
|
|
{
|
|
|
|
if (pspev!=NULL)
|
|
|
|
{
|
|
|
|
pspev->absmilliseconds=(ulong)minTime;
|
|
|
|
pspev->type=6;
|
|
|
|
pspev->id=spev_id++;
|
|
|
|
pspev->num=ev->d2;
|
|
|
|
pspev->den=ev->d3;
|
|
|
|
pspev->next=new SpecialEvent;
|
|
|
|
pspev=pspev->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete ev;
|
|
|
|
pspev->type=0;
|
|
|
|
pspev->absmilliseconds=(ulong)prevms;
|
|
|
|
pspev->next=NULL;
|
|
|
|
if (firsttempo==0) firsttempo=tempo;
|
|
|
|
ctl->tempo=firsttempo;
|
|
|
|
|
|
|
|
//writeSPEV();
|
|
|
|
for (int i=0;i<info->ntracks;i++)
|
|
|
|
{
|
|
|
|
tracks[i]->init();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
NoteArray *MidiPlayer::parseNotes(void)
|
|
|
|
{
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("player::Parsing Notes...\n");
|
|
|
|
#endif
|
|
|
|
NoteArray *na=new NoteArray();
|
|
|
|
int trk;
|
|
|
|
int minTrk;
|
|
|
|
double minTime=0;
|
|
|
|
double maxTime;
|
|
|
|
for (int i=0;i<info->ntracks;i++)
|
|
|
|
{
|
|
|
|
tracks[i]->init();
|
|
|
|
};
|
|
|
|
ulong tempo=1000000;
|
|
|
|
ulong tmp;
|
|
|
|
Midi_event *ev=new Midi_event;
|
|
|
|
//ulong mspass;
|
|
|
|
double prevms=0;
|
|
|
|
int j;
|
|
|
|
int parsing=1;
|
|
|
|
while (parsing)
|
|
|
|
{
|
|
|
|
prevms=minTime;
|
|
|
|
trk=0;
|
|
|
|
minTrk=0;
|
|
|
|
maxTime=minTime + 2 * 60000L;
|
|
|
|
minTime=maxTime;
|
|
|
|
while (trk<info->ntracks)
|
|
|
|
{
|
|
|
|
if (tracks[trk]->absMsOfNextEvent()<minTime)
|
|
|
|
{
|
|
|
|
minTrk=trk;
|
|
|
|
minTime=tracks[minTrk]->absMsOfNextEvent();
|
|
|
|
};
|
|
|
|
trk++;
|
|
|
|
};
|
|
|
|
if ((minTime==maxTime))
|
|
|
|
{
|
|
|
|
parsing=0;
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("END of parsing\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// mspass=(ulong)(minTime-prevms);
|
|
|
|
trk=0;
|
|
|
|
while (trk<info->ntracks)
|
|
|
|
{
|
|
|
|
tracks[trk]->currentMs(minTime);
|
|
|
|
trk++;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
trk=minTrk;
|
|
|
|
tracks[trk]->readEvent(ev);
|
|
|
|
if (ev->command==MIDI_NOTEON)
|
|
|
|
{
|
|
|
|
if (ev->vel==0) {printf("note off at %g\n",minTime);na->add((ulong)minTime,ev->chn,0, ev->note);}
|
|
|
|
else {printf("note on at %g\n",minTime);na->add((ulong)minTime,ev->chn,1,ev->note);}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (ev->command==MIDI_NOTEOFF) na->add((ulong)minTime,ev->chn,0, ev->note);
|
|
|
|
if (ev->command==MIDI_PGM_CHANGE) na->add((ulong)minTime,ev->chn, 2,ev->patch);
|
|
|
|
if (ev->command==MIDI_SYSTEM_PREFIX)
|
|
|
|
{
|
|
|
|
if (((ev->command|ev->chn)==META_EVENT)&&(ev->d1==ME_SET_TEMPO))
|
|
|
|
{
|
|
|
|
tempo=(ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2]);
|
|
|
|
for (j=0;j<info->ntracks;j++)
|
|
|
|
{
|
|
|
|
tracks[j]->changeTempo(tempo);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
delete ev;
|
|
|
|
for (int i=0;i<info->ntracks;i++)
|
|
|
|
{
|
|
|
|
tracks[i]->init();
|
|
|
|
};
|
|
|
|
return na;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
|
|
|
|
void MidiPlayer::play(bool calloutput,void output(void))
|
|
|
|
{
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("Playing...\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (midi->midiPorts()+midi->synthDevices()==0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,"Player :: There are no midi ports !\n");
|
|
|
|
ctl->error=1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
midi->openDev();
|
|
|
|
if (midi->ok()==0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,"Player :: Couldn't play !\n");
|
|
|
|
ctl->error=1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
midi->setVolumePercentage(ctl->volumepercentage);
|
|
|
|
midi->initDev();
|
|
|
|
// parsePatchesUsed(tracks,info,ctl->gm);
|
|
|
|
midi->setPatchesToUse(info->patchesUsed);
|
|
|
|
|
|
|
|
int trk;
|
|
|
|
int minTrk;
|
|
|
|
double minTime=0;
|
|
|
|
double maxTime;
|
|
|
|
int i;
|
|
|
|
ulong tempo=(ulong)(500000 * ctl->ratioTempo);
|
|
|
|
for (i=0;i<info->ntracks;i++)
|
|
|
|
{
|
|
|
|
tracks[i]->init();
|
|
|
|
tracks[i]->changeTempo(tempo);
|
|
|
|
}
|
|
|
|
|
|
|
|
midi->tmrStart(info->ticksPerCuarterNote);
|
|
|
|
MidiEvent *ev=new MidiEvent;
|
|
|
|
ctl->ev=ev;
|
|
|
|
ctl->ticksTotal=info->ticksTotal;
|
|
|
|
ctl->ticksPlayed=0;
|
|
|
|
//ctl->millisecsPlayed=0;
|
|
|
|
ulong ticksplayed=0;
|
|
|
|
double absTimeAtChangeTempo=0;
|
|
|
|
double absTime=0;
|
|
|
|
double diffTime=0;
|
|
|
|
MiditqStatus *midistat;
|
|
|
|
//ulong mspass;
|
|
|
|
double prevms=0;
|
|
|
|
int j;
|
|
|
|
int halt=0;
|
|
|
|
ctl->tempo=tempo;
|
|
|
|
ctl->num=4;
|
|
|
|
ctl->den=4;
|
|
|
|
int playing;
|
|
|
|
ctl->paused=0;
|
|
|
|
if ((ctl->message!=0)&&(ctl->message & PLAYER_SETPOS))
|
|
|
|
{
|
|
|
|
ctl->moving=1;
|
|
|
|
ctl->message&=~PLAYER_SETPOS;
|
|
|
|
midi->sync(1);
|
|
|
|
midi->tmrStop();
|
|
|
|
midi->closeDev();
|
|
|
|
midistat = new MiditqStatus();
|
|
|
|
setPos(ctl->gotomsec,midistat);
|
|
|
|
minTime=ctl->gotomsec;
|
|
|
|
prevms=(ulong)minTime;
|
|
|
|
midi->openDev();
|
|
|
|
midi->tmrStart(info->ticksPerCuarterNote);
|
|
|
|
diffTime=ctl->gotomsec;
|
|
|
|
midistat->sendData(midi,ctl->gm);
|
|
|
|
delete midistat;
|
|
|
|
midi->setPatchesToUse(info->patchesUsed);
|
|
|
|
ctl->moving=0;
|
|
|
|
} else
|
|
|
|
for (i=0;i<16;i++)
|
|
|
|
{
|
|
|
|
if (ctl->forcepgm[i])
|
|
|
|
{
|
|
|
|
midi->chnPatchChange(i, ctl->pgm[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
timeval begintv;
|
|
|
|
gettimeofday(&begintv, NULL);
|
|
|
|
ctl->beginmillisec=begintv.tv_sec*1000+begintv.tv_usec/1000;
|
|
|
|
ctl->OK=1;
|
|
|
|
ctl->playing=playing=1;
|
|
|
|
|
|
|
|
while (playing)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
if (ctl->message!=0)
|
|
|
|
{
|
|
|
|
if (ctl->message & PLAYER_DOPAUSE)
|
|
|
|
{
|
|
|
|
diffTime=minTime;
|
|
|
|
ctl->message&=~PLAYER_DOPAUSE;
|
|
|
|
midi->sync(1);
|
|
|
|
midi->tmrStop();
|
|
|
|
ctl->paused=1;
|
|
|
|
midi->closeDev();
|
|
|
|
while ((ctl->paused)&&(!(ctl->message&PLAYER_DOSTOP))
|
|
|
|
&&(!(ctl->message&PLAYER_HALT))) sleep(1);
|
|
|
|
midi->openDev();
|
|
|
|
midi->tmrStart();
|
|
|
|
ctl->OK=1;
|
|
|
|
printf("Continue playing ... \n");
|
|
|
|
};
|
|
|
|
if (ctl->message & PLAYER_DOSTOP)
|
|
|
|
{
|
|
|
|
ctl->message&=~PLAYER_DOSTOP;
|
|
|
|
playing=0;
|
|
|
|
};
|
|
|
|
if (ctl->message & PLAYER_HALT)
|
|
|
|
{
|
|
|
|
ctl->message&=~PLAYER_HALT;
|
|
|
|
playing=0;
|
|
|
|
halt=1;
|
|
|
|
};
|
|
|
|
if (ctl->message & PLAYER_SETPOS)
|
|
|
|
{
|
|
|
|
ctl->moving=1;
|
|
|
|
ctl->message&=~PLAYER_SETPOS;
|
|
|
|
midi->sync(1);
|
|
|
|
midi->tmrStop();
|
|
|
|
midi->closeDev();
|
|
|
|
midistat = new midiStat();
|
|
|
|
SetPos(ctl->gotomsec,midistat);
|
|
|
|
minTime=ctl->gotomsec;
|
|
|
|
prevms=(ulong)minTime;
|
|
|
|
midi->openDev();
|
|
|
|
midi->tmrStart();
|
|
|
|
diffTime=ctl->gotomsec;
|
|
|
|
ctl->moving=0;
|
|
|
|
midistat->sendData(midi,ctl->gm);
|
|
|
|
delete midistat;
|
|
|
|
ctl->OK=1;
|
|
|
|
while (ctl->OK==1) ;
|
|
|
|
ctl->moving=0;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
prevms=minTime;
|
|
|
|
// ctl->millisecsPlayed=minTime;
|
|
|
|
trk=0;
|
|
|
|
minTrk=0;
|
|
|
|
maxTime=minTime + 120000L /* milliseconds */;
|
|
|
|
minTime=maxTime;
|
|
|
|
playing=0;
|
|
|
|
while (trk<info->ntracks)
|
|
|
|
{
|
|
|
|
if (tracks[trk]->absMsOfNextEvent()<minTime)
|
|
|
|
{
|
|
|
|
minTrk=trk;
|
|
|
|
minTime=tracks[minTrk]->absMsOfNextEvent();
|
|
|
|
playing=1;
|
|
|
|
}
|
|
|
|
trk++;
|
|
|
|
}
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("minTime %g\n",minTime);
|
|
|
|
#endif
|
|
|
|
// if ((minTime==maxTime)/* || (minTicks> 60000L)*/)
|
|
|
|
if (playing==0)
|
|
|
|
{
|
|
|
|
// playing=0;
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("END of playing\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// mspass=(ulong)(minTime-prevms);
|
|
|
|
trk=0;
|
|
|
|
while (trk<info->ntracks)
|
|
|
|
{
|
|
|
|
tracks[trk]->currentMs(minTime);
|
|
|
|
trk++;
|
|
|
|
}
|
|
|
|
midi->wait(minTime-diffTime);
|
|
|
|
}
|
|
|
|
trk=minTrk;
|
|
|
|
tracks[trk]->readEvent(ev);
|
|
|
|
switch (ev->command)
|
|
|
|
{
|
|
|
|
case (MIDI_NOTEON) :
|
|
|
|
midi->noteOn(ev->chn, ev->note, ev->vel);break;
|
|
|
|
case (MIDI_NOTEOFF):
|
|
|
|
midi->noteOff(ev->chn, ev->note, ev->vel);break;
|
|
|
|
case (MIDI_KEY_PRESSURE) :
|
|
|
|
midi->keyPressure(ev->chn, ev->note,ev->vel);break;
|
|
|
|
case (MIDI_PGM_CHANGE) :
|
|
|
|
if (!ctl->forcepgm[ev->chn])
|
|
|
|
midi->chnPatchChange(ev->chn, (ctl->gm==1)?(ev->patch):(MT32toGM[ev->patch]));break;
|
|
|
|
case (MIDI_CHN_PRESSURE) :
|
|
|
|
midi->chnPressure(ev->chn, ev->vel);break;
|
|
|
|
case (MIDI_PITCH_BEND) :
|
|
|
|
midi->chnPitchBender(ev->chn, ev->d1,ev->d2);break;
|
|
|
|
case (MIDI_CTL_CHANGE) :
|
|
|
|
midi->chnController(ev->chn, ev->ctl,ev->d1);break;
|
|
|
|
case (MIDI_SYSTEM_PREFIX) :
|
|
|
|
if ((ev->command|ev->chn)==META_EVENT)
|
|
|
|
{
|
|
|
|
if ((ev->d1==5)||(ev->d1==1))
|
|
|
|
{
|
|
|
|
ctl->SPEVplayed++;
|
|
|
|
}
|
|
|
|
if (ev->d1==ME_SET_TEMPO)
|
|
|
|
{
|
|
|
|
absTimeAtChangeTempo=absTime;
|
|
|
|
ticksplayed=0;
|
|
|
|
ctl->SPEVplayed++;
|
|
|
|
tempo=(ulong)(((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2]))*ctl->ratioTempo);
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("Tempo : %ld %g (ratio : %g)\n",tempo,tempoToMetronomeTempo(tempo),ctl->ratioTempo);
|
|
|
|
#endif
|
|
|
|
midi->tmrSetTempo((int)tempoToMetronomeTempo(tempo));
|
|
|
|
ctl->tempo=tempo;
|
|
|
|
for (j=0;j<info->ntracks;j++)
|
|
|
|
{
|
|
|
|
tracks[j]->changeTempo(tempo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ev->d1==ME_TIME_SIGNATURE)
|
|
|
|
{
|
|
|
|
ctl->num=ev->d2;
|
|
|
|
ctl->den=ev->d3;
|
|
|
|
ctl->SPEVplayed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (calloutput)
|
|
|
|
{
|
|
|
|
midi->sync();
|
|
|
|
output();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
ctl->ev=NULL;
|
|
|
|
delete ev;
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("Syncronizing ...\n");
|
|
|
|
#endif
|
|
|
|
if (halt)
|
|
|
|
midi->sync(1);
|
|
|
|
else
|
|
|
|
midi->sync();
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("Closing device ...\n");
|
|
|
|
#endif
|
|
|
|
midi->allNotesOff();
|
|
|
|
midi->closeDev();
|
|
|
|
ctl->playing=0;
|
|
|
|
#ifdef PLAYERDEBUG
|
|
|
|
printf("Bye...\n");
|
|
|
|
#endif
|
|
|
|
ctl->OK=1;
|
|
|
|
ctl->finished=1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MidiPlayer::setPos(ulong gotomsec,MiditqStatus *midistat)
|
|
|
|
{
|
|
|
|
int trk,minTrk;
|
|
|
|
ulong tempo=(ulong)(500000 * ctl->ratioTempo);
|
|
|
|
double minTime=0,maxTime,prevms=0;
|
|
|
|
int i,j,likeplaying=1;
|
|
|
|
|
|
|
|
MidiEvent *ev=new MidiEvent;
|
|
|
|
ctl->SPEVplayed=0;
|
|
|
|
for (i=0;i<info->ntracks;i++)
|
|
|
|
{
|
|
|
|
tracks[i]->init();
|
|
|
|
tracks[i]->changeTempo(tempo);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0;i<16;i++)
|
|
|
|
{
|
|
|
|
if (ctl->forcepgm[i]) midistat->chnPatchChange(i, ctl->pgm[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (likeplaying)
|
|
|
|
{
|
|
|
|
trk=0;
|
|
|
|
minTrk=0;
|
|
|
|
maxTime=minTime + 120000L; /*milliseconds (2 minutes)*/
|
|
|
|
minTime=maxTime;
|
|
|
|
while (trk<info->ntracks)
|
|
|
|
{
|
|
|
|
if (tracks[trk]->absMsOfNextEvent()<minTime)
|
|
|
|
{
|
|
|
|
minTrk=trk;
|
|
|
|
minTime=tracks[minTrk]->absMsOfNextEvent();
|
|
|
|
}
|
|
|
|
trk++;
|
|
|
|
}
|
|
|
|
if (minTime==maxTime)
|
|
|
|
{
|
|
|
|
likeplaying=0;
|
|
|
|
#ifdef GENERAL_DEBUG_MESSAGES
|
|
|
|
printf("END of likeplaying\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (minTime>=gotomsec)
|
|
|
|
{
|
|
|
|
prevms=gotomsec;
|
|
|
|
likeplaying=0;
|
|
|
|
#ifdef GENERAL_DEBUG_MESSAGES
|
|
|
|
printf("Position reached !! \n");
|
|
|
|
#endif
|
|
|
|
minTime=gotomsec;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prevms=minTime;
|
|
|
|
}
|
|
|
|
trk=0;
|
|
|
|
while (trk<info->ntracks)
|
|
|
|
{
|
|
|
|
tracks[trk]->currentMs(minTime);
|
|
|
|
trk++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (likeplaying)
|
|
|
|
{
|
|
|
|
trk=minTrk;
|
|
|
|
tracks[trk]->readEvent(ev);
|
|
|
|
switch (ev->command)
|
|
|
|
{
|
|
|
|
/* case (MIDI_NOTEON) :
|
|
|
|
midistat->noteOn(ev->chn, ev->note, ev->vel);break;
|
|
|
|
case (MIDI_NOTEOFF):
|
|
|
|
midistat->noteOff(ev->chn, ev->note, ev->vel);break;
|
|
|
|
case (MIDI_KEY_PRESSURE) :
|
|
|
|
midistat->keyPressure(ev->chn, ev->note,ev->vel);break;
|
|
|
|
*/
|
|
|
|
case (MIDI_PGM_CHANGE) :
|
|
|
|
if (!ctl->forcepgm[ev->chn]) midistat->chnPatchChange(ev->chn, ev->patch);break;
|
|
|
|
case (MIDI_CHN_PRESSURE) :
|
|
|
|
midistat->chnPressure(ev->chn, ev->vel);break;
|
|
|
|
case (MIDI_PITCH_BEND) :
|
|
|
|
midistat->chnPitchBender(ev->chn, ev->d1,ev->d2);break;
|
|
|
|
case (MIDI_CTL_CHANGE) :
|
|
|
|
midistat->chnController(ev->chn, ev->ctl,ev->d1);break;
|
|
|
|
case (MIDI_SYSTEM_PREFIX) :
|
|
|
|
if ((ev->command|ev->chn)==META_EVENT)
|
|
|
|
{
|
|
|
|
if ((ev->d1==5)||(ev->d1==1))
|
|
|
|
{
|
|
|
|
ctl->SPEVplayed++;
|
|
|
|
}
|
|
|
|
if (ev->d1==ME_SET_TEMPO)
|
|
|
|
{
|
|
|
|
ctl->SPEVplayed++;
|
|
|
|
tempo=(ulong)(((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2]))*ctl->ratioTempo);
|
|
|
|
|
|
|
|
midistat->tmrSetTempo((int)tempoToMetronomeTempo(tempo));
|
|
|
|
for (j=0;j<info->ntracks;j++)
|
|
|
|
{
|
|
|
|
tracks[j]->changeTempo(tempo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ev->d1==ME_TIME_SIGNATURE)
|
|
|
|
{
|
|
|
|
ctl->num=ev->d2;
|
|
|
|
ctl->den=ev->d3;
|
|
|
|
ctl->SPEVplayed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete ev;
|
|
|
|
ctl->tempo=tempo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MidiPlayer::debugSpecialEvents(void)
|
|
|
|
{
|
|
|
|
SpecialEvent *pspev=spev;
|
|
|
|
printf("**************************************\n");
|
|
|
|
while ((pspev!=NULL)&&(pspev->type!=0))
|
|
|
|
{
|
|
|
|
printf("t:%d ticks:%d diff:%ld abs:%ld s:%s tempo:%ld\n",pspev->type,pspev->ticks,pspev->diffmilliseconds,pspev->absmilliseconds,pspev->text,pspev->tempo);
|
|
|
|
pspev=pspev->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void MidiPlayer::setParseSong(bool b)
|
|
|
|
{
|
|
|
|
parsesong=b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MidiPlayer::setGenerateBeats(bool b)
|
|
|
|
{
|
|
|
|
generatebeats=b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MidiPlayer::setTempoRatio(double ratio)
|
|
|
|
{
|
|
|
|
if (songLoaded)
|
|
|
|
{
|
|
|
|
ctl->ratioTempo=ratio;
|
|
|
|
parseInfoData(info,tracks,ctl->ratioTempo);
|
|
|
|
if (parsesong)
|
|
|
|
{
|
|
|
|
parseSpecialEvents();
|
|
|
|
if (generatebeats) generateBeats();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ctl->tempo=(ulong)((ctl->tempo*ctl->ratioTempo)/ratio);
|
|
|
|
ctl->ratioTempo=ratio;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef T2MS
|
|
|
|
#undef MS2T
|