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