Sélectionner une révision Git
Audio.cc 6,46 Kio
#include "Audio.hh"
#include <QJsonObject>
#include <QJsonArray>
using namespace Vivy;
// AudioContext class implementation
// Create an audio contecxt from a file
AudioContext::AudioContext(const QString &path)
: Super(path)
{
}
QString
AudioContext::getElementName() const noexcept
{
return "Audio" + Super::getElementName();
}
QJsonDocument
AudioContext::getProperties() const noexcept
{
return Super::getProperties();
}
// AudioStream class implementation
// Constructor, need an AVFormat and an AVStream
AudioStream::AudioStream(AVCodec *streamCodec, AVFormatContext *formatPtr, AVStream *streamArg,
int index)
: Super(streamCodec, formatPtr, streamArg, index)
{
SwrContext *s = dataSwrContext.get();
av_opt_set_int(s, "in_channel_count", codecContext->channels, 0);
av_opt_set_int(s, "out_channel_count", 1, 0);
av_opt_set_int(s, "in_channel_layout", static_cast<int64_t>(codecContext->channel_layout), 0);
av_opt_set_int(s, "out_channel_layout", AV_CH_LAYOUT_MONO, 0);
av_opt_set_int(s, "in_sample_rate", codecContext->sample_rate, 0);
av_opt_set_int(s, "out_sample_rate", resamplerSampleRate, 0);
av_opt_set_sample_fmt(s, "in_sample_fmt", codecContext->sample_fmt, 0);
av_opt_set_sample_fmt(s, "out_sample_fmt", AV_SAMPLE_FMT_DBL, 0);
swr_init(s);
if (!swr_is_initialized(s))
throw std::runtime_error("failed to initialize SwrContext resampler");
}
AudioStream::~AudioStream() noexcept
{
if (dataPtr) {
qDebug() << "Free data ptr";
free(dataPtr);
}
qDebug() << "Delete stream object";
}
QJsonObject
AudioStream::getProperties() const noexcept
{
QJsonObject ret = Super::getProperties();
ret.insert("Sample rate", codecParams->sample_rate);
ret.insert("Bit rate", static_cast<int>(codecParams->bit_rate));
ret.insert("Channels", codecParams->channels);
return ret;
}
// Decode the data, don't call it if data already decoded
void
AudioStream::decodeData()
{
if (isDecoded())
throw std::logic_error("audio stream is already resampled");
qDebug() << "Launch decoding of stream" << streamIndexInContext;
AVPacket packet;
av_init_packet(&packet);
// Iterate through frames
while (av_read_frame(dataFormat, &packet) >= 0) {
// Only decode audio
if (packet.stream_index != streamIndexInContext) {
av_packet_unref(&packet);
continue;
}
// Decode one frame
int response = avcodec_send_packet(codecContext.get(), &packet);
if (response < 0) {
throw std::runtime_error(
QStringLiteral("error n°%1 while sending a packet to the decoder")
.arg(response)
.toStdString());
}
double *buffer = nullptr;
int oldFrameNbSamples = 0;
while (response >= 0) {
response = avcodec_receive_frame(codecContext.get(), dataFrame.get());
if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
break;
} else if (response < 0) {
throw std::runtime_error(
QStringLiteral("error n°%1 while receiving a from from the decoder")
.arg(response)
.toStdString());
}
// Reuse buffer if possible
if (oldFrameNbSamples < dataFrame->nb_samples) {
dataDeleter(buffer);
oldFrameNbSamples = dataFrame->nb_samples;
av_samples_alloc(reinterpret_cast<uint8_t **>(&buffer), nullptr, 1,
dataFrame->nb_samples, AV_SAMPLE_FMT_DBL, 0);
}
// Resample frame
if (const int frame_count_int = swr_convert(
dataSwrContext.get(), reinterpret_cast<uint8_t **>(&buffer),
dataFrame->nb_samples,
const_cast<const uint8_t **>(reinterpret_cast<uint8_t **>(dataFrame->data)),
dataFrame->nb_samples);
frame_count_int < 0) {
throw std::runtime_error("error on frame count, is negative but should not be");
}
// Append resampled frames to data
else {
const size_t frame_count = static_cast<size_t>(frame_count_int);
dataPtr = reinterpret_cast<double *>(
realloc(dataPtr, (dataSize + static_cast<size_t>(dataFrame->nb_samples)) *
sizeof(double)));
memcpy(dataPtr + dataSize, buffer, frame_count * sizeof(double));
dataSize += frame_count;
}
}
dataDeleter(buffer);
av_packet_unref(&packet);
}
qDebug() << "Decoding data finished for stream" << streamIndexInContext
<< "dataPtr =" << dataPtr << "with dataSize =" << dataSize;
}
// Delete decoded data, clean up thing
void
AudioStream::cleanUpData() noexcept
{
free(dataPtr);
dataPtr = nullptr;
dataSize = 0;
}
// Get the number of channels in the audio context stream
int
AudioStream::getChannels() const noexcept
{
return codecContext->channels;
}
// Get the sample rate
int
AudioStream::getSampleRate() const noexcept
{
return codecContext->sample_rate;
}
// Get the sample rate of the decoded data (need to call decodeData for that
// value to have a meaning).
int
AudioStream::getDataSampleRate() const noexcept
{
return resamplerSampleRate;
}
// Get the bit rate of the stream
qint64
AudioStream::getBitRate() const noexcept
{
return codecContext->bit_rate;
}
// Get the information about the decoded state of this stream
bool
AudioStream::isDecoded() const noexcept
{
return dataPtr != nullptr;
}
// Get the decoded data's size, will return 0 if the data is not decoded. Don't
// rely on this behaviour to get the decoded state of the stream!
size_t
AudioStream::getDecodedDataSize() const noexcept
{
return dataSize;
}
// Get the decoded data, safe to call even if the data is not decoded has the
// pointer will be null. You must check it with an `if` statement.
double *
AudioStream::getDecodedData() const noexcept
{
return dataPtr;
}
// Get the chunk size of the decoded data
size_t
AudioStream::getDecodedChunkSize() const noexcept
{
return 512;
}
// Get the decalage of the decoded data
size_t
AudioStream::getDecodedDecalage() const noexcept
{
constexpr size_t overlap = 128; // The overlap
return getDecodedChunkSize() - overlap;
}