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
OMF()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
~OMF()140 OMF::~OMF ()
141 {
142 /* clean up */
143 sqlite3_close (db);
144 fclose (file);
145 }
146
147 void
set_sample_rate(int sr)148 OMF::set_sample_rate (int sr)
149 {
150 sample_rate = sr;
151 }
152
153 void
set_session_name(const std::string & str)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
set_version(int v)161 OMF::set_version (int v)
162 {
163 version = v;
164 }
165
166 int
init()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
get_audio_info(const std::string & path)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
add_id(XMLNode * node)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*
new_playlist_node()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*
new_diskstream_node()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
set_region_sources(XMLNode * region,SourceInfo * sinfo)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
legalize_name(string & name)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
set_route_node_channels(XMLNode * route,int in,int out,bool send_to_master)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*
new_route_node()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*
new_region_node()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*
new_source_node()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*
get_known_source(const char * name)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 *
read_name(size_t offset,size_t len)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
get_offset_and_length(const char * offstr,const char * lenstr,uint32_t & offset,uint32_t & len)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
create_xml()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
print_help(const char * execname)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
main(int argc,char * argv[])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 }
1052