Run dos2unix (#6198)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Lv2Effect.h - implementation of LV2 effect
|
||||
*
|
||||
* Copyright (c) 2018-2020 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Lv2Instrument.h - implementation of LV2 instrument
|
||||
*
|
||||
* Copyright (c) 2018-2020 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */
|
||||
|
||||
#include "allegro.h"
|
||||
|
||||
Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq, double *offset_ptr = nullptr);
|
||||
/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */
|
||||
|
||||
#include "allegro.h"
|
||||
|
||||
Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq, double *offset_ptr = nullptr);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,2 @@
|
||||
// allegroserial.cpp -- convert track to memory buffer and back to structure
|
||||
|
||||
// allegroserial.cpp -- convert track to memory buffer and back to structure
|
||||
|
||||
|
||||
@@ -1,461 +1,461 @@
|
||||
// midifile reader
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "stdio.h"
|
||||
#include "string.h"
|
||||
#include "assert.h"
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include "allegro.h"
|
||||
#include "algsmfrd_internal.h"
|
||||
#include "mfmidi.h"
|
||||
#include "trace.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef class Alg_note_list {
|
||||
public:
|
||||
Alg_note_ptr note;
|
||||
class Alg_note_list *next;
|
||||
Alg_note_list(Alg_note_ptr n, class Alg_note_list *list) {
|
||||
note = n; next = list; }
|
||||
} *Alg_note_list_ptr;
|
||||
|
||||
|
||||
class Alg_midifile_reader: public Midifile_reader {
|
||||
public:
|
||||
istream *file;
|
||||
Alg_seq_ptr seq;
|
||||
int divisions;
|
||||
Alg_note_list_ptr note_list;
|
||||
Alg_track_ptr track;
|
||||
int track_number; // the number of the (current) track
|
||||
// chan is actual_channel + channel_offset_per_track * track_num +
|
||||
// channel_offset_per_track * port
|
||||
long channel_offset_per_track; // used to encode track number into channel
|
||||
// default is 0, set this to 0 to merge all tracks to 16 channels
|
||||
long channel_offset_per_port; // used to encode port number into channel
|
||||
// default is 16, set to 0 to ignore port prefix meta events
|
||||
// while reading, this is channel_offset_per_track * track_num
|
||||
int channel_offset;
|
||||
|
||||
Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) {
|
||||
file = &f;
|
||||
note_list = nullptr;
|
||||
seq = new_seq;
|
||||
channel_offset_per_track = 0;
|
||||
channel_offset_per_port = 16;
|
||||
track_number = -1; // no tracks started yet, 1st will be #0
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
// delete destroys the seq member as well, so set it to NULL if you
|
||||
// copied the pointer elsewhere
|
||||
~Alg_midifile_reader();
|
||||
// the following is used to load the Alg_seq from the file:
|
||||
bool parse();
|
||||
|
||||
void set_nomerge(bool flag) { Mf_nomerge = flag; }
|
||||
void set_skipinit(bool flag) { Mf_skipinit = flag; }
|
||||
long get_currtime() { return Mf_currtime; }
|
||||
|
||||
protected:
|
||||
int meta_channel; // the channel for meta events, set by MIDI chan prefix
|
||||
int port; // value from the portprefix meta event
|
||||
|
||||
double get_time();
|
||||
void update(int chan, int key, Alg_parameter_ptr param);
|
||||
void *Mf_malloc(size_t size) { return malloc(size); }
|
||||
void Mf_free(void *obj, size_t size) { free(obj); }
|
||||
/* Methods to be called while processing the MIDI file. */
|
||||
void Mf_starttrack();
|
||||
void Mf_endtrack();
|
||||
int Mf_getc();
|
||||
void Mf_chanprefix(int chan);
|
||||
void Mf_portprefix(int port);
|
||||
void Mf_eot();
|
||||
void Mf_error(char *);
|
||||
void Mf_error(const char *);
|
||||
void Mf_header(int,int,int);
|
||||
void Mf_on(int,int,int);
|
||||
void Mf_off(int,int,int);
|
||||
void Mf_pressure(int,int,int);
|
||||
void Mf_controller(int,int,int);
|
||||
void Mf_pitchbend(int,int,int);
|
||||
void Mf_program(int,int);
|
||||
void Mf_chanpressure(int,int);
|
||||
void binary_msg(int len, unsigned char *msg, const char *attr_string);
|
||||
void Mf_sysex(int,unsigned char*);
|
||||
void Mf_arbitrary(int,unsigned char*);
|
||||
void Mf_metamisc(int,int,unsigned char*);
|
||||
void Mf_seqnum(int);
|
||||
void Mf_smpte(int,int,int,int,int);
|
||||
void Mf_timesig(int,int,int,int);
|
||||
void Mf_tempo(int);
|
||||
void Mf_keysig(int,int);
|
||||
void Mf_sqspecific(int,unsigned char*);
|
||||
void Mf_text(int,int,unsigned char*);
|
||||
};
|
||||
|
||||
|
||||
Alg_midifile_reader::~Alg_midifile_reader()
|
||||
{
|
||||
while (note_list) {
|
||||
Alg_note_list_ptr to_be_freed = note_list;
|
||||
note_list = note_list->next;
|
||||
delete to_be_freed;
|
||||
}
|
||||
finalize(); // free Mf reader memory
|
||||
}
|
||||
|
||||
|
||||
bool Alg_midifile_reader::parse()
|
||||
{
|
||||
channel_offset = 0;
|
||||
seq->convert_to_beats();
|
||||
midifile();
|
||||
seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur()));
|
||||
return midifile_error != 0;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_starttrack()
|
||||
{
|
||||
// printf("starting new track\n");
|
||||
// create a new track that will share the sequence time map
|
||||
// since time is in beats, the seconds parameter is false
|
||||
track_number++;
|
||||
seq->add_track(track_number); // make sure track exists
|
||||
track = seq->track(track_number); // keep pointer to current track
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_endtrack()
|
||||
{
|
||||
// note: track is already part of seq, so do not add it here
|
||||
// printf("finished track, length %d number %d\n", track->len, track_num / 100);
|
||||
channel_offset += seq->channel_offset_per_track;
|
||||
track = nullptr;
|
||||
double now = get_time();
|
||||
if (seq->get_beat_dur() < now) seq->set_beat_dur(now);
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
||||
int Alg_midifile_reader::Mf_getc()
|
||||
{
|
||||
return file->get();
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_chanprefix(int chan)
|
||||
{
|
||||
meta_channel = chan;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_portprefix(int p)
|
||||
{
|
||||
port = p;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_eot()
|
||||
{
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_error(char *msg)
|
||||
{
|
||||
fprintf(stdout, "Midifile reader error: %s\n", msg);
|
||||
}
|
||||
|
||||
void Alg_midifile_reader::Mf_error(const char *msg)
|
||||
{
|
||||
Mf_error(const_cast<char*>(msg));
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_header(int format, int ntrks, int division)
|
||||
{
|
||||
if (format > 1) {
|
||||
char msg[80];
|
||||
//#pragma warning(disable: 4996) // msg is long enough
|
||||
sprintf(msg, "file format %d not implemented", format);
|
||||
//#pragma warning(default: 4996)
|
||||
Mf_error(msg);
|
||||
}
|
||||
divisions = division;
|
||||
}
|
||||
|
||||
|
||||
double Alg_midifile_reader::get_time()
|
||||
{
|
||||
double beat = ((double) get_currtime()) / divisions;
|
||||
return beat;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_on(int chan, int key, int vel)
|
||||
{
|
||||
assert(!seq->get_units_are_seconds());
|
||||
if (vel == 0) {
|
||||
Mf_off(chan, key, vel);
|
||||
return;
|
||||
}
|
||||
Alg_note_ptr note = new Alg_note();
|
||||
note_list = new Alg_note_list(note, note_list);
|
||||
/* trace("on: %d at %g\n", key, get_time()); */
|
||||
note->time = get_time();
|
||||
note->chan = chan + channel_offset + port * channel_offset_per_port;
|
||||
note->dur = 0;
|
||||
note->set_identifier(key);
|
||||
note->pitch = (float) key;
|
||||
note->loud = (float) vel;
|
||||
track->append(note);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_off(int chan, int key, int vel)
|
||||
{
|
||||
double time = get_time();
|
||||
Alg_note_list_ptr *p = ¬e_list;
|
||||
while (*p) {
|
||||
if ((*p)->note->get_identifier() == key &&
|
||||
(*p)->note->chan ==
|
||||
chan + channel_offset + port * channel_offset_per_port) {
|
||||
(*p)->note->dur = time - (*p)->note->time;
|
||||
// trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur);
|
||||
Alg_note_list_ptr to_be_freed = *p;
|
||||
*p = to_be_freed->next;
|
||||
delete to_be_freed;
|
||||
} else {
|
||||
p = &((*p)->next);
|
||||
}
|
||||
}
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param)
|
||||
{
|
||||
Alg_update_ptr update = new Alg_update;
|
||||
update->time = get_time();
|
||||
update->chan = chan;
|
||||
if (chan != -1) {
|
||||
update->chan = chan + channel_offset + port * channel_offset_per_port;
|
||||
}
|
||||
update->set_identifier(key);
|
||||
update->parameter = *param;
|
||||
// prevent the destructor from destroying the string twice!
|
||||
// the new Update takes the string from param
|
||||
if (param->attr_type() == 's') param->s = nullptr;
|
||||
track->append(update);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_pressure(int chan, int key, int val)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("pressurer"));
|
||||
parameter.r = val / 127.0;
|
||||
update(chan, key, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_controller(int chan, int control, int val)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
char name[32];
|
||||
//#pragma warning(disable: 4996) // name is long enough
|
||||
sprintf(name, "control%dr", control);
|
||||
//#pragma warning(default: 4996)
|
||||
parameter.set_attr(symbol_table.insert_string(name));
|
||||
parameter.r = val / 127.0;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("bendr"));
|
||||
parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_program(int chan, int program)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("programi"));
|
||||
parameter.i = program;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_chanpressure(int chan, int val)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("pressurer"));
|
||||
parameter.r = val / 127.0;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::binary_msg(int len, unsigned char *msg,
|
||||
const char *attr_string)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
char *hexstr = new char[len * 2 + 1];
|
||||
for (int i = 0; i < len; i++) {
|
||||
//#pragma warning(disable: 4996) // hexstr is long enough
|
||||
sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i]));
|
||||
//#pragma warning(default: 4996)
|
||||
}
|
||||
parameter.s = hexstr;
|
||||
parameter.set_attr(symbol_table.insert_string(attr_string));
|
||||
update(meta_channel, -1, ¶meter);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_sysex(int len, unsigned char *msg)
|
||||
{
|
||||
// sysex messages become updates with attribute sysexs and a hex string
|
||||
binary_msg(len, msg, "sysexs");
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_arbitrary(int len, unsigned char *msg)
|
||||
{
|
||||
Mf_error("arbitrary data ignored");
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_metamisc(int type, int len, unsigned char *msg)
|
||||
{
|
||||
char text[128];
|
||||
//#pragma warning(disable: 4996) // text is long enough
|
||||
sprintf(text, "metamsic data, type 0x%x, ignored", type);
|
||||
//#pragma warning(default: 4996)
|
||||
Mf_error(text);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_seqnum(int n)
|
||||
{
|
||||
Mf_error("seqnum data ignored");
|
||||
}
|
||||
|
||||
|
||||
static const char *fpsstr[4] = {"24", "25", "29.97", "30"};
|
||||
|
||||
void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs,
|
||||
int frames, int subframes)
|
||||
{
|
||||
// string will look like "24fps:01h:27m:07s:19.00f"
|
||||
// 30fps (drop frame) is notated as "29.97fps"
|
||||
char text[32];
|
||||
int fps = (hours >> 6) & 3;
|
||||
hours &= 0x1F;
|
||||
//#pragma warning(disable: 4996) // text is long enough
|
||||
sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df",
|
||||
fpsstr[fps], hours, mins, secs, frames, subframes);
|
||||
//#pragma warning(default: 4996)
|
||||
Alg_parameter smpteoffset;
|
||||
smpteoffset.s = heapify(text);
|
||||
smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets"));
|
||||
update(meta_channel, -1, &smpteoffset);
|
||||
// Mf_error("SMPTE data ignored");
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4)
|
||||
{
|
||||
seq->set_time_sig(double(get_currtime()) / divisions, i1, 1 << i2);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_tempo(int tempo)
|
||||
{
|
||||
double beat = get_currtime();
|
||||
beat = beat / divisions; // convert to quarters
|
||||
// 6000000 us/min / n us/beat => beat / min
|
||||
double bpm = 60000000.0 / tempo;
|
||||
seq->insert_tempo(bpm, beat);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_keysig(int key, int mode)
|
||||
{
|
||||
Alg_parameter key_parm;
|
||||
key_parm.set_attr(symbol_table.insert_string("keysigi"));
|
||||
// use 0 for C major, 1 for G, -1 for F, etc., that is,
|
||||
// the number of sharps, where flats are negative sharps
|
||||
key_parm.i = key; //<<<---- fix this
|
||||
// use -1 to mean "all channels"
|
||||
update(meta_channel, -1, &key_parm);
|
||||
Alg_parameter mode_parm;
|
||||
mode_parm.set_attr(symbol_table.insert_string("modea"));
|
||||
mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") :
|
||||
symbol_table.insert_string("minor"));
|
||||
update(meta_channel, -1, &mode_parm);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_sqspecific(int len, unsigned char *msg)
|
||||
{
|
||||
// sequencer specific messages become updates with attribute sqspecifics
|
||||
// and a hex string for the value
|
||||
binary_msg(len, msg, "sqspecifics");
|
||||
}
|
||||
|
||||
|
||||
char *heapify2(int len, unsigned char *s)
|
||||
{
|
||||
char *h = new char[len + 1];
|
||||
memcpy(h, s, len);
|
||||
h[len] = 0;
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_text(int type, int len, unsigned char *msg)
|
||||
{
|
||||
Alg_parameter text;
|
||||
text.s = heapify2(len, msg);
|
||||
const char *attr = "miscs";
|
||||
if (type == 1) attr = "texts";
|
||||
else if (type == 2) attr = "copyrights";
|
||||
else if (type == 3)
|
||||
attr = (track_number == 0 ? "seqnames" : "tracknames");
|
||||
else if (type == 4) attr = "instruments";
|
||||
else if (type == 5) attr = "lyrics";
|
||||
else if (type == 6) attr = "markers";
|
||||
else if (type == 7) attr = "cues";
|
||||
text.set_attr(symbol_table.insert_string(attr));
|
||||
update(meta_channel, -1, &text);
|
||||
}
|
||||
|
||||
|
||||
// parse file into a seq.
|
||||
Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq)
|
||||
{
|
||||
assert(new_seq);
|
||||
Alg_midifile_reader ar(file, new_seq);
|
||||
bool err = ar.parse();
|
||||
ar.seq->set_real_dur(ar.seq->get_time_map()->
|
||||
beat_to_time(ar.seq->get_beat_dur()));
|
||||
return (err ? alg_error_syntax : alg_no_error);
|
||||
}
|
||||
// midifile reader
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "stdio.h"
|
||||
#include "string.h"
|
||||
#include "assert.h"
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include "allegro.h"
|
||||
#include "algsmfrd_internal.h"
|
||||
#include "mfmidi.h"
|
||||
#include "trace.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef class Alg_note_list {
|
||||
public:
|
||||
Alg_note_ptr note;
|
||||
class Alg_note_list *next;
|
||||
Alg_note_list(Alg_note_ptr n, class Alg_note_list *list) {
|
||||
note = n; next = list; }
|
||||
} *Alg_note_list_ptr;
|
||||
|
||||
|
||||
class Alg_midifile_reader: public Midifile_reader {
|
||||
public:
|
||||
istream *file;
|
||||
Alg_seq_ptr seq;
|
||||
int divisions;
|
||||
Alg_note_list_ptr note_list;
|
||||
Alg_track_ptr track;
|
||||
int track_number; // the number of the (current) track
|
||||
// chan is actual_channel + channel_offset_per_track * track_num +
|
||||
// channel_offset_per_track * port
|
||||
long channel_offset_per_track; // used to encode track number into channel
|
||||
// default is 0, set this to 0 to merge all tracks to 16 channels
|
||||
long channel_offset_per_port; // used to encode port number into channel
|
||||
// default is 16, set to 0 to ignore port prefix meta events
|
||||
// while reading, this is channel_offset_per_track * track_num
|
||||
int channel_offset;
|
||||
|
||||
Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) {
|
||||
file = &f;
|
||||
note_list = nullptr;
|
||||
seq = new_seq;
|
||||
channel_offset_per_track = 0;
|
||||
channel_offset_per_port = 16;
|
||||
track_number = -1; // no tracks started yet, 1st will be #0
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
// delete destroys the seq member as well, so set it to NULL if you
|
||||
// copied the pointer elsewhere
|
||||
~Alg_midifile_reader();
|
||||
// the following is used to load the Alg_seq from the file:
|
||||
bool parse();
|
||||
|
||||
void set_nomerge(bool flag) { Mf_nomerge = flag; }
|
||||
void set_skipinit(bool flag) { Mf_skipinit = flag; }
|
||||
long get_currtime() { return Mf_currtime; }
|
||||
|
||||
protected:
|
||||
int meta_channel; // the channel for meta events, set by MIDI chan prefix
|
||||
int port; // value from the portprefix meta event
|
||||
|
||||
double get_time();
|
||||
void update(int chan, int key, Alg_parameter_ptr param);
|
||||
void *Mf_malloc(size_t size) { return malloc(size); }
|
||||
void Mf_free(void *obj, size_t size) { free(obj); }
|
||||
/* Methods to be called while processing the MIDI file. */
|
||||
void Mf_starttrack();
|
||||
void Mf_endtrack();
|
||||
int Mf_getc();
|
||||
void Mf_chanprefix(int chan);
|
||||
void Mf_portprefix(int port);
|
||||
void Mf_eot();
|
||||
void Mf_error(char *);
|
||||
void Mf_error(const char *);
|
||||
void Mf_header(int,int,int);
|
||||
void Mf_on(int,int,int);
|
||||
void Mf_off(int,int,int);
|
||||
void Mf_pressure(int,int,int);
|
||||
void Mf_controller(int,int,int);
|
||||
void Mf_pitchbend(int,int,int);
|
||||
void Mf_program(int,int);
|
||||
void Mf_chanpressure(int,int);
|
||||
void binary_msg(int len, unsigned char *msg, const char *attr_string);
|
||||
void Mf_sysex(int,unsigned char*);
|
||||
void Mf_arbitrary(int,unsigned char*);
|
||||
void Mf_metamisc(int,int,unsigned char*);
|
||||
void Mf_seqnum(int);
|
||||
void Mf_smpte(int,int,int,int,int);
|
||||
void Mf_timesig(int,int,int,int);
|
||||
void Mf_tempo(int);
|
||||
void Mf_keysig(int,int);
|
||||
void Mf_sqspecific(int,unsigned char*);
|
||||
void Mf_text(int,int,unsigned char*);
|
||||
};
|
||||
|
||||
|
||||
Alg_midifile_reader::~Alg_midifile_reader()
|
||||
{
|
||||
while (note_list) {
|
||||
Alg_note_list_ptr to_be_freed = note_list;
|
||||
note_list = note_list->next;
|
||||
delete to_be_freed;
|
||||
}
|
||||
finalize(); // free Mf reader memory
|
||||
}
|
||||
|
||||
|
||||
bool Alg_midifile_reader::parse()
|
||||
{
|
||||
channel_offset = 0;
|
||||
seq->convert_to_beats();
|
||||
midifile();
|
||||
seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur()));
|
||||
return midifile_error != 0;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_starttrack()
|
||||
{
|
||||
// printf("starting new track\n");
|
||||
// create a new track that will share the sequence time map
|
||||
// since time is in beats, the seconds parameter is false
|
||||
track_number++;
|
||||
seq->add_track(track_number); // make sure track exists
|
||||
track = seq->track(track_number); // keep pointer to current track
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_endtrack()
|
||||
{
|
||||
// note: track is already part of seq, so do not add it here
|
||||
// printf("finished track, length %d number %d\n", track->len, track_num / 100);
|
||||
channel_offset += seq->channel_offset_per_track;
|
||||
track = nullptr;
|
||||
double now = get_time();
|
||||
if (seq->get_beat_dur() < now) seq->set_beat_dur(now);
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
||||
int Alg_midifile_reader::Mf_getc()
|
||||
{
|
||||
return file->get();
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_chanprefix(int chan)
|
||||
{
|
||||
meta_channel = chan;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_portprefix(int p)
|
||||
{
|
||||
port = p;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_eot()
|
||||
{
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_error(char *msg)
|
||||
{
|
||||
fprintf(stdout, "Midifile reader error: %s\n", msg);
|
||||
}
|
||||
|
||||
void Alg_midifile_reader::Mf_error(const char *msg)
|
||||
{
|
||||
Mf_error(const_cast<char*>(msg));
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_header(int format, int ntrks, int division)
|
||||
{
|
||||
if (format > 1) {
|
||||
char msg[80];
|
||||
//#pragma warning(disable: 4996) // msg is long enough
|
||||
sprintf(msg, "file format %d not implemented", format);
|
||||
//#pragma warning(default: 4996)
|
||||
Mf_error(msg);
|
||||
}
|
||||
divisions = division;
|
||||
}
|
||||
|
||||
|
||||
double Alg_midifile_reader::get_time()
|
||||
{
|
||||
double beat = ((double) get_currtime()) / divisions;
|
||||
return beat;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_on(int chan, int key, int vel)
|
||||
{
|
||||
assert(!seq->get_units_are_seconds());
|
||||
if (vel == 0) {
|
||||
Mf_off(chan, key, vel);
|
||||
return;
|
||||
}
|
||||
Alg_note_ptr note = new Alg_note();
|
||||
note_list = new Alg_note_list(note, note_list);
|
||||
/* trace("on: %d at %g\n", key, get_time()); */
|
||||
note->time = get_time();
|
||||
note->chan = chan + channel_offset + port * channel_offset_per_port;
|
||||
note->dur = 0;
|
||||
note->set_identifier(key);
|
||||
note->pitch = (float) key;
|
||||
note->loud = (float) vel;
|
||||
track->append(note);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_off(int chan, int key, int vel)
|
||||
{
|
||||
double time = get_time();
|
||||
Alg_note_list_ptr *p = ¬e_list;
|
||||
while (*p) {
|
||||
if ((*p)->note->get_identifier() == key &&
|
||||
(*p)->note->chan ==
|
||||
chan + channel_offset + port * channel_offset_per_port) {
|
||||
(*p)->note->dur = time - (*p)->note->time;
|
||||
// trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur);
|
||||
Alg_note_list_ptr to_be_freed = *p;
|
||||
*p = to_be_freed->next;
|
||||
delete to_be_freed;
|
||||
} else {
|
||||
p = &((*p)->next);
|
||||
}
|
||||
}
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param)
|
||||
{
|
||||
Alg_update_ptr update = new Alg_update;
|
||||
update->time = get_time();
|
||||
update->chan = chan;
|
||||
if (chan != -1) {
|
||||
update->chan = chan + channel_offset + port * channel_offset_per_port;
|
||||
}
|
||||
update->set_identifier(key);
|
||||
update->parameter = *param;
|
||||
// prevent the destructor from destroying the string twice!
|
||||
// the new Update takes the string from param
|
||||
if (param->attr_type() == 's') param->s = nullptr;
|
||||
track->append(update);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_pressure(int chan, int key, int val)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("pressurer"));
|
||||
parameter.r = val / 127.0;
|
||||
update(chan, key, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_controller(int chan, int control, int val)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
char name[32];
|
||||
//#pragma warning(disable: 4996) // name is long enough
|
||||
sprintf(name, "control%dr", control);
|
||||
//#pragma warning(default: 4996)
|
||||
parameter.set_attr(symbol_table.insert_string(name));
|
||||
parameter.r = val / 127.0;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("bendr"));
|
||||
parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_program(int chan, int program)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("programi"));
|
||||
parameter.i = program;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_chanpressure(int chan, int val)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("pressurer"));
|
||||
parameter.r = val / 127.0;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::binary_msg(int len, unsigned char *msg,
|
||||
const char *attr_string)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
char *hexstr = new char[len * 2 + 1];
|
||||
for (int i = 0; i < len; i++) {
|
||||
//#pragma warning(disable: 4996) // hexstr is long enough
|
||||
sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i]));
|
||||
//#pragma warning(default: 4996)
|
||||
}
|
||||
parameter.s = hexstr;
|
||||
parameter.set_attr(symbol_table.insert_string(attr_string));
|
||||
update(meta_channel, -1, ¶meter);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_sysex(int len, unsigned char *msg)
|
||||
{
|
||||
// sysex messages become updates with attribute sysexs and a hex string
|
||||
binary_msg(len, msg, "sysexs");
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_arbitrary(int len, unsigned char *msg)
|
||||
{
|
||||
Mf_error("arbitrary data ignored");
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_metamisc(int type, int len, unsigned char *msg)
|
||||
{
|
||||
char text[128];
|
||||
//#pragma warning(disable: 4996) // text is long enough
|
||||
sprintf(text, "metamsic data, type 0x%x, ignored", type);
|
||||
//#pragma warning(default: 4996)
|
||||
Mf_error(text);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_seqnum(int n)
|
||||
{
|
||||
Mf_error("seqnum data ignored");
|
||||
}
|
||||
|
||||
|
||||
static const char *fpsstr[4] = {"24", "25", "29.97", "30"};
|
||||
|
||||
void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs,
|
||||
int frames, int subframes)
|
||||
{
|
||||
// string will look like "24fps:01h:27m:07s:19.00f"
|
||||
// 30fps (drop frame) is notated as "29.97fps"
|
||||
char text[32];
|
||||
int fps = (hours >> 6) & 3;
|
||||
hours &= 0x1F;
|
||||
//#pragma warning(disable: 4996) // text is long enough
|
||||
sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df",
|
||||
fpsstr[fps], hours, mins, secs, frames, subframes);
|
||||
//#pragma warning(default: 4996)
|
||||
Alg_parameter smpteoffset;
|
||||
smpteoffset.s = heapify(text);
|
||||
smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets"));
|
||||
update(meta_channel, -1, &smpteoffset);
|
||||
// Mf_error("SMPTE data ignored");
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4)
|
||||
{
|
||||
seq->set_time_sig(double(get_currtime()) / divisions, i1, 1 << i2);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_tempo(int tempo)
|
||||
{
|
||||
double beat = get_currtime();
|
||||
beat = beat / divisions; // convert to quarters
|
||||
// 6000000 us/min / n us/beat => beat / min
|
||||
double bpm = 60000000.0 / tempo;
|
||||
seq->insert_tempo(bpm, beat);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_keysig(int key, int mode)
|
||||
{
|
||||
Alg_parameter key_parm;
|
||||
key_parm.set_attr(symbol_table.insert_string("keysigi"));
|
||||
// use 0 for C major, 1 for G, -1 for F, etc., that is,
|
||||
// the number of sharps, where flats are negative sharps
|
||||
key_parm.i = key; //<<<---- fix this
|
||||
// use -1 to mean "all channels"
|
||||
update(meta_channel, -1, &key_parm);
|
||||
Alg_parameter mode_parm;
|
||||
mode_parm.set_attr(symbol_table.insert_string("modea"));
|
||||
mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") :
|
||||
symbol_table.insert_string("minor"));
|
||||
update(meta_channel, -1, &mode_parm);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_sqspecific(int len, unsigned char *msg)
|
||||
{
|
||||
// sequencer specific messages become updates with attribute sqspecifics
|
||||
// and a hex string for the value
|
||||
binary_msg(len, msg, "sqspecifics");
|
||||
}
|
||||
|
||||
|
||||
char *heapify2(int len, unsigned char *s)
|
||||
{
|
||||
char *h = new char[len + 1];
|
||||
memcpy(h, s, len);
|
||||
h[len] = 0;
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_text(int type, int len, unsigned char *msg)
|
||||
{
|
||||
Alg_parameter text;
|
||||
text.s = heapify2(len, msg);
|
||||
const char *attr = "miscs";
|
||||
if (type == 1) attr = "texts";
|
||||
else if (type == 2) attr = "copyrights";
|
||||
else if (type == 3)
|
||||
attr = (track_number == 0 ? "seqnames" : "tracknames");
|
||||
else if (type == 4) attr = "instruments";
|
||||
else if (type == 5) attr = "lyrics";
|
||||
else if (type == 6) attr = "markers";
|
||||
else if (type == 7) attr = "cues";
|
||||
text.set_attr(symbol_table.insert_string(attr));
|
||||
update(meta_channel, -1, &text);
|
||||
}
|
||||
|
||||
|
||||
// parse file into a seq.
|
||||
Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq)
|
||||
{
|
||||
assert(new_seq);
|
||||
Alg_midifile_reader ar(file, new_seq);
|
||||
bool err = ar.parse();
|
||||
ar.seq->set_real_dur(ar.seq->get_time_map()->
|
||||
beat_to_time(ar.seq->get_beat_dur()));
|
||||
return (err ? alg_error_syntax : alg_no_error);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,183 +1,183 @@
|
||||
// allegrowr.cpp -- write sequence to an Allegro file (text)
|
||||
|
||||
#include "assert.h"
|
||||
#include "stdlib.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
#include "memory.h"
|
||||
using namespace std;
|
||||
#include "strparse.h"
|
||||
#include "allegro.h"
|
||||
|
||||
// Note about precision: %g prints 6 significant digits. For 1ms precision,
|
||||
// the maximum magnitude is 999.999, i.e. 1000s < 17minutes. For anything
|
||||
// over 1000s, time in seconds will be printed with 10ms precision, which
|
||||
// is not good. Therefore, times and durations are printed as %.4d, which
|
||||
// gives 100us precision.
|
||||
// The following define allows you to change this decision:
|
||||
/* #define TIMFMT "%.4d" */
|
||||
#define TIMPREC 4
|
||||
#define TIMFMT fixed << setprecision(TIMPREC)
|
||||
#define GFMT resetiosflags(ios::floatfield) << setprecision(6)
|
||||
|
||||
void parameter_print(ostream &file, Alg_parameter_ptr p)
|
||||
{
|
||||
file << " -" << p->attr_name() << ":";
|
||||
switch (p->attr_type()) {
|
||||
case 'a':
|
||||
file << "'" << alg_attr_name(p->a) << "'";
|
||||
break;
|
||||
case 'i':
|
||||
file << p->i;
|
||||
break;
|
||||
case 'l':
|
||||
file << (p->l ? "true" : "false");
|
||||
break;
|
||||
case 'r':
|
||||
file << p->r;
|
||||
break;
|
||||
case 's': {
|
||||
string str;
|
||||
string_escape(str, p->s, "\"");
|
||||
file << str;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n,
|
||||
Alg_events &events)
|
||||
// write #track <n> <trackname-or-sequencename>
|
||||
// if we write the name on the "#track" line, then we do *not* want
|
||||
// to write again as an update: "-seqnames:"Jordu", so if we do
|
||||
// find a name and write it, return a pointer to it so the track
|
||||
// writer knows what update (if any) to skip
|
||||
{
|
||||
Alg_event_ptr e = nullptr; // e is the result, default is NULL
|
||||
file << "#track " << n;
|
||||
const char *attr = symbol_table.insert_string(
|
||||
n == 0 ? "seqnames" : "tracknames");
|
||||
// search for name in events with timestamp of 0
|
||||
for (int i = 0; i < events.length(); i++) {
|
||||
Alg_event_ptr ue = events[i];
|
||||
if (ue->time > 0) break;
|
||||
if (ue->is_update()) {
|
||||
Alg_update_ptr u = (Alg_update_ptr) ue;
|
||||
if (u->parameter.attr == attr) {
|
||||
file << " " << u->parameter.s;
|
||||
e = ue; // return the update event we found
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
file << endl; // end of line containing #track [<name>]
|
||||
return e; // return parameter event with name if one was found
|
||||
}
|
||||
|
||||
|
||||
void Alg_seq::write(ostream &file, bool in_secs, double offset)
|
||||
{
|
||||
int i, j;
|
||||
if (in_secs) convert_to_seconds();
|
||||
else convert_to_beats();
|
||||
file << "#offset " << offset << endl;
|
||||
Alg_event_ptr update_to_skip = write_track_name(file, 0, track_list[0]);
|
||||
Alg_beats &beats = time_map->beats;
|
||||
for (i = 0; i < beats.len - 1; i++) {
|
||||
Alg_beat_ptr b = &(beats[i]);
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << b->time;
|
||||
} else {
|
||||
file << "TW" << TIMFMT << b->beat / 4;
|
||||
}
|
||||
double tempo = (beats[i + 1].beat - b->beat) /
|
||||
(beats[i + 1].time - beats[i].time);
|
||||
file << " -tempor:" << GFMT << tempo * 60 << "\n";
|
||||
}
|
||||
if (time_map->last_tempo_flag) { // we have final tempo:
|
||||
Alg_beat_ptr b = &(beats[beats.len - 1]);
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << b->time;
|
||||
} else {
|
||||
file << "TW" << TIMFMT << b->beat / 4;
|
||||
}
|
||||
file << " -tempor:" << GFMT << time_map->last_tempo * 60.0 << "\n";
|
||||
}
|
||||
|
||||
// write the time signatures
|
||||
for (i = 0; i < time_sig.length(); i++) {
|
||||
Alg_time_sig &ts = time_sig[i];
|
||||
double time = ts.beat;
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << time << " V- -timesig_numr:" <<
|
||||
GFMT << ts.num << "\n";
|
||||
file << "T" << TIMFMT << time << " V- -timesig_denr:" <<
|
||||
GFMT << ts.den << "\n";
|
||||
} else {
|
||||
double wholes = ts.beat / 4;
|
||||
file << "TW" << TIMFMT << wholes << " V- -timesig_numr:" <<
|
||||
GFMT << ts.num << "\n";
|
||||
file << "TW" << TIMFMT << wholes << " V- -timesig_denr:" <<
|
||||
GFMT << ts.den << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < track_list.length(); j++) {
|
||||
Alg_events ¬es = track_list[j];
|
||||
if (j != 0) update_to_skip = write_track_name(file, j, notes);
|
||||
// now write the notes at beat positions
|
||||
for (i = 0; i < notes.length(); i++) {
|
||||
Alg_event_ptr e = notes[i];
|
||||
// if we already wrote this event as a track or sequence name,
|
||||
// do not write it again
|
||||
if (e == update_to_skip) continue;
|
||||
double start = e->time;
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << start;
|
||||
} else {
|
||||
file << "TW" << TIMFMT << start / 4;
|
||||
}
|
||||
// write the channel as Vn or V-
|
||||
if (e->chan == -1) file << " V-";
|
||||
else file << " V" << e->chan;
|
||||
// write the note or update data
|
||||
if (e->is_note()) {
|
||||
Alg_note_ptr n = (Alg_note_ptr) e;
|
||||
double dur = n->dur;
|
||||
file << " K" << n->get_identifier() <<
|
||||
" P" << GFMT << n->pitch;
|
||||
if (in_secs) {
|
||||
file << " U" << TIMFMT << dur;
|
||||
} else {
|
||||
file << " Q" << TIMFMT << dur;
|
||||
}
|
||||
file << " L" << GFMT << n->loud;
|
||||
Alg_parameters_ptr p = n->parameters;
|
||||
while (p) {
|
||||
parameter_print(file, &(p->parm));
|
||||
p = p->next;
|
||||
}
|
||||
} else { // an update
|
||||
assert(e->is_update());
|
||||
Alg_update_ptr u = (Alg_update_ptr) e;
|
||||
if (u->get_identifier() != -1) {
|
||||
file << " K" << u->get_identifier();
|
||||
}
|
||||
parameter_print(file, &(u->parameter));
|
||||
}
|
||||
file << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Alg_seq::write(const char *filename, double offset)
|
||||
{
|
||||
ofstream file(filename);
|
||||
if (file.fail()) return false;
|
||||
write(file, units_are_seconds, offset);
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
// allegrowr.cpp -- write sequence to an Allegro file (text)
|
||||
|
||||
#include "assert.h"
|
||||
#include "stdlib.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
#include "memory.h"
|
||||
using namespace std;
|
||||
#include "strparse.h"
|
||||
#include "allegro.h"
|
||||
|
||||
// Note about precision: %g prints 6 significant digits. For 1ms precision,
|
||||
// the maximum magnitude is 999.999, i.e. 1000s < 17minutes. For anything
|
||||
// over 1000s, time in seconds will be printed with 10ms precision, which
|
||||
// is not good. Therefore, times and durations are printed as %.4d, which
|
||||
// gives 100us precision.
|
||||
// The following define allows you to change this decision:
|
||||
/* #define TIMFMT "%.4d" */
|
||||
#define TIMPREC 4
|
||||
#define TIMFMT fixed << setprecision(TIMPREC)
|
||||
#define GFMT resetiosflags(ios::floatfield) << setprecision(6)
|
||||
|
||||
void parameter_print(ostream &file, Alg_parameter_ptr p)
|
||||
{
|
||||
file << " -" << p->attr_name() << ":";
|
||||
switch (p->attr_type()) {
|
||||
case 'a':
|
||||
file << "'" << alg_attr_name(p->a) << "'";
|
||||
break;
|
||||
case 'i':
|
||||
file << p->i;
|
||||
break;
|
||||
case 'l':
|
||||
file << (p->l ? "true" : "false");
|
||||
break;
|
||||
case 'r':
|
||||
file << p->r;
|
||||
break;
|
||||
case 's': {
|
||||
string str;
|
||||
string_escape(str, p->s, "\"");
|
||||
file << str;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n,
|
||||
Alg_events &events)
|
||||
// write #track <n> <trackname-or-sequencename>
|
||||
// if we write the name on the "#track" line, then we do *not* want
|
||||
// to write again as an update: "-seqnames:"Jordu", so if we do
|
||||
// find a name and write it, return a pointer to it so the track
|
||||
// writer knows what update (if any) to skip
|
||||
{
|
||||
Alg_event_ptr e = nullptr; // e is the result, default is NULL
|
||||
file << "#track " << n;
|
||||
const char *attr = symbol_table.insert_string(
|
||||
n == 0 ? "seqnames" : "tracknames");
|
||||
// search for name in events with timestamp of 0
|
||||
for (int i = 0; i < events.length(); i++) {
|
||||
Alg_event_ptr ue = events[i];
|
||||
if (ue->time > 0) break;
|
||||
if (ue->is_update()) {
|
||||
Alg_update_ptr u = (Alg_update_ptr) ue;
|
||||
if (u->parameter.attr == attr) {
|
||||
file << " " << u->parameter.s;
|
||||
e = ue; // return the update event we found
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
file << endl; // end of line containing #track [<name>]
|
||||
return e; // return parameter event with name if one was found
|
||||
}
|
||||
|
||||
|
||||
void Alg_seq::write(ostream &file, bool in_secs, double offset)
|
||||
{
|
||||
int i, j;
|
||||
if (in_secs) convert_to_seconds();
|
||||
else convert_to_beats();
|
||||
file << "#offset " << offset << endl;
|
||||
Alg_event_ptr update_to_skip = write_track_name(file, 0, track_list[0]);
|
||||
Alg_beats &beats = time_map->beats;
|
||||
for (i = 0; i < beats.len - 1; i++) {
|
||||
Alg_beat_ptr b = &(beats[i]);
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << b->time;
|
||||
} else {
|
||||
file << "TW" << TIMFMT << b->beat / 4;
|
||||
}
|
||||
double tempo = (beats[i + 1].beat - b->beat) /
|
||||
(beats[i + 1].time - beats[i].time);
|
||||
file << " -tempor:" << GFMT << tempo * 60 << "\n";
|
||||
}
|
||||
if (time_map->last_tempo_flag) { // we have final tempo:
|
||||
Alg_beat_ptr b = &(beats[beats.len - 1]);
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << b->time;
|
||||
} else {
|
||||
file << "TW" << TIMFMT << b->beat / 4;
|
||||
}
|
||||
file << " -tempor:" << GFMT << time_map->last_tempo * 60.0 << "\n";
|
||||
}
|
||||
|
||||
// write the time signatures
|
||||
for (i = 0; i < time_sig.length(); i++) {
|
||||
Alg_time_sig &ts = time_sig[i];
|
||||
double time = ts.beat;
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << time << " V- -timesig_numr:" <<
|
||||
GFMT << ts.num << "\n";
|
||||
file << "T" << TIMFMT << time << " V- -timesig_denr:" <<
|
||||
GFMT << ts.den << "\n";
|
||||
} else {
|
||||
double wholes = ts.beat / 4;
|
||||
file << "TW" << TIMFMT << wholes << " V- -timesig_numr:" <<
|
||||
GFMT << ts.num << "\n";
|
||||
file << "TW" << TIMFMT << wholes << " V- -timesig_denr:" <<
|
||||
GFMT << ts.den << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < track_list.length(); j++) {
|
||||
Alg_events ¬es = track_list[j];
|
||||
if (j != 0) update_to_skip = write_track_name(file, j, notes);
|
||||
// now write the notes at beat positions
|
||||
for (i = 0; i < notes.length(); i++) {
|
||||
Alg_event_ptr e = notes[i];
|
||||
// if we already wrote this event as a track or sequence name,
|
||||
// do not write it again
|
||||
if (e == update_to_skip) continue;
|
||||
double start = e->time;
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << start;
|
||||
} else {
|
||||
file << "TW" << TIMFMT << start / 4;
|
||||
}
|
||||
// write the channel as Vn or V-
|
||||
if (e->chan == -1) file << " V-";
|
||||
else file << " V" << e->chan;
|
||||
// write the note or update data
|
||||
if (e->is_note()) {
|
||||
Alg_note_ptr n = (Alg_note_ptr) e;
|
||||
double dur = n->dur;
|
||||
file << " K" << n->get_identifier() <<
|
||||
" P" << GFMT << n->pitch;
|
||||
if (in_secs) {
|
||||
file << " U" << TIMFMT << dur;
|
||||
} else {
|
||||
file << " Q" << TIMFMT << dur;
|
||||
}
|
||||
file << " L" << GFMT << n->loud;
|
||||
Alg_parameters_ptr p = n->parameters;
|
||||
while (p) {
|
||||
parameter_print(file, &(p->parm));
|
||||
p = p->next;
|
||||
}
|
||||
} else { // an update
|
||||
assert(e->is_update());
|
||||
Alg_update_ptr u = (Alg_update_ptr) e;
|
||||
if (u->get_identifier() != -1) {
|
||||
file << " K" << u->get_identifier();
|
||||
}
|
||||
parameter_print(file, &(u->parameter));
|
||||
}
|
||||
file << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Alg_seq::write(const char *filename, double offset)
|
||||
{
|
||||
ofstream file(filename);
|
||||
if (file.fail()) return false;
|
||||
write(file, units_are_seconds, offset);
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,491 +1,491 @@
|
||||
/*
|
||||
* Read a Standard MIDI File. Externally-assigned function pointers are
|
||||
* called upon recognizing things in the file. See midifile(3).
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Change Log
|
||||
* Date | who : Change
|
||||
*-----------+-----------------------------------------------------------------
|
||||
* 2-Mar-92 | GWL : created changelog; MIDIFILE_ERROR to satisfy compiler
|
||||
*****************************************************************************/
|
||||
|
||||
#include "stdio.h"
|
||||
#include "mfmidi.h"
|
||||
#include "string.h"
|
||||
#include "assert.h"
|
||||
|
||||
#define MIDIFILE_ERROR -1
|
||||
|
||||
/* public stuff */
|
||||
extern int abort_flag;
|
||||
|
||||
|
||||
void Midifile_reader::midifile()
|
||||
{
|
||||
int ntrks;
|
||||
midifile_error = 0;
|
||||
|
||||
ntrks = readheader();
|
||||
if (midifile_error) return;
|
||||
if (ntrks <= 0) {
|
||||
mferror("No tracks!");
|
||||
/* no need to return since midifile_error is set */
|
||||
}
|
||||
while (ntrks-- > 0 && !midifile_error) readtrack();
|
||||
}
|
||||
|
||||
int Midifile_reader::readmt(const char *s, int skip)
|
||||
/* read through the "MThd" or "MTrk" header string */
|
||||
/* if skip == 1, we attempt to skip initial garbage. */
|
||||
{
|
||||
assert(strlen(s) == 4); // must be "MThd" or "MTrk"
|
||||
int nread = 0;
|
||||
char b[4];
|
||||
char buff[32];
|
||||
int c;
|
||||
const char *errmsg = "expecting ";
|
||||
|
||||
retry:
|
||||
while ( nread<4 ) {
|
||||
c = Mf_getc();
|
||||
if ( c == EOF ) {
|
||||
errmsg = "EOF while expecting ";
|
||||
goto err;
|
||||
}
|
||||
b[nread++] = c;
|
||||
}
|
||||
/* See if we found the 4 characters we're looking for */
|
||||
if ( s[0]==b[0] && s[1]==b[1] && s[2]==b[2] && s[3]==b[3] )
|
||||
return(0);
|
||||
if ( skip ) {
|
||||
/* If we are supposed to skip initial garbage, */
|
||||
/* try again with the next character. */
|
||||
b[0]=b[1];
|
||||
b[1]=b[2];
|
||||
b[2]=b[3];
|
||||
nread = 3;
|
||||
goto retry;
|
||||
}
|
||||
err:
|
||||
//#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths
|
||||
(void) strcpy(buff,errmsg);
|
||||
(void) strcat(buff,s);
|
||||
//#pragma warning(default: 4996) // turn it back on
|
||||
mferror(buff);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int Midifile_reader::egetc()
|
||||
/* read a single character and abort on EOF */
|
||||
{
|
||||
int c = Mf_getc();
|
||||
|
||||
if ( c == EOF ) {
|
||||
mferror("premature EOF");
|
||||
return EOF;
|
||||
}
|
||||
Mf_toberead--;
|
||||
return(c);
|
||||
}
|
||||
|
||||
int Midifile_reader::readheader()
|
||||
/* read a header chunk */
|
||||
{
|
||||
int format, ntrks, division;
|
||||
|
||||
if ( readmt("MThd",Mf_skipinit) == EOF )
|
||||
return(0);
|
||||
|
||||
Mf_toberead = read32bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
format = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
ntrks = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
division = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
|
||||
Mf_header(format,ntrks,division);
|
||||
|
||||
/* flush any extra stuff, in case the length of header is not 6 */
|
||||
while ( Mf_toberead > 0 && !midifile_error)
|
||||
(void) egetc();
|
||||
return(ntrks);
|
||||
}
|
||||
|
||||
void Midifile_reader::readtrack()
|
||||
/* read a track chunk */
|
||||
{
|
||||
/* This array is indexed by the high half of a status byte. It's */
|
||||
/* value is either the number of bytes needed (1 or 2) for a channel */
|
||||
/* message, or 0 (meaning it's not a channel message). */
|
||||
static int chantype[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */
|
||||
2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */
|
||||
};
|
||||
long lookfor, lng;
|
||||
int c, c1, type;
|
||||
int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
|
||||
int running = 0; /* 1 when running status used */
|
||||
int status = 0; /* (possibly running) status byte */
|
||||
int needed;
|
||||
|
||||
if ( readmt("MTrk",0) == EOF )
|
||||
return;
|
||||
|
||||
Mf_toberead = read32bit();
|
||||
|
||||
if (midifile_error) return;
|
||||
|
||||
Mf_currtime = 0L;
|
||||
|
||||
Mf_starttrack();
|
||||
|
||||
while ( Mf_toberead > 0 ) {
|
||||
|
||||
Mf_currtime += readvarinum(); /* delta time */
|
||||
if (midifile_error) return;
|
||||
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
|
||||
if ( sysexcontinue && c != 0xf7 ) {
|
||||
mferror("didn't find expected continuation of a sysex");
|
||||
return;
|
||||
}
|
||||
if ( (c & 0x80) == 0 ) { /* running status? */
|
||||
if ( status == 0 ) {
|
||||
mferror("unexpected running status");
|
||||
return;
|
||||
}
|
||||
running = 1;
|
||||
} else {
|
||||
status = c;
|
||||
running = 0;
|
||||
}
|
||||
|
||||
needed = chantype[ (status>>4) & 0xf ];
|
||||
|
||||
if ( needed ) { /* ie. is it a channel message? */
|
||||
|
||||
if ( running )
|
||||
c1 = c;
|
||||
else {
|
||||
c1 = egetc();
|
||||
if (midifile_error) return;
|
||||
}
|
||||
chanmessage( status, c1, (needed>1) ? egetc() : 0 );
|
||||
if (midifile_error) return;
|
||||
continue;;
|
||||
}
|
||||
|
||||
switch ( c ) {
|
||||
|
||||
case 0xff: /* meta event */
|
||||
|
||||
type = egetc();
|
||||
if (midifile_error) return;
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
msginit();
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
unsigned char c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
metaevent(type);
|
||||
break;
|
||||
|
||||
case 0xf0: /* start of system exclusive */
|
||||
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
msginit();
|
||||
msgadd(0xf0);
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
if ( c==0xf7 || Mf_nomerge==0 )
|
||||
sysex();
|
||||
else
|
||||
sysexcontinue = 1; /* merge into next msg */
|
||||
break;
|
||||
|
||||
case 0xf7: /* sysex continuation or arbitrary stuff */
|
||||
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
|
||||
if ( ! sysexcontinue )
|
||||
msginit();
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
if ( ! sysexcontinue ) {
|
||||
Mf_arbitrary(msgleng(), msg());
|
||||
}
|
||||
else if ( c == 0xf7 ) {
|
||||
sysex();
|
||||
sysexcontinue = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
badbyte(c);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
Mf_endtrack();
|
||||
return;
|
||||
}
|
||||
|
||||
void Midifile_reader::badbyte(int c)
|
||||
{
|
||||
char buff[32];
|
||||
//#pragma warning(disable: 4996) // safe in this case
|
||||
(void) sprintf(buff,"unexpected byte: 0x%02x",c);
|
||||
//#pragma warning(default: 4996)
|
||||
mferror(buff);
|
||||
}
|
||||
|
||||
void Midifile_reader::metaevent(int type)
|
||||
{
|
||||
int leng = msgleng();
|
||||
// made this unsigned to avoid sign extend
|
||||
unsigned char *m = msg();
|
||||
|
||||
switch ( type ) {
|
||||
case 0x00:
|
||||
Mf_seqnum(to16bit(m[0],m[1]));
|
||||
break;
|
||||
case 0x01: /* Text event */
|
||||
case 0x02: /* Copyright notice */
|
||||
case 0x03: /* Sequence/Track name */
|
||||
case 0x04: /* Instrument name */
|
||||
case 0x05: /* Lyric */
|
||||
case 0x06: /* Marker */
|
||||
case 0x07: /* Cue point */
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
case 0x0b:
|
||||
case 0x0c:
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
case 0x0f:
|
||||
/* These are all text events */
|
||||
Mf_text(type,leng,m);
|
||||
break;
|
||||
case 0x20:
|
||||
Mf_chanprefix(m[0]);
|
||||
break;
|
||||
case 0x21:
|
||||
Mf_portprefix(m[0]);
|
||||
break;
|
||||
case 0x2f: /* End of Track */
|
||||
Mf_eot();
|
||||
break;
|
||||
case 0x51: /* Set tempo */
|
||||
Mf_tempo(to32bit(0,m[0],m[1],m[2]));
|
||||
break;
|
||||
case 0x54:
|
||||
Mf_smpte(m[0],m[1],m[2],m[3],m[4]);
|
||||
break;
|
||||
case 0x58:
|
||||
Mf_timesig(m[0],m[1],m[2],m[3]);
|
||||
break;
|
||||
case 0x59:
|
||||
Mf_keysig(m[0],m[1]);
|
||||
break;
|
||||
case 0x7f:
|
||||
Mf_sqspecific(leng,m);
|
||||
break;
|
||||
default:
|
||||
Mf_metamisc(type,leng,m);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::sysex()
|
||||
{
|
||||
Mf_sysex(msgleng(), msg());
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::chanmessage(int status, int c1, int c2)
|
||||
{
|
||||
int chan = status & 0xf;
|
||||
|
||||
switch ( status & 0xf0 ) {
|
||||
case NOTEOFF:
|
||||
Mf_off(chan,c1,c2);
|
||||
break;
|
||||
case NOTEON:
|
||||
Mf_on(chan,c1,c2);
|
||||
break;
|
||||
case PRESSURE:
|
||||
Mf_pressure(chan,c1,c2);
|
||||
break;
|
||||
case CONTROLLER:
|
||||
Mf_controller(chan,c1,c2);
|
||||
break;
|
||||
case PITCHBEND:
|
||||
Mf_pitchbend(chan,c1,c2);
|
||||
break;
|
||||
case PROGRAM:
|
||||
Mf_program(chan,c1);
|
||||
break;
|
||||
case CHANPRESSURE:
|
||||
Mf_chanpressure(chan,c1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* readvarinum - read a varying-length number, and return the */
|
||||
/* number of characters it took. */
|
||||
|
||||
long Midifile_reader::readvarinum()
|
||||
{
|
||||
long value;
|
||||
int c;
|
||||
|
||||
c = egetc();
|
||||
if (midifile_error) return 0;
|
||||
|
||||
value = (long) c;
|
||||
if ( c & 0x80 ) {
|
||||
value &= 0x7f;
|
||||
do {
|
||||
c = egetc();
|
||||
if (midifile_error) return 0;
|
||||
value = (value << 7) + (c & 0x7f);
|
||||
} while (c & 0x80);
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
long Midifile_reader::to32bit(int c1, int c2, int c3, int c4)
|
||||
{
|
||||
long value = 0L;
|
||||
|
||||
value = (c1 & 0xff);
|
||||
value = (value<<8) + (c2 & 0xff);
|
||||
value = (value<<8) + (c3 & 0xff);
|
||||
value = (value<<8) + (c4 & 0xff);
|
||||
return (value);
|
||||
}
|
||||
|
||||
int Midifile_reader::to16bit(int c1, int c2)
|
||||
{
|
||||
return ((c1 & 0xff ) << 8) + (c2 & 0xff);
|
||||
}
|
||||
|
||||
long Midifile_reader::read32bit()
|
||||
{
|
||||
int c1, c2, c3, c4;
|
||||
|
||||
c1 = egetc(); if (midifile_error) return 0;
|
||||
c2 = egetc(); if (midifile_error) return 0;
|
||||
c3 = egetc(); if (midifile_error) return 0;
|
||||
c4 = egetc(); if (midifile_error) return 0;
|
||||
return to32bit(c1,c2,c3,c4);
|
||||
}
|
||||
|
||||
int Midifile_reader::read16bit()
|
||||
{
|
||||
int c1, c2;
|
||||
c1 = egetc(); if (midifile_error) return 0;
|
||||
c2 = egetc(); if (midifile_error) return 0;
|
||||
return to16bit(c1,c2);
|
||||
}
|
||||
|
||||
void Midifile_reader::mferror(char *s)
|
||||
{
|
||||
Mf_error(s);
|
||||
midifile_error = 1;
|
||||
}
|
||||
|
||||
void Midifile_reader::mferror(const char *s)
|
||||
{
|
||||
mferror(const_cast<char *>(s));
|
||||
}
|
||||
|
||||
/* The code below allows collection of a system exclusive message of */
|
||||
/* arbitrary length. The Msgbuff is expanded as necessary. The only */
|
||||
/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */
|
||||
|
||||
#define MSGINCREMENT 128
|
||||
|
||||
Midifile_reader::Midifile_reader()
|
||||
{
|
||||
Mf_nomerge = 0;
|
||||
Mf_currtime = 0L;
|
||||
Mf_skipinit = 0;
|
||||
Mf_toberead = 0;
|
||||
|
||||
Msgbuff = 0; /* message buffer */
|
||||
Msgsize = 0; /* Size of currently allocated Msg */
|
||||
Msgindex = 0; /* index of next available location in Msg */
|
||||
}
|
||||
|
||||
void Midifile_reader::finalize()
|
||||
{
|
||||
if (Msgbuff) Mf_free(Msgbuff, Msgsize);
|
||||
Msgbuff = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::msginit()
|
||||
{
|
||||
Msgindex = 0;
|
||||
}
|
||||
|
||||
unsigned char *Midifile_reader::msg()
|
||||
{
|
||||
return(Msgbuff);
|
||||
}
|
||||
|
||||
int Midifile_reader::msgleng()
|
||||
{
|
||||
return(Msgindex);
|
||||
}
|
||||
|
||||
void Midifile_reader::msgadd(int c)
|
||||
{
|
||||
/* If necessary, allocate larger message buffer. */
|
||||
if ( Msgindex >= Msgsize )
|
||||
msgenlarge();
|
||||
Msgbuff[Msgindex++] = c;
|
||||
}
|
||||
|
||||
void Midifile_reader::msgenlarge()
|
||||
{
|
||||
unsigned char *newmess;
|
||||
unsigned char *oldmess = Msgbuff;
|
||||
int oldleng = Msgsize;
|
||||
|
||||
Msgsize += MSGINCREMENT;
|
||||
newmess = (unsigned char *) Mf_malloc((sizeof(unsigned char) * Msgsize) );
|
||||
|
||||
/* copy old message into larger new one */
|
||||
if ( oldmess != 0 ) {
|
||||
memcpy(newmess, oldmess, oldleng);
|
||||
Mf_free(oldmess, oldleng);
|
||||
}
|
||||
Msgbuff = newmess;
|
||||
}
|
||||
/*
|
||||
* Read a Standard MIDI File. Externally-assigned function pointers are
|
||||
* called upon recognizing things in the file. See midifile(3).
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Change Log
|
||||
* Date | who : Change
|
||||
*-----------+-----------------------------------------------------------------
|
||||
* 2-Mar-92 | GWL : created changelog; MIDIFILE_ERROR to satisfy compiler
|
||||
*****************************************************************************/
|
||||
|
||||
#include "stdio.h"
|
||||
#include "mfmidi.h"
|
||||
#include "string.h"
|
||||
#include "assert.h"
|
||||
|
||||
#define MIDIFILE_ERROR -1
|
||||
|
||||
/* public stuff */
|
||||
extern int abort_flag;
|
||||
|
||||
|
||||
void Midifile_reader::midifile()
|
||||
{
|
||||
int ntrks;
|
||||
midifile_error = 0;
|
||||
|
||||
ntrks = readheader();
|
||||
if (midifile_error) return;
|
||||
if (ntrks <= 0) {
|
||||
mferror("No tracks!");
|
||||
/* no need to return since midifile_error is set */
|
||||
}
|
||||
while (ntrks-- > 0 && !midifile_error) readtrack();
|
||||
}
|
||||
|
||||
int Midifile_reader::readmt(const char *s, int skip)
|
||||
/* read through the "MThd" or "MTrk" header string */
|
||||
/* if skip == 1, we attempt to skip initial garbage. */
|
||||
{
|
||||
assert(strlen(s) == 4); // must be "MThd" or "MTrk"
|
||||
int nread = 0;
|
||||
char b[4];
|
||||
char buff[32];
|
||||
int c;
|
||||
const char *errmsg = "expecting ";
|
||||
|
||||
retry:
|
||||
while ( nread<4 ) {
|
||||
c = Mf_getc();
|
||||
if ( c == EOF ) {
|
||||
errmsg = "EOF while expecting ";
|
||||
goto err;
|
||||
}
|
||||
b[nread++] = c;
|
||||
}
|
||||
/* See if we found the 4 characters we're looking for */
|
||||
if ( s[0]==b[0] && s[1]==b[1] && s[2]==b[2] && s[3]==b[3] )
|
||||
return(0);
|
||||
if ( skip ) {
|
||||
/* If we are supposed to skip initial garbage, */
|
||||
/* try again with the next character. */
|
||||
b[0]=b[1];
|
||||
b[1]=b[2];
|
||||
b[2]=b[3];
|
||||
nread = 3;
|
||||
goto retry;
|
||||
}
|
||||
err:
|
||||
//#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths
|
||||
(void) strcpy(buff,errmsg);
|
||||
(void) strcat(buff,s);
|
||||
//#pragma warning(default: 4996) // turn it back on
|
||||
mferror(buff);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int Midifile_reader::egetc()
|
||||
/* read a single character and abort on EOF */
|
||||
{
|
||||
int c = Mf_getc();
|
||||
|
||||
if ( c == EOF ) {
|
||||
mferror("premature EOF");
|
||||
return EOF;
|
||||
}
|
||||
Mf_toberead--;
|
||||
return(c);
|
||||
}
|
||||
|
||||
int Midifile_reader::readheader()
|
||||
/* read a header chunk */
|
||||
{
|
||||
int format, ntrks, division;
|
||||
|
||||
if ( readmt("MThd",Mf_skipinit) == EOF )
|
||||
return(0);
|
||||
|
||||
Mf_toberead = read32bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
format = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
ntrks = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
division = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
|
||||
Mf_header(format,ntrks,division);
|
||||
|
||||
/* flush any extra stuff, in case the length of header is not 6 */
|
||||
while ( Mf_toberead > 0 && !midifile_error)
|
||||
(void) egetc();
|
||||
return(ntrks);
|
||||
}
|
||||
|
||||
void Midifile_reader::readtrack()
|
||||
/* read a track chunk */
|
||||
{
|
||||
/* This array is indexed by the high half of a status byte. It's */
|
||||
/* value is either the number of bytes needed (1 or 2) for a channel */
|
||||
/* message, or 0 (meaning it's not a channel message). */
|
||||
static int chantype[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */
|
||||
2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */
|
||||
};
|
||||
long lookfor, lng;
|
||||
int c, c1, type;
|
||||
int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
|
||||
int running = 0; /* 1 when running status used */
|
||||
int status = 0; /* (possibly running) status byte */
|
||||
int needed;
|
||||
|
||||
if ( readmt("MTrk",0) == EOF )
|
||||
return;
|
||||
|
||||
Mf_toberead = read32bit();
|
||||
|
||||
if (midifile_error) return;
|
||||
|
||||
Mf_currtime = 0L;
|
||||
|
||||
Mf_starttrack();
|
||||
|
||||
while ( Mf_toberead > 0 ) {
|
||||
|
||||
Mf_currtime += readvarinum(); /* delta time */
|
||||
if (midifile_error) return;
|
||||
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
|
||||
if ( sysexcontinue && c != 0xf7 ) {
|
||||
mferror("didn't find expected continuation of a sysex");
|
||||
return;
|
||||
}
|
||||
if ( (c & 0x80) == 0 ) { /* running status? */
|
||||
if ( status == 0 ) {
|
||||
mferror("unexpected running status");
|
||||
return;
|
||||
}
|
||||
running = 1;
|
||||
} else {
|
||||
status = c;
|
||||
running = 0;
|
||||
}
|
||||
|
||||
needed = chantype[ (status>>4) & 0xf ];
|
||||
|
||||
if ( needed ) { /* ie. is it a channel message? */
|
||||
|
||||
if ( running )
|
||||
c1 = c;
|
||||
else {
|
||||
c1 = egetc();
|
||||
if (midifile_error) return;
|
||||
}
|
||||
chanmessage( status, c1, (needed>1) ? egetc() : 0 );
|
||||
if (midifile_error) return;
|
||||
continue;;
|
||||
}
|
||||
|
||||
switch ( c ) {
|
||||
|
||||
case 0xff: /* meta event */
|
||||
|
||||
type = egetc();
|
||||
if (midifile_error) return;
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
msginit();
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
unsigned char c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
metaevent(type);
|
||||
break;
|
||||
|
||||
case 0xf0: /* start of system exclusive */
|
||||
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
msginit();
|
||||
msgadd(0xf0);
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
if ( c==0xf7 || Mf_nomerge==0 )
|
||||
sysex();
|
||||
else
|
||||
sysexcontinue = 1; /* merge into next msg */
|
||||
break;
|
||||
|
||||
case 0xf7: /* sysex continuation or arbitrary stuff */
|
||||
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
|
||||
if ( ! sysexcontinue )
|
||||
msginit();
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
if ( ! sysexcontinue ) {
|
||||
Mf_arbitrary(msgleng(), msg());
|
||||
}
|
||||
else if ( c == 0xf7 ) {
|
||||
sysex();
|
||||
sysexcontinue = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
badbyte(c);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
Mf_endtrack();
|
||||
return;
|
||||
}
|
||||
|
||||
void Midifile_reader::badbyte(int c)
|
||||
{
|
||||
char buff[32];
|
||||
//#pragma warning(disable: 4996) // safe in this case
|
||||
(void) sprintf(buff,"unexpected byte: 0x%02x",c);
|
||||
//#pragma warning(default: 4996)
|
||||
mferror(buff);
|
||||
}
|
||||
|
||||
void Midifile_reader::metaevent(int type)
|
||||
{
|
||||
int leng = msgleng();
|
||||
// made this unsigned to avoid sign extend
|
||||
unsigned char *m = msg();
|
||||
|
||||
switch ( type ) {
|
||||
case 0x00:
|
||||
Mf_seqnum(to16bit(m[0],m[1]));
|
||||
break;
|
||||
case 0x01: /* Text event */
|
||||
case 0x02: /* Copyright notice */
|
||||
case 0x03: /* Sequence/Track name */
|
||||
case 0x04: /* Instrument name */
|
||||
case 0x05: /* Lyric */
|
||||
case 0x06: /* Marker */
|
||||
case 0x07: /* Cue point */
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
case 0x0b:
|
||||
case 0x0c:
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
case 0x0f:
|
||||
/* These are all text events */
|
||||
Mf_text(type,leng,m);
|
||||
break;
|
||||
case 0x20:
|
||||
Mf_chanprefix(m[0]);
|
||||
break;
|
||||
case 0x21:
|
||||
Mf_portprefix(m[0]);
|
||||
break;
|
||||
case 0x2f: /* End of Track */
|
||||
Mf_eot();
|
||||
break;
|
||||
case 0x51: /* Set tempo */
|
||||
Mf_tempo(to32bit(0,m[0],m[1],m[2]));
|
||||
break;
|
||||
case 0x54:
|
||||
Mf_smpte(m[0],m[1],m[2],m[3],m[4]);
|
||||
break;
|
||||
case 0x58:
|
||||
Mf_timesig(m[0],m[1],m[2],m[3]);
|
||||
break;
|
||||
case 0x59:
|
||||
Mf_keysig(m[0],m[1]);
|
||||
break;
|
||||
case 0x7f:
|
||||
Mf_sqspecific(leng,m);
|
||||
break;
|
||||
default:
|
||||
Mf_metamisc(type,leng,m);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::sysex()
|
||||
{
|
||||
Mf_sysex(msgleng(), msg());
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::chanmessage(int status, int c1, int c2)
|
||||
{
|
||||
int chan = status & 0xf;
|
||||
|
||||
switch ( status & 0xf0 ) {
|
||||
case NOTEOFF:
|
||||
Mf_off(chan,c1,c2);
|
||||
break;
|
||||
case NOTEON:
|
||||
Mf_on(chan,c1,c2);
|
||||
break;
|
||||
case PRESSURE:
|
||||
Mf_pressure(chan,c1,c2);
|
||||
break;
|
||||
case CONTROLLER:
|
||||
Mf_controller(chan,c1,c2);
|
||||
break;
|
||||
case PITCHBEND:
|
||||
Mf_pitchbend(chan,c1,c2);
|
||||
break;
|
||||
case PROGRAM:
|
||||
Mf_program(chan,c1);
|
||||
break;
|
||||
case CHANPRESSURE:
|
||||
Mf_chanpressure(chan,c1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* readvarinum - read a varying-length number, and return the */
|
||||
/* number of characters it took. */
|
||||
|
||||
long Midifile_reader::readvarinum()
|
||||
{
|
||||
long value;
|
||||
int c;
|
||||
|
||||
c = egetc();
|
||||
if (midifile_error) return 0;
|
||||
|
||||
value = (long) c;
|
||||
if ( c & 0x80 ) {
|
||||
value &= 0x7f;
|
||||
do {
|
||||
c = egetc();
|
||||
if (midifile_error) return 0;
|
||||
value = (value << 7) + (c & 0x7f);
|
||||
} while (c & 0x80);
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
long Midifile_reader::to32bit(int c1, int c2, int c3, int c4)
|
||||
{
|
||||
long value = 0L;
|
||||
|
||||
value = (c1 & 0xff);
|
||||
value = (value<<8) + (c2 & 0xff);
|
||||
value = (value<<8) + (c3 & 0xff);
|
||||
value = (value<<8) + (c4 & 0xff);
|
||||
return (value);
|
||||
}
|
||||
|
||||
int Midifile_reader::to16bit(int c1, int c2)
|
||||
{
|
||||
return ((c1 & 0xff ) << 8) + (c2 & 0xff);
|
||||
}
|
||||
|
||||
long Midifile_reader::read32bit()
|
||||
{
|
||||
int c1, c2, c3, c4;
|
||||
|
||||
c1 = egetc(); if (midifile_error) return 0;
|
||||
c2 = egetc(); if (midifile_error) return 0;
|
||||
c3 = egetc(); if (midifile_error) return 0;
|
||||
c4 = egetc(); if (midifile_error) return 0;
|
||||
return to32bit(c1,c2,c3,c4);
|
||||
}
|
||||
|
||||
int Midifile_reader::read16bit()
|
||||
{
|
||||
int c1, c2;
|
||||
c1 = egetc(); if (midifile_error) return 0;
|
||||
c2 = egetc(); if (midifile_error) return 0;
|
||||
return to16bit(c1,c2);
|
||||
}
|
||||
|
||||
void Midifile_reader::mferror(char *s)
|
||||
{
|
||||
Mf_error(s);
|
||||
midifile_error = 1;
|
||||
}
|
||||
|
||||
void Midifile_reader::mferror(const char *s)
|
||||
{
|
||||
mferror(const_cast<char *>(s));
|
||||
}
|
||||
|
||||
/* The code below allows collection of a system exclusive message of */
|
||||
/* arbitrary length. The Msgbuff is expanded as necessary. The only */
|
||||
/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */
|
||||
|
||||
#define MSGINCREMENT 128
|
||||
|
||||
Midifile_reader::Midifile_reader()
|
||||
{
|
||||
Mf_nomerge = 0;
|
||||
Mf_currtime = 0L;
|
||||
Mf_skipinit = 0;
|
||||
Mf_toberead = 0;
|
||||
|
||||
Msgbuff = 0; /* message buffer */
|
||||
Msgsize = 0; /* Size of currently allocated Msg */
|
||||
Msgindex = 0; /* index of next available location in Msg */
|
||||
}
|
||||
|
||||
void Midifile_reader::finalize()
|
||||
{
|
||||
if (Msgbuff) Mf_free(Msgbuff, Msgsize);
|
||||
Msgbuff = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::msginit()
|
||||
{
|
||||
Msgindex = 0;
|
||||
}
|
||||
|
||||
unsigned char *Midifile_reader::msg()
|
||||
{
|
||||
return(Msgbuff);
|
||||
}
|
||||
|
||||
int Midifile_reader::msgleng()
|
||||
{
|
||||
return(Msgindex);
|
||||
}
|
||||
|
||||
void Midifile_reader::msgadd(int c)
|
||||
{
|
||||
/* If necessary, allocate larger message buffer. */
|
||||
if ( Msgindex >= Msgsize )
|
||||
msgenlarge();
|
||||
Msgbuff[Msgindex++] = c;
|
||||
}
|
||||
|
||||
void Midifile_reader::msgenlarge()
|
||||
{
|
||||
unsigned char *newmess;
|
||||
unsigned char *oldmess = Msgbuff;
|
||||
int oldleng = Msgsize;
|
||||
|
||||
Msgsize += MSGINCREMENT;
|
||||
newmess = (unsigned char *) Mf_malloc((sizeof(unsigned char) * Msgsize) );
|
||||
|
||||
/* copy old message into larger new one */
|
||||
if ( oldmess != 0 ) {
|
||||
memcpy(newmess, oldmess, oldleng);
|
||||
Mf_free(oldmess, oldleng);
|
||||
}
|
||||
Msgbuff = newmess;
|
||||
}
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
#include <cstddef>
|
||||
|
||||
#define NOTEOFF 0x80
|
||||
#define NOTEON 0x90
|
||||
#define PRESSURE 0xa0
|
||||
#define CONTROLLER 0xb0
|
||||
#define PITCHBEND 0xe0
|
||||
#define PROGRAM 0xc0
|
||||
#define CHANPRESSURE 0xd0
|
||||
|
||||
/* These are the strings used in keynote to identify Standard MIDI File */
|
||||
/* meta text messages. */
|
||||
|
||||
#define METATEXT "Text Event"
|
||||
#define METACOPYRIGHT "Copyright Notice"
|
||||
#define METASEQUENCE "Sequence/Track Name"
|
||||
#define METAINSTRUMENT "Instrument Name"
|
||||
#define METALYRIC "Lyric"
|
||||
#define METAMARKER "Marker"
|
||||
#define METACUE "Cue Point"
|
||||
#define METAUNRECOGNIZED "Unrecognized"
|
||||
|
||||
|
||||
class Midifile_reader {
|
||||
public:
|
||||
void midifile();
|
||||
int Mf_nomerge; /* 1 => continue'ed system exclusives are */
|
||||
/* not collapsed. */
|
||||
long Mf_currtime; /* current time in delta-time units */
|
||||
int Mf_skipinit; /* 1 if initial garbage should be skipped */
|
||||
Midifile_reader();
|
||||
// call finalize() when done or you may leak memory.
|
||||
void finalize(); /* clean up before deletion */
|
||||
// Note: rather than finalize, we should have ~Midifile_reader(),
|
||||
// but at least VC++ complains that there is no Mf_free(), even
|
||||
// though Mf_free is declared as virtual and this is an abstract
|
||||
// class. I don't understand this, so finalize() is a workaround. -RBD
|
||||
|
||||
protected:
|
||||
int midifile_error;
|
||||
|
||||
virtual void *Mf_malloc(size_t size) = 0; /* malloc() */
|
||||
virtual void Mf_free(void *obj, size_t size) = 0; /* free() */
|
||||
/* Methods to be called while processing the MIDI file. */
|
||||
virtual void Mf_starttrack() = 0;
|
||||
virtual void Mf_endtrack() = 0;
|
||||
virtual int Mf_getc() = 0;
|
||||
virtual void Mf_chanprefix(int) = 0;
|
||||
virtual void Mf_portprefix(int) = 0;
|
||||
virtual void Mf_eot() = 0;
|
||||
virtual void Mf_error(char *) = 0;
|
||||
virtual void Mf_header(int,int,int) = 0;
|
||||
virtual void Mf_on(int,int,int) = 0;
|
||||
virtual void Mf_off(int,int,int) = 0;
|
||||
virtual void Mf_pressure(int,int,int) = 0;
|
||||
virtual void Mf_controller(int,int,int) = 0;
|
||||
virtual void Mf_pitchbend(int,int,int) = 0;
|
||||
virtual void Mf_program(int,int) = 0;
|
||||
virtual void Mf_chanpressure(int,int) = 0;
|
||||
virtual void Mf_sysex(int,unsigned char*) = 0;
|
||||
virtual void Mf_arbitrary(int,unsigned char*) = 0;
|
||||
virtual void Mf_metamisc(int,int,unsigned char*) = 0;
|
||||
virtual void Mf_seqnum(int) = 0;
|
||||
virtual void Mf_smpte(int,int,int,int,int) = 0;
|
||||
virtual void Mf_timesig(int,int,int,int) = 0;
|
||||
virtual void Mf_tempo(int) = 0;
|
||||
virtual void Mf_keysig(int,int) = 0;
|
||||
virtual void Mf_sqspecific(int,unsigned char*) = 0;
|
||||
virtual void Mf_text(int,int,unsigned char*) = 0;
|
||||
|
||||
private:
|
||||
long Mf_toberead;
|
||||
|
||||
long readvarinum();
|
||||
long read32bit();
|
||||
int read16bit();
|
||||
void msgenlarge();
|
||||
unsigned char *msg();
|
||||
int readheader();
|
||||
void readtrack();
|
||||
void sysex();
|
||||
void msginit();
|
||||
int egetc();
|
||||
int msgleng();
|
||||
|
||||
int readmt(const char*,int);
|
||||
long to32bit(int,int,int,int);
|
||||
int to16bit(int,int);
|
||||
void mferror(char *);
|
||||
void mferror(const char *);
|
||||
void badbyte(int);
|
||||
void metaevent(int);
|
||||
void msgadd(int);
|
||||
void chanmessage(int,int,int);
|
||||
|
||||
unsigned char *Msgbuff;
|
||||
long Msgsize;
|
||||
long Msgindex;
|
||||
};
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#define NOTEOFF 0x80
|
||||
#define NOTEON 0x90
|
||||
#define PRESSURE 0xa0
|
||||
#define CONTROLLER 0xb0
|
||||
#define PITCHBEND 0xe0
|
||||
#define PROGRAM 0xc0
|
||||
#define CHANPRESSURE 0xd0
|
||||
|
||||
/* These are the strings used in keynote to identify Standard MIDI File */
|
||||
/* meta text messages. */
|
||||
|
||||
#define METATEXT "Text Event"
|
||||
#define METACOPYRIGHT "Copyright Notice"
|
||||
#define METASEQUENCE "Sequence/Track Name"
|
||||
#define METAINSTRUMENT "Instrument Name"
|
||||
#define METALYRIC "Lyric"
|
||||
#define METAMARKER "Marker"
|
||||
#define METACUE "Cue Point"
|
||||
#define METAUNRECOGNIZED "Unrecognized"
|
||||
|
||||
|
||||
class Midifile_reader {
|
||||
public:
|
||||
void midifile();
|
||||
int Mf_nomerge; /* 1 => continue'ed system exclusives are */
|
||||
/* not collapsed. */
|
||||
long Mf_currtime; /* current time in delta-time units */
|
||||
int Mf_skipinit; /* 1 if initial garbage should be skipped */
|
||||
Midifile_reader();
|
||||
// call finalize() when done or you may leak memory.
|
||||
void finalize(); /* clean up before deletion */
|
||||
// Note: rather than finalize, we should have ~Midifile_reader(),
|
||||
// but at least VC++ complains that there is no Mf_free(), even
|
||||
// though Mf_free is declared as virtual and this is an abstract
|
||||
// class. I don't understand this, so finalize() is a workaround. -RBD
|
||||
|
||||
protected:
|
||||
int midifile_error;
|
||||
|
||||
virtual void *Mf_malloc(size_t size) = 0; /* malloc() */
|
||||
virtual void Mf_free(void *obj, size_t size) = 0; /* free() */
|
||||
/* Methods to be called while processing the MIDI file. */
|
||||
virtual void Mf_starttrack() = 0;
|
||||
virtual void Mf_endtrack() = 0;
|
||||
virtual int Mf_getc() = 0;
|
||||
virtual void Mf_chanprefix(int) = 0;
|
||||
virtual void Mf_portprefix(int) = 0;
|
||||
virtual void Mf_eot() = 0;
|
||||
virtual void Mf_error(char *) = 0;
|
||||
virtual void Mf_header(int,int,int) = 0;
|
||||
virtual void Mf_on(int,int,int) = 0;
|
||||
virtual void Mf_off(int,int,int) = 0;
|
||||
virtual void Mf_pressure(int,int,int) = 0;
|
||||
virtual void Mf_controller(int,int,int) = 0;
|
||||
virtual void Mf_pitchbend(int,int,int) = 0;
|
||||
virtual void Mf_program(int,int) = 0;
|
||||
virtual void Mf_chanpressure(int,int) = 0;
|
||||
virtual void Mf_sysex(int,unsigned char*) = 0;
|
||||
virtual void Mf_arbitrary(int,unsigned char*) = 0;
|
||||
virtual void Mf_metamisc(int,int,unsigned char*) = 0;
|
||||
virtual void Mf_seqnum(int) = 0;
|
||||
virtual void Mf_smpte(int,int,int,int,int) = 0;
|
||||
virtual void Mf_timesig(int,int,int,int) = 0;
|
||||
virtual void Mf_tempo(int) = 0;
|
||||
virtual void Mf_keysig(int,int) = 0;
|
||||
virtual void Mf_sqspecific(int,unsigned char*) = 0;
|
||||
virtual void Mf_text(int,int,unsigned char*) = 0;
|
||||
|
||||
private:
|
||||
long Mf_toberead;
|
||||
|
||||
long readvarinum();
|
||||
long read32bit();
|
||||
int read16bit();
|
||||
void msgenlarge();
|
||||
unsigned char *msg();
|
||||
int readheader();
|
||||
void readtrack();
|
||||
void sysex();
|
||||
void msginit();
|
||||
int egetc();
|
||||
int msgleng();
|
||||
|
||||
int readmt(const char*,int);
|
||||
long to32bit(int,int,int,int);
|
||||
int to16bit(int,int);
|
||||
void mferror(char *);
|
||||
void mferror(const char *);
|
||||
void badbyte(int);
|
||||
void metaevent(int);
|
||||
void msgadd(int);
|
||||
void chanmessage(int,int,int);
|
||||
|
||||
unsigned char *Msgbuff;
|
||||
long Msgsize;
|
||||
long Msgindex;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
// #include <iostream> -- for debugging (cout)
|
||||
#include "ctype.h"
|
||||
using namespace std;
|
||||
#include "strparse.h"
|
||||
|
||||
void String_parse::skip_space()
|
||||
{
|
||||
while ((*str)[pos] && isspace((*str)[pos])) {
|
||||
pos = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char String_parse::peek()
|
||||
{
|
||||
return (*str)[pos];
|
||||
}
|
||||
|
||||
|
||||
void String_parse::get_nonspace_quoted(string &field)
|
||||
{
|
||||
field.clear();
|
||||
skip_space();
|
||||
bool quoted = false;
|
||||
if ((*str)[pos] == '"') {
|
||||
quoted = true;
|
||||
field.append(1, '"');
|
||||
pos = pos + 1;
|
||||
}
|
||||
while ((*str)[pos] && (quoted || !isspace((*str)[pos]))) {
|
||||
if ((*str)[pos] == '"') {
|
||||
if (quoted) {
|
||||
field.append(1, '"');
|
||||
pos = pos + 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ((*str)[pos] == '\\') {
|
||||
pos = pos + 1;
|
||||
}
|
||||
if ((*str)[pos]) {
|
||||
field.append(1, (*str)[pos]);
|
||||
pos = pos + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *const escape_chars[] = {"\\n", "\\t", "\\\\", "\\r", "\\\""};
|
||||
|
||||
|
||||
void string_escape(string &result, const char *str, const char *quote)
|
||||
{
|
||||
int length = (int) strlen(str);
|
||||
if (quote[0]) {
|
||||
result.append(1, quote[0]);
|
||||
}
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!isalnum((unsigned char) str[i])) {
|
||||
const char *const chars = "\n\t\\\r\"";
|
||||
const char *const special = strchr(chars, str[i]);
|
||||
if (special) {
|
||||
result.append(escape_chars[special - chars]);
|
||||
} else {
|
||||
result.append(1, str[i]);
|
||||
}
|
||||
} else {
|
||||
result.append(1, str[i]);
|
||||
}
|
||||
}
|
||||
result.append(1, quote[0]);
|
||||
}
|
||||
|
||||
void String_parse::get_remainder(std::string &field)
|
||||
{
|
||||
field.clear();
|
||||
skip_space();
|
||||
int len = str->length() - pos;
|
||||
if ((len > 0) && ((*str)[len - 1] == '\n')) { // if str ends in newline,
|
||||
len--; // reduce length to ignore newline
|
||||
}
|
||||
field.insert(0, *str, pos, len);
|
||||
}
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
// #include <iostream> -- for debugging (cout)
|
||||
#include "ctype.h"
|
||||
using namespace std;
|
||||
#include "strparse.h"
|
||||
|
||||
void String_parse::skip_space()
|
||||
{
|
||||
while ((*str)[pos] && isspace((*str)[pos])) {
|
||||
pos = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char String_parse::peek()
|
||||
{
|
||||
return (*str)[pos];
|
||||
}
|
||||
|
||||
|
||||
void String_parse::get_nonspace_quoted(string &field)
|
||||
{
|
||||
field.clear();
|
||||
skip_space();
|
||||
bool quoted = false;
|
||||
if ((*str)[pos] == '"') {
|
||||
quoted = true;
|
||||
field.append(1, '"');
|
||||
pos = pos + 1;
|
||||
}
|
||||
while ((*str)[pos] && (quoted || !isspace((*str)[pos]))) {
|
||||
if ((*str)[pos] == '"') {
|
||||
if (quoted) {
|
||||
field.append(1, '"');
|
||||
pos = pos + 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ((*str)[pos] == '\\') {
|
||||
pos = pos + 1;
|
||||
}
|
||||
if ((*str)[pos]) {
|
||||
field.append(1, (*str)[pos]);
|
||||
pos = pos + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *const escape_chars[] = {"\\n", "\\t", "\\\\", "\\r", "\\\""};
|
||||
|
||||
|
||||
void string_escape(string &result, const char *str, const char *quote)
|
||||
{
|
||||
int length = (int) strlen(str);
|
||||
if (quote[0]) {
|
||||
result.append(1, quote[0]);
|
||||
}
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!isalnum((unsigned char) str[i])) {
|
||||
const char *const chars = "\n\t\\\r\"";
|
||||
const char *const special = strchr(chars, str[i]);
|
||||
if (special) {
|
||||
result.append(escape_chars[special - chars]);
|
||||
} else {
|
||||
result.append(1, str[i]);
|
||||
}
|
||||
} else {
|
||||
result.append(1, str[i]);
|
||||
}
|
||||
}
|
||||
result.append(1, quote[0]);
|
||||
}
|
||||
|
||||
void String_parse::get_remainder(std::string &field)
|
||||
{
|
||||
field.clear();
|
||||
skip_space();
|
||||
int len = str->length() - pos;
|
||||
if ((len > 0) && ((*str)[len - 1] == '\n')) { // if str ends in newline,
|
||||
len--; // reduce length to ignore newline
|
||||
}
|
||||
field.insert(0, *str, pos, len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// strparse.h -- header for String_parse class
|
||||
|
||||
class String_parse {
|
||||
public:
|
||||
int pos;
|
||||
std::string *str;
|
||||
void init(std::string *s) {
|
||||
str = s;
|
||||
pos = 0;
|
||||
}
|
||||
void skip_space();
|
||||
char peek();
|
||||
void get_nonspace_quoted(std::string &field);
|
||||
// get the remaining characters, skipping initial spaces and final return
|
||||
void get_remainder(std::string &field);
|
||||
};
|
||||
|
||||
void string_escape(std::string &result, const char *s, const char *quote);
|
||||
// strparse.h -- header for String_parse class
|
||||
|
||||
class String_parse {
|
||||
public:
|
||||
int pos;
|
||||
std::string *str;
|
||||
void init(std::string *s) {
|
||||
str = s;
|
||||
pos = 0;
|
||||
}
|
||||
void skip_space();
|
||||
char peek();
|
||||
void get_nonspace_quoted(std::string &field);
|
||||
// get the remaining characters, skipping initial spaces and final return
|
||||
void get_remainder(std::string &field);
|
||||
};
|
||||
|
||||
void string_escape(std::string &result, const char *s, const char *quote);
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
// trace.cpp -- debugging print function
|
||||
//
|
||||
// (I think this was created to provide a generic print function
|
||||
// for use in non-command-line Windows applications where printf
|
||||
// does not work. Currently, it is not used, but kept around for
|
||||
// possible debugging needs. -RBD)
|
||||
|
||||
#include "stdarg.h"
|
||||
#include "stdio.h"
|
||||
#include "crtdbg.h"
|
||||
|
||||
|
||||
void trace(char *format, ...)
|
||||
{
|
||||
char msg[256];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_vsnprintf_s(msg, 256, _TRUNCATE, format, args);
|
||||
va_end(args);
|
||||
#ifdef _DEBUG
|
||||
_CrtDbgReport(_CRT_WARN, nullptr, nullptr, nullptr, msg);
|
||||
#else
|
||||
printf(msg);
|
||||
#endif
|
||||
}
|
||||
// trace.cpp -- debugging print function
|
||||
//
|
||||
// (I think this was created to provide a generic print function
|
||||
// for use in non-command-line Windows applications where printf
|
||||
// does not work. Currently, it is not used, but kept around for
|
||||
// possible debugging needs. -RBD)
|
||||
|
||||
#include "stdarg.h"
|
||||
#include "stdio.h"
|
||||
#include "crtdbg.h"
|
||||
|
||||
|
||||
void trace(char *format, ...)
|
||||
{
|
||||
char msg[256];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_vsnprintf_s(msg, 256, _TRUNCATE, format, args);
|
||||
va_end(args);
|
||||
#ifdef _DEBUG
|
||||
_CrtDbgReport(_CRT_WARN, nullptr, nullptr, nullptr, msg);
|
||||
#else
|
||||
printf(msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
void trace(char *format, ...);
|
||||
|
||||
void trace(char *format, ...);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user