main
6f8fc97 ยท 1 month ago 10 commits
  1#include "audioProcessor.hpp"
  2
  3#include <iomanip>
  4/* Reading and writing audio files using ffmpeg */
  5void AudioProcessor::readAudioWithFFmpeg() {
  6    size_t readSamples = 0;
  7    const int sample = 48 * 1000;
  8    const size_t totalSamples = audioData.duration * sample;
  9    audioData.samples.resize(totalSamples);
 10    audioData.header.sampleRate = sample;
 11
 12    const std::string ffmpegInputCmd =
 13        "ffmpeg -i " + inputFile + " -ar " + std::to_string(sample) + " -f s16le -ac 1 -";
 14    if (FILE *pipein = popen(ffmpegInputCmd.c_str(), "r")) {
 15        readSamples = fread(audioData.samples.data(), sizeof(int16_t), totalSamples, pipein);
 16        pclose(pipein);
 17
 18        if (readSamples == 0) {
 19            throw std::runtime_error("Failed to read input audio");
 20        }
 21    } else {
 22        throw std::runtime_error("Failed to open input audio");
 23    }
 24}
 25
 26void AudioProcessor::writeAudioWithFFmpeg() {
 27    const int sample = 48 * 1000;
 28    const size_t readSamples = audioData.samples.size();
 29    const std::vector<int16_t> &outputBuffer = audioData.samples;
 30    const std::string ffmpegOutputCmd =
 31        "ffmpeg -y -f s16le -ar " + std::to_string(sample) + " -ac 1 -i - " + outputFile;
 32
 33    if (FILE *pipeout = popen(ffmpegOutputCmd.c_str(), "w")) {
 34        fwrite(outputBuffer.data(), sizeof(int16_t), readSamples, pipeout);
 35        pclose(pipeout);
 36    } else {
 37        throw std::runtime_error("Failed to open output pipe");
 38    }
 39}
 40
 41void AudioProcessor::resample(uint32_t expectedSampleRate) {
 42    if (audioData.header.sampleRate == expectedSampleRate) {
 43        return;
 44    }
 45    uint32_t oldSampleRate = audioData.header.sampleRate;
 46    float ratio = static_cast<float>(expectedSampleRate) / oldSampleRate;
 47    size_t newLength = static_cast<size_t>(std::ceil(audioData.samples.size() * ratio));
 48    std::vector<int16_t> output(newLength);
 49
 50    // Linear interpolation
 51    for (size_t i = 0; i < newLength; i++) {
 52        float pos = i / ratio;
 53        size_t idx = static_cast<size_t>(pos);
 54        float frac = pos - idx;
 55        output[i] = (idx + 1 < audioData.samples.size())
 56                        ? static_cast<int16_t>(
 57                              audioData.samples[idx] +
 58                              frac * (audioData.samples[idx + 1] - audioData.samples[idx]))
 59                        : audioData.samples[idx];
 60    }
 61    audioData.samples = output;
 62    audioData.header.sampleRate = expectedSampleRate;
 63    audioData.header.dataSize = output.size() * sizeof(int16_t);
 64    audioData.header.chunkSize = audioData.header.dataSize + 36;
 65    audioData.header.byteRate =
 66        audioData.header.sampleRate * audioData.header.numChannels * sizeof(int16_t);
 67    audioData.duration = calculateDuration(audioData.header);
 68    /* we don't need to change this coz we are not changing the number of channels
 69    newHeader.blockAlign = newHeader.numChannels * sizeof(int16_t); */
 70
 71    std::cout << "Resampled audio from " << oldSampleRate << " to " << expectedSampleRate << "Hz"
 72              << std::endl;
 73}
 74
 75void AudioProcessor::readRawAudioFile(bool headerOnly) {
 76    std::ifstream file(inputFile, std::ios::binary);
 77    if (!file) throw std::runtime_error("File not found!");
 78    if (!file.is_open()) throw std::runtime_error("Failed to open file!");
 79
 80    file.read(reinterpret_cast<char *>(&audioData.header), sizeof(WAVHeader));
 81    if (std::string(audioData.header.chunkID, 4) != "RIFF" ||
 82        std::string(audioData.header.format, 4) != "WAVE")
 83        throw std::runtime_error("Not a valid WAV file!");
 84
 85    while (std::string(audioData.header.dataID, 4) != "data") {
 86        file.seekg(audioData.header.dataSize, std::ios::cur);
 87        file.read(audioData.header.dataID, 4);
 88        file.read(reinterpret_cast<char *>(&audioData.header.dataSize),
 89                  sizeof(audioData.header.dataSize));
 90        if (file.eof()) {
 91            std::cerr << "No 'data' chunk found in the WAV file." << std::endl;
 92            throw std::runtime_error("Invalid WAV file");
 93        }
 94    }
 95
 96    audioData.duration = calculateDuration(audioData.header);
 97    audioData.samples.resize(audioData.header.dataSize / sizeof(int16_t));
 98    if (!headerOnly)
 99        file.read(reinterpret_cast<char *>(audioData.samples.data()), audioData.header.dataSize);
100    file.close();
101}
102
103void AudioProcessor::writeRawAudioFile() {
104    std::ofstream file(outputFile, std::ios::binary);
105    file.write(reinterpret_cast<char *>(&audioData.header), sizeof(WAVHeader));
106    file.write(reinterpret_cast<char *>(audioData.samples.data()), audioData.header.dataSize);
107    file.close();
108}
109
110void AudioProcessor::processFrame(const int16_t *input, int16_t *output, int frameSize) {
111    std::vector<int16_t> frame(input, input + frameSize);
112    size_t expectedFrameSize = audioData.header.sampleRate / 1000 * MAX_PROCESSABLE_MS;
113
114    if (frameSize < (int)expectedFrameSize) {
115        frame.resize(expectedFrameSize, 0);
116    }
117    agcManager->process(frame);
118    std::copy(frame.begin(), frame.begin() + frameSize, output);
119}
120
121void AudioProcessor::performAGC() {
122    size_t frameCount = 0;
123    size_t readSamples = audioData.samples.size();
124    size_t frameSize = audioData.header.sampleRate / 1000 * MAX_PROCESSABLE_MS;
125    std::vector<int16_t> outputBuffer(readSamples);
126
127    if (readSamples == 0) {
128        throw std::runtime_error("No audio samples found");
129    }
130    bool supportedSampleRate = false;
131    for (uint32_t i : SUPPORTED_SAMPLE_RATES) {
132        if (audioData.header.sampleRate == i) {
133            supportedSampleRate = true;
134            break;
135        }
136    }
137    if (!supportedSampleRate) {
138        throw std::runtime_error("Unsupported sample rate");
139    }
140
141    agcManager = std::make_unique<AGC>(audioData.header.sampleRate);
142    auto processStart = std::chrono::steady_clock::now();
143
144    std::cout << "Processing audio..." << std::endl;
145    double percentage = 0.0;
146    for (size_t i = 0; i < readSamples; i += frameSize) {
147        size_t remainingSamples = std::min(frameSize, readSamples - i);
148        processFrame(audioData.samples.data() + i, outputBuffer.data() + i, remainingSamples);
149        percentage = (static_cast<double>(i) / readSamples) * 100;
150        std::cout << "Processed " << std::fixed << std::setprecision(2) << percentage << "%\r";
151        frameCount++;
152    }
153    audioData.samples = std::move(outputBuffer);
154    auto processEnd = std::chrono::steady_clock::now();
155
156    std::cout
157        << "Time taken to process " << frameCount << " frames: "
158        << std::chrono::duration_cast<std::chrono::milliseconds>(processEnd - processStart).count()
159        << "ms" << std::endl;
160}