1 /* Demo Recorder for MAEMO 5
2 * Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2,
5 * or (at your option) any later version, as published by the Free
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 public class DemoRecorder : Hildon.Program {
22 ApplicationSettings application_settings;
23 Hildon.StackableWindow window;
25 SettingsStructures.ProjectSettings project;
27 EncodePipeline encodepipeline;
28 PlayPipeline playpipeline;
29 RecordPipeline recordpipeline;
30 PlayerTransport player;
32 uint track_counter { get { return (null != tracks) ? tracks.get_children().length() : 0; }}
37 uint playback_timer_id;
38 uint recording_timer_id;
39 uint encoding_timer_id;
40 ulong recording_position_duration_id;
41 Osso.Context osso_context;
42 //int64 track_position;
43 //int64 record_position;
44 //int64 track_duration;
46 public signal void encoding_starting();
47 public signal void encoding_started();
48 public signal void encoding_ending();
49 public signal void encoding_ended();
50 public signal void recording_starting();
51 public signal void recording_started();
52 public signal void recording_ending();
53 public signal void recording_ended();
54 public signal void playback_starting();
55 public signal void playback_started();
56 public signal void playback_ending();
57 public signal void playback_ended();
58 public signal void playback_pausing();
59 public signal void playback_paused();
60 public signal void playback_resuming();
61 public signal void playback_resumed();
62 public signal void playback_position_duration(int64 position, int64 duration);
63 public signal void recording_position_duration(int64 position, int64 duration);
64 public signal void encoding_position_duration(int64 position, int64 duration);
66 public void playpipeline_position_duration_callback(Gst.Pipeline sender, int64 position, int64 duration) {
67 playback_position_duration(position, duration);
68 if (position >= duration) playback_time_elapsed();
70 public void recordpipeline_position_duration_callback(Gst.Pipeline sender, int64 position, int64 duration) {
71 recording_position_duration(position, duration);
72 if (0 < duration && position >= duration) recording_time_elapsed();
74 public void encodepipeline_position_duration_callback(Gst.Pipeline sender, int64 position, int64 duration) {
75 encoding_position_duration(position, duration);
76 if (position >= duration) encoding_time_elapsed();
78 private bool update_time_and_duration_play_pipeline() {
79 playpipeline.get_duration_info();
82 private bool update_time_and_duration_record_pipeline() {
83 recordpipeline.get_duration_info();
86 private bool update_time_and_duration_encode_pipeline() {
87 encodepipeline.get_duration_info();
90 private void recording_time_elapsed() {
91 Source.remove(recording_timer_id);
93 this should only ever fire for fixed time 'recording'
94 i.e. bouncing or mixing tracks
95 hence we can use it as a trigger
98 private void encoding_time_elapsed() {
99 Source.remove(encoding_timer_id);
101 this should only ever fire for fixed time 'recording'
102 i.e. bouncing or mixing tracks
103 hence we can use it as a trigger
106 private void playback_time_elapsed() {
107 if (!this.recording) Source.remove(playback_timer_id);
111 osso_context = new Osso.Context("DemoRecorder", "0.1", false, null);
112 project = SettingsStructures.ProjectSettings();
115 window = new Hildon.StackableWindow();
116 window.destroy.connect(Gtk.main_quit);
117 window.delete_event.connect(on_delete_event);
118 window.set_title("Demo Recorder");
120 Gtk.VBox container = new Gtk.VBox(false, 0);
121 Hildon.PannableArea scrollwin = new Hildon.PannableArea();
122 tracks = new Gtk.VBox(false, 0);
123 player = new PlayerTransport();
124 player.window = this.window;
125 this.playback_position_duration.connect(player.position_duration_callback);
126 player.play_clicked.connect(mixer_play_all);
127 player.pause_toggled.connect(mixer_pause);
128 player.stop_clicked.connect(mixer_stop);
129 player.volume_updated.connect(mixer_volume_updated);
130 player.panorama_updated.connect(mixer_panorama_updated);
131 player.eq_updated.connect(mixer_eq_updated);
132 player.record_toggled.connect(mixer_record);
133 container.pack_start(player, false, false, 0);
134 scrollwin.add_with_viewport(tracks);
135 container.pack_end(scrollwin, true, true, 0);
136 window.add(container);
137 mixer = new MixerBin("mixer");
138 //playpipeline = new PlayPipeline("mixer");
141 private void mixer_volume_updated(PlayerTransport sender, double volume) {
142 mixer.set_volume(volume);
145 private void mixer_panorama_updated(PlayerTransport sender, double panorama) {
146 mixer.set_panorama(panorama);
149 private void mixer_eq_updated(PlayerTransport sender, int band, double val) {
150 mixer.set_eq(band, val);
153 private bool keep_screen_on() {
154 if (this.recording || this.playing) {
155 Osso.Status status = this.osso_context.display_blanking_pause();
156 /*if (Osso.Status.OK == status) {
157 stdout.printf("%s\n", "OK");
159 else if (Osso.Status.INVALID == status) {
160 stdout.printf("%s\n", "INVALID");
162 else if (Osso.Status.ERROR == status) {
163 stdout.printf("%s\n", "ERROR");
166 stdout.printf("%s\n", "UNKNOWN");
172 Source.remove(screen_timer_id);
177 private void mixer_play_all() {
178 /// TODO clean up old mixer first?
179 mixer = new MixerBin("mixer");
181 if (0 < project.tracks.length()) {
184 if (0 != screen_timer_id) Source.remove(screen_timer_id);
185 screen_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND * 20, keep_screen_on);
186 var children = tracks.get_children();
187 foreach (var child in children) {
188 TrackTransport tt = child as TrackTransport;
189 if (null != tt && tt.get_active_state()) {
190 mixer.add_track(tt.track_bin);
193 //for (int i = 0; i < tracks.children.length(); ++i) {
194 //Object tt = tracks.children.nth_data(i);
195 //stdout.printf("Type %s\n",tt.get_type().name());
197 //if (tt.track_bin.IsActive) {
198 //stdout.printf("Track %s active and has been added\n", i.to_string());
199 //track.bin.set_start_time((Gst.ClockTime)(Time.Nanoseconds.SECOND * idx));
200 //mixer.add_track(tt.track_bin);
205 /*while (idx < project.tracks.length()) {
206 Track track = project.tracks.nth_data(idx);
208 //stdout.printf("Track %s active and has been added\n", idx.to_string());
209 stdout.printf("Track_bin %s active and has been added\n", track.bin.IsActive.to_string());
210 //track.bin.set_start_time((Gst.ClockTime)(Time.Nanoseconds.SECOND * idx));
211 mixer.add_track(track.bin);
215 playpipeline = new PlayPipeline("player", mixer);
216 this.playback_position_duration.connect(player.position_duration_callback);
217 playpipeline.position_duration.connect(playpipeline_position_duration_callback);
221 update_time_and_duration_play_pipeline();
222 if (0 != playback_timer_id) Source.remove(playback_timer_id);
223 playback_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND / 10, update_time_and_duration_play_pipeline);
225 if (this.recording) {
228 if (0 != screen_timer_id) Source.remove(screen_timer_id);
229 screen_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND * 20, keep_screen_on);
231 this.playback_position_duration.disconnect(player.position_duration_callback);
232 this.recording_position_duration.connect(player.position_duration_callback);
233 recordpipeline.position_duration.connect(recordpipeline_position_duration_callback);
234 recording_starting();
235 recordpipeline.record();
237 update_time_and_duration_record_pipeline();
238 if (0 != recording_timer_id) Source.remove(recording_timer_id);
239 recording_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND / 10, update_time_and_duration_record_pipeline);
243 private void mixer_play() {
250 private void mixer_pause(bool val) {
253 playpipeline.pause();
263 private void mixer_record(bool val) {
268 this.recording = false;
272 private void mixer_stop() {
273 if (this.recording) {
275 this.recording = false;
276 recordpipeline.stop();
277 //this.import_track_from_uri(Filename.to_uri(recordpipeline.get_location()));
278 Track track = Track.with_uri(Filename.to_uri(recordpipeline.get_location()), "Track-" + (project.last_track_number).to_string(), true, true, 1.0, 0.0);
279 project.tracks.append(track);
280 add_track_transport(track);
281 recordpipeline = null;
288 this.playing = false;
294 private void construct_menu() {
295 Hildon.AppMenu menu = new Hildon.AppMenu();
296 var btnNew = new Gtk.Button.with_label("New project");
297 btnNew.clicked.connect(new_project);
299 var btnOpen = new Gtk.Button.with_label("Open project");
300 btnOpen.clicked.connect(open_project);
301 menu.append(btnOpen);
302 var btnSave = new Gtk.Button.with_label("Save project");
303 btnSave.clicked.connect(save_project);
304 menu.append(btnSave);
305 var btnImport = new Gtk.Button.with_label("Import track");
306 btnImport.clicked.connect(open_import);
307 menu.append(btnImport);
308 var btnStartACM = new Gtk.Button.with_label("Input settings");
309 btnStartACM.clicked.connect(start_acm);
310 menu.append(btnStartACM);
311 var btnBounce = new Gtk.Button.with_label("Bounce tracks");
312 btnBounce.clicked.connect(prepare_bounce);
313 menu.append(btnBounce);
314 var btnMixdown = new Gtk.Button.with_label("Mixdown");
315 btnMixdown.clicked.connect(prepare_mixdown);
316 menu.append(btnMixdown);
317 var btnSettings = new Gtk.Button.with_label("Settings");
318 btnSettings.clicked.connect(show_settings);
319 menu.append(btnSettings);
320 var btnAbout = new Gtk.Button.with_label("About");
321 btnAbout.clicked.connect(show_about);
322 menu.append(btnAbout);
324 window.set_app_menu(menu);
326 private void show_about() {
327 AboutDialog dlg = new AboutDialog(window);
328 dlg.set_transient_for(window);
333 private void show_settings() {
334 File settings = File.new_for_path(Environment.get_home_dir() + "/.demorecorder/settings.xml");
336 FirstRunWizard dlg = new FirstRunWizard(window, "Demo Recorder");
337 dlg.set_transient_for(window);
338 dlg.app_settings = application_settings;
340 application_settings = dlg.app_settings;
341 bool saved = ApplicationSettings.save_settings(settings.get_path(), application_settings, ref errors);
345 private void new_project() {
347 // is the current project dirty?
349 // run an are you sure dialog
350 // Do you want to save your current project first? Yes, No, Cancel
351 YesNoCancelDialog alert = new YesNoCancelDialog(window, "Save changes to current project?", "You have unsaved changes in your current project.\nWould you like to save them first?", Gtk.ResponseType.CANCEL);
352 alert.set_transient_for(window);
353 int ret = alert.run();
356 // yes - save_project()
357 if (Gtk.ResponseType.YES == ret) {
360 // cancel - return and do nothing
361 else if (Gtk.ResponseType.CANCEL == ret || Gtk.ResponseType.NO != ret) {
364 // carry on and create_project()
366 ProjectSettingsDialog dlg = new ProjectSettingsDialog.with_values(window, "New project settings", "New project", "", "", application_settings.working_directory);
367 dlg.set_transient_for(window);
369 if (Gtk.ResponseType.OK == ret) {
371 this.project = SettingsStructures.ProjectSettings();
372 this.project.name = dlg.get_project_name();
373 this.project.working_directory = dlg.get_project_directory();
374 // ensure that the directories exist.
375 DirUtils.create_with_parents(this.project.working_directory + "/tracks", 0755);
376 DirUtils.create_with_parents(this.project.working_directory + "/mixes", 0755);
377 DirUtils.create_with_parents(this.project.working_directory + "/tmp", 0755);
378 this.project.location = dlg.get_project_filename();
384 // Now should clean up the display in preparation for working on this new project.
385 //load_project(this.project.location);
386 application_settings.last_project_name = project.name;
387 application_settings.last_project_location = project.location;
388 window.set_title(project.name);
393 private void open_project() {
394 // is the current project dirty?
396 // run an are you sure dialog
397 // Do you want to save your current project first? Yes, No, Cancel
398 YesNoCancelDialog alert = new YesNoCancelDialog(window, "Save changes to current project?", "You have unsaved changes in your current project.\nWould you like to save them first?", Gtk.ResponseType.CANCEL);
399 alert.set_transient_for(window);
400 int ret = alert.run();
403 // yes - save_project()
404 if (Gtk.ResponseType.YES == ret) {
407 // cancel - return and do nothing
408 else if (Gtk.ResponseType.CANCEL == ret || Gtk.ResponseType.NO != ret) {
411 // carry on and load_project()
414 Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(window, Gtk.FileChooserAction.OPEN);
415 file_chooser.set_show_upnp(false);
416 file_chooser.set_current_folder(application_settings.working_directory);
417 int ret = file_chooser.run();
418 if (Gtk.ResponseType.OK == ret) {
419 load_project(file_chooser.get_filename());
421 file_chooser.destroy ();
425 private void load_project(string location) {
427 project = SettingsStructures.ProjectSettings();
429 if (XmlHelpers.parse_project_settings_file(location, "1.0", ref project, ref errors)) {
430 // need to tear down and build up the interface again.
431 // as a test let's save it for inspection
432 //save_project_settings(project.location);
434 application_settings.last_project_name = project.name;
435 application_settings.last_project_location = project.location;
436 if (ApplicationSettings.save_settings(Environment.get_home_dir() + "/.demorecorder/settings.xml", application_settings, ref errors)) {
437 window.set_title(project.name);
438 for (int idx = 0; idx < project.tracks.length(); ++idx) {
439 add_track_transport((Track)project.tracks.nth_data(idx));
443 stdout.printf("%s\n", errors);
448 Hildon.Note note = new Hildon.Note.information(window, "Project couldn't be opened!\n%s".printf(errors));
455 private void remove_all_tracks() {
456 List<Gtk.Widget> children = tracks.get_children();
457 for (int idx = 0; idx < children.length(); ++idx) {
458 tracks.remove((Gtk.Widget)children.nth_data(idx));
460 for (int idx = ((int)project.tracks.length()) - 1; idx > -1; --idx) {
461 project.tracks.delete_link(project.tracks.nth(idx));
463 player.position_duration_callback(0, 0);
465 private void remove_track_interactive(TrackTransport tt) {
468 switch(application_settings.remove_track_action) {
469 case ApplicationSettings.RemoveTrackAction.DELETE_FILE:
471 message = "Track deleted.";
472 FileUtils.remove(Filename.from_uri(tt.track.uri, out ns));
474 case ApplicationSettings.RemoveTrackAction.PRESERVE_FILE:
476 message = "Track removed.";
478 case ApplicationSettings.RemoveTrackAction.PROMPT:
479 YesNoCancelDialog dlg = new YesNoCancelDialog(window, "Remove Track", "Do you want to delete the source file permanently?", Gtk.ResponseType.CANCEL);
480 dlg.set_transient_for(window);
483 if (Gtk.ResponseType.YES == ret) {
485 message = "Track deleted.";
486 FileUtils.remove(Filename.from_uri(tt.track.uri, out ns));
488 else if (Gtk.ResponseType.NO == ret) {
490 message = "Track removed.";
497 Hildon.Note note = new Hildon.Note.information(window, "%s".printf(message));
503 private void remove_track(TrackTransport tt) {
504 project.tracks.remove(tt.track);
506 // rebuild project.tracks
507 for (int idx = (int)project.tracks.length(); idx >= 0; --idx) {
508 project.tracks.remove_link(project.tracks.nth(idx));
510 foreach (var child in tracks.get_children()) {
511 project.tracks.append(((TrackTransport)child).track);
513 project_dirty = true;
517 private void save_project() {
518 // rebuild track settings
519 for (int idx = (int)project.tracks.length(); idx >= 0; --idx) {
520 project.tracks.remove_link(project.tracks.nth(idx));
522 foreach (var child in tracks.get_children()) {
523 project.tracks.append(((TrackTransport)child).track);
525 if (0 < project.location.length) {
526 save_project_settings(project.location);
529 do_save_as_project();
531 //stdout.printf(project.to_xml_string());
533 private void do_save_as_project() {
535 Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(window, Gtk.FileChooserAction.SAVE);
536 file_chooser.set_show_upnp(false);
537 int ret = file_chooser.run();
538 if (Gtk.ResponseType.OK == ret) {
539 project.location = file_chooser.get_filename();
540 save_project_settings(project.location);
542 file_chooser.destroy ();
545 private void save_project_settings(string location) {
546 bool success = false;
547 Hildon.gtk_window_set_progress_indicator(window, (uint)true);
548 string contents = project.to_xml_string();
549 // should ask each ui element for it's updated settings to be on the safe side first.
551 if (0 < contents.length)
553 contents = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + contents;
554 if (FileUtils.set_contents(location, contents, contents.length)) {
555 project_dirty = false;
556 // alert user of success? no!
557 //Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Project saved.");
558 //((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
562 // alert user of failure to save file.
563 Hildon.Note alert = new Hildon.Note.information(window, "Project couldn't be saved!\nFailed to save the project settings file.");
570 // alert user of failure to create serialize settings.
571 Hildon.Note alert = new Hildon.Note.information(window, "Project couldn't be saved!\nFailed to serialize the project settings.");
576 Hildon.gtk_window_set_progress_indicator(window, (uint)false);
579 private void start_acm() {
580 AcmPopUp acm = new AcmPopUp.with_settings("Input Settings", window, project.monitor);
581 acm.set_transient_for(window);
583 if (Gtk.ResponseType.OK == acm.run()) {
584 project.monitor = acm.get_settings_structure();
585 project_dirty = true;
592 private void prepare_mixdown() {
593 MixdownPopUp dlg = new MixdownPopUp.with_settings("Mixdown your track", window, project.mixdown);
594 dlg.set_transient_for(window);
595 if (Gtk.ResponseType.OK == dlg.run()) {
596 project.mixdown = dlg.get_settings_structure();
597 project_dirty = true;
604 private void do_mixdown() {
605 Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int",
606 "rate", typeof (int), 48000,
607 "channels", typeof (int), 2,
608 "depth", typeof (int), 16);
609 mixer = new MixerBin("mixer");
611 if (0 < project.tracks.length()) {
612 while (idx < project.tracks.length()) {
613 Track track = project.tracks.nth_data(idx);
615 mixer.add_track(track.bin);
619 this.recording = false;
620 this.playing = false;
621 Gst.Element? encoder = EncoderFactory.get_encoder_bin("encoder", project.mixdown.encoder, project.mixdown.quality, "artist=%s,title=%s,album=%s".printf(project.mixdown.artist, project.mixdown.title, project.mixdown.album));
622 if (null != encoder) {
623 encodepipeline = new EncodePipeline("mixdown", project.mixdown.location, mixer, encoder, caps);
624 encodepipeline.position_duration.connect(encodepipeline_position_duration_callback);
625 Hildon.gtk_window_set_progress_indicator(window, (uint)true);
627 encodepipeline.encode();
629 update_time_and_duration_encode_pipeline();
630 if (0 != recording_timer_id) Source.remove(encoding_timer_id);
631 encoding_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND, update_time_and_duration_encode_pipeline);
632 ProgressPopUp dlg = new ProgressPopUp("Mixing down", window);
633 dlg.set_message("Mixing down your track.\nThis may take some time depending on the duration of the audio.\n");
634 dlg.set_transient_for(window);
635 this.encoding_position_duration.connect((p,d) => {
636 double percent = ((double)(p / Time.Nanoseconds.MILLISECOND) / (double)(d / Time.Nanoseconds.MILLISECOND));
637 dlg.set_progress(percent);
638 dlg.set_progress_text("%s of %s completed".printf(Time.time_to_string(p), Time.time_to_string(d)));
641 // this.mixer_stop();
644 encodepipeline.end_of_stream.connect((e) => {encoding_ending();dlg.close_me();encoding_time_elapsed();this.mixer_stop();encoding_ended();});
645 if (Gtk.ResponseType.CANCEL == dlg.run()) {
646 // Should really stop the mixer and delete the files!
653 Hildon.gtk_window_set_progress_indicator(window, (uint)false);
654 Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Mixdown complete");
655 ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
658 Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Mixdown failed! Encoder not available.");
659 ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
664 private void prepare_bounce() {
665 project.last_track_number++;
666 string new_location = "%s/tracks/bounce-%s.wav".printf(project.working_directory, project.last_track_number.to_string());
667 string name = "bounce-" + project.last_track_number.to_string();
668 do_bounce(new_location);
671 private void do_bounce(string filename) {
672 Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int",
673 "rate", typeof (int), 48000,
674 "channels", typeof (int), 2,
675 "depth", typeof (int), 16);
676 //Gst.Element src = Gst.ElementFactory.make("pulsesrc", "input");
677 //src.set_property("device", "source.hw0");
678 mixer = new MixerBin("mixer");
680 if (0 < project.tracks.length()) {
681 while (idx < project.tracks.length()) {
682 Track track = project.tracks.nth_data(idx);
684 mixer.add_track(track.bin);
688 this.recording = true;
689 this.playing = false;
690 recordpipeline = new RecordPipeline("bouncer", filename, mixer, caps, "wavenc");
691 recordpipeline.bouncing = true;
692 recordpipeline.position_duration.connect(recordpipeline_position_duration_callback);
693 Hildon.gtk_window_set_progress_indicator(window, (uint)true);
694 recording_starting();
695 recordpipeline.record();
697 update_time_and_duration_record_pipeline();
698 if (0 != recording_timer_id) Source.remove(recording_timer_id);
699 recording_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND, update_time_and_duration_record_pipeline);
700 ProgressPopUp dlg = new ProgressPopUp("Bouncing tracks", window);
701 dlg.set_transient_for(window);
702 dlg.set_message("Bouncing your selected tracks.\nThis may take some time depending on the duration of the audio.\n");
703 this.recording_position_duration.connect((p,d) => {
704 double percent = ((double)(p / Time.Nanoseconds.MILLISECOND) / (double)(d / Time.Nanoseconds.MILLISECOND));
705 dlg.set_progress(percent);
706 dlg.set_progress_text("%s of %s completed".printf(Time.time_to_string(p), Time.time_to_string(d)));
710 this.recording = false;
711 if (0 != recording_timer_id) Source.remove(recording_timer_id);
714 if (Gtk.ResponseType.CANCEL == dlg.run()) {
715 // Should really stop the mixer and delete the files!
717 this.recording = false;
722 Hildon.gtk_window_set_progress_indicator(window, (uint)false);
723 Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Bounced track added to mixer");
724 ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
728 public void prepare_record() {
729 project.last_track_number++;
730 string new_location = "%s/tracks/track-%s.wav".printf(project.working_directory, project.last_track_number.to_string());
731 string name = "Track-" + project.last_track_number.to_string();
732 do_record(name, new_location);
735 public void do_record(string name, string filename) {
736 Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int",
737 "rate", typeof (int), 48000,
738 "channels", typeof (int), 2,
739 "depth", typeof (int), 16);
740 InputBin src = new InputBin.with_settings("input", project.monitor.input);
741 //Gst.Element src = Gst.ElementFactory.make("pulsesrc", "input");
742 //src.set_property("device", "source.hw0");
743 recordpipeline = new RecordPipeline("recorder", filename, src, caps, "wavenc");
744 recordpipeline.bouncing = false;
745 this.recording = true;
746 //recordpipeline.record();
749 public void open_import() {
750 Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(window, Gtk.FileChooserAction.OPEN);
751 file_chooser.set_show_upnp(false);
752 file_chooser.set_current_folder(application_settings.working_directory);
753 if (file_chooser.run () == Gtk.ResponseType.OK) {
754 import_track_from_uri(file_chooser.get_uri());
756 file_chooser.destroy ();
759 public void import_track_from_uri(string uri) {
760 /// TODO - Should become import track from uri and convert to wav and name appropriately
761 ++project.last_track_number;
762 project_dirty = true;
763 Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int",
764 "rate", typeof (int), 48000,
765 "channels", typeof (int), 2,
766 "depth", typeof (int), 16);
767 /// TODO check that directories exist
768 string new_location = "%s/tracks/import-%s.wav".printf(project.working_directory, project.last_track_number.to_string());
769 string name = "Import-" + project.last_track_number.to_string();
770 ImportBin import_bin = new ImportBin(name);
771 import_bin.set_uri(uri);
773 /// TODO encode to correct destination and add track
774 this.recording = false;
775 this.playing = false;
776 recordpipeline = new RecordPipeline("importer", new_location, import_bin, caps, "wavenc");
777 recordpipeline.bouncing = true;
778 recordpipeline.position_duration.connect(recordpipeline_position_duration_callback);
779 Hildon.gtk_window_set_progress_indicator(window, (uint)true);
780 recording_starting();
781 recordpipeline.record();
783 update_time_and_duration_record_pipeline();
784 if (0 != recording_timer_id) Source.remove(recording_timer_id);
785 recording_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND, update_time_and_duration_record_pipeline);
786 ProgressPopUp dlg = new ProgressPopUp("Importing track", window);
787 dlg.set_transient_for(window);
788 dlg.set_message("Importing your selected track.\nThis may take some time depending on the duration of the audio.\n");
789 this.recording_position_duration.connect((p,d) => {
790 double percent = ((double)(p / Time.Nanoseconds.MILLISECOND) / (double)(d / Time.Nanoseconds.MILLISECOND));
791 dlg.set_progress(percent);
792 dlg.set_progress_text("%s of %s completed".printf(Time.time_to_string(p), Time.time_to_string(d)));
794 recordpipeline.end_of_stream.connect(() => {
796 recordpipeline.stop();
797 if (0 != recording_timer_id) Source.remove(recording_timer_id);
798 Track track = Track.with_uri(Filename.to_uri(new_location), name, true, true, 1.0, 0.0);
799 project.tracks.append(track);
800 add_track_transport(track);
801 this.recording = false;
803 if (Gtk.ResponseType.CANCEL == dlg.run()) {
804 // Should really stop the mixer and delete the files!
806 //Track track = new Track.with_uri(Filename.to_uri(new_location), name, true, true, 1.0, 0.0);
807 //project.tracks.append(track);
808 //add_track_transport(track);
809 recordpipeline.stop();
810 this.recording = false;
814 Hildon.gtk_window_set_progress_indicator(window, (uint)false);
815 Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Imported track added to mixer");
816 ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
819 private void add_track_transport(Track track) {
820 TrackTransport trackt = new TrackTransport();
821 trackt.window = this.window;
822 trackt.track_bin = track.bin;
823 trackt.track = track;
824 trackt.title = track.label; //"Track %n".printf(project.track.length());
825 trackt.name = track.label;
826 trackt.delete_clicked.connect((tt) => {this.remove_track_interactive(tt);});
827 trackt.volume_updated.connect((volume) => {trackt.track_bin.set_volume(volume);});
828 trackt.panorama_updated.connect((panorama) => {trackt.track_bin.set_panorama(panorama);});
829 trackt.eq_updated.connect((band, val) => {trackt.track_bin.set_eq(band, val);});
830 trackt.echo_max_delay_updated.connect((val) => {trackt.track_bin.set_echo_max_delay(val);});
831 trackt.echo_delay_updated.connect((val) => {trackt.track_bin.set_echo_delay(val);});
832 trackt.echo_feedback_updated.connect((val) => {trackt.track_bin.set_echo_feedback(val);});
833 trackt.echo_intensity_updated.connect((val) => {trackt.track_bin.set_echo_intensity(val);});
834 trackt.echo_toggled.connect((val) => {trackt.track_bin.set_echo_active(val);});
835 trackt.dynamics_mode_updated.connect((val) => {trackt.track_bin.set_dynamics_mode(val);});
836 trackt.dynamics_characteristics_updated.connect((val) => {trackt.track_bin.set_dynamics_characteristics(val);});
837 trackt.dynamics_ratio_updated.connect((val) => {trackt.track_bin.set_dynamics_ratio(val);});
838 trackt.dynamics_threshold_updated.connect((val) => {trackt.track_bin.set_dynamics_threshold(val);});
839 trackt.dynamics_toggled.connect((val) => {trackt.track_bin.set_dynamics_active(val);});
840 trackt.active_toggled.connect((val) => {trackt.track.active = val;});
841 this.playback_starting.connect(trackt.playback_starting_callback);
842 this.playback_ending.connect(trackt.playback_ending_callback);
843 this.recording_starting.connect(trackt.recording_starting_callback);
844 this.recording_ending.connect(trackt.recording_ending_callback);
845 trackt.set_active_state(track.active);
846 tracks.pack_start(trackt, false, false, 4);
853 application_settings = new ApplicationSettings();
854 bool first_run = true;
855 string settings_path = "";
858 // can we get at our settings file?
859 File home_dir = File.new_for_path (Environment.get_home_dir());
860 // do we have a .demorecorder dir
861 File app_dir = File.new_for_path(home_dir.get_path() + "/.demorecorder");
862 if (app_dir.query_exists(null)) {
863 // now check for the existence of the settings file_chooser
864 settings = File.new_for_path(app_dir.get_path() + "/settings.xml");
865 if (settings.query_exists(null)) {
869 // if not assume first run and show settings wizard
873 int dir_ret = DirUtils.create_with_parents(app_dir.get_path(), 0755);
875 settings = File.new_for_path(app_dir.get_path() + "/settings.xml");
877 // create and save settings
879 FirstRunWizard dlg = new FirstRunWizard(window, "Demo Recorder");
880 dlg.set_transient_for(window);
882 application_settings = dlg.app_settings;
883 bool saved = ApplicationSettings.save_settings(settings.get_path(), application_settings, ref errors);
887 // may as well load the settings now
888 application_settings = ApplicationSettings.load_settings(settings.get_path(), "1.0", out errors);
889 if (null == application_settings) {
890 // this is major, bail for now
891 Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Failed to create settings!\n%s\nQuiting application.".printf(errors));
892 ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND * 2);
893 stdout.printf(errors + "\n");
898 bool has_last_project = (0 < application_settings.last_project_location.length);
899 // we have either old or new settings
900 if (has_last_project) {
901 switch(application_settings.open_action) {
902 case ApplicationSettings.OpenAction.RECENT_PROJECTS:
903 // show recent projects dialog
905 load_project(application_settings.last_project_location);
907 case ApplicationSettings.OpenAction.NAMED_PROJECT:
908 if (0 < application_settings.named_project_location.length) {
909 load_project(application_settings.named_project_location);
912 // show recent projects dialog
914 load_project(application_settings.last_project_location);
917 case ApplicationSettings.OpenAction.LAST_PROJECT:
918 load_project(application_settings.last_project_location);
932 private bool on_delete_event() {
934 // do we have a dirty project?
936 // what should we do?
937 switch(application_settings.close_action) {
938 case ApplicationSettings.CloseAction.AUTO_SAVE_PROJECT:
942 case ApplicationSettings.CloseAction.PROMPT_SAVE_PROJECT:
943 YesNoCancelDialog alert = new YesNoCancelDialog(window, "Save changes to current project?", "You have unsaved changes in your current project.\nWould you like to save them first?", Gtk.ResponseType.CANCEL);
944 alert.set_transient_for(window);
945 int yn_ret = alert.run();
948 // yes - save_project()
949 if (Gtk.ResponseType.YES == yn_ret) {
954 // cancel - return and do nothing
955 else if (Gtk.ResponseType.NO == yn_ret) {
959 else { //(Gtk.ResponseType.CANCEL != ret) {
963 default: // DO_NOTHING