Fix windows builds, rename icons following 4e96285ba5
[ardour.git] / tools / omf / omftool.cc
1 /*  Rewritten for Ardour by Paul Davis <paul@linuxaudiosystems.com>, Feb 2010
2     but based on ...
3  */
4
5 /*  REAPER OMF plug-in
6     Copyright (C) 2009 Hannes Breul
7
8     Provides OMF import.
9
10     Based on the m3u example included in the Reaper SDK,
11     Copyright (C) 2005-2008 Cockos Incorporated
12
13     Original source available at:
14     http://www.reaper.fm/sdk/plugin/plugin.php#ext_dl
15
16     This software is provided 'as-is', without any express or implied
17     warranty.  In no event will the authors be held liable for any damages
18     arising from the use of this software.
19
20     Permission is granted to anyone to use this software for any purpose,
21     including commercial applications, and to alter it and redistribute it
22     freely, subject to the following restrictions:
23
24     1. The origin of this software must not be misrepresented; you must not
25     claim that you wrote the original software. If you use this software
26     in a product, an acknowledgment in the product documentation would be
27     appreciated but is not required.
28     2. Altered source versions must be plainly marked as such, and must not be
29     misrepresented as being the original software.
30     3. This notice may not be removed or altered from any source distribution.
31 */
32
33 #ifndef __STDC_FORMAT_MACROS
34 #define __STDC_FORMAT_MACROS /* PRI<foo>; C++ requires explicit requesting of these */
35 #endif
36
37 #include <iostream>
38
39 #include <getopt.h>
40 #include <stdio.h>
41 #include <math.h>
42 #include <stdlib.h>
43 #include <stdarg.h>
44 #include <string.h>
45 #include <time.h>
46 #include <inttypes.h>
47 #include <sys/errno.h>
48 #include <sndfile.h>
49 #include <glibmm.h>
50
51 #include "pbd/xml++.h"
52 #include "pbd/basename.h"
53 #include "omftool.h"
54
55 //#define DEBUG(fmt,...) fprintf (stderr, fmt, ## __VA_ARGS__)
56 #define DEBUG(fmt,...)
57 #define INFO(fmt,...) fprintf (stdout, fmt, ## __VA_ARGS__)
58
59 using namespace std;
60 using namespace PBD;
61
62 OMF::OMF ()
63 {
64         char sbuf[256];
65
66         bigEndian = false;
67         id_counter = 0;
68         session_name = "omfsession";
69         base_dir = ".";
70         sample_rate = 0;
71         frame_rate = 0;
72         version = 3000;
73         db = 0;
74         file = 0;
75
76         session = new XMLNode ("Session");
77         sources = new XMLNode ("Sources");
78         routes = new XMLNode ("Routes");
79         regions = new XMLNode ("Regions");
80         playlists = new XMLNode ("Playlists");
81         diskstreams = new XMLNode ("DiskStreams");
82         locations = new XMLNode ("Locations");
83         options = new XMLNode ("Options");
84         options = new XMLNode ("RouteGroups");
85
86         /* add master, default 2in/2out */
87
88         XMLNode* master = new_route_node ();
89         master->add_property ("name", "master");
90         set_route_node_channels (master, 2, 2, false);
91
92         XMLNode* tempo_map = new XMLNode ("TempoMap");
93         XMLNode* tempo = new XMLNode ("Tempo");
94         tempo->add_property ("start", "1|1|0");
95         tempo->add_property ("beats-per-minute", "120.0");
96         tempo->add_property ("note-type", "4.0");
97         tempo->add_property ("movable", "no");
98         tempo_map->add_child_nocopy (*tempo);
99         XMLNode* meter = new XMLNode ("Meter");
100         meter->add_property ("start", "1|1|0");
101         meter->add_property ("beats-per-bar", "4.0");
102         meter->add_property ("note-type", "4.0");
103         meter->add_property ("movable", "no");
104         tempo_map->add_child_nocopy (*meter);
105
106         XMLNode* click = new XMLNode ("Click");
107         XMLNode* io = new XMLNode ("IO");
108         click->add_child_nocopy (*io);
109         io->add_property ("name", "click");
110         add_id (io);
111         io->add_property ("direction", "Output");
112         io->add_property ("default-type", "audio");
113         XMLNode* port = new XMLNode ("Port");
114         io->add_child_nocopy (*port);
115         port->add_property ("type", "audio");
116         port->add_property ("name", "click/audio_out 1");
117         XMLNode* connection = new XMLNode ("Connection");
118         connection->add_property ("other", "system:playback_1");
119         port->add_child_nocopy (*connection);
120
121         port = new XMLNode ("Port");
122         io->add_child_nocopy (*port);
123         port->add_property ("type", "audio");
124         port->add_property ("name", "click/audio_out 2");
125         connection = new XMLNode ("Connection");
126         connection->add_property ("other", "system:playback_2");
127         port->add_child_nocopy (*connection);
128
129         session->add_child_nocopy (*options);
130         session->add_child_nocopy (*sources);
131         session->add_child_nocopy (*regions);
132         session->add_child_nocopy (*playlists);
133         session->add_child_nocopy (*diskstreams);
134         session->add_child_nocopy (*routes);
135         session->add_child_nocopy (*locations);
136         session->add_child_nocopy (*tempo_map);
137         session->add_child_nocopy (*click);
138 }
139
140 OMF::~OMF ()
141 {
142         /* clean up */
143         sqlite3_close (db);
144         fclose (file);
145 }
146
147 void
148 OMF::set_sample_rate (int sr)
149 {
150         sample_rate = sr;
151 }
152
153 void
154 OMF::set_session_name (const std::string& str)
155 {
156         base_dir = Glib::path_get_dirname (str); // returns "." if no dirs were given
157         session_name = Glib::path_get_basename (str);
158 }
159
160 void
161 OMF::set_version (int v)
162 {
163         version = v;
164 }
165
166 int
167 OMF::init ()
168 {
169         /* create directory tree */
170
171         string dir;
172
173         audiofile_path_vector.push_back (base_dir);
174         audiofile_path_vector.push_back (session_name);
175         audiofile_path_vector.push_back ("interchange");
176         audiofile_path_vector.push_back (session_name);
177         audiofile_path_vector.push_back ("audiofiles");
178
179         dir = Glib::build_filename (audiofile_path_vector);
180         g_mkdir_with_parents (dir.c_str(), 0775);
181
182         /* and the rest */
183
184
185         vector<string> v;
186         v.push_back (base_dir);
187         v.push_back (session_name);
188
189         vector<string> d;
190         d.push_back ("analysis");
191         d.push_back ("dead_sounds");
192         d.push_back ("export");
193         d.push_back ("peaks");
194
195         for (vector<string>::iterator i = d.begin(); i != d.end(); ++i) {
196                 v.push_back (*i);
197                 dir = Glib::build_filename (v);
198                 g_mkdir_with_parents (dir.c_str(), 0775);
199                 v.pop_back ();
200         }
201
202         return 0;
203 }
204
205 bool
206 OMF::get_audio_info (const std::string& path)
207 {
208         SNDFILE *sf;
209         SF_INFO sf_info;
210
211         sf_info.format = 0; // libsndfile says to clear this before sf_open().
212
213         if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) {
214                 char errbuf[256];
215                 cerr << "Cannot open source file " << path << sf_error_str (0, errbuf, sizeof (errbuf) - 1) << endl;
216                 return false;
217         }
218
219         if (known_sources.find (Glib::path_get_basename (path)) != known_sources.end()) {
220                 /* already exists */
221                 return true;
222         }
223
224         XMLNode* source = new_source_node();
225
226         known_sources.insert (pair<string,SourceInfo*>
227                               (Glib::path_get_basename (path),
228                                new SourceInfo (sf_info.channels,
229                                                sf_info.samplerate,
230                                                sf_info.frames,
231                                                source)));
232
233         source->add_property ("name", basename_nosuffix (path));
234         cerr << "Source file " << basename_nosuffix (path) << " = " << sf_info.channels << '/' << sf_info.samplerate << '/' << sf_info.frames << endl;
235         sf_close (sf);
236         return true;
237 }
238
239 void
240 OMF::add_id (XMLNode* node)
241 {
242         char sbuf[64];
243         id_counter++;
244         snprintf (sbuf, sizeof (sbuf), "%" PRId64, id_counter);
245         node->add_property ("id", sbuf);
246 }
247
248 XMLNode*
249 OMF::new_playlist_node ()
250 {
251         XMLNode* playlist = new XMLNode ("Playlist");
252         playlists->add_child_nocopy (*playlist);
253         add_id (playlist);
254         playlist->add_property ("type", "audio");
255         playlist->add_property ("frozen", "no");
256
257         return playlist;
258 }
259
260 XMLNode*
261 OMF::new_diskstream_node ()
262 {
263         XMLNode* diskstream = new XMLNode ("AudioDiskstream");
264         diskstreams->add_child_nocopy (*diskstream);
265         add_id (diskstream);
266         diskstream->add_property ("flags", "Recordable");
267         diskstream->add_property ("speed", "1");
268         diskstream->add_property ("channels", "1");
269
270         return diskstream;
271 }
272 void
273 OMF::set_region_sources (XMLNode* region, SourceInfo* sinfo)
274 {
275         char buf[256];
276
277         region->add_property ("name", sinfo->node->property ("name")->value());
278
279         for (int i = 0; i < sinfo->channels; ++i) {
280                 snprintf (buf, sizeof (buf), "source-%d", i);
281                 region->add_property (buf, sinfo->node->property ("id")->value());
282         }
283 }
284
285 void
286 OMF::legalize_name (string& name)
287 {
288         string::size_type pos;
289         string illegal_chars = ":";
290         pos = 0;
291
292         while ((pos = name.find_first_of (illegal_chars, pos)) != string::npos) {
293                 name.replace (pos, 1, "_");
294                 pos += 1;
295         }
296 }
297
298 void
299 OMF::set_route_node_channels (XMLNode* route, int in, int out, bool send_to_master)
300 {
301         XMLNode* input_io;
302         XMLNode* output_io;
303         char sbuf[256];
304         string name = route->property ("name")->value();
305
306         legalize_name (name);
307
308         output_io = new XMLNode ("IO");
309         route->add_child_nocopy (*output_io);
310         output_io->add_property ("name", name);
311         add_id (output_io);
312         output_io->add_property ("direction", "Output");
313         output_io->add_property ("default-type", "audio");
314
315         input_io = new XMLNode ("IO");
316         route->add_child_nocopy (*input_io);
317         input_io->add_property ("name", name);
318         add_id (input_io);
319         input_io->add_property ("direction", "Input");
320         input_io->add_property ("default-type", "audio");
321
322         for (int i = 0; i < out; ++i) {
323                 XMLNode* port = new XMLNode ("Port");
324                 output_io->add_child_nocopy (*port);
325                 port->add_property ("type", "audio");
326
327                 snprintf (sbuf, sizeof (sbuf), "%s/audio_out %d", name.c_str(), i+1);
328
329                 port->add_property ("name", sbuf);
330                 XMLNode* connection = new XMLNode ("Connection");
331
332                 if (send_to_master) {
333                         if (i % 2) {
334                                 snprintf (sbuf, sizeof (sbuf), "master/audio_in 2");
335                         } else {
336                                 snprintf (sbuf, sizeof (sbuf), "master/audio_in 1");
337                         }
338                 } else {
339                         if (i % 2) {
340                                 snprintf (sbuf, sizeof (sbuf), "system:playback_2");
341                         } else {
342                                 snprintf (sbuf, sizeof (sbuf), "system:playback_1");
343                         }
344                 }
345
346                 connection->add_property ("other", sbuf);
347                 port->add_child_nocopy (*connection);
348         }
349
350         for (int i = 0; i < in; ++i) {
351                 XMLNode* port = new XMLNode ("Port");
352                 input_io->add_child_nocopy (*port);
353                 port->add_property ("type", "audio");
354
355                 snprintf (sbuf, sizeof (sbuf), "%s/audio_out %d", name.c_str(), i+1);
356
357                 port->add_property ("name", sbuf);
358                 XMLNode* connection = new XMLNode ("Connection");
359
360                 if (i % 2) {
361                         snprintf (sbuf, sizeof (sbuf), "system:capture_2");
362                 } else {
363                         snprintf (sbuf, sizeof (sbuf), "system:capture_1");
364                 }
365
366                 connection->add_property ("other", sbuf);
367                 port->add_child_nocopy (*connection);
368         }
369
370         /* add main out processor */
371
372         XMLNode* outs = new XMLNode ("Processor");
373         route->add_child_nocopy (*outs);
374         add_id (outs);
375         outs->add_property ("name", name);
376         outs->add_property ("active", "yes");
377         outs->add_property ("own-input", "yes");
378         outs->add_property ("own-output", send_to_master ? "no" : "yes");
379         outs->add_property ("output", name);
380         outs->add_property ("type", "main-outs");
381         outs->add_property ("role", "Main");
382
383         /* Panner setup */
384
385         XMLNode* panner = new XMLNode ("Panner");
386         outs->add_child_nocopy (*panner);
387
388         panner->add_property ("linked", "no");
389         panner->add_property ("link-direction", "SameDirection");
390         panner->add_property ("bypassed", "no");
391
392         for (int i = 0; i < out; ++i) {
393                 XMLNode* panout = new XMLNode ("Output");
394                 panner->add_child_nocopy (*panout);
395                 panout->add_property ("x", "0");
396                 panout->add_property ("y", "0");
397         }
398
399         for (int i = 0; i < in; ++i) {
400                 XMLNode* spanner = new XMLNode ("StreamPanner");
401                 panner->add_child_nocopy (*spanner);
402                 spanner->add_property ("x", "0");
403                 spanner->add_property ("type", "Equal Power Stereo");
404                 spanner->add_property ("muted", "no");
405                 spanner->add_property ("mono", "no");
406
407                 XMLNode* spc = new XMLNode ("Controllable");
408                 spanner->add_child_nocopy (*spc);
409                 add_id (spc);
410                 spc->add_property ("name", "panner");
411                 spc->add_property ("flags", "");
412         }
413 }
414
415 XMLNode*
416 OMF::new_route_node ()
417 {
418         char sbuf[256];
419         XMLNode* route = new XMLNode ("Route");
420
421         routes->add_child_nocopy (*route);
422         add_id (route);
423         route->add_property ("default-type","audio");
424         route->add_property ("active","yes");
425         route->add_property ("phase-invert","no");
426         route->add_property ("denormal-protection","no");
427         route->add_property ("meter-point","MeterPostFader");
428         snprintf (sbuf, sizeof (sbuf), "editor=%" PRId64 ":signal=%" PRId64, id_counter, id_counter);
429         route->add_property ("order-keys", sbuf);
430         route->add_property ("self-solo","no");
431         route->add_property ("soloed-by-others","0");
432         route->add_property ("mode","Normal");
433
434         /* other boilerplate */
435
436         XMLNode* controllable = new XMLNode ("Controllable");
437         route->add_child_nocopy (*controllable);
438         controllable->add_property ("name", "solo");
439         add_id (controllable);
440         controllable->add_property ("flags", "Toggle");
441
442         XMLNode* mutemaster = new XMLNode ("MuteMaster");
443         route->add_child_nocopy (*mutemaster);
444         mutemaster->add_property ("mute-point", "");
445
446         XMLNode* remotecontrol = new XMLNode ("RemoteControl");
447         route->add_child_nocopy (*remotecontrol);
448         remotecontrol->add_property ("id", route->property ("id")->value());
449
450         XMLNode* amp = new XMLNode ("Processor");
451         route->add_child_nocopy (*amp);
452         add_id (amp);
453         amp->add_property ("name", "Amp");
454         amp->add_property ("active", "yes");
455         amp->add_property ("type", "amp");
456         amp->add_property ("gain", "1.0");
457
458         XMLNode* meter = new XMLNode ("Processor");
459         route->add_child_nocopy (*meter);
460         add_id (meter);
461         meter->add_property ("name", "Meter");
462         meter->add_property ("active", "yes");
463         meter->add_property ("type", "meter");
464
465         XMLNode* extra = new XMLNode ("Extra");
466         route->add_child_nocopy (*extra);
467         XMLNode* gui = new XMLNode ("GUI");
468         extra->add_child_nocopy (*gui);
469         snprintf (sbuf, sizeof (sbuf), "%d:%d:%d",
470                   random() % 65536,
471                   random() % 65536,
472                   random() % 65536);
473         gui->add_property ("color", sbuf);
474         gui->add_property ("shown-mixer", "yes");
475         gui->add_property ("height", "62");
476         gui->add_property ("shown-editor", "yes");
477
478         return route;
479 }
480
481 XMLNode*
482 OMF::new_region_node ()
483 {
484         XMLNode* region = new XMLNode ("Region");
485         XMLNode* region_extra = new XMLNode ("Extra");
486         XMLNode* gui_extra = new XMLNode ("GUI");
487         char sbuf[256];
488
489         region_extra->add_child_nocopy (*gui_extra);
490         region->add_child_nocopy (*region_extra);
491
492         /* boilerplate */
493
494         region->add_property ("ancestral-start", "0");
495         region->add_property ("ancestral-start", "0");
496         region->add_property ("ancestral-length", "0");
497         region->add_property ("stretch", "1");
498         region->add_property ("shift", "1");
499         region->add_property ("first-edit", "nothing");
500         region->add_property ("layer", "0");
501         region->add_property ("sync-position", "0");
502         region->add_property ("flags", "Opaque,DefaultFadeIn,DefaultFadeOut,FadeIn,FadeOut,External");
503         region->add_property ("scale-gain", "1");
504         region->add_property ("channels", "1");
505         gui_extra->add_property ("waveform-visible","yes");
506         gui_extra->add_property ("envelope-visible", "no");
507         gui_extra->add_property ("waveform-rectified", "no");
508         gui_extra->add_property ("waveform-logscaled","no");
509
510         add_id (region);
511         return region;
512 }
513
514 XMLNode*
515 OMF::new_source_node ()
516 {
517         XMLNode* source;
518
519         source = new XMLNode ("Source");
520         add_id (source);
521         source->add_property ("type", "audio");
522         source->add_property ("flags", "CanRename");
523
524         sources->add_child_nocopy (*source);
525
526         return source;
527 }
528
529 OMF::SourceInfo*
530 OMF::get_known_source (const char* name)
531 {
532         string s (name);
533         KnownSources::iterator i = known_sources.find (s);
534         if (i != known_sources.end()) {
535                 return i->second;
536         }
537         return 0;
538 }
539
540 char *
541 OMF::read_name (size_t offset, size_t len)
542 {
543         char* buf = (char*) malloc (len+1);
544         fseek (file, offset, SEEK_SET);
545         fread (buf, len, 1, file);
546         buf[len] = '\0';
547         return buf;
548 }
549
550 bool
551 OMF::get_offset_and_length (const char* offstr, const char* lenstr, uint32_t& offset, uint32_t& len)
552 {
553         if (sscanf (offstr, "%d", &offset) == 0) {
554                 cerr << "bad offset\n";
555                 return false;
556         }
557
558         if (sscanf (lenstr, "%d", &len) == 0) {
559                 cerr << "bad length\n";
560                 return false;
561         }
562
563         if (((int32_t) offset) <= 0) {
564                 cerr << "illegal offset\n";
565                 return false;
566         }
567
568         if (((int32_t) len) <= 0) {
569                 cerr << "illegal length\n";
570                 return false;
571         }
572
573         return true;
574 }
575
576 int
577 OMF::create_xml ()
578 {
579         XMLNode* region;
580         XMLNode* playlist;
581         XMLNode* diskstream;
582         SourceInfo* sinfo;
583         char sbuf[256];
584         int route_max_channels;
585         int major;
586         int minor;
587         int micro;
588
589         major = version / 1000;
590         minor = version - (major * 1000);
591         micro = version - (major * 1000) - (minor * 100);
592
593         snprintf (sbuf, sizeof (sbuf), "%d.%d.%d", major, minor, micro);
594
595         session->add_property ("version", sbuf);
596         session->add_property ("name", session_name);
597
598         char **tracks;
599         int numtracks;
600         sqlite3_get_table(db, "SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT object FROM data WHERE property = 'OMFI:OOBJ:ObjClass' AND value = 'CMOB' LIMIT 1) AND property = 'OMFI:MOBJ:Slots')", &tracks, &numtracks, 0, 0);
601
602         for (int i = 1; i <= numtracks; i++) {
603
604                 int descCount;
605                 char **desc;
606
607                 sqlite3_get_table(db, sqlite3_mprintf("SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:MSLT:Segment' LIMIT 1) AND value = 'SEQU' LIMIT 1", tracks[i]), &desc, &descCount, 0, 0);
608                 sqlite3_free_table(desc);
609
610                 sinfo = 0;
611                 route_max_channels = 0;
612
613                 INFO ("Processing track %d / %d...\n", i, numtracks);
614
615                 if (descCount <= 0) {
616                         continue;
617                 }
618
619                 /* create a new route, which will mean that we need a new diskstream and playlist too
620                  */
621
622                 XMLNode* route = new_route_node ();
623                 XMLNode* playlist = new_playlist_node ();
624                 XMLNode* diskstream = new_diskstream_node ();
625
626                 /* route and playlist both need diskstream ID */
627
628                 route->add_property ("diskstream-id", diskstream->property ("id")->value());
629                 playlist->add_property ("orig-diskstream-id", diskstream->property ("id")->value());
630
631                 char **name;
632                 int nameCount;
633                 //sqlite3_get_table(db, sqlite3_mprintf("SELECT d2.offset, d2.length FROM data d1, data d2 WHERE d1.object LIKE '%s' AND d1.property LIKE 'OMFI:MSLT:TrackDesc' AND d2.object LIKE d1.value AND d2.property LIKE 'OMFI:TRKD:TrackName' LIMIT 1", tracks[i]), &name, &nameCount, 0, 0);
634                 sqlite3_get_table(db, sqlite3_mprintf("SELECT offset, length FROM data WHERE object IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:MSLT:TrackDesc' LIMIT 1) AND property = 'OMFI:TRKD:TrackName' LIMIT 1", tracks[i]), &name, &nameCount, 0, 0);
635                 if (nameCount > 0) {
636                         uint32_t nOffs;
637                         uint32_t nLen;
638                         if (get_offset_and_length (name[2], name[3], nOffs, nLen)) {
639                                 char* nBuf = read_name (nOffs, nLen);
640                                 route->add_property ("name", nBuf);
641                                 playlist->add_property ("name", nBuf);
642                                 diskstream->add_property ("name", nBuf);
643                                 diskstream->add_property ("playlist", nBuf);
644                                 free (nBuf);
645                         } else {
646                                 INFO ("Track %d has unreadable name\n", i);
647                                 snprintf (sbuf, sizeof (sbuf), "Track %d", i);
648                                 route->add_property ("name", sbuf);
649                                 playlist->add_property ("name", sbuf);
650                                 diskstream->add_property ("name", sbuf);
651                                 diskstream->add_property ("playlist", sbuf);
652                         }
653                 } else {
654                         INFO ("Track %d has no name\n", i);
655                         snprintf (sbuf, sizeof (sbuf), "Track %d", i);
656                         route->add_property ("name", sbuf);
657                         playlist->add_property ("name", sbuf);
658                         diskstream->add_property ("name", sbuf);
659                         diskstream->add_property ("playlist", sbuf);
660                 }
661                 sqlite3_free_table(name);
662
663                 char **rate;
664                 int rateCount;
665                 int num = 1, denom = 1;
666
667                 sqlite3_get_table(db, sqlite3_mprintf("SELECT offset FROM data WHERE object = %s AND property = 'OMFI:MSLT:EditRate' LIMIT 1", tracks[i]), &rate, &rateCount, 0, 0);
668
669                 if (rateCount > 0) {
670                         uint32_t rOffs = atoi(rate[1]);
671                         //sscanf(rate[1], "%d", &rOffs);
672                         fseek(file, rOffs, SEEK_SET);
673                         fread(&denom, 4, 1, file);
674                         denom = e32(denom);
675                         fread(&num, 4, 1, file);
676                         num = e32(num);
677                         INFO ("Rate = %d / %d\n", num, denom);
678                         if (frame_rate == 0) {
679                                 frame_rate = (double) num / (double) denom;
680                         }
681                         if (sample_rate == 0) {
682                                 sample_rate = denom;
683                         }
684                 } else {
685                         INFO ("OMF file is missing frame rate information for track %d\n", i);
686                         frame_rate = 0.04; // 25FPS
687                         if (sample_rate == 0) {
688                                 sample_rate = 44100;
689                         }
690                 }
691
692                 sqlite3_free_table(rate);
693
694                 char **items;
695                 int itemCount;
696                 //sqlite3_get_table(db, sqlite3_mprintf("SELECT d3.value FROM data d1, data d2, data d3 WHERE d1.object LIKE '%s' AND d1.property LIKE 'OMFI:MSLT:Segment' AND d2.object LIKE d1.value AND d2.property LIKE 'OMFI:SEQU:Components' AND d3.object LIKE d2.value", tracks[i]), &items, &itemCount, 0, 0);
697                 sqlite3_get_table(db, sqlite3_mprintf("SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:MSLT:Segment' LIMIT 1) AND property = 'OMFI:SEQU:Components' LIMIT 1)", tracks[i]), &items, &itemCount, 0, 0);
698                 double position = 0.0;
699                 int j;
700                 double fadeTime = 0.0;
701
702                 for (j = 1; j <= itemCount; j++) {
703
704                         printf("  item %d / %d\n", j, itemCount);
705
706                         char **len;
707                         int lenCount;
708                         double length = 0.0;
709                         int lenFrames = 0;
710
711                         region = 0;
712
713                         sqlite3_get_table(db, sqlite3_mprintf("SELECT value FROM data WHERE object = %s AND property = 'OMFI:CPNT:Length' LIMIT 1", items[j]), &len, &lenCount, 0, 0);
714
715                         if (lenCount <= 0) {
716                                 sqlite3_free_table(len);
717                                 continue;
718                         }
719
720                         char **type;
721                         int typeCount;
722
723                         sqlite3_get_table(db, sqlite3_mprintf("SELECT value FROM data WHERE object = %s AND property = 'OMFI:OOBJ:ObjClass' LIMIT 1", items[j]), &type, &typeCount, 0, 0);
724
725                         if (typeCount <= 0) {
726                                 sqlite3_free_table(type);
727                                 sqlite3_free_table(len);
728                                 continue;
729                         }
730
731                         lenFrames = atoi(len[1]);
732                         length = lenFrames * frame_rate;
733
734                         if (!strcmp(type[1], "TRAN")) {
735
736                                 position -= length;
737                                 char **effID;
738                                 int effIDCount;
739                                 sqlite3_get_table(db, sqlite3_mprintf("SELECT offset, length FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:TRAN:Effect' LIMIT 1) AND property = 'OMFI:EFFE:EffectKind' LIMIT 1) AND property = 'OMFI:EDEF:EffectID' LIMIT 1", items[j]), &effID, &effIDCount, 0, 0);
740                                 if (effIDCount > 0) {
741                                         uint32_t eOffs;
742                                         uint32_t eLen;
743                                         if (get_offset_and_length (effID[2], effID[3], eOffs, eLen)) {
744                                                 char* eBuf = read_name (eOffs, eLen);
745                                                 if (!strcmp(eBuf, "omfi:effect:StereoAudioDissolve") | !strcmp(eBuf, "omfi:effect:SimpleMonoAudioDissolve")) {
746                                                         fadeTime = length;
747                                                 }
748                                         }
749                                 }
750                                 sqlite3_free_table(effID);
751
752                         } else if (!strcmp(type[1], "FILL")) {
753
754                                 position += length;
755
756                         } else if (!strcmp(type[1], "NEST")) {
757
758                                 char **itemName;
759                                 int itemNameCount;
760                                 int64_t start = 0;
761
762                                 sqlite3_get_table(db, sqlite3_mprintf("SELECT offset, length FROM data WHERE object IN (SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:NEST:Slots' LIMIT 1)) AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property = 'OMFI:MOBJ:MobID' LIMIT 1) AND property = 'OMFI:MOBJ:Name' LIMIT 1", items[j]), &itemName, &itemNameCount, 0, 0);
763
764                                 char **startTime;
765                                 int startTimeCount;
766                                 //sqlite3_get_table(db, sqlite3_mprintf("SELECT d3.value FROM data d1, data d2, data d3 WHERE d1.object LIKE '%s' AND d1.property LIKE 'OMFI:NEST:SLOTS' AND d2.object LIKE d1.value AND d3.object LIKE d2.value AND d3.property LIKE 'OMFI:SCLP:StartTime' LIMIT 1", items[j]), &startTime, &startTimeCount, 0, 0);
767                                 sqlite3_get_table(db, sqlite3_mprintf("SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:NEST:Slots' LIMIT 1)) AND property = 'OMFI:SCLP:StartTime' LIMIT 1", items[j]), &startTime, &startTimeCount, 0, 0);
768                                 if (startTimeCount > 0) {
769                                         start = atoi(startTime[1]);
770                                 }
771
772                                 sqlite3_free_table(startTime);
773
774                                 char **itemEffect;
775                                 int itemEffectCount;
776                                 //sqlite3_get_table(db, sqlite3_mprintf("select d7.offset from data d1, data d2, data d3, data d4, data d5, data d6, data d7 where d1.object like '%s' and d1.property like 'OMFI:NEST:Slots' and d2.object like d1.value and d3.object like d2.value and d3.property like 'OMFI:EFFE:EffectSlots' and d4.object like d3.value and d5.object like d4.value and d5.property like 'OMFI:ESLT:ArgValue' and d6.object like d4.value and d6.property like 'OMFI:ESLT:ArgID' and d6.value like '1' and d7.object like d5.value and d7.property like 'OMFI:CVAL:Value' LIMIT 2", items[j]), &itemEffect, &itemEffectCount, 0, 0);
777                                 sqlite3_get_table(db, sqlite3_mprintf("SELECT offset FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:NEST:Slots' LIMIT 1)) AND property = 'OMFI:EFFE:EffectSlots' LIMIT 1) LIMIT 2) AND property = 'OMFI:ESLT:ArgValue' LIMIT 2) AND property like 'OMFI:CVAL:Value' LIMIT 1", items[j]), &itemEffect, &itemEffectCount, 0, 0);
778                                 if (itemEffectCount > 0) {
779                                         int effNum = 1;
780                                         int effDenom = 1;
781                                         uint32_t effOffs = atoi(itemEffect[1]);
782                                         //sscanf(itemEffect[1], "%d", &effOffs);
783                                         fseek(file, effOffs, SEEK_SET);
784                                         fread(&effDenom, 4, 1, file);
785                                         fread(&effNum, 4, 1, file);
786                                         double vol = (double) effNum / (double) effDenom;
787                                         //ctx->AddLine("VOLPAN %.8f 0.000000 1.000000 -1.000000", vol);
788                                         DEBUG("VOLPAN %.8f 0.000000 1.000000 -1.000000\n", vol);
789                                 }
790                                 sqlite3_free_table(itemEffect);
791
792                                 char **sourceFile;
793                                 int sourceFileCount;
794                                 //sqlite3_get_table(db, sqlite3_mprintf("SELECT d10.offset, d10.length FROM data d1, data d2, data d3, data d4, data d5, data d6, data d7, data d8, data d9, data d10 WHERE d1.object LIKE '%s' AND d1.property LIKE 'OMFI:NEST:Slots' AND d2.object LIKE d1.value AND d3.object LIKE d2.value AND d3.property LIKE 'OMFI:SCLP:SourceID' AND d4.value LIKE d3.value AND d4.property LIKE 'OMFI:MOBJ:MobID' AND d5.object LIKE d4.object AND d5.property LIKE 'OMFI:MOBJ:Slots' AND d6.object LIKE d5.value AND d7.object LIKE d6.value AND d7.property LIKE 'OMFI:MSLT:Segment' AND d8.object LIKE d7.value AND d8.property LIKE 'OMFI:SCLP:SourceID' AND d9.value LIKE d8.value AND d9.property LIKE 'OMFI:MOBJ:MobID' AND d10.object LIKE d9.object AND d10.property LIKE 'OMFI:MOBJ:Name' LIMIT 1", items[j]), &sourceFile, &sourceFileCount, 0, 0);
795                                 sqlite3_get_table(db, sqlite3_mprintf("SELECT offset, length FROM data WHERE object IN (SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:NEST:Slots' LIMIT 1) LIMIT 3) AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property = 'OMFI:MOBJ:MobID' LIMIT 1) AND property = 'OMFI:MOBJ:Slots' LIMIT 1) LIMIT 1) AND property = 'OMFI:MSLT:Segment' LIMIT 1) AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property = 'OMFI:MOBJ:MobID' LIMIT 1) AND property LIKE 'OMFI:MOBJ:Name' LIMIT 1", items[j]), &sourceFile, &sourceFileCount, 0, 0);
796                                 if (sourceFileCount > 0) {
797                                         uint32_t sfOffs;
798                                         uint32_t sfLen;
799
800                                         if (get_offset_and_length (sourceFile[2], sourceFile[3], sfOffs, sfLen)) {
801                                                 char *sfBuf = read_name (sfOffs, sfLen);
802
803                                                 if ((sinfo = get_known_source (sfBuf)) == 0) {
804                                                         cerr << "Reference to unknown source [" << sfBuf << "]1" << endl;
805                                                         return -1;
806                                                 }
807
808                                                 free (sfBuf);
809                                         } else {
810                                                 cerr << "offs/len illegal\n";
811                                         }
812                                 } else {
813                                         char **fallbackFile;
814                                         int fallbackFileCount;
815                                         //sqlite3_get_table(db, sqlite3_mprintf("SELECT d9.object FROM data d1, data d2, data d3, data d4, data d5, data d6, data d7, data d8, data d9 WHERE d1.object LIKE '%s' AND d1.property LIKE 'OMFI:NEST:Slots' AND d2.object LIKE d1.value AND d3.object LIKE d2.value AND d3.property LIKE 'OMFI:SCLP:SourceID' AND d4.value LIKE d3.value AND d4.property LIKE 'OMFI:MOBJ:MobID' AND d5.object LIKE d4.object AND d5.property LIKE 'OMFI:MOBJ:Slots' AND d6.object LIKE d5.value AND d7.object LIKE d6.value AND d7.property LIKE 'OMFI:MSLT:Segment' AND d8.object LIKE d7.value AND d8.property LIKE 'OMFI:SCLP:SourceID' AND d9.value LIKE d8.value AND d9.property LIKE 'OMFI:MDAT:MobID' LIMIT 1", items[j]), &fallbackFile, &fallbackFileCount, 0, 0);
816                                         sqlite3_get_table(db, sqlite3_mprintf("SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:NEST:Slots' LIMIT 1) LIMIT 3) AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property = 'OMFI:MOBJ:MobID' LIMIT 1) AND property = 'OMFI:MOBJ:Slots' LIMIT 1) LIMIT 1) AND property = 'OMFI:MSLT:Segment' LIMIT 1) AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property = 'OMFI:MDAT:MobID' LIMIT 1", items[j]), &fallbackFile, &fallbackFileCount, 0, 0);
817                                         if (fallbackFileCount > 0) {
818                                                 if ((sinfo = get_known_source (fallbackFile[1])) == 0) {
819                                                         cerr << "Reference to unknown source [" << fallbackFile[1] << "]2" << endl;
820                                                         return -1;
821                                                 }
822
823                                         } else {
824                                                 cerr << "no fallback file\n";
825                                         }
826
827                                         sqlite3_free_table(fallbackFile);
828                                 }
829
830
831                                 if (sinfo) {
832
833                                         region = new_region_node ();
834                                         playlist->add_child_nocopy (*region);
835
836                                         snprintf (sbuf, sizeof (sbuf), "%" PRId64, llrintf (position * sample_rate));
837                                         region->add_property ("position", sbuf);
838                                         snprintf (sbuf, sizeof (sbuf), "%" PRId64, llrintf (length * sample_rate));
839                                         region->add_property ("length", sbuf);
840                                         snprintf (sbuf, sizeof (sbuf), "%" PRId64, llrintf (start * frame_rate * sample_rate));
841                                         region->add_property ("start", sbuf);
842                                         set_region_sources (region, sinfo);
843
844                                         route_max_channels = max (route_max_channels, sinfo->channels);
845                                 }
846
847                                 sqlite3_free_table(sourceFile);
848                                 sqlite3_free_table(itemName);
849                                 position += length;
850
851                         } else if (!strcmp(type[1], "SCLP")) {
852
853                                 char **itemName;
854                                 int itemNameCount;
855                                 int64_t start = 0;
856                                 //sqlite3_get_table(db, sqlite3_mprintf("SELECT d5.offset, d5.length FROM data d3, data d4, data d5 WHERE d3.object LIKE '%s' AND d3.property LIKE 'OMFI:SCLP:SourceID' AND d4.value LIKE d3.value AND d5.object LIKE d4.object AND d5.property LIKE 'OMFI:MOBJ:Name' LIMIT 1", items[j]), &itemName, &itemNameCount, 0, 0);
857                                 sqlite3_get_table(db, sqlite3_mprintf("SELECT offset, length FROM data WHERE object IN (SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property = 'OMFI:MOBJ:MobID' LIMIT 1) AND property = 'OMFI:MOBJ:Name' LIMIT 1", items[j]), &itemName, &itemNameCount, 0, 0);
858
859                                 fadeTime = 0.0;
860
861                                 char **startTime;
862                                 int startTimeCount;
863                                 //sqlite3_get_table(db, sqlite3_mprintf("SELECT d3.value FROM data d3 WHERE d3.object LIKE '%s' AND d3.property LIKE 'OMFI:SCLP:StartTime'", items[j]), &startTime, &startTimeCount, 0, 0);
864                                 sqlite3_get_table(db, sqlite3_mprintf("SELECT value FROM data WHERE object = %s AND property = 'OMFI:SCLP:StartTime' LIMIT 1", items[j]), &startTime, &startTimeCount, 0, 0);
865                                 if (startTimeCount > 0) {
866                                         start = atoi(startTime[1]);
867                                 }
868                                 sqlite3_free_table(startTime);
869
870                                 char **sourceFile;
871                                 int sourceFileCount;
872                                 //sqlite3_get_table(db, sqlite3_mprintf("SELECT d10.offset, d10.length FROM data d3, data d4, data d5, data d6, data d7, data d8, data d9, data d10 WHERE d3.object LIKE '%s' AND d3.property LIKE 'OMFI:SCLP:SourceID' AND d4.value LIKE d3.value AND d4.property LIKE 'OMFI:MOBJ:MobID' AND d5.object LIKE d4.object AND d5.property LIKE 'OMFI:MOBJ:Slots' AND d6.object LIKE d5.value AND d7.object LIKE d6.value AND d7.property LIKE 'OMFI:MSLT:Segment' AND d8.object LIKE d7.value AND d8.property LIKE 'OMFI:SCLP:SourceID' AND d9.value LIKE d8.value AND d9.property LIKE 'OMFI:MOBJ:MobID' AND d10.object LIKE d9.object AND d10.property LIKE 'OMFI:MOBJ:Name' LIMIT 1", items[j]), &sourceFile, &sourceFileCount, 0, 0);
873                                 sqlite3_get_table(db, sqlite3_mprintf("SELECT offset, length FROM data WHERE object IN (SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property = 'OMFI:MOBJ:MobID' LIMIT 1) AND property = 'OMFI:MOBJ:Slots' LIMIT 1) LIMIT 1) AND property = 'OMFI:MSLT:Segment' LIMIT 1) AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property = 'OMFI:MOBJ:MobID' LIMIT 1) AND property LIKE 'OMFI:MOBJ:Name' LIMIT 1", items[j]), &sourceFile, &sourceFileCount, 0, 0);
874
875                                 if (sourceFileCount > 0) {
876                                         uint32_t sfOffs;
877                                         uint32_t sfLen;
878
879                                         if (get_offset_and_length (sourceFile[2], sourceFile[3], sfOffs, sfLen)) {
880                                                 cerr << "get source file from " << sfOffs << " + " << sfLen << endl;
881                                                 char *sfBuf = read_name (sfOffs, sfLen);
882
883                                                 if ((sinfo = get_known_source (sfBuf)) == 0) {
884                                                         cerr << "Reference to unknown source [" << sfBuf << ']' << endl;
885                                                         return -1;
886                                                 }
887
888                                                 free (sfBuf);
889                                         } else {
890                                                 cerr << "can't get off+len\n";
891                                         }
892                                 } else {
893                                         char **fallbackFile;
894                                         int fallbackFileCount;
895                                         //sqlite3_get_table(db, sqlite3_mprintf("SELECT d9.object FROM data d3, data d4, data d5, data d6, data d7, data d8, data d9 WHERE d3.object LIKE '%s' AND d3.property LIKE 'OMFI:SCLP:SourceID' AND d4.value LIKE d3.value AND d4.property LIKE 'OMFI:MOBJ:MobID' AND d5.object LIKE d4.object AND d5.property LIKE 'OMFI:MOBJ:Slots' AND d6.object LIKE d5.value AND d7.object LIKE d6.value AND d7.property LIKE 'OMFI:MSLT:Segment' AND d8.object LIKE d7.value AND d8.property LIKE 'OMFI:SCLP:SourceID' AND d9.value LIKE d8.value AND d9.property LIKE 'OMFI:MDAT:MobID' LIMIT 1", items[j]), &fallbackFile, &fallbackFileCount, 0, 0);
896                                         sqlite3_get_table(db, sqlite3_mprintf("SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object IN (SELECT object FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT value FROM data WHERE object IN (SELECT object FROM data WHERE value IN (SELECT value FROM data WHERE object = %s AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property = 'OMFI:MOBJ:MobID' LIMIT 1) AND property = 'OMFI:MOBJ:Slots' LIMIT 1) LIMIT 1) AND property = 'OMFI:MSLT:Segment' LIMIT 1) AND property = 'OMFI:SCLP:SourceID' LIMIT 1)AND property = 'OMFI:SCLP:SourceID' LIMIT 1) AND property LIKE 'OMFI:MDAT:MobID' LIMIT 1", items[j]), &fallbackFile, &fallbackFileCount, 0, 0);
897                                         if (fallbackFileCount > 0) {
898                                                 if ((sinfo = get_known_source (fallbackFile[1])) == 0) {
899                                                         cerr << "Reference to unknown source [" << fallbackFile[1] << ']' << endl;
900                                                         return -1;
901                                                 }
902
903                                         }
904                                         sqlite3_free_table(fallbackFile);
905                                 }
906
907                                 if (sinfo) {
908
909                                         region = new_region_node ();
910                                         playlist->add_child_nocopy (*region);
911
912                                         snprintf (sbuf, sizeof (sbuf), "%" PRId64, llrintf (position * sample_rate));
913                                         region->add_property ("position", sbuf);
914                                         snprintf (sbuf, sizeof (sbuf), "%" PRId64, llrintf (length * sample_rate));
915                                         region->add_property ("length", sbuf);
916                                         snprintf (sbuf, sizeof (sbuf), "%" PRId64, llrintf (start * frame_rate * sample_rate));
917                                         region->add_property ("start", sbuf);
918                                         set_region_sources (region, sinfo);
919
920                                         route_max_channels = max (route_max_channels, sinfo->channels);
921                                 }
922
923                                 sqlite3_free_table(sourceFile);
924                                 sqlite3_free_table(itemName);
925                                 position += length;
926                         }
927
928                         sqlite3_free_table(type);
929                         sqlite3_free_table(len);
930                 }
931
932                 /* finalize route information */
933
934                 cerr << "Set up track with " << route_max_channels << " channels" << endl;
935                 set_route_node_channels (route, route_max_channels, route_max_channels, true);
936                 sqlite3_free_table(items);
937         }
938
939         sqlite3_free_table(tracks);
940
941         id_counter++;
942         snprintf (sbuf, sizeof (sbuf), "%" PRId64, id_counter);
943         session->add_property ("id-counter", sbuf);
944         snprintf (sbuf, sizeof (sbuf), "%" PRId32, sample_rate);
945         session->add_property ("sample-rate", sbuf);
946
947         XMLTree xml;
948
949         xml.set_root (session);
950
951         vector<string> v;
952
953         v.push_back (base_dir);
954         v.push_back (session_name);
955         v.push_back (session_name + ".ardour");
956
957         xml.write (Glib::build_filename(v).c_str());
958         return 0;
959 }
960
961
962 static void
963 print_help (const char* execname)
964 {
965         cout << execname
966              << " [ -r sample-rate ]"
967              << " [ -n session-name ]"
968              << " [ -v ardour-session-version ]"
969              << " OMF2_session_file"
970              << endl;
971         exit (1);
972 }
973
974 int
975 main (int argc, char* argv[])
976 {
977         const char *execname = strrchr (argv[0], '/');
978         const char* optstring = "r:n:v:h";
979         const char* session_name = 0;
980         int         sample_rate = 0;
981         int         version = 0;
982
983         const struct option longopts[] = {
984                 { "rate", 1, 0, 'r' },
985                 { "name", 1, 0, 'n' },
986                 { "version", 1, 0, 'v' },
987                 { "help", 0, 0, 'h' },
988                 { 0, 0, 0, 0 }
989         };
990
991
992         int option_index = 0;
993         int c = 0;
994
995         while (1) {
996                 c = getopt_long (argc, argv, optstring, longopts, &option_index);
997
998                 if (c == -1) {
999                         break;
1000                 }
1001
1002                 switch (c) {
1003                 case 'r':
1004                         sample_rate = atoi (optarg);
1005                         break;
1006
1007                 case 'n':
1008                         session_name = optarg;
1009                         break;
1010
1011                 case 'v':
1012                         version = atoi (optarg);
1013                         break;
1014
1015                 case 'h':
1016                 default:
1017                         print_help (execname);
1018                         break;
1019                 }
1020         }
1021
1022         if (optind > argc) {
1023                 print_help (execname);
1024                 /*NOTREACHED*/
1025         }
1026
1027         OMF omf;
1028
1029         if (version) {
1030                 omf.set_version (version);
1031         }
1032
1033         if (sample_rate) {
1034                 omf.set_sample_rate (sample_rate);
1035         }
1036
1037         if (session_name) {
1038                 omf.set_session_name (session_name);
1039         } else {
1040                 omf.set_session_name (basename_nosuffix (argv[optind]));
1041         }
1042
1043         if (omf.init () == 0) {
1044
1045                 if (omf.load (argv[optind++]) == 0) {
1046                         omf.create_xml ();
1047                 }
1048         }
1049
1050         return 0;
1051 }