summaryrefslogtreecommitdiffabout
path: root/lib/message.cc
blob: 8f9e68aba21358d1cb84647e8683f8da7199acf2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#include <algorithm>
#include <iterator>
#include <midillo/message.h>
#include <midillo/util.h>
#include <midillo/exception.h>

namespace midillo {
    using std::copy;
    using std::ostream_iterator;

    unsigned long message_t::calculate_save_size(int& rs) const {
	unsigned long rv = 0;
	if(status!=rs) {
	    ++rv;
	    rs = status;
	}else if((status&status_event_bits)==status_system) {
	    rs = -1;
	    ++rv; // XXX: is it really needed?
	}
	switch(status&status_event_bits) {
	    case status_note_off:
	    case status_note_on:
	    case status_polyphonic_key_pressure: // aka status_aftertouch
	    case status_control_change:
	    case status_pitch_wheel_change:
		rv += 2; break;
	    case status_program_change:
	    case status_channel_pressure:
		++rv; break;
	    case status_system:
		switch(status&status_system_bits) {
		    case status_system_sysex:
		    case status_system_end_of_sysex:
			rv += data.size()+1; break;
		    case status_system_MTC_quarter_frame:
		    case status_system_song_select:
			++rv; break;
		    case status_system_song_position_pointer:
			rv += 2; break;
		    case status_system_tune_request:
		    case status_system_timing_clock: // aka status_system_midi_clock
		    case status_system_midi_tick:
		    case status_system_start: // aka status_system_midi_start
		    case status_system_stop: // aka status_system_midi_stop
		    case status_system_continue: // aka status_system_midi_continue
		    case status_system_active_sense:
			break; /* XXX: ensure there is no data? */
		    case status_system_meta: // also reset, but not for the purpose of midi file
			++rv;
			rv += calcVLsize(data.size());
			rv += data.size();
			break;
		    default:
			throw exception(CODEPOINT,"Internal error");
			break;
		}
		break;
	    default:
		throw exception(CODEPOINT,"Internal error");
		break;
	}
	return rv;
    }

    void message_t::save(int& rs,ostream& s) const {
	if(status!=rs) {
	    s.put(status);
	    if(!s.good())
		throw exception_output_error(CODEPOINT,"Error writing midi status byte");
	    rs = status;
	}else if((status&status_event_bits)==status_system) {
	    rs = -1;
	    s.put(status); // XXX: is it really needed?
	    if(!s.good())
		throw exception_output_error(CODEPOINT,"Error writing midi system status byte");
	}
	switch(status&status_event_bits) {
	    case status_note_off:
	    case status_note_on:
	    case status_polyphonic_key_pressure: // aka status_aftertouch
	    case status_control_change:
	    case status_pitch_wheel_change:
		save_data(s,2); break;
	    case status_program_change:
	    case status_channel_pressure:
		save_data(s,1); break;
	    case status_system:
		switch(status&status_system_bits) {
		    case status_system_sysex:
		    case status_system_end_of_sysex:
			save_sysex(s); break;
		    case status_system_MTC_quarter_frame:
		    case status_system_song_select:
			save_data(s,1); break;
		    case status_system_song_position_pointer:
			save_data(s,2); break;
		    case status_system_tune_request:
		    case status_system_timing_clock: // aka status_system_midi_clock
		    case status_system_midi_tick:
		    case status_system_start: // aka status_system_midi_start
		    case status_system_stop: // aka status_system_midi_stop
		    case status_system_continue: // aka status_system_midi_continue
		    case status_system_active_sense:
			break; /* XXX: ensure there is no data? */
		    case status_system_meta: // also reset, but not for the purpose of midi file
			s.put(meta_status&0xFF);
			if(!s.good())
			    throw exception_output_error(CODEPOINT,"Error writing meta event");
			writeVL(s,data.size());
			save_data(s);
			break;
		    default:
			throw exception(CODEPOINT,"Internal error");
			break;
		}
		break;
	    default:
		throw exception(CODEPOINT,"Internal error");
		break;
	}
    }

    void message_t::load(int& rs,istream& s) {
	data.clear();
	status = s.get();
	if(!s.good())
	    throw exception_input_error(CODEPOINT,"Error reading MIDI event status byte");
	if(status&status_bit) {
	    if((status&status_event_bits)!=status_system)
		rs = status;
	}else{
	    if(rs<0)
		throw exception_invalid_input(CODEPOINT,"Attempt to rely on the absent running status");
	    data.push_back(status);
	    status = rs;
	}
	switch(status&status_event_bits) {
	    case status_note_off:
	    case status_note_on:
	    case status_polyphonic_key_pressure: // a.k.a. status_aftertouch
	    case status_control_change:
	    case status_pitch_wheel_change:
		load_data(s,2); break;
	    case status_program_change:
	    case status_channel_pressure:
		load_data(s,1); break;
	    case status_system:
		switch(status&status_system_bits) {
		    case status_system_sysex:
		    case status_system_end_of_sysex:
			load_sysex(s); break;
		    case status_system_MTC_quarter_frame:
		    case status_system_song_select:
			load_data(s,1); break;
		    case status_system_song_position_pointer:
			load_data(s,2); break;
		    case status_system_tune_request:
		    case status_system_timing_clock: // a.k.a. status_system_midi_clock
		    case status_system_midi_tick:
		    case status_system_start: // a.k.a. status_system_midi_start
		    case status_system_stop: // a.k.a. status_system_midi_stop
		    case status_system_continue: // a.k.a. status_system_midi_continue
		    case status_system_active_sense:
			break;
		    case status_system_meta: // also, reset, but not in midi files
			{
			    meta_status = s.get();
			    if(!s.good())
				throw exception_input_error(CODEPOINT,"Error reading meta event type");
			    int l = readVL(s);
			    load_data(s,l);
			}
			break;
		    default:
			throw exception(CODEPOINT,"Internal error");
			break;
		}
		break;
	    default:
		throw exception(CODEPOINT,"Internal error");
		break;
	}
    }

    void message_t::save_data(ostream& s,int c) const {
	if(c!=data.size())
	    throw exception(CODEPOINT,"Writing corrupt data");
	save_data(s);
    }

    void message_t::save_data(ostream& s) const {
	for(bytes_t::const_iterator i=data.begin();i!=data.end();++i)
	    s.put(*i);
	if(!s.good())
	    throw exception_output_error(CODEPOINT,"Error writing MIDI message data");
    }

    void message_t::load_data(istream& s,int c) {
	c -= data.size();
	if(c<0)
	    throw exception(CODEPOINT,"Internal error");
	while(c-- > 0) {
	    data.push_back(s.get());
	    if(!s.good())
		throw exception_input_error(CODEPOINT,"Error reading MIDI data");
	}
    }

    void message_t::save_sysex(ostream& s) const {
	save_data(s);
	s.put(0xF7); /* XXX: Or is it better to put it into data? */
	if(!s.good())
	    throw exception_output_error(CODEPOINT,"Error writing sysex data");
    }

    void message_t::load_sysex(istream& s) {
	int b = s.get();
	if(!s.good())
	    throw exception_input_error(CODEPOINT,"Error reading sysex data");
	assert(!(b&0x80)); // manufacturer ought to be 7 bit. not sure if it belongs here, it may well be continuation.
	do {
	    data.push_back(b);
	    b = s.get();
	    if(!s.good())
		throw exception_input_error(CODEPOINT,"Error reading sysex data");
	}while(b!=0xF7);
    }

    void message_t::dump(ostream& s) const {
	std::ios::fmtflags ff = s.flags();
	int w = s.width(2);
	s.unsetf(std::ios::dec); s.setf(std::ios::hex);
	s << "status=" << status;
	if(is_meta()) {
	    s << ", meta_status=" << meta_status;
	}
	if(!data.empty()) {
	    s << ", data: ";
	    copy(
		    data.begin(), data.end(),
		    ostream_iterator<int>(s," ") );
	}
	s.width(w);
	s.flags(ff);
    }

}