commit 14e2cea74cac8d969b1655c33dfbda535ff1a991
parent a5b3451b622cc0eeef4470b9c0cd5a170512956f
Author: Alexander Kojevnikov <alexander@kojevnikov.com>
Date: Sun, 26 Aug 2012 19:55:20 -0700
Merge branch 'wx'
Diffstat:
49 files changed, 2715 insertions(+), 2122 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -32,15 +32,10 @@ po/.intltool-merge-cache
po/POTFILES
po/stamp-it
samples/
-src/*.c
src/*.o
src/spek
-!src/spek-audio.c
-!src/spek-fft.c
-!src/spek-platform.c
src/*.stamp
stamp-h1
-.svn
test*
web/version
xmldocs.make
diff --git a/Makefile.am b/Makefile.am
@@ -1,11 +1,8 @@
-# Makefile.am
-
SUBDIRS = \
data \
man \
po \
- src \
- vapi
+ src
EXTRA_DIST = \
intltool-extract.in \
diff --git a/autogen.sh b/autogen.sh
@@ -5,6 +5,7 @@ test -n "$srcdir" || srcdir=$(dirname "$0")
test -n "$srcdir" || srcdir=.
(
cd "$srcdir" &&
- AUTOPOINT='intltoolize --automake --copy' autoreconf -fiv -Wall
+ touch config.rpath &&
+ AUTOPOINT='intltoolize --automake --copy' autoreconf -fiv
) || exit
-test -n "$NOCONFIGURE" || "$srcdir/configure" --enable-maintainer-mode "$@"
-\ No newline at end of file
+test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
+\ No newline at end of file
diff --git a/configure.ac b/configure.ac
@@ -1,48 +1,56 @@
-# configure.ac
-
-AC_INIT([spek],[0.7])
-AC_CONFIG_SRCDIR([src/spek.vala])
+AC_INIT([spek],[0.8.0])
+AC_CONFIG_SRCDIR([src/spek.cc])
AC_CONFIG_HEADERS([config.h])
-AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-xz])
-
-# Enable silent rules is available
-m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
-
-AM_MAINTAINER_MODE
+AM_INIT_AUTOMAKE([1.11.1 foreign no-dist-gzip dist-xz])
+AM_SILENT_RULES([yes])
-AC_PROG_CC
-AC_PROG_CC_STDC
-AM_PROG_VALAC([0.12.0])
+AC_PROG_CC_C99
+AC_PROG_CXX
AC_PROG_INSTALL
-IT_PROG_INTLTOOL([0.35])
+IT_PROG_INTLTOOL([0.40.0])
-pkg_modules="gtk+-2.0 >= 2.18.0 libavformat >= 52.111 libavcodec >= 52.123 libavutil"
-PKG_CHECK_MODULES(SPEK, [$pkg_modules])
-AC_SUBST(SPEK_CFLAGS)
-AC_SUBST(SPEK_LIBS)
-
-SPEK_PACKAGES="--pkg gtk+-2.0 --pkg gio-2.0"
-AC_SUBST(SPEK_PACKAGES)
+AC_CANONICAL_HOST
+AC_MSG_CHECKING([the OS])
+AS_CASE([$host],
+ [*-*-mingw*], [
+ os="WIN"
+ AC_DEFINE([OS_WIN], [1], [Windows])
+ ],
+ [*-*-darwin*], [
+ os="OSX"
+ AC_DEFINE([OS_OSX], [1], [OS X])
+ ],
+ [*], [
+ os="UNIX"
+ AC_DEFINE([OS_UNIX], [1], [Unix])
+ ]
+)
+AC_MSG_RESULT([$os])
AC_CHECK_LIB(m, log10)
-AC_CHECK_LIB(gthread-2.0, g_thread_init)
-# Check for GDK Quartz and MacOSX integration package
-_gdk_tgt=`$PKG_CONFIG --variable=target gdk-2.0`
-AM_CONDITIONAL([GDK_TARGET_QUARTZ], [test x$_gdk_tgt = xquartz])
-if test "x$_gdk_tgt" = xquartz; then
- PKG_CHECK_MODULES(IGE_MAC, ige-mac-integration)
- IGE_MAC_LIBS="$IGE_MAC_LIBS -framework CoreFoundation -framework ApplicationServices"
- AC_SUBST(IGE_MAC_LIBS)
- AC_SUBST(IGE_MAC_CFLAGS)
- AC_DEFINE(G_OS_DARWIN, 1, [Platform detection macro missing in GLib])
+PKG_CHECK_MODULES(FFMPEG, [libavformat >= 52.111 libavcodec >= 52.123 libavutil])
+
+AM_OPTIONS_WXCONFIG
+reqwx=2.8.0
+AM_PATH_WXCONFIG($reqwx, wx=1)
+if test "$wx" != 1; then
+ AC_MSG_ERROR([
+ wxWidgets must be installed on your system.
+
+ Please check that wx-config is in path, the directory
+ where wxWidgets libraries are installed (returned by
+ 'wx-config --libs' or 'wx-config --static --libs' command)
+ is in LD_LIBRARY_PATH or equivalent variable and
+ wxWidgets version is $reqwx or above.
+ ])
fi
GETTEXT_PACKAGE=spek
AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [Gettext Package])
AC_SUBST(GETTEXT_PACKAGE)
-AM_GNU_GETTEXT_VERSION([1.11])
-AM_GLIB_GNU_GETTEXT
+AM_GNU_GETTEXT_VERSION([0.18.1])
+AM_GNU_GETTEXT([external])
AC_CONFIG_FILES([
Makefile
@@ -61,7 +69,6 @@ AC_CONFIG_FILES([
man/spek.1
po/Makefile.in
src/Makefile
- vapi/Makefile
web/version
])
-AC_OUTPUT
-\ No newline at end of file
+AC_OUTPUT
diff --git a/dist/osx/README b/dist/osx/README
@@ -1,21 +0,0 @@
-Building the OS X bundle
-========================
-
-Install and configure [jhbuild][1] and [ige-mac-bundler][2].
-
-Get the release tarball and unpack it somewhere. Also copy the tarball to ~/gtk/source/pkgs/.
-
-Add this line to ~/.jhbuildrc-custom:
-
- moduleset=os.environ['HOME'] + 'path/to/spek/dist/osx/spek.modules'
-
-Run:
-
- $ jhbuild build spek
- $ jhbuild shell
- $ dist/osx/bundle.sh
-
-Note: On Lion, built in libiconv conflicts with the bundled version. To work around, accompany all jhbuild commands with `--skip=libiconv` so that everything uses the system version of the library (see issue 51).
-
-[1]: http://sourceforge.net/apps/trac/gtk-osx/wiki/Build
-[2]: http://sourceforge.net/apps/trac/gtk-osx/wiki/Bundle
diff --git a/dist/osx/README.md b/dist/osx/README.md
@@ -0,0 +1,34 @@
+# Building the OS X bundle
+
+Using MacPorts install build dependencies:
+
+ port install git-core autoconf automake intltool yasm.
+
+Download and build wxWidgets, example configure flags:
+
+ ./configure --prefix=$HOME/usr --disable-shared --with-osx_cocoa \
+ --with-jpeg=builtin --with-png=builtin --with-regex=builtin \
+ --with-tiff=builtin --with-zlib=builtin --with-expat=builtin
+ make && make install
+
+Copy the wxWidgets m4 macro to the MacPorts tree:
+
+ sudo cp $HOME/usr/share/aclocal/wxwin.m4 /opt/local/share/aclocal/
+
+Download and build FFmpeg, example configure flags:
+
+ ./configure --prefix=$HOME/usr --disable-shared --disable-debug --disable-doc \
+ --enable-gpl --enable-version3 --disable-nonfree --disable-ffmpeg --disable-ffplay \
+ --disable-ffprobe --disable-ffserver --disable-avdevice --disable-swscale \
+ --disable-postproc --enable-pthreads --disable-encoders --disable-muxers \
+ --disable-devices --disable-filters --disable-swresample
+ make && make install
+
+Clone and build Spek, example configure flags:
+
+ PKG_CONFIG_PATH=$HOME/usr/lib/pkgconfig ./autogen.sh --with-wx-config=$HOME/usr/bin/wx-config
+
+Bundle Spek:
+
+ ./dist/osx/bundle.sh
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
@@ -1,10 +1,7 @@
[encoding: UTF-8]
data/spek.desktop.in.in
-src/spek-audio.c
-src/spek-pipeline.vala
-src/spek-preferences-dialog.vala
-src/spek-preferences.vala
-src/spek-ruler.vala
-src/spek-spectrogram.vala
-src/spek-window.vala
-src/spek.vala
+src/spek-audio-desc.cc
+src/spek-preferences-dialog.cc
+src/spek-spectrogram.cc
+src/spek-window.cc
+src/spek.cc
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
@@ -1,7 +1 @@
data/spek.desktop.in
-src/spek.c
-src/spek-pipeline.c
-src/spek-preferences-dialog.c
-src/spek-preferences.c
-src/spek-spectrogram.c
-src/spek-window.c
diff --git a/po/spek.pot b/po/spek.pot
@@ -8,15 +8,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-08-26 19:30-0700\n"
+"POT-Creation-Date: 2012-08-26 19:50-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: ../data/spek.desktop.in.in.h:1
msgid "Spectrum Analyser"
@@ -34,194 +33,161 @@ msgstr ""
msgid "View spectrograms of your audio files"
msgstr ""
-#: ../src/spek-audio.c:47
+#: ../src/spek-audio-desc.cc:34
+#, c-format
+msgid "%d kbps"
+msgstr ""
+
+#: ../src/spek-audio-desc.cc:37
+#, c-format
+msgid "%d Hz"
+msgstr ""
+
+#: ../src/spek-audio-desc.cc:65
msgid "Cannot open input file"
msgstr ""
-#: ../src/spek-audio.c:54
+#: ../src/spek-audio-desc.cc:68
msgid "Cannot find stream info"
msgstr ""
-#: ../src/spek-audio.c:66
+#: ../src/spek-audio-desc.cc:71
msgid "The file contains no audio streams"
msgstr ""
-#: ../src/spek-audio.c:73
+#: ../src/spek-audio-desc.cc:74
msgid "Cannot find decoder"
msgstr ""
-#: ../src/spek-audio.c:91
+#: ../src/spek-audio-desc.cc:77
msgid "Unknown duration"
msgstr ""
-#: ../src/spek-audio.c:95
+#: ../src/spek-audio-desc.cc:80
msgid "No audio channels"
msgstr ""
-#: ../src/spek-audio.c:99
+#: ../src/spek-audio-desc.cc:83
msgid "Cannot open decoder"
msgstr ""
-#: ../src/spek-audio.c:120
+#: ../src/spek-audio-desc.cc:86
msgid "Unsupported sample format"
msgstr ""
-#: ../src/spek-pipeline.vala:70
-#, c-format
-msgid "%d kbps"
-msgstr ""
-
-#: ../src/spek-pipeline.vala:73
-#, c-format
-msgid "%d Hz"
-msgstr ""
-
-#: ../src/spek-pipeline.vala:78
-#, c-format
-msgid "%d bit"
-msgid_plural "%d bits"
-msgstr[0] ""
-msgstr[1] ""
-
-#: ../src/spek-pipeline.vala:81
-#, c-format
-msgid "%d channel"
-msgid_plural "%d channels"
-msgstr[0] ""
-msgstr[1] ""
-
#. TRANSLATORS: first %s is the error message, second %s is stream description.
-#: ../src/spek-pipeline.vala:88
+#: ../src/spek-audio-desc.cc:91
#, c-format
msgid "%s: %s"
msgstr ""
-#: ../src/spek-preferences-dialog.vala:47
+#: ../src/spek-preferences-dialog.cc:62
msgid "Preferences"
msgstr ""
-#: ../src/spek-preferences-dialog.vala:51
+#: ../src/spek-preferences-dialog.cc:67
msgid "(system default)"
msgstr ""
#. TRANSLATORS: The name of a section in the Preferences dialog.
-#: ../src/spek-preferences-dialog.vala:59
+#: ../src/spek-preferences-dialog.cc:74
msgid "General"
msgstr ""
-#: ../src/spek-preferences-dialog.vala:69
-msgid "_Language:"
-msgstr ""
-
-#: ../src/spek-preferences-dialog.vala:86
-msgid "Check for _updates"
+#: ../src/spek-preferences-dialog.cc:82
+msgid "Language:"
msgstr ""
-#. TRANSLATORS: keep "00" unchanged, it's used to calc the text width
-#: ../src/spek-spectrogram.vala:200
-msgid "00 kHz"
+#: ../src/spek-preferences-dialog.cc:97
+msgid "Check for &updates"
msgstr ""
-#: ../src/spek-spectrogram.vala:206
+#: ../src/spek-spectrogram.cc:152
#, c-format
msgid "%d kHz"
msgstr ""
-#. TRANSLATORS: keep "-00" unchanged, it's used to calc the text width
-#: ../src/spek-spectrogram.vala:257
-msgid "-00 dB"
-msgstr ""
-
-#: ../src/spek-spectrogram.vala:263
+#: ../src/spek-spectrogram.cc:157
#, c-format
msgid "%d dB"
msgstr ""
-#: ../src/spek-window.vala:35
-msgid "_File"
-msgstr ""
-
-#: ../src/spek-window.vala:39
-msgid "_Edit"
+#. TRANSLATORS: keep "00" unchanged, it's used to calc the text width
+#: ../src/spek-spectrogram.cc:259
+msgid "00 kHz"
msgstr ""
-#: ../src/spek-window.vala:41
-msgid "_Help"
+#. TRANSLATORS: keep "-00" unchanged, it's used to calc the text width
+#: ../src/spek-spectrogram.cc:287
+msgid "-00 dB"
msgstr ""
-#: ../src/spek-window.vala:76
+#: ../src/spek-window.cc:70
msgid "Spek - Acoustic Spectrum Analyser"
msgstr ""
-#: ../src/spek-window.vala:103
-msgid ""
-"A new version of Spek is available on <a href=\"http://www.spek-project.org"
-"\">www.spek-project.org</a>"
+#: ../src/spek-window.cc:84
+msgid "&File"
msgstr ""
-#: ../src/spek-window.vala:117
-msgid "All files"
+#: ../src/spek-window.cc:91
+msgid "&Edit"
msgstr ""
-#: ../src/spek-window.vala:120
-msgid "PNG images"
+#: ../src/spek-window.cc:98
+msgid "&Help"
msgstr ""
-#: ../src/spek-window.vala:123
-msgid "Audio files"
+#: ../src/spek-window.cc:125
+msgid "A new version of Spek is available, click to download."
msgstr ""
#. TRANSLATORS: window title, %s is replaced with the file name
-#: ../src/spek-window.vala:175
+#: ../src/spek-window.cc:164
#, c-format
msgid "Spek - %s"
msgstr ""
-#: ../src/spek-window.vala:180
-msgid "Open File"
+#: ../src/spek-window.cc:211
+msgid "All files"
msgstr ""
-#: ../src/spek-window.vala:197
-msgid "Save Spectrogram"
+#: ../src/spek-window.cc:213
+msgid "Audio files"
msgstr ""
-#. Suggested name is <file_name>.png
-#: ../src/spek-window.vala:204
-msgid "Untitled"
+#: ../src/spek-window.cc:227
+msgid "Open File"
msgstr ""
-#: ../src/spek-window.vala:259
-msgid "Copyright © 2010-2011 Alexander Kojevnikov"
+#: ../src/spek-window.cc:249
+msgid "PNG images"
msgstr ""
-#: ../src/spek-window.vala:264
-msgid "Spek Website"
+#: ../src/spek-window.cc:255
+msgid "Save Spectrogram"
msgstr ""
-#. TRANSLATORS: Add your name here
-#: ../src/spek-window.vala:274
-msgid "translator-credits"
+#. Suggested name is <file_name>.png
+#: ../src/spek-window.cc:263
+msgid "Untitled"
msgstr ""
-#: ../src/spek.vala:25
-msgid "Display the version and exit"
+#. TRANSLATORS: Add your name here
+#: ../src/spek-window.cc:300
+msgid "translator-credits"
msgstr ""
-#: ../src/spek.vala:42
-msgid "[FILE]"
+#: ../src/spek-window.cc:306
+msgid "Copyright (c) 2010-2012 Alexander Kojevnikov and contributors"
msgstr ""
-#: ../src/spek.vala:46
-#, c-format
-msgid "Run `%s --help` to see a full list of available command line options.\n"
+#: ../src/spek-window.cc:309
+msgid "Spek Website"
msgstr ""
#. TRANSLATORS: first %s is the package name, second %s is the package version.
-#: ../src/spek.vala:52
+#: ../src/spek.cc:90
#, c-format
-msgid "%s version %s\n"
-msgstr ""
-
-#: ../src/spek.vala:57
-msgid "Specify a single file\n"
+msgid "%s version %s"
msgstr ""
diff --git a/src/Makefile.am b/src/Makefile.am
@@ -1,39 +1,47 @@
bin_PROGRAMS = spek
spek_SOURCES = \
- spek.vala \
+ spek-audio-desc.cc \
+ spek-audio-desc.hh \
spek-audio.c \
+ spek-audio.h \
+ spek-events.cc \
+ spek-events.hh \
spek-fft.c \
- spek-pipeline.vala \
- spek-platform.c \
- spek-preferences.vala \
- spek-preferences-dialog.vala \
- spek-ruler.vala \
- spek-spectrogram.vala \
- spek-window.vala
+ spek-fft.h \
+ spek-palette.c \
+ spek-palette.h \
+ spek-pipeline.c \
+ spek-pipeline.h \
+ spek-platform.cc \
+ spek-platform.hh \
+ spek-preferences-dialog.cc \
+ spek-preferences-dialog.hh \
+ spek-preferences.cc \
+ spek-preferences.hh \
+ spek-ruler.cc \
+ spek-ruler.hh \
+ spek-spectrogram.cc \
+ spek-spectrogram.hh \
+ spek-window.cc \
+ spek-window.hh \
+ spek.cc
-AM_CPPFLAGS = \
+spek_CPPFLAGS = \
-include config.h \
- $(SPEK_CFLAGS) \
- $(IGE_MAC_CFLAGS) \
- -DLOCALEDIR=\""$(localedir)"\" \
- -DPKGDATADIR=\""$(pkgdatadir)"\" \
- -DPKGLIBDIR=\""$(pkglibdir)"\"
+ -pthread \
+ $(WX_CPPFLAGS)
+
+spek_CFLAGS = \
+ $(FFMPEG_CFLAGS) \
+ $(WX_CFLAGS_ONLY)
-VALAFLAGS = \
- --thread \
- --vapidir=$(srcdir)/../vapi \
- --pkg config \
- --pkg spek-audio \
- --pkg spek-fft \
- --pkg spek-platform \
- @SPEK_PACKAGES@
+spek_CXXFLAGS = \
+ $(WX_CXXFLAGS_ONLY)
spek_LDADD = \
- $(SPEK_LIBS) \
- $(IGE_MAC_LIBS)
+ $(FFMPEG_LIBS) \
+ $(WX_LIBS)
-EXTRA_DIST = \
- spek-audio.h \
- spek-fft.h \
- spek-platform.h
+spek_LDFLAGS = \
+ -pthread
diff --git a/src/spek-audio-desc.cc b/src/spek-audio-desc.cc
@@ -0,0 +1,95 @@
+/* spek-audio-desc.cc
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wx/arrstr.h>
+#include <wx/intl.h>
+
+#include "spek-audio.h"
+
+#include "spek-audio-desc.hh"
+
+wxString spek_audio_desc(const struct spek_audio_properties *properties)
+{
+ wxArrayString items;
+
+ if (properties->codec_name) {
+ items.Add(wxString::FromUTF8(properties->codec_name));
+ }
+ if (properties->bit_rate) {
+ items.Add(wxString::Format(_("%d kbps"), (properties->bit_rate + 500) / 1000));
+ }
+ if (properties->sample_rate) {
+ items.Add(wxString::Format(_("%d Hz"), properties->sample_rate));
+ }
+ // Include bits per sample only if there is no bitrate.
+ if (properties->bits_per_sample && !properties->bit_rate) {
+ items.Add(wxString::Format(
+ wxPLURAL("%d bit", "%d bits", properties->bits_per_sample),
+ properties->bits_per_sample
+ ));
+ }
+ if (properties->channels) {
+ items.Add(wxString::Format(
+ wxPLURAL("%d channel", "%d channels", properties->channels),
+ properties->channels
+ ));
+ }
+
+ wxString desc;
+ for (int i = 0; i < items.GetCount(); ++i) {
+ if (i) {
+ desc += wxT(", ");
+ }
+ desc += items[i];
+ }
+
+ if (properties->error) {
+ wxString error;
+ switch (properties->error) {
+ case SPEK_AUDIO_CANNOT_OPEN_FILE:
+ error = _("Cannot open input file");
+ break;
+ case SPEK_AUDIO_NO_STREAMS:
+ error = _("Cannot find stream info");
+ break;
+ case SPEK_AUDIO_NO_AUDIO:
+ error = _("The file contains no audio streams");
+ break;
+ case SPEK_AUDIO_NO_DECODER:
+ error = _("Cannot find decoder");
+ break;
+ case SPEK_AUDIO_NO_DURATION:
+ error = _("Unknown duration");
+ break;
+ case SPEK_AUDIO_NO_CHANNELS:
+ error = _("No audio channels");
+ break;
+ case SPEK_AUDIO_CANNOT_OPEN_DECODER:
+ error = _("Cannot open decoder");
+ break;
+ case SPEK_AUDIO_BAD_SAMPLE_FORMAT:
+ error = _("Unsupported sample format");
+ break;
+ }
+
+ // TRANSLATORS: first %s is the error message, second %s is stream description.
+ desc = wxString::Format(_("%s: %s"), error.c_str(), desc.c_str());
+ }
+
+ return desc;
+}
diff --git a/src/spek-audio-desc.hh b/src/spek-audio-desc.hh
@@ -0,0 +1,28 @@
+/* spek-audio-desc.hh
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_AUDIO_DESC_HH_
+#define SPEK_AUDIO_DESC_HH_
+
+#include <wx/string.h>
+
+struct spek_audio_properties;
+
+wxString spek_audio_desc(const struct spek_audio_properties *properties);
+
+#endif
diff --git a/src/spek-audio.c b/src/spek-audio.c
@@ -1,6 +1,6 @@
/* spek-audio.c
*
- * Copyright (C) 2010 Alexander Kojevnikov <alexander@kojevnikov.com>
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
*
* Spek is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,142 +16,165 @@
* along with Spek. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <glib.h>
-#include <glib/gi18n.h>
+#include <string.h>
+
+#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
#include <libavutil/mathematics.h>
+#include "spek-platform.hh"
+
#include "spek-audio.h"
-void spek_audio_init () {
- /* TODO: register only audio decoders */
- av_register_all ();
+struct spek_audio_context
+{
+ char *file_name; // TODO: needed?
+ char *short_name;
+ AVFormatContext *format_context;
+ int audio_stream;
+ AVCodecContext *codec_context;
+ AVStream *stream;
+ AVCodec *codec;
+ int buffer_size;
+ AVPacket *packet;
+ int offset;
+
+ struct spek_audio_properties properties;
+};
+
+
+void spek_audio_init()
+{
+ // TODO: register only audio decoders.
+ av_register_all();
+}
+
+const struct spek_audio_properties * spek_audio_get_properties(struct spek_audio_context *cx)
+{
+ return &cx->properties;
}
-SpekAudioContext * spek_audio_open (const gchar *file_name) {
- SpekAudioContext *cx;
- int i;
-
- cx = g_new0 (SpekAudioContext, 1);
- cx->file_name = g_strdup (file_name);
-#ifdef G_OS_WIN32
- /* av_open_input_file() cannot open files with Unicode chars in it
- * when running under Windows. When this happens we will re-try
- * using the corresponding short file name.
- */
- cx->short_name = g_win32_locale_filename_from_utf8 (file_name);
-#endif
-
- if (avformat_open_input (&cx->format_context, file_name, NULL, NULL) != 0) {
+struct spek_audio_context * spek_audio_open(const char *path)
+{
+ // TODO: malloc and initialise explicitely
+ struct spek_audio_context *cx = calloc(1, sizeof(struct spek_audio_context));
+ cx->file_name = strdup(path);
+ // av_open_input_file() cannot open files with Unicode chars in it
+ // when running under Windows. When this happens we will re-try
+ // using the corresponding short file name.
+ // TODO: test if it's already fixed in FFmpeg
+ cx->short_name = spek_platform_short_path(path);
+
+ if (avformat_open_input(&cx->format_context, path, NULL, NULL) != 0) {
if (!cx->short_name ||
- avformat_open_input (&cx->format_context, cx->short_name, NULL, NULL) != 0 ) {
- cx->error = _("Cannot open input file");
+ avformat_open_input(&cx->format_context, cx->short_name, NULL, NULL) != 0 ) {
+ cx->properties.error = SPEK_AUDIO_CANNOT_OPEN_FILE;
return cx;
}
}
- if (avformat_find_stream_info (cx->format_context, NULL) < 0) {
- /* 24-bit APE returns an error but parses the stream info just fine */
+ if (avformat_find_stream_info(cx->format_context, NULL) < 0) {
+ // 24-bit APE returns an error but parses the stream info just fine.
if (cx->format_context->nb_streams <= 0) {
- cx->error = _("Cannot find stream info");
+ cx->properties.error = SPEK_AUDIO_NO_STREAMS;
return cx;
}
}
cx->audio_stream = -1;
- for (i = 0; i < cx->format_context->nb_streams; i++) {
+ for (int i = 0; i < cx->format_context->nb_streams; i++) {
if (cx->format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
cx->audio_stream = i;
break;
}
}
if (cx->audio_stream == -1) {
- cx->error = _("The file contains no audio streams");
+ cx->properties.error = SPEK_AUDIO_NO_AUDIO;
return cx;
}
cx->stream = cx->format_context->streams[cx->audio_stream];
cx->codec_context = cx->stream->codec;
- cx->codec = avcodec_find_decoder (cx->codec_context->codec_id);
+ cx->codec = avcodec_find_decoder(cx->codec_context->codec_id);
if (cx->codec == NULL) {
- cx->error = _("Cannot find decoder");
+ cx->properties.error = SPEK_AUDIO_NO_DECODER;
return cx;
}
- /* We can already fill in the stream info even if the codec won't be able to open it */
- cx->codec_name = g_strdup (cx->codec->long_name);
- cx->bit_rate = cx->codec_context->bit_rate;
- cx->sample_rate = cx->codec_context->sample_rate;
- cx->bits_per_sample = cx->codec_context->bits_per_raw_sample;
- if (!cx->bits_per_sample) {
- /* APE uses bpcs, FLAC uses bprs. */
- cx->bits_per_sample = cx->codec_context->bits_per_coded_sample;
+ // We can already fill in the stream info even if the codec won't be able to open it.
+ cx->properties.codec_name = strdup(cx->codec->long_name);
+ cx->properties.bit_rate = cx->codec_context->bit_rate;
+ cx->properties.sample_rate = cx->codec_context->sample_rate;
+ cx->properties.bits_per_sample = cx->codec_context->bits_per_raw_sample;
+ if (!cx->properties.bits_per_sample) {
+ // APE uses bpcs, FLAC uses bprs.
+ cx->properties.bits_per_sample = cx->codec_context->bits_per_coded_sample;
}
- cx->channels = cx->codec_context->channels;
+ cx->properties.channels = cx->codec_context->channels;
if (cx->stream->duration != AV_NOPTS_VALUE) {
- cx->duration = cx->stream->duration * av_q2d (cx->stream->time_base);
+ cx->properties.duration = cx->stream->duration * av_q2d(cx->stream->time_base);
} else if (cx->format_context->duration != AV_NOPTS_VALUE) {
- cx->duration = cx->format_context->duration / (double) AV_TIME_BASE;
+ cx->properties.duration = cx->format_context->duration / (double) AV_TIME_BASE;
} else {
- cx->error = _("Unknown duration");
+ cx->properties.error = SPEK_AUDIO_NO_DURATION;
return cx;
}
- if (cx->channels <= 0) {
- cx->error = _("No audio channels");
+ if (cx->properties.channels <= 0) {
+ cx->properties.error = SPEK_AUDIO_NO_CHANNELS;
return cx;
}
- if (avcodec_open2 (cx->codec_context, cx->codec, NULL) < 0) {
- cx->error = _("Cannot open decoder");
+ if (avcodec_open2(cx->codec_context, cx->codec, NULL) < 0) {
+ cx->properties.error = SPEK_AUDIO_CANNOT_OPEN_DECODER;
return cx;
}
switch (cx->codec_context->sample_fmt) {
- case SAMPLE_FMT_S16:
- cx->width = 16;
- cx->fp = FALSE;
+ case AV_SAMPLE_FMT_S16:
+ cx->properties.width = 16;
+ cx->properties.fp = false;
break;
- case SAMPLE_FMT_S32:
- cx->width = 32;
- cx->fp = FALSE;
+ case AV_SAMPLE_FMT_S32:
+ cx->properties.width = 32;
+ cx->properties.fp = false;
break;
- case SAMPLE_FMT_FLT:
- cx->width = 32;
- cx->fp = TRUE;
+ case AV_SAMPLE_FMT_FLT:
+ cx->properties.width = 32;
+ cx->properties.fp = true;
break;
- case SAMPLE_FMT_DBL:
- cx->width = 64;
- cx->fp = TRUE;
+ case AV_SAMPLE_FMT_DBL:
+ cx->properties.width = 64;
+ cx->properties.fp = true;
break;
default:
- cx->error = _("Unsupported sample format");
+ cx->properties.error = SPEK_AUDIO_BAD_SAMPLE_FORMAT;
return cx;
}
cx->buffer_size = (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2;
- cx->buffer = av_malloc (cx->buffer_size);
- cx->packet = av_mallocz (sizeof (AVPacket));
- av_init_packet (cx->packet);
+ cx->properties.buffer = av_malloc(cx->buffer_size);
+ cx->packet = av_mallocz(sizeof(AVPacket));
+ av_init_packet(cx->packet);
cx->offset = 0;
return cx;
}
-void spek_audio_start (SpekAudioContext *cx, gint samples) {
- gint64 rate = cx->sample_rate * (gint64) cx->stream->time_base.num;
- gint64 duration = (gint64) (cx->duration * cx->stream->time_base.den / cx->stream->time_base.num);
- cx->error_base = samples * (gint64) cx->stream->time_base.den;
- cx->frames_per_interval = av_rescale_rnd (duration, rate, cx->error_base, AV_ROUND_DOWN);
- cx->error_per_interval = (duration * rate) % cx->error_base;
+void spek_audio_start(struct spek_audio_context *cx, int samples)
+{
+ int64_t rate = cx->properties.sample_rate * (int64_t) cx->stream->time_base.num;
+ int64_t duration = (int64_t)
+ (cx->properties.duration * cx->stream->time_base.den / cx->stream->time_base.num);
+ cx->properties.error_base = samples * (int64_t)cx->stream->time_base.den;
+ cx->properties.frames_per_interval = av_rescale_rnd(
+ duration, rate, cx->properties.error_base, AV_ROUND_DOWN);
+ cx->properties.error_per_interval = (duration * rate) % cx->properties.error_base;
}
-gint spek_audio_read (SpekAudioContext *cx) {
- gint buffer_size;
- gint len;
- gint res;
-
- if (cx->error) {
+int spek_audio_read(struct spek_audio_context *cx) {
+ if (cx->properties.error) {
return -1;
}
for (;;) {
while (cx->packet->size > 0) {
- buffer_size = cx->buffer_size;
- len = avcodec_decode_audio3 (
- cx->codec_context, (int16_t *) cx->buffer, &buffer_size, cx->packet);
+ int buffer_size = cx->buffer_size;
+ int len = avcodec_decode_audio3(
+ cx->codec_context, (int16_t *)cx->properties.buffer, &buffer_size, cx->packet);
if (len < 0) {
- /* Error, skip the frame. */
+ // Error, skip the frame.
cx->packet->size = 0;
break;
}
@@ -159,10 +182,10 @@ gint spek_audio_read (SpekAudioContext *cx) {
cx->packet->size -= len;
cx->offset += len;
if (buffer_size <= 0) {
- /* No data yet, get more frames */
+ // No data yet, get more frames.
continue;
}
- /* We have data, return it and come back for more later */
+ // We have data, return it and come back for more later.
return buffer_size;
}
if (cx->packet->data) {
@@ -171,46 +194,49 @@ gint spek_audio_read (SpekAudioContext *cx) {
cx->offset = 0;
av_free_packet (cx->packet);
}
- while ((res = av_read_frame (cx->format_context, cx->packet)) >= 0) {
+
+ int res = 0;
+ while ((res = av_read_frame(cx->format_context, cx->packet)) >= 0) {
if (cx->packet->stream_index == cx->audio_stream) {
break;
}
- av_free_packet (cx->packet);
+ av_free_packet(cx->packet);
}
if (res < 0) {
- /* End of file or error. */
+ // End of file or error.
return 0;
}
}
}
-void spek_audio_close (SpekAudioContext *cx) {
+void spek_audio_close (struct spek_audio_context *cx)
+{
if (cx->file_name != NULL) {
- g_free (cx->file_name);
+ free(cx->file_name);
}
if (cx->short_name != NULL) {
- g_free (cx->short_name);
+ free(cx->short_name);
}
- if (cx->codec_name != NULL) {
- g_free (cx->codec_name);
+ if (cx->properties.codec_name != NULL) {
+ free(cx->properties.codec_name);
}
- if (cx->buffer) {
- av_free (cx->buffer);
+ if (cx->properties.buffer) {
+ av_free(cx->properties.buffer);
}
if (cx->packet) {
if (cx->packet->data) {
cx->packet->data -= cx->offset;
cx->packet->size += cx->offset;
cx->offset = 0;
- av_free_packet (cx->packet);
+ av_free_packet(cx->packet);
}
- av_free (cx->packet);
+ av_free(cx->packet);
}
if (cx->codec_context != NULL) {
- avcodec_close (cx->codec_context);
+ avcodec_close(cx->codec_context);
}
if (cx->format_context != NULL) {
- av_close_input_file (cx->format_context);
+ av_close_input_file(cx->format_context);
}
- g_free (cx);
+ free(cx);
}
diff --git a/src/spek-audio.h b/src/spek-audio.h
@@ -1,6 +1,6 @@
/* spek-audio.h
*
- * Copyright (C) 2010 Alexander Kojevnikov <alexander@kojevnikov.com>
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
*
* Spek is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,62 +16,72 @@
* along with Spek. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef __SPEK_AUDIO_H__
-#define __SPEK_AUDIO_H__
+#ifndef SPEK_AUDIO_H_
+#define SPEK_AUDIO_H_
-#include <glib.h>
-#include <libavformat/avformat.h>
-#include <libavcodec/avcodec.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
-typedef struct {
- /* Internal data */
- gchar *short_name;
- AVFormatContext *format_context;
- gint audio_stream;
- AVCodecContext *codec_context;
- AVStream *stream;
- AVCodec *codec;
- gint buffer_size;
- AVPacket *packet;
- gint offset;
+#include <stdbool.h>
+#include <stdint.h>
- /* Exposed properties */
- gchar *file_name;
- gchar *codec_name;
- gchar *error;
- gint bit_rate;
- gint sample_rate;
- gint bits_per_sample;
- gint width; /* number of bits used to store a sample */
- gboolean fp; /* floating-point sample representation */
- gint channels;
- gdouble duration;
- guint8 *buffer;
- gint64 frames_per_interval;
- gint64 error_per_interval;
- gint64 error_base;
-} SpekAudioContext;
+struct spek_audio_context;
-/* Initialise FFmpeg, should be called once on start up */
-void spek_audio_init ();
+enum spek_audio_error
+{
+ SPEK_AUDIO_OK = 0,
+ SPEK_AUDIO_CANNOT_OPEN_FILE,
+ SPEK_AUDIO_NO_STREAMS,
+ SPEK_AUDIO_NO_AUDIO,
+ SPEK_AUDIO_NO_DECODER,
+ SPEK_AUDIO_NO_DURATION,
+ SPEK_AUDIO_NO_CHANNELS,
+ SPEK_AUDIO_CANNOT_OPEN_DECODER,
+ SPEK_AUDIO_BAD_SAMPLE_FORMAT,
+};
-/* Open the file, check if it has an audio stream which can be decoded.
- * On error, initialises the `error` field in the returned context.
- */
-SpekAudioContext * spek_audio_open (const gchar *file_name);
+struct spek_audio_properties
+{
+ char *codec_name;
+ enum spek_audio_error error;
+ int bit_rate;
+ int sample_rate;
+ int bits_per_sample;
+ int width; // number of bits used to store a sample
+ bool fp; // floating-point sample representation
+ int channels;
+ double duration;
+ // TODO: these four guys don't belong here, move them somewhere else when revamping the pipeline
+ uint8_t *buffer;
+ int64_t frames_per_interval;
+ int64_t error_per_interval;
+ int64_t error_base;
+};
-/* Prepare the context for reading audio samples. */
-void spek_audio_start (SpekAudioContext *cx, gint samples);
+// Initialise FFmpeg, should be called once on start up.
+void spek_audio_init();
-/* Read and decode the opened audio stream.
- * Returns -1 on error, 0 if there's nothing left to read
- * or the number of bytes decoded into the buffer.
- */
-gint spek_audio_read (SpekAudioContext *cx);
+// Open the file, check if it has an audio stream which can be decoded.
+// On error, initialises the `error` field in the returned context.
+struct spek_audio_context * spek_audio_open(const char *path);
-/* Closes the file opened with spek_audio_open,
- * frees all allocated buffers and the context
- */
-void spek_audio_close (SpekAudioContext *cx);
+const struct spek_audio_properties * spek_audio_get_properties(struct spek_audio_context *cs);
+
+// Prepare the context for reading audio samples.
+void spek_audio_start(struct spek_audio_context *cx, int samples);
+
+// Read and decode the opened audio stream.
+// Returns -1 on error, 0 if there's nothing left to read
+// or the number of bytes decoded into the buffer.
+int spek_audio_read(struct spek_audio_context *cx);
+
+// Closes the file opened with spek_audio_open,
+// frees all allocated buffers and the context
+void spek_audio_close(struct spek_audio_context *cx);
+
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/src/spek-events.cc b/src/spek-events.cc
@@ -0,0 +1,45 @@
+/* spek-events.cc
+ *
+ * Copyright (C) 2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "spek-events.hh"
+
+//IMPLEMENT_DYNAMIC_CLASS(SpekHaveSampleEvent, wxEvent)
+DEFINE_EVENT_TYPE(SPEK_HAVE_SAMPLE)
+
+SpekHaveSampleEvent::SpekHaveSampleEvent(int bands, int sample, float *values, bool free_values)
+ : wxEvent(), bands(bands), sample(sample), values(values), free_values(free_values)
+{
+ SetEventType(SPEK_HAVE_SAMPLE);
+}
+
+SpekHaveSampleEvent::SpekHaveSampleEvent(const SpekHaveSampleEvent& other)
+{
+ SetEventType(SPEK_HAVE_SAMPLE);
+ this->bands = other.bands;
+ this->sample = other.sample;
+ this->values = (float *)malloc(this->bands * sizeof(float));
+ memcpy(this->values, other.values, this->bands * sizeof(float));
+ this->free_values = true;
+}
+
+SpekHaveSampleEvent::~SpekHaveSampleEvent()
+{
+ if (this->free_values) {
+ free(this->values);
+ }
+}
diff --git a/src/spek-events.hh b/src/spek-events.hh
@@ -0,0 +1,53 @@
+/* spek-events.hh
+ *
+ * Copyright (C) 2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_EVENTS_HH_
+#define SPEK_EVENTS_HH_
+
+#include <wx/wx.h>
+
+class SpekHaveSampleEvent: public wxEvent
+{
+public:
+ SpekHaveSampleEvent(int bands, int sample, float *values, bool free_values);
+ SpekHaveSampleEvent(const SpekHaveSampleEvent& other);
+ ~SpekHaveSampleEvent();
+
+ int get_bands() const { return this->bands; }
+ int get_sample() const { return this->sample; }
+ const float *get_values() const { return this->values; }
+
+ wxEvent *Clone() const { return new SpekHaveSampleEvent(*this); }
+// DECLARE_DYNAMIC_CLASS(SpekHaveSampleEvent);
+
+private:
+ int bands;
+ int sample;
+ float *values;
+ bool free_values;
+};
+
+typedef void (wxEvtHandler::*SpekHaveSampleEventFunction)(SpekHaveSampleEvent&);
+
+DECLARE_EVENT_TYPE(SPEK_HAVE_SAMPLE, wxID_ANY)
+
+#define SPEK_EVT_HAVE_SAMPLE(fn) \
+ DECLARE_EVENT_TABLE_ENTRY(SPEK_HAVE_SAMPLE, -1, -1, \
+ (wxObjectEventFunction) (SpekHaveSampleEventFunction) &fn, (wxObject *) NULL ),
+
+#endif
diff --git a/src/spek-fft.c b/src/spek-fft.c
@@ -1,6 +1,6 @@
/* spek-fft.c
*
- * Copyright (C) 2010 Alexander Kojevnikov <alexander@kojevnikov.com>
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
*
* Spek is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,43 +17,49 @@
*/
#include <math.h>
+#include <libavcodec/avfft.h>
#include <libavutil/mem.h>
#include "spek-fft.h"
-SpekFftPlan * spek_fft_plan_new (gint n, gint threshold) {
- gint bits;
- SpekFftPlan *p = g_new0 (SpekFftPlan, 1);
- p->input = av_mallocz (sizeof (gfloat) * n);
- p->output = av_mallocz (sizeof (gfloat) * (n / 2 + 1));
+struct spek_fft_plan * spek_fft_plan_new(int n, int threshold)
+{
+ struct spek_fft_plan *p = malloc(sizeof(struct spek_fft_plan));
+ p->input = av_mallocz(sizeof(float) * n);
+ p->output = av_mallocz(sizeof(float) * (n / 2 + 1));
p->threshold = threshold;
- for (bits = 0; n; n >>= 1, bits++);
+ int bits = 0;
+ while (n) {
+ n >>= 1;
+ ++bits;
+ }
p->n = 1 << --bits;
- p->cx = av_rdft_init (bits, DFT_R2C);
+ p->cx = av_rdft_init(bits, DFT_R2C);
return p;
}
-void spek_fft_execute (SpekFftPlan *p) {
- int i;
- int n = p->n;
-
- av_rdft_calc (p->cx, p->input);
+void spek_fft_execute(struct spek_fft_plan *p)
+{
+ av_rdft_calc(p->cx, p->input);
- /* Calculate magnitudes */
+ // Calculate magnitudes.
+ int n = p->n;
p->output[0] = p->input[0] * p->input[0] / (n * n);
p->output[n / 2] = p->input[1] * p->input[1] / (n * n);
- for (i = 1; i < n / 2; i++) {
- gfloat val;
- val = p->input[i * 2] * p->input[i * 2] + p->input[i * 2 + 1] * p->input[i * 2 + 1];
+ for (int i = 1; i < n / 2; i++) {
+ float val =
+ p->input[i * 2] * p->input[i * 2] +
+ p->input[i * 2 + 1] * p->input[i * 2 + 1];
val /= n * n;
val = 10.0 * log10f (val);
p->output[i] = val < p->threshold ? p->threshold : val;
}
}
-void spek_fft_destroy (SpekFftPlan *p) {
- av_rdft_end (p->cx);
- av_free (p->input);
- av_free (p->output);
- g_free (p);
+void spek_fft_delete(struct spek_fft_plan *p)
+{
+ av_rdft_end(p->cx);
+ av_free(p->input);
+ av_free(p->output);
+ free(p);
}
diff --git a/src/spek-fft.h b/src/spek-fft.h
@@ -1,6 +1,6 @@
/* spek-fft.h
*
- * Copyright (C) 2010 Alexander Kojevnikov <alexander@kojevnikov.com>
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
*
* Spek is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,30 +16,38 @@
* along with Spek. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef __SPEK_FFT_H__
-#define __SPEK_FFT_H__
+#ifndef SPEK_FFT_H_
+#define SPEK_FFT_H_
-#include <glib.h>
-#include <libavcodec/avfft.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct RDFTContext;
-typedef struct {
- /* Internal data */
- RDFTContext *cx;
- gint n;
- gint threshold;
+struct spek_fft_plan
+{
+ // Internal data.
+ struct RDFTContext *cx;
+ int n;
+ int threshold;
- /* Exposed properties */
- gfloat *input;
- gfloat *output;
-} SpekFftPlan;
+ // Exposed properties.
+ float *input;
+ float *output;
+};
-/* Allocate buffers and create a new FFT plan */
-SpekFftPlan * spek_fft_plan_new (gint n, gint threshold);
+// Allocate buffers and create a new FFT plan.
+struct spek_fft_plan * spek_fft_plan_new(int n, int threshold);
-/* Execute the FFT on plan->input */
-void spek_fft_execute (SpekFftPlan *p);
+// Execute the FFT on plan->input.
+void spek_fft_execute(struct spek_fft_plan *p);
-/* Destroy the plan and de-allocate buffers */
-void spek_fft_destroy (SpekFftPlan *p);
+// Destroy the plan and de-allocate buffers.
+void spek_fft_delete(struct spek_fft_plan *p);
+
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/src/spek-palette.c b/src/spek-palette.c
@@ -0,0 +1,61 @@
+/* spek-palette.c
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "spek-palette.h"
+
+// Modified version of Dan Bruton's algorithm:
+// http://www.physics.sfasu.edu/astro/color/spectra.html
+uint32_t spek_palette_spectrum(double level)
+{
+ level *= 0.6625;
+ double r = 0.0, g = 0.0, b = 0.0;
+ if (level >= 0 && level < 0.15) {
+ r = (0.15 - level) / (0.15 + 0.075);
+ g = 0.0;
+ b = 1.0;
+ } else if (level >= 0.15 && level < 0.275) {
+ r = 0.0;
+ g = (level - 0.15) / (0.275 - 0.15);
+ b = 1.0;
+ } else if (level >= 0.275 && level < 0.325) {
+ r = 0.0;
+ g = 1.0;
+ b = (0.325 - level) / (0.325 - 0.275);
+ } else if (level >= 0.325 && level < 0.5) {
+ r = (level - 0.325) / (0.5 - 0.325);
+ g = 1.0;
+ b = 0.0;
+ } else if (level >= 0.5 && level < 0.6625) {
+ r = 1.0;
+ g = (0.6625 - level) / (0.6625 - 0.5f);
+ b = 0.0;
+ }
+
+ // Intensity correction.
+ double cf = 1.0;
+ if (level >= 0.0 && level < 0.1) {
+ cf = level / 0.1;
+ }
+ cf *= 255.0;
+
+ // Pack RGB values into a 32-bit uint.
+ uint32_t rr = (uint32_t) (r * cf + 0.5);
+ uint32_t gg = (uint32_t) (g * cf + 0.5);
+ uint32_t bb = (uint32_t) (b * cf + 0.5);
+ return (rr << 16) + (gg << 8) + bb;
+}
diff --git a/src/spek-palette.h b/src/spek-palette.h
@@ -0,0 +1,34 @@
+/* spek-palette.h
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_PALETTE_HH_
+#define SPEK_PALETTE_HH_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+uint32_t spek_palette_spectrum(double level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/spek-pipeline.c b/src/spek-pipeline.c
@@ -0,0 +1,368 @@
+/* spek-pipeline.c
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Conversion of decoded samples into an FFT-happy format is heavily
+ * influenced by GstSpectrum which is part of gst-plugins-good.
+ * The original code:
+ * (c) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ * (c) 2006 Stefan Kost <ensonic@users.sf.net>
+ * (c) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ */
+
+#include <assert.h>
+#include <math.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "spek-audio.h"
+#include "spek-fft.h"
+
+#include "spek-pipeline.h"
+
+enum
+{
+ NFFT = 64 // Number of FFTs to pre-fetch.
+};
+
+struct spek_pipeline
+{
+ struct spek_audio_context *cx;
+ const struct spek_audio_properties *properties;
+ int bands;
+ int samples;
+ int threshold;
+ spek_pipeline_cb cb;
+ void *cb_data;
+
+ struct spek_fft_plan *fft;
+ float *coss; // Pre-computed cos table.
+ int nfft; // Size of the FFT transform.
+ int input_size;
+ int input_pos;
+ float *input;
+ float *output;
+
+ pthread_t reader_thread;
+ bool has_reader_thread;
+ pthread_mutex_t reader_mutex;
+ bool has_reader_mutex;
+ pthread_cond_t reader_cond;
+ bool has_reader_cond;
+ pthread_t worker_thread;
+ bool has_worker_thread;
+ pthread_mutex_t worker_mutex;
+ bool has_worker_mutex;
+ pthread_cond_t worker_cond;
+ bool has_worker_cond;
+ bool worker_done;
+ volatile bool quit;
+};
+
+// Forward declarations.
+static void * reader_func(void *);
+static void * worker_func(void *);
+static void reader_sync(struct spek_pipeline *p, int pos);
+static float average_input(const struct spek_pipeline *p, void *buffer);
+
+struct spek_pipeline * spek_pipeline_open(
+ const char *path, int bands, int samples, int threshold, spek_pipeline_cb cb, void *cb_data)
+{
+ struct spek_pipeline *p = malloc(sizeof(struct spek_pipeline));
+ p->cx = spek_audio_open(path);
+ p->properties = spek_audio_get_properties(p->cx);
+ p->bands = bands;
+ p->samples = samples;
+ p->threshold = threshold;
+ p->cb = cb;
+ p->cb_data = cb_data;
+
+ p->coss = NULL;
+ p->fft = NULL;
+ p->input = NULL;
+ p->output = NULL;
+ p->has_reader_thread = false;
+ p->has_reader_mutex = false;
+ p->has_reader_cond = false;
+ p->has_worker_thread = false;
+ p->has_worker_mutex = false;
+ p->has_worker_cond = false;
+
+ if (!p->properties->error) {
+ p->nfft = 2 * bands - 2;
+ p->coss = malloc(p->nfft * sizeof(float));
+ float cf = 2.0f * (float)M_PI / p->nfft;
+ for (int i = 0; i < p->nfft; ++i) {
+ p->coss[i] = cosf(cf * i);
+ }
+ p->fft = spek_fft_plan_new(p->nfft, threshold);
+ p->input_size = p->nfft * (NFFT * 2 + 1);
+ p->input = malloc(p->input_size * sizeof(float));
+ p->output = malloc(bands * sizeof(float));
+ spek_audio_start(p->cx, samples);
+ }
+
+ return p;
+}
+
+const struct spek_audio_properties * spek_pipeline_properties(struct spek_pipeline *pipeline)
+{
+ return pipeline->properties;
+}
+
+void spek_pipeline_start(struct spek_pipeline *p)
+{
+ if (p->properties->error) return;
+
+ p->input_pos = 0;
+ p->worker_done = false;
+ p->quit = false;
+
+ p->has_reader_mutex = !pthread_mutex_init(&p->reader_mutex, NULL);
+ p->has_reader_cond = !pthread_cond_init(&p->reader_cond, NULL);
+ p->has_worker_mutex = !pthread_mutex_init(&p->worker_mutex, NULL);
+ p->has_worker_cond = !pthread_cond_init(&p->worker_cond, NULL);
+
+ p->has_reader_thread = !pthread_create(&p->reader_thread, NULL, &reader_func, p);
+ if (!p->has_reader_thread) {
+ spek_pipeline_close(p);
+ }
+}
+
+void spek_pipeline_close(struct spek_pipeline *p)
+{
+ if (p->has_reader_thread) {
+ p->quit = true;
+ pthread_join(p->reader_thread, NULL);
+ p->has_reader_thread = false;
+ }
+ if (p->has_worker_cond) {
+ pthread_cond_destroy(&p->worker_cond);
+ p->has_worker_cond = false;
+ }
+ if (p->has_worker_mutex) {
+ pthread_mutex_destroy(&p->worker_mutex);
+ p->has_worker_mutex = false;
+ }
+ if (p->has_reader_cond) {
+ pthread_cond_destroy(&p->reader_cond);
+ p->has_reader_cond = false;
+ }
+ if (p->has_reader_mutex) {
+ pthread_mutex_destroy(&p->reader_mutex);
+ p->has_reader_mutex = false;
+ }
+ if (p->output) {
+ free(p->output);
+ p->output = NULL;
+ }
+ if (p->input) {
+ free(p->input);
+ p->input = NULL;
+ }
+ if (p->fft) {
+ spek_fft_delete(p->fft);
+ p->fft = NULL;
+ }
+ if (p->coss) {
+ free(p->coss);
+ p->coss = NULL;
+ }
+ if (p->cx) {
+ spek_audio_close(p->cx);
+ p->cx = NULL;
+ }
+ free(p);
+}
+
+static void * reader_func(void *pp)
+{
+ struct spek_pipeline *p = pp;
+
+ p->has_worker_thread = !pthread_create(&p->worker_thread, NULL, &worker_func, p);
+ if (!p->has_worker_thread) {
+ return NULL;
+ }
+
+ int pos = 0, prev_pos = 0;
+ int block_size = p->properties->width * p->properties->channels / 8;
+ int size;
+ while ((size = spek_audio_read(p->cx)) > 0) {
+ if (p->quit) break;
+
+ uint8_t *buffer = p->properties->buffer;
+ while (size >= block_size) {
+ p->input[pos] = average_input(p, buffer);
+ buffer += block_size;
+ size -= block_size;
+ pos = (pos + 1) % p->input_size;
+
+ // Wake up the worker if we have enough data.
+ if ((pos > prev_pos ? pos : pos + p->input_size) - prev_pos == p->nfft * NFFT) {
+ reader_sync(p, prev_pos = pos);
+ }
+ }
+ assert(size == 0);
+ }
+
+ if (pos != prev_pos) {
+ // Process the remaining data.
+ reader_sync(p, pos);
+ }
+
+ // Force the worker to quit.
+ reader_sync(p, -1);
+ pthread_join(p->worker_thread, NULL);
+ return NULL;
+}
+
+static void reader_sync(struct spek_pipeline *p, int pos)
+{
+ pthread_mutex_lock(&p->reader_mutex);
+ while (!p->worker_done) {
+ pthread_cond_wait(&p->reader_cond, &p->reader_mutex);
+ }
+ p->worker_done = false;
+ pthread_mutex_unlock(&p->reader_mutex);
+
+ pthread_mutex_lock(&p->worker_mutex);
+ p->input_pos = pos;
+ pthread_cond_signal(&p->worker_cond);
+ pthread_mutex_unlock(&p->worker_mutex);
+}
+
+static void * worker_func(void *pp)
+{
+ struct spek_pipeline *p = pp;
+
+ int sample = 0;
+ int64_t frames = 0;
+ int64_t num_fft = 0;
+ int64_t acc_error = 0;
+ int head = 0, tail = 0;
+ int prev_head = 0;
+
+ memset(p->output, 0, sizeof(float) * p->bands);
+
+ while (true) {
+ pthread_mutex_lock(&p->reader_mutex);
+ p->worker_done = true;
+ pthread_cond_signal(&p->reader_cond);
+ pthread_mutex_unlock(&p->reader_mutex);
+
+ pthread_mutex_lock(&p->worker_mutex);
+ while (tail == p->input_pos) {
+ pthread_cond_wait(&p->worker_cond, &p->worker_mutex);
+ }
+ tail = p->input_pos;
+ pthread_mutex_unlock(&p->worker_mutex);
+
+ if (tail == -1) {
+ return NULL;
+ }
+
+ while (true) {
+ head = (head + 1) % p->input_size;
+ if (head == tail) {
+ head = prev_head;
+ break;
+ }
+ frames++;
+
+ // If we have enough frames for an FFT or we have
+ // all frames required for the interval run and FFT.
+ bool int_full =
+ acc_error < p->properties->error_base &&
+ frames == p->properties->frames_per_interval;
+ bool int_over =
+ acc_error >= p->properties->error_base &&
+ frames == 1 + p->properties->frames_per_interval;
+
+ if (frames % p->nfft == 0 || ((int_full || int_over) && num_fft == 0)) {
+ prev_head = head;
+ for (int i = 0; i < p->nfft; i++) {
+ float val = p->input[(p->input_size + head - p->nfft + i) % p->input_size];
+ // TODO: allow the user to chose the window function
+ // Hamming window.
+ // val *= 0.53836f - 0.46164f * coss[i];
+ // Hann window.
+ val *= 0.5f * (1.0f - p->coss[i]);
+ p->fft->input[i] = val;
+ }
+ spek_fft_execute(p->fft);
+ num_fft++;
+ for (int i = 0; i < p->bands; i++) {
+ p->output[i] += p->fft->output[i];
+ }
+ }
+
+ // Do we have the FFTs for one interval?
+ if (int_full || int_over) {
+ if (int_over) {
+ acc_error -= p->properties->error_base;
+ } else {
+ acc_error += p->properties->error_per_interval;
+ }
+
+ for (int i = 0; i < p->bands; i++) {
+ p->output[i] /= num_fft;
+ }
+
+ if (sample == p->samples) break;
+ p->cb(sample++, p->output, p->cb_data);
+
+ memset(p->output, 0, sizeof(float) * p->bands);
+ frames = 0;
+ num_fft = 0;
+ }
+ }
+ }
+}
+
+static float average_input(const struct spek_pipeline *p, void *buffer)
+{
+ int channels = p->properties->channels;
+ float res = 0.0f;
+ if (p->properties->fp) {
+ if (p->properties->width == 32) {
+ float *b = buffer;
+ for (int i = 0; i < channels; i++) {
+ res += b[i];
+ }
+ } else {
+ assert(p->properties->width == 64);
+ double *b = buffer;
+ for (int i = 0; i < channels; i++) {
+ res += (float) b[i];
+ }
+ }
+ } else {
+ if (p->properties->width == 16) {
+ int16_t *b = buffer;
+ for (int i = 0; i < channels; i++) {
+ res += b[i] / (float) INT16_MAX;
+ }
+ } else {
+ assert (p->properties->width == 32);
+ int32_t *b = buffer;
+ for (int i = 0; i < channels; i++) {
+ res += b[i] / (float) INT32_MAX;
+ }
+ }
+ }
+ return res / channels;
+}
diff --git a/src/spek-pipeline.h b/src/spek-pipeline.h
@@ -0,0 +1,44 @@
+/* spek-pipeline.h
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_PIPELINE_H_
+#define SPEK_PIPELINE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spek_pipeline;
+struct spek_audio_properties;
+
+typedef void (*spek_pipeline_cb)(int sample, float *values, void *cb_data);
+
+struct spek_pipeline * spek_pipeline_open(
+ const char *path, int bands, int samples, int threshold, spek_pipeline_cb cb, void *cb_data);
+
+const struct spek_audio_properties * spek_pipeline_properties(struct spek_pipeline *pipeline);
+
+void spek_pipeline_start(struct spek_pipeline *pipeline);
+
+void spek_pipeline_close(struct spek_pipeline *pipeline);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/spek-pipeline.vala b/src/spek-pipeline.vala
@@ -1,302 +0,0 @@
-/* spek-pipeline.vala
- *
- * Copyright (C) 2010 Alexander Kojevnikov <alexander@kojevnikov.com>
- *
- * Spek 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 3 of the License, or
- * (at your option) any later version.
- *
- * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
- *
- * Conversion of decoded samples into an FFT-happy format is heavily
- * influenced by GstSpectrum which is part of gst-plugins-good.
- * The original code:
- * (c) 1999 Erik Walthinsen <omega@cse.ogi.edu>
- * (c) 2006 Stefan Kost <ensonic@users.sf.net>
- * (c) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
- */
-
-namespace Spek {
- public class Pipeline {
- public string description { get; private set; }
- public int sample_rate { get; private set; }
- public double duration { get { return cx.duration; } }
- public delegate void Callback (int sample, float[] values);
-
- private Audio.Context cx;
- private int bands;
- private int samples;
- private int threshold;
- private Callback cb;
-
- private Fft.Plan fft;
- private int nfft; // Size of the FFT transform.
- private float[] coss; // Pre-computed cos table.
- private const int NFFT = 64; // Number of FFTs to pre-fetch.
- private int input_size;
- private int input_pos;
- private float[] input;
- private float[] output;
-
- private unowned Thread<void*> reader_thread = null;
- private unowned Thread<void*> worker_thread;
- private Mutex reader_mutex;
- private Cond reader_cond;
- private Mutex worker_mutex;
- private Cond worker_cond;
- private bool worker_done = false;
- private bool quit = false;
-
- public Pipeline (string file_name, int bands, int samples, int threshold, Callback cb) {
- this.cx = new Audio.Context (file_name);
- this.bands = bands;
- this.samples = samples;
- this.threshold = threshold;
- this.cb = cb;
-
- // Build the description string.
- string[] items = {};
- if (cx.codec_name != null) {
- items += cx.codec_name;
- }
- if (cx.bit_rate != 0) {
- items += _("%d kbps").printf ((cx.bit_rate + 500) / 1000);
- }
- if (cx.sample_rate != 0) {
- items += _("%d Hz").printf (cx.sample_rate);
- }
- // Show bits per sample only if there is no bitrate.
- if (cx.bits_per_sample != 0 && cx.bit_rate == 0) {
- items += ngettext (
- "%d bit", "%d bits", cx.bits_per_sample).printf (cx.bits_per_sample);
- }
- if (cx.channels != 0) {
- items += ngettext ("%d channel", "%d channels", cx.channels).
- printf (cx.channels);
- }
- description = items.length > 0 ? string.joinv (", ", items) : "";
-
- if (cx.error != null) {
- // TRANSLATORS: first %s is the error message, second %s is stream description.
- description = _("%s: %s").printf (cx.error, description);
- } else {
- this.sample_rate = cx.sample_rate;
- this.nfft = 2 * bands - 2;
- this.coss = new float[nfft];
- float cf = 2f * (float) Math.PI / this.nfft;
- for (int i = 0; i < this.nfft; i++) {
- this.coss[i] = Math.cosf (cf * i);
- }
- this.fft = new Fft.Plan (nfft, threshold);
- this.input_size = nfft * (NFFT * 2 + 1);
- this.input = new float[input_size];
- this.output = new float[bands];
- this.cx.start (samples);
- }
- }
-
- ~Pipeline () {
- stop ();
- }
-
- public void start () {
- stop ();
-
- if (cx.error != null) return;
-
- input_pos = 0;
- reader_mutex = new Mutex ();
- reader_cond = new Cond ();
- worker_mutex = new Mutex ();
- worker_cond = new Cond ();
-
- try {
- reader_thread = Thread.create<void*> (reader_func, true);
- } catch (ThreadError e) {
- stop ();
- }
- }
-
- public void stop () {
- if (reader_thread != null) {
- lock (quit) {
- quit = true;
- }
- reader_thread.join ();
- quit = false;
- reader_thread = null;
- }
- }
-
- private void * reader_func () {
- var timeval = TimeVal ();
- timeval.get_current_time ();
-
- int pos = 0, prev_pos = 0;
- int block_size = cx.width * cx.channels / 8;
- int size;
-
- try {
- worker_thread = Thread.create<void*> (worker_func, true);
- } catch (ThreadError e) {
- return null;
- }
-
- while ((size = cx.read ()) > 0) {
- lock (quit) if (quit) break;
-
- uint8 *buffer = (uint8 *) cx.buffer;
- while (size >= block_size) {
- input[pos] = average_input (buffer);
- buffer += block_size;
- size -= block_size;
- pos = (pos + 1) % input_size;
-
- // Wake up the worker if we have enough data.
- if ((pos > prev_pos ? pos : pos + input_size) - prev_pos == nfft * NFFT) {
- reader_sync (prev_pos = pos);
- }
- }
- assert (size == 0);
- }
-
- if (pos != prev_pos) {
- // Process the remaining data.
- reader_sync (pos);
- }
- // Force the worker to quit.
- reader_sync (-1);
- worker_thread.join ();
- return null;
- }
-
- private void reader_sync (int pos) {
- reader_mutex.lock ();
- while (!worker_done) reader_cond.wait (reader_mutex);
- worker_done = false;
- reader_mutex.unlock ();
-
- worker_mutex.lock ();
- input_pos = pos;
- worker_cond.signal ();
- worker_mutex.unlock ();
- }
-
- private void * worker_func () {
- int sample = 0;
- int64 frames = 0;
- int64 num_fft = 0;
- int64 acc_error = 0;
- int head = 0, tail = 0;
- int prev_head = 0;
-
- Memory.set (output, 0, sizeof (float) * bands);
-
- while (true) {
- reader_mutex.lock ();
- worker_done = true;
- reader_cond.signal ();
- reader_mutex.unlock ();
-
- worker_mutex.lock ();
- while (tail == input_pos) worker_cond.wait (worker_mutex);
- tail = input_pos;
- worker_mutex.unlock ();
-
- if (tail == -1) {
- return null;
- }
-
- while (true) {
- head = (head + 1) % input_size;
- if (head == tail) {
- head = prev_head;
- break;
- }
- frames++;
-
- // If we have enough frames for an FFT or we have
- // all frames required for the interval run and FFT.
- bool int_full = acc_error < cx.error_base && frames == cx.frames_per_interval;
- bool int_over = acc_error >= cx.error_base && frames == 1 + cx.frames_per_interval;
- if (frames % nfft == 0 || ((int_full || int_over) && num_fft == 0)) {
- prev_head = head;
- for (int i = 0; i < nfft; i++) {
- float val = input[(input_size + head - nfft + i) % input_size];
- // TODO: allow the user to chose the window function
- // Hamming window.
-// val *= 0.53836f - 0.46164f * coss[i];
- // Hann window.
- val *= 0.5f * (1f - coss[i]);
- fft.input[i] = val;
- }
- fft.execute ();
- num_fft++;
- for (int i = 0; i < bands; i++) {
- output[i] += fft.output[i];
- }
- }
- // Do we have the FFTs for one interval?
- if (int_full || int_over) {
- if (int_over) {
- acc_error -= cx.error_base;
- } else {
- acc_error += cx.error_per_interval;
- }
-
- for (int i = 0; i < bands; i++) {
- output[i] /= num_fft;
- }
-
- if (sample == samples) break;
- cb (sample++, output);
-
- Memory.set (output, 0, sizeof (float) * bands);
- frames = 0;
- num_fft = 0;
- }
- }
- }
- }
-
- private float average_input (uint8 *buffer) {
- int channels = cx.channels;
- float res = 0f;
- if (cx.fp) {
- if (cx.width == 32) {
- float *p = (float *) buffer;
- for (int i = 0; i < channels; i++) {
- res += p[i];
- }
- } else {
- assert (cx.width == 64);
- double *p = (double *) buffer;
- for (int i = 0; i < channels; i++) {
- res += (float) p[i];
- }
- }
- } else {
- if (cx.width == 16) {
- int16 *p = (int16 *) buffer;
- for (int i = 0; i < channels; i++) {
- res += p[i] / (float) int16.MAX;
- }
- } else {
- assert (cx.width == 32);
- int32 *p = (int32 *) buffer;
- for (int i = 0; i < channels; i++) {
- res += p[i] / (float) int32.MAX;
- }
- }
- }
- return res / channels;
- }
- }
-}
diff --git a/src/spek-platform.c b/src/spek-platform.c
@@ -1,209 +0,0 @@
-/* spek-platform.c
- *
- * Copyright (C) 2010,2011 Alexander Kojevnikov <alexander@kojevnikov.com>
- *
- * Spek 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 3 of the License, or
- * (at your option) any later version.
- *
- * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <gtk/gtk.h>
-
-#ifdef G_OS_WIN32
-#include <windows.h>
-#include <shellapi.h>
-#endif
-
-#ifdef G_OS_DARWIN
-#include <gtkosxapplication.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <ApplicationServices/ApplicationServices.h>
-#endif
-
-#include "spek-platform.h"
-
-void spek_platform_init () {
-#ifdef G_OS_DARWIN
- g_object_new (GTK_TYPE_OSX_APPLICATION, NULL);
-#endif
-}
-
-void spek_platform_fix_args (gchar **argv, gint argc) {
-#ifdef G_OS_WIN32
- /* Because MinGW does not support Unicode arguments we are going to
- * get them using Windows API. In addition, GLib's option parser
- * doesn't work well with utf-8 strings on Windows, converting
- * them to URIs works around this problem.
- */
- int i;
- gchar *s, *t;
- wchar_t **wargv;
- int wargc;
- wargv = CommandLineToArgvW (GetCommandLineW (), &wargc);
- for (i = 0; i < argc; i++) {
- s = g_utf16_to_utf8 (wargv[i], -1, NULL, NULL, NULL);
- if (s) {
- t = g_filename_to_uri (s, NULL, NULL);
- g_free (s);
- if (t) {
- g_free (argv[i]);
- argv[i] = t;
- }
- }
- }
- LocalFree (wargv);
-#endif
-}
-
-#ifdef G_OS_DARWIN
-static void accel_map_foreach (gpointer data, const gchar *accel_path, guint accel_key, GdkModifierType
-accel_mods, gboolean changed)
-{
- if (accel_mods & GDK_CONTROL_MASK) {
- accel_mods &= ~GDK_CONTROL_MASK;
- accel_mods |= GDK_META_MASK;
- gtk_accel_map_change_entry (accel_path, accel_key, accel_mods, FALSE);
- }
-}
-#endif
-
-void spek_platform_fix_ui (GtkUIManager *ui)
-{
-#ifdef G_OS_DARWIN
- GtkOSXApplication *app = NULL;
- GtkOSXApplicationMenuGroup *group = NULL;
- GtkWidget *menubar = NULL;
- GtkWidget *file_quit = NULL;
- GtkWidget *edit_preferences = NULL;
- GtkWidget *help_about = NULL;
-
- app = g_object_new (GTK_TYPE_OSX_APPLICATION, NULL);
- menubar = gtk_ui_manager_get_widget (ui, "/MenuBar");
- file_quit = gtk_ui_manager_get_widget (ui, "/MenuBar/File/FileQuit");
- edit_preferences = gtk_ui_manager_get_widget (ui, "/MenuBar/Edit/EditPreferences");
- help_about = gtk_ui_manager_get_widget (ui, "/MenuBar/Help/HelpAbout");
-
- gtk_widget_hide (menubar);
- gtk_widget_hide (file_quit);
- gtk_osxapplication_set_menu_bar (app, GTK_MENU_SHELL (menubar));
-
- group = gtk_osxapplication_add_app_menu_group (app);
- gtk_osxapplication_add_app_menu_item (app, group, GTK_MENU_ITEM (help_about));
- group = gtk_osxapplication_add_app_menu_group (app);
- gtk_osxapplication_add_app_menu_item (app, group, GTK_MENU_ITEM (edit_preferences));
-
- gtk_accel_map_foreach (NULL, accel_map_foreach);
-
- gtk_osxapplication_ready (app);
-#endif
-}
-
-gchar *spek_platform_locale_dir () {
- static gchar *locale_dir = NULL;
-
- if (!locale_dir) {
-#ifdef G_OS_WIN32
- gchar *win32_dir;
-
- win32_dir = g_win32_get_package_installation_directory_of_module (NULL);
- locale_dir = g_build_filename (win32_dir, "share", "locale", NULL);
-
- g_free (win32_dir);
-#else
-#ifdef G_OS_DARWIN
- GtkOSXApplication *app = NULL;
- const gchar *res_dir;
-
- app = g_object_new (GTK_TYPE_OSX_APPLICATION, NULL);
- res_dir = gtk_osxapplication_get_resource_path (app);
- locale_dir = g_build_filename (res_dir, "share", "locale", NULL);
-#else
- locale_dir = LOCALEDIR;
-#endif
-#endif
- }
-
- return locale_dir;
-}
-
-void spek_platform_show_uri (const gchar *uri) {
-#ifdef G_OS_WIN32
- /* gtk_show_uri doesn't work on Windows... */
- ShellExecuteA (NULL, "open", uri, "", NULL, SW_SHOWNORMAL);
-#else
-#ifdef G_OS_DARWIN
- /* ...or on OS X */
- CFStringRef str = NULL;
- CFURLRef url = NULL;
-
- str = CFStringCreateWithCString (NULL, uri, kCFStringEncodingASCII);
- url = CFURLCreateWithString (NULL, str, NULL);
- LSOpenCFURLRef (url, NULL);
- CFRelease (url);
- CFRelease (str);
-#else
- gtk_show_uri (NULL, uri, gtk_get_current_event_time (), NULL);
-#endif
-#endif
-}
-
-gchar *spek_platform_read_line (const gchar *uri) {
-#ifdef G_OS_DARWIN
- /* GIO doesn't work on OS X */
- CFStringRef str = NULL;
- CFURLRef url = NULL;
- CFDataRef data = NULL;
- CFIndex length = 0;
- gchar *buf = NULL;
-
- str = CFStringCreateWithCString (NULL, uri, kCFStringEncodingASCII);
- url = CFURLCreateWithString (NULL, str, NULL);
- if (CFURLCreateDataAndPropertiesFromResource (NULL, url, &data, NULL, NULL, NULL)) {
- length = CFDataGetLength (data);
- buf = (gchar *) g_malloc (length + 1);
- CFDataGetBytes (data, CFRangeMake (0, length), (UInt8 *) buf);
- buf[length] = '\0';
- g_strchomp (buf);
- CFRelease (data);
- }
- CFRelease (url);
- CFRelease (str);
- return buf;
-#else
- gchar *line = NULL;
- GFile *file = NULL;
- GFileInputStream *file_stream = NULL;
-
- file = g_file_new_for_uri (uri);
- file_stream = g_file_read (file, NULL, NULL);
- if (file_stream) {
- GDataInputStream *data_stream = NULL;
-
- data_stream = g_data_input_stream_new (G_INPUT_STREAM (file_stream));
- line = g_data_input_stream_read_line (data_stream, NULL, NULL, NULL);
-
- g_object_unref (data_stream);
- g_object_unref (file_stream);
- }
- g_object_unref (file);
- return line;
-#endif
-}
-
-gdouble spek_platform_get_font_scale () {
-#ifdef G_OS_DARWIN
- /* Pango/Quartz fonts are smaller than on X. */
- return 1.4;
-#endif
- return 1.0;
-}
diff --git a/src/spek-platform.cc b/src/spek-platform.cc
@@ -0,0 +1,79 @@
+/* spek-platform.cc
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cstring>
+
+#ifdef OS_OSX
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+#include <wx/filename.h>
+#include <wx/stdpaths.h>
+#include <wx/utils.h>
+
+#include "spek-platform.hh"
+
+void SpekPlatform::init()
+{
+#ifdef OS_OSX
+ ProcessSerialNumber PSN;
+ GetCurrentProcess(&PSN);
+ TransformProcessType(&PSN, kProcessTransformToForegroundApplication);
+#endif
+}
+
+wxString SpekPlatform::config_path(const wxString& app_name)
+{
+#ifdef OS_WIN
+ wxFileName file_name(wxStandardPaths::Get().GetUserConfigDir(), wxEmptyString);
+#else
+ wxFileName file_name(wxGetHomeDir(), wxEmptyString);
+ file_name.AppendDir(wxT(".config"));
+#endif
+ file_name.AppendDir(app_name);
+ file_name.Mkdir(0755, wxPATH_MKDIR_FULL);
+ file_name.SetFullName(wxT("preferences"));
+ return file_name.GetFullPath();
+}
+
+bool SpekPlatform::can_change_language()
+{
+#ifdef OS_UNIX
+ return false;
+#else
+ return true;
+#endif
+}
+
+double SpekPlatform::font_scale()
+{
+#ifdef OS_OSX
+ return 1.3;
+#else
+ return 1.0;
+#endif
+}
+
+char * spek_platform_short_path(const char *path)
+{
+#ifdef OS_WIN
+ wxFileName file_name(wxString(path, wxConvUTF8));
+ return strdup(file_name.GetShortPath().char_str(wxConvFile));
+#endif
+ return NULL;
+}
diff --git a/src/spek-platform.h b/src/spek-platform.h
@@ -1,45 +0,0 @@
-/* spek-platform.h
- *
- * Copyright (C) 2010,2011 Alexander Kojevnikov <alexander@kojevnikov.com>
- *
- * Spek 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 3 of the License, or
- * (at your option) any later version.
- *
- * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __SPEK_PLATFORM_H__
-#define __SPEK_PLATFORM_H__
-
-#include <gtk/gtk.h>
-
-/* Platform-specific initialisation */
-void spek_platform_init ();
-
-/* Convert from UTF-16 to UTF-8 when running on Windows */
-void spek_platform_fix_args (gchar **argv, gint argc);
-
-/* OSX has its own approach to menus and accelerators */
-void spek_platform_fix_ui (GtkUIManager *ui);
-
-/* Platform-specific locale directory */
-gchar *spek_platform_locale_dir ();
-
-/* Open a link in the browser */
-void spek_platform_show_uri (const gchar *uri);
-
-/* Read a line from a uri */
-gchar *spek_platform_read_line (const gchar *uri);
-
-/* Fonts are smaller on OS X */
-gdouble spek_platform_get_font_scale ();
-
-#endif
diff --git a/src/spek-platform.hh b/src/spek-platform.hh
@@ -0,0 +1,52 @@
+/* spek-platform.hh
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_PLATFORM_HH_
+#define SPEK_PLATFORM_HH_
+
+#ifdef __cplusplus
+#include <wx/string.h>
+
+class SpekPlatform
+{
+public:
+ // Platform-specific initialisation code.
+ static void init();
+
+ // Not quite XDG-compatible, but close enough.
+ static wxString config_path(const wxString& app_name);
+
+ // Setting non-default locale under GTK+ is tricky (see e.g. how FileZilla does it). We will
+ // just disable the language setting for GTK+ users and will always use the system locale.
+ static bool can_change_language();
+
+ // Fonts are smaller on OSX.
+ static double font_scale();
+};
+
+extern "C" {
+#endif
+
+// Returns a 8.3 version of the UTF8-encoded path on Windows and NULL on other platforms.
+char * spek_platform_short_path(const char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/spek-preferences-dialog.cc b/src/spek-preferences-dialog.cc
@@ -0,0 +1,114 @@
+/* spek-preferences-dialog.cc
+ *
+ * Copyright (C) 2011-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "spek-preferences.hh"
+
+#include "spek-preferences-dialog.hh"
+
+// List all languages with a decent (e.g. 80%) number of translated
+// strings. Don't translate language names. Keep the first line intact.
+static const char *available_languages[] =
+{
+ "", "",
+ "cs", "Čeština",
+ "da", "Dansk",
+ "de", "Deutsch",
+ "en", "English",
+ "eo", "Esperanto",
+ "es", "Español",
+ "fr", "Français",
+ "it", "Italiano",
+ "ja", "日本語",
+ "nl", "Nederlands",
+ "pl", "Polski",
+ "pt_BR", "Português brasileiro",
+ "ru", "Русский",
+ "sv", "Svenska",
+ "uk", "Українська",
+ "zh_CN", "中文(简体)",
+ "zh_TW", "中文(台灣)",
+ NULL
+};
+
+#ifndef wxCLOSE
+// wxWidgets 2.8 doesn't have wxCLOSE, replace with wxOK for now.
+#define wxCLOSE wxOK
+#endif
+
+#define ID_LANGUAGE (wxID_HIGHEST + 1)
+#define ID_CHECK (wxID_HIGHEST + 2)
+
+BEGIN_EVENT_TABLE(SpekPreferencesDialog, wxDialog)
+ EVT_CHOICE(ID_LANGUAGE, SpekPreferencesDialog::on_language)
+ EVT_CHECKBOX(ID_CHECK, SpekPreferencesDialog::on_check)
+END_EVENT_TABLE()
+
+SpekPreferencesDialog::SpekPreferencesDialog(wxWindow *parent) :
+ wxDialog(parent, -1, _("Preferences"))
+{
+ for (int i = 0; available_languages[i]; ++i) {
+ this->languages.Add(wxString::FromUTF8(available_languages[i]));
+ }
+ this->languages[1] = _("(system default)");
+
+ wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
+ wxSizer *inner_sizer = new wxBoxSizer(wxVERTICAL);
+ sizer->Add(inner_sizer, 1, wxALL, 12);
+
+ // TRANSLATORS: The name of a section in the Preferences dialog.
+ wxStaticText *general_label = new wxStaticText(this, -1, _("General"));
+ wxFont font = general_label->GetFont();
+ font.SetWeight(wxFONTWEIGHT_BOLD);
+ general_label->SetFont(font);
+ inner_sizer->Add(general_label);
+
+ wxSizer *language_sizer = new wxBoxSizer(wxHORIZONTAL);
+ inner_sizer->Add(language_sizer, 0, wxLEFT | wxTOP, 12);
+ wxStaticText *language_label = new wxStaticText(this, -1, _("Language:"));
+ language_sizer->Add(language_label, 0, wxALIGN_CENTER_VERTICAL);
+
+ wxChoice *language_choice = new wxChoice(this, ID_LANGUAGE);
+ language_sizer->Add(language_choice, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 12);
+ int active_index = 0;
+ wxString active_language = SpekPreferences::get().get_language();
+ for (int i = 0; i < this->languages.GetCount(); i += 2) {
+ language_choice->Append(this->languages[i + 1]);
+ if (this->languages[i] == active_language) {
+ active_index = i / 2;
+ }
+ }
+ language_choice->SetSelection(active_index);
+
+ wxCheckBox *check_update = new wxCheckBox(this, ID_CHECK, _("Check for &updates"));
+ inner_sizer->Add(check_update, 0 ,wxLEFT | wxTOP, 12);
+ check_update->SetValue(SpekPreferences::get().get_check_update());
+
+ sizer->Add(CreateButtonSizer(wxCLOSE), 0, wxALIGN_RIGHT | wxBOTTOM | wxRIGHT, 12);
+ sizer->SetSizeHints(this);
+ SetSizer(sizer);
+}
+
+void SpekPreferencesDialog::on_language(wxCommandEvent& event)
+{
+ SpekPreferences::get().set_language(this->languages[event.GetSelection() * 2]);
+}
+
+void SpekPreferencesDialog::on_check(wxCommandEvent& event)
+{
+ SpekPreferences::get().set_check_update(event.IsChecked());
+}
diff --git a/src/spek-preferences-dialog.hh b/src/spek-preferences-dialog.hh
@@ -0,0 +1,38 @@
+/* spek-preferences-dialog.hh
+ *
+ * Copyright (C) 2011-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_PREFERENCES_DIALOG_HH_
+#define SPEK_PREFERENCES_DIALOG_HH_
+
+#include <wx/wx.h>
+
+class SpekPreferencesDialog : public wxDialog
+{
+public:
+ SpekPreferencesDialog(wxWindow *parent);
+
+private:
+ void on_language(wxCommandEvent& event);
+ void on_check(wxCommandEvent& event);
+
+ wxArrayString languages;
+
+ DECLARE_EVENT_TABLE()
+};
+
+#endif
diff --git a/src/spek-preferences-dialog.vala b/src/spek-preferences-dialog.vala
@@ -1,110 +0,0 @@
-/* spek-preferences-dialog.vala
- *
- * Copyright (C) 2011 Alexander Kojevnikov <alexander@kojevnikov.com>
- *
- * Spek 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 3 of the License, or
- * (at your option) any later version.
- *
- * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
- */
-
-using Gtk;
-
-namespace Spek {
- public class PreferencesDialog : Gtk.Dialog {
- // List all languages with a decent (e.g. 80%) number of translated
- // strings. Don't translate language names. Keep the first line intact.
- private static string[,] languages = {
- {"", null},
- {"cs", "Čeština"},
- {"da", "Dansk"},
- {"de", "Deutsch"},
- {"en", "English"},
- {"eo", "Esperanto"},
- {"es", "Español"},
- {"fr", "Français"},
- {"it", "Italiano"},
- {"ja", "日本語"},
- {"nl", "Nederlands"},
- {"pl", "Polski"},
- {"pt_BR", "Português brasileiro"},
- {"ru", "Русский"},
- {"sv", "Svenska"},
- {"uk", "Українська"},
- {"zh_CN", "中文(简体)"},
- {"zh_TW", "中文(台灣)"}
- };
-
- public PreferencesDialog () {
- title = _("Preferences");
- modal = true;
- resizable = false;
- window_position = WindowPosition.CENTER_ON_PARENT;
- languages[0,1] = _("(system default)");
-
- var alignment = new Alignment (0.5f, 0.5f, 1f, 1f);
- alignment.set_padding (12, 12, 12, 12);
- var box = new VBox (false, 0);
-
- var general_box = new VBox (false, 6);
- // TRANSLATORS: The name of a section in the Preferences dialog.
- var general_label = new Label (_("General"));
- var attributes = new Pango.AttrList ();
- attributes.insert (Pango.attr_weight_new (Pango.Weight.BOLD));
- general_label.attributes = attributes;
- general_label.xalign = 0;
- general_box.pack_start (general_label, false, false, 0);
- var general_alignment = new Alignment (0.5f, 0.5f, 1f, 1f);
- general_alignment.left_padding = 12;
- var general_subbox = new VBox (false, 6);
- var language_box = new HBox (false, 12);
- var language_label = new Label.with_mnemonic (_("_Language:"));
- language_box.pack_start (language_label, false, false, 0);
- var language_combo = new ComboBox.text ();
- int active_language = 0;
- var prefs = Preferences.instance;
- for (int i = 0; i < languages.length[0]; i++) {
- language_combo.append_text (languages[i,1]);
- if (languages[i,0] == prefs.language) {
- active_language = i;
- }
- }
- language_combo.active = active_language;
- language_combo.changed.connect (
- () => prefs.language = languages[language_combo.active,0]);
- language_label.mnemonic_widget = language_combo;
- language_box.pack_start (language_combo, false, false, 0);
- general_subbox.pack_start(language_box, false, false, 0);
- var check_update = new CheckButton.with_mnemonic (_("Check for _updates"));
- check_update.active = prefs.check_update;
- check_update.toggled.connect (
- () => prefs.check_update = check_update.active);
- general_subbox.pack_start (check_update, false, false, 0);
- general_alignment.add (general_subbox);
- general_box.pack_start (general_alignment, false, false, 0);
-
- box.pack_start (general_box, false, false, 0);
- alignment.add (box);
- var vbox = (VBox) get_content_area ();
- vbox.pack_start (alignment, false, false, 0);
- vbox.show_all ();
-
- add_button (Stock.CLOSE, ResponseType.CLOSE);
- set_default_response (ResponseType.CLOSE);
- response.connect (on_response);
- }
-
- private void on_response (Dialog dialog, int response_id) {
- Preferences.instance.save ();
- destroy ();
- }
- }
-}
-\ No newline at end of file
diff --git a/src/spek-preferences.cc b/src/spek-preferences.cc
@@ -0,0 +1,100 @@
+/* spek-preferences.cc
+ *
+ * Copyright (C) 2011-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wx/string.h>
+
+#include "spek-platform.hh"
+
+#include "spek-preferences.hh"
+
+SpekPreferences& SpekPreferences::get()
+{
+ static SpekPreferences instance;
+ return instance;
+}
+
+void SpekPreferences::init()
+{
+ if (this->locale) {
+ delete this->locale;
+ }
+ this->locale = new wxLocale();
+
+ int lang = wxLANGUAGE_DEFAULT;
+ wxString code = this->get_language();
+ if (!code.IsEmpty()) {
+ const wxLanguageInfo *info = wxLocale::FindLanguageInfo(code);
+ if (info) {
+ lang = info->Language;
+ }
+ }
+ this->locale->Init(lang);
+ this->locale->AddCatalog(wxT(GETTEXT_PACKAGE));
+}
+
+SpekPreferences::SpekPreferences() : locale(NULL)
+{
+ wxString path = SpekPlatform::config_path(wxT("spek"));
+ this->config = new wxFileConfig(
+ wxEmptyString,
+ wxEmptyString,
+ path,
+ wxEmptyString,
+ wxCONFIG_USE_LOCAL_FILE,
+ wxConvUTF8
+ );
+}
+
+bool SpekPreferences::get_check_update()
+{
+ bool result = true;
+ this->config->Read(wxT("/update/check"), &result);
+ return result;
+}
+
+void SpekPreferences::set_check_update(bool value)
+{
+ this->config->Write(wxT("/update/check"), value);
+ this->config->Flush();
+}
+
+long SpekPreferences::get_last_update()
+{
+ long result = 0;
+ this->config->Read(wxT("/update/last"), &result);
+ return result;
+}
+
+void SpekPreferences::set_last_update(long value)
+{
+ this->config->Write(wxT("/update/last"), value);
+ this->config->Flush();
+}
+
+wxString SpekPreferences::get_language()
+{
+ wxString result(wxT(""));
+ this->config->Read(wxT("/general/language"), &result);
+ return result;
+}
+
+void SpekPreferences::set_language(const wxString& value)
+{
+ this->config->Write(wxT("/general/language"), value);
+ this->config->Flush();
+}
diff --git a/src/spek-preferences.hh b/src/spek-preferences.hh
@@ -0,0 +1,47 @@
+/* spek-preferences.hh
+ *
+ * Copyright (C) 2011-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_PREFERENCES_HH_
+#define SPEK_PREFERENCES_HH_
+
+#include <wx/fileconf.h>
+#include <wx/intl.h>
+
+class SpekPreferences
+{
+public:
+ static SpekPreferences& get();
+
+ void init();
+ bool get_check_update();
+ void set_check_update(bool value);
+ long get_last_update();
+ void set_last_update(long value);
+ wxString get_language();
+ void set_language(const wxString& value);
+
+private:
+ SpekPreferences();
+ SpekPreferences(const SpekPreferences&);
+ void operator=(const SpekPreferences&);
+
+ wxLocale *locale;
+ wxFileConfig *config;
+};
+
+#endif
diff --git a/src/spek-preferences.vala b/src/spek-preferences.vala
@@ -1,102 +0,0 @@
-/* spek-preferences.vala
- *
- * Copyright (C) 2011 Alexander Kojevnikov <alexander@kojevnikov.com>
- *
- * Spek 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 3 of the License, or
- * (at your option) any later version.
- *
- * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
- */
-
-namespace Spek {
- public class Preferences {
- private KeyFile key_file;
- private string file_name;
- private bool can_save = true;
-
- private Preferences () {
- file_name = Path.build_filename (Environment.get_user_config_dir (), "spek");
- if (DirUtils.create_with_parents (file_name, 0755) != 0) {
- this.can_save = false;
- }
- file_name = Path.build_filename (file_name, "preferences");
- this.key_file = new KeyFile ();
- try {
- key_file.load_from_file (file_name, KeyFileFlags.NONE);
- } catch (KeyFileError e) {
- } catch (FileError e) {
- }
- }
-
- ~Preferences () {
- save ();
- }
-
- private static Preferences _instance;
- public static Preferences instance {
- get {
- if (_instance == null) {
- _instance = new Preferences ();
- }
- return _instance;
- }
- }
-
- public void save () {
- if (!can_save) {
- return;
- }
- var output = FileStream.open (file_name, "w+");
- if (output != null) {
- output.puts (key_file.to_data ());
- }
- }
-
- public bool check_update {
- get {
- try {
- return key_file.get_boolean ("update", "check");
- } catch (KeyFileError e) {
- }
- return true;
- }
- set {
- key_file.set_boolean ("update", "check", value);
- }
- }
-
- public int last_update {
- get {
- try {
- return key_file.get_integer ("update", "last");
- } catch (KeyFileError e) {
- }
- return 0;
- }
- set {
- key_file.set_integer ("update", "last", value);
- }
- }
-
- public string language {
- owned get {
- try {
- return key_file.get_string ("general", "language");
- } catch (KeyFileError e) {
- }
- return "";
- }
- set {
- key_file.set_string ("general", "language", value);
- }
- }
- }
-}
-\ No newline at end of file
diff --git a/src/spek-ruler.cc b/src/spek-ruler.cc
@@ -0,0 +1,94 @@
+/* spek-ruler.cc
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cmath>
+
+#include "spek-ruler.hh"
+
+SpekRuler::SpekRuler(
+ int x, int y, Position pos, wxString sample_label,
+ int *factors, int units, double spacing,
+ double scale, double offset, formatter_cb formatter)
+ :
+ x(x), y(y), pos(pos), sample_label(sample_label),
+ factors(factors), units(units), spacing(spacing),
+ scale(scale), offset(offset), formatter(formatter)
+{
+}
+
+void SpekRuler::draw(wxDC& dc)
+{
+ // Mesure the sample label.
+ wxSize size = dc.GetTextExtent(sample_label);
+ int len = this->pos == TOP || this->pos == BOTTOM ? size.GetWidth() : size.GetHeight();
+
+ // Select the factor to use, we want some space between the labels.
+ int factor = 0;
+ for (int i = 0; factors[i]; ++i) {
+ if (fabs(this->scale * factors[i]) >= this->spacing * len) {
+ factor = factors[i];
+ break;
+ }
+ }
+
+ // Draw the ticks.
+ this->draw_tick(dc, 0);
+ this->draw_tick(dc, units);
+
+ if (factor > 0) {
+ for (int tick = factor; tick < units; tick += factor) {
+ if (fabs(this->scale * (units - tick)) < len * 1.2) {
+ break;
+ }
+ this->draw_tick(dc, tick);
+ }
+ }
+}
+
+void SpekRuler::draw_tick(wxDC& dc, int tick)
+{
+ double GAP = 10;
+ double TICK_LEN = 4;
+
+ wxString label = this->formatter(tick);
+ int value = this->pos == TOP || this->pos == BOTTOM ? tick : this->units - tick;
+ double p = this->offset + this->scale * value;
+ wxSize size = dc.GetTextExtent(label);
+ int w = size.GetWidth();
+ int h = size.GetHeight();
+
+ if (this->pos == TOP) {
+ dc.DrawText(label, this->x + p - w / 2, this->y - GAP - h);
+ } else if (this->pos == RIGHT){
+ dc.DrawText(label, this->x + GAP, this->y + p - h / 2);
+ } else if (this->pos == BOTTOM) {
+ dc.DrawText(label, this->x + p - w / 2, this->y + GAP);
+ } else if (this->pos == LEFT){
+ dc.DrawText(label, this->x - w - GAP, this->y + p - h / 2);
+ }
+
+ if (this->pos == TOP) {
+ dc.DrawLine(this->x + p, this->y, this->x + p, this->y - TICK_LEN);
+ } else if (this->pos == RIGHT) {
+ dc.DrawLine(this->x, this->y + p, this->x + TICK_LEN, this->y + p);
+ } else if (this->pos == BOTTOM) {
+ dc.DrawLine(this->x + p, this->y, this->x + p, this->y + TICK_LEN);
+ } else if (this->pos == LEFT) {
+ dc.DrawLine(this->x, this->y + p, this->x - TICK_LEN, this->y + p);
+ }
+}
diff --git a/src/spek-ruler.hh b/src/spek-ruler.hh
@@ -0,0 +1,61 @@
+/* spek-ruler.hh
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_RULER_HH_
+#define SPEK_RULER_HH_
+
+#include <wx/dc.h>
+#include <wx/string.h>
+
+class SpekRuler
+{
+public:
+ enum Position
+ {
+ TOP,
+ RIGHT,
+ BOTTOM,
+ LEFT
+ };
+
+ typedef wxString (*formatter_cb)(int unit);
+
+ SpekRuler(
+ int x, int y, Position pos, wxString sample_label,
+ int *factors, int units, double spacing,
+ double scale, double offset, formatter_cb formatter
+ );
+
+ void draw(wxDC& dc);
+
+protected:
+ void draw_tick(wxDC& dc, int tick);
+
+ int x;
+ int y;
+ Position pos;
+ wxString sample_label;
+ int *factors;
+ int units;
+ double spacing;
+ double scale;
+ double offset;
+ formatter_cb formatter;
+};
+
+#endif
diff --git a/src/spek-ruler.vala b/src/spek-ruler.vala
@@ -1,123 +0,0 @@
-/* spek-ruler.vala
- *
- * Copyright (C) 2010,2011 Alexander Kojevnikov <alexander@kojevnikov.com>
- *
- * Spek 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 3 of the License, or
- * (at your option) any later version.
- *
- * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
- */
-
-using Cairo;
-using Pango;
-
-namespace Spek {
- class Ruler : GLib.Object {
- public enum Position {
- TOP,
- RIGHT,
- BOTTOM,
- LEFT
- }
-
- private Position pos;
- private string sample_label;
- private int[] factors;
- private int units;
- private double spacing;
- private Measure measure;
- private Place place;
- private FormatTick format_tick;
-
- public delegate double Measure (int unit);
- public delegate double Place (double p);
- public delegate string FormatTick (int unit);
-
- public Ruler (
- Position pos, string sample_label,
- int[] factors, int units, double spacing,
- Measure measure, Place place, FormatTick format_tick) {
- this.pos = pos;
- this.sample_label = sample_label;
- this.factors = factors;
- this.units = units;
- this.spacing = spacing;
- this.measure = measure;
- this.place = place;
- this.format_tick = format_tick;
- }
-
- public void draw (Cairo.Context cr, Pango.Layout layout) {
- // Mesure the sample label.
- int w, h;
- layout.set_text (sample_label, -1);
- layout.get_pixel_size (out w, out h);
- var size = pos == Position.TOP || pos == Position.BOTTOM ? w : h;
-
- // Select the factor to use, we want some space between the labels.
- int factor = 0;
- foreach (var f in factors) {
- if (measure (f) >= spacing * size) {
- factor = f;
- break;
- }
- }
-
- // Add the ticks.
- int[] ticks = { 0, units };
- if (factor > 0) {
- for (var tick = factor; tick < units; tick += factor) {
- if (measure (units - tick) < size * 1.2) {
- break;
- }
- ticks += tick;
- }
- // TODO: `ticks = ticks[0:-1]` crashes, file a bug.
- }
-
- // Draw the ticks.
- double GAP = 10;
- double TICK_LEN = 4;
- foreach (var tick in ticks) {
- var label = format_tick (tick);
- var p = place (measure (
- pos == Position.TOP || pos == Position.BOTTOM
- ? tick : units - tick));
- layout.set_text (label, -1);
- layout.get_pixel_size (out w, out h);
- if (pos == Position.TOP) {
- cr.move_to (p - w / 2, -GAP - h);
- } else if (pos == Position.RIGHT){
- cr.move_to (GAP, p + h / 4);
- } else if (pos == Position.BOTTOM) {
- cr.move_to (p - w / 2, GAP + h);
- } else if (pos == Position.LEFT){
- cr.move_to (-w - GAP, p + h / 4);
- }
- cairo_show_layout_line (cr, layout.get_line (0));
- if (pos == Position.TOP) {
- cr.move_to (p, 0);
- cr.rel_line_to (0, -TICK_LEN);
- } else if (pos == Position.RIGHT) {
- cr.move_to (0, p);
- cr.rel_line_to (TICK_LEN, 0);
- } else if (pos == Position.BOTTOM) {
- cr.move_to (p, 0);
- cr.rel_line_to (0, TICK_LEN);
- } else if (pos == Position.LEFT) {
- cr.move_to (0, p);
- cr.rel_line_to (-TICK_LEN, 0);
- }
- cr.stroke ();
- }
- }
- }
-}
-\ No newline at end of file
diff --git a/src/spek-spectrogram.cc b/src/spek-spectrogram.cc
@@ -0,0 +1,371 @@
+/* spek-spectrogram.cc
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cmath>
+#include <wx/dcbuffer.h>
+
+#include "spek-audio.h"
+#include "spek-audio-desc.hh"
+#include "spek-events.hh"
+#include "spek-palette.h"
+#include "spek-pipeline.h"
+#include "spek-platform.hh"
+#include "spek-ruler.hh"
+
+#include "spek-spectrogram.hh"
+
+BEGIN_EVENT_TABLE(SpekSpectrogram, wxPanel)
+ EVT_IDLE(SpekSpectrogram::on_idle)
+ EVT_PAINT(SpekSpectrogram::on_paint)
+ EVT_SIZE(SpekSpectrogram::on_size)
+ SPEK_EVT_HAVE_SAMPLE(SpekSpectrogram::on_have_sample)
+END_EVENT_TABLE()
+
+enum
+{
+ THRESHOLD = -92,
+ NFFT = 2048,
+ BANDS = NFFT / 2 + 1,
+ LPAD = 60,
+ TPAD = 60,
+ RPAD = 90,
+ BPAD = 40,
+ GAP = 10,
+ RULER = 10,
+};
+
+// Forward declarations.
+static wxString trim(wxDC& dc, const wxString& s, int length, bool trim_end);
+
+SpekSpectrogram::SpekSpectrogram(wxFrame *parent) :
+ wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE),
+ pipeline(NULL),
+ properties(NULL),
+ palette(RULER, BANDS),
+ image(1, 1),
+ prev_width(-1)
+{
+ SetBackgroundStyle(wxBG_STYLE_CUSTOM);
+
+ // Pre-draw the palette.
+ for (int y = 0; y < BANDS; y++) {
+ uint32_t color = spek_palette_spectrum(y / (double) BANDS);
+ this->palette.SetRGB(
+ wxRect(0, BANDS - y - 1, RULER, 1),
+ color >> 16,
+ (color >> 8) & 0xFF,
+ color & 0xFF
+ );
+ }
+}
+
+SpekSpectrogram::~SpekSpectrogram()
+{
+ this->stop();
+}
+
+void SpekSpectrogram::open(const wxString& path)
+{
+ this->path = path;
+ start();
+}
+
+void SpekSpectrogram::save(const wxString& path)
+{
+ wxSize size = GetClientSize();
+ wxBitmap bitmap(size.GetWidth(), size.GetHeight());
+ wxMemoryDC dc(bitmap);
+ render(dc);
+ bitmap.SaveFile(path, wxBITMAP_TYPE_PNG);
+}
+
+void SpekSpectrogram::on_idle(wxIdleEvent& evt)
+{
+ Update();
+}
+
+void SpekSpectrogram::on_paint(wxPaintEvent& evt)
+{
+ wxAutoBufferedPaintDC dc(this);
+ render(dc);
+}
+
+void SpekSpectrogram::on_size(wxSizeEvent& evt)
+{
+ wxSize size = GetClientSize();
+ bool width_changed = this->prev_width != size.GetWidth();
+ this->prev_width = size.GetWidth();
+
+ if (!this->path.IsEmpty() && width_changed) {
+ start();
+ }
+}
+
+void SpekSpectrogram::on_have_sample(SpekHaveSampleEvent& event)
+{
+ static double log10_threshold = log10(-THRESHOLD);
+ int bands = event.get_bands();
+ int sample = event.get_sample();
+ const float *values = event.get_values();
+
+ // TODO: check image size, quit if wrong.
+ for (int y = 0; y < bands; y++) {
+ double level = log10(1.0 - THRESHOLD + values[y]) / log10_threshold;
+ if (level > 1.0) level = 1.0;
+ uint32_t color = spek_palette_spectrum(level);
+ this->image.SetRGB(
+ sample,
+ bands - y - 1,
+ color >> 16,
+ (color >> 8) & 0xFF,
+ color & 0xFF
+ );
+ }
+
+ // TODO: refresh only one pixel column
+ this->Refresh();
+}
+
+static wxString time_formatter(int unit)
+{
+ // TODO: i18n
+ return wxString::Format(wxT("%d:%02d"), unit / 60, unit % 60);
+}
+
+static wxString freq_formatter(int unit)
+{
+ return wxString::Format(_("%d kHz"), unit / 1000);
+}
+
+static wxString density_formatter(int unit)
+{
+ return wxString::Format(_("%d dB"), -unit);
+}
+
+void SpekSpectrogram::render(wxDC& dc)
+{
+ wxSize size = GetClientSize();
+ int w = size.GetWidth();
+ int h = size.GetHeight();
+
+ // Initialise.
+ dc.SetBackground(*wxBLACK_BRUSH);
+ dc.SetBackgroundMode(wxTRANSPARENT);
+ dc.SetPen(*wxWHITE_PEN);
+ dc.SetBrush(*wxTRANSPARENT_BRUSH);
+ dc.SetTextForeground(wxColour(255, 255, 255));
+ wxFont normal_font = wxFont(
+ (int)round(9 * SpekPlatform::font_scale()),
+ wxFONTFAMILY_SWISS,
+ wxFONTSTYLE_NORMAL,
+ wxFONTWEIGHT_NORMAL
+ );
+ wxFont large_font = wxFont(normal_font);
+ large_font.SetPointSize((int)round(10 * SpekPlatform::font_scale()));
+ large_font.SetWeight(wxFONTWEIGHT_BOLD);
+ wxFont small_font = wxFont(normal_font);
+ small_font.SetPointSize((int)round(8 * SpekPlatform::font_scale()));
+ dc.SetFont(normal_font);
+ int normal_height = dc.GetTextExtent(wxT("dummy")).GetHeight();
+ dc.SetFont(large_font);
+ int large_height = dc.GetTextExtent(wxT("dummy")).GetHeight();
+ dc.SetFont(small_font);
+ int small_height = dc.GetTextExtent(wxT("dummy")).GetHeight();
+
+ // Clean the background.
+ dc.Clear();
+
+ // Spek version
+ dc.SetFont(large_font);
+ wxString package_name(wxT(PACKAGE_NAME));
+ dc.DrawText(
+ package_name,
+ w - RPAD + GAP,
+ TPAD - 2 * GAP - normal_height - large_height
+ );
+ int package_name_width = dc.GetTextExtent(package_name + wxT(" ")).GetWidth();
+ dc.SetFont(small_font);
+ dc.DrawText(
+ wxT(PACKAGE_VERSION),
+ w - RPAD + GAP + package_name_width,
+ TPAD - 2 * GAP - normal_height - small_height
+ );
+
+ if (this->image.GetHeight() > 1) {
+ // Draw the spectrogram.
+ wxBitmap bmp(this->image.Scale(w - LPAD - RPAD, h - TPAD - BPAD));
+ dc.DrawBitmap(bmp, LPAD, TPAD);
+
+ // File name.
+ dc.SetFont(large_font);
+ dc.DrawText(
+ trim(dc, this->path, w - LPAD - RPAD, false),
+ LPAD,
+ TPAD - 2 * GAP - normal_height - large_height
+ );
+
+ // File properties.
+ dc.SetFont(normal_font);
+ dc.DrawText(
+ trim(dc, this->desc, w - LPAD - RPAD, true),
+ LPAD,
+ TPAD - GAP - normal_height
+ );
+
+ // Prepare to draw the rulers.
+ dc.SetFont(small_font);
+
+ // Time ruler.
+ double duration = this->properties->duration;
+ int time_factors[] = {1, 2, 5, 10, 20, 30, 1*60, 2*60, 5*60, 10*60, 20*60, 30*60, 0};
+ SpekRuler time_ruler(
+ LPAD,
+ h - BPAD,
+ SpekRuler::BOTTOM,
+ // TODO: i18n
+ wxT("00:00"),
+ time_factors,
+ (int)duration,
+ 1.5,
+ (w - LPAD - RPAD) / duration,
+ 0.0,
+ time_formatter
+ );
+ time_ruler.draw(dc);
+
+ // Frequency ruler.
+ int freq = this->properties->sample_rate / 2;
+ int freq_factors[] = {1000, 2000, 5000, 10000, 20000, 0};
+ SpekRuler freq_ruler(
+ LPAD,
+ TPAD,
+ SpekRuler::LEFT,
+ // TRANSLATORS: keep "00" unchanged, it's used to calc the text width
+ _("00 kHz"),
+ freq_factors,
+ freq,
+ 3.0,
+ (h - TPAD - BPAD) / (double)freq,
+ 0.0,
+ freq_formatter
+ );
+ freq_ruler.draw(dc);
+ }
+
+ // Border around the spectrogram.
+ dc.DrawRectangle(LPAD, TPAD, w - LPAD - RPAD, h - TPAD - BPAD);
+
+ // The palette.
+ wxBitmap bmp(this->palette.Scale(RULER, h - TPAD - BPAD + 1));
+ dc.DrawBitmap(bmp, w - RPAD + GAP, TPAD);
+
+ // Prepare to draw the ruler.
+ dc.SetFont(small_font);
+
+ // Spectral density.
+ int density_factors[] = {1, 2, 5, 10, 20, 50, 0};
+ SpekRuler density_ruler(
+ w - RPAD + GAP + RULER,
+ TPAD,
+ SpekRuler::RIGHT,
+ // TRANSLATORS: keep "-00" unchanged, it's used to calc the text width
+ _("-00 dB"),
+ density_factors,
+ -THRESHOLD,
+ 3.0,
+ (h - TPAD - BPAD) / (double)THRESHOLD,
+ h - TPAD - BPAD,
+ density_formatter
+ );
+ density_ruler.draw(dc);
+}
+
+static void pipeline_cb(int sample, float *values, void *cb_data)
+{
+ SpekHaveSampleEvent event(BANDS, sample, values, false);
+ SpekSpectrogram *s = (SpekSpectrogram *)cb_data;
+ wxPostEvent(s, event);
+}
+
+void SpekSpectrogram::start()
+{
+ this->stop();
+
+ // The number of samples is the number of pixels available for the image.
+ // The number of bands is fixed, FFT results are very different for
+ // different values but we need some consistency.
+ wxSize size = GetClientSize();
+ int samples = size.GetWidth() - LPAD - RPAD;
+ if (samples > 0) {
+ this->image.Create(samples, BANDS);
+ this->pipeline = spek_pipeline_open(
+ this->path.utf8_str(),
+ BANDS,
+ samples,
+ THRESHOLD,
+ pipeline_cb,
+ this
+ );
+ spek_pipeline_start(this->pipeline);
+ this->properties = spek_pipeline_properties(this->pipeline);
+ this->desc = spek_audio_desc(this->properties);
+ } else {
+ this->image.Create(1, 1);
+ }
+
+ Refresh();
+}
+
+void SpekSpectrogram::stop()
+{
+ if (this->pipeline) {
+ spek_pipeline_close(this->pipeline);
+ this->pipeline = NULL;
+ this->properties = NULL;
+ }
+}
+
+// Trim `s` so that it fits into `length`.
+static wxString trim(wxDC& dc, const wxString& s, int length, bool trim_end)
+{
+ if (length <= 0) {
+ return wxEmptyString;
+ }
+
+ // Check if the entire string fits.
+ wxSize size = dc.GetTextExtent(s);
+ if (size.GetWidth() <= length) {
+ return s;
+ }
+
+ // Binary search FTW!
+ wxString fix(wxT("..."));
+ int i = 0;
+ int k = s.length();
+ while (k - i > 1) {
+ int j = (i + k) / 2;
+ size = dc.GetTextExtent(trim_end ? s.substr(0, j) + fix : fix + s.substr(j));
+ if (trim_end != (size.GetWidth() > length)) {
+ i = j;
+ } else {
+ k = j;
+ }
+ }
+
+ return trim_end ? s.substr(0, i) + fix : fix + s.substr(k);
+}
diff --git a/src/spek-spectrogram.hh b/src/spek-spectrogram.hh
@@ -0,0 +1,57 @@
+/* spek-spectrogram.hh
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_SPECTROGRAM_HH_
+#define SPEK_SPECTROGRAM_HH_
+
+#include <wx/wx.h>
+
+class SpekHaveSampleEvent;
+struct spek_audio_properties;
+struct spek_pipeline;
+
+class SpekSpectrogram : public wxPanel
+{
+public:
+ SpekSpectrogram(wxFrame *parent);
+ ~SpekSpectrogram();
+ void open(const wxString& path);
+ void save(const wxString& path);
+
+private:
+ void on_idle(wxIdleEvent& evt);
+ void on_paint(wxPaintEvent& evt);
+ void on_size(wxSizeEvent& evt);
+ void on_have_sample(SpekHaveSampleEvent& evt);
+ void render(wxDC& dc);
+
+ void start();
+ void stop();
+
+ spek_pipeline *pipeline;
+ const spek_audio_properties *properties;
+ wxString path;
+ wxString desc;
+ wxImage palette;
+ wxImage image;
+ int prev_width;
+
+ DECLARE_EVENT_TABLE()
+};
+
+#endif
diff --git a/src/spek-spectrogram.vala b/src/spek-spectrogram.vala
@@ -1,319 +0,0 @@
-/* spek-spectrogram.vala
- *
- * Copyright (C) 2010-2011 Alexander Kojevnikov <alexander@kojevnikov.com>
- *
- * Spek 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 3 of the License, or
- * (at your option) any later version.
- *
- * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
- */
-
-using Cairo;
-using Gdk;
-using Gtk;
-using Pango;
-
-namespace Spek {
- class Spectrogram : DrawingArea {
-
- public string file_name { get; private set; }
- private Pipeline pipeline;
- private string info;
- private const int THRESHOLD = -92;
- private const int NFFT = 2048;
- private const int BANDS = NFFT / 2 + 1;
-
- private ImageSurface image;
- private ImageSurface palette;
-
- private const int LPAD = 60;
- private const int TPAD = 60;
- private const int RPAD = 80;
- private const int BPAD = 40;
- private const int GAP = 10;
- private const int RULER = 10;
- private double FONT_SCALE = Platform.get_font_scale ();
-
- public Spectrogram () {
- // Pre-draw the palette.
- palette = new ImageSurface (Format.RGB24, RULER, BANDS);
- for (int y = 0; y < BANDS; y++) {
- var color = get_color (y / (double) BANDS);
- for (int x = 0; x < RULER; x++) {
- put_pixel (palette, x, y, color);
- }
- }
- show_all ();
- }
-
- public void open (string file_name) {
- this.file_name = file_name;
- this.info = "";
-
- start ();
- }
-
- public void save (string file_name) {
- Allocation allocation;
- get_allocation (out allocation);
- var surface = new ImageSurface (Format.RGB24, allocation.width, allocation.height);
- draw (new Cairo.Context (surface));
- surface.write_to_png (file_name);
- }
-
- private void start () {
- if (pipeline != null) {
- pipeline.stop ();
- }
-
- // The number of samples is the number of pixels available for the image.
- // The number of bands is fixed, FFT results are very different for
- // different values but we need some consistency.
- Allocation allocation;
- get_allocation (out allocation);
- int samples = allocation.width - LPAD - RPAD;
- if (samples > 0) {
- image = new ImageSurface (Format.RGB24, samples, BANDS);
- pipeline = new Pipeline (file_name, BANDS, samples, THRESHOLD, data_cb);
- pipeline.start ();
- info = pipeline.description;
- } else {
- image = null;
- pipeline = null;
- }
-
- queue_draw ();
- }
-
- private int prev_width = -1;
- protected override void size_allocate (Gdk.Rectangle allocation) {
- base.size_allocate (allocation);
-
- bool width_changed = prev_width != allocation.width;
- prev_width = allocation.width;
-
- if (file_name != null && width_changed) {
- start ();
- }
- }
-
- private double log10_threshold = Math.log10 (-THRESHOLD);
- private void data_cb (int sample, float[] values) {
- for (int y = 0; y < BANDS; y++) {
- var level = double.min (
- 1.0, Math.log10 (1.0 - THRESHOLD + values[y]) / log10_threshold);
- put_pixel (image, sample, y, get_color (level));
- }
- Idle.add (() => { queue_draw (); return false; });
- }
-
- protected override bool expose_event (EventExpose event) {
- var window = get_window ();
- var cr = cairo_create (window);
-
- // Clip to the exposed area.
- cr.rectangle (event.area.x, event.area.y, event.area.width, event.area.height);
- cr.clip ();
-
- draw (cr);
- return true;
- }
-
- private void draw (Cairo.Context cr) {
- Allocation allocation;
- get_allocation (out allocation);
- double w = allocation.width;
- double h = allocation.height;
- int text_width, text_height;
-
- // Clean the background.
- cr.set_source_rgb (0, 0, 0);
- cr.paint ();
-
- // Spek version
- cr.set_source_rgb (1, 1, 1);
- var layout = cairo_create_layout (cr);
- layout.set_font_description (FontDescription.from_string (
- "Sans " + (9 * FONT_SCALE).to_string ()));
- layout.set_width (RPAD * Pango.SCALE);
- layout.set_text ("dummy", -1);
- layout.get_pixel_size (out text_width, out text_height);
- int line_height = text_height;
- layout.set_font_description (FontDescription.from_string (
- "Sans Bold " + (10 * FONT_SCALE).to_string ()));
- layout.set_text (Config.PACKAGE_NAME + " ", -1);
- layout.get_pixel_size (out text_width, out text_height);
- cr.move_to (w - RPAD + GAP, TPAD - 2 * GAP - line_height);
- cairo_show_layout_line (cr, layout.get_line (0));
- layout.set_font_description (FontDescription.from_string (
- "Sans " + (9 * FONT_SCALE).to_string ()));
- layout.set_text (Config.PACKAGE_VERSION, -1);
- cr.rel_move_to (text_width, 0);
- cairo_show_layout_line (cr, layout.get_line (0));
-
- if (image != null) {
- // Draw the spectrogram.
- cr.translate (LPAD, h - BPAD);
- cr.scale (1, -(h - TPAD - BPAD) / image.get_height ());
- cr.set_source_surface (image, 0, 0);
- cr.paint ();
- cr.identity_matrix ();
-
- // Prepare to draw the rulers.
- cr.set_source_rgb (1, 1, 1);
- cr.set_line_width (1);
- cr.set_antialias (Antialias.NONE);
- layout.set_font_description (FontDescription.from_string (
- "Sans " + (8 * FONT_SCALE).to_string ()));
- layout.set_width (-1);
-
- // Time ruler.
- var duration_seconds = (int) pipeline.duration;
- var time_ruler = new Ruler (
- Ruler.Position.BOTTOM,
- // TODO: i18n
- "00:00",
- {1, 2, 5, 10, 20, 30, 1*60, 2*60, 5*60, 10*60, 20*60, 30*60},
- duration_seconds,
- 1.5,
- unit => (w - LPAD - RPAD) * unit / duration_seconds,
- p => p,
- // TODO: i18n
- unit => "%d:%02d".printf (unit / 60, unit % 60));
- cr.translate (LPAD, h - BPAD);
- time_ruler.draw (cr, layout);
- cr.identity_matrix ();
-
- // Frequency ruler.
- var freq = pipeline.sample_rate / 2;
- var rate_ruler = new Ruler (
- Ruler.Position.LEFT,
- // TRANSLATORS: keep "00" unchanged, it's used to calc the text width
- _("00 kHz"),
- {1000, 2000, 5000, 10000, 20000},
- freq,
- 3.0,
- unit => (h - TPAD - BPAD) * unit / freq,
- p => p,
- unit => _("%d kHz").printf (unit / 1000));
- cr.translate (LPAD, TPAD);
- rate_ruler.draw (cr, layout);
- cr.identity_matrix ();
-
- // File properties.
- cr.move_to (LPAD, TPAD - GAP);
- layout.set_font_description (FontDescription.from_string (
- "Sans " + (9 * FONT_SCALE).to_string ()));
- layout.set_width ((int) (w - LPAD - RPAD) * Pango.SCALE);
- layout.set_ellipsize (EllipsizeMode.END);
- layout.set_text (info, -1);
- cairo_show_layout_line (cr, layout.get_line (0));
- layout.get_pixel_size (out text_width, out text_height);
-
- // File name.
- cr.move_to (LPAD, TPAD - 2 * GAP - text_height);
- layout.set_font_description (FontDescription.from_string (
- "Sans Bold " + (10 * FONT_SCALE).to_string ()));
- layout.set_width ((int) (w - LPAD - RPAD) * Pango.SCALE);
- layout.set_ellipsize (EllipsizeMode.START);
- layout.set_text (file_name, -1);
- cairo_show_layout_line (cr, layout.get_line (0));
- }
-
- // Border around the spectrogram.
- cr.set_source_rgb (1, 1, 1);
- cr.set_line_width (1);
- cr.set_antialias (Antialias.NONE);
- cr.rectangle (LPAD, TPAD, w - LPAD - RPAD, h - TPAD - BPAD);
- cr.stroke ();
-
- // The palette.
- cr.translate (w - RPAD + GAP, h - BPAD);
- cr.scale (1, -(h - TPAD - BPAD + 1) / palette.get_height ());
- cr.set_source_surface (palette, 0, 0);
- cr.paint ();
- cr.identity_matrix ();
-
- // Prepare to draw the ruler.
- cr.set_source_rgb (1, 1, 1);
- cr.set_line_width (1);
- cr.set_antialias (Antialias.NONE);
- layout.set_font_description (FontDescription.from_string (
- "Sans " + (8 * FONT_SCALE).to_string ()));
- layout.set_width (-1);
-
- // Spectral density.
- var density_ruler = new Ruler (
- Ruler.Position.RIGHT,
- // TRANSLATORS: keep "-00" unchanged, it's used to calc the text width
- _("-00 dB"),
- {1, 2, 5, 10, 20, 50},
- -THRESHOLD,
- 3.0,
- unit => -(h - TPAD - BPAD) * unit / THRESHOLD,
- p => h - TPAD - BPAD - p,
- unit => _("%d dB").printf (-unit));
- cr.translate (w - RPAD + GAP + RULER, TPAD);
- density_ruler.draw (cr, layout);
- cr.identity_matrix ();
- }
-
- private void put_pixel (ImageSurface surface, int x, int y, uint32 color) {
- var i = y * surface.get_stride () + x * 4;
- unowned uchar[] data = surface.get_data ();
-
- // Translate uchar* to uint32* to avoid dealing with endianness.
- uint32 *p = (uint32 *) (&data[i]);
- *p = color;
- }
-
- // Modified version of Dan Bruton's algorithm:
- // http://www.physics.sfasu.edu/astro/color/spectra.html
- private uint32 get_color (double level) {
- level *= 0.6625;
- double r = 0.0, g = 0.0, b = 0.0;
- if (level >= 0 && level < 0.15) {
- r = (0.15 - level) / (0.15 + 0.075);
- g = 0.0;
- b = 1.0;
- } else if (level >= 0.15 && level < 0.275) {
- r = 0.0;
- g = (level - 0.15) / (0.275 - 0.15);
- b = 1.0;
- } else if (level >= 0.275 && level < 0.325) {
- r = 0.0;
- g = 1.0;
- b = (0.325 - level) / (0.325 - 0.275);
- } else if (level >= 0.325 && level < 0.5) {
- r = (level - 0.325) / (0.5 - 0.325);
- g = 1.0;
- b = 0.0;
- } else if (level >= 0.5 && level < 0.6625) {
- r = 1.0;
- g = (0.6625 - level) / (0.6625 - 0.5f);
- b = 0.0;
- }
-
- // Intensity correction.
- double cf = 1.0;
- if (level >= 0.0 && level < 0.1) {
- cf = level / 0.1;
- }
- cf *= 255.0;
-
- // Pack RGB values into Cairo-happy format.
- uint32 rr = (uint32) (r * cf + 0.5);
- uint32 gg = (uint32) (g * cf + 0.5);
- uint32 bb = (uint32) (b * cf + 0.5);
- return (rr << 16) + (gg << 8) + bb;
- }
- }
-}
diff --git a/src/spek-window.cc b/src/spek-window.cc
@@ -0,0 +1,385 @@
+/* spek-window.cc
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+
+#include <wx/aboutdlg.h>
+#include <wx/artprov.h>
+#include <wx/dnd.h>
+#include <wx/filename.h>
+#include <wx/protocol/http.h>
+#include <wx/sstream.h>
+
+#include "spek-preferences-dialog.hh"
+#include "spek-preferences.hh"
+#include "spek-spectrogram.hh"
+
+#include "spek-window.hh"
+
+DECLARE_EVENT_TYPE(SPEK_NOTIFY_EVENT, -1)
+DEFINE_EVENT_TYPE(SPEK_NOTIFY_EVENT)
+
+BEGIN_EVENT_TABLE(SpekWindow, wxFrame)
+ EVT_MENU(wxID_OPEN, SpekWindow::on_open)
+ EVT_MENU(wxID_SAVE, SpekWindow::on_save)
+ EVT_MENU(wxID_EXIT, SpekWindow::on_exit)
+ EVT_MENU(wxID_PREFERENCES, SpekWindow::on_preferences)
+ EVT_MENU(wxID_ABOUT, SpekWindow::on_about)
+ EVT_COMMAND(-1, SPEK_NOTIFY_EVENT, SpekWindow::on_notify)
+END_EVENT_TABLE()
+
+// Forward declarations.
+static void * check_version(void *);
+
+class SpekDropTarget : public wxFileDropTarget
+{
+public:
+ SpekDropTarget(SpekWindow *window) : wxFileDropTarget(), window(window) {}
+
+protected:
+ virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames){
+ if (filenames.GetCount() == 1) {
+ window->open(filenames[0]);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ SpekWindow *window;
+};
+
+SpekWindow::SpekWindow(const wxString& path) :
+ path(path), wxFrame(NULL, -1, wxEmptyString)
+{
+ this->description = _("Spek - Acoustic Spectrum Analyser");
+ SetTitle(this->description);
+ // TODO: test on all platforms
+ //SetIcon(wxIcon(wxT("spek")));
+
+ wxMenuBar *menu = new wxMenuBar();
+
+ wxMenu *menu_file = new wxMenu();
+ wxMenuItem *menu_file_open = new wxMenuItem(menu_file, wxID_OPEN);
+ menu_file->Append(menu_file_open);
+ wxMenuItem *menu_file_save = new wxMenuItem(menu_file, wxID_SAVE);
+ menu_file->Append(menu_file_save);
+ menu_file->AppendSeparator();
+ menu_file->Append(wxID_EXIT);
+ menu->Append(menu_file, _("&File")); // TODO: stock text
+
+ wxMenu *menu_edit = new wxMenu();
+ wxMenuItem *menu_edit_prefs = new wxMenuItem(menu_edit, wxID_PREFERENCES);
+ // TODO: check if this is needed
+ menu_edit_prefs->SetItemLabel(menu_edit_prefs->GetItemLabelText() + wxT("\tCtrl+E"));
+ menu_edit->Append(menu_edit_prefs);
+ menu->Append(menu_edit, _("&Edit")); // TODO: stock text
+
+ wxMenu *menu_help = new wxMenu();
+ wxMenuItem *menu_help_about = new wxMenuItem(menu_help, wxID_ABOUT);
+ // TODO: check if this is needed
+ menu_help_about->SetItemLabel(menu_help_about->GetItemLabelText() + wxT("\tF1"));
+ menu_help->Append(menu_help_about);
+ menu->Append(menu_help, _("&Help")); // TODO: stock text
+
+ SetMenuBar(menu);
+
+ wxToolBar *toolbar = CreateToolBar();
+ // TODO: bundled file open/save icons suck, provide our own (tango?)
+ toolbar->AddTool(
+ wxID_OPEN,
+ menu_file_open->GetItemLabelText(),
+ wxArtProvider::GetBitmap(wxART_FILE_OPEN)
+ );
+ toolbar->AddTool(
+ wxID_SAVE,
+ menu_file_save->GetItemLabelText(),
+ wxArtProvider::GetBitmap(wxART_FILE_SAVE)
+ );
+ toolbar->Realize();
+
+ wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
+
+ // wxInfoBar is too limited, construct a custom one.
+ wxPanel *info_bar = new wxPanel(this);
+ info_bar->Hide();
+ info_bar->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT));
+ info_bar->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
+ wxSizer *info_sizer = new wxBoxSizer(wxHORIZONTAL);
+ wxStaticText *label = new wxStaticText(
+ info_bar, -1, _("A new version of Spek is available, click to download."));
+ label->SetCursor(*new wxCursor(wxCURSOR_HAND));
+ label->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(SpekWindow::on_visit));
+ // This second Connect() handles clicks on the border
+ info_bar->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(SpekWindow::on_visit));
+ info_sizer->Add(label, 1, wxALIGN_CENTER_VERTICAL | wxALL, 6);
+ // TODO: gtk-close won't work on win/osx
+ wxBitmapButton *button = new wxBitmapButton(
+ info_bar, -1, wxArtProvider::GetBitmap(wxT("gtk-close")),
+ wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
+ button->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SpekWindow::on_close));
+ info_sizer->Add(button, 0, wxALIGN_CENTER_VERTICAL);
+ info_bar->SetSizer(info_sizer);
+ sizer->Add(info_bar, 0, wxEXPAND);
+
+ this->spectrogram = new SpekSpectrogram(this);
+ sizer->Add(this->spectrogram, 1, wxEXPAND);
+
+ this->cur_dir = wxGetHomeDir();
+
+ if (!path.IsEmpty()) {
+ open(path);
+ }
+
+ SetDropTarget(new SpekDropTarget(this));
+
+ SetSizer(sizer);
+
+ pthread_t thread;
+ pthread_create(&thread, NULL, &check_version, this);
+}
+
+void SpekWindow::open(const wxString& path)
+{
+ wxFileName file_name(path);
+ if (file_name.FileExists()) {
+ this->path = path;
+ wxString full_name = file_name.GetFullName();
+ // TRANSLATORS: window title, %s is replaced with the file name
+ wxString title = wxString::Format(_("Spek - %s"), full_name.c_str());
+ // TODO: make sure the above works on all platforms, both in x32 and x64.
+ SetTitle(title);
+
+ this->spectrogram->open(path);
+ }
+}
+
+// TODO: s/audio/media/
+static const char *audio_extensions[] = {
+ "3gp",
+ "aac",
+ "aif",
+ "aifc",
+ "aiff",
+ "amr",
+ "awb",
+ "ape",
+ "au",
+ "dts",
+ "flac",
+ "gsm",
+ "m4a",
+ "m4p",
+ "mp3",
+ "mp4",
+ "mp+",
+ "mpc",
+ "mpp",
+ "oga",
+ "ogg",
+ "ra",
+ "ram",
+ "snd",
+ "wav",
+ "wma",
+ "wv",
+ NULL
+};
+
+void SpekWindow::on_open(wxCommandEvent& event)
+{
+ static wxString filters = wxEmptyString;
+ static int filter_index = 1;
+
+ if (filters.IsEmpty()) {
+ filters.Alloc(1024);
+ filters += _("All files");
+ filters += wxT("|*.*|");
+ filters += _("Audio files");
+ filters += wxT("|");
+ for (int i = 0; audio_extensions[i]; ++i) {
+ if (i) {
+ filters += wxT(";");
+ }
+ filters += wxT("*.");
+ filters += wxString::FromAscii(audio_extensions[i]);
+ }
+ filters.Shrink();
+ }
+
+ wxFileDialog *dlg = new wxFileDialog(
+ this,
+ _("Open File"),
+ this->cur_dir,
+ wxEmptyString,
+ filters,
+ wxFD_OPEN
+ );
+ dlg->SetFilterIndex(filter_index);
+
+ if (dlg->ShowModal() == wxID_OK) {
+ this->cur_dir = dlg->GetDirectory();
+ filter_index = dlg->GetFilterIndex();
+ open(dlg->GetPath());
+ }
+
+ dlg->Destroy();
+}
+
+void SpekWindow::on_save(wxCommandEvent& event)
+{
+ static wxString filters = wxEmptyString;
+
+ if (filters.IsEmpty()) {
+ filters = _("PNG images");
+ filters += wxT("|*.png");
+ }
+
+ wxFileDialog *dlg = new wxFileDialog(
+ this,
+ _("Save Spectrogram"),
+ this->cur_dir,
+ wxEmptyString,
+ filters,
+ wxFD_SAVE
+ );
+
+ // Suggested name is <file_name>.png
+ wxString name = _("Untitled");
+ if (!this->path.IsEmpty()) {
+ wxFileName file_name(this->path);
+ name = file_name.GetFullName();
+ }
+ name += wxT(".png");
+ dlg->SetFilename(name);
+
+ if (dlg->ShowModal() == wxID_OK) {
+ this->cur_dir = dlg->GetDirectory();
+ this->spectrogram->save(dlg->GetPath());
+ }
+
+ dlg->Destroy();
+}
+
+void SpekWindow::on_exit(wxCommandEvent& event)
+{
+ Close(true);
+}
+
+void SpekWindow::on_preferences(wxCommandEvent& event)
+{
+ SpekPreferencesDialog dlg(this);
+ dlg.ShowModal();
+}
+
+void SpekWindow::on_about(wxCommandEvent& event)
+{
+ wxAboutDialogInfo info;
+ info.AddDeveloper(wxT("Alexander Kojevnikov"));
+ info.AddDeveloper(wxT("Fabian Deutsch"));
+ info.AddDeveloper(wxT("Jonathan Gonzalez V"));
+ info.AddDeveloper(wxT("Stefan Kost"));
+ info.AddDeveloper(wxT("Thibault North"));
+ info.AddArtist(wxT("Olga Vasylevska"));
+ // TRANSLATORS: Add your name here
+ wxString translator = _("translator-credits");
+ if (translator != wxT("translator-credits")) {
+ info.AddTranslator(translator);
+ }
+ info.SetName(wxT("Spek"));
+ info.SetVersion(wxT(PACKAGE_VERSION));
+ info.SetCopyright(_("Copyright (c) 2010-2012 Alexander Kojevnikov and contributors"));
+ info.SetDescription(this->description);
+#ifdef OS_UNIX
+ info.SetWebSite(wxT("http://spek-project.org/"), _("Spek Website"));
+ // TODO
+ // info.SetIcon();
+#endif
+ wxAboutBox(info);
+}
+
+void SpekWindow::on_notify(wxCommandEvent& event)
+{
+ this->GetSizer()->Show((size_t)0);
+ this->Layout();
+}
+
+void SpekWindow::on_visit(wxCommandEvent& event)
+{
+ wxLaunchDefaultBrowser(wxT("http://spek-project.org"));
+}
+
+void SpekWindow::on_close(wxCommandEvent& event)
+{
+ wxWindow *self = ((wxWindow *)event.GetEventObject())->GetGrandParent();
+ self->GetSizer()->Hide((size_t)0);
+ self->Layout();
+}
+
+static void * check_version(void *p)
+{
+ // Does the user want to check for updates?
+ SpekPreferences& prefs = SpekPreferences::get();
+ if (!prefs.get_check_update()) {
+ return NULL;
+ }
+
+ // Calculate the number of days since 0001-01-01, borrowed from GLib.
+ wxDateTime now = wxDateTime::Now();
+ int year = now.GetYear() - 1;
+ int days = year * 365;
+ days += (year >>= 2); // divide by 4 and add
+ days -= (year /= 25); // divides original # years by 100
+ days += year >> 2; // divides by 4, which divides original by 400
+ days += now.GetDayOfYear();
+
+ // When was the last update?
+ int diff = days - prefs.get_last_update();
+ if (diff < 7) {
+ return NULL;
+ }
+
+ // Get the version number.
+ wxString version;
+ wxHTTP http;
+ if (http.Connect(wxT("spek-project.org"))) {
+ wxInputStream *stream = http.GetInputStream(wxT("/version"));
+ if (stream) {
+ wxStringOutputStream out(&version);
+ stream->Read(out);
+ version.Trim();
+ delete stream;
+ }
+ }
+
+ if (version.IsEmpty()) {
+ return NULL;
+ }
+
+ if (version > wxT(PACKAGE_VERSION)) {
+ SpekWindow *self = (SpekWindow *)p;
+ wxCommandEvent event(SPEK_NOTIFY_EVENT, -1);
+ event.SetEventObject(self);
+ wxPostEvent(self, event);
+ }
+
+ // Update the preferences.
+ prefs.set_check_update(true);
+ prefs.set_last_update(days);
+ return NULL;
+}
diff --git a/src/spek-window.hh b/src/spek-window.hh
@@ -0,0 +1,50 @@
+/* spek-window.hh
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_WINDOW_HH_
+#define SPEK_WINDOW_HH_
+
+#include <wx/wx.h>
+
+class SpekSpectrogram;
+
+class SpekWindow : public wxFrame
+{
+public:
+ SpekWindow(const wxString& path);
+ void open(const wxString& path);
+
+private:
+ void on_open(wxCommandEvent& event);
+ void on_save(wxCommandEvent& event);
+ void on_exit(wxCommandEvent& event);
+ void on_preferences(wxCommandEvent& event);
+ void on_about(wxCommandEvent& event);
+ void on_notify(wxCommandEvent& event);
+ void on_visit(wxCommandEvent& event);
+ void on_close(wxCommandEvent& event);
+
+ SpekSpectrogram *spectrogram;
+ wxString path;
+ wxString cur_dir;
+ wxString description;
+
+ DECLARE_EVENT_TABLE()
+};
+
+#endif
diff --git a/src/spek-window.vala b/src/spek-window.vala
@@ -1,354 +0,0 @@
-/* spek-window.vala
- *
- * Copyright (C) 2010-2011 Alexander Kojevnikov <alexander@kojevnikov.com>
- *
- * Spek 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 3 of the License, or
- * (at your option) any later version.
- *
- * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
- */
-
-using Gdk;
-using Gtk;
-
-namespace Spek {
- public class Window : Gtk.Window {
-
- private UIManager ui;
- private InfoBar info_bar;
- private Spectrogram spectrogram;
- private string description;
- private string cur_dir;
- private FileFilter filter_all;
- private FileFilter filter_audio;
- private FileFilter filter_png;
-
- private const Gtk.ActionEntry[] ACTION_ENTRIES = {
- { "File", null, N_("_File") },
- { "FileOpen", Stock.OPEN, null, null, null, on_file_open },
- { "FileSave", Stock.SAVE, null, null, null, on_file_save },
- { "FileQuit", Stock.QUIT, null, null, null, on_file_quit },
- { "Edit", null, N_("_Edit") },
- { "EditPreferences", Stock.PREFERENCES, null, "<Ctrl>E", null, on_edit_preferences },
- { "Help", null, N_("_Help") },
- { "HelpAbout", Stock.ABOUT, null, "F1", null, on_help_about }
- };
-
- private const string UI = """
-<ui>
- <menubar name='MenuBar'>
- <menu action='File'>
- <menuitem action='FileOpen'/>
- <menuitem action='FileSave'/>
- <separator/>
- <menuitem action='FileQuit'/>
- </menu>
- <menu action='Edit'>
- <menuitem action='EditPreferences'/>
- </menu>
- <menu action='Help'>
- <menuitem action='HelpAbout'/>
- </menu>
- </menubar>
-
- <toolbar name='ToolBar'>
- <toolitem action='FileOpen'/>
- <toolitem action='FileSave'/>
- <separator expand='true'/>
- <toolitem action='HelpAbout'/>
- </toolbar>
-</ui>
-""";
-
- private const Gtk.TargetEntry[] DEST_TARGET_ENTRIES = {
- { "text/uri-list", 0, 0 }
- };
-
- public Window (string? file_name) {
- description = title = _("Spek - Acoustic Spectrum Analyser");
- set_default_icon_name ("spek");
- set_default_size (640, 480);
- destroy.connect (Gtk.main_quit);
-
- var actions = new Gtk.ActionGroup ("Actions");
- actions.set_translation_domain (Config.GETTEXT_PACKAGE);
- actions.add_actions (ACTION_ENTRIES, this);
- ui = new UIManager ();
- ui.insert_action_group (actions, 0);
- add_accel_group (ui.get_accel_group ());
- try {
- ui.add_ui_from_string (UI, -1);
- } catch (Error e) {
- warning ("Could not load the UI: %s\n", e.message);
- }
-
- var menubar = ui.get_widget ("/MenuBar");
- var toolbar = (Toolbar) ui.get_widget ("/ToolBar");
- toolbar.set_style (ToolbarStyle.BOTH_HORIZ);
- ((ToolItem) ui.get_widget ("/ToolBar/FileOpen")).is_important = true;
- ((ToolItem) ui.get_widget ("/ToolBar/FileSave")).is_important = true;
- ((ToolItem) ui.get_widget ("/ToolBar/HelpAbout")).is_important = true;
-
- info_bar = new InfoBar.with_buttons (Stock.OK, ResponseType.OK);
- var label = new Label (null);
- label.use_markup = true;
- label.set_markup (_("A new version of Spek is available on <a href=\"http://www.spek-project.org\">www.spek-project.org</a>"));
- label.ellipsize = Pango.EllipsizeMode.END;
- label.xalign = 0f;
- label.activate_link.connect (uri => { Platform.show_uri (uri); return true; });
- label.show();
- var content_area = (Container) info_bar.get_content_area();
- content_area.add(label);
- info_bar.message_type = MessageType.INFO;
- info_bar.response.connect(() => info_bar.hide());
-
- spectrogram = new Spectrogram ();
- cur_dir = Environment.get_home_dir ();
-
- filter_all = new FileFilter ();
- filter_all.set_name (_("All files"));
- filter_all.add_pattern ("*");
- filter_png = new FileFilter ();
- filter_png.set_name (_("PNG images"));
- filter_png.add_pattern ("*.png");
- filter_audio = new FileFilter ();
- filter_audio.set_name (_("Audio files"));
- foreach (var ext in audio_extensions) {
- filter_audio.add_pattern (ext);
- }
-
- var vbox = new VBox (false, 0);
- vbox.pack_start (menubar, false, true, 0);
- vbox.pack_start (toolbar, false, true, 0);
- vbox.pack_start (info_bar, false, true, 0);
- vbox.pack_start (spectrogram, true, true, 0);
- add (vbox);
- menubar.show_all ();
- toolbar.show_all ();
- spectrogram.show_all ();
- vbox.show ();
-
- Platform.fix_ui (ui);
- show ();
-
- // Set up Drag and Drop
- drag_dest_set (this, DestDefaults.ALL, DEST_TARGET_ENTRIES, DragAction.COPY);
- drag_data_received.connect (on_dropped);
-
- if (file_name != null) {
- open_file (file_name);
- }
-
- try {
- Thread.create<void*> (check_version, false);
- } catch (ThreadError e) {
- }
- }
-
- void on_dropped (DragContext cx, int x, int y, SelectionData data, uint info, uint time) {
- if (data.get_length () > 0 && data.get_format () == 8) {
- string[] files = data.get_uris ();
- if (files.length > 0) {
- try {
- open_file (Filename.from_uri (files[0]));
- drag_finish (cx, true, false, time);
- return;
- } catch (ConvertError e) {}
- }
- }
- drag_finish (cx, false, false, time);
- }
-
- private void open_file (string file_name) {
- cur_dir = Path.get_dirname (file_name);
- spectrogram.open (file_name);
-
- // TRANSLATORS: window title, %s is replaced with the file name
- title = _("Spek - %s").printf (Path.get_basename (file_name));
- }
-
- private void on_file_open () {
- var chooser = new FileChooserDialog (
- _("Open File"), this, FileChooserAction.OPEN,
- Stock.CANCEL, ResponseType.CANCEL,
- Stock.OPEN, ResponseType.ACCEPT, null);
- chooser.set_default_response (ResponseType.ACCEPT);
- chooser.select_multiple = false;
- chooser.set_current_folder (cur_dir);
- chooser.add_filter (filter_all);
- chooser.add_filter (filter_audio);
- chooser.set_filter (filter_audio);
- if (chooser.run () == ResponseType.ACCEPT) {
- open_file (chooser.get_filename ());
- }
- chooser.destroy ();
- }
-
- private void on_file_save () {
- var chooser = new FileChooserDialog (
- _("Save Spectrogram"), this, FileChooserAction.SAVE,
- Stock.CANCEL, ResponseType.CANCEL,
- Stock.SAVE, ResponseType.ACCEPT, null);
- chooser.set_default_response (ResponseType.ACCEPT);
- chooser.set_current_folder (cur_dir);
-
- // Suggested name is <file_name>.png
- var file_name = Path.get_basename (spectrogram.file_name ?? _("Untitled"));
- file_name += ".png";
- chooser.set_current_name (file_name);
- chooser.add_filter (filter_png);
- chooser.set_filter (filter_png);
- if (chooser.run () == ResponseType.ACCEPT) {
- file_name = chooser.get_filename ();
- cur_dir = Path.get_dirname (file_name);
- spectrogram.save (file_name);
- }
- chooser.destroy ();
- }
-
- private void on_file_quit () {
- destroy ();
- }
-
- private void on_edit_preferences () {
- var dlg = new PreferencesDialog ();
- dlg.transient_for = this;
- dlg.run ();
- }
-
- private void on_help_about () {
- string[] authors = {
- "Primary Development:",
- "\tAlexander Kojevnikov (maintainer)",
- "",
- "Contributors:",
- "\tFabian Deutsch",
- "\tJonathan Gonzalez V",
- "\tStefan Kost",
- "\tThibault North"
- };
- string[] artists = {
- "Olga Vasylevska"
- };
- string license = "Copyright (C) 2010-2011 Alexander Kojevnikov";
- license += "\n\n";
- license += "Spek is free software: you can redistribute it and/or modify ";
- license += "it under the terms of the GNU General Public License as published by ";
- license += "the Free Software Foundation, either version 3 of the License, or ";
- license += "(at your option) any later version.";
- license += "\n\n";
- license += "Spek is distributed in the hope that it will be useful, ";
- license += "but WITHOUT ANY WARRANTY; without even the implied warranty of ";
- license += "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ";
- license += "GNU General Public License for more details.";
- license += "\n\n";
- license += "You should have received a copy of the GNU General Public License ";
- license += "along with Spek. If not, see http://www.gnu.org/licenses/";
-
- var dlg = new AboutDialog ();
- dlg.program_name = "Spek";
- dlg.version = Config.PACKAGE_VERSION;
- dlg.copyright = _("Copyright \xc2\xa9 2010-2011 Alexander Kojevnikov");
- dlg.comments = description;
- dlg.set ("authors", authors);
-// dlg.set ("documenters", documenters);
- dlg.set ("artists", artists);
- dlg.website_label = _("Spek Website");
- dlg.website = "http://www.spek-project.org/";
- dlg.license = license;
- dlg.wrap_license = true;
- try {
- dlg.logo = IconTheme.get_default ().load_icon ("spek", 128, IconLookupFlags.FORCE_SVG);
- } catch (Error e) {
- dlg.logo_icon_name = "spek";
- }
- // TRANSLATORS: Add your name here
- dlg.translator_credits = _("translator-credits");
- dlg.set_transient_for (this);
- dlg.destroy_with_parent = true;
- dlg.response.connect (id => dlg.destroy ());
- dlg.set_url_hook (url_hook);
- dlg.modal = true;
- dlg.present ();
- }
-
- private void url_hook (AboutDialog about, string link) {
- Platform.show_uri (link);
- }
-
- // TODO: s/audio/media/
- private string[] audio_extensions = {
- "*.3gp",
- "*.aac",
- "*.aif",
- "*.aifc",
- "*.aiff",
- "*.amr",
- "*.awb",
- "*.ape",
- "*.au",
- "*.dts",
- "*.flac",
- "*.gsm",
- "*.m4a",
- "*.m4p",
- "*.mp3",
- "*.mp4",
- "*.mp+",
- "*.mpc",
- "*.mpp",
- "*.oga",
- "*.ogg",
- "*.ra",
- "*.ram",
- "*.snd",
- "*.wav",
- "*.wma",
- "*.wv"
- };
-
- private void * check_version () {
- // Does the user want to check for updates?
- var prefs = Preferences.instance;
- var check = prefs.check_update;
- if (!check) {
- return null;
- }
-
- // When was the last update?
- var time_val = TimeVal ();
- time_val.get_current_time ();
- Date today = Date ();
- today.set_time_val (time_val);
- int day = prefs.last_update;
- int diff = (int) today.get_julian () - day;
- if (diff < 7) {
- return null;
- }
-
- // Get the version number.
- var version = Platform.read_line ("http://www.spek-project.org/version");
- if (version == null) {
- return null;
- }
-
- if (version != null && version > Config.PACKAGE_VERSION) {
- Idle.add (() => { info_bar.show_all (); return false; });
- }
-
- // Update the preferences.
- prefs.check_update = check;
- prefs.last_update = (int) today.get_julian ();
- prefs.save ();
- return null;
- }
- }
-}
diff --git a/src/spek.cc b/src/spek.cc
@@ -0,0 +1,112 @@
+/* spek.cc
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wx/cmdline.h>
+#include <wx/log.h>
+#include <wx/socket.h>
+
+#include "spek-audio.h"
+#include "spek-platform.hh"
+#include "spek-preferences.hh"
+
+#include "spek-window.hh"
+
+class Spek: public wxApp
+{
+public:
+ Spek() : wxApp(), quit(false) {}
+
+protected:
+ virtual bool OnInit();
+ virtual int OnRun();
+
+private:
+ wxString path;
+ bool quit;
+};
+
+IMPLEMENT_APP(Spek)
+
+bool Spek::OnInit()
+{
+ wxInitAllImageHandlers();
+ wxSocketBase::Initialize();
+
+ SpekPlatform::init();
+ SpekPreferences::get().init();
+ spek_audio_init();
+
+ static const wxCmdLineEntryDesc desc[] = {{
+ wxCMD_LINE_SWITCH,
+ wxT_2("h"),
+ wxT_2("help"),
+ wxT_2("Show this help message"),
+ wxCMD_LINE_VAL_NONE,
+ wxCMD_LINE_OPTION_HELP
+ }, {
+ wxCMD_LINE_SWITCH,
+ wxT_2("V"),
+ wxT_2("version"),
+ wxT_2("Display the version and exit")
+ }, {
+ wxCMD_LINE_PARAM,
+ NULL,
+ NULL,
+ wxT_2("FILE"),
+ wxCMD_LINE_VAL_STRING,
+ wxCMD_LINE_PARAM_OPTIONAL
+ }, {
+ // TODO: use wxCMD_LINE_DESC_END after settling on wx29.
+ wxCMD_LINE_NONE, NULL, NULL, NULL, wxCMD_LINE_VAL_NONE, 0
+ }
+ };
+
+ wxCmdLineParser parser(desc, argc, argv);
+ int ret = parser.Parse(true);
+ if (ret == 1) {
+ return false;
+ }
+ if (ret == -1) {
+ this->quit = true;
+ return true;
+ }
+ if (parser.Found(wxT("version"))) {
+ // TRANSLATORS: first %s is the package name, second %s is the package version.
+ wxPrintf(_("%s version %s"), wxT(PACKAGE_NAME), wxT(PACKAGE_VERSION));
+ wxPrintf(wxT("\n"));
+ this->quit = true;
+ return true;
+ }
+ if (parser.GetParamCount()) {
+ this->path = parser.GetParam();
+ }
+
+ SpekWindow *window = new SpekWindow(this->path);
+ window->Show(true);
+ SetTopWindow(window);
+ return true;
+}
+
+int Spek::OnRun()
+{
+ if (quit) {
+ return 0;
+ }
+
+ return wxApp::OnRun();
+}
diff --git a/src/spek.vala b/src/spek.vala
@@ -1,75 +0,0 @@
-/* spek.vala
- *
- * Copyright (C) 2010-2011 Alexander Kojevnikov <alexander@kojevnikov.com>
- *
- * Spek 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 3 of the License, or
- * (at your option) any later version.
- *
- * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>.
- */
-
-namespace Spek {
- bool version = false;
- [CCode (array_length = false, array_null_terminated = true)]
- string[] files = null;
-
- const OptionEntry[] options = {
- { "version", 'V', 0, OptionArg.NONE, ref version, N_("Display the version and exit"), null },
- { "", 0, 0, OptionArg.FILENAME_ARRAY, ref files, null, null },
- { null }
- };
-
- int main (string[] args) {
- Platform.fix_args (args);
-
- if (Preferences.instance.language.length > 0) {
- Environment.set_variable ("LANGUAGE", Preferences.instance.language, true);
- }
-
- Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Platform.locale_dir ());
- Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
- Intl.textdomain (Config.GETTEXT_PACKAGE);
-
- try {
- Gtk.init_with_args (ref args, _("[FILE]"), (OptionEntry[]) options, Config.GETTEXT_PACKAGE);
- } catch (Error e) {
- print (e.message);
- print ("\n");
- print (_("Run `%s --help` to see a full list of available command line options.\n"), args[0]);
- return 1;
- }
-
- if (version) {
- // TRANSLATORS: first %s is the package name, second %s is the package version.
- print (_("%s version %s\n"), Config.PACKAGE_NAME, Config.PACKAGE_VERSION);
- return 0;
- }
-
- if (files != null && files.length != 1) {
- print (_("Specify a single file\n"));
- return 1;
- }
-
- Platform.init ();
- Audio.init ();
- var file_name = files == null ? null : files[0];
- if (file_name != null && file_name.has_prefix ("file://")) {
- try {
- file_name = Filename.from_uri (file_name);
- } catch (ConvertError e) {
- }
- }
- var window = new Window (file_name);
- Gtk.main ();
- window.destroy ();
- return 0;
- }
-}
-\ No newline at end of file
diff --git a/vapi/Makefile.am b/vapi/Makefile.am
@@ -1,8 +0,0 @@
-noinst_DATA = \
- config.vapi \
- spek-audio.vapi \
- spek-fft.vapi \
- spek-platform.vapi
-
-EXTRA_DIST = \
- $(noinst_DATA)
diff --git a/vapi/config.vapi b/vapi/config.vapi
@@ -1,16 +0,0 @@
-[CCode (prefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
-namespace Config {
- /* Package information */
- public const string PACKAGE_NAME;
- public const string PACKAGE_STRING;
- public const string PACKAGE_VERSION;
-
- /* Gettext package */
- public const string GETTEXT_PACKAGE;
-
- /* Configured paths - these variables are not present in config.h, they are
- * passed to underlying C code as cmd line macros. */
- public const string LOCALEDIR; /* /usr/local/share/locale */
- public const string PKGDATADIR; /* /usr/local/share/spek */
- public const string PKGLIBDIR; /* /usr/local/lib/spek */
-}
diff --git a/vapi/spek-audio.vapi b/vapi/spek-audio.vapi
@@ -1,29 +0,0 @@
-[CCode (cprefix = "SpekAudio", lower_case_cprefix = "spek_audio_", cheader_filename = "spek-audio.h")]
-namespace Spek.Audio {
- [Compact]
- [CCode (free_function = "spek_audio_close")]
- public class Context {
- public string file_name;
- public string codec_name;
- public string error;
- public int bit_rate;
- public int sample_rate;
- public int bits_per_sample;
- public int width;
- public bool fp;
- public int channels;
- public double duration;
- public uint8 *buffer;
- public int64 frames_per_interval;
- public int64 error_per_interval;
- public int64 error_base;
-
- [CCode (cname = "spek_audio_open")]
- public Context (string file_name);
- [CCode (cname = "spek_audio_start")]
- public int start (int samples);
- [CCode (cname = "spek_audio_read")]
- public int read ();
- }
- public static void init ();
-}
diff --git a/vapi/spek-fft.vapi b/vapi/spek-fft.vapi
@@ -1,16 +0,0 @@
-[CCode (cprefix = "SpekFft", lower_case_cprefix = "spek_fft_", cheader_filename = "spek-fft.h")]
-namespace Spek.Fft {
- [Compact]
- [CCode (free_function = "spek_fft_destroy")]
- public class Plan {
- [CCode (array_length = false)]
- public unowned float[] input;
- [CCode (array_length = false)]
- public unowned float[] output;
-
- [CCode (cname = "spek_fft_plan_new")]
- public Plan (int n, int threshold);
- [CCode (cname = "spek_fft_execute")]
- public void execute ();
- }
-}
diff --git a/vapi/spek-platform.vapi b/vapi/spek-platform.vapi
@@ -1,10 +0,0 @@
-[CCode (cprefix = "SpekPlatform", lower_case_cprefix = "spek_platform_", cheader_filename = "spek-platform.h")]
-namespace Spek.Platform {
- public static void init ();
- public static void fix_args (string[] args);
- public static void fix_ui (Gtk.UIManager ui);
- public static unowned string locale_dir ();
- public static void show_uri (string uri);
- public static string read_line (string uri);
- public static double get_font_scale ();
-}