/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * GnoWavCut -- a GNOME/GTK+ based RIFF PCM Wave file splitter * Copyright (C) 2000 Yoichi Imai * * 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. */ #include "config.h" #include #include "main.h" #include "split.h" #include "utils.h" #include "prefs.h" #include #include #include #include #include #include #include #define BUFFER 4096 #define WAVE_FILE_SUFFIX "wav" gboolean continue_writing = TRUE; static gboolean split_create_list(GnoWavCut *gnowavcut, SplitDialog *sdialog); static void split_dialog_clicked_cb(GtkWidget *widget, gint button_number, gpointer data); static void split_create_dialog(SplitDialog *sdialog); static void split_write(SplitDialog *sdialog); static void split_set_wave_header(WaveInfo *dest_info, WaveInfo *base_info, guint32 data_size); static SplitStatus split_write_loop(SplitDialog *sdialog); static void split_update_progress_all(SplitDialog *sdialog, int file_num); static void split_update_progress_file(SplitDialog *sdialog, guint32 start, guint32 end, guint32 cur); static SplitStatus split_write_wave(SplitDialog *sdialog, gchar *base_file_name, int old_fd, WaveInfo *wi, TimeData *time); static SplitStatus split_write_wave_with_filtering(SplitDialog *sdialog, gchar *base_file_name, int old_fd, WaveInfo *wi, TimeData *time_data); static void split_start(GnoWavCut *gnowavcut, gboolean use_filter); static void split_update_progress_file(SplitDialog *sdialog, guint32 start, guint32 end, guint32 cur) { gchar *label_text; guint32 total_size, now_size; gfloat total_mbyte, now_mbyte; gfloat ratio; total_mbyte = (gfloat) 0; now_mbyte = (gfloat) 0; ratio = 0.0; total_size = end - start; now_size = cur - start; if(total_size > 0) total_mbyte = (gfloat) total_size / 1024 / 1024; if(now_size > 0) now_mbyte = (gfloat) now_size / 1024 / 1024; label_text = g_strdup_printf(_("%.2fMByte / %.2fMByte"), now_mbyte, total_mbyte); if(now_size > 0 && total_size > 0) ratio = (gfloat) now_size / total_size; gtk_adjustment_set_value(GTK_ADJUSTMENT(sdialog->hadj_file), ratio); gtk_label_set_text(GTK_LABEL(sdialog->label_file), label_text); } static void split_update_progress_all(SplitDialog *sdialog, int file_num) { int max_file_num; gchar *file_text; gchar *file_name; gchar *suffix; max_file_num = sdialog->max_file_num; file_text = g_strdup_printf(_("File: %d / %d"), file_num - 1, max_file_num); if(sdialog->use_filter) suffix = prefs_get_suffix(); else suffix = g_strdup(WAVE_FILE_SUFFIX); file_name = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s-%d%.2d.%s", sdialog->dir_name, sdialog->base_name, sdialog->safely_base_num, file_num, suffix); g_free(suffix); gtk_adjustment_set_value(GTK_ADJUSTMENT(sdialog->hadj_all), (gfloat) file_num - 1); gtk_label_set_text(GTK_LABEL(sdialog->label_filename), file_name); gtk_label_set_text(GTK_LABEL(sdialog->label_all), file_text); g_free(file_text); } static void split_set_wave_header(WaveInfo *dest_info, WaveInfo *base_info, guint32 data_size) { strncpy(dest_info->id_riff, "RIFF", 4); dest_info->riff_size = WAVE_HEADER_SIZE + data_size - 8; strncpy(dest_info->type_riff, "WAVE", 4); strncpy(dest_info->id_fmt, "fmt ", 4); dest_info->fmt_size = 16; dest_info->format_tag = FORMAT_PCM; dest_info->channels = base_info->channels; dest_info->samples_per_sec = base_info->samples_per_sec; dest_info->size_per_sec = base_info->size_per_sec; dest_info->size_per_sample = base_info->size_per_sample; dest_info->bits_per_sample = base_info->bits_per_sample; strncpy(dest_info->id_data, "data", 4); dest_info->data_size = data_size; } /* base_file_name is like "/tmp/test-001" , which doesn't include suffix */ static SplitStatus split_write_wave(SplitDialog *sdialog, gchar *base_file_name, int old_fd, WaveInfo *wi, TimeData *time_data) { gchar *file_name; int new_fd; guint16 buf[BUFFER]; guint32 now_point, len; file_name = g_strdup_printf("%s.%s", base_file_name, WAVE_FILE_SUFFIX); new_fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); g_free(file_name); if(new_fd < 0) { sdialog->error_msg = g_strdup_printf(_("Can't open %s to write (%s)"), file_name, strerror(errno)); return SPLIT_ERROR; } if(write(new_fd, wi, WAVE_HEADER_SIZE) != WAVE_HEADER_SIZE) { sdialog->error_msg = g_strdup(_("Header writing error")); close(new_fd); return SPLIT_ERROR; } if((now_point = lseek(old_fd, time_data->start, SEEK_SET)) < 0) { sdialog->error_msg = g_strdup(_("lseek error")); close(new_fd); return SPLIT_ERROR; } while(now_point < time_data->end) { if(now_point > time_data->end - BUFFER * sizeof(guint16)) { len = time_data->end - now_point; } else { len = BUFFER * sizeof(guint16); } if(read(old_fd, buf, len) != len) { sdialog->error_msg = g_strdup_printf(_("Can't read old wave file. (%s)"), strerror(errno)); close(new_fd); return SPLIT_ERROR; } if(write(new_fd, buf, len) != len) { sdialog->error_msg = g_strdup_printf(_("Can't write new wave file. (%s)"), strerror(errno)); close(new_fd); return SPLIT_ERROR; } now_point += len; split_update_progress_file(sdialog, time_data->start, time_data->end, now_point); GTK_EVENTS_FLUSH(); if(!continue_writing) { close(new_fd); return SPLIT_CANCEL; } } close(new_fd); return SPLIT_SUCCESS; } static SplitStatus split_write_wave_with_filtering(SplitDialog *sdialog, gchar *base_file_name, int old_fd, WaveInfo *wi, TimeData *time_data) { gchar *file_name; int new_fd; guint16 buf[BUFFER]; guint32 now_point, len; int pipe_fd[2]; int pid; gchar *suffix; gchar **filter_command_array; suffix = prefs_get_suffix(); file_name = g_strdup_printf("%s.%s", base_file_name, suffix); g_free(suffix); new_fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if(new_fd < 0) { sdialog->error_msg = g_strdup_printf(_("Can't open %s to write (%s)"), file_name, strerror(errno)); return SPLIT_ERROR; } close(new_fd); if(pipe(pipe_fd) == -1) { sdialog->error_msg = g_strdup(_("Can't pipe()")); return SPLIT_ERROR; } pid = fork(); if(pid == -1) { sdialog->error_msg = g_strdup(_("Can't fork()")); return SPLIT_ERROR; } if(pid == 0) { filter_command_array = utils_get_command_array(settings->filter_command, file_name); close(pipe_fd[1]); dup2(pipe_fd[0], 0); if(execvp(filter_command_array[0], filter_command_array)) { g_warning("Process error"); g_warning(strerror(errno)); exit(1); } } close(pipe_fd[0]); if(write(pipe_fd[1], wi, WAVE_HEADER_SIZE) != WAVE_HEADER_SIZE) { sdialog->error_msg = g_strdup(_("Header writing error")); kill(pid, SIGTERM); return SPLIT_ERROR; } if((now_point = lseek(old_fd, time_data->start, SEEK_SET)) < 0) { sdialog->error_msg = g_strdup(_("lseek error")); kill(pid, SIGTERM); return SPLIT_ERROR; } while(now_point < time_data->end) { if(now_point > time_data->end - BUFFER * sizeof(guint16)) { len = time_data->end - now_point; } else { len = BUFFER * sizeof(guint16); } if(read(old_fd, buf, len) != len) { sdialog->error_msg = g_strdup_printf(_("Can't read old wave file. (%s)"), strerror(errno)); kill(pid, SIGTERM); return SPLIT_ERROR; } if(write(pipe_fd[1], buf, len) != len) { sdialog->error_msg = g_strdup_printf(_("Can't write new wave file.")); kill(pid, SIGTERM); return SPLIT_ERROR; } now_point += len; split_update_progress_file(sdialog, time_data->start, time_data->end, now_point); GTK_EVENTS_FLUSH(); if(!continue_writing) { kill(pid, SIGTERM); return SPLIT_CANCEL; } } close(pipe_fd[1]); /* kill(pid, SIGTERM); */ return SPLIT_SUCCESS; } static SplitStatus split_write_loop(SplitDialog *sdialog) { int old_fd; GSList *timelist, *cur; WaveInfo wi; guint32 data_size; int file_num; gchar *base_file_name; TimeData *time_data; SplitStatus status; timelist = sdialog->timelist; if((old_fd = open(sdialog->old_wave_name, O_RDONLY)) == -1) { sdialog->error_msg = g_strdup(_("File open error(base wave file)")); return SPLIT_ERROR; } file_num = 0; for(cur = timelist; cur != NULL; cur = cur->next) { file_num++; time_data = (TimeData *)cur->data; if(!continue_writing) return SPLIT_CANCEL; split_update_progress_all(sdialog, file_num); GTK_EVENTS_FLUSH(); data_size = time_data->end - time_data->start; split_set_wave_header(&wi, sdialog->wave_info, data_size); base_file_name = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s-%d%.2d", sdialog->dir_name, sdialog->base_name, sdialog->safely_base_num, file_num); if (sdialog->use_filter) status = split_write_wave_with_filtering(sdialog, base_file_name, old_fd, &wi, time_data); else status = split_write_wave(sdialog, base_file_name, old_fd, &wi, time_data); if(status != SPLIT_SUCCESS) { close(old_fd); return status; } } close(old_fd); return SPLIT_SUCCESS; } static void split_write(SplitDialog *sdialog) { int i, j; struct stat s; gchar *file_name; SplitStatus status; gchar *suffix; if(sdialog->use_filter) { suffix = prefs_get_suffix(); } else { suffix = g_strdup(WAVE_FILE_SUFFIX); } for(i = 0; i < 100; i++) { for(j = 1; j < g_slist_length(sdialog->timelist) + 1; j++) { file_name = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s-%d%.2d.%s", sdialog->dir_name, sdialog->base_name, i, j, suffix); if(stat(file_name, &s) == 0) { g_free(file_name); break; } g_free(file_name); } if(j == g_slist_length(sdialog->timelist) + 1) { break; } } g_free(suffix); if(i >= 100) { utils_msgbox_error(_("There are too many files in selected directory.")); gtk_widget_destroy(sdialog->dialog); return; } sdialog->safely_base_num = i; status = split_write_loop(sdialog); gtk_widget_destroy(sdialog->dialog); switch (status) { case SPLIT_SUCCESS: utils_msgbox_info(_("Success to save!")); break; case SPLIT_CANCEL: utils_msgbox_warning(_("Canceled to split")); break; default: /* Error */ if(sdialog->error_msg) { utils_msgbox_error(sdialog->error_msg); g_free(sdialog->error_msg); } break; } g_slist_foreach(sdialog->timelist, (GFunc)g_free, NULL); g_slist_free(sdialog->timelist); g_free(sdialog); } static void split_start(GnoWavCut *gnowavcut, gboolean use_filter) { SplitDialog *sdialog; struct stat s; if(gnowavcut->file_name == NULL) return; utils_gnowavcut_if_playing(gnowavcut, FALSE); sdialog = g_new0(SplitDialog, 1); sdialog->wave_info = gnowavcut->wave_info; sdialog->timelist = NULL; sdialog->parent_window = &(GNOME_APP(gnowavcut->app)->parent_object); sdialog->old_wave_name = gnowavcut->file_name; sdialog->use_filter = use_filter; sdialog->base_name = gtk_entry_get_text(GTK_ENTRY(gnowavcut->entry_base)); if(strcmp(sdialog->base_name, "") == 0) { utils_msgbox_error(_("Base Name entry is empty.")); return; } if(strstr(gtk_entry_get_text(GTK_ENTRY(gnowavcut->entry_base)), "/") != NULL) { utils_msgbox_error(_("Can't use \"/\" for Base name.")); return; } sdialog->dir_name = gtk_entry_get_text(GTK_ENTRY(gnome_file_entry_gtk_entry(GNOME_FILE_ENTRY(gnowavcut->fentry_dir)))); if(stat(sdialog->dir_name, &s) < 0) { utils_msgbox_error(_("Selected directory is not found.")); return; } if(!S_ISDIR(s.st_mode)) { utils_msgbox_error(_("Selected directory is wrong.")); return; } if(split_create_list(gnowavcut, sdialog) == FALSE) { utils_msgbox_error(_("Split point list creation error")); g_slist_foreach(sdialog->timelist, (GFunc)g_free, NULL); g_slist_free(sdialog->timelist); return; } continue_writing = TRUE; split_create_dialog(sdialog); } static gboolean split_create_list(GnoWavCut *gnowavcut, SplitDialog *sdialog) { gchar *s_start, *s_end; guint32 start, end, i; TimeData *time_data; GtkWidget *clist; clist = gnowavcut->clist; if(GTK_CLIST(clist)->rows <= 0) return FALSE; for(i = 0; i < GTK_CLIST(clist)->rows; i++) { gtk_clist_get_text(GTK_CLIST(clist), i, 0, &s_start); if(s_start == NULL) return FALSE; if(utils_get_cur_from_mini_time(sdialog->wave_info, s_start, &start) == FALSE) return FALSE; gtk_clist_get_text(GTK_CLIST(clist), i, 1, &s_end); if(s_end == NULL) return FALSE; if(utils_get_cur_from_mini_time(sdialog->wave_info, s_end, &end) == FALSE) return FALSE; if(start > end) return FALSE; if(start == end) continue; time_data = g_new0(TimeData, 1); time_data->start = start; time_data->end = end; sdialog->timelist = g_slist_append(sdialog->timelist, time_data); } sdialog->max_file_num = g_slist_length(sdialog->timelist); return TRUE; } static void split_create_dialog(SplitDialog *sdialog) { gchar *file_num; sdialog->dialog = gnome_dialog_new(_("Splitting wave file..."), GNOME_STOCK_BUTTON_CANCEL, NULL); gnome_dialog_set_parent (GNOME_DIALOG (sdialog->dialog), sdialog->parent_window); file_num = g_strdup_printf(_("File: 0 / %d"), sdialog->max_file_num); sdialog->label_all = gtk_label_new(file_num); g_free(file_num); sdialog->hadj_all = gtk_adjustment_new(0.0, 0.0, (gfloat) sdialog->max_file_num, 0.1, 0.1, 0.1); sdialog->progress_all = gtk_progress_bar_new_with_adjustment(GTK_ADJUSTMENT(sdialog->hadj_all)); sdialog->label_filename = gtk_label_new(""); sdialog->label_file = gtk_label_new(_("0MByte / 0MByte")); sdialog->hadj_file = gtk_adjustment_new(0.0, 0.0, 1.0, 0.1, 0.1, 0.1); sdialog->progress_file = gtk_progress_bar_new_with_adjustment(GTK_ADJUSTMENT(sdialog->hadj_file)); gtk_widget_show(sdialog->label_all); gtk_widget_show(sdialog->label_filename); gtk_widget_show(sdialog->label_file); gtk_widget_show(sdialog->progress_all); gtk_widget_show(sdialog->progress_file); gtk_widget_set_usize(sdialog->progress_all, 200, -2); gtk_widget_set_usize(sdialog->progress_file, 200, -2); gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(sdialog->dialog)->vbox), sdialog->label_all, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(sdialog->dialog)->vbox), sdialog->progress_all, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(sdialog->dialog)->vbox), sdialog->label_filename, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(sdialog->dialog)->vbox), sdialog->label_file, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(sdialog->dialog)->vbox), sdialog->progress_file, TRUE, TRUE, 0); gtk_signal_connect(GTK_OBJECT(GNOME_DIALOG(sdialog->dialog)), "clicked", GTK_SIGNAL_FUNC(split_dialog_clicked_cb), sdialog); gtk_window_set_modal(GTK_WINDOW(sdialog->dialog), TRUE); gtk_widget_show(sdialog->dialog); split_write(sdialog); } static void split_dialog_clicked_cb(GtkWidget *widget, gint button_number, gpointer data) { continue_writing = FALSE; } void split_start_cb(GtkWidget *widget, gpointer data) { split_start((GnoWavCut *)data, FALSE); } void split_start_with_filtering_cb(GtkWidget *widget, gpointer data) { split_start((GnoWavCut *)data, TRUE); }