aca8cf5a2576d5d0bfbe8b0e5ced87eba9a8c7cd
[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;
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 (jack_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(jack_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 jack_nframes_t
291 SMFSource::read_unlocked (MidiRingBuffer& dst, jack_nframes_t start, jack_nframes_t cnt, jack_nframes_t stamp_offset) const
292 {
293         //cerr << "SMF - read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl;
294
295         jack_nframes_t time = 0;
296
297         // FIXME: ugh
298         unsigned char ev_buf[MidiBuffer::max_event_size()];
299         MidiEvent ev;
300         ev.time = 0;
301         ev.size = MidiBuffer::max_event_size();
302         ev.buffer = ev_buf;
303
304         // FIXME: it would be an impressive feat to actually make this any slower :)
305         
306         fseek(_fd, _header_size, 0);
307         
308         while (!feof(_fd)) {
309                 int ret = read_event(ev);
310                 if (ret == -1) { // EOF
311                         //cerr << "SMF - EOF\n";
312                         break;
313                 }
314
315                 if (ret == 0) { // meta-event (skipped)
316                         //cerr << "SMF - META\n";
317                         time += ev.time; // just accumulate delta time and ignore event
318                         continue;
319                 }
320
321                 time += ev.time; // accumulate delta time
322                 ev.time = time; // set ev.time to actual time (relative to source start)
323
324                 if (ev.time >= start) {
325                         if (ev.time > start + cnt) {
326                                 break;
327                         } else {
328                                 ev.time += stamp_offset;
329                                 dst.write(ev);
330                         }
331                 }
332         }
333         
334         return cnt;
335 }
336
337 jack_nframes_t
338 SMFSource::write_unlocked (MidiRingBuffer& src, jack_nframes_t cnt)
339 {
340         //cerr << "SMF WRITE -- " << _length << "--" << cnt << endl;
341         
342         MidiBuffer buf(1024); // FIXME: allocation, size?
343         src.read(buf, /*_length*/0, _length + cnt); // FIXME?
344
345         fseek(_fd, 0, SEEK_END);
346
347         // FIXME: start of source time?
348         
349         for (size_t i=0; i < buf.size(); ++i) {
350                 const MidiEvent& ev = buf[i];
351                 assert(ev.time >= _timeline_position);
352                 uint32_t delta_time = (ev.time - _timeline_position) - _last_ev_time;
353                 
354                 /*printf("SMF - writing event, delta = %u, size = %zu, data = ",
355                         delta_time, ev.size);
356                 for (size_t i=0; i < ev.size; ++i) {
357                         printf("%X ", ev.buffer[i]);
358                 }
359                 printf("\n");
360                 */
361                 size_t stamp_size = write_var_len(delta_time);
362                 fwrite(ev.buffer, 1, ev.size, _fd);
363                 _last_ev_time += delta_time;
364                 _track_size += stamp_size + ev.size;
365         }
366
367         fflush(_fd);
368
369         if (buf.size() > 0) {
370                 ViewDataRangeReady (_length, cnt); /* EMIT SIGNAL */
371         }
372
373         update_length(_length, cnt);
374         return cnt;
375 }
376
377 XMLNode&
378 SMFSource::get_state ()
379 {
380         XMLNode& root (MidiSource::get_state());
381         char buf[16];
382         snprintf (buf, sizeof (buf), "0x%x", (int)_flags);
383         root.add_property ("flags", buf);
384         return root;
385 }
386
387 int
388 SMFSource::set_state (const XMLNode& node)
389 {
390         const XMLProperty* prop;
391
392         if (MidiSource::set_state (node)) {
393                 return -1;
394         }
395
396         if ((prop = node.property (X_("flags"))) != 0) {
397
398                 int ival;
399                 sscanf (prop->value().c_str(), "0x%x", &ival);
400                 _flags = Flag (ival);
401
402         } else {
403
404                 _flags = Flag (0);
405
406         }
407
408         assert(_name.find("/") == string::npos);
409
410         return 0;
411 }
412
413 void
414 SMFSource::mark_for_remove ()
415 {
416         if (!writable()) {
417                 return;
418         }
419         _flags = Flag (_flags | RemoveAtDestroy);
420 }
421
422 void
423 SMFSource::mark_streaming_write_completed ()
424 {
425         if (!writable()) {
426                 return;
427         }
428         
429         flush_footer();
430
431 #if 0
432         Glib::Mutex::Lock lm (_lock);
433
434
435         next_peak_clear_should_notify = true;
436
437         if (_peaks_built || pending_peak_builds.empty()) {
438                 _peaks_built = true;
439                  PeaksReady (); /* EMIT SIGNAL */
440         }
441 #endif
442 }
443
444 void
445 SMFSource::mark_take (string id)
446 {
447         if (writable()) {
448                 _take_id = id;
449         }
450 }
451
452 int
453 SMFSource::move_to_trash (const string trash_dir_name)
454 {
455         string newpath;
456
457         if (!writable()) {
458                 return -1;
459         }
460
461         /* don't move the file across filesystems, just
462            stick it in the 'trash_dir_name' directory
463            on whichever filesystem it was already on.
464         */
465
466         newpath = Glib::path_get_dirname (_path);
467         newpath = Glib::path_get_dirname (newpath);
468
469         newpath += '/';
470         newpath += trash_dir_name;
471         newpath += '/';
472         newpath += Glib::path_get_basename (_path);
473
474         if (access (newpath.c_str(), F_OK) == 0) {
475
476                 /* the new path already exists, try versioning */
477                 
478                 char buf[PATH_MAX+1];
479                 int version = 1;
480                 string newpath_v;
481
482                 snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
483                 newpath_v = buf;
484
485                 while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
486                         snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
487                         newpath_v = buf;
488                 }
489                 
490                 if (version == 999) {
491                         PBD::error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
492                                           newpath)
493                               << endmsg;
494                 } else {
495                         newpath = newpath_v;
496                 }
497
498         } else {
499
500                 /* it doesn't exist, or we can't read it or something */
501
502         }
503
504         if (::rename (_path.c_str(), newpath.c_str()) != 0) {
505                 PBD::error << string_compose (_("cannot rename midi file source from %1 to %2 (%3)"),
506                                   _path, newpath, strerror (errno))
507                       << endmsg;
508                 return -1;
509         }
510 #if 0
511         if (::unlink (peakpath.c_str()) != 0) {
512                 PBD::error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
513                                   peakpath, _path, strerror (errno))
514                       << endmsg;
515                 /* try to back out */
516                 rename (newpath.c_str(), _path.c_str());
517                 return -1;
518         }
519             
520         _path = newpath;
521         peakpath = "";
522 #endif  
523         /* file can not be removed twice, since the operation is not idempotent */
524
525         _flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
526
527         return 0;
528 }
529
530 // FIXME: Merge this with audiofilesource somehow (make a generic filesource?)
531 bool
532 SMFSource::find (string pathstr, bool must_exist, bool& isnew)
533 {
534         string::size_type pos;
535         bool ret = false;
536
537         isnew = false;
538
539         /* clean up PATH:CHANNEL notation so that we are looking for the correct path */
540
541         if ((pos = pathstr.find_last_of (':')) == string::npos) {
542                 pathstr = pathstr;
543         } else {
544                 pathstr = pathstr.substr (0, pos);
545         }
546
547         if (pathstr[0] != '/') {
548
549                 /* non-absolute pathname: find pathstr in search path */
550
551                 vector<string> dirs;
552                 int cnt;
553                 string fullpath;
554                 string keeppath;
555
556                 if (_search_path.length() == 0) {
557                         PBD::error << _("FileSource: search path not set") << endmsg;
558                         goto out;
559                 }
560
561                 split (_search_path, dirs, ':');
562
563                 cnt = 0;
564                 
565                 for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
566
567                         fullpath = *i;
568                         if (fullpath[fullpath.length()-1] != '/') {
569                                 fullpath += '/';
570                         }
571                         fullpath += pathstr;
572                         
573                         if (access (fullpath.c_str(), R_OK) == 0) {
574                                 keeppath = fullpath;
575                                 ++cnt;
576                         } 
577                 }
578
579                 if (cnt > 1) {
580
581                         PBD::error << string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, _search_path) << endmsg;
582                         goto out;
583
584                 } else if (cnt == 0) {
585
586                         if (must_exist) {
587                                 PBD::error << string_compose(_("Filesource: cannot find required file (%1): while searching %2"), pathstr, _search_path) << endmsg;
588                                 goto out;
589                         } else {
590                                 isnew = true;
591                         }
592                 }
593                 
594                 _name = pathstr;
595                 _path = keeppath;
596                 ret = true;
597
598         } else {
599                 
600                 /* external files and/or very very old style sessions include full paths */
601                 
602                 _path = pathstr;
603                 _name = pathstr.substr (pathstr.find_last_of ('/') + 1);
604                 
605                 if (access (_path.c_str(), R_OK) != 0) {
606
607                         /* file does not exist or we cannot read it */
608
609                         if (must_exist) {
610                                 PBD::error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
611                                 goto out;
612                         }
613                         
614                         if (errno != ENOENT) {
615                                 PBD::error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
616                                 goto out;
617                         }
618                         
619                         /* a new file */
620
621                         isnew = true;
622                         ret = true;
623
624                 } else {
625                         
626                         /* already exists */
627
628                         ret = true;
629                 }
630         }
631         
632   out:
633         return ret;
634 }
635
636 void
637 SMFSource::set_search_path (string p)
638 {
639         _search_path = p;
640 }
641
642
643 void
644 SMFSource::set_allow_remove_if_empty (bool yn)
645 {
646         if (writable()) {
647                 _allow_remove_if_empty = yn;
648         }
649 }
650
651 int
652 SMFSource::set_name (string newname, bool destructive)
653 {
654         //Glib::Mutex::Lock lm (_lock); FIXME
655         string oldpath = _path;
656         string newpath = Session::change_midi_path_by_name (oldpath, _name, newname, destructive);
657
658         if (newpath.empty()) {
659                 PBD::error << string_compose (_("programming error: %1"), "cannot generate a changed midi path") << endmsg;
660                 return -1;
661         }
662
663         if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
664                 PBD::error << string_compose (_("cannot rename midi file for %1 to %2"), _name, newpath) << endmsg;
665                 return -1;
666         }
667
668         _name = Glib::path_get_basename (newpath);
669         _path = newpath;
670
671         return 0;//rename_peakfile (peak_path (_path));
672 }
673
674 bool
675 SMFSource::is_empty (string path)
676 {
677         /* XXX fix me */
678
679         return false;
680 }
681
682
683 void
684 SMFSource::write_chunk_header(char id[4], uint32_t length)
685 {
686         const uint32_t length_be = GUINT32_TO_BE(length);
687
688         fwrite(id, 1, 4, _fd);
689         fwrite(&length_be, 4, 1, _fd);
690 }
691
692 void
693 SMFSource::write_chunk(char id[4], uint32_t length, void* data)
694 {
695         write_chunk_header(id, length);
696         
697         fwrite(data, 1, length, _fd);
698 }
699
700 /** Returns the size (in bytes) of the value written. */
701 size_t
702 SMFSource::write_var_len(uint32_t value)
703 {
704         size_t ret = 0;
705
706         uint32_t buffer = value & 0x7F;
707
708         while ( (value >>= 7) ) {
709                 buffer <<= 8;
710                 buffer |= ((value & 0x7F) | 0x80);
711         }
712
713         while (true) {
714                 //printf("Writing var len byte %X\n", (unsigned char)buffer);
715                 ++ret;
716                 fputc(buffer, _fd);
717                 if (buffer & 0x80)
718                         buffer >>= 8;
719                 else
720                         break;
721         }
722
723         return ret;
724 }
725
726 uint32_t
727 SMFSource::read_var_len() const
728 {
729         assert(!feof(_fd));
730
731         //int offset = ftell(_fd);
732         //cerr << "SMF - reading var len at " << offset << endl;
733
734         uint32_t value;
735         unsigned char c;
736
737         if ( (value = getc(_fd)) & 0x80 ) {
738                 value &= 0x7F;
739                 do {
740                         assert(!feof(_fd));
741                         value = (value << 7) + ((c = getc(_fd)) & 0x7F);
742                 } while (c & 0x80);
743         }
744
745         return value;
746 }