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