summaryrefslogtreecommitdiffabout
path: root/lib
Unidiff
Diffstat (limited to 'lib') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/.gitignore7
-rw-r--r--lib/MThd.cc38
-rw-r--r--lib/MTrk.cc34
-rw-r--r--lib/Makefile.am15
-rw-r--r--lib/SMF.cc62
-rw-r--r--lib/chunk.cc31
-rw-r--r--lib/event.cc58
-rw-r--r--lib/message.cc247
-rw-r--r--lib/util.cc96
9 files changed, 588 insertions, 0 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 0000000..dc5e416
--- a/dev/null
+++ b/lib/.gitignore
@@ -0,0 +1,7 @@
1*.lo
2*.o
3Makefile.in
4.libs
5.deps
6Makefile
7*.la
diff --git a/lib/MThd.cc b/lib/MThd.cc
new file mode 100644
index 0000000..110c98a
--- a/dev/null
+++ b/lib/MThd.cc
@@ -0,0 +1,38 @@
1#include <midillo/MThd.h>
2#include <midillo/util.h>
3#include <midillo/exception.h>
4
5namespace midillo {
6 using std::endl;
7
8 void MThd_t::load(istream& s) {
9 header.load(s);
10 if(header.id_number!=chunk_id_MThd)
11 throw exception_unexpected_input(CODEPOINT,"MThd chunk expected");
12 if(header.length!=6)
13 throw exception_invalid_input(CODEPOINT,"MThd chunk is not 6 bytes long");
14 load_data(s);
15 }
16
17 void MThd_t::load_data(istream& s) {
18 fmt = read16(s);
19 ntracks = read16(s);
20 division = read16(s);
21 }
22
23 void MThd_t::save(ostream& s) const {
24 header.save(s);
25 write16(s,fmt);
26 write16(s,ntracks);
27 write16(s,division);
28 }
29
30 void MThd_t::dump(ostream& s) const {
31 std::ios::fmtflags ff = s.flags();
32 s.unsetf(std::ios::hex); s.setf(std::ios::dec);
33 s << " " << header << endl
34 << " fmt=" << fmt << ", " << ntracks << " track(s), division=" << division << endl;
35 s.flags(ff);
36 }
37
38}
diff --git a/lib/MTrk.cc b/lib/MTrk.cc
new file mode 100644
index 0000000..fa1e0f8
--- a/dev/null
+++ b/lib/MTrk.cc
@@ -0,0 +1,34 @@
1#include <algorithm>
2#include <iterator>
3#include <midillo/MTrk.h>
4#include <midillo/exception.h>
5
6namespace midillo {
7 using std::copy;
8 using std::ostream_iterator;
9 using std::endl;
10
11 void MTrk_t::load(istream& s) {
12 header.load(s);
13 if(header.id_number!=chunk_id_MTrk)
14 throw exception_unexpected_input(CODEPOINT,"MTrk chunk expected");
15 events.load(s);
16 }
17
18 void MTrk_t::save(ostream& s) const {
19 chunk_header_t h = header;
20 h.id_number = chunk_id_MTrk;
21 h.length = events.calculate_save_size();
22 h.save(s);
23 events.save(s);
24 }
25
26 void MTrk_t::dump(ostream& s) const {
27 s << " " << header << endl
28 << " ";
29 copy(
30 events.begin(), events.end(),
31 ostream_iterator<event_t>(s,"\n ") );
32 }
33
34}
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..de49c97
--- a/dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,15 @@
1lib_LTLIBRARIES = libmidillo.la
2
3INCLUDES = -I${top_srcdir}/include -I${top_srcdir}
4AM_CXXFLAGS = ${KONFORKA_CFLAGS}
5LDADD = ${KONFORKA_LIBS}
6
7libmidillo_la_SOURCES = \
8 util.cc \
9 SMF.cc \
10 chunk.cc \
11 MThd.cc MTrk.cc \
12 event.cc \
13 message.cc
14
15libmidillo_la_LDFLAGS = -version-info 0:0:0
diff --git a/lib/SMF.cc b/lib/SMF.cc
new file mode 100644
index 0000000..ba3179d
--- a/dev/null
+++ b/lib/SMF.cc
@@ -0,0 +1,62 @@
1#include <iostream>
2#include <fstream>
3#include <algorithm>
4#include <iterator>
5#include <midillo/SMF.h>
6
7namespace midillo {
8 using std::ifstream;
9 using std::ofstream;
10 using std::cin;
11 using std::cout;
12 using std::copy;
13 using std::ostream_iterator;
14 using std::endl;
15
16 void SMF_t::load(const char *f,bool stdinable) {
17 if(stdinable && !strcmp(f,"-")) {
18 load(cin);
19 }else{
20 ifstream s(f,std::ios::in|std::ios::binary);
21 load(s);
22 }
23 }
24
25 void SMF_t::load(istream& s) {
26 mthd.load(s);
27 tracks.resize(mthd.ntracks);
28 tracks_t::iterator i = tracks.begin();
29 for(int t=0;t<mthd.ntracks;++t,++i) {
30 i->load(s);
31 }
32 }
33
34 void SMF_t::save(const char *f,bool stdoutable) const {
35 if(stdoutable && !strcmp(f,"-")) {
36 save(cout);
37 }else{
38 ofstream s(f,std::ios::out|std::ios::trunc|std::ios::binary);
39 save(s);
40 }
41 }
42
43 void SMF_t::save(ostream& s) const {
44 mthd.save(s);
45 for(tracks_t::const_iterator i=tracks.begin();i!=tracks.end();++i) {
46 i->save(s);
47 }
48 }
49
50 void SMF_t::dump(ostream& s) const {
51 std::ios::fmtflags ff = s.flags();
52 s.unsetf(std::ios::hex); s.setf(std::ios::dec);
53 s
54 << "SMF with " << tracks.size() << " track(s)" << endl
55 << mthd << endl;
56 copy(
57 tracks.begin(), tracks.end(),
58 ostream_iterator<MTrk_t>(s,"\n") );
59 s.flags(ff);
60 }
61
62}
diff --git a/lib/chunk.cc b/lib/chunk.cc
new file mode 100644
index 0000000..7cc15ff
--- a/dev/null
+++ b/lib/chunk.cc
@@ -0,0 +1,31 @@
1#include <midillo/chunk.h>
2#include <midillo/util.h>
3#include <midillo/exception.h>
4
5namespace midillo {
6
7 void chunk_header_t::load(istream& s) {
8 s.read((char*)id_chars,sizeof(id_chars));
9 if(!s.good())
10 throw exception_input_error(CODEPOINT,"Error reading chunk header");
11 length = read32(s);
12 }
13
14 void chunk_header_t::save(ostream& s) const {
15 s.write((char*)id_chars,sizeof(id_chars));
16 if(!s.good())
17 throw exception_output_error(CODEPOINT,"Error writing chunk header");
18 write32(s,length);
19 }
20
21 void chunk_header_t::dump(ostream& s) const {
22 std::ios::fmtflags ff = s.flags();
23 s.unsetf(std::ios::hex); s.setf(std::ios::dec);
24 s
25 << id_chars[0] << id_chars[1]
26 << id_chars[2] << id_chars[3]
27 << " chunk of " << length << " byte(s)";
28 s.flags(ff);
29 }
30
31}
diff --git a/lib/event.cc b/lib/event.cc
new file mode 100644
index 0000000..39438fa
--- a/dev/null
+++ b/lib/event.cc
@@ -0,0 +1,58 @@
1#include <midillo/event.h>
2#include <midillo/util.h>
3
4namespace midillo {
5
6 void event_t::load(int& rs,istream& s) {
7 deltat = readVL(s);
8 message.load(rs,s);
9 }
10
11 void event_t::save(int& rs,ostream& s) const {
12 writeVL(s,deltat);
13 message.save(rs,s);
14 }
15
16 unsigned long event_t::calculate_save_size(int& rs) const {
17 return calcVLsize(deltat) + message.calculate_save_size(rs);
18 }
19
20 void event_t::dump(ostream& s) const {
21 std::ios::fmtflags ff = s.flags();
22 s.unsetf(std::ios::hex); s.setf(std::ios::dec);
23 s << "deltat=" << deltat << " [" << message << "]";
24 s.flags(ff);
25 }
26
27
28 events_t::iterator events_t::append_event() {
29 static event_t empty;
30 return insert(end(),empty);
31 }
32
33 void events_t::load(istream& s) {
34 int rs = -1;
35 for(;;) {
36 iterator i=append_event();
37 i->load(rs,s);
38 if(i->message.is_meta(meta_EOT))
39 break;
40 }
41 }
42
43 void events_t::save(ostream& s) const {
44 int rs = -1;
45 for(const_iterator i=begin();i!=end();++i) {
46 i->save(rs,s);
47 }
48 }
49
50 unsigned long events_t::calculate_save_size() const {
51 unsigned long rv = 0;
52 int rs = -1;
53 for(const_iterator i=begin();i!=end();++i) {
54 rv += i->calculate_save_size(rs);
55 }
56 return rv;
57 }
58}
diff --git a/lib/message.cc b/lib/message.cc
new file mode 100644
index 0000000..8f9e68a
--- a/dev/null
+++ b/lib/message.cc
@@ -0,0 +1,247 @@
1#include <algorithm>
2#include <iterator>
3#include <midillo/message.h>
4#include <midillo/util.h>
5#include <midillo/exception.h>
6
7namespace midillo {
8 using std::copy;
9 using std::ostream_iterator;
10
11 unsigned long message_t::calculate_save_size(int& rs) const {
12 unsigned long rv = 0;
13 if(status!=rs) {
14 ++rv;
15 rs = status;
16 }else if((status&status_event_bits)==status_system) {
17 rs = -1;
18 ++rv; // XXX: is it really needed?
19 }
20 switch(status&status_event_bits) {
21 case status_note_off:
22 case status_note_on:
23 case status_polyphonic_key_pressure: // aka status_aftertouch
24 case status_control_change:
25 case status_pitch_wheel_change:
26 rv += 2; break;
27 case status_program_change:
28 case status_channel_pressure:
29 ++rv; break;
30 case status_system:
31 switch(status&status_system_bits) {
32 case status_system_sysex:
33 case status_system_end_of_sysex:
34 rv += data.size()+1; break;
35 case status_system_MTC_quarter_frame:
36 case status_system_song_select:
37 ++rv; break;
38 case status_system_song_position_pointer:
39 rv += 2; break;
40 case status_system_tune_request:
41 case status_system_timing_clock: // aka status_system_midi_clock
42 case status_system_midi_tick:
43 case status_system_start: // aka status_system_midi_start
44 case status_system_stop: // aka status_system_midi_stop
45 case status_system_continue: // aka status_system_midi_continue
46 case status_system_active_sense:
47 break; /* XXX: ensure there is no data? */
48 case status_system_meta: // also reset, but not for the purpose of midi file
49 ++rv;
50 rv += calcVLsize(data.size());
51 rv += data.size();
52 break;
53 default:
54 throw exception(CODEPOINT,"Internal error");
55 break;
56 }
57 break;
58 default:
59 throw exception(CODEPOINT,"Internal error");
60 break;
61 }
62 return rv;
63 }
64
65 void message_t::save(int& rs,ostream& s) const {
66 if(status!=rs) {
67 s.put(status);
68 if(!s.good())
69 throw exception_output_error(CODEPOINT,"Error writing midi status byte");
70 rs = status;
71 }else if((status&status_event_bits)==status_system) {
72 rs = -1;
73 s.put(status); // XXX: is it really needed?
74 if(!s.good())
75 throw exception_output_error(CODEPOINT,"Error writing midi system status byte");
76 }
77 switch(status&status_event_bits) {
78 case status_note_off:
79 case status_note_on:
80 case status_polyphonic_key_pressure: // aka status_aftertouch
81 case status_control_change:
82 case status_pitch_wheel_change:
83 save_data(s,2); break;
84 case status_program_change:
85 case status_channel_pressure:
86 save_data(s,1); break;
87 case status_system:
88 switch(status&status_system_bits) {
89 case status_system_sysex:
90 case status_system_end_of_sysex:
91 save_sysex(s); break;
92 case status_system_MTC_quarter_frame:
93 case status_system_song_select:
94 save_data(s,1); break;
95 case status_system_song_position_pointer:
96 save_data(s,2); break;
97 case status_system_tune_request:
98 case status_system_timing_clock: // aka status_system_midi_clock
99 case status_system_midi_tick:
100 case status_system_start: // aka status_system_midi_start
101 case status_system_stop: // aka status_system_midi_stop
102 case status_system_continue: // aka status_system_midi_continue
103 case status_system_active_sense:
104 break; /* XXX: ensure there is no data? */
105 case status_system_meta: // also reset, but not for the purpose of midi file
106 s.put(meta_status&0xFF);
107 if(!s.good())
108 throw exception_output_error(CODEPOINT,"Error writing meta event");
109 writeVL(s,data.size());
110 save_data(s);
111 break;
112 default:
113 throw exception(CODEPOINT,"Internal error");
114 break;
115 }
116 break;
117 default:
118 throw exception(CODEPOINT,"Internal error");
119 break;
120 }
121 }
122
123 void message_t::load(int& rs,istream& s) {
124 data.clear();
125 status = s.get();
126 if(!s.good())
127 throw exception_input_error(CODEPOINT,"Error reading MIDI event status byte");
128 if(status&status_bit) {
129 if((status&status_event_bits)!=status_system)
130 rs = status;
131 }else{
132 if(rs<0)
133 throw exception_invalid_input(CODEPOINT,"Attempt to rely on the absent running status");
134 data.push_back(status);
135 status = rs;
136 }
137 switch(status&status_event_bits) {
138 case status_note_off:
139 case status_note_on:
140 case status_polyphonic_key_pressure: // a.k.a. status_aftertouch
141 case status_control_change:
142 case status_pitch_wheel_change:
143 load_data(s,2); break;
144 case status_program_change:
145 case status_channel_pressure:
146 load_data(s,1); break;
147 case status_system:
148 switch(status&status_system_bits) {
149 case status_system_sysex:
150 case status_system_end_of_sysex:
151 load_sysex(s); break;
152 case status_system_MTC_quarter_frame:
153 case status_system_song_select:
154 load_data(s,1); break;
155 case status_system_song_position_pointer:
156 load_data(s,2); break;
157 case status_system_tune_request:
158 case status_system_timing_clock: // a.k.a. status_system_midi_clock
159 case status_system_midi_tick:
160 case status_system_start: // a.k.a. status_system_midi_start
161 case status_system_stop: // a.k.a. status_system_midi_stop
162 case status_system_continue: // a.k.a. status_system_midi_continue
163 case status_system_active_sense:
164 break;
165 case status_system_meta: // also, reset, but not in midi files
166 {
167 meta_status = s.get();
168 if(!s.good())
169 throw exception_input_error(CODEPOINT,"Error reading meta event type");
170 int l = readVL(s);
171 load_data(s,l);
172 }
173 break;
174 default:
175 throw exception(CODEPOINT,"Internal error");
176 break;
177 }
178 break;
179 default:
180 throw exception(CODEPOINT,"Internal error");
181 break;
182 }
183 }
184
185 void message_t::save_data(ostream& s,int c) const {
186 if(c!=data.size())
187 throw exception(CODEPOINT,"Writing corrupt data");
188 save_data(s);
189 }
190
191 void message_t::save_data(ostream& s) const {
192 for(bytes_t::const_iterator i=data.begin();i!=data.end();++i)
193 s.put(*i);
194 if(!s.good())
195 throw exception_output_error(CODEPOINT,"Error writing MIDI message data");
196 }
197
198 void message_t::load_data(istream& s,int c) {
199 c -= data.size();
200 if(c<0)
201 throw exception(CODEPOINT,"Internal error");
202 while(c-- > 0) {
203 data.push_back(s.get());
204 if(!s.good())
205 throw exception_input_error(CODEPOINT,"Error reading MIDI data");
206 }
207 }
208
209 void message_t::save_sysex(ostream& s) const {
210 save_data(s);
211 s.put(0xF7); /* XXX: Or is it better to put it into data? */
212 if(!s.good())
213 throw exception_output_error(CODEPOINT,"Error writing sysex data");
214 }
215
216 void message_t::load_sysex(istream& s) {
217 int b = s.get();
218 if(!s.good())
219 throw exception_input_error(CODEPOINT,"Error reading sysex data");
220 assert(!(b&0x80)); // manufacturer ought to be 7 bit. not sure if it belongs here, it may well be continuation.
221 do {
222 data.push_back(b);
223 b = s.get();
224 if(!s.good())
225 throw exception_input_error(CODEPOINT,"Error reading sysex data");
226 }while(b!=0xF7);
227 }
228
229 void message_t::dump(ostream& s) const {
230 std::ios::fmtflags ff = s.flags();
231 int w = s.width(2);
232 s.unsetf(std::ios::dec); s.setf(std::ios::hex);
233 s << "status=" << status;
234 if(is_meta()) {
235 s << ", meta_status=" << meta_status;
236 }
237 if(!data.empty()) {
238 s << ", data: ";
239 copy(
240 data.begin(), data.end(),
241 ostream_iterator<int>(s," ") );
242 }
243 s.width(w);
244 s.flags(ff);
245 }
246
247}
diff --git a/lib/util.cc b/lib/util.cc
new file mode 100644
index 0000000..4bbe6f3
--- a/dev/null
+++ b/lib/util.cc
@@ -0,0 +1,96 @@
1#include <midillo/util.h>
2#include <midillo/exception.h>
3
4namespace midillo {
5
6 unsigned long read32(istream& s) {
7 unsigned long rv = 0;
8 for(int t=0;t<4;++t) {
9 rv<<=8;
10 rv|=0xFF&s.get();
11 if(!s.good())
12 throw exception_input_error(CODEPOINT,"Input error");
13 }
14 return rv;
15 }
16
17 unsigned int read16(istream& s) {
18 unsigned int rv = 0;
19 for(int t=0;t<2;++t) {
20 rv<<=8;
21 rv|=0xFF&s.get();
22 if(!s.good())
23 throw exception_input_error(CODEPOINT,"Input error");
24 }
25 return rv;
26 }
27
28 unsigned long readVL(istream& s) {
29 unsigned long rv=s.get();
30 if(!s.good())
31 throw exception_input_error(CODEPOINT,"Error reading VLQ");
32 int b;
33 if(rv&0x80) {
34 rv&=0x7F;
35 do {
36 rv = (rv<<7)|( (b=s.get())&0x7F );
37 if(!s.good())
38 throw exception_input_error(CODEPOINT,"Error reading VLQ");
39 }while(b&0x80);
40 }
41 return rv;
42 }
43
44 void write32(ostream& s,unsigned long d) {
45 for(int t=3;t>=0;--t) {
46 s.put(((const char*)&d)[t]);
47 if(!s.good())
48 throw exception_output_error(CODEPOINT,"Output error");
49 }
50 }
51
52 void write16(ostream& s,unsigned int d) {
53 for(int t=1;t>=0;--t) {
54 s.put(((const char*)&d)[t]);
55 if(!s.good())
56 throw exception_output_error(CODEPOINT,"Output error");
57 }
58 }
59
60 void writeVL(ostream& s,unsigned long d) {
61 // TODO: I don't think this is perfectly written code
62 unsigned long tmp = d&0x7F;
63 while(d>>=7) {
64 tmp<<=8;
65 tmp |=((d&0x7F)|0x80);
66 }
67 for(;;) {
68 s.put(tmp&0xFF);
69 if(!s.good())
70 throw exception_output_error(CODEPOINT,"Error writing VLQ");
71 if(tmp&0x80)
72 tmp>>=8;
73 else
74 break;
75 }
76 }
77
78 unsigned long calcVLsize(unsigned long d) {
79 unsigned long rv = 0;
80 // TODO: imperfect code revisited
81 unsigned long tmp = d&0x7F;
82 while(d>>=7) {
83 tmp<<=8;
84 tmp |=((d&0x7F)|0x80);
85 }
86 for(;;) {
87 ++rv;
88 if(tmp&0x80)
89 tmp>>=8;
90 else
91 break;
92 }
93 return rv;
94 }
95
96}