commit 9d12bd49fe8bbdf835c958832c869d0133dd9ad3
parent d9177f7d238405adc2a17594769ac9bdf208e600
Author: Alexander Kojevnikov <alexander@kojevnikov.com>
Date: Tue, 14 Aug 2012 20:45:45 -0700
spek_audio_desc
Diffstat:
6 files changed, 213 insertions(+), 96 deletions(-)
diff --git a/src/Makefile.am b/src/Makefile.am
@@ -4,6 +4,8 @@ spek_SOURCES = \
spek.cc \
spek-audio.c \
spek-audio.h \
+ spek-audio-desc.cc \
+ spek-audio-desc.hh \
spek-fft.c \
spek-fft.h \
spek-platform.cc \
diff --git a/src/spek-audio-desc.cc b/src/spek-audio-desc.cc
@@ -0,0 +1,95 @@
+/* spek-audio-desc.cc
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wx/arrstr.h>
+#include <wx/intl.h>
+
+#include "spek-audio.h"
+
+#include "spek-audio-desc.hh"
+
+wxString spek_audio_desc(const struct spek_audio_properties *properties)
+{
+ wxArrayString items;
+
+ if (properties->codec_name) {
+ items.Add(wxString::FromUTF8(properties->codec_name));
+ }
+ if (properties->bit_rate) {
+ items.Add(wxString::Format(_("%d kbps"), (properties->bit_rate + 500) / 1000));
+ }
+ if (properties->sample_rate) {
+ items.Add(wxString::Format(_("%d Hz"), properties->sample_rate));
+ }
+ // Include bits per sample only if there is no bitrate.
+ if (properties->bits_per_sample && !properties->bit_rate) {
+ items.Add(wxString::Format(
+ wxPLURAL("%d bit", "%d bits", properties->bits_per_sample),
+ properties->bits_per_sample
+ ));
+ }
+ if (properties->channels) {
+ items.Add(wxString::Format(
+ wxPLURAL("%d channel", "%d channels", properties->channels),
+ properties->channels
+ ));
+ }
+
+ wxString desc;
+ for (int i = 0; i < items.GetCount(); ++i) {
+ if (i) {
+ desc += wxT(", ");
+ }
+ desc += items[i];
+ }
+
+ if (properties->error) {
+ wxString error;
+ switch (properties->error) {
+ case SPEK_AUDIO_CANNOT_OPEN_FILE:
+ error = _("Cannot open input file");
+ break;
+ case SPEK_AUDIO_NO_STREAMS:
+ error = _("Cannot find stream info");
+ break;
+ case SPEK_AUDIO_NO_AUDIO:
+ error = _("The file contains no audio streams");
+ break;
+ case SPEK_AUDIO_NO_DECODER:
+ error = _("Cannot find decoder");
+ break;
+ case SPEK_AUDIO_NO_DURATION:
+ error = _("Unknown duration");
+ break;
+ case SPEK_AUDIO_NO_CHANNELS:
+ error = _("No audio channels");
+ break;
+ case SPEK_AUDIO_CANNOT_OPEN_DECODER:
+ error = _("Cannot open decoder");
+ break;
+ case SPEK_AUDIO_BAD_SAMPLE_FORMAT:
+ error = _("Unsupported sample format");
+ break;
+ }
+
+ // TRANSLATORS: first %s is the error message, second %s is stream description.
+ desc = wxString::Format(_("%s: %s"), error.c_str(), desc.c_str());
+ }
+
+ return desc;
+}
diff --git a/src/spek-audio-desc.hh b/src/spek-audio-desc.hh
@@ -0,0 +1,28 @@
+/* spek-audio-desc.hh
+ *
+ * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
+ *
+ * Spek is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Spek is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Spek. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPEK_AUDIO_DESC_HH_
+#define SPEK_AUDIO_DESC_HH_
+
+#include <wx/string.h>
+
+struct spek_audio_properties;
+
+wxString spek_audio_desc(const struct spek_audio_properties *properties);
+
+#endif
diff --git a/src/spek-audio.c b/src/spek-audio.c
@@ -26,8 +26,22 @@
#include "spek-audio.h"
-// TODO: move translations to UI code, return an error code instead.
-#define _
+struct spek_audio_context
+{
+ char *file_name; // TODO: needed?
+ char *short_name;
+ AVFormatContext *format_context;
+ int audio_stream;
+ AVCodecContext *codec_context;
+ AVStream *stream;
+ AVCodec *codec;
+ int buffer_size;
+ AVPacket *packet;
+ int offset;
+
+ struct spek_audio_properties properties;
+};
+
void spek_audio_init()
{
@@ -35,26 +49,33 @@ void spek_audio_init()
av_register_all();
}
-struct spek_audio_context * spek_audio_open(const char *file_name)
+const struct spek_audio_properties * spek_audio_get_properties(struct spek_audio_context *cx)
+{
+ return &cx->properties;
+}
+
+struct spek_audio_context * spek_audio_open(const char *path)
{
- struct spek_audio_context *cx = malloc(sizeof(struct spek_audio_context));
- cx->file_name = strdup(file_name);
+ // TODO: malloc and initialise explicitely
+ struct spek_audio_context *cx = calloc(1, sizeof(struct spek_audio_context));
+ cx->file_name = strdup(path);
// av_open_input_file() cannot open files with Unicode chars in it
// when running under Windows. When this happens we will re-try
// using the corresponding short file name.
- cx->short_name = spek_platform_short_path(file_name);
+ // TODO: test if it's already fixed in FFmpeg
+ cx->short_name = spek_platform_short_path(path);
- if (avformat_open_input(&cx->format_context, file_name, NULL, NULL) != 0) {
+ if (avformat_open_input(&cx->format_context, path, NULL, NULL) != 0) {
if (!cx->short_name ||
avformat_open_input(&cx->format_context, cx->short_name, NULL, NULL) != 0 ) {
- cx->error = _("Cannot open input file");
+ cx->properties.error = SPEK_AUDIO_CANNOT_OPEN_FILE;
return cx;
}
}
if (avformat_find_stream_info(cx->format_context, NULL) < 0) {
// 24-bit APE returns an error but parses the stream info just fine.
if (cx->format_context->nb_streams <= 0) {
- cx->error = _("Cannot find stream info");
+ cx->properties.error = SPEK_AUDIO_NO_STREAMS;
return cx;
}
}
@@ -66,65 +87,65 @@ struct spek_audio_context * spek_audio_open(const char *file_name)
}
}
if (cx->audio_stream == -1) {
- cx->error = _("The file contains no audio streams");
+ cx->properties.error = SPEK_AUDIO_NO_AUDIO;
return cx;
}
cx->stream = cx->format_context->streams[cx->audio_stream];
cx->codec_context = cx->stream->codec;
cx->codec = avcodec_find_decoder(cx->codec_context->codec_id);
if (cx->codec == NULL) {
- cx->error = _("Cannot find decoder");
+ cx->properties.error = SPEK_AUDIO_NO_DECODER;
return cx;
}
// We can already fill in the stream info even if the codec won't be able to open it.
- cx->codec_name = 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) {
+ cx->properties.codec_name = strdup(cx->codec->long_name);
+ cx->properties.bit_rate = cx->codec_context->bit_rate;
+ cx->properties.sample_rate = cx->codec_context->sample_rate;
+ cx->properties.bits_per_sample = cx->codec_context->bits_per_raw_sample;
+ if (!cx->properties.bits_per_sample) {
// APE uses bpcs, FLAC uses bprs.
- cx->bits_per_sample = cx->codec_context->bits_per_coded_sample;
+ cx->properties.bits_per_sample = cx->codec_context->bits_per_coded_sample;
}
- cx->channels = cx->codec_context->channels;
+ cx->properties.channels = cx->codec_context->channels;
if (cx->stream->duration != AV_NOPTS_VALUE) {
- cx->duration = cx->stream->duration * av_q2d(cx->stream->time_base);
+ cx->properties.duration = cx->stream->duration * av_q2d(cx->stream->time_base);
} else if (cx->format_context->duration != AV_NOPTS_VALUE) {
- cx->duration = cx->format_context->duration / (double) AV_TIME_BASE;
+ cx->properties.duration = cx->format_context->duration / (double) AV_TIME_BASE;
} else {
- cx->error = _("Unknown duration");
+ cx->properties.error = SPEK_AUDIO_NO_DURATION;
return cx;
}
- if (cx->channels <= 0) {
- cx->error = _("No audio channels");
+ if (cx->properties.channels <= 0) {
+ cx->properties.error = SPEK_AUDIO_NO_CHANNELS;
return cx;
}
if (avcodec_open2(cx->codec_context, cx->codec, NULL) < 0) {
- cx->error = _("Cannot open decoder");
+ cx->properties.error = SPEK_AUDIO_CANNOT_OPEN_DECODER;
return cx;
}
switch (cx->codec_context->sample_fmt) {
case SAMPLE_FMT_S16:
- cx->width = 16;
- cx->fp = false;
+ cx->properties.width = 16;
+ cx->properties.fp = false;
break;
case SAMPLE_FMT_S32:
- cx->width = 32;
- cx->fp = false;
+ cx->properties.width = 32;
+ cx->properties.fp = false;
break;
case SAMPLE_FMT_FLT:
- cx->width = 32;
- cx->fp = true;
+ cx->properties.width = 32;
+ cx->properties.fp = true;
break;
case SAMPLE_FMT_DBL:
- cx->width = 64;
- cx->fp = true;
+ cx->properties.width = 64;
+ cx->properties.fp = true;
break;
default:
- cx->error = _("Unsupported sample format");
+ cx->properties.error = SPEK_AUDIO_BAD_SAMPLE_FORMAT;
return cx;
}
cx->buffer_size = (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2;
- cx->buffer = av_malloc(cx->buffer_size);
+ cx->properties.buffer = av_malloc(cx->buffer_size);
cx->packet = av_mallocz(sizeof(AVPacket));
av_init_packet(cx->packet);
cx->offset = 0;
@@ -133,16 +154,17 @@ struct spek_audio_context * spek_audio_open(const char *file_name)
void spek_audio_start(struct spek_audio_context *cx, int samples)
{
- int64_t rate = cx->sample_rate * (int64_t) cx->stream->time_base.num;
+ int64_t rate = cx->properties.sample_rate * (int64_t) cx->stream->time_base.num;
int64_t duration = (int64_t)
- (cx->duration * cx->stream->time_base.den / cx->stream->time_base.num);
- cx->error_base = samples * (int64_t)cx->stream->time_base.den;
- cx->frames_per_interval = av_rescale_rnd(duration, rate, cx->error_base, AV_ROUND_DOWN);
- cx->error_per_interval = (duration * rate) % cx->error_base;
+ (cx->properties.duration * cx->stream->time_base.den / cx->stream->time_base.num);
+ cx->properties.error_base = samples * (int64_t)cx->stream->time_base.den;
+ cx->properties.frames_per_interval = av_rescale_rnd(
+ duration, rate, cx->properties.error_base, AV_ROUND_DOWN);
+ cx->properties.error_per_interval = (duration * rate) % cx->properties.error_base;
}
int spek_audio_read(struct spek_audio_context *cx) {
- if (cx->error) {
+ if (cx->properties.error) {
return -1;
}
@@ -150,7 +172,7 @@ int spek_audio_read(struct spek_audio_context *cx) {
while (cx->packet->size > 0) {
int buffer_size = cx->buffer_size;
int len = avcodec_decode_audio3(
- cx->codec_context, (int16_t *)cx->buffer, &buffer_size, cx->packet);
+ cx->codec_context, (int16_t *)cx->properties.buffer, &buffer_size, cx->packet);
if (len < 0) {
// Error, skip the frame.
cx->packet->size = 0;
@@ -195,11 +217,11 @@ void spek_audio_close (struct spek_audio_context *cx)
if (cx->short_name != NULL) {
free(cx->short_name);
}
- if (cx->codec_name != NULL) {
- free(cx->codec_name);
+ if (cx->properties.codec_name != NULL) {
+ free(cx->properties.codec_name);
}
- if (cx->buffer) {
- av_free(cx->buffer);
+ if (cx->properties.buffer) {
+ av_free(cx->properties.buffer);
}
if (cx->packet) {
if (cx->packet->data) {
diff --git a/src/spek-audio.h b/src/spek-audio.h
@@ -26,29 +26,25 @@ extern "C" {
#include <stdbool.h>
#include <stdint.h>
-struct AVFormatContext;
-struct AVCodecContext;
-struct AVStream;
-struct AVCodec;
-struct AVPacket;
+struct spek_audio_context;
-struct spek_audio_context
+enum spek_audio_error
{
- // Internal data.
- char *short_name;
- AVFormatContext *format_context;
- int audio_stream;
- AVCodecContext *codec_context;
- AVStream *stream;
- AVCodec *codec;
- int buffer_size;
- AVPacket *packet;
- int offset;
+ SPEK_AUDIO_OK = 0,
+ SPEK_AUDIO_CANNOT_OPEN_FILE,
+ SPEK_AUDIO_NO_STREAMS,
+ SPEK_AUDIO_NO_AUDIO,
+ SPEK_AUDIO_NO_DECODER,
+ SPEK_AUDIO_NO_DURATION,
+ SPEK_AUDIO_NO_CHANNELS,
+ SPEK_AUDIO_CANNOT_OPEN_DECODER,
+ SPEK_AUDIO_BAD_SAMPLE_FORMAT,
+};
- // Exposed properties.
- char *file_name;
+struct spek_audio_properties
+{
char *codec_name;
- char *error;
+ enum spek_audio_error error;
int bit_rate;
int sample_rate;
int bits_per_sample;
@@ -56,6 +52,7 @@ struct spek_audio_context
bool fp; // floating-point sample representation
int channels;
double duration;
+ // TODO: these four guys don't belong here, move them somewhere else when revamping the pipeline
uint8_t *buffer;
int64_t frames_per_interval;
int64_t error_per_interval;
@@ -67,7 +64,9 @@ void spek_audio_init();
// Open the file, check if it has an audio stream which can be decoded.
// On error, initialises the `error` field in the returned context.
-struct spek_audio_context * spek_audio_open(const char *file_name);
+struct spek_audio_context * spek_audio_open(const char *path);
+
+const struct spek_audio_properties * spek_audio_get_properties(struct spek_audio_context *cs);
// Prepare the context for reading audio samples.
void spek_audio_start(struct spek_audio_context *cx, int samples);
diff --git a/src/spek-pipeline.vala b/src/spek-pipeline.vala
@@ -25,9 +25,6 @@
namespace Spek {
public class Pipeline {
- public string description { get; private set; }
- public int sample_rate { get; private set; }
- public double duration { get { return cx.duration; } }
public delegate void Callback (int sample, float[] values);
private Audio.Context cx;
@@ -61,33 +58,7 @@ namespace Spek {
this.threshold = threshold;
this.cb = cb;
- // Build the description string.
- string[] items = {};
- if (cx.codec_name != null) {
- items += cx.codec_name;
- }
- if (cx.bit_rate != 0) {
- items += _("%d kbps").printf ((cx.bit_rate + 500) / 1000);
- }
- if (cx.sample_rate != 0) {
- items += _("%d Hz").printf (cx.sample_rate);
- }
- // Show bits per sample only if there is no bitrate.
- if (cx.bits_per_sample != 0 && cx.bit_rate == 0) {
- items += ngettext (
- "%d bit", "%d bits", cx.bits_per_sample).printf (cx.bits_per_sample);
- }
- if (cx.channels != 0) {
- items += ngettext ("%d channel", "%d channels", cx.channels).
- printf (cx.channels);
- }
- description = items.length > 0 ? string.joinv (", ", items) : "";
-
- if (cx.error != null) {
- // TRANSLATORS: first %s is the error message, second %s is stream description.
- description = _("%s: %s").printf (cx.error, description);
- } else {
- this.sample_rate = cx.sample_rate;
+ if (!cx.error) {
this.nfft = 2 * bands - 2;
this.coss = new float[nfft];
float cf = 2f * (float) Math.PI / this.nfft;
@@ -109,7 +80,7 @@ namespace Spek {
public void start () {
stop ();
- if (cx.error != null) return;
+ if (!cx.error) return;
input_pos = 0;
reader_mutex = new Mutex ();