mirror of
https://github.com/alexkay/spek.git
synced 2025-04-16 08:40:33 +03:00
C++ify spek_audio
This commit is contained in:
parent
a75d6408e8
commit
da184788d1
@ -5,8 +5,8 @@ AM_INIT_AUTOMAKE([1.11.1 foreign no-dist-gzip dist-xz])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
AC_LANG([C++])
|
||||
AC_PROG_CXX([clang++ g++])
|
||||
CXXFLAGS="$CXXFLAGS -std=c++11"
|
||||
AC_PROG_CXX([g++47 g++])
|
||||
CXXFLAGS="$CXXFLAGS -std=gnu++0x -Wall -Wextra"
|
||||
AC_PROG_CXXCPP
|
||||
AC_PROG_RANLIB
|
||||
AC_PROG_INSTALL
|
||||
|
@ -14,18 +14,18 @@ libspek_a_SOURCES = \
|
||||
|
||||
libspek_a_CPPFLAGS = \
|
||||
-include config.h \
|
||||
-pthread
|
||||
-pthread \
|
||||
$(WX_CPPFLAGS)
|
||||
|
||||
libspek_a_CXXFLAGS = \
|
||||
$(FFMPEG_CFLAGS)
|
||||
$(FFMPEG_CFLAGS) \
|
||||
$(WX_CXXFLAGS_ONLY)
|
||||
|
||||
bin_PROGRAMS = spek
|
||||
|
||||
spek_SOURCES = \
|
||||
spek-artwork.cc \
|
||||
spek-artwork.h \
|
||||
spek-audio-desc.cc \
|
||||
spek-audio-desc.h \
|
||||
spek-events.cc \
|
||||
spek-events.h \
|
||||
spek-platform.cc \
|
||||
|
@ -1,103 +0,0 @@
|
||||
/* 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.h"
|
||||
|
||||
#define ngettext wxPLURAL
|
||||
|
||||
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(
|
||||
ngettext("%d bit", "%d bits", properties->bits_per_sample),
|
||||
properties->bits_per_sample
|
||||
));
|
||||
}
|
||||
if (properties->channels) {
|
||||
items.Add(wxString::Format(
|
||||
ngettext("%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;
|
||||
case SPEK_AUDIO_OK:
|
||||
break;
|
||||
}
|
||||
|
||||
if (desc.IsEmpty()) {
|
||||
desc = error;
|
||||
} else {
|
||||
// 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;
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/* spek-audio-desc.h
|
||||
*
|
||||
* Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
|
||||
*
|
||||
* Spek is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Spek is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Spek. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SPEK_AUDIO_DESC_H_
|
||||
#define SPEK_AUDIO_DESC_H_
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
struct spek_audio_properties;
|
||||
|
||||
wxString spek_audio_desc(const struct spek_audio_properties *properties);
|
||||
|
||||
#endif
|
@ -16,10 +16,8 @@
|
||||
* along with Spek. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
extern "C" {
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/mathematics.h>
|
||||
@ -27,166 +25,253 @@ extern "C" {
|
||||
|
||||
#include "spek-audio.h"
|
||||
|
||||
struct spek_audio_context
|
||||
class AudioFileImpl : public AudioFile
|
||||
{
|
||||
public:
|
||||
AudioFileImpl(
|
||||
AudioError error, AVFormatContext *format_context, int audio_stream,
|
||||
const std::string& codec_name, int bit_rate, int sample_rate, int bits_per_sample,
|
||||
int channels, double duration, bool is_planar, int width, bool fp
|
||||
);
|
||||
~AudioFileImpl() override;
|
||||
void start(int samples) override;
|
||||
int read() override;
|
||||
|
||||
AudioError get_error() const override { return this->error; }
|
||||
std::string get_codec_name() const override { return this->codec_name; }
|
||||
int get_bit_rate() const override { return this->bit_rate; }
|
||||
int get_sample_rate() const override { return this->sample_rate; }
|
||||
int get_bits_per_sample() const override { return this->bits_per_sample; }
|
||||
int get_channels() const override { return this->channels; }
|
||||
double get_duration() const override { return this->duration; }
|
||||
int get_width() const override { return this->width; }
|
||||
bool get_fp() const override { return this->fp; }
|
||||
const uint8_t *get_buffer() const override { return this->buffer; }
|
||||
int64_t get_frames_per_interval() const override { return this->frames_per_interval; }
|
||||
int64_t get_error_per_interval() const override { return this->error_per_interval; }
|
||||
int64_t get_error_base() const override { return this->error_base; }
|
||||
|
||||
private:
|
||||
AudioError error;
|
||||
AVFormatContext *format_context;
|
||||
int audio_stream;
|
||||
AVCodecContext *codec_context;
|
||||
AVStream *stream;
|
||||
AVCodec *codec;
|
||||
int buffer_size;
|
||||
std::string codec_name;
|
||||
int bit_rate;
|
||||
int sample_rate;
|
||||
int bits_per_sample;
|
||||
int channels;
|
||||
double duration;
|
||||
bool is_planar;
|
||||
int width;
|
||||
bool fp;
|
||||
|
||||
AVPacket packet;
|
||||
int offset;
|
||||
int is_planar;
|
||||
AVFrame *frame;
|
||||
|
||||
struct spek_audio_properties properties;
|
||||
int buffer_size;
|
||||
uint8_t *buffer;
|
||||
// TODO: these guys don't belong here, move them somewhere else when revamping the pipeline
|
||||
int64_t frames_per_interval;
|
||||
int64_t error_per_interval;
|
||||
int64_t error_base;
|
||||
};
|
||||
|
||||
|
||||
void spek_audio_init()
|
||||
Audio::Audio()
|
||||
{
|
||||
av_register_all();
|
||||
}
|
||||
|
||||
const struct spek_audio_properties * spek_audio_get_properties(struct spek_audio_context *cx)
|
||||
std::unique_ptr<AudioFile> Audio::open(const std::string& file_name)
|
||||
{
|
||||
return &cx->properties;
|
||||
}
|
||||
AudioError error = AudioError::OK;
|
||||
|
||||
struct spek_audio_context * spek_audio_open(const char *path)
|
||||
{
|
||||
// TODO: malloc and initialise explicitely
|
||||
struct spek_audio_context *cx =
|
||||
(spek_audio_context *)calloc(1, sizeof(struct spek_audio_context));
|
||||
|
||||
if (avformat_open_input(&cx->format_context, path, NULL, NULL) != 0) {
|
||||
cx->properties.error = SPEK_AUDIO_CANNOT_OPEN_FILE;
|
||||
return cx;
|
||||
AVFormatContext *format_context = nullptr;
|
||||
if (avformat_open_input(&format_context, file_name.c_str(), nullptr, nullptr) != 0) {
|
||||
error = AudioError::CANNOT_OPEN_FILE;
|
||||
}
|
||||
if (avformat_find_stream_info(cx->format_context, NULL) < 0) {
|
||||
|
||||
if (!error && avformat_find_stream_info(format_context, nullptr) < 0) {
|
||||
// 24-bit APE returns an error but parses the stream info just fine.
|
||||
if (cx->format_context->nb_streams <= 0) {
|
||||
cx->properties.error = SPEK_AUDIO_NO_STREAMS;
|
||||
return cx;
|
||||
// TODO: old comment, verify
|
||||
if (format_context->nb_streams <= 0) {
|
||||
error = AudioError::NO_STREAMS;
|
||||
}
|
||||
}
|
||||
cx->audio_stream = -1;
|
||||
for (int i = 0; i < cx->format_context->nb_streams; i++) {
|
||||
if (cx->format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
cx->audio_stream = i;
|
||||
break;
|
||||
|
||||
int audio_stream = -1;
|
||||
if (!error) {
|
||||
for (unsigned int i = 0; i < format_context->nb_streams; i++) {
|
||||
if (format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
audio_stream = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cx->audio_stream == -1) {
|
||||
cx->properties.error = SPEK_AUDIO_NO_AUDIO;
|
||||
return cx;
|
||||
if (audio_stream == -1) {
|
||||
error = AudioError::NO_AUDIO;
|
||||
}
|
||||
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->properties.error = SPEK_AUDIO_NO_DECODER;
|
||||
return cx;
|
||||
|
||||
AVStream *stream = nullptr;
|
||||
AVCodecContext *codec_context = nullptr;
|
||||
AVCodec *codec = nullptr;
|
||||
if (!error) {
|
||||
stream = format_context->streams[audio_stream];
|
||||
codec_context = stream->codec;
|
||||
codec = avcodec_find_decoder(codec_context->codec_id);
|
||||
if (!codec) {
|
||||
error = AudioError::NO_DECODER;
|
||||
}
|
||||
}
|
||||
// We can already fill in the stream info even if the codec won't be able to open it.
|
||||
cx->properties.codec_name = strdup(cx->codec->long_name);
|
||||
cx->properties.bit_rate = cx->codec_context->bit_rate;
|
||||
cx->properties.sample_rate = cx->codec_context->sample_rate;
|
||||
cx->properties.bits_per_sample = cx->codec_context->bits_per_raw_sample;
|
||||
if (!cx->properties.bits_per_sample) {
|
||||
// APE uses bpcs, FLAC uses bprs.
|
||||
cx->properties.bits_per_sample = cx->codec_context->bits_per_coded_sample;
|
||||
|
||||
std::string codec_name;
|
||||
int bit_rate = 0;
|
||||
int sample_rate = 0;
|
||||
int bits_per_sample = 0;
|
||||
int channels = 0;
|
||||
double duration = 0;
|
||||
if (!error) {
|
||||
// We can already fill in the stream info even if the codec won't be able to open it.
|
||||
codec_name = codec->long_name;
|
||||
bit_rate = codec_context->bit_rate;
|
||||
sample_rate = codec_context->sample_rate;
|
||||
bits_per_sample = codec_context->bits_per_raw_sample;
|
||||
if (!bits_per_sample) {
|
||||
// APE uses bpcs, FLAC uses bprs.
|
||||
// TODO: old comment, verify
|
||||
bits_per_sample = codec_context->bits_per_coded_sample;
|
||||
}
|
||||
channels = codec_context->channels;
|
||||
|
||||
if (stream->duration != AV_NOPTS_VALUE) {
|
||||
duration = stream->duration * av_q2d(stream->time_base);
|
||||
} else if (format_context->duration != AV_NOPTS_VALUE) {
|
||||
duration = format_context->duration / (double) AV_TIME_BASE;
|
||||
} else {
|
||||
error = AudioError::NO_DURATION;
|
||||
}
|
||||
|
||||
if (!error && channels <= 0) {
|
||||
error = AudioError::NO_CHANNELS;
|
||||
}
|
||||
}
|
||||
cx->properties.channels = cx->codec_context->channels;
|
||||
if (cx->stream->duration != AV_NOPTS_VALUE) {
|
||||
cx->properties.duration = cx->stream->duration * av_q2d(cx->stream->time_base);
|
||||
} else if (cx->format_context->duration != AV_NOPTS_VALUE) {
|
||||
cx->properties.duration = cx->format_context->duration / (double) AV_TIME_BASE;
|
||||
} else {
|
||||
cx->properties.error = SPEK_AUDIO_NO_DURATION;
|
||||
return cx;
|
||||
|
||||
if (!error && avcodec_open2(codec_context, codec, nullptr) < 0) {
|
||||
error = AudioError::CANNOT_OPEN_DECODER;
|
||||
}
|
||||
if (cx->properties.channels <= 0) {
|
||||
cx->properties.error = SPEK_AUDIO_NO_CHANNELS;
|
||||
return cx;
|
||||
|
||||
bool is_planar = false;
|
||||
int width = 0;
|
||||
bool fp = false;
|
||||
if (!error) {
|
||||
is_planar = av_sample_fmt_is_planar(codec_context->sample_fmt);
|
||||
width = av_get_bytes_per_sample(codec_context->sample_fmt);
|
||||
AVSampleFormat fmt = codec_context->sample_fmt;
|
||||
if (fmt == AV_SAMPLE_FMT_S16 || fmt == AV_SAMPLE_FMT_S16P ||
|
||||
fmt == AV_SAMPLE_FMT_S32 || fmt == AV_SAMPLE_FMT_S32P) {
|
||||
fp = false;
|
||||
} else if (fmt == AV_SAMPLE_FMT_FLT || fmt == AV_SAMPLE_FMT_FLTP ||
|
||||
fmt == AV_SAMPLE_FMT_DBL || fmt == AV_SAMPLE_FMT_DBLP ) {
|
||||
fp = true;
|
||||
} else {
|
||||
error = AudioError::BAD_SAMPLE_FORMAT;
|
||||
}
|
||||
}
|
||||
if (avcodec_open2(cx->codec_context, cx->codec, NULL) < 0) {
|
||||
cx->properties.error = SPEK_AUDIO_CANNOT_OPEN_DECODER;
|
||||
return cx;
|
||||
}
|
||||
cx->is_planar = av_sample_fmt_is_planar(cx->codec_context->sample_fmt);
|
||||
cx->properties.width = av_get_bytes_per_sample(cx->codec_context->sample_fmt);
|
||||
switch (cx->codec_context->sample_fmt) {
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
cx->properties.fp = false;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
cx->properties.fp = true;
|
||||
break;
|
||||
default:
|
||||
cx->properties.error = SPEK_AUDIO_BAD_SAMPLE_FORMAT;
|
||||
return cx;
|
||||
}
|
||||
cx->buffer_size = 0;
|
||||
cx->properties.buffer = NULL;
|
||||
av_init_packet(&cx->packet);
|
||||
cx->frame = avcodec_alloc_frame();
|
||||
cx->offset = 0;
|
||||
return cx;
|
||||
|
||||
return std::unique_ptr<AudioFile>(new AudioFileImpl(
|
||||
error, format_context, audio_stream,
|
||||
codec_name, bit_rate, sample_rate, bits_per_sample,
|
||||
channels, duration, is_planar, width, fp
|
||||
));
|
||||
}
|
||||
|
||||
void spek_audio_start(struct spek_audio_context *cx, int samples)
|
||||
AudioFileImpl::AudioFileImpl(
|
||||
AudioError error, AVFormatContext *format_context, int audio_stream,
|
||||
const std::string& codec_name, int bit_rate, int sample_rate, int bits_per_sample,
|
||||
int channels, double duration, bool is_planar, int width, bool fp
|
||||
) :
|
||||
error(error), format_context(format_context), audio_stream(audio_stream),
|
||||
codec_name(codec_name), bit_rate(bit_rate),
|
||||
sample_rate(sample_rate), bits_per_sample(bits_per_sample),
|
||||
channels(channels), duration(duration), is_planar(is_planar), width(width), fp(fp)
|
||||
{
|
||||
int64_t rate = cx->properties.sample_rate * (int64_t) cx->stream->time_base.num;
|
||||
int64_t duration = (int64_t)
|
||||
(cx->properties.duration * cx->stream->time_base.den / cx->stream->time_base.num);
|
||||
cx->properties.error_base = samples * (int64_t)cx->stream->time_base.den;
|
||||
cx->properties.frames_per_interval = av_rescale_rnd(
|
||||
duration, rate, cx->properties.error_base, AV_ROUND_DOWN);
|
||||
cx->properties.error_per_interval = (duration * rate) % cx->properties.error_base;
|
||||
av_init_packet(&this->packet);
|
||||
this->packet.data = nullptr;
|
||||
this->packet.size = 0;
|
||||
this->offset = 0;
|
||||
this->frame = avcodec_alloc_frame();
|
||||
this->buffer_size = 0;
|
||||
this->buffer = nullptr;
|
||||
this->frames_per_interval = 0;
|
||||
this->error_per_interval = 0;
|
||||
this->error_base = 0;
|
||||
}
|
||||
|
||||
int spek_audio_read(struct spek_audio_context *cx) {
|
||||
if (cx->properties.error) {
|
||||
AudioFileImpl::~AudioFileImpl()
|
||||
{
|
||||
if (this->buffer) {
|
||||
av_freep(&this->buffer);
|
||||
}
|
||||
if (this->frame) {
|
||||
avcodec_free_frame(&this->frame);
|
||||
}
|
||||
if (this->packet.data) {
|
||||
this->packet.data -= this->offset;
|
||||
this->packet.size += this->offset;
|
||||
this->offset = 0;
|
||||
av_free_packet(&this->packet);
|
||||
}
|
||||
if (this->format_context) {
|
||||
avformat_close_input(&this->format_context);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioFileImpl::start(int samples)
|
||||
{
|
||||
AVStream *stream = this->format_context->streams[this->audio_stream];
|
||||
int64_t rate = this->sample_rate * (int64_t)stream->time_base.num;
|
||||
int64_t duration = (int64_t)(this->duration * stream->time_base.den / stream->time_base.num);
|
||||
this->error_base = samples * (int64_t)stream->time_base.den;
|
||||
this->frames_per_interval = av_rescale_rnd(duration, rate, this->error_base, AV_ROUND_DOWN);
|
||||
this->error_per_interval = (duration * rate) % this->error_base;
|
||||
}
|
||||
|
||||
int AudioFileImpl::read()
|
||||
{
|
||||
if (!!this->error) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
while (cx->packet.size > 0) {
|
||||
avcodec_get_frame_defaults(cx->frame);
|
||||
while (this->packet.size > 0) {
|
||||
avcodec_get_frame_defaults(this->frame);
|
||||
auto codec_context = this->format_context->streams[this->audio_stream]->codec;
|
||||
int got_frame = 0;
|
||||
int len = avcodec_decode_audio4(cx->codec_context, cx->frame, &got_frame, &cx->packet);
|
||||
int len = avcodec_decode_audio4(codec_context, this->frame, &got_frame, &this->packet);
|
||||
if (len < 0) {
|
||||
// Error, skip the frame.
|
||||
break;
|
||||
}
|
||||
cx->packet.data += len;
|
||||
cx->packet.size -= len;
|
||||
cx->offset += len;
|
||||
this->packet.data += len;
|
||||
this->packet.size -= len;
|
||||
this->offset += len;
|
||||
if (!got_frame) {
|
||||
// No data yet, get more frames.
|
||||
continue;
|
||||
}
|
||||
// We have data, return it and come back for more later.
|
||||
int samples = cx->frame->nb_samples;
|
||||
int channels = cx->properties.channels;
|
||||
int width = cx->properties.width;
|
||||
int samples = this->frame->nb_samples;
|
||||
int channels = this->channels;
|
||||
int width = this->width;
|
||||
int buffer_size = samples * channels * width;
|
||||
if (buffer_size > cx->buffer_size) {
|
||||
cx->properties.buffer = (uint8_t*)av_realloc(cx->properties.buffer, buffer_size);
|
||||
cx->buffer_size = buffer_size;
|
||||
if (buffer_size > this->buffer_size) {
|
||||
this->buffer = (uint8_t*)av_realloc(this->buffer, buffer_size);
|
||||
this->buffer_size = buffer_size;
|
||||
}
|
||||
if (cx->is_planar) {
|
||||
if (this->is_planar) {
|
||||
for (int channel = 0; channel < channels; ++channel) {
|
||||
uint8_t *buffer = cx->properties.buffer + channel * width;
|
||||
uint8_t *data = cx->frame->data[channel];
|
||||
uint8_t *buffer = this->buffer + channel * width;
|
||||
uint8_t *data = this->frame->data[channel];
|
||||
for (int sample = 0; sample < samples; ++sample) {
|
||||
for (int i = 0; i < width; ++i) {
|
||||
*buffer++ = *data++;
|
||||
@ -195,23 +280,23 @@ int spek_audio_read(struct spek_audio_context *cx) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
memcpy(cx->properties.buffer, cx->frame->data[0], buffer_size);
|
||||
memcpy(this->buffer, this->frame->data[0], buffer_size);
|
||||
}
|
||||
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);
|
||||
if (this->packet.data) {
|
||||
this->packet.data -= this->offset;
|
||||
this->packet.size += this->offset;
|
||||
this->offset = 0;
|
||||
av_free_packet(&this->packet);
|
||||
}
|
||||
|
||||
int res = 0;
|
||||
while ((res = av_read_frame(cx->format_context, &cx->packet)) >= 0) {
|
||||
if (cx->packet.stream_index == cx->audio_stream) {
|
||||
while ((res = av_read_frame(this->format_context, &this->packet)) >= 0) {
|
||||
if (this->packet.stream_index == this->audio_stream) {
|
||||
break;
|
||||
}
|
||||
av_free_packet(&cx->packet);
|
||||
av_free_packet(&this->packet);
|
||||
}
|
||||
if (res < 0) {
|
||||
// End of file or error.
|
||||
@ -219,29 +304,3 @@ int spek_audio_read(struct spek_audio_context *cx) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void spek_audio_close(struct spek_audio_context *cx)
|
||||
{
|
||||
if (cx->properties.codec_name != NULL) {
|
||||
free(cx->properties.codec_name);
|
||||
}
|
||||
if (cx->frame != NULL) {
|
||||
avcodec_free_frame(&cx->frame);
|
||||
}
|
||||
if (cx->packet.data) {
|
||||
cx->packet.data -= cx->offset;
|
||||
cx->packet.size += cx->offset;
|
||||
cx->offset = 0;
|
||||
av_free_packet(&cx->packet);
|
||||
}
|
||||
if (cx->properties.buffer) {
|
||||
av_freep(&cx->properties.buffer);
|
||||
}
|
||||
if (cx->codec_context != NULL) {
|
||||
avcodec_close(cx->codec_context);
|
||||
}
|
||||
if (cx->format_context != NULL) {
|
||||
avformat_close_input(&cx->format_context);
|
||||
}
|
||||
free(cx);
|
||||
}
|
||||
|
@ -19,61 +19,58 @@
|
||||
#ifndef SPEK_AUDIO_H_
|
||||
#define SPEK_AUDIO_H_
|
||||
|
||||
#include <tr1/cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
struct spek_audio_context;
|
||||
class AudioFile;
|
||||
enum class AudioError;
|
||||
|
||||
enum spek_audio_error
|
||||
class Audio
|
||||
{
|
||||
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,
|
||||
public:
|
||||
Audio();
|
||||
|
||||
std::unique_ptr<AudioFile> open(const std::string& file_name);
|
||||
};
|
||||
|
||||
struct spek_audio_properties
|
||||
class AudioFile
|
||||
{
|
||||
char *codec_name;
|
||||
enum spek_audio_error error;
|
||||
int bit_rate;
|
||||
int sample_rate;
|
||||
int bits_per_sample;
|
||||
int width; // number of bits used to store a sample
|
||||
bool fp; // floating-point sample representation
|
||||
int channels;
|
||||
double duration;
|
||||
// TODO: these four guys don't belong here, move them somewhere else when revamping the pipeline
|
||||
uint8_t *buffer;
|
||||
int64_t frames_per_interval;
|
||||
int64_t error_per_interval;
|
||||
int64_t error_base;
|
||||
public:
|
||||
virtual ~AudioFile() {}
|
||||
|
||||
virtual void start(int samples) = 0;
|
||||
virtual int read() = 0;
|
||||
|
||||
virtual AudioError get_error() const = 0;
|
||||
virtual std::string get_codec_name() const = 0;
|
||||
virtual int get_bit_rate() const = 0;
|
||||
virtual int get_sample_rate() const = 0;
|
||||
virtual int get_bits_per_sample() const = 0;
|
||||
virtual int get_channels() const = 0;
|
||||
virtual double get_duration() const = 0;
|
||||
virtual int get_width() const = 0;
|
||||
virtual bool get_fp() const = 0;
|
||||
virtual const uint8_t *get_buffer() const = 0;
|
||||
virtual int64_t get_frames_per_interval() const = 0;
|
||||
virtual int64_t get_error_per_interval() const = 0;
|
||||
virtual int64_t get_error_base() const = 0;
|
||||
};
|
||||
|
||||
// Initialise FFmpeg, should be called once on start up.
|
||||
void spek_audio_init();
|
||||
enum class AudioError
|
||||
{
|
||||
OK,
|
||||
CANNOT_OPEN_FILE,
|
||||
NO_STREAMS,
|
||||
NO_AUDIO,
|
||||
NO_DECODER,
|
||||
NO_DURATION,
|
||||
NO_CHANNELS,
|
||||
CANNOT_OPEN_DECODER,
|
||||
BAD_SAMPLE_FORMAT,
|
||||
};
|
||||
|
||||
// Open the file, check if it has an audio stream which can be decoded.
|
||||
// On error, initialises the `error` field in the returned context.
|
||||
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);
|
||||
|
||||
// Read and decode the opened audio stream.
|
||||
// Returns -1 on error, 0 if there's nothing left to read
|
||||
// or the number of bytes decoded into the buffer.
|
||||
int spek_audio_read(struct spek_audio_context *cx);
|
||||
|
||||
// Closes the file opened with spek_audio_open,
|
||||
// frees all allocated buffers and the context
|
||||
void spek_audio_close(struct spek_audio_context *cx);
|
||||
inline bool operator!(AudioError error) {
|
||||
return error == AudioError::OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* spek-events.cc
|
||||
*
|
||||
* Copyright (C) 2012 Alexander Kojevnikov <alexander@kojevnikov.com>
|
||||
* Copyright (C) 2012-2013 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
|
||||
@ -27,7 +27,7 @@ SpekHaveSampleEvent::SpekHaveSampleEvent(int bands, int sample, float *values, b
|
||||
SetEventType(SPEK_HAVE_SAMPLE);
|
||||
}
|
||||
|
||||
SpekHaveSampleEvent::SpekHaveSampleEvent(const SpekHaveSampleEvent& other)
|
||||
SpekHaveSampleEvent::SpekHaveSampleEvent(const SpekHaveSampleEvent& other) : wxEvent(other)
|
||||
{
|
||||
SetEventType(SPEK_HAVE_SAMPLE);
|
||||
this->bands = other.bands;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* spek-pipeline.cc
|
||||
*
|
||||
* Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
|
||||
* Copyright (C) 2010-2013 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
|
||||
@ -32,11 +32,17 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <wx/intl.h>
|
||||
|
||||
#include "spek-audio.h"
|
||||
#include "spek-fft.h"
|
||||
|
||||
#include "spek-pipeline.h"
|
||||
|
||||
#define ngettext wxPLURAL
|
||||
|
||||
enum
|
||||
{
|
||||
NFFT = 64 // Number of FFTs to pre-fetch.
|
||||
@ -44,8 +50,7 @@ enum
|
||||
|
||||
struct spek_pipeline
|
||||
{
|
||||
struct spek_audio_context *cx;
|
||||
const struct spek_audio_properties *properties;
|
||||
std::unique_ptr<AudioFile> file;
|
||||
int bands;
|
||||
int samples;
|
||||
spek_pipeline_cb cb;
|
||||
@ -79,14 +84,13 @@ struct spek_pipeline
|
||||
static void * reader_func(void *);
|
||||
static void * worker_func(void *);
|
||||
static void reader_sync(struct spek_pipeline *p, int pos);
|
||||
static float average_input(const struct spek_pipeline *p, void *buffer);
|
||||
static float average_input(const struct spek_pipeline *p, const void *buffer);
|
||||
|
||||
struct spek_pipeline * spek_pipeline_open(
|
||||
const char *path, int bands, int samples, spek_pipeline_cb cb, void *cb_data)
|
||||
std::unique_ptr<AudioFile> file, int bands, int samples, spek_pipeline_cb cb, void *cb_data)
|
||||
{
|
||||
struct spek_pipeline *p = (spek_pipeline*)malloc(sizeof(struct spek_pipeline));
|
||||
p->cx = spek_audio_open(path);
|
||||
p->properties = spek_audio_get_properties(p->cx);
|
||||
spek_pipeline *p = new spek_pipeline();
|
||||
p->file = std::move(file);
|
||||
p->bands = bands;
|
||||
p->samples = samples;
|
||||
p->cb = cb;
|
||||
@ -103,7 +107,7 @@ struct spek_pipeline * spek_pipeline_open(
|
||||
p->has_worker_mutex = false;
|
||||
p->has_worker_cond = false;
|
||||
|
||||
if (!p->properties->error) {
|
||||
if (!p->file->get_error()) {
|
||||
p->nfft = 2 * bands - 2;
|
||||
p->coss = (float*)malloc(p->nfft * sizeof(float));
|
||||
float cf = 2.0f * (float)M_PI / p->nfft;
|
||||
@ -114,20 +118,17 @@ struct spek_pipeline * spek_pipeline_open(
|
||||
p->input_size = p->nfft * (NFFT * 2 + 1);
|
||||
p->input = (float*)malloc(p->input_size * sizeof(float));
|
||||
p->output = (float*)malloc(bands * sizeof(float));
|
||||
spek_audio_start(p->cx, samples);
|
||||
p->file->start(samples);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
const struct spek_audio_properties * spek_pipeline_properties(struct spek_pipeline *pipeline)
|
||||
{
|
||||
return pipeline->properties;
|
||||
}
|
||||
|
||||
void spek_pipeline_start(struct spek_pipeline *p)
|
||||
{
|
||||
if (p->properties->error) return;
|
||||
if (!!p->file->get_error()) {
|
||||
return;
|
||||
}
|
||||
|
||||
p->input_pos = 0;
|
||||
p->worker_done = false;
|
||||
@ -183,11 +184,106 @@ void spek_pipeline_close(struct spek_pipeline *p)
|
||||
free(p->coss);
|
||||
p->coss = NULL;
|
||||
}
|
||||
if (p->cx) {
|
||||
spek_audio_close(p->cx);
|
||||
p->cx = NULL;
|
||||
|
||||
p->file.reset();
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
std::string spek_pipeline_desc(const struct spek_pipeline *pipeline)
|
||||
{
|
||||
std::vector<std::string> items;
|
||||
|
||||
if (!pipeline->file->get_codec_name().empty()) {
|
||||
items.push_back(pipeline->file->get_codec_name());
|
||||
}
|
||||
free(p);
|
||||
if (pipeline->file->get_bit_rate()) {
|
||||
items.push_back(std::string(
|
||||
wxString::Format(_("%d kbps"), (pipeline->file->get_bit_rate() + 500) / 1000).utf8_str()
|
||||
));
|
||||
}
|
||||
if (pipeline->file->get_sample_rate()) {
|
||||
items.push_back(std::string(
|
||||
wxString::Format(_("%d Hz"), pipeline->file->get_sample_rate()).utf8_str()
|
||||
));
|
||||
}
|
||||
// Include bits per sample only if there is no bitrate.
|
||||
if (pipeline->file->get_bits_per_sample() && !pipeline->file->get_bit_rate()) {
|
||||
items.push_back(std::string(
|
||||
wxString::Format(
|
||||
ngettext("%d bit", "%d bits", pipeline->file->get_bits_per_sample()),
|
||||
pipeline->file->get_bits_per_sample()
|
||||
).utf8_str()
|
||||
));
|
||||
}
|
||||
if (pipeline->file->get_channels()) {
|
||||
items.push_back(std::string(
|
||||
wxString::Format(
|
||||
ngettext("%d channel", "%d channels", pipeline->file->get_channels()),
|
||||
pipeline->file->get_channels()
|
||||
).utf8_str()
|
||||
));
|
||||
}
|
||||
|
||||
std::string desc;
|
||||
for (const auto& item : items) {
|
||||
if (!desc.empty()) {
|
||||
desc.append(", ");
|
||||
}
|
||||
desc.append(item);
|
||||
}
|
||||
|
||||
wxString error;
|
||||
switch (pipeline->file->get_error()) {
|
||||
case AudioError::CANNOT_OPEN_FILE:
|
||||
error = _("Cannot open input file");
|
||||
break;
|
||||
case AudioError::NO_STREAMS:
|
||||
error = _("Cannot find stream info");
|
||||
break;
|
||||
case AudioError::NO_AUDIO:
|
||||
error = _("The file contains no audio streams");
|
||||
break;
|
||||
case AudioError::NO_DECODER:
|
||||
error = _("Cannot find decoder");
|
||||
break;
|
||||
case AudioError::NO_DURATION:
|
||||
error = _("Unknown duration");
|
||||
break;
|
||||
case AudioError::NO_CHANNELS:
|
||||
error = _("No audio channels");
|
||||
break;
|
||||
case AudioError::CANNOT_OPEN_DECODER:
|
||||
error = _("Cannot open decoder");
|
||||
break;
|
||||
case AudioError::BAD_SAMPLE_FORMAT:
|
||||
error = _("Unsupported sample format");
|
||||
break;
|
||||
case AudioError::OK:
|
||||
break;
|
||||
}
|
||||
|
||||
auto error_string = std::string(error.utf8_str());
|
||||
if (desc.empty()) {
|
||||
desc = error_string;
|
||||
} else if (!error_string.empty()) {
|
||||
// TRANSLATORS: first %s is the error message, second %s is stream description.
|
||||
desc = std::string(
|
||||
wxString::Format(_("%s: %s"), error_string.c_str(), desc.c_str()).utf8_str()
|
||||
);
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
double spek_pipeline_duration(const struct spek_pipeline *pipeline)
|
||||
{
|
||||
return pipeline->file->get_duration();
|
||||
}
|
||||
|
||||
int spek_pipeline_sample_rate(const struct spek_pipeline *pipeline)
|
||||
{
|
||||
return pipeline->file->get_sample_rate();
|
||||
}
|
||||
|
||||
static void * reader_func(void *pp)
|
||||
@ -200,12 +296,12 @@ static void * reader_func(void *pp)
|
||||
}
|
||||
|
||||
int pos = 0, prev_pos = 0;
|
||||
int block_size = p->properties->width * p->properties->channels;
|
||||
int block_size = p->file->get_width() * p->file->get_channels();
|
||||
int size;
|
||||
while ((size = spek_audio_read(p->cx)) > 0) {
|
||||
while ((size = p->file->read()) > 0) {
|
||||
if (p->quit) break;
|
||||
|
||||
uint8_t *buffer = p->properties->buffer;
|
||||
const uint8_t *buffer = p->file->get_buffer();
|
||||
while (size >= block_size) {
|
||||
p->input[pos] = average_input(p, buffer);
|
||||
buffer += block_size;
|
||||
@ -290,11 +386,11 @@ static void * worker_func(void *pp)
|
||||
// If we have enough frames for an FFT or we have
|
||||
// all frames required for the interval run and FFT.
|
||||
bool int_full =
|
||||
acc_error < p->properties->error_base &&
|
||||
frames == p->properties->frames_per_interval;
|
||||
acc_error < p->file->get_error_base() &&
|
||||
frames == p->file->get_frames_per_interval();
|
||||
bool int_over =
|
||||
acc_error >= p->properties->error_base &&
|
||||
frames == 1 + p->properties->frames_per_interval;
|
||||
acc_error >= p->file->get_error_base() &&
|
||||
frames == 1 + p->file->get_frames_per_interval();
|
||||
|
||||
if (frames % p->nfft == 0 || ((int_full || int_over) && num_fft == 0)) {
|
||||
prev_head = head;
|
||||
@ -317,9 +413,9 @@ static void * worker_func(void *pp)
|
||||
// Do we have the FFTs for one interval?
|
||||
if (int_full || int_over) {
|
||||
if (int_over) {
|
||||
acc_error -= p->properties->error_base;
|
||||
acc_error -= p->file->get_error_base();
|
||||
} else {
|
||||
acc_error += p->properties->error_per_interval;
|
||||
acc_error += p->file->get_error_per_interval();
|
||||
}
|
||||
|
||||
for (int i = 0; i < p->bands; i++) {
|
||||
@ -337,31 +433,31 @@ static void * worker_func(void *pp)
|
||||
}
|
||||
}
|
||||
|
||||
static float average_input(const struct spek_pipeline *p, void *buffer)
|
||||
static float average_input(const struct spek_pipeline *p, const void *buffer)
|
||||
{
|
||||
int channels = p->properties->channels;
|
||||
int channels = p->file->get_channels();
|
||||
float res = 0.0f;
|
||||
if (p->properties->fp) {
|
||||
if (p->properties->width == 4) {
|
||||
if (p->file->get_fp()) {
|
||||
if (p->file->get_width() == 4) {
|
||||
float *b = (float*)buffer;
|
||||
for (int i = 0; i < channels; i++) {
|
||||
res += b[i];
|
||||
}
|
||||
} else {
|
||||
assert(p->properties->width == 8);
|
||||
assert(p->file->get_width() == 8);
|
||||
double *b = (double*)buffer;
|
||||
for (int i = 0; i < channels; i++) {
|
||||
res += (float) b[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (p->properties->width == 2) {
|
||||
if (p->file->get_width() == 2) {
|
||||
int16_t *b = (int16_t*)buffer;
|
||||
for (int i = 0; i < channels; i++) {
|
||||
res += b[i] / (float) INT16_MAX;
|
||||
}
|
||||
} else {
|
||||
assert (p->properties->width == 4);
|
||||
assert (p->file->get_width() == 4);
|
||||
int32_t *b = (int32_t*)buffer;
|
||||
for (int i = 0; i < channels; i++) {
|
||||
res += b[i] / (float) INT32_MAX;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* spek-pipeline.h
|
||||
*
|
||||
* Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
|
||||
* Copyright (C) 2010-2013 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
|
||||
@ -19,18 +19,23 @@
|
||||
#ifndef SPEK_PIPELINE_H_
|
||||
#define SPEK_PIPELINE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Audio;
|
||||
struct spek_pipeline;
|
||||
struct spek_audio_properties;
|
||||
|
||||
typedef void (*spek_pipeline_cb)(int sample, float *values, void *cb_data);
|
||||
|
||||
struct spek_pipeline * spek_pipeline_open(
|
||||
const char *path, int bands, int samples, spek_pipeline_cb cb, void *cb_data);
|
||||
|
||||
const struct spek_audio_properties * spek_pipeline_properties(struct spek_pipeline *pipeline);
|
||||
std::unique_ptr<AudioFile> file, int bands, int samples, spek_pipeline_cb cb, void *cb_data
|
||||
);
|
||||
|
||||
void spek_pipeline_start(struct spek_pipeline *pipeline);
|
||||
|
||||
void spek_pipeline_close(struct spek_pipeline *pipeline);
|
||||
|
||||
std::string spek_pipeline_desc(const struct spek_pipeline *pipeline);
|
||||
double spek_pipeline_duration(const struct spek_pipeline *pipeline);
|
||||
int spek_pipeline_sample_rate(const struct spek_pipeline *pipeline);
|
||||
|
||||
#endif
|
||||
|
@ -86,7 +86,7 @@ SpekPreferencesDialog::SpekPreferencesDialog(wxWindow *parent) :
|
||||
language_sizer->Add(language_choice, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 12);
|
||||
int active_index = 0;
|
||||
wxString active_language = SpekPreferences::get().get_language();
|
||||
for (int i = 0; i < this->languages.GetCount(); i += 2) {
|
||||
for (unsigned int i = 0; i < this->languages.GetCount(); i += 2) {
|
||||
language_choice->Append(this->languages[i + 1]);
|
||||
if (this->languages[i] == active_language) {
|
||||
active_index = i / 2;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* spek-spectrogram.cc
|
||||
*
|
||||
* Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
|
||||
* Copyright (C) 2010-2013 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
|
||||
@ -20,15 +20,13 @@
|
||||
|
||||
#include <wx/dcbuffer.h>
|
||||
|
||||
#include <spek-audio.h>
|
||||
#include <spek-palette.h>
|
||||
#include <spek-pipeline.h>
|
||||
#include <spek-utils.h>
|
||||
|
||||
#include "spek-audio-desc.h"
|
||||
#include "spek-audio.h"
|
||||
#include "spek-events.h"
|
||||
#include "spek-palette.h"
|
||||
#include "spek-pipeline.h"
|
||||
#include "spek-platform.h"
|
||||
#include "spek-ruler.h"
|
||||
#include "spek-utils.h"
|
||||
|
||||
#include "spek-spectrogram.h"
|
||||
|
||||
@ -63,6 +61,7 @@ SpekSpectrogram::SpekSpectrogram(wxFrame *parent) :
|
||||
parent, -1, wxDefaultPosition, wxDefaultSize,
|
||||
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS
|
||||
),
|
||||
audio(new Audio()), // TODO: refactor
|
||||
pipeline(NULL),
|
||||
duration(0.0),
|
||||
sample_rate(0),
|
||||
@ -111,7 +110,6 @@ void SpekSpectrogram::save(const wxString& path)
|
||||
void SpekSpectrogram::on_char(wxKeyEvent& evt)
|
||||
{
|
||||
bool C = evt.GetModifiers() == wxMOD_CONTROL;
|
||||
bool S = evt.GetModifiers() == wxMOD_SHIFT;
|
||||
bool CS = evt.GetModifiers() == (wxMOD_CONTROL | wxMOD_SHIFT);
|
||||
bool dn = evt.GetKeyCode() == WXK_DOWN;
|
||||
bool up = evt.GetKeyCode() == WXK_UP;
|
||||
@ -133,13 +131,13 @@ void SpekSpectrogram::on_char(wxKeyEvent& evt)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void SpekSpectrogram::on_paint(wxPaintEvent& evt)
|
||||
void SpekSpectrogram::on_paint(wxPaintEvent&)
|
||||
{
|
||||
wxAutoBufferedPaintDC dc(this);
|
||||
render(dc);
|
||||
}
|
||||
|
||||
void SpekSpectrogram::on_size(wxSizeEvent& evt)
|
||||
void SpekSpectrogram::on_size(wxSizeEvent&)
|
||||
{
|
||||
wxSize size = GetClientSize();
|
||||
bool width_changed = this->prev_width != size.GetWidth();
|
||||
@ -366,17 +364,17 @@ void SpekSpectrogram::start()
|
||||
if (samples > 0) {
|
||||
this->image.Create(samples, BANDS);
|
||||
this->pipeline = spek_pipeline_open(
|
||||
this->path.utf8_str(),
|
||||
this->audio->open(std::string(this->path.utf8_str())),
|
||||
BANDS,
|
||||
samples,
|
||||
pipeline_cb,
|
||||
this
|
||||
);
|
||||
spek_pipeline_start(this->pipeline);
|
||||
const spek_audio_properties *properties = spek_pipeline_properties(this->pipeline);
|
||||
this->desc = spek_audio_desc(properties);
|
||||
this->duration = properties->duration;
|
||||
this->sample_rate = properties->sample_rate;
|
||||
// TODO: extract conversion into a utility function.
|
||||
this->desc = wxString::FromUTF8(spek_pipeline_desc(this->pipeline).c_str());
|
||||
this->duration = spek_pipeline_duration(this->pipeline);
|
||||
this->sample_rate = spek_pipeline_sample_rate(this->pipeline);
|
||||
} else {
|
||||
this->image.Create(1, 1);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* spek-spectrogram.h
|
||||
*
|
||||
* Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com>
|
||||
* Copyright (C) 2010-2013 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
|
||||
@ -19,10 +19,12 @@
|
||||
#ifndef SPEK_SPECTROGRAM_H_
|
||||
#define SPEK_SPECTROGRAM_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class Audio;
|
||||
class SpekHaveSampleEvent;
|
||||
struct spek_audio_properties;
|
||||
struct spek_pipeline;
|
||||
|
||||
class SpekSpectrogram : public wxWindow
|
||||
@ -43,6 +45,7 @@ private:
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
std::unique_ptr<Audio> audio;
|
||||
spek_pipeline *pipeline;
|
||||
wxString path;
|
||||
wxString desc;
|
||||
|
@ -57,7 +57,7 @@ public:
|
||||
SpekDropTarget(SpekWindow *window) : wxFileDropTarget(), window(window) {}
|
||||
|
||||
protected:
|
||||
virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames){
|
||||
virtual bool OnDropFiles(wxCoord, wxCoord, const wxArrayString& filenames){
|
||||
if (filenames.GetCount() == 1) {
|
||||
window->open(filenames[0]);
|
||||
return true;
|
||||
@ -70,7 +70,7 @@ private:
|
||||
};
|
||||
|
||||
SpekWindow::SpekWindow(const wxString& path) :
|
||||
path(path), wxFrame(NULL, -1, wxEmptyString, wxDefaultPosition, wxSize(640, 480))
|
||||
wxFrame(NULL, -1, wxEmptyString, wxDefaultPosition, wxSize(640, 480)), path(path)
|
||||
{
|
||||
this->description = _("Spek - Acoustic Spectrum Analyser");
|
||||
SetTitle(this->description);
|
||||
@ -221,7 +221,7 @@ static const char *audio_extensions[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
void SpekWindow::on_open(wxCommandEvent& event)
|
||||
void SpekWindow::on_open(wxCommandEvent&)
|
||||
{
|
||||
static wxString filters = wxEmptyString;
|
||||
static int filter_index = 1;
|
||||
@ -261,7 +261,7 @@ void SpekWindow::on_open(wxCommandEvent& event)
|
||||
dlg->Destroy();
|
||||
}
|
||||
|
||||
void SpekWindow::on_save(wxCommandEvent& event)
|
||||
void SpekWindow::on_save(wxCommandEvent&)
|
||||
{
|
||||
static wxString filters = wxEmptyString;
|
||||
|
||||
@ -296,25 +296,25 @@ void SpekWindow::on_save(wxCommandEvent& event)
|
||||
dlg->Destroy();
|
||||
}
|
||||
|
||||
void SpekWindow::on_exit(wxCommandEvent& event)
|
||||
void SpekWindow::on_exit(wxCommandEvent&)
|
||||
{
|
||||
Close(true);
|
||||
}
|
||||
|
||||
void SpekWindow::on_preferences(wxCommandEvent& event)
|
||||
void SpekWindow::on_preferences(wxCommandEvent&)
|
||||
{
|
||||
SpekPreferencesDialog dlg(this);
|
||||
dlg.ShowModal();
|
||||
}
|
||||
|
||||
void SpekWindow::on_help(wxCommandEvent& event)
|
||||
void SpekWindow::on_help(wxCommandEvent&)
|
||||
{
|
||||
wxLaunchDefaultBrowser(
|
||||
wxString::Format(wxT("http://spek-project.org/man-%s.html"), wxT(PACKAGE_VERSION))
|
||||
);
|
||||
}
|
||||
|
||||
void SpekWindow::on_about(wxCommandEvent& event)
|
||||
void SpekWindow::on_about(wxCommandEvent&)
|
||||
{
|
||||
wxAboutDialogInfo info;
|
||||
info.AddDeveloper(wxT("Alexander Kojevnikov"));
|
||||
@ -341,13 +341,13 @@ void SpekWindow::on_about(wxCommandEvent& event)
|
||||
wxAboutBox(info);
|
||||
}
|
||||
|
||||
void SpekWindow::on_notify(wxCommandEvent& event)
|
||||
void SpekWindow::on_notify(wxCommandEvent&)
|
||||
{
|
||||
this->GetSizer()->Show((size_t)0);
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
void SpekWindow::on_visit(wxCommandEvent& event)
|
||||
void SpekWindow::on_visit(wxCommandEvent&)
|
||||
{
|
||||
wxLaunchDefaultBrowser(wxT("http://spek-project.org"));
|
||||
}
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include <wx/log.h>
|
||||
#include <wx/socket.h>
|
||||
|
||||
#include <spek-audio.h>
|
||||
|
||||
#include "spek-artwork.h"
|
||||
#include "spek-platform.h"
|
||||
#include "spek-preferences.h"
|
||||
@ -53,8 +51,6 @@ bool Spek::OnInit()
|
||||
wxInitAllImageHandlers();
|
||||
wxSocketBase::Initialize();
|
||||
|
||||
spek_audio_init();
|
||||
|
||||
spek_artwork_init();
|
||||
spek_platform_init();
|
||||
SpekPreferences::get().init();
|
||||
@ -70,7 +66,9 @@ bool Spek::OnInit()
|
||||
wxCMD_LINE_SWITCH,
|
||||
wxT_2("V"),
|
||||
wxT_2("version"),
|
||||
wxT_2("Display the version and exit")
|
||||
wxT_2("Display the version and exit"),
|
||||
wxCMD_LINE_VAL_NONE,
|
||||
wxCMD_LINE_PARAM_OPTIONAL
|
||||
}, {
|
||||
wxCMD_LINE_PARAM,
|
||||
NULL,
|
||||
|
Loading…
x
Reference in New Issue
Block a user