Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 224 additions & 0 deletions app/mediaAnalyser/AvCorrector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
#include "AvCorrector.hpp"

#include <AvTranscoder/data/decoded/AudioFrame.hpp>
#include <AvTranscoder/encoder/AudioEncoder.hpp>
#include <AvTranscoder/file/OutputFile.hpp>
#include <AvTranscoder/filter/FilterGraph.hpp>
#include <AvTranscoder/codec/AudioCodec.hpp>

AvCorrector::AvCorrector(const std::vector<avtranscoder::InputStreamDesc>& arrayToCorrect, const std::string& correctionOutputName)
: _totalNbSamplesToCorrect(0)
, _cumulOfSamplesCorrected(0)
, _inputNbChannels()
, _inputSampleRate()
, _currentReaderSamplesMaxValue()
, _audioReader()
, _correctedStreamDescs()
, _outputStream(&std::cout)
, _progressionFileName()
{
for (int i = 0; i < arrayToCorrect.size(); ++i)
{
const avtranscoder::InputStreamDesc inputStreamDesc = arrayToCorrect.at(i);
// Create reader to convert to float planar
avtranscoder::AudioReader* reader = new avtranscoder::AudioReader(inputStreamDesc);
reader->continueWithGenerator();
_audioReader.push_back(reader);

// Get data from audio stream
const avtranscoder::AudioProperties* audioProperties = reader->getSourceAudioProperties();
const int nbChannels = inputStreamDesc._channelIndexArray.empty() ?
audioProperties->getNbChannels() : inputStreamDesc._channelIndexArray.size();
_inputNbChannels.push_back(nbChannels);
const size_t sampleRate = audioProperties->getSampleRate();
_inputSampleRate.push_back(sampleRate);

const int nbSamples = (audioProperties->getNbSamples() / audioProperties->getNbChannels()) * nbChannels;
_nbSamplesToCorrect.push_back(nbSamples);
_totalNbSamplesToCorrect += nbSamples;

reader->updateOutput(sampleRate, nbChannels, audioProperties->getSampleFormatName());

std::stringstream outputStreamFileName;
if(!correctionOutputName.empty())
{
outputStreamFileName << correctionOutputName << "_" << i << ".wav";
}
else
{
outputStreamFileName << inputStreamDesc._filename.substr(0, inputStreamDesc._filename.size() - 4)
<< "_corrected_" << i << ".wav";
}
avtranscoder::InputStreamDesc outputStreamDesc(outputStreamFileName.str(), 0);
_correctedStreamDescs.push_back(outputStreamDesc);
}
}

AvCorrector::~AvCorrector()
{
for(std::vector<avtranscoder::AudioReader*>::iterator it = _audioReader.begin(); it != _audioReader.end(); ++it)
{
delete(*it);
}
}

void AvCorrector::correct(const float gain)
{
// open file to print duration
std::ofstream outputFile;
if(! _progressionFileName.empty())
{
outputFile.open(_progressionFileName.c_str());
_outputStream = &outputFile;
}

for (int i = 0; i < _audioReader.size(); ++i)
{
avtranscoder::AudioReader* reader = _audioReader.at(i);
const avtranscoder::AudioProperties* inputAudioProperties = reader->getSourceAudioProperties();

avtranscoder::AudioFrameDesc audioFrameDesc(inputAudioProperties->getSampleRate(),
_inputNbChannels.at(i),
inputAudioProperties->getSampleFormatName());

avtranscoder::AudioEncoder* encoder = new avtranscoder::AudioEncoder(inputAudioProperties->getCodecName());
encoder->setupAudioEncoder(audioFrameDesc);

avtranscoder::OutputFile* outputFile = new avtranscoder::OutputFile(_correctedStreamDescs.at(i)._filename);
outputFile->addAudioStream(encoder->getAudioCodec());
outputFile->beginWrap();

bool emptyFrameDecoded = false;

_currentReaderSamplesMaxValue = getSampleMaxValue(inputAudioProperties->getSampleFormatName());

// Decode audio streams
int count = 0;
while(count < _nbSamplesToCorrect.at(i))
{
if(isEndOfCorrection())
{
break;
}

avtranscoder::AudioFrame* srcFrame = (avtranscoder::AudioFrame*) _audioReader.at(i)->readNextFrame();

// empty frame: go to the end of process
if(srcFrame == NULL)
{
emptyFrameDecoded = true;
break;
}

const size_t nbChannels = _inputNbChannels.at(i);
const size_t nbSamples = srcFrame->getNbSamplesPerChannel();
const size_t bytesPerSample = srcFrame->getBytesPerSample();

avtranscoder::AudioFrame* audioFrame = new avtranscoder::AudioFrame(audioFrameDesc, false);
audioFrame->setNbSamplesPerChannel(nbSamples);
audioFrame->allocateData();

unsigned char* srcData = srcFrame->getData()[0];
unsigned char* dstData = audioFrame->getData()[0];

// Correct input
const int correctedSamples = correctFrame(srcData, dstData, nbChannels, nbSamples, bytesPerSample, gain);

avtranscoder::CodedData data;
encoder->encodeFrame(*audioFrame, data);
outputFile->wrap(data, 0);

delete audioFrame;

count += correctedSamples;
_cumulOfSamplesCorrected += correctedSamples;
printProgress();
}

outputFile->endWrap();
delete encoder;
delete outputFile;
}
}

void AvCorrector::printProgress()
{
const int p = (float)_cumulOfSamplesCorrected / _totalNbSamplesToCorrect * 100;

// print progression to file
if(!_progressionFileName.empty())
{
_outputStream->seekp(0);
*_outputStream << p;
}
// print progression to console
else
*_outputStream << "[" << std::setw(3) << p << "%]\r" << std::flush;
}

int AvCorrector::getSampleMaxValue(const std::string& sampleFormat)
{
if(sampleFormat == "s8")
return 0xFF000000;
if(sampleFormat == "s16")
return 0xFFFF0000;
if(sampleFormat == "s32")
return 0xFFFFFF00;
throw std::runtime_error("Cannot handle such a sample format: " + sampleFormat);
}

size_t AvCorrector::correctFrame(unsigned char* srcData, unsigned char* dstData, const size_t nbChannels, const size_t nbSamples,
const size_t bytesPerSample, const float gain)
{
const int half = _currentReaderSamplesMaxValue / 2;

size_t correctedSamples = 0;
const size_t channelDataSize = nbSamples * bytesPerSample;

int byte = 0;
for(size_t c = 0; c < nbChannels; c++)
{
// Get channel data
unsigned char* channelData = &srcData[c*channelDataSize];
for(size_t s = 0; s < nbSamples; s++)
{
// Correct audio sample
Sample audioSample;
for (size_t b = 0; b < bytesPerSample; ++b)
{
audioSample.bytes[b] = (*channelData);
channelData++;
}

if(audioSample.value > half)
{
const int value = audioSample.value - _currentReaderSamplesMaxValue;
int gainedValue = value * gain;
if(gainedValue - value == 0)
{
if(gainedValue > 0)
gainedValue++;
else
gainedValue--;
}
audioSample.value = (gainedValue + _currentReaderSamplesMaxValue) & _currentReaderSamplesMaxValue;
} else {
audioSample.value = (int)(audioSample.value * gain) & _currentReaderSamplesMaxValue;
}

for (size_t b = 0; b < bytesPerSample; ++b)
{
dstData[byte] = audioSample.bytes[b];
byte++;
}
correctedSamples++;
}
}
return correctedSamples;
}


bool AvCorrector::isEndOfCorrection()
{
return _cumulOfSamplesCorrected >= _totalNbSamplesToCorrect;
}
51 changes: 51 additions & 0 deletions app/mediaAnalyser/AvCorrector.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#ifndef AVCORRECTOR_HPP
#define AVCORRECTOR_HPP

#include <AvTranscoder/reader/AudioReader.hpp>

union Sample {
int value;
unsigned char bytes[4];
};

class AvCorrector
{
public:
AvCorrector(const std::vector<avtranscoder::InputStreamDesc>& arrayToCorrect, const std::string& correctionOutputName = "");
~AvCorrector();

void correct(const float gain);
std::vector<avtranscoder::InputStreamDesc> getOutputStreamDescs() { return _correctedStreamDescs; }
void setProgressionFile(const std::string& progressionFileName) { _progressionFileName = progressionFileName; }

private:
int getSampleMaxValue(const std::string& sampleFormat);
void printProgress();

size_t correctFrame(unsigned char* srcData, unsigned char* dstData, const size_t nbChannels, const size_t nbSamples,
const size_t bytesPerSample, const float gain);

bool isEndOfCorrection();

private:
// for progress
size_t _totalNbSamplesToCorrect;
size_t _cumulOfSamplesCorrected;
std::vector<size_t> _nbSamplesToCorrect;

// to check audio before correct
std::vector<size_t> _inputNbChannels;
std::vector<size_t> _inputSampleRate;

int _currentReaderSamplesMaxValue;

// To print the progession to a stream
std::ostream* _outputStream;
std::string _progressionFileName;

// for io
std::vector<avtranscoder::AudioReader*> _audioReader;
std::vector<avtranscoder::InputStreamDesc> _correctedStreamDescs;
};

#endif
17 changes: 11 additions & 6 deletions app/mediaAnalyser/AvSoundFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,23 @@ AvSoundFile::~AvSoundFile()
}
}

size_t AvSoundFile::getTotalNbSamplesToAnalyse()
{
size_t newTotalNbSamplesToAnalyse = 0;
for(size_t i = 0; i < _inputSampleRate.size(); i++)
{
newTotalNbSamplesToAnalyse += _forceDurationToAnalyse * _inputSampleRate.at(i) * _inputNbChannels.at(i);
}
return newTotalNbSamplesToAnalyse;
}

void AvSoundFile::analyse(Loudness::analyser::LoudnessAnalyser& analyser)
{
// update number of samples to analyse
if(_forceDurationToAnalyse)
{
size_t newTotalNbSamplesToAnalyse = 0;
for(size_t i = 0; i < _inputSampleRate.size(); i++)
{
newTotalNbSamplesToAnalyse += _forceDurationToAnalyse * _inputSampleRate.at(i) * _inputNbChannels.at(i);
}
// set total number of samples to analyse
_totalNbSamplesToAnalyse = newTotalNbSamplesToAnalyse;
_totalNbSamplesToAnalyse = getTotalNbSamplesToAnalyse();
}

// open file to print duration
Expand Down
7 changes: 7 additions & 0 deletions app/mediaAnalyser/AvSoundFile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ class AvSoundFile
*/
bool isEndOfAnalysis();

/**
* @brief Compute the total number of samples to analyse from the several inputs
* sample rate, the number of channels and the expected duration.
* @return the number of samples to analyse
*/
size_t getTotalNbSamplesToAnalyse();

private:
// for loudness analyser
size_t _nbChannelsToAnalyse;
Expand Down
Loading