-rw-r--r-- | lib/message.cc | 247 |
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 | |||
7 | namespace 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 | } | ||