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.
24 #include <glibmm/miscutils.h>
25 #include <midi++/events.h>
27 #include <ardour/smf_reader.h>
28 #include <ardour/midi_util.h>
35 SMFReader::SMFReader(const std::string filename)
37 //, _unit(TimeUnit::BEATS, 192)
42 if (filename.length() > 0) {
48 SMFReader::~SMFReader()
56 SMFReader::open(const string& filename) throw (logic_error, UnsupportedTime)
59 throw logic_error("Attempt to start new read while write in progress.");
61 cout << "Opening SMF file " << filename << " for reading." << endl;
63 _fd = fopen(filename.c_str(), "r+");
66 // Read type (bytes 8..9)
67 fseek(_fd, 0, SEEK_SET);
70 fread(mthd, 1, 4, _fd);
71 if (strcmp(mthd, "MThd")) {
72 cerr << filename << " is not an SMF file, aborting." << endl;
78 // Read type (bytes 8..9)
79 fseek(_fd, 8, SEEK_SET);
81 fread(&type_be, 2, 1, _fd);
82 _type = GUINT16_FROM_BE(type_be);
84 // Read number of tracks (bytes 10..11)
85 uint16_t num_tracks_be = 0;
86 fread(&num_tracks_be, 2, 1, _fd);
87 _num_tracks = GUINT16_FROM_BE(num_tracks_be);
89 // Read PPQN (bytes 12..13)
92 fread(&ppqn_be, 2, 1, _fd);
93 _ppqn = GUINT16_FROM_BE(ppqn_be);
95 // TODO: Absolute (SMPTE seconds) time support
96 if ((_ppqn & 0x8000) != 0)
97 throw UnsupportedTime();
99 //_unit = TimeUnit::beats(_ppqn);
110 /** Seek to the start of a given track, starting from 1.
111 * Returns true if specified track was found.
114 SMFReader::seek_to_track(unsigned track) throw (std::logic_error)
117 throw logic_error("Seek to track 0 out of range (must be >= 1)");
120 throw logic_error("Attempt to seek to track on unopened SMF file.");
122 unsigned track_pos = 0;
124 fseek(_fd, 14, SEEK_SET);
127 uint32_t chunk_size = 0;
130 fread(id, 1, 4, _fd);
132 if (!strcmp(id, "MTrk")) {
134 //std::cerr << "Found track " << track_pos << endl;
136 std::cerr << "Unknown chunk ID " << id << endl;
139 uint32_t chunk_size_be;
140 fread(&chunk_size_be, 4, 1, _fd);
141 chunk_size = GUINT32_FROM_BE(chunk_size_be);
143 if (track_pos == track)
146 fseek(_fd, chunk_size, SEEK_CUR);
149 if (!feof(_fd) && track_pos == track) {
151 _track_size = chunk_size;
159 /** Read an event from the current position in file.
161 * File position MUST be at the beginning of a delta time, or this will die very messily.
162 * ev.buffer must be of size ev.size, and large enough for the event. The returned event
163 * will have it's time field set to it's delta time (so it's the caller's responsibility
164 * to keep track of delta time, even for ignored events).
166 * Returns event length (including status byte) on success, 0 if event was
167 * skipped (eg a meta event), or -1 on EOF (or end of track).
169 * If @a buf is not large enough to hold the event, 0 will be returned, but ev_size
170 * set to the actual size of the event.
173 SMFReader::read_event(size_t buf_len,
176 uint32_t* delta_time)
177 throw (std::logic_error, PrematureEOF, CorruptFile)
180 throw logic_error("Attempt to read from unopened SMF file");
182 if (!_fd || feof(_fd)) {
191 //cerr.flags(ios::hex);
192 //cerr << "SMF - Reading event at offset 0x" << ftell(_fd) << endl;
193 //cerr.flags(ios::dec);
195 // Running status state
196 static uint8_t last_status = 0;
197 static uint32_t last_size = 0;
199 *delta_time = read_var_len(_fd);
200 int status = fgetc(_fd);
202 throw PrematureEOF();
203 else if (status > 0xFF)
207 if (last_status == 0)
209 status = last_status;
210 *ev_size = last_size;
211 fseek(_fd, -1, SEEK_CUR);
212 //cerr << "RUNNING STATUS, size = " << *ev_size << endl;
214 last_status = status;
215 *ev_size = midi_event_size(status) + 1;
216 last_size = *ev_size;
217 //cerr << "NORMAL STATUS, size = " << *ev_size << endl;
220 buf[0] = (uint8_t)status;
222 if (status == 0xFF) {
225 throw PrematureEOF();
226 uint8_t type = fgetc(_fd);
227 const uint32_t size = read_var_len(_fd);
228 /*cerr.flags(ios::hex);
229 cerr << "SMF - meta 0x" << (int)type << ", size = ";
230 cerr.flags(ios::dec);
231 cerr << size << endl;*/
233 if ((uint8_t)type == 0x2F) {
234 //cerr << "SMF - hit EOT" << endl;
235 return -1; // we hit the logical EOF anyway...
237 fseek(_fd, size, SEEK_CUR);
242 if (*ev_size > buf_len || *ev_size == 0 || feof(_fd)) {
243 //cerr << "Skipping event" << endl;
244 // Skip event, return 0
245 fseek(_fd, *ev_size - 1, SEEK_CUR);
248 // Read event, return size
251 fread(buf+1, 1, *ev_size - 1, _fd);
253 if ((buf[0] & 0xF0) == 0x90 && buf[2] == 0) {
254 buf[0] = (0x80 | (buf[0] & 0x0F));
274 SMFReader::read_var_len(FILE* fd) throw (PrematureEOF)
277 throw PrematureEOF();
282 if ( (value = getc(fd)) & 0x80 ) {
286 throw PrematureEOF();
287 value = (value << 7) + ((c = getc(fd)) & 0x7F);
295 } // namespace ARDOUR