1 /* wavbreaker - A tool to split a wave file up into multiple waves.
2  * Copyright (C) 2002-2006 Timothy Robinson
3  * Copyright (C) 2006-2019 Thomas Perl
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 #include <config.h>
21 
22 #include "moodbar.h"
23 #include "popupmessage.h"
24 #include "wavbreaker.h"
25 
26 #if defined(WANT_MOODBAR)
27 
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <sys/wait.h>
32 #include <libgen.h>
33 
34 #include <locale.h>
35 #include "gettext.h"
36 
37 static pid_t moodbar_pid;
38 static gboolean moodbar_cancelled;
39 static GtkWidget *moodbar_wait_dialog;
40 static guint moodbar_update_source_id;
41 static gchar *moodbar_filename;
42 
43 static void
cancel_moodbar_process(GtkWidget * widget,gpointer user_data)44 cancel_moodbar_process(GtkWidget *widget, gpointer user_data)
45 {
46     moodbar_cancelled = TRUE;
47     if (moodbar_pid != 0) {
48         kill(moodbar_pid, SIGKILL);
49         moodbar_pid = 0;
50     }
51 
52     if (moodbar_filename) {
53         /* remove (partial) .mood file */
54         unlink(moodbar_filename);
55         free(moodbar_filename);
56         moodbar_filename = NULL;
57     }
58 }
59 
60 static void
hide_moodbar_process(GtkWidget * widget,gpointer user_data)61 hide_moodbar_process(GtkWidget *widget, gpointer user_data)
62 {
63     gtk_widget_hide(GTK_WIDGET(user_data));
64 }
65 
66 void
moodbar_abort()67 moodbar_abort()
68 {
69     cancel_moodbar_process(NULL, NULL);
70 }
71 
72 static gboolean
update_moodbar_func(gpointer user_data)73 update_moodbar_func(gpointer user_data)
74 {
75 	GtkWidget *child = user_data;
76 
77 	gtk_progress_bar_pulse(GTK_PROGRESS_BAR(child));
78 
79 	int child_status;
80 	if (waitpid(moodbar_pid, &child_status, WNOHANG) != 0) {
81 		gtk_widget_destroy(moodbar_wait_dialog);
82 		moodbar_wait_dialog = NULL;
83 
84 		if (child_status != 0 && !moodbar_cancelled) {
85 			popupmessage_show(NULL, _("Cannot launch \"moodbar\""), _("wavbreaker could not launch the moodbar application, which is needed to generate the moodbar. You can download the moodbar package from:\n\n      http://amarok.kde.org/wiki/Moodbar"));
86 		}
87 
88 		moodbar_update_source_id = 0;
89 
90 		wavbreaker_update_moodbar_state();
91 
92 		return FALSE;
93 	}
94 
95 	return TRUE;
96 }
97 
98 static gchar *
get_moodbar_filename(const gchar * filename)99 get_moodbar_filename(const gchar *filename)
100 {
101 	/* replace ".xxx" with ".mood" */
102 	gchar *fn = (gchar*)malloc(strlen(filename)+2);
103 	strcpy(fn, filename);
104 	strcpy((gchar*)(fn+strlen(fn)-4), ".mood");
105 	return fn;
106 }
107 
108 void
moodbar_generate(GtkWidget * window,const gchar * filename)109 moodbar_generate(GtkWidget *window, const gchar *filename)
110 {
111 	struct stat st;
112 	GtkWidget *child, *cancel_button;
113 
114 	if (moodbar_filename) {
115 		free(moodbar_filename);
116 	}
117 	moodbar_filename = get_moodbar_filename(filename);
118 
119 	if (stat(moodbar_filename, &st) == 0) {
120 		return;
121 	}
122 
123 	if (moodbar_wait_dialog != NULL) {
124 		return;
125 	}
126 
127 	moodbar_cancelled = FALSE;
128 	moodbar_pid = fork();
129 
130 	if (moodbar_pid == 0) {
131 		if (execlp("moodbar", "moodbar", "-o", moodbar_filename, filename, (char*)NULL) == -1) {
132 			fprintf(stderr, "Error running moodbar: %s (Have you installed the \"moodbar\" package?)\n", strerror(errno));
133 			_exit(-1);
134 		}
135 	} else if (moodbar_pid > 0) {
136 		moodbar_wait_dialog = gtk_message_dialog_new(GTK_WINDOW(window),
137 							  GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
138 							  GTK_MESSAGE_INFO,
139 							  GTK_BUTTONS_NONE,
140 							  _("Generating moodbar"));
141 
142 		child = gtk_progress_bar_new();
143 		gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(moodbar_wait_dialog))), child, FALSE, TRUE, 0);
144 
145 		cancel_button = gtk_button_new_with_mnemonic(_("_Hide window"));
146 		g_signal_connect(G_OBJECT(cancel_button), "clicked", G_CALLBACK(hide_moodbar_process), moodbar_wait_dialog);
147 		gtk_dialog_add_action_widget(GTK_DIALOG(moodbar_wait_dialog), cancel_button, -1);
148 
149 		cancel_button = gtk_button_new_with_mnemonic(_("_Cancel"));
150 		g_signal_connect(G_OBJECT(cancel_button), "clicked", G_CALLBACK(cancel_moodbar_process), NULL);
151 		gtk_dialog_add_action_widget(GTK_DIALOG(moodbar_wait_dialog), cancel_button, -1);
152 
153 		gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(moodbar_wait_dialog),
154                 _("The moodbar tool analyzes your audio file and generates a colorful representation of the audio data."));
155 		gtk_window_set_title(GTK_WINDOW(moodbar_wait_dialog), _("Generating moodbar"));
156 		gtk_widget_show_all(moodbar_wait_dialog);
157 
158 		// Update progress bar/window in idle thread
159 		if (moodbar_update_source_id) {
160 			g_source_remove(moodbar_update_source_id);
161 		}
162 		moodbar_update_source_id = g_timeout_add(500, update_moodbar_func, child);
163 	} else {
164 		fprintf(stderr, "fork() failed for moodbar process: %s\n", strerror(errno));
165 	}
166 }
167 
168 MoodbarData *
moodbar_open(const gchar * filename)169 moodbar_open(const gchar *filename)
170 {
171 	gchar *fn = get_moodbar_filename(filename);
172 
173     FILE *fp = fopen(fn, "rb");
174 
175     if (!fp) {
176 		free(fn);
177 		return NULL;
178     }
179 
180     MoodbarData *result = calloc(sizeof(MoodbarData), 1);
181 
182     fseek(fp, 0, SEEK_END);
183     long length = ftell(fp);
184     fseek(fp, 0, SEEK_SET);
185 
186 	result->numFrames = length/3;
187 
188     result->frames = calloc(result->numFrames, sizeof(GdkRGBA));
189 
190     unsigned long pos = 0;
191 
192     guchar tmp[3];
193     while (fread(&tmp, 3, 1, fp) > 0) {
194         result->frames[pos].red = (float)tmp[0]/255.f;
195         result->frames[pos].green = (float)tmp[1]/255.f;
196         result->frames[pos].blue = (float)tmp[2]/255.f;
197         pos++;
198     }
199 
200     fclose(fp);
201     free(fn);
202 
203 	return result;
204 }
205 
206 void
moodbar_free(MoodbarData * data)207 moodbar_free(MoodbarData *data)
208 {
209 	free(data->frames);
210 	free(data);
211 }
212 
213 #else
214 void
moodbar_generate(GtkWidget * window,const gchar * filename)215 moodbar_generate(GtkWidget *window, const gchar *filename)
216 {
217 }
218 
219 MoodbarData *
moodbar_open(const gchar * filename)220 moodbar_open(const gchar *filename)
221 {
222     return NULL;
223 }
224 
225 void
moodbar_free(MoodbarData * data)226 moodbar_free(MoodbarData *data)
227 {
228 }
229 #endif
230