ardour
SMFReader.cpp
Go to the documentation of this file.
1 /* This file is part of Evoral.
2  * Copyright(C) 2008 David Robillard <http://drobilla.net>
3  * Copyright(C) 2000-2008 Paul Davis
4  *
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
8  * version.
9  *
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.
13  *
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
17  */
18 
19 #include <cstring>
20 #include <cstdio>
21 #include <cassert>
22 #include <iostream>
23 #include <glibmm/miscutils.h>
24 
25 #include "evoral/midi_util.h"
26 #include "evoral/SMFReader.hpp"
27 
28 using namespace std;
29 
30 namespace Evoral {
31 
32 
33 SMFReader::SMFReader(const string& filename)
34  : _fd(NULL)
35  , _ppqn(0)
36  , _track(0)
37  , _track_size(0)
38 {
39  if (filename.length() > 0) {
40  open(filename);
41  }
42 }
43 
44 
46 {
47  if (_fd)
48  close();
49 }
50 
51 
52 bool
53 SMFReader::open(const string& filename) throw (logic_error, UnsupportedTime)
54 {
55  if (_fd)
56  throw logic_error("Attempt to start new read while write in progress.");
57 
58  cout << "Opening SMF file " << filename << " for reading." << endl;
59 
60  _fd = fopen(filename.c_str(), "r+");
61 
62  if (_fd) {
63  // Read type (bytes 8..9)
64  fseek(_fd, 0, SEEK_SET);
65  char mthd[5];
66  mthd[4] = '\0';
67  fread(mthd, 1, 4, _fd);
68  if (strcmp(mthd, "MThd")) {
69  cerr << filename << " is not an SMF file, aborting." << endl;
70  fclose(_fd);
71  _fd = NULL;
72  return false;
73  }
74 
75  // Read type (bytes 8..9)
76  fseek(_fd, 8, SEEK_SET);
77  uint16_t type_be = 0;
78  fread(&type_be, 2, 1, _fd);
79  _type = GUINT16_FROM_BE(type_be);
80 
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);
85 
86  // Read PPQN (bytes 12..13)
87  uint16_t ppqn_be = 0;
88  fread(&ppqn_be, 2, 1, _fd);
89  _ppqn = GUINT16_FROM_BE(ppqn_be);
90 
91  // TODO: Absolute (SMPTE seconds) time support
92  if ((_ppqn & 0x8000) != 0)
93  throw UnsupportedTime();
94 
95  seek_to_track(1);
96 
97  return true;
98  } else {
99  return false;
100  }
101 }
102 
103 
107 bool
108 SMFReader::seek_to_track(unsigned track) throw (std::logic_error)
109 {
110  if (track == 0)
111  throw logic_error("Seek to track 0 out of range (must be >= 1)");
112 
113  if (!_fd)
114  throw logic_error("Attempt to seek to track on unopened SMF file.");
115 
116  unsigned track_pos = 0;
117 
118  fseek(_fd, 14, SEEK_SET);
119  char id[5];
120  id[4] = '\0';
121  uint32_t chunk_size = 0;
122 
123  while (!feof(_fd)) {
124  fread(id, 1, 4, _fd);
125 
126  if (!strcmp(id, "MTrk")) {
127  ++track_pos;
128  } else {
129  std::cerr << "Unknown chunk ID " << id << endl;
130  }
131 
132  uint32_t chunk_size_be;
133  fread(&chunk_size_be, 4, 1, _fd);
134  chunk_size = GUINT32_FROM_BE(chunk_size_be);
135 
136  if (track_pos == track)
137  break;
138 
139  fseek(_fd, chunk_size, SEEK_CUR);
140  }
141 
142  if (!feof(_fd) && track_pos == track) {
143  _track = track;
144  _track_size = chunk_size;
145  return true;
146  } else {
147  return false;
148  }
149 }
150 
151 
165 int
166 SMFReader::read_event(size_t buf_len,
167  uint8_t* buf,
168  uint32_t* ev_size,
169  uint32_t* delta_time)
170  throw (std::logic_error, PrematureEOF, CorruptFile)
171 {
172  if (_track == 0)
173  throw logic_error("Attempt to read from unopened SMF file");
174 
175  if (!_fd || feof(_fd)) {
176  return -1;
177  }
178 
179  assert(buf_len > 0);
180  assert(buf);
181  assert(ev_size);
182  assert(delta_time);
183 
184  // Running status state
185  static uint8_t last_status = 0;
186  static uint32_t last_size = 0;
187 
188  *delta_time = read_var_len(_fd);
189  int status = fgetc(_fd);
190  if (status == EOF)
191  throw PrematureEOF();
192  else if (status > 0xFF)
193  throw CorruptFile();
194 
195  if (status < 0x80) {
196  if (last_status == 0)
197  throw CorruptFile();
198  status = last_status;
199  *ev_size = last_size;
200  fseek(_fd, -1, SEEK_CUR);
201  } else {
202  last_status = status;
203  *ev_size = midi_event_size(status);
204  last_size = *ev_size;
205  }
206 
207  buf[0] = (uint8_t)status;
208 
209  if (status == 0xFF) {
210  *ev_size = 0;
211  if (feof(_fd))
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;*/
219 
220  if ((uint8_t)type == 0x2F) {
221  return -1; // we hit the logical EOF anyway...
222  } else {
223  fseek(_fd, size, SEEK_CUR);
224  return 0;
225  }
226  }
227 
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);
232  return 0;
233  } else {
234  // Read event, return size
235  if (ferror(_fd))
236  throw CorruptFile();
237 
238  fread(buf+1, 1, *ev_size - 1, _fd);
239 
240  if ((buf[0] & 0xF0) == 0x90 && buf[2] == 0) {
241  buf[0] = (0x80 | (buf[0] & 0x0F));
242  buf[2] = 0x40;
243  }
244 
245  return *ev_size;
246  }
247 }
248 
249 
250 void
252 {
253  if (_fd)
254  fclose(_fd);
255 
256  _fd = NULL;
257 }
258 
259 
260 uint32_t
262 {
263  if (feof(fd))
264  throw PrematureEOF();
265 
266  uint32_t value;
267  uint8_t c;
268 
269  if ( (value = getc(fd)) & 0x80 ) {
270  value &= 0x7F;
271  do {
272  if (feof(fd))
273  throw PrematureEOF();
274  value = (value << 7) + ((c = getc(fd)) & 0x7F);
275  } while (c & 0x80);
276  }
277 
278  return value;
279 }
280 
281 
282 } // namespace Evoral
283 
int read_event(size_t buf_len, uint8_t *buf, uint32_t *ev_size, uint32_t *ev_delta_time)
Definition: SMFReader.cpp:166
static int midi_event_size(uint8_t status)
Definition: midi_util.h:39
Definition: Beats.hpp:239
bool seek_to_track(unsigned track)
Definition: SMFReader.cpp:108
static uint32_t read_var_len(FILE *fd)
Definition: SMFReader.cpp:261
bool open(const std::string &filename)
Definition: SMFReader.cpp:53