mirror of
https://github.com/alexkay/spek.git
synced 2025-04-21 19:07:37 +03:00
Merge branch 'wx'
This commit is contained in:
commit
14e2cea74c
.gitignoreMakefile.amautogen.shconfigure.ac
dist/osx
po
src
Makefile.amspek-audio-desc.ccspek-audio-desc.hhspek-audio.cspek-audio.hspek-events.ccspek-events.hhspek-fft.cspek-fft.hspek-palette.cspek-palette.hspek-pipeline.cspek-pipeline.hspek-pipeline.valaspek-platform.cspek-platform.ccspek-platform.hspek-platform.hhspek-preferences-dialog.ccspek-preferences-dialog.hhspek-preferences-dialog.valaspek-preferences.ccspek-preferences.hhspek-preferences.valaspek-ruler.ccspek-ruler.hhspek-ruler.valaspek-spectrogram.ccspek-spectrogram.hhspek-spectrogram.valaspek-window.ccspek-window.hhspek-window.valaspek.ccspek.vala
vapi
5
.gitignore
vendored
5
.gitignore
vendored
@ -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
|
||||
|
@ -1,11 +1,8 @@
|
||||
# Makefile.am
|
||||
|
||||
SUBDIRS = \
|
||||
data \
|
||||
man \
|
||||
po \
|
||||
src \
|
||||
vapi
|
||||
src
|
||||
|
||||
EXTRA_DIST = \
|
||||
intltool-extract.in \
|
||||
|
@ -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 "$@"
|
||||
test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
|
77
configure.ac
77
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])
|
||||
AM_INIT_AUTOMAKE([1.11.1 foreign no-dist-gzip dist-xz])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
# Enable silent rules is available
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
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
|
||||
AC_OUTPUT
|
||||
|
21
dist/osx/README
vendored
21
dist/osx/README
vendored
@ -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
|
34
dist/osx/README.md
vendored
Normal file
34
dist/osx/README.md
vendored
Normal file
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
198
po/spek.pot
198
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
|
||||
msgid "Cannot open input file"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio.c:54
|
||||
msgid "Cannot find stream info"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio.c:66
|
||||
msgid "The file contains no audio streams"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio.c:73
|
||||
msgid "Cannot find decoder"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio.c:91
|
||||
msgid "Unknown duration"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio.c:95
|
||||
msgid "No audio channels"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio.c:99
|
||||
msgid "Cannot open decoder"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio.c:120
|
||||
msgid "Unsupported sample format"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-pipeline.vala:70
|
||||
#: ../src/spek-audio-desc.cc:34
|
||||
#, c-format
|
||||
msgid "%d kbps"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-pipeline.vala:73
|
||||
#: ../src/spek-audio-desc.cc:37
|
||||
#, 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-audio-desc.cc:65
|
||||
msgid "Cannot open input file"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-pipeline.vala:81
|
||||
#, c-format
|
||||
msgid "%d channel"
|
||||
msgid_plural "%d channels"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
#: ../src/spek-audio-desc.cc:68
|
||||
msgid "Cannot find stream info"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio-desc.cc:71
|
||||
msgid "The file contains no audio streams"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio-desc.cc:74
|
||||
msgid "Cannot find decoder"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio-desc.cc:77
|
||||
msgid "Unknown duration"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio-desc.cc:80
|
||||
msgid "No audio channels"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio-desc.cc:83
|
||||
msgid "Cannot open decoder"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-audio-desc.cc:86
|
||||
msgid "Unsupported sample format"
|
||||
msgstr ""
|
||||
|
||||
#. 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:"
|
||||
#: ../src/spek-preferences-dialog.cc:82
|
||||
msgid "Language:"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-preferences-dialog.vala:86
|
||||
msgid "Check for _updates"
|
||||
#: ../src/spek-preferences-dialog.cc:97
|
||||
msgid "Check for &updates"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: keep "00" unchanged, it's used to calc the text width
|
||||
#: ../src/spek-spectrogram.vala:200
|
||||
msgid "00 kHz"
|
||||
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"
|
||||
#. 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:39
|
||||
msgid "_Edit"
|
||||
#. 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:41
|
||||
msgid "_Help"
|
||||
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
|
||||
#: ../src/spek-window.cc:211
|
||||
msgid "All files"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-window.cc:213
|
||||
msgid "Audio files"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-window.cc:227
|
||||
msgid "Open File"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-window.vala:197
|
||||
#: ../src/spek-window.cc:249
|
||||
msgid "PNG images"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-window.cc:255
|
||||
msgid "Save Spectrogram"
|
||||
msgstr ""
|
||||
|
||||
#. Suggested name is <file_name>.png
|
||||
#: ../src/spek-window.vala:204
|
||||
#: ../src/spek-window.cc:263
|
||||
msgid "Untitled"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-window.vala:259
|
||||
msgid "Copyright © 2010-2011 Alexander Kojevnikov"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek-window.vala:264
|
||||
msgid "Spek Website"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: Add your name here
|
||||
#: ../src/spek-window.vala:274
|
||||
#: ../src/spek-window.cc:300
|
||||
msgid "translator-credits"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek.vala:25
|
||||
msgid "Display the version and exit"
|
||||
#: ../src/spek-window.cc:306
|
||||
msgid "Copyright (c) 2010-2012 Alexander Kojevnikov and contributors"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/spek.vala:42
|
||||
msgid "[FILE]"
|
||||
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 ""
|
||||
|
@ -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)
|
||||
|
||||
VALAFLAGS = \
|
||||
--thread \
|
||||
--vapidir=$(srcdir)/../vapi \
|
||||
--pkg config \
|
||||
--pkg spek-audio \
|
||||
--pkg spek-fft \
|
||||
--pkg spek-platform \
|
||||
@SPEK_PACKAGES@
|
||||
spek_CFLAGS = \
|
||||
$(FFMPEG_CFLAGS) \
|
||||
$(WX_CFLAGS_ONLY)
|
||||
|
||||
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
|
||||
|
95
src/spek-audio-desc.cc
Normal file
95
src/spek-audio-desc.cc
Normal file
@ -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;
|
||||
}
|
28
src/spek-audio-desc.hh
Normal file
28
src/spek-audio-desc.hh
Normal file
@ -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
|
212
src/spek-audio.c
212
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();
|
||||
}
|
||||
|
||||
SpekAudioContext * spek_audio_open (const gchar *file_name) {
|
||||
SpekAudioContext *cx;
|
||||
int i;
|
||||
const struct spek_audio_properties * spek_audio_get_properties(struct spek_audio_context *cx)
|
||||
{
|
||||
return &cx->properties;
|
||||
}
|
||||
|
||||
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
|
||||
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, file_name, NULL, NULL) != 0) {
|
||||
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);
|
||||
}
|
||||
|
110
src/spek-audio.h
110
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
|
||||
|
45
src/spek-events.cc
Normal file
45
src/spek-events.cc
Normal file
@ -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);
|
||||
}
|
||||
}
|
53
src/spek-events.hh
Normal file
53
src/spek-events.hh
Normal file
@ -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
|
@ -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;
|
||||
void spek_fft_execute(struct spek_fft_plan *p)
|
||||
{
|
||||
av_rdft_calc(p->cx, p->input);
|
||||
|
||||
// Calculate magnitudes.
|
||||
int n = p->n;
|
||||
|
||||
av_rdft_calc (p->cx, p->input);
|
||||
|
||||
/* Calculate magnitudes */
|
||||
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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
typedef struct {
|
||||
/* Internal data */
|
||||
RDFTContext *cx;
|
||||
gint n;
|
||||
gint threshold;
|
||||
struct RDFTContext;
|
||||
|
||||
/* Exposed properties */
|
||||
gfloat *input;
|
||||
gfloat *output;
|
||||
} SpekFftPlan;
|
||||
struct spek_fft_plan
|
||||
{
|
||||
// Internal data.
|
||||
struct RDFTContext *cx;
|
||||
int n;
|
||||
int threshold;
|
||||
|
||||
/* Allocate buffers and create a new FFT plan */
|
||||
SpekFftPlan * spek_fft_plan_new (gint n, gint threshold);
|
||||
// Exposed properties.
|
||||
float *input;
|
||||
float *output;
|
||||
};
|
||||
|
||||
/* Execute the FFT on plan->input */
|
||||
void spek_fft_execute (SpekFftPlan *p);
|
||||
// Allocate buffers and create a new FFT plan.
|
||||
struct spek_fft_plan * spek_fft_plan_new(int n, int threshold);
|
||||
|
||||
/* Destroy the plan and de-allocate buffers */
|
||||
void spek_fft_destroy (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_delete(struct spek_fft_plan *p);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
61
src/spek-palette.c
Normal file
61
src/spek-palette.c
Normal file
@ -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;
|
||||
}
|
34
src/spek-palette.h
Normal file
34
src/spek-palette.h
Normal file
@ -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
|
368
src/spek-pipeline.c
Normal file
368
src/spek-pipeline.c
Normal file
@ -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;
|
||||
}
|
44
src/spek-pipeline.h
Normal file
44
src/spek-pipeline.h
Normal file
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
79
src/spek-platform.cc
Normal file
79
src/spek-platform.cc
Normal file
@ -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;
|
||||
}
|
@ -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
|
52
src/spek-platform.hh
Normal file
52
src/spek-platform.hh
Normal file
@ -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
|
114
src/spek-preferences-dialog.cc
Normal file
114
src/spek-preferences-dialog.cc
Normal file
@ -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());
|
||||
}
|
38
src/spek-preferences-dialog.hh
Normal file
38
src/spek-preferences-dialog.hh
Normal file
@ -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
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
100
src/spek-preferences.cc
Normal file
100
src/spek-preferences.cc
Normal file
@ -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();
|
||||
}
|
47
src/spek-preferences.hh
Normal file
47
src/spek-preferences.hh
Normal file
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
94
src/spek-ruler.cc
Normal file
94
src/spek-ruler.cc
Normal file
@ -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);
|
||||
}
|
||||
}
|
61
src/spek-ruler.hh
Normal file
61
src/spek-ruler.hh
Normal file
@ -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
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
371
src/spek-spectrogram.cc
Normal file
371
src/spek-spectrogram.cc
Normal file
@ -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);
|
||||
}
|
57
src/spek-spectrogram.hh
Normal file
57
src/spek-spectrogram.hh
Normal file
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
385
src/spek-window.cc
Normal file
385
src/spek-window.cc
Normal file
@ -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;
|
||||
}
|
50
src/spek-window.hh
Normal file
50
src/spek-window.hh
Normal file
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
112
src/spek.cc
Normal file
112
src/spek.cc
Normal file
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
noinst_DATA = \
|
||||
config.vapi \
|
||||
spek-audio.vapi \
|
||||
spek-fft.vapi \
|
||||
spek-platform.vapi
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(noinst_DATA)
|
@ -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 */
|
||||
}
|
@ -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 ();
|
||||
}
|
@ -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 ();
|
||||
}
|
||||
}
|
@ -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 ();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user