2 Copyright (C) 2008 Paul Davis
3 Written by Dave Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <glibmm/miscutils.h>
24 #include <ardour/smf_reader.h>
25 #include <ardour/midi_events.h>
26 #include <ardour/midi_util.h>
33 SMFReader::SMFReader(const std::string filename)
35 //, _unit(TimeUnit::BEATS, 192)
40 if (filename.length() > 0) {
46 SMFReader::~SMFReader()
54 SMFReader::open(const string& filename) throw (logic_error, UnsupportedTime)
57 throw logic_error("Attempt to start new read while write in progress.");
59 cout << "Opening SMF file " << filename << " for reading." << endl;
61 _fd = fopen(filename.c_str(), "r+");
64 // Read type (bytes 8..9)
65 fseek(_fd, 0, SEEK_SET);
68 fread(mthd, 1, 4, _fd);
69 if (strcmp(mthd, "MThd")) {
70 cerr << filename << " is not an SMF file, aborting." << endl;
76 // Read type (bytes 8..9)
77 fseek(_fd, 8, SEEK_SET);
79 fread(&type_be, 2, 1, _fd);
80 _type = GUINT16_FROM_BE(type_be);
82 // Read number of tracks (bytes 10..11)
83 uint16_t num_tracks_be = 0;
84 fread(&num_tracks_be, 2, 1, _fd);
85 _num_tracks = GUINT16_FROM_BE(num_tracks_be);
87 // Read PPQN (bytes 12..13)
90 fread(&ppqn_be, 2, 1, _fd);
91 _ppqn = GUINT16_FROM_BE(ppqn_be);
93 // TODO: Absolute (SMPTE seconds) time support
94 if ((_ppqn & 0x8000) != 0)
95 throw UnsupportedTime();
97 //_unit = TimeUnit::beats(_ppqn);
108 /** Seek to the start of a given track, starting from 1.
109 * Returns true if specified track was found.
112 SMFReader::seek_to_track(unsigned track) throw (std::logic_error)
115 throw logic_error("Seek to track 0 out of range (must be >= 1)");
118 throw logic_error("Attempt to seek to track on unopened SMF file.");
120 unsigned track_pos = 0;
122 fseek(_fd, 14, SEEK_SET);
125 uint32_t chunk_size = 0;
128 fread(id, 1, 4, _fd);
130 if (!strcmp(id, "MTrk")) {
132 //std::cerr << "Found track " << track_pos << endl;
134 std::cerr << "Unknown chunk ID " << id << endl;
137 uint32_t chunk_size_be;
138 fread(&chunk_size_be, 4, 1, _fd);
139 chunk_size = GUINT32_FROM_BE(chunk_size_be);
141 if (track_pos == track)
144 fseek(_fd, chunk_size, SEEK_CUR);
147 if (!feof(_fd) && track_pos == track) {
149 _track_size = chunk_size;
157 /** Read an event from the current position in file.
159 * File position MUST be at the beginning of a delta time, or this will die very messily.
160 * ev.buffer must be of size ev.size, and large enough for the event. The returned event
161 * will have it's time field set to it's delta time (so it's the caller's responsibility
162 * to keep track of delta time, even for ignored events).
164 * Returns event length (including status byte) on success, 0 if event was
165 * skipped (eg a meta event), or -1 on EOF (or end of track).
167 * If @a buf is not large enough to hold the event, 0 will be returned, but ev_size
168 * set to the actual size of the event.
171 SMFReader::read_event(size_t buf_len,
174 uint32_t* delta_time)
175 throw (std::logic_error, PrematureEOF, CorruptFile)
178 throw logic_error("Attempt to read from unopened SMF file");
180 if (!_fd || feof(_fd)) {
189 //cerr.flags(ios::hex);
190 //cerr << "SMF - Reading event at offset 0x" << ftell(_fd) << endl;
191 //cerr.flags(ios::dec);
193 // Running status state
194 static uint8_t last_status = 0;
195 static uint32_t last_size = 0;
197 *delta_time = read_var_len();
198 int status = fgetc(_fd);
200 throw PrematureEOF();
201 else if (status > 0xFF)
205 if (last_status == 0)
207 status = last_status;
208 *ev_size = last_size;
209 fseek(_fd, -1, SEEK_CUR);
210 //cerr << "RUNNING STATUS, size = " << *ev_size << endl;
212 last_status = status;
213 *ev_size = midi_event_size(status) + 1;
214 last_size = *ev_size;
215 //cerr << "NORMAL STATUS, size = " << *ev_size << endl;
218 buf[0] = (uint8_t)status;
220 if (status == 0xFF) {
223 throw PrematureEOF();
224 uint8_t type = fgetc(_fd);
225 const uint32_t size = read_var_len();
226 /*cerr.flags(ios::hex);
227 cerr << "SMF - meta 0x" << (int)type << ", size = ";
228 cerr.flags(ios::dec);
229 cerr << size << endl;*/
231 if ((uint8_t)type == 0x2F) {
232 //cerr << "SMF - hit EOT" << endl;
233 return -1; // we hit the logical EOF anyway...
235 fseek(_fd, size, SEEK_CUR);
240 if (*ev_size > buf_len || *ev_size == 0 || feof(_fd)) {
241 //cerr << "Skipping event" << endl;
242 // Skip event, return 0
243 fseek(_fd, *ev_size - 1, SEEK_CUR);
246 // Read event, return size
249 fread(buf+1, 1, *ev_size - 1, _fd);
251 if ((buf[0] & 0xF0) == 0x90 && buf[2] == 0) {
252 buf[0] = (0x80 | (buf[0] & 0x0F));
272 SMFReader::read_var_len() const throw(PrematureEOF)
275 throw PrematureEOF();
280 if ( (value = getc(_fd)) & 0x80 ) {
284 throw PrematureEOF();
285 value = (value << 7) + ((c = getc(_fd)) & 0x7F);
293 } // namespace ARDOUR