/* * Sweep, a sound wave editor. * * Copyright (C) 2000 Conrad Parker * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sweep_app.h" #include "file_sndfile.h" #include "sample.h" #include "interface.h" #include "sample-display.h" #include "question_dialogs.h" #include "preferences.h" #define LAST_LOAD_KEY "Last_Load" #define LAST_SAVE_KEY "Last_Save" #ifdef HAVE_OGGVORBIS extern sw_sample * vorbis_sample_reload (sw_sample * sample); extern sw_sample * vorbis_sample_load (char * pathname); extern int vorbis_save_options_dialog (sw_sample * sample, char * pathname); #endif #ifdef HAVE_SPEEX extern sw_sample * speex_sample_reload (sw_sample * sample); extern sw_sample * speex_sample_load (char * pathname); extern int speex_save_options_dialog (sw_sample * sample, char * pathname); #endif #ifdef HAVE_MAD extern sw_sample * mad_sample_reload (sw_sample * sample); extern sw_sample * mad_sample_load (char * pathname); #endif void mp3_unsupported_dialog (void) { info_dialog_new (_("MP3 export unsupported"), NULL, _("Export to MP3 format cannot legally be supported " "in free software\n" "due to patent licensing restrictions.\n\n" "Please use Ogg Vorbis format instead, which\n" "provides better quality and is free.")); } /* This source code can compile against both version 0 and version 1 ** of libsndfile. */ typedef struct { sw_file_format_t file_format; gchar * name; gchar * exts; /* comma separated extensions */ } sw_file_format_desc; void file_guess_method (sw_sample * sample, char * pathname); #if ! (defined (SNDFILE_1)) static sw_file_format_desc file_formats [] = { { SWEEP_FILE_FORMAT_RAW, N_("Raw PCM (headerless)"), "raw", }, { SWEEP_FILE_FORMAT_AIFF, "Apple/SGI AIFF,AIFF-C", "aiff,aifc" }, { SWEEP_FILE_FORMAT_AU, "Sun/NeXT AU", "au", }, { SWEEP_FILE_FORMAT_SVX, "Amiga IFF,SVX8,SVX16", "svx", }, { SWEEP_FILE_FORMAT_PAF, "Ensoniq PARIS", "paf", }, { SWEEP_FILE_FORMAT_IRCAM, "IRCAM/Berkeley/CARL SF", "sf", }, { SWEEP_FILE_FORMAT_VOC, "Creative Labs VOC", "voc", }, { SWEEP_FILE_FORMAT_WAV, "Microsoft WAV", "wav,riff", }, }; #endif static gboolean sweep_dir_exists (char * pathname) { struct stat statbuf; char buf[512]; gchar * dirname; dirname = g_dirname (pathname); if (dirname && strcmp (dirname, "") && stat (dirname, &statbuf) == -1) { switch (errno) { case ENOENT: snprintf (buf, sizeof (buf), _("%s does not exist."), dirname); info_dialog_new (_("Directory does not exist"), NULL, buf); g_free (dirname); return FALSE; break; default: break; } } /* dirname is either NULL, "" or an existing directory */ return TRUE; } #ifdef HAVE_MAD static gboolean file_guess_mp3 (char * pathname) { gchar * ext; ext = strrchr (pathname, '.'); if (ext != NULL) { ext++; if (!g_strncasecmp (ext, "mp3", 4)) return TRUE; if (!g_strncasecmp (ext, "mp2", 4)) return TRUE; } return FALSE; } #endif sw_sample * sample_reload (sw_sample * sample) { sw_sample * new_sample = NULL; #ifdef HAVE_OGGVORBIS new_sample = vorbis_sample_reload (sample); #endif if (new_sample == NULL) new_sample = sndfile_sample_reload (sample, FALSE); #ifdef HAVE_SPEEX if (new_sample == NULL) new_sample = speex_sample_reload (sample); #endif #ifdef HAVE_MAD if (new_sample == NULL && file_guess_mp3 (sample->pathname)) new_sample = mad_sample_reload (sample); #endif if (new_sample == NULL) new_sample = sndfile_sample_reload (sample, TRUE); return new_sample; } static sw_sample * try_sample_load (char * pathname) { sw_sample * sample = NULL; #ifdef HAVE_OGGVORBIS sample = vorbis_sample_load (pathname); #endif if (sample == NULL) sample = sndfile_sample_load (pathname, FALSE); #ifdef HAVE_SPEEX if (sample == NULL) sample = speex_sample_load (pathname); #endif #ifdef HAVE_MAD if (sample == NULL && file_guess_mp3 (pathname)) sample = mad_sample_load (pathname); #endif if (sample == NULL) sample = sndfile_sample_load (pathname, TRUE); if (sample != NULL) recent_manager_add_item (pathname); return sample; } sw_sample * sample_load (char * pathname) { if (sweep_dir_exists (pathname)) { if (strcmp (pathname, "-") == 0) { try_sample_load (pathname); } else if (access(pathname, R_OK) == -1) { switch (errno) { case ENOENT: create_sample_new_dialog_defaults (pathname); /*sweep_perror (errno, pathname);*/ break; default: sweep_perror (errno, _("Unable to read\n%s"), pathname); break; } } else { prefs_set_string (LAST_LOAD_KEY, pathname); return try_sample_load (pathname); } } return NULL; } void sample_load_cb(GtkWidget * widget, gpointer data) { GtkWidget *dialog; gchar *load_current_file; gint win_width, win_height; GSList *filenames, *list; filenames = list = NULL; win_width = gdk_screen_width () / 2; win_height = gdk_screen_height () / 2; dialog = gtk_file_chooser_dialog_new (_("Sweep: Open Files"), data, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_widget_set_size_request (dialog, win_width, win_height); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); sweep_set_window_icon (GTK_WINDOW(dialog)); attach_window_close_accel(GTK_WINDOW(dialog)); load_current_file = prefs_get_string (LAST_LOAD_KEY); if (load_current_file) { gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(dialog), load_current_file); g_free(load_current_file); } if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { filenames = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog)); for (list = filenames; list; list = list->next) { if (list->data) { sample_load ((gchar *)list->data); g_free (list->data); } } if (filenames) g_slist_free(filenames); } else { /* do nothing so exit */ sample_bank_remove(NULL); } gtk_widget_destroy (dialog); } gboolean sample_mtime_changed (sw_sample * sample) { struct stat statbuf; /* a last_mtime of 0 indicates that the file is not from disk */ if (sample->last_mtime == 0) return FALSE; if (stat (sample->pathname, &statbuf) == -1) { sweep_perror (errno, sample->pathname); return FALSE; } return (sample->last_mtime != statbuf.st_mtime); } void sample_revert_ok_cb (GtkWidget * widget, gpointer data) { sw_sample * sample = (sw_sample *)data; sample_reload (sample); } void sample_revert_cb (GtkWidget * widget, gpointer data) { sw_view * view = (sw_view *)data; sw_sample * sample; char buf[512]; sample = view->sample; snprintf (buf, sizeof (buf), _("Are you sure you want to revert %s to\n%s?\n\n" "All changes and undo information will be lost."), g_basename (sample->pathname), sample->pathname); question_dialog_new (sample, _("Revert file"), buf, _("Revert"), _("Don't revert"), G_CALLBACK (sample_revert_ok_cb), sample, NULL, NULL, SWEEP_EDIT_MODE_ALLOC); } void sample_store_and_free_pathname (sw_sample * sample, gchar * pathname) { prefs_set_string (LAST_SAVE_KEY, pathname); sample_set_pathname (sample, pathname); #ifdef DEVEL_CODE g_free (pathname); #endif } void file_guess_method (sw_sample * sample, char * pathname) { int i; gchar * ext; #if defined (SNDFILE_1) SF_FORMAT_INFO info ; int count ; #else gchar ** exts, **e; sw_file_format_desc * desc; #endif g_assert (sample->file_method == SWEEP_FILE_METHOD_BY_EXTENSION); if (pathname == NULL) goto guess_raw; ext = strrchr (pathname, '.'); if (ext != NULL) { ext++; #ifdef HAVE_OGGVORBIS if (!g_strncasecmp (ext, "ogg", SW_DIR_LEN)) { sample->file_method = SWEEP_FILE_METHOD_OGGVORBIS; sample->file_format = 0; return; } #endif #ifdef HAVE_SPEEX if (!g_strncasecmp (ext, "spx", SW_DIR_LEN)) { sample->file_method = SWEEP_FILE_METHOD_SPEEX; sample->file_format = 0; return; } #endif /* MP3 has dummy annoying dialog support */ if (!g_strncasecmp (ext, "mp3", SW_DIR_LEN)) { sample->file_method = SWEEP_FILE_METHOD_MP3; sample->file_format = 0; return; } #if defined (SNDFILE_1) sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ; for (i = 0; i < count ; i++) { info.format = i ; sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info)) ; if (!g_strncasecmp (ext, info.extension, SW_DIR_LEN)) { sample->file_method = SWEEP_FILE_METHOD_LIBSNDFILE; sample->file_format = info.format; break; /* NB. this break is essential to ensure that eg. a file * ending in ".wav" is guessed as RIFF/WAV not Sphere/NIST, * which also may use the ending ".wav"; assuming RIFF/WAV is * more common we prefer to use this format when guessing. */ } } /* Catch some common conventions not offered by libsndfile */ if (sample->file_method == SWEEP_FILE_METHOD_BY_EXTENSION) { if (!g_strncasecmp(ext, "aifc", SW_DIR_LEN) || !g_strncasecmp(ext, "aif", SW_DIR_LEN)) { sample->file_method = SWEEP_FILE_METHOD_LIBSNDFILE; sample->file_format = SF_FORMAT_AIFF; } } #else for (i = 0; i < sizeof(file_formats)/sizeof(sw_file_format_desc); i++) { desc = &file_formats[i]; exts = g_strsplit (desc->exts, ",", 16); for (e = exts; *e; e++) { if (!g_strncasecmp (*e, ext, SW_DIR_LEN)) { sample->file_method = SWEEP_FILE_METHOD_LIBSNDFILE; sample->file_format = desc->file_format; } } g_strfreev (exts); } #endif } guess_raw: if (sample->file_method == SWEEP_FILE_METHOD_BY_EXTENSION) { sample->file_method = SWEEP_FILE_METHOD_LIBSNDFILE; #if defined (SNDFILE_1) sample->file_format = SF_FORMAT_RAW; #else sample->file_format = SWEEP_FILE_FORMAT_RAW; #endif } return; } typedef struct { sw_sample * sample; char * pathname; } save_as_data; static void overwrite_ok_cb (GtkWidget * widget, gpointer data) { save_as_data * sd = (save_as_data *)data; sw_sample * sample = sd->sample; char * pathname = sd->pathname; if (sample->file_method == SWEEP_FILE_METHOD_BY_EXTENSION) { file_guess_method (sample, pathname); } switch (sample->file_method) { case SWEEP_FILE_METHOD_LIBSNDFILE: sndfile_save_options_dialog (sample, pathname); break; #ifdef HAVE_OGGVORBIS case SWEEP_FILE_METHOD_OGGVORBIS: vorbis_save_options_dialog (sample, pathname); break; #endif #ifdef HAVE_SPEEX case SWEEP_FILE_METHOD_SPEEX: speex_save_options_dialog (sample, pathname); break; #endif case SWEEP_FILE_METHOD_MP3: mp3_unsupported_dialog (); break; default: g_assert_not_reached (); break; } g_free (sd); } static void overwrite_cancel_cb (GtkWidget * widget, gpointer data) { save_as_data * sd = (save_as_data *)data; gchar * msg; msg = g_strdup_printf (_("Save as %s cancelled"), g_basename (sd->pathname)); sample_set_tmp_message (sd->sample, msg); g_free (msg); g_free (sd); } static void file_set_format_cb (GtkWidget * widget, gpointer data) { sw_sample * sample = (sw_sample *)data; sample->file_method = (sw_file_method_t) GPOINTER_TO_INT(g_object_get_data (G_OBJECT(widget), "method")); sample->file_format = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(widget), "format")); } static GtkWidget * create_save_menu (sw_sample * sample) { GtkWidget * menu; GtkWidget * menuitem; int i; #if defined (SNDFILE_1) SF_FORMAT_INFO info ; int count ; #else sw_file_format_desc * desc; #endif menu = gtk_menu_new (); sample->file_method = SWEEP_FILE_METHOD_BY_EXTENSION; menuitem = gtk_menu_item_new_with_label (_("By extension")); gtk_menu_append (GTK_MENU(menu), menuitem); gtk_object_set_data (GTK_OBJECT(menuitem), "method", SWEEP_FILE_METHOD_BY_EXTENSION); gtk_object_set_data (GTK_OBJECT(menuitem), "format", 0); gtk_signal_connect (GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(file_set_format_cb), sample); gtk_widget_show (menuitem); menuitem = gtk_menu_item_new (); /* Separator */ gtk_menu_append (GTK_MENU(menu), menuitem); gtk_widget_show (menuitem); /* libsndfile formats */ #if defined (SNDFILE_1) sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ; for (i = 0; i < count ; i++) { info.format = i ; sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info)) ; menuitem = gtk_menu_item_new_with_label (info.name); gtk_menu_append (GTK_MENU(menu), menuitem); gtk_object_set_data (GTK_OBJECT(menuitem), "method", GINT_TO_POINTER(SWEEP_FILE_METHOD_LIBSNDFILE)); gtk_object_set_data (GTK_OBJECT(menuitem), "format", GINT_TO_POINTER(info.format)); gtk_signal_connect (GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(file_set_format_cb), sample); gtk_widget_show (menuitem); } #else for (i = 0; i < sizeof(file_formats)/sizeof(sw_file_format_desc); i++) { desc = &file_formats[i]; menuitem = gtk_menu_item_new_with_label (_(desc->name)); gtk_menu_append (GTK_MENU(menu), menuitem); gtk_object_set_data (GTK_OBJECT(menuitem), "method", GINT_TO_POINTER(SWEEP_FILE_METHOD_LIBSNDFILE)); gtk_object_set_data (GTK_OBJECT(menuitem), "format", GINT_TO_POINTER(desc->file_format)); gtk_signal_connect (GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(file_set_format_cb), sample); gtk_widget_show (menuitem); } #endif menuitem = gtk_menu_item_new (); /* Separator */ gtk_menu_append (GTK_MENU(menu), menuitem); gtk_widget_show (menuitem); /* MP3 (encoding not supported due to patent restrictions) */ menuitem = gtk_menu_item_new_with_label (_("MP3 (Use Ogg Vorbis instead)")); gtk_widget_set_sensitive (menuitem, FALSE); gtk_menu_append (GTK_MENU(menu), menuitem); gtk_widget_show (menuitem); #ifdef HAVE_OGGVORBIS /* Ogg Vorbis */ menuitem = gtk_menu_item_new_with_label ("Ogg Vorbis (Xiph.org)"); gtk_menu_append (GTK_MENU(menu), menuitem); gtk_object_set_data (GTK_OBJECT(menuitem), "method", GINT_TO_POINTER(SWEEP_FILE_METHOD_OGGVORBIS)); gtk_object_set_data (GTK_OBJECT(menuitem), "format", GINT_TO_POINTER(0)); gtk_signal_connect (GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(file_set_format_cb), sample); gtk_widget_show (menuitem); #endif #ifdef HAVE_SPEEX /* Speex */ menuitem = gtk_menu_item_new_with_label ("Speex (Xiph.org)"); gtk_menu_append (GTK_MENU(menu), menuitem); gtk_object_set_data (GTK_OBJECT(menuitem), "method", GINT_TO_POINTER(SWEEP_FILE_METHOD_SPEEX)); gtk_object_set_data (GTK_OBJECT(menuitem), "format", GINT_TO_POINTER(0)); gtk_signal_connect (GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(file_set_format_cb), sample); gtk_widget_show (menuitem); #endif return menu; } void sample_save_as_cb(GtkWidget * widget, gpointer data) { GtkWidget *dialog; gint win_width, win_height; sw_view * view = (sw_view *)data; sw_sample * sample; GtkWidget * save_options; GtkWidget * frame; GtkWidget * hbox; GtkWidget * label; GtkWidget * option_menu; GtkWidget * save_menu; struct stat statbuf; gchar *filename; gint retval; save_as_data * sd; char buf[512]; char * last_save; win_width = gdk_screen_width () / 2; win_height = gdk_screen_height () / 2; sample = view->sample; dialog = gtk_file_chooser_dialog_new (_("Sweep: Save file"), GTK_WINDOW(view->window), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); //gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); attach_window_close_accel(GTK_WINDOW(dialog)); sweep_set_window_icon (GTK_WINDOW(dialog)); save_options = gtk_hbox_new (TRUE, 1); frame = gtk_frame_new (_("Save Options")); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_box_pack_start (GTK_BOX (save_options), frame, TRUE, TRUE, 4); hbox = gtk_hbox_new (FALSE, 4); gtk_container_set_border_width (GTK_CONTAINER (hbox), 4); gtk_container_add (GTK_CONTAINER (frame), hbox); gtk_widget_show (hbox); label = gtk_label_new (_("Determine File Type:")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); option_menu = gtk_option_menu_new (); gtk_box_pack_start (GTK_BOX (hbox), option_menu, TRUE, TRUE, 0); gtk_widget_show (option_menu); save_menu = create_save_menu (sample); gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), save_menu); gtk_widget_show (frame); /* pack the containing save_options hbox into the save-dialog */ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), save_options, FALSE, FALSE, 0); gtk_widget_show (save_options); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_widget_set_size_request (dialog, win_width, win_height); if (strcmp (g_path_get_dirname(sample->pathname), ".") == 0) { last_save = prefs_get_string (LAST_SAVE_KEY); if (last_save != NULL) { gchar * last_save_dir = g_dirname (last_save); gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), last_save_dir); gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), sample->pathname); g_free (last_save_dir); g_free (last_save); } } else { retval = gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(dialog), sample->pathname); /* FIXME: bug (local only?) causes gtk_file_chooser_set_filename to fail silently in some cases*/ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); //printf("filename pre: %s\n", filename); //printf("sample->pathname: %s\n", sample->pathname); } retval = gtk_dialog_run (GTK_DIALOG (dialog)); filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); //printf("filename post: %s\n", filename); sample = view->sample; sd = g_malloc (sizeof (save_as_data)); sd->sample = sample; sd->pathname = filename; if (retval == GTK_RESPONSE_ACCEPT) { if (!sweep_dir_exists (filename)) { g_free (sd); g_free (filename); return; } if (stat (filename, &statbuf) == -1) { switch (errno) { case ENOENT: /* If it doesn't exist, it's ok to save as */ overwrite_ok_cb (NULL, sd); break; default: sweep_perror (errno, filename); break; } } else { /* file exists */ if (access(filename, W_OK) == -1) { sweep_perror (errno, _("You are not allowed to write to\n%s"), filename); } else { snprintf (buf, sizeof (buf), _("%s exists. Overwrite?"), filename); question_dialog_new (sample, _("File exists"), buf, _("Overwrite"), _("Don't overwrite"), G_CALLBACK (overwrite_ok_cb), sd, G_CALLBACK (overwrite_cancel_cb), sd, SWEEP_EDIT_MODE_META); } } /* FIXME: wrapped this due to the above gtk_file_chooser_set_filename problem */ } else if (sd->pathname != NULL) { gchar * msg; msg = g_strdup_printf (_("Save as %s cancelled"), g_basename (sd->pathname)); sample_set_tmp_message (sd->sample, msg); g_free (msg); } else { g_free (sd); g_free (filename); } gtk_widget_destroy (dialog); } static void sample_save_ok_cb(GtkWidget * widget, gpointer data) { sw_view * view = (sw_view *)data; sw_sample * sample = view->sample; switch (sample->file_method) { case SWEEP_FILE_METHOD_LIBSNDFILE: sndfile_sample_save (sample, sample->pathname); break; #ifdef HAVE_OGGVORBIS case SWEEP_FILE_METHOD_OGGVORBIS: vorbis_save_options_dialog (sample, sample->pathname); break; #endif #ifdef HAVE_SPEEX case SWEEP_FILE_METHOD_SPEEX: speex_save_options_dialog (sample, sample->pathname); break; #endif case SWEEP_FILE_METHOD_MP3: mp3_unsupported_dialog (); break; default: sample_save_as_cb (widget, data); break; } } void sample_save_cb (GtkWidget * widget, gpointer data) { sw_view * view = (sw_view *)data; sw_sample * sample; char buf[512]; sample = view->sample; if (sample->last_mtime == 0 || sample->file_method == SWEEP_FILE_METHOD_MP3) { sample_save_as_cb (widget, data); } else if (sample_mtime_changed (sample)) { snprintf (buf, sizeof (buf), _("%s\n has changed on disk.\n\n" "Are you sure you want to save?"), sample->pathname); question_dialog_new (sample, _("File modified"), buf, _("Save"), _("Don't save"), G_CALLBACK (sample_save_ok_cb), view, NULL, NULL, SWEEP_EDIT_MODE_ALLOC); } else { sample_save_ok_cb (widget, view); } }