This commit is contained in:
Alexander Kojevnikov 2011-07-16 15:59:13 +08:00
parent 72bf4ad3ea
commit 045253bd70
20 changed files with 1554 additions and 1554 deletions

2
dist/osx/bundle.sh vendored
View File

@ -66,7 +66,7 @@ hdiutil convert -quiet -format UDBZ -o $DMG_FILE $DMG_FILE.master
echo "Done."
if [ ! "x$1" = "x-m" ]; then
rm $DMG_FILE.master
rm $DMG_FILE.master
fi
cd ../..

64
dist/osx/launcher.sh vendored
View File

@ -46,27 +46,27 @@ if test "$APPLELANGUAGES"; then
# A language ordering exists.
# Test, item per item, to see whether there is an corresponding locale.
for L in $APPLELANGUAGES; do
#test for exact matches:
#test for exact matches:
if test -f "$I18NDIR/${L}/LC_MESSAGES/$APP.mo"; then
export LANG=$L
export LANG=$L
break
fi
#This is a special case, because often the original strings are in US
#English and there is no translation file.
if test "x$L" == "xen_US"; then
export LANG=$L
break
fi
#OK, now test for just the first two letters:
#This is a special case, because often the original strings are in US
#English and there is no translation file.
if test "x$L" == "xen_US"; then
export LANG=$L
break
fi
#OK, now test for just the first two letters:
if test -f "$I18NDIR/${L:0:2}/LC_MESSAGES/$APP.mo"; then
export LANG=${L:0:2}
break
fi
#Same thing, but checking for any english variant.
if test "x${L:0:2}" == "xen"; then
export LANG=$L
break
fi;
export LANG=${L:0:2}
break
fi
#Same thing, but checking for any english variant.
if test "x${L:0:2}" == "xen"; then
export LANG=$L
break
fi;
done
fi
unset APPLELANGUAGES L
@ -75,7 +75,7 @@ unset APPLELANGUAGES L
APPLECOLLATION=`defaults read .GlobalPreferences AppleCollationOrder`
if test -z ${LANG} -a -n $APPLECOLLATION; then
if test -f "$I18NDIR/${APPLECOLLATION:0:2}/LC_MESSAGES/$APP.mo"; then
export LANG=${APPLECOLLATION:0:2}
export LANG=${APPLECOLLATION:0:2}
fi
fi
if test ! -z $APPLECOLLATION; then
@ -104,21 +104,21 @@ if test -n $LANG; then
#locale; otherwise, if it's longer than two characters, then it's
#probably a good message locale and we'll go with it.
if test $LANG == ${APPLELOCALE:0:5} -o $LANG != ${LANG:0:2}; then
export LC_MESSAGES=$LANG
export LC_MESSAGES=$LANG
#Next try if the Applelocale is longer than 2 chars and the language
#bit matches $LANG
elif test $LANG == ${APPLELOCALE:0:2} -a $APPLELOCALE > ${APPLELOCALE:0:2}; then
export LC_MESSAGES=${APPLELOCALE:0:5}
export LC_MESSAGES=${APPLELOCALE:0:5}
#Fail. Get a list of the locales in $PREFIX/share/locale that match
#our two letter language code and pick the first one, special casing
#english to set en_US
elif test $LANG == "en"; then
export LC_MESSAGES="en_US"
export LC_MESSAGES="en_US"
else
LOC=`find $PREFIX/share/locale -name $LANG???`
for L in $LOC; do
export LC_MESSAGES=$L
done
LOC=`find $PREFIX/share/locale -name $LANG???`
for L in $LOC; do
export LC_MESSAGES=$L
done
fi
else
#All efforts have failed, so default to US english
@ -129,15 +129,15 @@ CURRENCY=`echo $APPLELOCALE | sed -En 's/.*currency=([[:alpha:]]+).*/\1/p'`
if test "x$CURRENCY" != "x"; then
#The user has set a special currency. Gtk doesn't install LC_MONETARY files, but Apple does in /usr/share/locale, so we're going to look there for a locale to set LC_CURRENCY to.
if test -f /usr/local/share/$LC_MESSAGES/LC_MONETARY; then
if test -a `cat /usr/local/share/$LC_MESSAGES/LC_MONETARY` == $CURRENCY; then
export LC_MONETARY=$LC_MESSAGES
fi
if test -a `cat /usr/local/share/$LC_MESSAGES/LC_MONETARY` == $CURRENCY; then
export LC_MONETARY=$LC_MESSAGES
fi
fi
if test -z "$LC_MONETARY"; then
FILES=`find /usr/share/locale -name LC_MONETARY -exec grep -H $CURRENCY {} \;`
if test -n "$FILES"; then
export LC_MONETARY=`echo $FILES | sed -En 's%/usr/share/locale/([[:alpha:]_]+)/LC_MONETARY.*%\1%p'`
fi
FILES=`find /usr/share/locale -name LC_MONETARY -exec grep -H $CURRENCY {} \;`
if test -n "$FILES"; then
export LC_MONETARY=`echo $FILES | sed -En 's%/usr/share/locale/([[:alpha:]_]+)/LC_MONETARY.*%\1%p'`
fi
fi
fi
#No currency value means that the AppleLocale governs:

View File

@ -2,44 +2,44 @@
<!DOCTYPE moduleset SYSTEM "moduleset.dtd">
<?xml-stylesheet type="text/xsl" href="moduleset.xsl"?>
<moduleset>
<include href="http://github.com/jralls/gtk-osx-build/raw/master/modulesets-stable/gtk-osx.modules"/>
<include href="http://github.com/jralls/gtk-osx-build/raw/master/modulesets-stable/gtk-osx.modules"/>
<repository type="tarball" name="spek" default="yes" href="http://spek.googlecode.com/files/"/>
<repository type="tarball" name="libav" href="http://libav.org/releases/"/>
<repository type="tarball" name="cairographics-dev" href="http://cairographics.org/snapshots/"/>
<repository type="git" name="github.com" href="git://github.com/"/>
<repository type="tarball" name="spek" default="yes" href="http://spek.googlecode.com/files/"/>
<repository type="tarball" name="libav" href="http://libav.org/releases/"/>
<repository type="tarball" name="cairographics-dev" href="http://cairographics.org/snapshots/"/>
<repository type="git" name="github.com" href="git://github.com/"/>
<autotools id="cairo" autogen-sh="configure" autogenargs="--enable-pdf --enable-quartz --enable-xlib=no --without-x">
<branch module="cairo-1.11.2.tar.gz" version="1.11.2" repo="cairographics-dev"/>
<dependencies>
<dep package="pixman"/>
</dependencies>
<after>
<dep package="meta-gtk-osx-bootstrap"/>
<dep package="fontconfig"/>
<dep package="freetype"/>
</after>
</autotools>
<autotools id="cairo" autogen-sh="configure" autogenargs="--enable-pdf --enable-quartz --enable-xlib=no --without-x">
<branch module="cairo-1.11.2.tar.gz" version="1.11.2" repo="cairographics-dev"/>
<dependencies>
<dep package="pixman"/>
</dependencies>
<after>
<dep package="meta-gtk-osx-bootstrap"/>
<dep package="fontconfig"/>
<dep package="freetype"/>
</after>
</autotools>
<autotools id="gtk-quartz-engine">
<branch module="jralls/gtk-quartz-engine" repo="github.com"/>
<after>
<dep package="meta-gtk-osx-core"/>
</after>
</autotools>
<autotools id="gtk-quartz-engine">
<branch module="jralls/gtk-quartz-engine" repo="github.com"/>
<after>
<dep package="meta-gtk-osx-core"/>
</after>
</autotools>
<autotools id="libav" autogenargs="--disable-static --enable-shared --enable-gpl --enable-version3 --disable-doc --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-ffserver --disable-avdevice --disable-swscale --enable-pthreads --disable-encoders --disable-muxers --disable-devices --disable-filters" autogen-sh="configure" autogen-template="%(srcdir)s/%(autogen-sh)s --prefix=%(prefix)s --libdir=%(libdir)s %(autogenargs)s">
<branch module="libav-0.6.2.tar.bz2" version="0.6.2" repo="libav" hash="sha1:b79dc56a08f4ef07b41d1a78b2251f21fde8b81d"/>
</autotools>
<autotools id="libav" autogenargs="--disable-static --enable-shared --enable-gpl --enable-version3 --disable-doc --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-ffserver --disable-avdevice --disable-swscale --enable-pthreads --disable-encoders --disable-muxers --disable-devices --disable-filters" autogen-sh="configure" autogen-template="%(srcdir)s/%(autogen-sh)s --prefix=%(prefix)s --libdir=%(libdir)s %(autogenargs)s">
<branch module="libav-0.6.2.tar.bz2" version="0.6.2" repo="libav" hash="sha1:b79dc56a08f4ef07b41d1a78b2251f21fde8b81d"/>
</autotools>
<autotools id="spek" autogen-sh="configure">
<branch module="spek-@VERSION@.tar.bz2" version="@VERSION@" repo="spek"/>
<dependencies>
<dep package="meta-gtk-osx-bootstrap"/>
<dep package="meta-gtk-osx-core"/>
<dep package="cairo"/>
<dep package="gtk-quartz-engine"/>
<dep package="libav"/>
</dependencies>
</autotools>
<autotools id="spek" autogen-sh="configure">
<branch module="spek-@VERSION@.tar.bz2" version="@VERSION@" repo="spek"/>
<dependencies>
<dep package="meta-gtk-osx-bootstrap"/>
<dep package="meta-gtk-osx-core"/>
<dep package="cairo"/>
<dep package="gtk-quartz-engine"/>
<dep package="libav"/>
</dependencies>
</autotools>
</moduleset>

View File

@ -22,188 +22,188 @@
#include "spek-audio.h"
void spek_audio_init () {
avcodec_init ();
/* TODO: register only audio decoders */
av_register_all ();
avcodec_init ();
/* TODO: register only audio decoders */
av_register_all ();
}
SpekAudioContext * spek_audio_open (const gchar *file_name) {
SpekAudioContext *cx;
int i;
SpekAudioContext *cx;
int i;
cx = g_new0 (SpekAudioContext, 1);
cx->file_name = g_strdup (file_name);
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);
/* av_open_input_file() cannot open files with Unicode chars in it
* when running under Windows. When this happens we will re-try
* using the corresponding short file name.
*/
cx->short_name = g_win32_locale_filename_from_utf8 (file_name);
#endif
if (av_open_input_file (&cx->format_context, file_name, NULL, 0, NULL) != 0) {
if (!cx->short_name ||
av_open_input_file (&cx->format_context, cx->short_name, NULL, 0, NULL) != 0 ) {
cx->error = _("Cannot open input file");
return cx;
}
}
if (av_find_stream_info (cx->format_context) < 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");
return cx;
}
}
cx->audio_stream = -1;
for (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");
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);
if (cx->codec == NULL) {
cx->error = _("Cannot find 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;
}
cx->channels = cx->codec_context->channels;
cx->duration = cx->stream->duration * av_q2d (cx->stream->time_base);
if (cx->channels <= 0) {
cx->error = _("No audio channels");
return cx;
}
if (avcodec_open (cx->codec_context, cx->codec) < 0) {
cx->error = _("Cannot open decoder");
return cx;
}
switch (cx->codec_context->sample_fmt) {
case SAMPLE_FMT_S16:
cx->width = 16;
cx->fp = FALSE;
break;
case SAMPLE_FMT_S32:
cx->width = 32;
cx->fp = FALSE;
break;
case SAMPLE_FMT_FLT:
cx->width = 32;
cx->fp = TRUE;
break;
case SAMPLE_FMT_DBL:
cx->width = 64;
cx->fp = TRUE;
break;
default:
cx->error = _("Unsupported 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->offset = 0;
return cx;
if (av_open_input_file (&cx->format_context, file_name, NULL, 0, NULL) != 0) {
if (!cx->short_name ||
av_open_input_file (&cx->format_context, cx->short_name, NULL, 0, NULL) != 0 ) {
cx->error = _("Cannot open input file");
return cx;
}
}
if (av_find_stream_info (cx->format_context) < 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");
return cx;
}
}
cx->audio_stream = -1;
for (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");
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);
if (cx->codec == NULL) {
cx->error = _("Cannot find 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;
}
cx->channels = cx->codec_context->channels;
cx->duration = cx->stream->duration * av_q2d (cx->stream->time_base);
if (cx->channels <= 0) {
cx->error = _("No audio channels");
return cx;
}
if (avcodec_open (cx->codec_context, cx->codec) < 0) {
cx->error = _("Cannot open decoder");
return cx;
}
switch (cx->codec_context->sample_fmt) {
case SAMPLE_FMT_S16:
cx->width = 16;
cx->fp = FALSE;
break;
case SAMPLE_FMT_S32:
cx->width = 32;
cx->fp = FALSE;
break;
case SAMPLE_FMT_FLT:
cx->width = 32;
cx->fp = TRUE;
break;
case SAMPLE_FMT_DBL:
cx->width = 64;
cx->fp = TRUE;
break;
default:
cx->error = _("Unsupported 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->offset = 0;
return cx;
}
void spek_audio_start (SpekAudioContext *cx, gint samples) {
gint64 rate = cx->sample_rate * (gint64) cx->stream->time_base.num;
cx->error_base = samples * (gint64) cx->stream->time_base.den;
cx->frames_per_interval = av_rescale_rnd (
cx->stream->duration, rate, cx->error_base, AV_ROUND_DOWN);
cx->error_per_interval = (cx->stream->duration * rate) % cx->error_base;
gint64 rate = cx->sample_rate * (gint64) cx->stream->time_base.num;
cx->error_base = samples * (gint64) cx->stream->time_base.den;
cx->frames_per_interval = av_rescale_rnd (
cx->stream->duration, rate, cx->error_base, AV_ROUND_DOWN);
cx->error_per_interval = (cx->stream->duration * rate) % cx->error_base;
}
gint spek_audio_read (SpekAudioContext *cx) {
gint buffer_size;
gint len;
gint res;
gint buffer_size;
gint len;
gint res;
if (cx->error) {
return -1;
}
if (cx->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);
if (len < 0) {
/* Error, skip the frame. */
cx->packet->size = 0;
break;
}
cx->packet->data += len;
cx->packet->size -= len;
cx->offset += len;
if (buffer_size <= 0) {
/* No data yet, get more frames */
continue;
}
/* We have data, return it and come back for more later */
return buffer_size;
}
if (cx->packet->data) {
cx->packet->data -= cx->offset;
cx->packet->size += cx->offset;
cx->offset = 0;
av_free_packet (cx->packet);
}
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);
}
if (res < 0) {
/* End of file or error. */
return 0;
}
}
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);
if (len < 0) {
/* Error, skip the frame. */
cx->packet->size = 0;
break;
}
cx->packet->data += len;
cx->packet->size -= len;
cx->offset += len;
if (buffer_size <= 0) {
/* No data yet, get more frames */
continue;
}
/* We have data, return it and come back for more later */
return buffer_size;
}
if (cx->packet->data) {
cx->packet->data -= cx->offset;
cx->packet->size += cx->offset;
cx->offset = 0;
av_free_packet (cx->packet);
}
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);
}
if (res < 0) {
/* End of file or error. */
return 0;
}
}
}
void spek_audio_close (SpekAudioContext *cx) {
if (cx->file_name != NULL) {
g_free (cx->file_name);
}
if (cx->short_name != NULL) {
g_free (cx->short_name);
}
if (cx->codec_name != NULL) {
g_free (cx->codec_name);
}
if (cx->buffer) {
av_free (cx->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 (cx->packet);
}
if (cx->codec_context != NULL) {
avcodec_close (cx->codec_context);
}
if (cx->format_context != NULL) {
av_close_input_file (cx->format_context);
}
g_free (cx);
if (cx->file_name != NULL) {
g_free (cx->file_name);
}
if (cx->short_name != NULL) {
g_free (cx->short_name);
}
if (cx->codec_name != NULL) {
g_free (cx->codec_name);
}
if (cx->buffer) {
av_free (cx->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 (cx->packet);
}
if (cx->codec_context != NULL) {
avcodec_close (cx->codec_context);
}
if (cx->format_context != NULL) {
av_close_input_file (cx->format_context);
}
g_free (cx);
}

View File

@ -24,32 +24,32 @@
#include <libavcodec/avcodec.h>
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;
/* 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;
/* 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;
/* 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;
/* Initialise FFmpeg, should be called once on start up */

View File

@ -22,38 +22,38 @@
#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));
p->threshold = threshold;
for (bits = 0; n; n >>= 1, bits++);
p->n = 1 << --bits;
p->cx = av_rdft_init (bits, DFT_R2C);
return p;
gint bits;
SpekFftPlan *p = g_new0 (SpekFftPlan, 1);
p->input = av_mallocz (sizeof (gfloat) * n);
p->output = av_mallocz (sizeof (gfloat) * (n / 2 + 1));
p->threshold = threshold;
for (bits = 0; n; n >>= 1, bits++);
p->n = 1 << --bits;
p->cx = av_rdft_init (bits, DFT_R2C);
return p;
}
void spek_fft_execute (SpekFftPlan *p) {
int i;
int n = p->n;
int i;
int n = p->n;
av_rdft_calc (p->cx, p->input);
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];
val /= n * n;
val = 10.0 * log10f (val);
p->output[i] = val < p->threshold ? p->threshold : val;
}
/* 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];
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);
av_rdft_end (p->cx);
av_free (p->input);
av_free (p->output);
g_free (p);
}

View File

@ -23,14 +23,14 @@
#include <libavcodec/avfft.h>
typedef struct {
/* Internal data */
RDFTContext *cx;
gint n;
gint threshold;
/* Internal data */
RDFTContext *cx;
gint n;
gint threshold;
/* Exposed properties */
gfloat *input;
gfloat *output;
/* Exposed properties */
gfloat *input;
gfloat *output;
} SpekFftPlan;
/* Allocate buffers and create a new FFT plan */

View File

@ -22,61 +22,61 @@
using Gtk;
namespace Spek {
public class MessageBar : HBox {
private Label label;
private Gtk.Window win;
public class MessageBar : HBox {
private Label label;
private Gtk.Window win;
public MessageBar (string message) {
homogeneous = false;
spacing = 0;
border_width = 6;
public MessageBar (string message) {
homogeneous = false;
spacing = 0;
border_width = 6;
win = new Gtk.Window (WindowType.POPUP);
win.name = "gtk-tooltips";
win.ensure_style ();
win.style_set.connect (() => style = win.style);
win = new Gtk.Window (WindowType.POPUP);
win.name = "gtk-tooltips";
win.ensure_style ();
win.style_set.connect (() => style = win.style);
label = new Label (null);
label.use_markup = true;
label.set_markup (message);
label.ellipsize = Pango.EllipsizeMode.END;
label.xalign = 0f;
label.activate_link.connect (uri => { Platform.show_uri (uri); return true; });
var button_box = new HBox (false, 0);
button_box.spacing = 3;
var close_button = new Button ();
close_button.image = new Gtk.Image.from_stock (Stock.CLOSE, IconSize.MENU);
close_button.relief = ReliefStyle.NONE;
close_button.clicked.connect (() => hide ());
label = new Label (null);
label.use_markup = true;
label.set_markup (message);
label.ellipsize = Pango.EllipsizeMode.END;
label.xalign = 0f;
label.activate_link.connect (uri => { Platform.show_uri (uri); return true; });
var button_box = new HBox (false, 0);
button_box.spacing = 3;
var close_button = new Button ();
close_button.image = new Gtk.Image.from_stock (Stock.CLOSE, IconSize.MENU);
close_button.relief = ReliefStyle.NONE;
close_button.clicked.connect (() => hide ());
pack_start (label, true, true, 0);
pack_start (button_box, false, false, 0);
pack_start (close_button, false, false, 0);
}
pack_start (label, true, true, 0);
pack_start (button_box, false, false, 0);
pack_start (close_button, false, false, 0);
}
private bool changing_style = false;
protected override void style_set (Style? previous_style) {
if (changing_style) {
return;
}
private bool changing_style = false;
protected override void style_set (Style? previous_style) {
if (changing_style) {
return;
}
changing_style = true;
style = win.style;
label.style = style;
changing_style = false;
}
changing_style = true;
style = win.style;
label.style = style;
changing_style = false;
}
protected override bool expose_event (Gdk.EventExpose event) {
if (!is_drawable ()) {
return false;
}
protected override bool expose_event (Gdk.EventExpose event) {
if (!is_drawable ()) {
return false;
}
var cr = Gdk.cairo_create (event.window);
var color = style.bg[StateType.NORMAL];
cr.set_source_rgb (color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0);
cr.rectangle (allocation.x, allocation.y, allocation.width, allocation.height);
cr.fill ();
return base.expose_event (event);
}
}
var cr = Gdk.cairo_create (event.window);
var color = style.bg[StateType.NORMAL];
cr.set_source_rgb (color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0);
cr.rectangle (allocation.x, allocation.y, allocation.width, allocation.height);
cr.fill ();
return base.expose_event (event);
}
}
}

View File

@ -24,279 +24,279 @@
*/
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);
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 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 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;
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;
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) : "";
// 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);
}
}
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 ();
}
~Pipeline () {
stop ();
}
public void start () {
stop ();
public void start () {
stop ();
if (cx.error != null) return;
if (cx.error != null) return;
input_pos = 0;
reader_mutex = new Mutex ();
reader_cond = new Cond ();
worker_mutex = new Mutex ();
worker_cond = new Cond ();
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 ();
}
}
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;
}
}
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 ();
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;
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;
}
try {
worker_thread = Thread.create<void*> (worker_func, true);
} catch (ThreadError e) {
return null;
}
while ((size = cx.read ()) > 0) {
lock (quit) if (quit) break;
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;
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);
}
// 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;
}
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 ();
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 ();
}
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;
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);
Memory.set (output, 0, sizeof (float) * bands);
while (true) {
reader_mutex.lock ();
worker_done = true;
reader_cond.signal ();
reader_mutex.unlock ();
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 ();
worker_mutex.lock ();
while (tail == input_pos) worker_cond.wait (worker_mutex);
tail = input_pos;
worker_mutex.unlock ();
if (tail == -1) {
return null;
}
if (tail == -1) {
return null;
}
while (true) {
head = (head + 1) % input_size;
if (head == tail) {
head = prev_head;
break;
}
frames++;
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;
}
// 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;
}
for (int i = 0; i < bands; i++) {
output[i] /= num_fft;
}
if (sample == samples) break;
cb (sample++, output);
if (sample == samples) break;
cb (sample++, output);
Memory.set (output, 0, sizeof (float) * bands);
frames = 0;
num_fft = 0;
}
}
}
}
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;
}
}
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;
}
}
}

View File

@ -34,34 +34,34 @@
void spek_platform_init () {
#ifdef G_OS_DARWIN
g_object_new (GTK_TYPE_OSX_APPLICATION, NULL);
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);
/* 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
}
@ -69,141 +69,141 @@ void spek_platform_fix_args (gchar **argv, gint argc) {
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);
}
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;
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");
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));
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));
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_accel_map_foreach (NULL, accel_map_foreach);
gtk_osxapplication_ready (app);
gtk_osxapplication_ready (app);
#endif
}
gchar *spek_platform_locale_dir () {
static gchar *locale_dir = NULL;
static gchar *locale_dir = NULL;
if (!locale_dir) {
if (!locale_dir) {
#ifdef G_OS_WIN32
gchar *win32_dir;
gchar *win32_dir;
win32_dir = g_win32_get_package_installation_directory_of_module (NULL);
locale_dir = g_build_filename (win32_dir, "share", "locale", NULL);
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);
g_free (win32_dir);
#else
#ifdef G_OS_DARWIN
GtkOSXApplication *app = NULL;
const gchar *res_dir;
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);
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;
locale_dir = LOCALEDIR;
#endif
#endif
}
}
return locale_dir;
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);
/* 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;
/* ...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);
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);
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;
/* 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;
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;
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;
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);
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;
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;
/* Pango/Quartz fonts are smaller than on X. */
return 1.4;
#endif
return 1.0;
return 1.0;
}

View File

@ -19,86 +19,86 @@
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},
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"},
{"de", "Deutsch"},
{"en", "English"},
{"es", "Español"},
{"fr", "Français"},
{"it", "Italiano"},
{"nl", "Nederlands"},
{"pl", "Polski"},
{"ru", "Русский"},
{"sv", "Svenska"},
{"uk", "Українська"}
};
{"de", "Deutsch"},
{"en", "English"},
{"es", "Español"},
{"fr", "Français"},
{"it", "Italiano"},
{"nl", "Nederlands"},
{"pl", "Polski"},
{"ru", "Русский"},
{"sv", "Svenska"},
{"uk", "Українська"}
};
public PreferencesDialog () {
title = _("Preferences");
modal = true;
resizable = false;
window_position = WindowPosition.CENTER_ON_PARENT;
languages[0,1] = _("(system default)");
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 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);
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 ();
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);
}
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 ();
}
}
private void on_response (Dialog dialog, int response_id) {
Preferences.instance.save ();
destroy ();
}
}
}

View File

@ -17,80 +17,80 @@
*/
namespace Spek {
public class Preferences {
private KeyFile key_file;
private string file_name;
public class Preferences {
private KeyFile key_file;
private string file_name;
private Preferences () {
file_name = Path.build_filename (Environment.get_user_config_dir (), "spek");
DirUtils.create_with_parents (file_name, 0755);
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) {
}
}
private Preferences () {
file_name = Path.build_filename (Environment.get_user_config_dir (), "spek");
DirUtils.create_with_parents (file_name, 0755);
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 ();
}
~Preferences () {
save ();
}
private static Preferences _instance;
public static Preferences instance {
get {
if (_instance == null) {
_instance = new Preferences ();
}
return _instance;
}
}
private static Preferences _instance;
public static Preferences instance {
get {
if (_instance == null) {
_instance = new Preferences ();
}
return _instance;
}
}
public void save () {
var output = FileStream.open (file_name, "w+");
if (output != null) {
output.puts (key_file.to_data ());
}
}
public void save () {
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 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 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);
}
}
}
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);
}
}
}
}

View File

@ -20,104 +20,104 @@ using Cairo;
using Pango;
namespace Spek {
class Ruler : GLib.Object {
public enum Position {
TOP,
RIGHT,
BOTTOM,
LEFT
}
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;
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 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 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;
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;
}
}
// 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.
}
// 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 ();
}
}
}
// 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 ();
}
}
}
}

View File

@ -22,298 +22,298 @@ using Gtk;
using Pango;
namespace Spek {
class Spectrogram : DrawingArea {
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;
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 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 ();
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 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 = "";
public void open (string file_name) {
this.file_name = file_name;
this.info = "";
start ();
}
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);
}
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 ();
}
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;
}
// 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 ();
}
queue_draw ();
}
private int prev_width = -1;
protected override void size_allocate (Gdk.Rectangle allocation) {
base.size_allocate (allocation);
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;
bool width_changed = prev_width != allocation.width;
prev_width = allocation.width;
if (file_name != null && width_changed) {
start ();
}
}
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; });
}
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);
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 ();
// 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;
}
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;
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 ();
// 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));
// 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 ();
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);
// 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 ();
// 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 ();
// 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 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));
}
// 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 ();
// 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 ();
// 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);
// 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 ();
}
// 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 ();
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;
}
// 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;
}
// 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;
// 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;
}
}
// 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;
}
}
}

View File

@ -20,29 +20,29 @@ using Gdk;
using Gtk;
namespace Spek {
public class Window : Gtk.Window {
public class Window : Gtk.Window {
private UIManager ui;
private MessageBar message_bar;
private Spectrogram spectrogram;
private string description;
private string cur_dir;
private FileFilter filter_all;
private FileFilter filter_audio;
private FileFilter filter_png;
private UIManager ui;
private MessageBar message_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 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 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 = """
private const string UI = """
<ui>
<menubar name='MenuBar'>
<menu action='File'>
@ -68,275 +68,275 @@ namespace Spek {
</ui>
""";
private const Gtk.TargetEntry[] DEST_TARGET_ENTRIES = {
{ "text/uri-list", 0, 0 }
};
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);
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 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;
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;
message_bar = new MessageBar (_("A new version of Spek is available on <a href=\"http://www.spek-project.org\">www.spek-project.org</a>"));
message_bar = new MessageBar (_("A new version of Spek is available on <a href=\"http://www.spek-project.org\">www.spek-project.org</a>"));
spectrogram = new Spectrogram ();
cur_dir = Environment.get_home_dir ();
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);
}
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 (message_bar, false, true, 0);
vbox.pack_start (spectrogram, true, true, 0);
add (vbox);
menubar.show_all ();
toolbar.show_all ();
spectrogram.show_all ();
vbox.show ();
var vbox = new VBox (false, 0);
vbox.pack_start (menubar, false, true, 0);
vbox.pack_start (toolbar, false, true, 0);
vbox.pack_start (message_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 ();
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);
// 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);
}
if (file_name != null) {
open_file (file_name);
}
try {
Thread.create<void*> (check_version, false);
} catch (ThreadError e) {
}
}
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);
}
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);
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));
}
// 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_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);
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 ();
}
// 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_file_quit () {
destroy ();
}
private void on_edit_preferences () {
var dlg = new PreferencesDialog ();
dlg.transient_for = this;
dlg.run ();
}
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/";
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";
}
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 ();
}
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";
}
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);
}
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"
};
// 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;
}
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;
}
// 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;
}
// 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 (() => { message_bar.show_all (); return false; });
}
if (version != null && version > Config.PACKAGE_VERSION) {
Idle.add (() => { message_bar.show_all (); return false; });
}
// Update the preferences.
prefs.check_update = check;
prefs.last_update = (int) today.get_julian ();
prefs.save ();
return null;
}
}
// Update the preferences.
prefs.check_update = check;
prefs.last_update = (int) today.get_julian ();
prefs.save ();
return null;
}
}
}

View File

@ -17,59 +17,59 @@
*/
namespace Spek {
bool version = false;
[CCode (array_length = false, array_null_terminated = true)]
string[] files = null;
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 }
};
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);
int main (string[] args) {
Platform.fix_args (args);
if (Preferences.instance.language.length > 0) {
Environment.set_variable ("LANGUAGE", Preferences.instance.language, true);
}
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);
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;
}
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 (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;
}
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;
}
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;
}
}

View File

@ -1,16 +1,16 @@
[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;
/* Package information */
public const string PACKAGE_NAME;
public const string PACKAGE_STRING;
public const string PACKAGE_VERSION;
/* Gettext package */
public const string GETTEXT_PACKAGE;
/* 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 */
/* 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 */
}

View File

@ -1,29 +1,29 @@
[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;
[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 ();
[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 ();
}

View File

@ -1,16 +1,16 @@
[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;
[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 ();
}
[CCode (cname = "spek_fft_plan_new")]
public Plan (int n, int threshold);
[CCode (cname = "spek_fft_execute")]
public void execute ();
}
}

View File

@ -1,10 +1,10 @@
[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 ();
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 ();
}