spek

Acoustic spectrum analyser https://github.com/alexkay/spek spek.cc
git clone http://git.hanabi.in/repos/spek.git
Log | Files | Refs | README

spek-audio.cc (11740B)


      1 #include <assert.h>
      2 
      3 extern "C" {
      4 #define __STDC_CONSTANT_MACROS
      5 #define __STDC_LIMIT_MACROS
      6 #include <libavformat/avformat.h>
      7 #include <libavcodec/avcodec.h>
      8 #include <libavutil/mathematics.h>
      9 }
     10 
     11 #include "spek-audio.h"
     12 
     13 class AudioFileImpl : public AudioFile
     14 {
     15 public:
     16     AudioFileImpl(
     17         AudioError error, AVFormatContext *format_context, AVCodecContext *codec_context,
     18         int audio_stream, const std::string& codec_name, int bit_rate, int sample_rate,
     19         int bits_per_sample, int streams, int channels, double duration
     20     );
     21     ~AudioFileImpl() override;
     22     void start(int channel, int samples) override;
     23     int read() override;
     24 
     25     AudioError get_error() const override { return this->error; }
     26     std::string get_codec_name() const override { return this->codec_name; }
     27     int get_bit_rate() const override { return this->bit_rate; }
     28     int get_sample_rate() const override { return this->sample_rate; }
     29     int get_bits_per_sample() const override { return this->bits_per_sample; }
     30     int get_streams() const override { return this->streams; }
     31     int get_channels() const override { return this->channels; }
     32     double get_duration() const override { return this->duration; }
     33     const float *get_buffer() const override { return this->buffer; }
     34     int64_t get_frames_per_interval() const override { return this->frames_per_interval; }
     35     int64_t get_error_per_interval() const override { return this->error_per_interval; }
     36     int64_t get_error_base() const override { return this->error_base; }
     37 
     38 private:
     39     AudioError error;
     40     AVFormatContext *format_context;
     41     AVCodecContext *codec_context;
     42     int audio_stream;
     43     std::string codec_name;
     44     int bit_rate;
     45     int sample_rate;
     46     int bits_per_sample;
     47     int streams;
     48     int channels;
     49     double duration;
     50 
     51     int channel;
     52 
     53     AVPacket packet;
     54     int offset;
     55     AVFrame *frame;
     56     int buffer_len;
     57     float *buffer;
     58     // TODO: these guys don't belong here, move them somewhere else when revamping the pipeline
     59     int64_t frames_per_interval;
     60     int64_t error_per_interval;
     61     int64_t error_base;
     62 };
     63 
     64 
     65 Audio::Audio()
     66 {
     67     av_register_all();
     68 }
     69 
     70 Audio::~Audio()
     71 {
     72     // This prevents a memory leak.
     73     av_lockmgr_register(nullptr);
     74 }
     75 
     76 std::unique_ptr<AudioFile> Audio::open(const std::string& file_name, int stream)
     77 {
     78     AudioError error = AudioError::OK;
     79 
     80     AVFormatContext *format_context = nullptr;
     81     if (avformat_open_input(&format_context, file_name.c_str(), nullptr, nullptr) != 0) {
     82         error = AudioError::CANNOT_OPEN_FILE;
     83     }
     84 
     85     if (!error && avformat_find_stream_info(format_context, nullptr) < 0) {
     86         // 24-bit APE returns an error but parses the stream info just fine.
     87         // TODO: old comment, verify
     88         if (format_context->nb_streams <= 0) {
     89             error = AudioError::NO_STREAMS;
     90         }
     91     }
     92 
     93     int audio_stream = -1;
     94     int streams = 0;
     95     if (!error) {
     96         for (unsigned int i = 0; i < format_context->nb_streams; i++) {
     97             if (format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
     98                 if (stream == streams) {
     99                     audio_stream = i;
    100                 }
    101                 streams++;
    102             }
    103         }
    104         if (audio_stream == -1) {
    105             error = AudioError::NO_AUDIO;
    106         }
    107     }
    108 
    109     AVStream *avstream = nullptr;
    110     AVCodecParameters *codecpar = nullptr;
    111     AVCodec *codec = nullptr;
    112     if (!error) {
    113         avstream = format_context->streams[audio_stream];
    114         codecpar = avstream->codecpar;
    115         codec = avcodec_find_decoder(codecpar->codec_id);
    116         if (!codec) {
    117             error = AudioError::NO_DECODER;
    118         }
    119     }
    120 
    121     std::string codec_name;
    122     int bit_rate = 0;
    123     int sample_rate = 0;
    124     int bits_per_sample = 0;
    125     int channels = 0;
    126     double duration = 0;
    127     if (!error) {
    128         // We can already fill in the stream info even if the codec won't be able to open it.
    129         codec_name = codec->long_name;
    130         bit_rate = codecpar->bit_rate;
    131         sample_rate = codecpar->sample_rate;
    132         bits_per_sample = codecpar->bits_per_raw_sample;
    133         if (!bits_per_sample) {
    134             // APE uses bpcs, FLAC uses bprs.
    135             bits_per_sample = codecpar->bits_per_coded_sample;
    136         }
    137         if (codecpar->codec_id == AV_CODEC_ID_AAC ||
    138             codecpar->codec_id == AV_CODEC_ID_MUSEPACK8 ||
    139             codecpar->codec_id == AV_CODEC_ID_WMAV1 ||
    140             codecpar->codec_id == AV_CODEC_ID_WMAV2) {
    141             // These decoders set both bps and bitrate.
    142             bits_per_sample = 0;
    143         }
    144         if (bits_per_sample) {
    145             bit_rate = 0;
    146         }
    147         channels = codecpar->channels;
    148 
    149         if (avstream->duration != AV_NOPTS_VALUE) {
    150             duration = avstream->duration * av_q2d(avstream->time_base);
    151         } else if (format_context->duration != AV_NOPTS_VALUE) {
    152             duration = format_context->duration / (double) AV_TIME_BASE;
    153         } else {
    154             error = AudioError::NO_DURATION;
    155         }
    156 
    157         if (!error && channels <= 0) {
    158             error = AudioError::NO_CHANNELS;
    159         }
    160     }
    161 
    162     AVCodecContext *codec_context = nullptr;
    163     if (!error) {
    164         error = AudioError::CANNOT_OPEN_DECODER;
    165         // Allocate a codec context for the decoder.
    166         codec_context = avcodec_alloc_context3(codec);
    167         if (codec_context) {
    168             // Copy codec parameters from input stream to output codec context.
    169             if (avcodec_parameters_to_context(codec_context, codecpar) == 0) {
    170                 // Finally, init the decoder.
    171                 if (avcodec_open2(codec_context, codec, nullptr) == 0) {
    172                     error = AudioError::OK;
    173                 }
    174             }
    175         }
    176     }
    177 
    178     if (!error) {
    179         AVSampleFormat fmt = (AVSampleFormat)codecpar->format;
    180         if (fmt != AV_SAMPLE_FMT_S16 && fmt != AV_SAMPLE_FMT_S16P &&
    181             fmt != AV_SAMPLE_FMT_S32 && fmt != AV_SAMPLE_FMT_S32P &&
    182             fmt != AV_SAMPLE_FMT_FLT && fmt != AV_SAMPLE_FMT_FLTP &&
    183             fmt != AV_SAMPLE_FMT_DBL && fmt != AV_SAMPLE_FMT_DBLP ) {
    184             error = AudioError::BAD_SAMPLE_FORMAT;
    185         }
    186     }
    187 
    188     return std::unique_ptr<AudioFile>(new AudioFileImpl(
    189         error, format_context, codec_context,
    190         audio_stream, codec_name, bit_rate, sample_rate,
    191         bits_per_sample, streams, channels, duration
    192     ));
    193 }
    194 
    195 AudioFileImpl::AudioFileImpl(
    196     AudioError error, AVFormatContext *format_context, AVCodecContext *codec_context,
    197     int audio_stream, const std::string& codec_name, int bit_rate, int sample_rate,
    198     int bits_per_sample, int streams, int channels, double duration
    199 ) :
    200     error(error), format_context(format_context), codec_context(codec_context),
    201     audio_stream(audio_stream), codec_name(codec_name), bit_rate(bit_rate),
    202     sample_rate(sample_rate),
    203     bits_per_sample(bits_per_sample), streams(streams), channels(channels), duration(duration)
    204 {
    205     av_init_packet(&this->packet);
    206     this->packet.data = nullptr;
    207     this->packet.size = 0;
    208     this->offset = 0;
    209     this->frame = av_frame_alloc();
    210     this->buffer_len = 0;
    211     this->buffer = nullptr;
    212     this->frames_per_interval = 0;
    213     this->error_per_interval = 0;
    214     this->error_base = 0;
    215 }
    216 
    217 AudioFileImpl::~AudioFileImpl()
    218 {
    219     if (this->buffer) {
    220         av_freep(&this->buffer);
    221     }
    222     if (this->frame) {
    223         av_frame_free(&this->frame);
    224     }
    225     if (this->packet.data) {
    226         this->packet.data -= this->offset;
    227         this->packet.size += this->offset;
    228         this->offset = 0;
    229         av_packet_unref(&this->packet);
    230     }
    231     if (this->codec_context) {
    232         avcodec_free_context(&codec_context);
    233     }
    234     if (this->format_context) {
    235         avformat_close_input(&this->format_context);
    236     }
    237 }
    238 
    239 void AudioFileImpl::start(int channel, int samples)
    240 {
    241     this->channel = channel;
    242     if (channel < 0 || channel >= this->channels) {
    243         assert(false);
    244         this->error = AudioError::NO_CHANNELS;
    245     }
    246 
    247     AVStream *stream = this->format_context->streams[this->audio_stream];
    248     int64_t rate = this->sample_rate * (int64_t)stream->time_base.num;
    249     int64_t duration = (int64_t)(this->duration * stream->time_base.den / stream->time_base.num);
    250     this->error_base = samples * (int64_t)stream->time_base.den;
    251     this->frames_per_interval = av_rescale_rnd(duration, rate, this->error_base, AV_ROUND_DOWN);
    252     this->error_per_interval = (duration * rate) % this->error_base;
    253 }
    254 
    255 int AudioFileImpl::read()
    256 {
    257     if (!!this->error) {
    258         return -1;
    259     }
    260 
    261     for (;;) {
    262         while (this->packet.size > 0) {
    263             av_frame_unref(this->frame);
    264             int got_frame = 0;
    265             int len = avcodec_decode_audio4(
    266                 this->codec_context, this->frame, &got_frame, &this->packet
    267             );
    268             if (len < 0) {
    269                 // Error, skip the frame.
    270                 break;
    271             }
    272             this->packet.data += len;
    273             this->packet.size -= len;
    274             this->offset += len;
    275             if (!got_frame) {
    276                 // No data yet, get more frames.
    277                 continue;
    278             }
    279             // We have data, return it and come back for more later.
    280             int samples = this->frame->nb_samples;
    281             if (samples > this->buffer_len) {
    282                 this->buffer = static_cast<float*>(
    283                     av_realloc(this->buffer, samples * sizeof(float))
    284                 );
    285                 this->buffer_len = samples;
    286             }
    287 
    288             AVSampleFormat format = static_cast<AVSampleFormat>(this->frame->format);
    289             int is_planar = av_sample_fmt_is_planar(format);
    290             for (int sample = 0; sample < samples; ++sample) {
    291                 uint8_t *data;
    292                 int offset;
    293                 if (is_planar) {
    294                     data = this->frame->data[this->channel];
    295                     offset = sample;
    296                 } else {
    297                     data = this->frame->data[0];
    298                     offset = sample * this->channels;
    299                 }
    300                 float value;
    301                 switch (format) {
    302                 case AV_SAMPLE_FMT_S16:
    303                 case AV_SAMPLE_FMT_S16P:
    304                     value = reinterpret_cast<int16_t*>(data)[offset]
    305                         / static_cast<float>(INT16_MAX);
    306                     break;
    307                 case AV_SAMPLE_FMT_S32:
    308                 case AV_SAMPLE_FMT_S32P:
    309                     value = reinterpret_cast<int32_t*>(data)[offset]
    310                         / static_cast<float>(INT32_MAX);
    311                     break;
    312                 case AV_SAMPLE_FMT_FLT:
    313                 case AV_SAMPLE_FMT_FLTP:
    314                     value = reinterpret_cast<float*>(data)[offset];
    315                     break;
    316                 case AV_SAMPLE_FMT_DBL:
    317                 case AV_SAMPLE_FMT_DBLP:
    318                     value = reinterpret_cast<double*>(data)[offset];
    319                     break;
    320                 default:
    321                     value = 0.0f;
    322                     break;
    323                 }
    324                 this->buffer[sample] = value;
    325             }
    326             return samples;
    327         }
    328         if (this->packet.data) {
    329             this->packet.data -= this->offset;
    330             this->packet.size += this->offset;
    331             this->offset = 0;
    332             av_packet_unref(&this->packet);
    333         }
    334 
    335         int res = 0;
    336         while ((res = av_read_frame(this->format_context, &this->packet)) >= 0) {
    337             if (this->packet.stream_index == this->audio_stream) {
    338                 break;
    339             }
    340             av_packet_unref(&this->packet);
    341         }
    342         if (res < 0) {
    343             // End of file or error.
    344             return 0;
    345         }
    346     }
    347 }