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