summaryrefslogtreecommitdiffabout
path: root/lib/message.cc
Unidiff
Diffstat (limited to 'lib/message.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/message.cc247
1 files changed, 247 insertions, 0 deletions
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}