1 /* This file is part of Evoral.
2 * Copyright(C) 2008 Dave Robillard <http://drobilla.net>
3 * Copyright(C) 2000-2008 Paul Davis
5 * Evoral is free software; you can redistribute it and/or modify it under the
6 * terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or(at your option) any later
10 * Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include <glibmm/miscutils.h>
25 #include "evoral/midi_util.h"
26 #include "evoral/SMFReader.hpp"
33 SMFReader::SMFReader(const string& filename)
39 if (filename.length() > 0) {
45 SMFReader::~SMFReader()
53 SMFReader::open(const string& filename) throw (logic_error, UnsupportedTime)
56 throw logic_error("Attempt to start new read while write in progress.");
58 cout << "Opening SMF file " << filename << " for reading." << endl;
60 _fd = fopen(filename.c_str(), "r+");
63 // Read type (bytes 8..9)
64 fseek(_fd, 0, SEEK_SET);
67 fread(mthd, 1, 4, _fd);
68 if (strcmp(mthd, "MThd")) {
69 cerr << filename << " is not an SMF file, aborting." << endl;
75 // Read type (bytes 8..9)
76 fseek(_fd, 8, SEEK_SET);
78 fread(&type_be, 2, 1, _fd);
79 _type = GUINT16_FROM_BE(type_be);
81 // Read number of tracks (bytes 10..11)
82 uint16_t num_tracks_be = 0;
83 fread(&num_tracks_be, 2, 1, _fd);
84 _num_tracks = GUINT16_FROM_BE(num_tracks_be);
86 // Read PPQN (bytes 12..13)
88 fread(&ppqn_be, 2, 1, _fd);
89 _ppqn = GUINT16_FROM_BE(ppqn_be);
91 // TODO: Absolute (SMPTE seconds) time support
92 if ((_ppqn & 0x8000) != 0)
93 throw UnsupportedTime();
104 /** Seek to the start of a given track, starting from 1.
105 * Returns true if specified track was found.
108 SMFReader::seek_to_track(unsigned track) throw (std::logic_error)
111 throw logic_error("Seek to track 0 out of range (must be >= 1)");
114 throw logic_error("Attempt to seek to track on unopened SMF file.");
116 unsigned track_pos = 0;
118 fseek(_fd, 14, SEEK_SET);
121 uint32_t chunk_size = 0;
124 fread(id, 1, 4, _fd);
126 if (!strcmp(id, "MTrk")) {
129 std::cerr << "Unknown chunk ID " << id << endl;
132 uint32_t chunk_size_be;
133 fread(&chunk_size_be, 4, 1, _fd);
134 chunk_size = GUINT32_FROM_BE(chunk_size_be);
136 if (track_pos == track)
139 fseek(_fd, chunk_size, SEEK_CUR);
142 if (!feof(_fd) && track_pos == track) {
144 _track_size = chunk_size;
152 /** Read an event from the current position in file.
154 * File position MUST be at the beginning of a delta time, or this will die very messily.
155 * ev.buffer must be of size ev.size, and large enough for the event. The returned event
156 * will have it's time field set to it's delta time (so it's the caller's responsibility
157 * to keep track of delta time, even for ignored events).
159 * Returns event length (including status byte) on success, 0 if event was
160 * skipped (eg a meta event), or -1 on EOF (or end of track).
162 * If @a buf is not large enough to hold the event, 0 will be returned, but ev_size
163 * set to the actual size of the event.
166 SMFReader::read_event(size_t buf_len,
169 uint32_t* delta_time)
170 throw (std::logic_error, PrematureEOF, CorruptFile)
173 throw logic_error("Attempt to read from unopened SMF file");
175 if (!_fd || feof(_fd)) {
184 // Running status state
185 static uint8_t last_status = 0;
186 static uint32_t last_size = 0;
188 *delta_time = read_var_len(_fd);
189 int status = fgetc(_fd);
191 throw PrematureEOF();
192 else if (status > 0xFF)
196 if (last_status == 0)
198 status = last_status;
199 *ev_size = last_size;
200 fseek(_fd, -1, SEEK_CUR);
202 last_status = status;
203 *ev_size = midi_event_size(status);
204 last_size = *ev_size;
207 buf[0] = (uint8_t)status;
209 if (status == 0xFF) {
212 throw PrematureEOF();
213 uint8_t type = fgetc(_fd);
214 const uint32_t size = read_var_len(_fd);
215 /*cerr.flags(ios::hex);
216 cerr << "SMF - meta 0x" << (int)type << ", size = ";
217 cerr.flags(ios::dec);
218 cerr << size << endl;*/
220 if ((uint8_t)type == 0x2F) {
221 return -1; // we hit the logical EOF anyway...
223 fseek(_fd, size, SEEK_CUR);
228 if (*ev_size > buf_len || *ev_size == 0 || feof(_fd)) {
229 //cerr << "SMF - Skipping event" << endl;
230 // Skip event, return 0
231 fseek(_fd, *ev_size - 1, SEEK_CUR);
234 // Read event, return size
238 fread(buf+1, 1, *ev_size - 1, _fd);
240 if ((buf[0] & 0xF0) == 0x90 && buf[2] == 0) {
241 buf[0] = (0x80 | (buf[0] & 0x0F));
261 SMFReader::read_var_len(FILE* fd) throw (PrematureEOF)
264 throw PrematureEOF();
269 if ( (value = getc(fd)) & 0x80 ) {
273 throw PrematureEOF();
274 value = (value << 7) + ((c = getc(fd)) & 0x7F);
282 } // namespace Evoral