--- /dev/null
+/* vim: set ts=4 sw=4 et: */
+/*
+ * maemo-recorder-ui.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#include <gst/gst.h>
+#include <glib/gi18n-lib.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <locale.h>
+#include <hildon-widgets/hildon-program.h>
+#include <hildon-widgets/hildon-note.h>
+#include <hildon-widgets/hildon-banner.h>
+#include <hildon-widgets/hildon-defines.h>
+#include <hildon-widgets/hildon-file-system-model.h>
+#include <hildon-widgets/hildon-file-chooser-dialog.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "maemo-recorder.h"
+#include "maemo-recorder-ui.h"
+#include "maemo-recorder-file.h"
+
+
+#define DEFAULT_REC_BLOCKSIZE "160"
+
+#define STOP_DELAY 500
+#define REC_UPDATE_INTERVAL 750
+#define PLAY_UPDATE_INTERVAL 200
+
+/* MACROs */
+
+#define GST_TIME_MINS(t) \
+ (guint) ((((GstClockTime)(t)) / (GST_SECOND * 60)) % 60)
+#define GST_TIME_SECS(t) \
+ (guint) ((((GstClockTime)(t)) / GST_SECOND) % 60)
+#define GST_TIME_TO_SECS(t) \
+ (gdouble) (((gdouble)(t)) / (gdouble) GST_SECOND) /* GST_SECOND should be 1e9 */
+#define GST_TIME_MSECS(t) \
+ (guint) (((GstClockTime)(t)) % GST_SECOND)
+
+#define RECORDER_APP_TITLE "Maemo Recorder"
+#define RECORDER_MSG_READY _("Ready")
+#define RECORDER_MSG_STOPPED _("Stopped")
+#define RECORDER_MSG_PAUSED _("Paused")
+#define RECORDER_MSG_PLAYING _("Playing")
+#define RECORDER_MSG_RECORDING _("Recording")
+#define RECORDER_FILE_UNTITLED _("Untitled")
+
+/* general enumerations */
+
+typedef enum
+{
+ DTX_OFF = 0,
+ DTX_ON = 1
+} DTX;
+
+/* menu codes */
+typedef enum
+{
+ MENU_FILE_NEW = 1,
+ MENU_FILE_OPEN,
+ MENU_FILE_SAVE,
+ MENU_FILE_SAVE_AS,
+ MENU_FILE_REC,
+ MENU_FILE_PLAY,
+ MENU_FILE_STOP,
+ MENU_FILE_QUIT
+} MenuActionCode;
+
+typedef enum
+{
+ PIPELINE_PLAY = 1,
+ PIPELINE_PLAY_MP3,
+ PIPELINE_REC
+} PipeLineType;
+
+/* function prototypes */
+
+static gboolean cbBus (GstBus *bus,
+ GstMessage *message,
+ gpointer data);
+
+static void pipelineStateChanged (GstElement *element,
+ GstState old,
+ GstState new,
+ GstState pending,
+ AppData *data);
+
+static void seekToTime(GstElement *pipeline, gdouble secs);
+static gboolean seekToZero(AppData *data, GstElement *pipeline);
+static void setLength(AppData *data, gdouble secs);
+static gboolean cbStopPlayback(AppData *data);
+static void cbStop(GtkWidget* widget, AppData *data);
+static void cbPlay(GtkWidget* widget, AppData *data);
+static void cbRec(GtkWidget* widget, AppData *data);
+static void cbNew(GtkWidget* widget, AppData *data);
+static void cbOpen(GtkWidget* widget, AppData *data);
+/*static void cbSave(GtkWidget* widget, AppData *data);*/
+static void cbSaveAs(GtkWidget* widget, AppData *data);
+static void cbItemClose(GtkWidget *widget, gpointer data);
+static void cbUserSeek(GtkAdjustment *adjustment, gpointer data);
+static gchar* cbFormatSeekbarValue(GtkScale *scale, gdouble value);
+static GtkWidget* createToolBar(AppData *data);
+static void createMenu( AppData *data );
+static gboolean createPipeline(AppData *app, PipeLineType type);
+static void openPlayPipeline( AppData *data );
+static gboolean destroyPipeline(AppData *data, PipeLineType type);
+static gboolean destroyPipelines(AppData *data);
+static void cbItemGroupChanged(gpointer data);
+static gboolean cbUpdateRecLength(AppData *data);
+static void cbDestroy(GtkWidget* widget, GdkEvent *event, gpointer data);
+static gboolean openURI(gpointer user_data);
+static gboolean closeFile(AppData *data);
+static gdouble guessMediaLength(AppData *data);
+static GstCaps *createCapsFilter(AudioFormat format);
+
+static void new_pad_cb (GstElement *wavparse, GstPad *new_pad, gpointer data)
+{
+ GstElement *sink;
+ AppData* app = (AppData*) data;
+
+ ULOG_INFO("new pad");
+
+ sink = gst_element_factory_make ("dsppcmsink", "sink");
+
+ gst_bin_add (GST_BIN (app->playPipeline), sink);
+
+ if (!gst_element_link (wavparse, sink))
+ g_error ("link(wavparse, sink) failed!");
+ gst_element_sync_state_with_parent(sink);
+
+}
+
+static gboolean createPipeline(AppData *app, PipeLineType type)
+{
+ GstElement *src = NULL;
+ GstElement *sink = NULL;
+ GstElement *filter = NULL;
+ GstElement *queue = NULL;
+ GstElement *pipeline = NULL;
+ GstElement *parse = NULL;
+ GstCaps *caps = NULL;
+
+ g_assert(NULL != app);
+
+ pipeline = gst_pipeline_new("pipeline");
+
+ gst_bus_add_watch(gst_pipeline_get_bus (GST_PIPELINE (pipeline)),
+ cbBus, app);
+
+ /* create elements */
+ switch (type)
+ {
+ case PIPELINE_PLAY_MP3:
+ ULOG_INFO("mp3 playback - queue");
+ src = gst_element_factory_make ("filesrc", "source");
+ queue = gst_element_factory_make ("queue", "queue");
+ sink = gst_element_factory_make ("dspmp3sink", "sink");
+
+ g_object_set(G_OBJECT (queue),
+ "max-size-buffers", 8192,
+ "min-threshold-bytes", 131072,
+ "min-threshold-buffers", 2048,
+ NULL );
+ g_object_set(G_OBJECT(src),
+ "location", app->openFileName,
+ NULL);
+ break;
+
+ case PIPELINE_PLAY:
+ src = gst_element_factory_make ("filesrc", "source");
+ /* we need also a filter to modify caps */
+ filter = gst_element_factory_make("capsfilter", "filter");
+ switch (app->filter)
+ {
+ case FORMAT_ILBC:
+ ULOG_INFO("using ilbc sink");
+ sink = gst_element_factory_make ("dspilbcsink", "sink");
+ break;
+
+ case FORMAT_PCMA:
+ case FORMAT_PCMU:
+ case FORMAT_PCM:
+ ULOG_INFO("using pcm sink");
+ sink = gst_element_factory_make ("dsppcmsink", "sink");
+ break;
+
+ case FORMAT_WAV:
+ ULOG_INFO("using wavparse & pcm sink");
+ parse = gst_element_factory_make ("wavparse", "parse");
+ break;
+
+ default:
+ break;
+ }
+
+ g_object_set(G_OBJECT(src),
+ "location", app->openFileName,
+ NULL);
+
+ caps = createCapsFilter(app->filter);
+ g_object_set(G_OBJECT(filter),
+ "caps", caps,
+ NULL);
+ break;
+
+ case PIPELINE_REC:
+ switch (app->filter)
+ {
+ case FORMAT_ILBC:
+ ULOG_INFO("using ilbc source");
+ src = gst_element_factory_make("dspilbcsrc", "source");
+ g_object_set(G_OBJECT(src),
+ "dtx", DTX_OFF,
+ NULL);
+ break;
+
+ case FORMAT_PCMA:
+ case FORMAT_PCMU:
+ case FORMAT_PCM:
+ ULOG_INFO("using pcm source");
+ src = gst_element_factory_make("dsppcmsrc", "source");
+ g_object_set(G_OBJECT (src),
+ "blocksize", DEFAULT_REC_BLOCKSIZE,
+ "dtx", DTX_OFF,
+ NULL);
+ break;
+
+ case FORMAT_WAV:
+ ULOG_INFO("using pcm source & wavenc");
+ src = gst_element_factory_make("dsppcmsrc", "source");
+ g_object_set(G_OBJECT (src),
+ "blocksize", DEFAULT_REC_BLOCKSIZE,
+ "dtx", DTX_OFF,
+ NULL);
+ parse = gst_element_factory_make("wavenc", "enc");
+ break;
+
+ default:
+ ULOG_WARN("Unknown filter type!");
+ break;
+ }
+
+ filter = gst_element_factory_make("capsfilter", "filter");
+ caps = createCapsFilter(app->filter);
+ g_object_set(G_OBJECT(filter),
+ "caps", caps,
+ NULL);
+
+ sink = gst_element_factory_make("filesink", "sink");
+
+ g_object_set(G_OBJECT(sink),
+ "location", app->saveFileName,
+ NULL);
+ break;
+
+ default:
+ ULOG_ERR("Invalid pipeline type!");
+ gst_object_unref(pipeline);
+ return FALSE;
+ }
+
+ if (!src || !pipeline)
+ {
+ ULOG_ERR("Could not create GstElement!");
+ return FALSE;
+ }
+
+ if (!sink && app->filter != FORMAT_WAV)
+ {
+ ULOG_ERR("Could not create GstElement!");
+ return FALSE;
+ }
+
+ ULOG_INFO("Create pipeline");
+
+ /* add to pipeline and link */
+ switch (type)
+ {
+ case PIPELINE_REC:
+ switch (app->filter)
+ {
+ case FORMAT_ILBC:
+ case FORMAT_PCM:
+ case FORMAT_PCMA:
+ if (!filter)
+ {
+ ULOG_ERR("Could not create filter GstElement!");
+ return FALSE;
+ }
+ gst_bin_add_many(GST_BIN(pipeline), src, filter, sink, NULL);
+
+ if (!gst_element_link_many (src, filter, sink, NULL))
+ {
+ ULOG_ERR("gst_element_link failed for src, filter and sink!");
+ return FALSE;
+ }
+ break;
+ case FORMAT_WAV:
+ gst_bin_add_many(GST_BIN(pipeline), src, parse, sink, NULL);
+ if (!gst_element_link_many (src, parse, sink, NULL))
+ {
+ ULOG_ERR("gst_element_link failed for src, parse and sink!");
+ }
+ break;
+
+ default:
+ break;
+
+ }
+ break;
+
+ case PIPELINE_PLAY:
+ switch (app->filter)
+ {
+ case FORMAT_ILBC:
+ case FORMAT_PCM:
+ case FORMAT_PCMA:
+ if (!filter)
+ {
+ ULOG_ERR("Could not create filter GstElement!");
+ return FALSE;
+ }
+ gst_bin_add_many(GST_BIN(pipeline), src, filter, sink, NULL);
+
+ if (!gst_element_link_many (src, filter, sink, NULL))
+ {
+ ULOG_ERR("gst_element_link failed for src, filter and sink!");
+ return FALSE;
+ }
+
+ break;
+ case FORMAT_WAV:
+ gst_bin_add_many(GST_BIN(pipeline), src, parse, NULL);
+ if (!gst_element_link_many (src, parse, NULL))
+ {
+ ULOG_ERR("gst_element_link failed for src, parse and sink!");
+ return FALSE;
+ }
+ app->playPipeline = pipeline;
+ g_signal_connect(parse, "pad_added",
+ G_CALLBACK(new_pad_cb), app);
+
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case PIPELINE_PLAY_MP3:
+ default:
+ gst_bin_add_many(GST_BIN(pipeline), src, queue, sink, NULL);
+
+ if(!gst_element_link_many(src, queue, sink, NULL))
+ {
+ ULOG_ERR("gst_element_link failed for src and sink!");
+ return FALSE;
+ }
+
+ break;
+ }
+
+ /* set application data */
+ if (type == PIPELINE_REC)
+ {
+ app->recPipeline = pipeline;
+ }
+ else
+ {
+ app->playPipeline = pipeline;
+ app->playPipelineType = type;
+ }
+
+ if (caps)
+ {
+ gst_caps_unref(caps);
+ caps = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean destroyPipelines(AppData *data)
+{
+ gboolean ret = FALSE;
+
+ /* ugly hack with pipeline types, but works though */
+ ret != destroyPipeline(data, PIPELINE_REC);
+ ret != destroyPipeline(data, PIPELINE_PLAY);
+ return ret;
+}
+
+static gboolean destroyPipeline(AppData *data, PipeLineType type)
+{
+ GstState state;
+ GstState pending;
+ GstElement *pipeline = NULL;
+
+ ULOG_INFO("%s() - Stopping playback/recording", G_STRFUNC);
+
+ switch (type)
+ {
+ case PIPELINE_REC:
+ pipeline = data->recPipeline;
+ /*
+ data->recPipeline = NULL;
+ */
+ break;
+ default:
+ pipeline = data->playPipeline;
+ /*
+ data->playPipeline = NULL;
+ */
+ break;
+ }
+
+ if (!GST_IS_ELEMENT(pipeline))
+ return TRUE;
+
+ /* this unallocates everything */
+ gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);
+
+ /* for some reason the state does not update manually */
+ gst_element_get_state(pipeline, &state,
+ &pending, GST_CLOCK_TIME_NONE);
+ pipelineStateChanged(pipeline,
+ state,
+ state,
+ pending,
+ data);
+
+ /*
+ gst_object_unref(pipeline);
+ */
+
+ return TRUE;
+}
+
+
+/* callbacks */
+
+static void pipelineStateChanged (GstElement *element,
+ GstState old,
+ GstState new,
+ GstState pending,
+ AppData * data)
+{
+ g_assert(NULL != data);
+
+ switch (new)
+ {
+ case GST_STATE_PLAYING:
+ if(APPSTATE_RECORDING == getAppState(data))
+ {
+ gchar *tmp = g_strdup_printf("%s...", RECORDER_MSG_RECORDING);
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, tmp);
+ g_free(tmp);
+ tmp = NULL;
+ ULOG_INFO("%s() - Recording", G_STRFUNC);
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry),
+ RECORDER_MSG_RECORDING);
+
+ gtk_widget_set_state(data->buttonRec, GTK_STATE_ACTIVE);
+
+ if (data->recUpdateId == 0 && gettimeofday(&data->recStartTv, NULL) == 0)
+ {
+ data->recUpdateId = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, REC_UPDATE_INTERVAL, (GSourceFunc) cbUpdateRecLength, data, NULL);
+ }
+ }
+ else
+ {
+ gchar *tmp = g_strdup_printf("%s...", RECORDER_MSG_PLAYING);
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, tmp);
+ g_free(tmp);
+ tmp = NULL;
+ ULOG_INFO("%s() - Playing", G_STRFUNC);
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry),
+ RECORDER_MSG_PLAYING);
+ gtk_widget_set_state(data->buttonPlay, GTK_STATE_ACTIVE);
+ /*
+ gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PAUSE);
+ */
+ }
+
+ break;
+
+ case GST_STATE_READY:
+ //hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, "Ready...");
+ ULOG_INFO("%s() - Ready", G_STRFUNC);
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry),
+ RECORDER_MSG_READY);
+ break;
+
+ case GST_STATE_PAUSED:
+ {
+ gint64 pos = 0;
+ GstFormat fmt = GST_FORMAT_TIME;
+ ULOG_INFO("%s() - Paused", G_STRFUNC);
+
+ /* if pipeline pos == 0 => stopped, else => paused */
+ if (GST_IS_ELEMENT(data->playPipeline) && gst_element_query_position(data->playPipeline, &fmt, &pos) && pos != 0)
+ {
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, RECORDER_MSG_PAUSED);
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry),
+ RECORDER_MSG_PAUSED);
+ /*
+ gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
+ gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
+ */
+ }
+ else
+ {
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, RECORDER_MSG_STOPPED);
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry),
+ RECORDER_MSG_STOPPED);
+ gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
+ gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
+ }
+ gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
+ }
+ break;
+
+ case GST_STATE_NULL:
+ ULOG_INFO("%s() - Null", G_STRFUNC);
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, RECORDER_MSG_STOPPED);
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry),
+ RECORDER_MSG_READY);
+ gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
+ gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
+ gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
+
+ break;
+
+ default:
+ ULOG_WARN("%s() - default case", G_STRFUNC);
+ break;
+ }
+}
+
+
+static gboolean cbBus(GstBus *bus,
+ GstMessage *message,
+ gpointer data)
+{
+ AppData *app = (AppData*)data;
+
+ switch (GST_MESSAGE_TYPE(message))
+ {
+ case GST_MESSAGE_WARNING:
+ {
+ GError *err;
+ gchar *debug;
+
+ gst_message_parse_error (message, &err, &debug);
+ ULOG_WARN("%s() - Warning: %s", G_STRFUNC, err->message);
+ g_error_free (err);
+ g_free (debug);
+ break;
+ }
+ case GST_MESSAGE_ERROR:
+ {
+ GError *err;
+ gchar *debug;
+
+ gst_message_parse_error (message, &err, &debug);
+ ULOG_ERR("%s() - Error: %s", G_STRFUNC, err->message);
+ g_error_free (err);
+ g_free (debug);
+ /* break; */
+ /* flow through to eos */
+ }
+ case GST_MESSAGE_EOS:
+ {
+ ULOG_INFO("%s() - eos", G_STRFUNC);
+
+ switch(getAppState(app))
+ {
+ case APPSTATE_PLAYING:
+ /* stop playback after a short break*/
+ g_timeout_add(STOP_DELAY, (GSourceFunc)cbStopPlayback, data);
+ break;
+
+ case APPSTATE_RECORDING:
+ gst_element_set_state(GST_ELEMENT(app->recPipeline),
+ GST_STATE_PAUSED);
+ destroyPipeline(app, PIPELINE_REC);
+ app->saved = FALSE;
+ break;
+ case APPSTATE_READY:
+ default:
+ /* destroyPipelines(app); */
+ break;
+ }
+ break;
+ }
+ case GST_MESSAGE_STATE_CHANGED:
+ {
+ GstState old;
+ GstState new;
+ GstState pending;
+
+ gst_message_parse_state_changed(message, &old, &new, &pending);
+
+ pipelineStateChanged(NULL, old, new, pending, app);
+
+ break;
+ }
+ default:
+ /* unhandled message */
+ ULOG_WARN("%s() - Unhandled message, type = %d", G_STRFUNC, message->type);
+ break;
+ }
+
+ /* remove message from the queue */
+ return TRUE;
+}
+
+static void cbDestroy(GtkWidget* widget, GdkEvent *event, gpointer data)
+{
+ AppData* app;
+
+ g_assert(data);
+
+ app = (AppData *) data;
+
+ if (!closeFile(app))
+ return;
+
+ destroyPipelines(app);
+ if (app->playPipeline)
+ gst_object_unref(GST_OBJECT(app->playPipeline));
+
+ if (app->recPipeline)
+ gst_object_unref(GST_OBJECT(app->recPipeline));
+
+ gtk_main_quit();
+}
+
+static gboolean cbCheckPosition (AppData *data)
+{
+ GstFormat fmt = GST_FORMAT_TIME;
+ gint64 pos = 0, len = 0;
+ static gboolean lengthSet = FALSE;
+
+ g_assert(NULL != data);
+
+ /* get length */
+ if(!lengthSet && gst_element_query_duration(data->playPipeline, &fmt, &len))
+ {
+ if (len > 0)
+ {
+ gdouble size = 0;
+ size = GST_TIME_TO_SECS(len);
+ setLength(data, size); /* sets lengthEntry and adjustment */
+ lengthSet = TRUE;
+ }
+ }
+
+ /* calculate position */
+ if (gst_element_query_position(data->playPipeline, &fmt, &pos))
+ {
+ gdouble time = GST_TIME_TO_SECS(pos);
+
+ ULOG_DEBUG("pos = %lld, time = %f",
+ pos,
+ time
+ );
+
+ gtk_adjustment_set_value(
+ GTK_ADJUSTMENT(data->mainViewData.adjustment),
+ time);
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
+ }
+
+ if (APPSTATE_PLAYING == getAppState(data))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void cbNew(GtkWidget* widget, AppData *data)
+{
+ g_assert(NULL != data);
+
+ if (!closeFile(data))
+ return;
+
+ /* remove pipelines if existing */
+ destroyPipelines(data);
+ ULOG_DEBUG_F("cbNew");
+ /* destroy tmp file */
+
+ /* clear filenames */
+ g_free(data->openFileName);
+ data->openFileName = NULL;
+ g_free(data->saveFileName);
+ data->saveFileName = NULL;
+/* data->filter = FORMAT_NONE;*/
+ data->file_format = FORMAT_NONE;
+
+ /* update display */
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry),
+ RECORDER_FILE_UNTITLED);
+ setLength(data, 0.0);
+ /* update the display + scale */
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(data->mainViewData.adjustment),
+ 0);
+ gtk_widget_set_sensitive(data->buttonSave, FALSE);
+ gtk_widget_set_sensitive(data->buttonSaveAs, FALSE);
+ data->saved = TRUE;
+
+ gtk_window_set_title(GTK_WINDOW(data->mainView), RECORDER_FILE_UNTITLED);
+
+ ULOG_DEBUG_F("cbNew end");
+}
+
+static void cbOpen(GtkWidget* widget, AppData *data)
+{
+ GtkWidget* dialog = NULL;
+ gchar *tmpfile = NULL;
+ gchar *selected = NULL;
+ AudioFormat format;
+ gchar *basename;
+ gdouble len = -1.0;
+#if 0
+ GtkFileFilter *filter;
+#endif
+
+ ULOG_DEBUG_F("begin");
+ g_assert(NULL != data);
+
+ if (!closeFile(data))
+ return;
+
+#if 0
+ /* create filter */
+ filter = gtk_file_filter_new();
+ gtk_file_filter_add_mime_type(filter, "audio/x-mp3");
+#endif
+
+ g_assert(GTK_IS_WINDOW(data->mainView));
+ /* create dialog */
+ dialog = hildon_file_chooser_dialog_new_with_properties(
+ GTK_WINDOW(data->mainView),
+ "action", GTK_FILE_CHOOSER_ACTION_OPEN,
+ "file-system-model", NULL,
+ "local-only", TRUE,
+ NULL
+ );
+
+ /* show it */
+ gtk_widget_show_all(dialog);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
+ {
+ selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+
+ ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
+
+ if (dialog != NULL && GTK_IS_WIDGET(dialog))
+ {
+ gtk_widget_destroy(dialog);
+ /*
+ ULOG_DEBUG("%s() - dialog destroyed", G_STRFUNC);
+ */
+ }
+
+ if (NULL == selected) /* no file selected */
+ return;
+
+ ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
+
+ if (openFile(selected, &format, &tmpfile))
+ {
+ ULOG_INFO("%s() - openFile() succeeded, format: %d, tmpfile %s", G_STRFUNC, format, tmpfile);
+
+ g_assert(tmpfile);
+
+ /* update filenames */
+ basename = g_path_get_basename(selected);
+
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), selected);
+ gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
+ g_free(basename);
+
+ g_free(data->openFileName);
+ data->openFileName = tmpfile;
+ data->file_format = format;
+ data->filter = format;
+ g_free(data->saveFileName);
+ data->saveFileName = NULL;
+ gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
+
+ len = guessMediaLength(data);
+ if (len > 0.0)
+ setLength(data, len);
+ else
+ setLength(data, 0.0);
+
+ data->saved = TRUE;
+ }
+ else
+ {
+ ULOG_WARN("%s() - openFile() failed", G_STRFUNC);
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not open file!"));
+ }
+
+ g_free(selected);
+
+ ULOG_DEBUG_F("end");
+}
+
+static gboolean
+openURI(gpointer user_data)
+{
+ AppData *data;
+ GnomeVFSURI *uri;
+ gchar *selected = NULL;
+ AudioFormat format;
+ gchar *tmpfile = NULL;
+ gchar *basename = NULL;
+ gdouble len = -1.0;
+
+ g_assert(user_data);
+ data = (AppData *) user_data;
+
+ if (NULL == data->mimeURI)
+ return FALSE;
+
+ uri = gnome_vfs_uri_new(data->mimeURI);
+ selected = g_strdup(gnome_vfs_uri_get_path(uri));
+
+ gnome_vfs_uri_unref(uri);
+ uri = NULL;
+
+ g_free(data->mimeURI);
+ data->mimeURI = NULL;
+
+ /* TODO: the following is duplicated in cbOpen(), move to a tryOpen() function ? */
+
+ if (NULL == selected)
+ return FALSE;
+
+ ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
+
+ if (openFile(selected, &format, &tmpfile))
+ {
+ ULOG_INFO("%s: openFile() succeeded, format: %d, tmpfile %s", G_STRFUNC, format, tmpfile);
+
+ g_assert(tmpfile);
+
+ /* update filenames */
+ basename = g_path_get_basename(selected);
+
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), selected);
+ gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
+ g_free(basename);
+
+ g_free(data->openFileName);
+ data->openFileName = tmpfile;
+ data->file_format = format;
+ data->filter = format;
+ g_free(data->saveFileName);
+ data->saveFileName = NULL;
+ gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
+
+ len = guessMediaLength(data);
+ if (len > 0.0)
+ setLength(data, len);
+ else
+ setLength(data, 0.0);
+
+ data->saved = TRUE;
+ }
+ else
+ {
+ ULOG_WARN("%s() - openFile() failed", G_STRFUNC);
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not open file!"));
+ }
+
+ g_free(selected);
+
+ return FALSE;
+}
+
+static void openPlayPipeline( AppData *data )
+{
+
+ GstFormat fmt = GST_FORMAT_TIME;
+ gint64 len;
+ gdouble size = 0;
+ /* create pipelines */
+ /* check file type */
+ switch (data->file_format)
+ {
+ case FORMAT_PCMA:
+ case FORMAT_PCMU:
+ case FORMAT_PCM:
+ case FORMAT_ILBC:
+ case FORMAT_WAV:
+ destroyPipelines(data);
+ data->filter = data->file_format;
+ createPipeline(data, PIPELINE_PLAY);
+ break;
+ case FORMAT_MP3:
+ destroyPipelines(data);
+ data->filter = data->file_format;
+ createPipeline(data, PIPELINE_PLAY_MP3);
+ break;
+ case FORMAT_NONE:
+ default:
+ ULOG_WARN("%s() - unknown file_format", G_STRFUNC);
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Unknown filetype!"));
+ return;
+
+ break;
+ }
+
+ if (!GST_IS_ELEMENT(data->playPipeline))
+ {
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not create pipeline!"));
+ return;
+ }
+
+ gst_element_set_state(GST_ELEMENT(data->playPipeline), GST_STATE_READY);
+ gst_element_get_state(GST_ELEMENT(data->playPipeline), NULL, NULL, GST_CLOCK_TIME_NONE /* or ns */);
+
+ /* calculate length */
+ if (gst_element_query_duration (data->playPipeline, &fmt, &len))
+ {
+ if (len > 0)
+ {
+ size = GST_TIME_TO_SECS(len);
+ ULOG_INFO("playSize: len:%lld size:%f", len, size);
+ setLength(data, size);
+ }
+ }
+ /*
+ else
+ {
+ ULOG_INFO("playSize else");
+ setLength(data, 0.0);
+ }
+ */
+}
+
+/* returns whether the action can proceed or should be aborted */
+static gboolean
+closeFile(AppData *data)
+{
+ GtkWidget *note;
+ gint i;
+
+ if (data->saved)
+ return TRUE;
+
+ note = hildon_note_new_confirmation_add_buttons(GTK_WINDOW(data->mainView), _("Save recording?"),
+ _("Yes"), GTK_RESPONSE_YES,
+ _("No"), GTK_RESPONSE_NO,
+ _("Cancel"), GTK_RESPONSE_CANCEL,
+ NULL);
+
+ i = gtk_dialog_run(GTK_DIALOG(note));
+ gtk_widget_destroy(note);
+
+ switch (i)
+ {
+ case GTK_RESPONSE_CANCEL:
+ return FALSE;
+
+ case GTK_RESPONSE_NO:
+ return TRUE;
+
+ case GTK_RESPONSE_YES:
+ {
+ cbSaveAs(NULL, data);
+ return data->saved;
+ }
+ default:
+ ULOG_WARN("%s(): unknown response from dialog: %d", G_STRFUNC, i);
+ }
+ return FALSE;
+}
+
+#if 0
+static void cbSave(GtkWidget* widget, AppData *data)
+{
+ GtkWidget* dialog = NULL;
+ const gchar *current;
+ gchar *selected = NULL;
+
+ g_assert(data);
+
+ ULOG_DEBUG("%s() - begin", G_STRFUNC);
+
+ current = gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
+ if (NULL == current || strcmp(current, RECORDER_FILE_UNTITLED) == 0)
+ {
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Nothing to save"));
+ return;
+ }
+
+ /* if saveFileName does not exist run saveas */
+ if (NULL == data->saveFileName)
+ {
+ /* create dialog */
+ dialog = GTK_WIDGET(hildon_file_chooser_dialog_new(
+ GTK_WINDOW(data->mainView),
+ GTK_FILE_CHOOSER_ACTION_SAVE));
+
+ /* show it */
+ gtk_widget_show_all(dialog);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
+ {
+ selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+
+ ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
+
+ gtk_widget_destroy(dialog);
+
+ if (NULL != selected)
+ {
+ ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC,
+ selected);
+ g_free(data->saveFileName);
+ data->saveFileName = g_strdup_printf("%s%s", selected, getExtension(data->file_format));
+ g_free(selected);
+ selected = NULL;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ /* save the file */
+ if (doSave(gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry)), data->saveFileName, data->file_format))
+ {
+ gchar *basename = g_path_get_basename(data->saveFileName);
+ ULOG_INFO("%s() - file succesfully saved!", G_STRFUNC);
+ g_free(data->openFileName);
+ data->openFileName = g_strdup(data->saveFileName);
+
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry),
+ data->saveFileName);
+ gtk_widget_set_sensitive(data->buttonSave, FALSE);
+ gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
+ g_free(basename);
+ }
+
+ ULOG_DEBUG("%s() - end", G_STRFUNC);
+}
+#endif
+
+static void cbSaveAs(GtkWidget* widget, AppData *data)
+{
+ GtkWidget* dialog = NULL;
+ const gchar *current;
+ gchar *selected = NULL;
+
+ g_assert(NULL != data);
+
+ ULOG_DEBUG("%s() - begin", G_STRFUNC);
+
+ current = gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
+ if (NULL == current || strcmp(current, RECORDER_FILE_UNTITLED) == 0)
+ {
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Nothing to save"));
+ return;
+ }
+
+ /* create dialog */
+ dialog = GTK_WIDGET(hildon_file_chooser_dialog_new(
+ GTK_WINDOW(data->mainView),
+ GTK_FILE_CHOOSER_ACTION_SAVE));
+
+ /* show it */
+ gtk_widget_show_all(dialog);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
+ {
+ selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+
+ ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
+ gtk_widget_destroy(dialog);
+
+ if (NULL == selected)
+ return;
+
+ ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
+
+ g_free(data->saveFileName);
+ data->saveFileName = NULL;
+
+ if (saveFile(selected, data->openFileName, data->file_format, &(data->saveFileName)))
+ {
+ gchar *basename;
+ const gchar *ext;
+
+ g_assert(data->saveFileName);
+
+ /* set new title that has the file name */
+ basename = g_path_get_basename(data->saveFileName);
+ ULOG_DEBUG("%s() - file '%s' succesfully saved!", G_STRFUNC, data->saveFileName);
+
+ gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
+
+ /* Houston, we have a kludge:
+ * for AU files we need to keep the old tmpfile for playback
+ * for RAW/iLBC files, we can remove the tmpfile and point openFileName to the saved file
+ */
+ ext = getExtension(data->file_format);
+ if (strcmp(ext, EXTENSION_AU) != 0)
+ {
+ g_free(data->openFileName);
+ data->openFileName = g_strdup(data->saveFileName);
+ }
+
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry),
+ data->saveFileName);
+
+ g_free(basename);
+ data->saved = TRUE;
+ }
+ else
+ {
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Saving file failed!"));
+ }
+
+ g_free(selected);
+ selected = NULL;
+
+ ULOG_DEBUG("%s() - end", G_STRFUNC);
+}
+
+static void cbRec(GtkWidget* widget, AppData *data)
+{
+ g_assert(NULL != data);
+
+ ULOG_DEBUG("%s() - begin", G_STRFUNC);
+
+ if (APPSTATE_READY != getAppState(data))
+ {
+ ULOG_WARN("%s() - state different than READY -> return", G_STRFUNC);
+ return;
+ }
+
+ if (!closeFile(data))
+ return;
+
+ /* clear filenames, use tmp file */
+ g_free(data->openFileName);
+ data->openFileName = NULL;
+
+ g_free(data->saveFileName);
+ data->saveFileName = NULL;
+
+ switch (data->filter)
+ {
+ case FORMAT_PCM:
+ data->saveFileName = g_strdup(DEFAULT_TMP_FILE);
+ data->openFileName = g_strdup(DEFAULT_TMP_FILE);
+ break;
+
+ case FORMAT_PCMA:
+ data->saveFileName = g_strdup(DEFAULT_TMP_PCMA_FILE);
+ data->openFileName = g_strdup(DEFAULT_TMP_PCMA_FILE);
+ break;
+
+ case FORMAT_PCMU:
+ data->saveFileName = g_strdup(DEFAULT_TMP_PCMU_FILE);
+ data->openFileName = g_strdup(DEFAULT_TMP_PCMU_FILE);
+ break;
+
+ case FORMAT_WAV:
+ data->saveFileName = g_strdup(DEFAULT_TMP_WAV_FILE);
+ data->openFileName = g_strdup(DEFAULT_TMP_WAV_FILE);
+ break;
+
+ case FORMAT_ILBC:
+ default:
+ data->saveFileName = g_strdup(DEFAULT_TMP_ILBC_FILE);
+ data->openFileName = g_strdup(DEFAULT_TMP_ILBC_FILE);
+ break;
+ }
+
+ ULOG_INFO("%s() - creating pipelines", G_STRFUNC);
+ /* start recording */
+ /* create related pipelines */
+ if (createPipeline(data, PIPELINE_REC))
+ {
+ ULOG_INFO("%s() - starting recording", G_STRFUNC);
+ /* start recording */
+ gst_element_set_state(GST_ELEMENT(data->recPipeline),
+ GST_STATE_PLAYING);
+
+ /* update display */
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry),
+ data->saveFileName);
+
+ setAppState(data, APPSTATE_RECORDING);
+ gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
+ gtk_window_set_title(GTK_WINDOW(data->mainView), RECORDER_FILE_UNTITLED);
+ data->file_format = data->filter;
+ }
+ else
+ {
+ ULOG_ERR("Could not create rec pipeline!");
+ hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not create pipeline"));
+ setAppState(data, APPSTATE_READY);
+ }
+
+ ULOG_DEBUG("%s() - end", G_STRFUNC);
+}
+
+static void cbPlay(GtkWidget* widget, AppData *data)
+{
+ const gchar * file = NULL;
+
+ g_assert(NULL != data);
+
+ ULOG_DEBUG("%s() - begin", G_STRFUNC);
+
+ file = gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
+ if (NULL == data->openFileName || NULL == file || strcmp(file, RECORDER_FILE_UNTITLED) == 0)
+ {
+ ULOG_WARN("%s() - nothing to play", G_STRFUNC);
+ return;
+ }
+
+ openPlayPipeline(data);
+
+ if (APPSTATE_PLAYING == getAppState(data))
+ {
+ if (GST_IS_ELEMENT(data->playPipeline))
+ {
+ gst_element_set_state(GST_ELEMENT(data->playPipeline), GST_STATE_PAUSED);
+ setAppState(data, APPSTATE_READY);
+ }
+ return;
+ }
+
+ if (APPSTATE_READY != getAppState(data))
+ {
+ ULOG_WARN("%s() - state different than PLAYING or READY -> return", G_STRFUNC);
+ return;
+ }
+
+ ULOG_INFO("filename %s", file);
+
+ /*openPlayPipeline( data );*/
+
+ /*if ( ! GST_IS_ELEMENT(data->playPipeline) )
+ {
+ if (g_strrstr(data->openFileName, EXTENSION_RAW))
+ {
+ ULOG_INFO("cbOpen() - file was raw, assuming audio/x-raw-int, 8kHz, 1 ch, 16-bit");
+ destroyPipelines(data);
+ createPipeline(data, PIPELINE_PLAY);
+ }
+ }*/
+
+ if (! GST_IS_ELEMENT(data->playPipeline))
+ {
+ ULOG_WARN("%s() - playPipeline does not exist", G_STRFUNC);
+ return;
+ }
+
+ gst_element_set_state(GST_ELEMENT(data->playPipeline),
+ GST_STATE_PLAYING);
+
+ setAppState(data, APPSTATE_PLAYING);
+
+ g_timeout_add(PLAY_UPDATE_INTERVAL, (GSourceFunc)cbCheckPosition, data);
+
+ ULOG_DEBUG("%s() - end", G_STRFUNC);
+}
+
+static void cbStop(GtkWidget* widget, AppData *data)
+{
+ g_assert(NULL != data);
+
+ ULOG_DEBUG("%s() - begin", G_STRFUNC);
+
+ /* check if we are playing/recording */
+
+ /*
+ if (APPSTATE_PLAYING != getAppState(data) &&
+ APPSTATE_RECORDING != getAppState(data))
+ {
+ ULOG_WARN("cbStop() - state different than PLAYING or RECORDING "
+ "-> return");
+ return;
+ }
+ */
+
+ /* stop playing or recording */
+ gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
+ gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
+ gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
+
+ /* destroy related pipeline */
+ switch(getAppState(data))
+ {
+ case APPSTATE_PLAYING:
+ /* don't destroy the playing pipeline. Instead, set the pipeline to PAUSED */
+ /* destroyPipeline(data, PIPELINE_PLAY); */
+ ULOG_INFO("%s() - Setting playPipeline state to PAUSED", G_STRFUNC);
+ gst_element_set_state(GST_ELEMENT(data->playPipeline),
+ GST_STATE_PAUSED);
+ /* flow through */
+ case APPSTATE_READY:
+ /* seek to zero, but not for PCM pipeline */
+ /* if (data->playPipelineType == PIPELINE_PLAY || seekToZero(data, GST_ELEMENT(data->playPipeline))) */
+ if ( !GST_IS_ELEMENT(data->playPipeline) || seekToZero(data, GST_ELEMENT(data->playPipeline)))
+ {
+ gtk_adjustment_set_value(
+ GTK_ADJUSTMENT(data->mainViewData.adjustment), 0);
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry),
+ RECORDER_MSG_STOPPED);
+ }
+ break;
+ case APPSTATE_RECORDING:
+ {
+ gdouble len = -1.0;
+ gst_element_set_state(GST_ELEMENT(data->recPipeline),
+ GST_STATE_PAUSED);
+ destroyPipeline(data, PIPELINE_REC);
+ gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
+ data->saved = FALSE;
+
+ len = guessMediaLength(data);
+ if (len > 0.0)
+ setLength(data, len);
+
+ break;
+ }
+
+ default:
+ /* seekToZero(data, GST_ELEMENT(data->playPipeline)); */
+ /* should not come here */
+ break;
+ }
+
+ setAppState(data, APPSTATE_READY);
+
+ ULOG_DEBUG("%s() - end", G_STRFUNC);
+}
+
+
+/* ui construction functions */
+
+static GtkWidget* createToolBar(AppData *data)
+{
+ GtkToolbar* toolBar = NULL;
+
+ GtkToolItem* new = NULL;
+ GtkToolItem* open = NULL;
+ GtkToolItem* save = NULL;
+ GtkToolItem* saveas = NULL;
+ GtkToolItem* sep = NULL;
+ GtkToolItem* play = NULL;
+ GtkToolItem* rec = NULL;
+ GtkToolItem* stop = NULL;
+
+ /* create buttons */
+ new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
+ open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
+ save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
+ data->buttonSave = GTK_WIDGET(save);
+ saveas = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE_AS);
+ data->buttonSaveAs = GTK_WIDGET(saveas);
+ gtk_widget_set_sensitive(data->buttonSave, FALSE);
+ gtk_widget_set_sensitive(data->buttonSaveAs, FALSE);
+ data->saved = TRUE;
+
+ rec = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_RECORD);
+ data->buttonRec = GTK_WIDGET(rec);
+ play = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
+ data->buttonPlay = GTK_WIDGET(play);
+ stop = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
+
+ /* create separator */
+ sep = gtk_separator_tool_item_new();
+
+ /* create the toolbar itself */
+ toolBar = GTK_TOOLBAR(gtk_toolbar_new());
+
+ /* add items to toolbar */
+ gtk_toolbar_insert(toolBar, new, -1);
+ gtk_toolbar_insert(toolBar, open, -1);
+ /*
+ gtk_toolbar_insert(toolBar, save, -1);
+ */
+ gtk_toolbar_insert(toolBar, saveas, -1);
+ gtk_toolbar_insert(toolBar, sep, -1);
+ gtk_toolbar_insert(toolBar, rec, -1);
+ gtk_toolbar_insert(toolBar, play, -1);
+ gtk_toolbar_insert(toolBar, stop, -1);
+
+ /* connect signals */
+ g_signal_connect(G_OBJECT(new), "clicked",
+ G_CALLBACK(cbNew),
+ data);
+ g_signal_connect(G_OBJECT(open), "clicked",
+ G_CALLBACK(cbOpen),
+ data);
+ /*
+ g_signal_connect(G_OBJECT(save), "clicked",
+ G_CALLBACK(cbSave),
+ data);
+ */
+ g_signal_connect(G_OBJECT(saveas), "clicked",
+ G_CALLBACK(cbSaveAs),
+ data);
+ g_signal_connect(G_OBJECT(rec), "clicked",
+ G_CALLBACK(cbRec),
+ data);
+ g_signal_connect(G_OBJECT(play), "clicked",
+ G_CALLBACK(cbPlay),
+ data);
+ g_signal_connect(G_OBJECT(stop), "clicked",
+ G_CALLBACK(cbStop),
+ data);
+
+ return GTK_WIDGET(toolBar);
+
+}
+
+static void cbItemGroupChanged( gpointer data )
+{
+ AppData* app = (AppData* ) data;
+ GValue active ={G_TYPE_INVALID};
+ gint pcma,ilbc,pcm;
+
+ g_value_init(&active, G_TYPE_INT);
+
+ g_object_get_property(G_OBJECT(app->radio_pcma), "active", &active);
+ pcma = g_value_get_int(&active);
+ g_object_get_property(G_OBJECT(app->radio_ilbc), "active", &active);
+ ilbc = g_value_get_int(&active);
+ g_object_get_property(G_OBJECT(app->radio_pcm), "active", &active);
+ pcm = g_value_get_int(&active);
+
+ ULOG_INFO("change type pcma=%d ilbc=%d pcm=%d",pcma, ilbc, pcm);
+ if ( pcma == 1 )
+ app->filter = FORMAT_PCMA;
+ else if ( ilbc == 1 )
+ app->filter = FORMAT_ILBC;
+ else if ( pcm == 1 )
+ app->filter = FORMAT_WAV;
+ else
+ app->filter = -1;
+
+ ULOG_INFO("filter type=%d", app->filter);
+}
+
+static void cbItemClose(GtkWidget *widget, gpointer data)
+{
+ g_assert(data);
+
+ if (!closeFile(data))
+ return;
+
+ gtk_main_quit();
+}
+
+/* Create the menu items needed for the main view */
+static void createMenu( AppData *data )
+{
+ /* Create needed variables */
+ GSList *group = NULL;
+ GtkMenu *main_menu;
+ GtkWidget *menu_file;
+ GtkWidget *menu_others;
+ GtkWidget *item_file;
+ GtkWidget *item_file_open;
+ GtkWidget *item_file_save_as;
+ GtkWidget *item_others;
+ GtkWidget *item_pcma;
+ GtkWidget *item_pcmu;
+ GtkWidget *item_ilbc;
+ GtkWidget *item_pcm;
+
+ /*
+ GtkWidget *item_radio_type1;
+ */
+ GtkWidget *item_close;
+ GtkWidget *item_separator;
+
+ /* Get the menu from view */
+ main_menu = GTK_MENU(gtk_menu_new());
+ hildon_window_set_menu(data->mainView, main_menu);
+
+ /* Create new submenu for "Others" */
+ menu_file = gtk_menu_new ();
+ menu_others = gtk_menu_new ();
+
+ /* Create menu items */
+ item_file = gtk_menu_item_new_with_label (_("File"));
+ item_file_open = gtk_menu_item_new_with_label(_("Open..."));
+ item_file_save_as = gtk_menu_item_new_with_label(_("Save as..."));
+ item_others = gtk_menu_item_new_with_label (_("Recording format"));
+
+ item_pcma = gtk_radio_menu_item_new_with_label(
+ group, "PCM A-law");
+ item_ilbc = gtk_radio_menu_item_new_with_label_from_widget(
+ GTK_RADIO_MENU_ITEM(item_pcma), "iLBC");
+ item_pcmu = gtk_radio_menu_item_new_with_label_from_widget(
+ GTK_RADIO_MENU_ITEM(item_pcma), "PCM u-law");
+ item_pcm = gtk_radio_menu_item_new_with_label_from_widget(
+ GTK_RADIO_MENU_ITEM(item_pcma), "WAV");
+
+ /* default is iLBC */
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_ilbc), TRUE);
+ data->filter = FORMAT_ILBC;
+
+ data->radio_pcma = item_pcma;
+ data->radio_ilbc = item_ilbc;
+ data->radio_pcm = item_pcm;
+ /*
+ data->radiotype = item_radio_type1;
+ */
+ item_close = gtk_menu_item_new_with_label(_("Close"));
+ item_separator = gtk_separator_menu_item_new();
+
+ /* Add menu items to right menus */
+ gtk_menu_append( main_menu, item_file );
+ gtk_menu_append( menu_file, item_file_open );
+ gtk_menu_append( menu_file, item_file_save_as );
+ gtk_menu_append( main_menu, item_others );
+ gtk_menu_append( menu_others, item_pcm );
+ gtk_menu_append( menu_others, item_pcma );
+ gtk_menu_append( menu_others, item_ilbc);
+
+ gtk_menu_append( main_menu, item_close );
+
+ /* Add others submenu to the "Others" item */
+ gtk_menu_item_set_submenu(
+ GTK_MENU_ITEM(item_file), menu_file );
+ gtk_menu_item_set_submenu(
+ GTK_MENU_ITEM(item_others), menu_others );
+
+ /* Attach the callback functions to the activate signal */
+ g_signal_connect( G_OBJECT( item_file_open), "activate",
+ GTK_SIGNAL_FUNC (cbOpen), data);
+ g_signal_connect( G_OBJECT( item_file_save_as), "activate",
+ GTK_SIGNAL_FUNC (cbSaveAs), data);
+ g_signal_connect( G_OBJECT( item_close ), "activate",
+ GTK_SIGNAL_FUNC (cbItemClose), data);
+
+ g_signal_connect_swapped(G_OBJECT(item_pcma), "activate", G_CALLBACK(cbItemGroupChanged), data);
+ g_signal_connect_swapped(G_OBJECT(item_pcm), "activate", G_CALLBACK(cbItemGroupChanged), data);
+
+ /* Make all menu widgets visible */
+
+ gtk_widget_show_all( GTK_WIDGET( main_menu ) );
+}
+
+gboolean maemo_recorder_ui_new(AppData *data)
+{
+ HildonProgram *app = NULL;
+ HildonWindow *window = NULL;
+ GtkWidget *hbox = NULL;
+ GtkWidget *vbox = NULL;
+ GtkWidget *label = NULL;
+ GtkWidget *entry1 = NULL;
+ GtkWidget *entry2 = NULL;
+ GtkWidget *entry3 = NULL;
+ GtkWidget *toolBar = NULL;
+ GtkWidget *table = NULL;
+ GtkWidget *scale = NULL;
+ GtkObject *adjustment = NULL;
+
+ g_assert(NULL != data);
+
+ app = HILDON_PROGRAM(hildon_program_get_instance());
+ g_set_application_name(RECORDER_APP_TITLE);
+
+ /* main window */
+ window = HILDON_WINDOW(hildon_window_new());
+
+ hildon_program_add_window(app, window);
+
+ /* content for main view */
+
+ /* create vbox, divides control area and view area */
+ vbox = gtk_vbox_new(FALSE, 0);
+
+ /* create hbox to divide control area */
+ hbox = gtk_hbox_new(FALSE, HILDON_MARGIN_DEFAULT);
+
+ /* create toolbar */
+ toolBar = createToolBar(data);
+
+ /* create table for labels */
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_homogeneous(GTK_TABLE(table), FALSE);
+
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 0);
+
+ label = gtk_label_new_with_mnemonic(_("Filename:"));
+ gtk_table_attach_defaults (GTK_TABLE (table),
+ label,
+ 0, 1, 0, 1);
+
+ entry1 = gtk_entry_new ();
+ gtk_entry_set_has_frame(GTK_ENTRY(entry1), FALSE);
+ gtk_entry_set_text (GTK_ENTRY (entry1), _(RECORDER_FILE_UNTITLED));
+ gtk_table_attach_defaults (GTK_TABLE (table), entry1, 1, 3, 0, 1);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry1);
+
+ label = gtk_label_new_with_mnemonic (_("Length:"));
+ gtk_table_attach_defaults (GTK_TABLE (table),
+ label,
+ 0, 1, 1, 2);
+
+ entry2 = gtk_entry_new ();
+ gtk_entry_set_has_frame(GTK_ENTRY(entry2), FALSE);
+ gtk_entry_set_text (GTK_ENTRY (entry2), "0:00.00");
+ gtk_table_attach_defaults (GTK_TABLE (table), entry2, 1, 3, 1, 2);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry2);
+
+ label = gtk_label_new_with_mnemonic (_("State:"));
+ gtk_table_attach_defaults (GTK_TABLE (table),
+ label,
+ 0, 1, 2, 3);
+
+ entry3 = gtk_entry_new ();
+ gtk_entry_set_has_frame(GTK_ENTRY(entry3), FALSE);
+ gtk_entry_set_text (GTK_ENTRY (entry3), RECORDER_MSG_READY);
+ gtk_table_attach_defaults (GTK_TABLE (table), entry3, 1, 3, 2, 3);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry3);
+
+ adjustment = gtk_adjustment_new (0.00,
+ 0.00,
+ 100.00,
+ 0.01,
+ 0.01,
+ 0);
+
+ scale = gtk_hscale_new(GTK_ADJUSTMENT(adjustment));
+
+/* gtk_table_attach_defaults (GTK_TABLE (table),
+ label,
+ 0, 3, 2, 3);
+*/
+ /* connect signals */
+ g_signal_connect(G_OBJECT(adjustment), "value-changed", G_CALLBACK(cbUserSeek), data);
+ g_signal_connect(G_OBJECT(scale), "format-value", G_CALLBACK(cbFormatSeekbarValue), data);
+ g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(cbDestroy), data);
+
+ /* packing the view */
+ gtk_container_add (GTK_CONTAINER(window), vbox);
+ gtk_box_pack_start (GTK_BOX(vbox), table, FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX(vbox), scale, FALSE, FALSE, 0);
+// gtk_box_pack_start (GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
+
+ hildon_window_add_toolbar(window, GTK_TOOLBAR(toolBar));
+
+ /* initialise the ui */
+ gtk_entry_set_editable(GTK_ENTRY(entry1), FALSE);
+ gtk_entry_set_editable(GTK_ENTRY(entry2), FALSE);
+ gtk_entry_set_editable(GTK_ENTRY(entry3), FALSE);
+
+ /* store needed widgets */
+ data->app = app;
+ data->mainView = window;
+ data->mainViewData.toolBar = GTK_WIDGET(toolBar);
+ data->mainViewData.fileNameEntry = GTK_WIDGET(entry1);
+ data->mainViewData.lengthEntry = GTK_WIDGET(entry2);
+ data->mainViewData.stateEntry = GTK_WIDGET(entry3);
+ data->mainViewData.adjustment = GTK_OBJECT(adjustment);
+
+ /* show the app */
+ gtk_widget_show_all(GTK_WIDGET(window));
+
+ createMenu(data);
+
+ return TRUE;
+}
+
+void
+maemo_recorder_mime_open(gpointer user_data, gint argc, gchar **argv)
+{
+ AppData *data;
+
+ ULOG_DEBUG("%s with %d arguments", __FUNCTION__, argc);
+
+ if (argc == 0)
+ return;
+
+ g_assert(user_data);
+ data = (AppData *) user_data;
+
+ if (argv[0] != NULL)
+ {
+ ULOG_DEBUG("request to open %s", argv[0]);
+ g_free(data->mimeURI);
+ data->mimeURI = g_strdup(argv[0]);
+ g_idle_add(openURI, (gpointer) data);
+ gtk_window_present(GTK_WINDOW(data->mainView));
+ }
+}
+
+static void seekToTime(GstElement *pipeline, gdouble secs)
+{
+ g_assert(NULL != pipeline);
+ ULOG_DEBUG("Seeking to: %.2f", secs);
+
+ /* time must be nanoseconds */
+ if (!gst_element_seek(pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, (gint64) (secs * GST_SECOND),
+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
+ {
+ ULOG_WARN("seekToTime failed!");
+ return;
+ }
+ ULOG_DEBUG("seekToTime succeeded");
+}
+
+static gboolean seekToZero(AppData *data, GstElement *pipeline)
+{
+ g_assert(NULL != pipeline);
+ ULOG_DEBUG("Seeking to zero");
+
+ /* time must be nanoseconds */
+ if (!gst_element_seek(pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, (gint64) 0,
+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
+ {
+ ULOG_ERR("seekToZero failed! Trying to destroy and re-create pipeline");
+ gint plType = data->playPipelineType;
+
+ /* gst_element_set_state(pipeline, GST_STATE_READY); */
+ destroyPipeline(data, plType);
+ return createPipeline(data, plType);
+ }
+
+ ULOG_DEBUG("seekToZero succeeded");
+ return TRUE;
+}
+
+static void setLength(AppData *data, gdouble secs)
+{
+ guint mins = 0;
+ gchar *tmp;
+
+ if (secs < 0.0)
+ return;
+
+ if (secs > 0)
+ {
+ g_object_set (G_OBJECT (data->mainViewData.adjustment),
+ "upper", secs,
+ NULL);
+ gtk_adjustment_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
+ }
+
+ if (secs >= 60.0)
+ {
+ mins = secs / 60;
+ secs -= mins * 60.0;
+ }
+
+ tmp = g_strdup_printf("%u:%05.2f", mins, secs);
+
+ /*
+ ULOG_INFO("Setting length to %s", tmp);
+ */
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.lengthEntry),
+ tmp);
+ g_free(tmp);
+}
+
+static gdouble guessMediaLength(AppData *data)
+{
+ GnomeVFSFileSize size = 0;
+ gdouble bitrate = 0.0;
+ gdouble len = -1.0;
+
+ if (data->openFileName)
+ size = getFileLength(data->openFileName);
+ else
+ return -1.0;
+
+ if (size == 0)
+ return -1.0;
+
+ ULOG_DEBUG("file size: %llu bytes", size);
+
+ switch (data->file_format)
+ {
+ case FORMAT_ILBC:
+ bitrate = ILBC_BITRATE_20;
+ break;
+
+ case FORMAT_PCMA:
+ case FORMAT_PCMU:
+ bitrate = PCMA_BITRATE;
+ break;
+
+ default:
+ return -1.0;
+ }
+ if (bitrate == 0.0)
+ return -1.0;
+
+ len = (((gdouble) size) * 8.0) / (bitrate);
+ ULOG_DEBUG("guessed media length: %.2f secs", len);
+
+ return len;
+}
+
+static GstCaps *createCapsFilter(AudioFormat format)
+{
+ switch (format)
+ {
+ case FORMAT_ILBC:
+ return gst_caps_new_simple(
+ GST_TYPE_ILBC,
+ "rate", G_TYPE_INT, ILBC_RATE,
+ "channels", G_TYPE_INT, DEFAULT_CHANNELS,
+ "mode", G_TYPE_INT, 20, /* 20 ms frames */
+ NULL);
+ case FORMAT_PCMA:
+ return gst_caps_new_simple(
+ GST_TYPE_PCMA,
+ "rate", G_TYPE_INT, DEFAULT_RATE,
+ "channels", G_TYPE_INT, DEFAULT_CHANNELS,
+ NULL);
+ case FORMAT_PCMU:
+ return gst_caps_new_simple(
+ GST_TYPE_PCMU,
+ "rate", G_TYPE_INT, DEFAULT_RATE,
+ "channels", G_TYPE_INT, DEFAULT_CHANNELS,
+ NULL);
+ case FORMAT_WAV:
+ case FORMAT_PCM:
+ return gst_caps_new_simple(
+ GST_TYPE_PCM,
+ "rate", G_TYPE_INT, PCM_RATE,
+ "signed", G_TYPE_BOOLEAN, TRUE,
+ "channels", G_TYPE_INT, DEFAULT_CHANNELS,
+ "endianness", G_TYPE_INT, PCM_ENDIANNESS,
+ "width", G_TYPE_INT, PCM_WIDTH,
+ "depth", G_TYPE_INT, PCM_DEPTH,
+ NULL);
+ default:
+ ULOG_WARN("%s(): creating ANY caps", G_STRFUNC);
+ return gst_caps_new_any();
+ }
+}
+
+static gboolean cbStopPlayback(AppData *data)
+{
+ gint ret;
+ ULOG_INFO("Stopping playback");
+
+ g_assert(data != NULL);
+
+ ret = gst_element_set_state(GST_ELEMENT(data->playPipeline),
+ GST_STATE_PAUSED);
+ if (seekToZero(data, GST_ELEMENT(data->playPipeline)))
+ {
+ gtk_adjustment_set_value(
+ GTK_ADJUSTMENT(data->mainViewData.adjustment), 0);
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
+ }
+ setAppState(data, APPSTATE_READY);
+ gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
+ gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
+
+ return FALSE;
+}
+
+static void cbUserSeek(GtkAdjustment *adjustment, gpointer data)
+{
+ /*ULOG_INFO("cbUserSeek");*/
+ AppData *app;
+
+ g_return_if_fail(data != NULL);
+ app = (AppData *) data;
+
+ if (getAppState(app) != APPSTATE_READY || NULL == app->playPipeline)
+ return;
+
+ seekToTime(app->playPipeline, gtk_adjustment_get_value(adjustment));
+}
+
+static gchar *cbFormatSeekbarValue(GtkScale *scale, gdouble value)
+{
+/* ULOG_INFO("cbFormatSeekbarValue");*/
+ gint mins = 0;
+ gint digits = gtk_scale_get_digits(scale);
+
+ if (value >= 60.0)
+ {
+ mins = value / 60;
+ value -= mins * 60.0;
+ return g_strdup_printf("%d:%0*.*f", mins, digits + 3, digits, value);
+ }
+ /* mins:sec.frac */
+ return g_strdup_printf("%0.*f", digits, value);
+}
+
+static gboolean cbUpdateRecLength(AppData *data)
+{
+ struct timeval tv;
+ guint mins = 0;
+ gdouble secs;
+ gchar *tmp;
+
+ if (gettimeofday(&tv, NULL) != 0)
+ return FALSE;
+
+ secs = tv.tv_sec - data->recStartTv.tv_sec;
+ secs += ((tv.tv_usec - data->recStartTv.tv_usec) / 1000000.0);
+
+ if (secs >= 60.0)
+ {
+ mins = secs / 60;
+ secs -= mins * 60.0;
+ tmp = g_strdup_printf("%u:%05.2f", mins, secs);
+ }
+ else
+ tmp = g_strdup_printf("%0.2f", secs);
+
+ gtk_entry_set_text(GTK_ENTRY(data->mainViewData.lengthEntry),
+ tmp);
+ g_free(tmp);
+
+ if (getAppState(data) == APPSTATE_RECORDING)
+ return TRUE;
+
+ data->recUpdateId = 0;
+ return FALSE;
+}
+