Fixes for GCC 4.3.
[ardour.git] / libs / ardour / smf_reader.cc
1 /*
2     Copyright (C) 2008 Paul Davis 
3     Written by Dave Robillard
4
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.
9
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.
14
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.
18 */
19
20 #include <cstring>
21 #include <cstdio>
22 #include <cassert>
23 #include <iostream>
24 #include <glibmm/miscutils.h>
25 #include <midi++/events.h>
26
27 #include <ardour/smf_reader.h>
28 #include <ardour/midi_util.h>
29
30 using namespace std;
31
32 namespace ARDOUR {
33
34
35 SMFReader::SMFReader(const std::string filename)
36         : _fd(NULL)
37         //, _unit(TimeUnit::BEATS, 192)
38         , _ppqn(0)
39         , _track(0)
40         , _track_size(0)
41 {
42         if (filename.length() > 0) {
43                 open(filename);
44         }
45 }
46
47
48 SMFReader::~SMFReader()
49 {
50         if (_fd)
51                 close();
52 }
53
54
55 bool
56 SMFReader::open(const string& filename) throw (logic_error, UnsupportedTime)
57 {
58         if (_fd)
59                 throw logic_error("Attempt to start new read while write in progress.");
60
61         cout << "Opening SMF file " << filename << " for reading." << endl;
62
63         _fd = fopen(filename.c_str(), "r+");
64
65         if (_fd) {
66                 // Read type (bytes 8..9)
67                 fseek(_fd, 0, SEEK_SET);
68                 char mthd[5];
69                 mthd[4] = '\0';
70                 fread(mthd, 1, 4, _fd);
71                 if (strcmp(mthd, "MThd")) {
72                         cerr << filename << " is not an SMF file, aborting." << endl;
73                         fclose(_fd);
74                         _fd = NULL;
75                         return false;
76                 }
77                 
78                 // Read type (bytes 8..9)
79                 fseek(_fd, 8, SEEK_SET);
80                 uint16_t type_be = 0;
81                 fread(&type_be, 2, 1, _fd);
82                 _type = GUINT16_FROM_BE(type_be);
83
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);
88
89                 // Read PPQN (bytes 12..13)
90
91                 uint16_t ppqn_be = 0;
92                 fread(&ppqn_be, 2, 1, _fd);
93                 _ppqn = GUINT16_FROM_BE(ppqn_be);
94                 
95                 // TODO: Absolute (SMPTE seconds) time support
96                 if ((_ppqn & 0x8000) != 0)
97                         throw UnsupportedTime();
98
99                 //_unit = TimeUnit::beats(_ppqn);
100
101                 seek_to_track(1);
102                 
103                 return true;
104         } else {
105                 return false;
106         }
107 }
108
109         
110 /** Seek to the start of a given track, starting from 1.
111  * Returns true if specified track was found.
112  */
113 bool
114 SMFReader::seek_to_track(unsigned track) throw (std::logic_error)
115 {
116         if (track == 0)
117                 throw logic_error("Seek to track 0 out of range (must be >= 1)");
118
119         if (!_fd)
120                 throw logic_error("Attempt to seek to track on unopened SMF file.");
121
122         unsigned track_pos = 0;
123
124         fseek(_fd, 14, SEEK_SET);
125         char id[5];
126         id[4] = '\0';
127         uint32_t chunk_size = 0;
128
129         while (!feof(_fd)) {
130                 fread(id, 1, 4, _fd);
131
132                 if (!strcmp(id, "MTrk")) {
133                         ++track_pos;
134                         //std::cerr << "Found track " << track_pos << endl;
135                 } else {
136                         std::cerr << "Unknown chunk ID " << id << endl;
137                 }
138
139                 uint32_t chunk_size_be;
140                 fread(&chunk_size_be, 4, 1, _fd);
141                 chunk_size = GUINT32_FROM_BE(chunk_size_be);
142
143                 if (track_pos == track)
144                         break;
145
146                 fseek(_fd, chunk_size, SEEK_CUR);
147         }
148
149         if (!feof(_fd) && track_pos == track) {
150                 _track = track;
151                 _track_size = chunk_size;
152                 return true;
153         } else {
154                 return false;
155         }
156 }
157
158         
159 /** Read an event from the current position in file.
160  *
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).
165  *
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).
168  *
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.
171  */
172 int
173 SMFReader::read_event(size_t    buf_len,
174                       uint8_t*  buf,
175                       uint32_t* ev_size,
176                       uint32_t* delta_time)
177                 throw (std::logic_error, PrematureEOF, CorruptFile)
178 {
179         if (_track == 0)
180                 throw logic_error("Attempt to read from unopened SMF file");
181
182         if (!_fd || feof(_fd)) {
183                 return -1;
184         }
185
186         assert(buf_len > 0);
187         assert(buf);
188         assert(ev_size);
189         assert(delta_time);
190
191         //cerr.flags(ios::hex);
192         //cerr << "SMF - Reading event at offset 0x" << ftell(_fd) << endl;
193         //cerr.flags(ios::dec);
194
195         // Running status state
196         static uint8_t  last_status = 0;
197         static uint32_t last_size   = 0;
198
199         *delta_time = read_var_len(_fd);
200         int status = fgetc(_fd);
201         if (status == EOF)
202                 throw PrematureEOF();
203         else if (status > 0xFF)
204                 throw CorruptFile();
205
206         if (status < 0x80) {
207                 if (last_status == 0)
208                         throw CorruptFile();
209                 status = last_status;
210                 *ev_size = last_size;
211                 fseek(_fd, -1, SEEK_CUR);
212                 //cerr << "RUNNING STATUS, size = " << *ev_size << endl;
213         } else {
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;
218         }
219
220         buf[0] = (uint8_t)status;
221
222         if (status == 0xFF) {
223                 *ev_size = 0;
224                 if (feof(_fd))
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;*/
232
233                 if ((uint8_t)type == 0x2F) {
234                         //cerr << "SMF - hit EOT" << endl;
235                         return -1; // we hit the logical EOF anyway...
236                 } else {
237                         fseek(_fd, size, SEEK_CUR);
238                         return 0;
239                 }
240         }
241
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);
246                 return 0;
247         } else {
248                 // Read event, return size
249                 if (ferror(_fd))
250                         throw CorruptFile();
251                 fread(buf+1, 1, *ev_size - 1, _fd);
252         
253                 if ((buf[0] & 0xF0) == 0x90 && buf[2] == 0) {
254                         buf[0] = (0x80 | (buf[0] & 0x0F));
255                         buf[2] = 0x40;
256                 }
257                 
258                 return *ev_size;
259         }
260 }
261
262
263 void
264 SMFReader::close()
265 {
266         if (_fd)
267                 fclose(_fd);
268
269         _fd = NULL;
270 }
271
272
273 uint32_t
274 SMFReader::read_var_len(FILE* fd) throw (PrematureEOF)
275 {
276         if (feof(fd))
277                 throw PrematureEOF();
278
279         uint32_t value;
280         uint8_t  c;
281
282         if ( (value = getc(fd)) & 0x80 ) {
283                 value &= 0x7F;
284                 do {
285                         if (feof(fd))
286                                 throw PrematureEOF();
287                         value = (value << 7) + ((c = getc(fd)) & 0x7F);
288                 } while (c & 0x80);
289         }
290
291         return value;
292 }
293
294
295 } // namespace ARDOUR
296