Rewrote MidiRingBuffer to more efficiently pack data (flat pack stamps, sizes, and...
[ardour.git] / libs / ardour / smf_source.cc
1 /*
2     Copyright (C) 2006 Paul Davis 
3         Written by Dave Robillard, 2006
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
21 #include <vector>
22
23 #include <sys/time.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <errno.h>
27
28 #include <pbd/mountpoint.h>
29 #include <pbd/pathscanner.h>
30 #include <pbd/stl_delete.h>
31 #include <pbd/strsplit.h>
32
33 #include <glibmm/miscutils.h>
34
35 #include <ardour/smf_source.h>
36 #include <ardour/session.h>
37 #include <ardour/midi_ring_buffer.h>
38 #include <ardour/midi_util.h>
39
40 #include "i18n.h"
41
42 using namespace ARDOUR;
43
44 string SMFSource::_search_path;
45
46 /*sigc::signal<void,struct tm*, time_t> SMFSource::HeaderPositionOffsetChanged;
47 bool                                  SMFSource::header_position_negative;
48 uint64_t                              SMFSource::header_position_offset;
49 */
50
51 SMFSource::SMFSource (Session& s, std::string path, Flag flags)
52         : MidiSource (s, region_name_from_path(path, false))
53         , _channel(0)
54         , _flags (Flag(flags | Writable)) // FIXME: this needs to be writable for now
55         , _allow_remove_if_empty(true)
56         , _timeline_position (0)
57         , _fd (0)
58         , _last_ev_time(0)
59         , _track_size(4) // 4 bytes for the ever-present EOT event
60         , _header_size(22)
61 {
62         /* constructor used for new internal-to-session files. file cannot exist */
63
64         if (init (path, false)) {
65                 throw failed_constructor ();
66         }
67         
68         if (open()) {
69                 throw failed_constructor ();
70         }
71         
72         assert(_name.find("/") == string::npos);
73 }
74
75 SMFSource::SMFSource (Session& s, const XMLNode& node)
76         : MidiSource (s, node)
77         , _channel(0)
78         , _flags (Flag (Writable|CanRename))
79         , _allow_remove_if_empty(true)
80         , _timeline_position (0)
81         , _fd (0)
82         , _last_ev_time(0)
83         , _track_size(4) // 4 bytes for the ever-present EOT event
84         , _header_size(22)
85 {
86         /* constructor used for existing internal-to-session files. file must exist */
87
88         if (set_state (node)) {
89                 throw failed_constructor ();
90         }
91         
92         if (init (_name, true)) {
93                 throw failed_constructor ();
94         }
95         
96         if (open()) {
97                 throw failed_constructor ();
98         }
99         
100         assert(_name.find("/") == string::npos);
101 }
102
103 SMFSource::~SMFSource ()
104 {
105         if (removable()) {
106                 unlink (_path.c_str());
107         }
108 }
109
110 bool
111 SMFSource::removable () const
112 {
113         return (_flags & Removable) && ((_flags & RemoveAtDestroy) || 
114                                       ((_flags & RemovableIfEmpty) && is_empty (_path)));
115 }
116
117 int
118 SMFSource::init (string pathstr, bool must_exist)
119 {
120         bool is_new = false;
121
122         if (!find (pathstr, must_exist, is_new)) {
123                 cerr << "cannot find " << pathstr << " with me = " << must_exist << endl;
124                 return -1;
125         }
126
127         if (is_new && must_exist) {
128                 return -1;
129         }
130
131         assert(_name.find("/") == string::npos);
132         return 0;
133 }
134
135 int
136 SMFSource::open()
137 {
138         cerr << "Opening SMF file " << path() << " writeable: " << writable() << endl;
139
140         assert(writable()); // FIXME;
141
142         _fd = fopen(path().c_str(), "r+");
143
144         // File already exists
145         if (_fd) {
146                 fseek(_fd, _header_size - 4, 0);
147                 uint32_t track_size_be = 0;
148                 fread(&track_size_be, 4, 1, _fd);
149                 _track_size = GUINT32_FROM_BE(track_size_be);
150                 cerr << "SMF - read track size " << _track_size << endl;
151
152         // We're making a new file
153         } else {
154                 _fd = fopen(path().c_str(), "w+");
155                 _track_size = 0;
156
157                 // write a tentative header just to pad things out so writing happens in the right spot
158                 flush_header();
159                 // FIXME: write the footer here too so it's a valid SMF (screw up writing ATM though)
160         }
161
162         return (_fd == 0) ? -1 : 0;
163 }
164
165 int
166 SMFSource::update_header (nframes_t when, struct tm&, time_t)
167 {
168         _timeline_position = when;
169         return flush_header();
170 }
171
172 int
173 SMFSource::flush_header ()
174 {
175         // FIXME: write timeline position somehow?
176         
177         cerr << "SMF Flushing header\n";
178
179         assert(_fd);
180
181         const uint16_t type     = GUINT16_TO_BE(0);    // SMF Type 0 (single track)
182         const uint16_t ntracks  = GUINT16_TO_BE(1);    // Number of tracks (always 1 for Type 0)
183         const uint16_t division = GUINT16_TO_BE(1920); // FIXME FIXME FIXME PPQN
184
185         char data[6];
186         memcpy(data, &type, 2);
187         memcpy(data+2, &ntracks, 2);
188         memcpy(data+4, &division, 2);
189
190         _fd = freopen(path().c_str(), "r+", _fd);
191         assert(_fd);
192         fseek(_fd, 0, 0);
193         write_chunk("MThd", 6, data);
194         //if (_track_size > 0) {
195                 write_chunk_header("MTrk", _track_size); 
196         //}
197
198         fflush(_fd);
199
200         return 0;
201 }
202
203 int
204 SMFSource::flush_footer()
205 {
206         cerr << "SMF - Writing EOT\n";
207
208         fseek(_fd, 0, SEEK_END);
209         write_var_len(1); // whatever...
210         char eot[4] = { 0xFF, 0x2F, 0x00 }; // end-of-track meta-event
211         fwrite(eot, 1, 4, _fd);
212         fflush(_fd);
213         return 0;
214 }
215
216 /** Returns the offset of the first event in the file with a time past @a start,
217  * relative to the start of the source.
218  *
219  * Returns -1 if not found.
220  */
221 /*
222 long
223 SMFSource::find_first_event_after(nframes_t start)
224 {
225         // FIXME: obviously this is slooow
226         
227         fseek(_fd, _header_size, 0);
228
229         while ( ! feof(_fd) ) {
230                 const uint32_t delta_time = read_var_len();
231
232                 if (delta_time > start)
233                         return delta_time;
234         }
235
236         return -1;
237 }
238 */
239
240 /** Read an event from the current position in file.
241  *
242  * File position MUST be at the beginning of a delta time, or this will die very messily.
243  * ev.buffer must be of size ev.size, and large enough for the event.  The returned event
244  * will have it's time field set to it's delta time (so it's the caller's responsibility
245  * to calculate a real time for the event).
246  *
247  * Returns event length (including status byte) on success, 0 if event was
248  * skipped (eg a meta event), or -1 on EOF (or end of track).
249  */
250 int
251 SMFSource::read_event(MidiEvent& ev) const
252 {
253         // - 4 is for the EOT event, which we don't actually want to read
254         //if (feof(_fd) || ftell(_fd) >= _header_size + _track_size - 4) {
255         if (feof(_fd)) {
256                 return -1;
257         }
258
259         uint32_t delta_time = read_var_len();
260         int status = fgetc(_fd);
261         assert(status != EOF); // FIXME die gracefully
262         if (status == 0xFF) {
263                 assert(!feof(_fd));
264                 int type = fgetc(_fd);
265                 if ((unsigned char)type == 0x2F) {
266                         //cerr << "SMF - hit EOT" << endl;
267                         return -1; // we hit the logical EOF anyway...
268                 } else {
269                         ev.size = 0;
270                         ev.time = delta_time; // this is needed regardless
271                         return 0;
272                 }
273         }
274
275         ev.buffer[0] = (unsigned char)status;
276         ev.size = midi_event_size(ev.buffer[0]) + 1;
277         fread(ev.buffer+1, 1, ev.size - 1, _fd);
278         ev.time = delta_time;
279
280         /*printf("SMF - read event, delta = %u, size = %zu, data = ",
281                 delta_time, ev.size);
282         for (size_t i=0; i < ev.size; ++i) {
283                 printf("%X ", ev.buffer[i]);
284         }
285         printf("\n");*/
286         
287         return ev.size;
288 }
289
290 nframes_t
291 SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const
292 {
293         //cerr << "SMF - read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl;
294
295         nframes_t time = 0;
296
297         _read_data_count = 0;
298
299         // FIXME: ugh
300         unsigned char ev_buf[MidiBuffer::max_event_size()];
301         MidiEvent ev;
302         ev.time = 0;
303         ev.size = MidiBuffer::max_event_size();
304         ev.buffer = ev_buf;
305
306         // FIXME: it would be an impressive feat to actually make this any slower :)
307         
308         fseek(_fd, _header_size, 0);
309         
310         while (!feof(_fd)) {
311                 int ret = read_event(ev);
312                 if (ret == -1) { // EOF
313                         //cerr << "SMF - EOF\n";
314                         break;
315                 }
316
317                 if (ret == 0) { // meta-event (skipped)
318                         //cerr << "SMF - META\n";
319                         time += ev.time; // just accumulate delta time and ignore event
320                         continue;
321                 }
322
323                 time += ev.time; // accumulate delta time
324                 ev.time = time; // set ev.time to actual time (relative to source start)
325
326                 if (ev.time >= start) {
327                         if (ev.time > start + cnt) {
328                                 break;
329                         } else {
330                                 ev.time += stamp_offset;
331                                 dst.write(ev.time, ev.size, ev.buffer);
332                         }
333                 }
334
335                 _read_data_count += ev.size;
336         }
337         
338         return cnt;
339 }
340
341 nframes_t
342 SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
343 {
344         _write_data_count = 0;
345
346         boost::shared_ptr<MidiBuffer> buf_ptr(new MidiBuffer(1024)); // FIXME: size?
347         MidiBuffer& buf = *buf_ptr.get();
348         src.read(buf, /*_length*/0, _length + cnt); // FIXME?
349
350         fseek(_fd, 0, SEEK_END);
351
352         // FIXME: start of source time?
353         
354         for (size_t i=0; i < buf.size(); ++i) {
355                 const MidiEvent& ev = buf[i];
356                 assert(ev.time >= _timeline_position);
357                 uint32_t delta_time = (ev.time - _timeline_position) - _last_ev_time;
358                 
359                 /*printf("SMF - writing event, delta = %u, size = %zu, data = ",
360                         delta_time, ev.size);
361                 for (size_t i=0; i < ev.size; ++i) {
362                         printf("%X ", ev.buffer[i]);
363                 }
364                 printf("\n");
365                 */
366                 size_t stamp_size = write_var_len(delta_time);
367                 fwrite(ev.buffer, 1, ev.size, _fd);
368                 _last_ev_time += delta_time;
369                 _track_size += stamp_size + ev.size;
370
371                 _write_data_count += ev.size;
372         }
373
374         fflush(_fd);
375
376         const nframes_t oldlen = _length;
377         update_length(oldlen, cnt);
378
379         ViewDataRangeReady (buf_ptr, oldlen, cnt); /* EMIT SIGNAL */
380         
381         return cnt;
382 }
383
384 XMLNode&
385 SMFSource::get_state ()
386 {
387         XMLNode& root (MidiSource::get_state());
388         char buf[16];
389         snprintf (buf, sizeof (buf), "0x%x", (int)_flags);
390         root.add_property ("flags", buf);
391         return root;
392 }
393
394 int
395 SMFSource::set_state (const XMLNode& node)
396 {
397         const XMLProperty* prop;
398
399         if (MidiSource::set_state (node)) {
400                 return -1;
401         }
402
403         if ((prop = node.property (X_("flags"))) != 0) {
404
405                 int ival;
406                 sscanf (prop->value().c_str(), "0x%x", &ival);
407                 _flags = Flag (ival);
408
409         } else {
410
411                 _flags = Flag (0);
412
413         }
414
415         assert(_name.find("/") == string::npos);
416
417         return 0;
418 }
419
420 void
421 SMFSource::mark_for_remove ()
422 {
423         if (!writable()) {
424                 return;
425         }
426         _flags = Flag (_flags | RemoveAtDestroy);
427 }
428
429 void
430 SMFSource::mark_streaming_write_completed ()
431 {
432         if (!writable()) {
433                 return;
434         }
435         
436         flush_footer();
437
438 #if 0
439         Glib::Mutex::Lock lm (_lock);
440
441
442         next_peak_clear_should_notify = true;
443
444         if (_peaks_built || pending_peak_builds.empty()) {
445                 _peaks_built = true;
446                  PeaksReady (); /* EMIT SIGNAL */
447         }
448 #endif
449 }
450
451 void
452 SMFSource::mark_take (string id)
453 {
454         if (writable()) {
455                 _take_id = id;
456         }
457 }
458
459 int
460 SMFSource::move_to_trash (const string trash_dir_name)
461 {
462         string newpath;
463
464         if (!writable()) {
465                 return -1;
466         }
467
468         /* don't move the file across filesystems, just
469            stick it in the 'trash_dir_name' directory
470            on whichever filesystem it was already on.
471         */
472
473         newpath = Glib::path_get_dirname (_path);
474         newpath = Glib::path_get_dirname (newpath);
475
476         newpath += '/';
477         newpath += trash_dir_name;
478         newpath += '/';
479         newpath += Glib::path_get_basename (_path);
480
481         if (access (newpath.c_str(), F_OK) == 0) {
482
483                 /* the new path already exists, try versioning */
484                 
485                 char buf[PATH_MAX+1];
486                 int version = 1;
487                 string newpath_v;
488
489                 snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
490                 newpath_v = buf;
491
492                 while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
493                         snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
494                         newpath_v = buf;
495                 }
496                 
497                 if (version == 999) {
498                         PBD::error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
499                                           newpath)
500                               << endmsg;
501                 } else {
502                         newpath = newpath_v;
503                 }
504
505         } else {
506
507                 /* it doesn't exist, or we can't read it or something */
508
509         }
510
511         if (::rename (_path.c_str(), newpath.c_str()) != 0) {
512                 PBD::error << string_compose (_("cannot rename midi file source from %1 to %2 (%3)"),
513                                   _path, newpath, strerror (errno))
514                       << endmsg;
515                 return -1;
516         }
517 #if 0
518         if (::unlink (peakpath.c_str()) != 0) {
519                 PBD::error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
520                                   peakpath, _path, strerror (errno))
521                       << endmsg;
522                 /* try to back out */
523                 rename (newpath.c_str(), _path.c_str());
524                 return -1;
525         }
526             
527         _path = newpath;
528         peakpath = "";
529 #endif  
530         /* file can not be removed twice, since the operation is not idempotent */
531
532         _flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
533
534         return 0;
535 }
536
537 // FIXME: Merge this with audiofilesource somehow (make a generic filesource?)
538 bool
539 SMFSource::find (string pathstr, bool must_exist, bool& isnew)
540 {
541         string::size_type pos;
542         bool ret = false;
543
544         isnew = false;
545
546         /* clean up PATH:CHANNEL notation so that we are looking for the correct path */
547
548         if ((pos = pathstr.find_last_of (':')) == string::npos) {
549                 pathstr = pathstr;
550         } else {
551                 pathstr = pathstr.substr (0, pos);
552         }
553
554         if (pathstr[0] != '/') {
555
556                 /* non-absolute pathname: find pathstr in search path */
557
558                 vector<string> dirs;
559                 int cnt;
560                 string fullpath;
561                 string keeppath;
562
563                 if (_search_path.length() == 0) {
564                         PBD::error << _("FileSource: search path not set") << endmsg;
565                         goto out;
566                 }
567
568                 split (_search_path, dirs, ':');
569
570                 cnt = 0;
571                 
572                 for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
573
574                         fullpath = *i;
575                         if (fullpath[fullpath.length()-1] != '/') {
576                                 fullpath += '/';
577                         }
578                         fullpath += pathstr;
579                         
580                         if (access (fullpath.c_str(), R_OK) == 0) {
581                                 keeppath = fullpath;
582                                 ++cnt;
583                         } 
584                 }
585
586                 if (cnt > 1) {
587
588                         PBD::error << string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, _search_path) << endmsg;
589                         goto out;
590
591                 } else if (cnt == 0) {
592
593                         if (must_exist) {
594                                 PBD::error << string_compose(_("Filesource: cannot find required file (%1): while searching %2"), pathstr, _search_path) << endmsg;
595                                 goto out;
596                         } else {
597                                 isnew = true;
598                         }
599                 }
600                 
601                 _name = pathstr;
602                 _path = keeppath;
603                 ret = true;
604
605         } else {
606                 
607                 /* external files and/or very very old style sessions include full paths */
608                 
609                 _path = pathstr;
610                 _name = pathstr.substr (pathstr.find_last_of ('/') + 1);
611                 
612                 if (access (_path.c_str(), R_OK) != 0) {
613
614                         /* file does not exist or we cannot read it */
615
616                         if (must_exist) {
617                                 PBD::error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
618                                 goto out;
619                         }
620                         
621                         if (errno != ENOENT) {
622                                 PBD::error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
623                                 goto out;
624                         }
625                         
626                         /* a new file */
627
628                         isnew = true;
629                         ret = true;
630
631                 } else {
632                         
633                         /* already exists */
634
635                         ret = true;
636                 }
637         }
638         
639   out:
640         return ret;
641 }
642
643 void
644 SMFSource::set_search_path (string p)
645 {
646         _search_path = p;
647 }
648
649
650 void
651 SMFSource::set_allow_remove_if_empty (bool yn)
652 {
653         if (writable()) {
654                 _allow_remove_if_empty = yn;
655         }
656 }
657
658 int
659 SMFSource::set_name (string newname, bool destructive)
660 {
661         //Glib::Mutex::Lock lm (_lock); FIXME
662         string oldpath = _path;
663         string newpath = Session::change_midi_path_by_name (oldpath, _name, newname, destructive);
664
665         if (newpath.empty()) {
666                 PBD::error << string_compose (_("programming error: %1"), "cannot generate a changed midi path") << endmsg;
667                 return -1;
668         }
669
670         if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
671                 PBD::error << string_compose (_("cannot rename midi file for %1 to %2"), _name, newpath) << endmsg;
672                 return -1;
673         }
674
675         _name = Glib::path_get_basename (newpath);
676         _path = newpath;
677
678         return 0;//rename_peakfile (peak_path (_path));
679 }
680
681 bool
682 SMFSource::is_empty (string path)
683 {
684         /* XXX fix me */
685
686         return false;
687 }
688
689
690 void
691 SMFSource::write_chunk_header(char id[4], uint32_t length)
692 {
693         const uint32_t length_be = GUINT32_TO_BE(length);
694
695         fwrite(id, 1, 4, _fd);
696         fwrite(&length_be, 4, 1, _fd);
697 }
698
699 void
700 SMFSource::write_chunk(char id[4], uint32_t length, void* data)
701 {
702         write_chunk_header(id, length);
703         
704         fwrite(data, 1, length, _fd);
705 }
706
707 /** Returns the size (in bytes) of the value written. */
708 size_t
709 SMFSource::write_var_len(uint32_t value)
710 {
711         size_t ret = 0;
712
713         uint32_t buffer = value & 0x7F;
714
715         while ( (value >>= 7) ) {
716                 buffer <<= 8;
717                 buffer |= ((value & 0x7F) | 0x80);
718         }
719
720         while (true) {
721                 //printf("Writing var len byte %X\n", (unsigned char)buffer);
722                 ++ret;
723                 fputc(buffer, _fd);
724                 if (buffer & 0x80)
725                         buffer >>= 8;
726                 else
727                         break;
728         }
729
730         return ret;
731 }
732
733 uint32_t
734 SMFSource::read_var_len() const
735 {
736         assert(!feof(_fd));
737
738         //int offset = ftell(_fd);
739         //cerr << "SMF - reading var len at " << offset << endl;
740
741         uint32_t value;
742         unsigned char c;
743
744         if ( (value = getc(_fd)) & 0x80 ) {
745                 value &= 0x7F;
746                 do {
747                         assert(!feof(_fd));
748                         value = (value << 7) + ((c = getc(_fd)) & 0x7F);
749                 } while (c & 0x80);
750         }
751
752         return value;
753 }