/* Demo Recorder for MAEMO 5 * Copyright (C) 2010 Dru Moore * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * or (at your option) any later version, as published by the Free * Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ namespace IdWorks { public class DemoRecorder : Hildon.Program { ApplicationSettings application_settings; Hildon.StackableWindow window; Gtk.VBox tracks; SettingsStructures.ProjectSettings project; bool project_dirty; EncodePipeline encodepipeline; PlayPipeline playpipeline; RecordPipeline recordpipeline; PlayerTransport player; MixerBin mixer; uint track_counter; bool recording; bool playing; bool encoding; uint screen_timer_id; uint playback_timer_id; uint recording_timer_id; uint encoding_timer_id; ulong recording_position_duration_id; Osso.Context osso_context; //int64 track_position; //int64 record_position; //int64 track_duration; public signal void encoding_starting(); public signal void encoding_started(); public signal void encoding_ending(); public signal void encoding_ended(); public signal void recording_starting(); public signal void recording_started(); public signal void recording_ending(); public signal void recording_ended(); public signal void playback_starting(); public signal void playback_started(); public signal void playback_ending(); public signal void playback_ended(); public signal void playback_pausing(); public signal void playback_paused(); public signal void playback_resuming(); public signal void playback_resumed(); public signal void playback_position_duration(int64 position, int64 duration); public signal void recording_position_duration(int64 position, int64 duration); public signal void encoding_position_duration(int64 position, int64 duration); public void playpipeline_position_duration_callback(Gst.Pipeline sender, int64 position, int64 duration) { playback_position_duration(position, duration); if (position >= duration) playback_time_elapsed(); } public void recordpipeline_position_duration_callback(Gst.Pipeline sender, int64 position, int64 duration) { recording_position_duration(position, duration); if (0 < duration && position >= duration) recording_time_elapsed(); } public void encodepipeline_position_duration_callback(Gst.Pipeline sender, int64 position, int64 duration) { encoding_position_duration(position, duration); if (position >= duration) encoding_time_elapsed(); } private bool update_time_and_duration_play_pipeline() { playpipeline.get_duration_info(); return true; } private bool update_time_and_duration_record_pipeline() { recordpipeline.get_duration_info(); return true; } private bool update_time_and_duration_encode_pipeline() { encodepipeline.get_duration_info(); return true; } private void recording_time_elapsed() { Source.remove(recording_timer_id); /* this should only ever fire for fixed time 'recording' i.e. bouncing or mixing tracks hence we can use it as a trigger */ } private void encoding_time_elapsed() { Source.remove(encoding_timer_id); /* this should only ever fire for fixed time 'recording' i.e. bouncing or mixing tracks hence we can use it as a trigger */ } private void playback_time_elapsed() { if (!this.recording) Source.remove(playback_timer_id); } construct { osso_context = new Osso.Context("DemoRecorder", "0.1", false, null); project = SettingsStructures.ProjectSettings(); recording = false; playing = false; track_counter = 0; window = new Hildon.StackableWindow(); window.destroy.connect(Gtk.main_quit); window.delete_event.connect(on_delete_event); window.set_title("Demo Recorder"); construct_menu(); Gtk.VBox container = new Gtk.VBox(false, 0); Hildon.PannableArea scrollwin = new Hildon.PannableArea(); tracks = new Gtk.VBox(false, 0); player = new PlayerTransport(); player.window = this.window; this.playback_position_duration.connect(player.position_duration_callback); player.play_clicked.connect(mixer_play_all); player.pause_toggled.connect(mixer_pause); player.stop_clicked.connect(mixer_stop); player.volume_updated.connect(mixer_volume_updated); player.panorama_updated.connect(mixer_panorama_updated); player.eq_updated.connect(mixer_eq_updated); player.record_toggled.connect(mixer_record); container.pack_start(player, false, false, 0); scrollwin.add_with_viewport(tracks); container.pack_end(scrollwin, true, true, 0); window.add(container); mixer = new MixerBin("mixer"); //playpipeline = new PlayPipeline("mixer"); } private void mixer_volume_updated(PlayerTransport sender, double volume) { mixer.set_volume(volume); } private void mixer_panorama_updated(PlayerTransport sender, double panorama) { mixer.set_panorama(panorama); } private void mixer_eq_updated(PlayerTransport sender, int band, double val) { mixer.set_eq(band, val); } private bool keep_screen_on() { if (this.recording || this.playing) { Osso.Status status = this.osso_context.display_blanking_pause(); /*if (Osso.Status.OK == status) { stdout.printf("%s\n", "OK"); } else if (Osso.Status.INVALID == status) { stdout.printf("%s\n", "INVALID"); } else if (Osso.Status.ERROR == status) { stdout.printf("%s\n", "ERROR"); } else { stdout.printf("%s\n", "UNKNOWN"); } stdout.flush();*/ return true; } else { Source.remove(screen_timer_id); return false; } } private void mixer_play_all() { /// TODO clean up old mixer first? mixer = new MixerBin("mixer"); int idx = 0; if (0 < project.tracks.length()) { this.playing = true; keep_screen_on(); if (0 != screen_timer_id) Source.remove(screen_timer_id); screen_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND * 20, keep_screen_on); // for (int i = 0; i < tracks.children.length(); ++i) { // TrackTransport tt = tracks.children.nth_data(i) as TrackTransport; // if (null != tt) { // if (tt.get_active_state()) { // stdout.printf("Track %s active and has been added\n", i.to_string()); // //track.bin.set_start_time((Gst.ClockTime)(Time.Nanoseconds.SECOND * idx)); // mixer.add_track(tt.track_bin); // } // } // tt = null; // } while (idx < project.tracks.length()) { Track track = project.tracks.nth_data(idx); if (track.active) { stdout.printf("Track %s active and has been added\n", idx.to_string()); //track.bin.set_start_time((Gst.ClockTime)(Time.Nanoseconds.SECOND * idx)); mixer.add_track(track.bin); } ++idx; } playpipeline = new PlayPipeline("player", mixer); this.playback_position_duration.connect(player.position_duration_callback); playpipeline.position_duration.connect(playpipeline_position_duration_callback); playback_starting(); playpipeline.play(); playback_started(); update_time_and_duration_play_pipeline(); if (0 != playback_timer_id) Source.remove(playback_timer_id); playback_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND / 10, update_time_and_duration_play_pipeline); } if (this.recording) { if (!this.playing) { keep_screen_on(); if (0 != screen_timer_id) Source.remove(screen_timer_id); screen_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND * 20, keep_screen_on); } this.playback_position_duration.disconnect(player.position_duration_callback); this.recording_position_duration.connect(player.position_duration_callback); recordpipeline.position_duration.connect(recordpipeline_position_duration_callback); recording_starting(); recordpipeline.record(); recording_started(); update_time_and_duration_record_pipeline(); if (0 != recording_timer_id) Source.remove(recording_timer_id); recording_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND / 10, update_time_and_duration_record_pipeline); } } private void mixer_play() { this.playing = true; playback_resuming(); playpipeline.play(); playback_resumed(); } private void mixer_pause(bool val) { if (val) { playback_pausing(); playpipeline.pause(); playback_paused(); } else { playback_resuming(); playpipeline.play(); playback_resumed(); } } private void mixer_record(bool val) { if (val) { prepare_record(); } else { this.recording = false; } } private void mixer_stop() { if (this.recording) { recording_ending(); this.recording = false; recordpipeline.stop(); //this.import_track_from_uri(Filename.to_uri(recordpipeline.get_location())); Track track = Track.with_uri(Filename.to_uri(recordpipeline.get_location()), "Track-" + (project.last_track_number).to_string(), true, true, 1.0, 0.0); project.tracks.append(track); add_track_transport(track); recordpipeline = null; recording_ended(); } if (this.playing) { playback_ending(); playpipeline.stop(); playpipeline = null; this.playing = false; playback_ended(); } //stdout.flush(); } private void construct_menu() { Hildon.AppMenu menu = new Hildon.AppMenu(); var btnNew = new Gtk.Button.with_label("New project"); btnNew.clicked.connect(new_project); menu.append(btnNew); var btnOpen = new Gtk.Button.with_label("Open project"); btnOpen.clicked.connect(open_project); menu.append(btnOpen); var btnSave = new Gtk.Button.with_label("Save project"); btnSave.clicked.connect(save_project); menu.append(btnSave); var btnImport = new Gtk.Button.with_label("Import track"); btnImport.clicked.connect(open_import); menu.append(btnImport); var btnStartACM = new Gtk.Button.with_label("Input settings"); btnStartACM.clicked.connect(start_acm); menu.append(btnStartACM); var btnBounce = new Gtk.Button.with_label("Bounce tracks"); btnBounce.clicked.connect(prepare_bounce); menu.append(btnBounce); var btnMixdown = new Gtk.Button.with_label("Mixdown"); btnMixdown.clicked.connect(prepare_mixdown); menu.append(btnMixdown); var btnSettings = new Gtk.Button.with_label("Settings"); btnSettings.clicked.connect(show_settings); menu.append(btnSettings); var btnAbout = new Gtk.Button.with_label("About"); btnAbout.clicked.connect(show_about); menu.append(btnAbout); menu.show_all(); window.set_app_menu(menu); } private void show_about() { AboutDialog dlg = new AboutDialog(window); dlg.set_transient_for(window); dlg.run(); dlg.destroy(); dlg = null; } private void show_settings() { File settings = File.new_for_path(Environment.get_home_dir() + "/.demorecorder/settings.xml"); string errors = ""; FirstRunWizard dlg = new FirstRunWizard(window, "Demo Recorder"); dlg.set_transient_for(window); dlg.app_settings = application_settings; dlg.run(); application_settings = dlg.app_settings; bool saved = ApplicationSettings.save_settings(settings.get_path(), application_settings, ref errors); dlg.destroy(); dlg = null; } private void new_project() { bool good = false; // is the current project dirty? if (project_dirty) { // run an are you sure dialog // Do you want to save your current project first? Yes, No, Cancel 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); alert.set_transient_for(window); int ret = alert.run(); alert.destroy(); alert = null; // yes - save_project() if (Gtk.ResponseType.YES == ret) { save_project(); } // cancel - return and do nothing else if (Gtk.ResponseType.CANCEL == ret || Gtk.ResponseType.NO != ret) { return; } // carry on and create_project() } ProjectSettingsDialog dlg = new ProjectSettingsDialog.with_values(window, "New project settings", "New project", "", "", application_settings.working_directory); dlg.set_transient_for(window); int ret = dlg.run(); if (Gtk.ResponseType.OK == ret) { good = true; this.project = SettingsStructures.ProjectSettings(); this.project.name = dlg.get_project_name(); this.project.working_directory = dlg.get_project_directory(); // ensure that the directories exist. DirUtils.create_with_parents(this.project.working_directory + "/tracks", 0755); DirUtils.create_with_parents(this.project.working_directory + "/mixes", 0755); DirUtils.create_with_parents(this.project.working_directory + "/tmp", 0755); this.project.location = dlg.get_project_filename(); save_project(); } dlg.destroy(); dlg = null; if (good) { // Now should clean up the display in preparation for working on this new project. //load_project(this.project.location); application_settings.last_project_name = project.name; application_settings.last_project_location = project.location; window.set_title(project.name); remove_all_tracks(); } } private void open_project() { // is the current project dirty? if (project_dirty) { // run an are you sure dialog // Do you want to save your current project first? Yes, No, Cancel 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); alert.set_transient_for(window); int ret = alert.run(); alert.destroy(); alert = null; // yes - save_project() if (Gtk.ResponseType.YES == ret) { save_project(); } // cancel - return and do nothing else if (Gtk.ResponseType.CANCEL == ret || Gtk.ResponseType.NO != ret) { return; } // carry on and load_project() } // get a filename Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(window, Gtk.FileChooserAction.OPEN); file_chooser.set_show_upnp(false); file_chooser.set_current_folder(application_settings.working_directory); int ret = file_chooser.run(); if (Gtk.ResponseType.OK == ret) { load_project(file_chooser.get_filename()); } file_chooser.destroy (); file_chooser = null; } private void load_project(string location) { remove_all_tracks(); project = SettingsStructures.ProjectSettings(); string errors = ""; if (XmlHelpers.parse_project_settings_file(location, "1.0", ref project, ref errors)) { // need to tear down and build up the interface again. // as a test let's save it for inspection //save_project_settings(project.location); /// TODO application_settings.last_project_name = project.name; application_settings.last_project_location = project.location; if (ApplicationSettings.save_settings(Environment.get_home_dir() + "/.demorecorder/settings.xml", application_settings, ref errors)) { window.set_title(project.name); for (int idx = 0; idx < project.tracks.length(); ++idx) { add_track_transport((Track)project.tracks.nth_data(idx)); } } else { stdout.printf("%s\n", errors); stdout.flush(); } } else { Hildon.Note note = new Hildon.Note.information(window, "Project couldn't be opened!\n%s".printf(errors)); note.run(); note.destroy(); note = null; } } private void remove_all_tracks() { List children = tracks.get_children(); for (int idx = 0; idx < children.length(); ++idx) { tracks.remove((Gtk.Widget)children.nth_data(idx)); } for (int idx = ((int)project.tracks.length()) - 1; idx > -1; --idx) { project.tracks.delete_link(project.tracks.nth(idx)); } } private void remove_track_interactive(TrackTransport tt) { string message = ""; string ns = ""; switch(application_settings.remove_track_action) { case ApplicationSettings.RemoveTrackAction.DELETE_FILE: remove_track(tt); message = "Track deleted."; FileUtils.remove(Filename.from_uri(tt.track.uri, out ns)); break; case ApplicationSettings.RemoveTrackAction.PRESERVE_FILE: remove_track(tt); message = "Track removed."; break; case ApplicationSettings.RemoveTrackAction.PROMPT: YesNoCancelDialog dlg = new YesNoCancelDialog(window, "Remove Track", "Do you want to delete the source file permanently?", Gtk.ResponseType.CANCEL); dlg.set_transient_for(window); int ret = dlg.run(); dlg.destroy(); if (Gtk.ResponseType.YES == ret) { remove_track(tt); message = "Track deleted."; FileUtils.remove(Filename.from_uri(tt.track.uri, out ns)); } else if (Gtk.ResponseType.NO == ret) { remove_track(tt); message = "Track removed."; } break; default: break; } if ("" != message) { Hildon.Note note = new Hildon.Note.information(window, "%s".printf(message)); note.run(); note.destroy(); note = null; } } private void remove_track(TrackTransport tt) { project.tracks.remove(tt.track); tracks.remove(tt); tt = null; } private void save_project() { if (0 < project.location.length) { save_project_settings(project.location); } else { do_save_as_project(); } //stdout.printf(project.to_xml_string()); } private void do_save_as_project() { // get a filename Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(window, Gtk.FileChooserAction.SAVE); file_chooser.set_show_upnp(false); int ret = file_chooser.run(); if (Gtk.ResponseType.OK == ret) { project.location = file_chooser.get_filename(); save_project_settings(project.location); } file_chooser.destroy (); file_chooser = null; } private void save_project_settings(string location) { bool success = false; Hildon.gtk_window_set_progress_indicator(window, (uint)true); string contents = project.to_xml_string(); // should ask each ui element for it's updated settings to be on the safe side first. /// TODO if (0 < contents.length) { contents = "\n" + contents; if (FileUtils.set_contents(location, contents, contents.length)) { project_dirty = false; // alert user of success? no! //Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Project saved."); //((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND); } else { // alert user of failure to save file. Hildon.Note alert = new Hildon.Note.information(window, "Project couldn't be saved!\nFailed to save the project settings file."); alert.run(); alert.destroy(); alert = null; } } else { // alert user of failure to create serialize settings. Hildon.Note alert = new Hildon.Note.information(window, "Project couldn't be saved!\nFailed to serialize the project settings."); alert.run(); alert.destroy(); alert = null; } Hildon.gtk_window_set_progress_indicator(window, (uint)false); } private void start_acm() { AcmPopUp acm = new AcmPopUp.with_settings("Input Settings", window, project.monitor); acm.set_transient_for(window); acm.play(); if (Gtk.ResponseType.OK == acm.run()) { project.monitor = acm.get_settings_structure(); project_dirty = true; } acm.stop(); acm.destroy(); acm = null; } private void prepare_mixdown() { MixdownPopUp dlg = new MixdownPopUp.with_settings("Mixdown your track", window, project.mixdown); dlg.set_transient_for(window); if (Gtk.ResponseType.OK == dlg.run()) { project.mixdown = dlg.get_settings_structure(); project_dirty = true; do_mixdown(); } dlg.destroy(); dlg = null; } private void do_mixdown() { Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int", "rate", typeof (int), 48000, "channels", typeof (int), 2, "depth", typeof (int), 16); mixer = new MixerBin("mixer"); int idx = 0; if (0 < project.tracks.length()) { while (idx < project.tracks.length()) { Track track = project.tracks.nth_data(idx); if (track.active) { mixer.add_track(track.bin); } ++idx; } this.recording = false; this.playing = false; 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)); if (null != encoder) { encodepipeline = new EncodePipeline("mixdown", project.mixdown.location, mixer, encoder, caps); encodepipeline.position_duration.connect(encodepipeline_position_duration_callback); Hildon.gtk_window_set_progress_indicator(window, (uint)true); encoding_starting(); encodepipeline.encode(); encoding_started(); update_time_and_duration_encode_pipeline(); if (0 != recording_timer_id) Source.remove(encoding_timer_id); encoding_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND, update_time_and_duration_encode_pipeline); ProgressPopUp dlg = new ProgressPopUp("Mixing down", window); dlg.set_message("Mixing down your track.\nThis may take some time depending on the duration of the audio.\n"); dlg.set_transient_for(window); this.encoding_position_duration.connect((p,d) => { double percent = ((double)(p / Time.Nanoseconds.MILLISECOND) / (double)(d / Time.Nanoseconds.MILLISECOND)); dlg.set_progress(percent); dlg.set_progress_text("%s of %s completed".printf(Time.time_to_string(p), Time.time_to_string(d))); //if (p >= d) { // dlg.close_me(); // this.mixer_stop(); //} }); encodepipeline.end_of_stream.connect((e) => {encoding_ending();dlg.close_me();encoding_time_elapsed();this.mixer_stop();encoding_ended();}); if (Gtk.ResponseType.CANCEL == dlg.run()) { // Should really stop the mixer and delete the files! /// TODO this.mixer_stop(); } dlg.destroy(); dlg = null; //encoder = null; Hildon.gtk_window_set_progress_indicator(window, (uint)false); Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Mixdown complete"); ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND); } else { Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Mixdown failed! Encoder not available."); ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND); } } } private void prepare_bounce() { project.last_track_number++; string new_location = "%s/tracks/bounce-%s.wav".printf(project.working_directory, project.last_track_number.to_string()); string name = "bounce-" + project.last_track_number.to_string(); do_bounce(new_location); } private void do_bounce(string filename) { Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int", "rate", typeof (int), 48000, "channels", typeof (int), 2, "depth", typeof (int), 16); //Gst.Element src = Gst.ElementFactory.make("pulsesrc", "input"); //src.set_property("device", "source.hw0"); mixer = new MixerBin("mixer"); int idx = 0; if (0 < project.tracks.length()) { while (idx < project.tracks.length()) { Track track = project.tracks.nth_data(idx); if (track.active) { mixer.add_track(track.bin); } ++idx; } this.recording = true; this.playing = false; recordpipeline = new RecordPipeline("bouncer", filename, mixer, caps, "wavenc"); recordpipeline.bouncing = true; recordpipeline.position_duration.connect(recordpipeline_position_duration_callback); Hildon.gtk_window_set_progress_indicator(window, (uint)true); recording_starting(); recordpipeline.record(); recording_started(); update_time_and_duration_record_pipeline(); if (0 != recording_timer_id) Source.remove(recording_timer_id); recording_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND, update_time_and_duration_record_pipeline); ProgressPopUp dlg = new ProgressPopUp("Bouncing tracks", window); dlg.set_transient_for(window); dlg.set_message("Bouncing your selected tracks.\nThis may take some time depending on the duration of the audio.\n"); this.recording_position_duration.connect((p,d) => { double percent = ((double)(p / Time.Nanoseconds.MILLISECOND) / (double)(d / Time.Nanoseconds.MILLISECOND)); dlg.set_progress(percent); dlg.set_progress_text("%s of %s completed".printf(Time.time_to_string(p), Time.time_to_string(d))); if (p >= d) { dlg.close_me(); this.mixer_stop(); this.recording = false; if (0 != recording_timer_id) Source.remove(recording_timer_id); } }); if (Gtk.ResponseType.CANCEL == dlg.run()) { // Should really stop the mixer and delete the files! /// TODO this.recording = false; this.mixer_stop(); } dlg.destroy(); dlg = null; Hildon.gtk_window_set_progress_indicator(window, (uint)false); Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Bounced track added to mixer"); ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND); } } public void prepare_record() { project.last_track_number++; string new_location = "%s/tracks/track-%s.wav".printf(project.working_directory, project.last_track_number.to_string()); string name = "Track-" + project.last_track_number.to_string(); do_record(name, new_location); } public void do_record(string name, string filename) { Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int", "rate", typeof (int), 48000, "channels", typeof (int), 2, "depth", typeof (int), 16); InputBin src = new InputBin.with_settings("input", project.monitor.input); //Gst.Element src = Gst.ElementFactory.make("pulsesrc", "input"); //src.set_property("device", "source.hw0"); recordpipeline = new RecordPipeline("recorder", filename, src, caps, "wavenc"); recordpipeline.bouncing = false; this.recording = true; //recordpipeline.record(); } public void open_import() { Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(window, Gtk.FileChooserAction.OPEN); file_chooser.set_show_upnp(false); file_chooser.set_current_folder(application_settings.working_directory); if (file_chooser.run () == Gtk.ResponseType.OK) { import_track_from_uri(file_chooser.get_uri()); } file_chooser.destroy (); } public void import_track_from_uri(string uri) { /// TODO - Should become import track from uri and convert to wav and name appropriately ++project.last_track_number; project_dirty = true; Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int", "rate", typeof (int), 48000, "channels", typeof (int), 2, "depth", typeof (int), 16); /// TODO check that directories exist string new_location = "%s/tracks/import-%s.wav".printf(project.working_directory, project.last_track_number.to_string()); string name = "Import-" + project.last_track_number.to_string(); ImportBin import_bin = new ImportBin(name); import_bin.set_uri(uri); /// TODO encode to correct destination and add track this.recording = false; this.playing = false; recordpipeline = new RecordPipeline("importer", new_location, import_bin, caps, "wavenc"); recordpipeline.bouncing = true; recordpipeline.position_duration.connect(recordpipeline_position_duration_callback); Hildon.gtk_window_set_progress_indicator(window, (uint)true); recording_starting(); recordpipeline.record(); recording_started(); update_time_and_duration_record_pipeline(); if (0 != recording_timer_id) Source.remove(recording_timer_id); recording_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND, update_time_and_duration_record_pipeline); ProgressPopUp dlg = new ProgressPopUp("Importing track", window); dlg.set_transient_for(window); dlg.set_message("Importing your selected track.\nThis may take some time depending on the duration of the audio.\n"); this.recording_position_duration.connect((p,d) => { double percent = ((double)(p / Time.Nanoseconds.MILLISECOND) / (double)(d / Time.Nanoseconds.MILLISECOND)); dlg.set_progress(percent); dlg.set_progress_text("%s of %s completed".printf(Time.time_to_string(p), Time.time_to_string(d))); }); recordpipeline.end_of_stream.connect(() => { dlg.close_me(); recordpipeline.stop(); if (0 != recording_timer_id) Source.remove(recording_timer_id); Track track = Track.with_uri(Filename.to_uri(new_location), name, true, true, 1.0, 0.0); project.tracks.append(track); add_track_transport(track); this.recording = false; }); if (Gtk.ResponseType.CANCEL == dlg.run()) { // Should really stop the mixer and delete the files! /// TODO //Track track = new Track.with_uri(Filename.to_uri(new_location), name, true, true, 1.0, 0.0); //project.tracks.append(track); //add_track_transport(track); recordpipeline.stop(); this.recording = false; } dlg.destroy(); dlg = null; Hildon.gtk_window_set_progress_indicator(window, (uint)false); Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Imported track added to mixer"); ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND); } private void add_track_transport(Track track) { TrackTransport trackt = new TrackTransport(); trackt.window = this.window; trackt.track_bin = track.bin; trackt.track = track; trackt.title = track.label; //"Track %n".printf(project.track.length()); trackt.name = track.label; trackt.delete_clicked.connect((tt) => {this.remove_track_interactive(tt);}); trackt.volume_updated.connect((volume) => {trackt.track_bin.set_volume(volume);}); trackt.panorama_updated.connect((panorama) => {trackt.track_bin.set_panorama(panorama);}); trackt.eq_updated.connect((band, val) => {trackt.track_bin.set_eq(band, val);}); trackt.echo_max_delay_updated.connect((val) => {trackt.track_bin.set_echo_max_delay(val);}); trackt.echo_delay_updated.connect((val) => {trackt.track_bin.set_echo_delay(val);}); trackt.echo_feedback_updated.connect((val) => {trackt.track_bin.set_echo_feedback(val);}); trackt.echo_intensity_updated.connect((val) => {trackt.track_bin.set_echo_intensity(val);}); trackt.echo_toggled.connect((val) => {trackt.track_bin.set_echo_active(val);}); trackt.dynamics_mode_updated.connect((val) => {trackt.track_bin.set_dynamics_mode(val);}); trackt.dynamics_characteristics_updated.connect((val) => {trackt.track_bin.set_dynamics_characteristics(val);}); trackt.dynamics_ratio_updated.connect((val) => {trackt.track_bin.set_dynamics_ratio(val);}); trackt.dynamics_threshold_updated.connect((val) => {trackt.track_bin.set_dynamics_threshold(val);}); trackt.dynamics_toggled.connect((val) => {trackt.track_bin.set_dynamics_active(val);}); trackt.active_toggled.connect((val) => {trackt.track.active = val;}); this.playback_starting.connect(trackt.playback_starting_callback); this.playback_ending.connect(trackt.playback_ending_callback); this.recording_starting.connect(trackt.recording_starting_callback); this.recording_ending.connect(trackt.recording_ending_callback); trackt.set_active_state(track.active); tracks.pack_start(trackt, false, false, 4); tracks.show_all(); } public void run() { window.show_all(); application_settings = new ApplicationSettings(); bool first_run = true; string settings_path = ""; string errors = ""; File settings; // can we get at our settings file? File home_dir = File.new_for_path (Environment.get_home_dir()); // do we have a .demorecorder dir File app_dir = File.new_for_path(home_dir.get_path() + "/.demorecorder"); if (app_dir.query_exists(null)) { // now check for the existence of the settings file_chooser settings = File.new_for_path(app_dir.get_path() + "/settings.xml"); if (settings.query_exists(null)) { first_run = false; } else { // if not assume first run and show settings wizard } } else { int dir_ret = DirUtils.create_with_parents(app_dir.get_path(), 0755); } settings = File.new_for_path(app_dir.get_path() + "/settings.xml"); if (first_run) { // create and save settings /// TODO FirstRunWizard dlg = new FirstRunWizard(window, "Demo Recorder"); dlg.set_transient_for(window); dlg.run(); application_settings = dlg.app_settings; bool saved = ApplicationSettings.save_settings(settings.get_path(), application_settings, ref errors); dlg.destroy(); dlg = null; } // may as well load the settings now application_settings = ApplicationSettings.load_settings(settings.get_path(), "1.0", out errors); if (null == application_settings) { // this is major, bail for now Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Failed to create settings!\n%s\nQuiting application.".printf(errors)); ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND * 2); stdout.printf(errors + "\n"); stdout.flush(); window.destroy(); exit(-1); } bool has_last_project = (0 < application_settings.last_project_location.length); // we have either old or new settings if (has_last_project) { switch(application_settings.open_action) { case ApplicationSettings.OpenAction.RECENT_PROJECTS: // show recent projects dialog /// TODO load_project(application_settings.last_project_location); break; case ApplicationSettings.OpenAction.NAMED_PROJECT: if (0 < application_settings.named_project_location.length) { load_project(application_settings.named_project_location); } else { // show recent projects dialog /// TODO load_project(application_settings.last_project_location); } break; case ApplicationSettings.OpenAction.LAST_PROJECT: load_project(application_settings.last_project_location); break; default: new_project(); break; } } else { new_project(); } Gtk.main(); } private bool on_delete_event() { bool ret = false; // do we have a dirty project? if (project_dirty) { // what should we do? switch(application_settings.close_action) { case ApplicationSettings.CloseAction.AUTO_SAVE_PROJECT: save_project(); ret = project_dirty; break; case ApplicationSettings.CloseAction.PROMPT_SAVE_PROJECT: 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); alert.set_transient_for(window); int yn_ret = alert.run(); alert.destroy(); alert = null; // yes - save_project() if (Gtk.ResponseType.YES == yn_ret) { save_project(); ret = project_dirty; break; } // cancel - return and do nothing else if (Gtk.ResponseType.NO == yn_ret) { ret = false; break; } else { //(Gtk.ResponseType.CANCEL != ret) { ret = true; break; } default: // DO_NOTHING ret = false; break; } } return ret; } } }