diff --git a/app/audio/sampleformat.cpp b/app/audio/sampleformat.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/app/decoder/waveinput.cpp b/app/decoder/waveinput.cpp new file mode 100644 index 000000000..9477139c7 --- /dev/null +++ b/app/decoder/waveinput.cpp @@ -0,0 +1,182 @@ +#include "waveinput.h" + +extern "C" { +#include +} + +#include + +WaveInput::WaveInput(const QString &f) : + file_(f) +{ +} + +WaveInput::~WaveInput() +{ + close(); +} + +bool WaveInput::open() +{ + if (!file_.open(QFile::ReadOnly)) { + return false; + } + + if (file_.read(4) != "RIFF") { + close(); + qDebug() << "No RIFF found"; + return false; + } + + // Skip filesize bytes + file_.seek(file_.pos() + 4); + + if (file_.read(4) != "WAVE") { + close(); + qDebug() << "No WAVE found"; + return false; + } + + // Find fmt_ section + if (!find_str(&file_, "fmt ")) { + close(); + qDebug() << "No fmt found"; + return false; + } + + // Skip fmt_ section size + file_.seek(file_.pos()+4); + + // Create data stream for reading bytes into types + QDataStream data_stream(&file_); + data_stream.setByteOrder(QDataStream::LittleEndian); + + // Read data type + uint16_t data_type; + data_stream >> data_type; + + bool data_is_float; + switch (data_type) { + case 1: // PCM Integer + data_is_float = false; + break; + case 3: + data_is_float = true; + break; + default: + // If it's neither float nor int, we can't work with this file + close(); + qDebug() << "Invalid WAV type" << data_type; + return false; + } + + // Read number of channels + uint16_t channel_count; + data_stream >> channel_count; + + uint64_t channel_layout = static_cast(av_get_default_channel_layout(channel_count)); + + int32_t sample_rate; + data_stream >> sample_rate; + + // Skip bytes per second value and bytes per sample value + file_.seek(file_.pos() + 6); + + uint16_t bits_per_sample; + data_stream >> bits_per_sample; + + SampleFormat format; + + switch (bits_per_sample) { + case 8: + format = SAMPLE_FMT_U8; + break; + case 16: + format = SAMPLE_FMT_S16; + break; + case 32: + if (data_is_float) { + format = SAMPLE_FMT_S32; + } else { + format = SAMPLE_FMT_FLT; + } + break; + case 64: + if (data_is_float) { + format = SAMPLE_FMT_S64; + } else { + format = SAMPLE_FMT_DBL; + } + break; + default: + // We don't know this format... + close(); + qDebug() << "Invalid format found" << bits_per_sample; + return false; + } + + // We're good to go! + params_ = AudioRenderingParams(sample_rate, channel_layout, format); + + if (!find_str(&file_, "data")) { + close(); + qDebug() << "No data tag found"; + return false; + } + + data_position_ = file_.pos() + 4; + + return true; +} + +bool WaveInput::is_open() +{ + return file_.isOpen(); +} + +QByteArray WaveInput::read(int offset, int length) +{ + if (!is_open()) { + return QByteArray(); + } + + file_.seek(offset + data_position_); + return file_.read(length); +} + +void WaveInput::read(int offset, char *buffer, int length) +{ + if (!is_open()) { + return; + } + + file_.seek(offset + data_position_); + file_.read(buffer, length); +} + +AudioRenderingParams WaveInput::params() +{ + return params_; +} + +void WaveInput::close() +{ + if (file_.isOpen()) { + file_.close(); + } +} + +bool WaveInput::find_str(QFile *f, const char *str) +{ + qint64 pos = f->pos(); + while (f->read(4) != str) { + if (f->atEnd()) { + return false; + } + + pos++; + f->seek(pos); + } + + return true; +} diff --git a/app/decoder/waveinput.h b/app/decoder/waveinput.h new file mode 100644 index 000000000..849afd135 --- /dev/null +++ b/app/decoder/waveinput.h @@ -0,0 +1,38 @@ +#ifndef WAVEINPUT_H +#define WAVEINPUT_H + +#include + +#include "render/audioparams.h" + +class WaveInput +{ +public: + WaveInput(const QString& f); + + ~WaveInput(); + + Q_DISABLE_COPY_MOVE(WaveInput) + + bool open(); + + bool is_open(); + + QByteArray read(int offset, int length); + void read(int offset, char *buffer, int length); + + AudioRenderingParams params(); + + void close(); + +private: + bool find_str(QFile* f, const char* str); + + AudioRenderingParams params_; + + QFile file_; + + qint64 data_position_; +}; + +#endif // WAVEINPUT_H diff --git a/app/decoder/waveoutput.cpp b/app/decoder/waveoutput.cpp new file mode 100644 index 000000000..83fd14400 --- /dev/null +++ b/app/decoder/waveoutput.cpp @@ -0,0 +1,144 @@ +#include "waveoutput.h" + +const int16_t kWAVIntegerFormat = 1; +const int16_t kWAVFloatFormat = 3; + +WaveOutput::WaveOutput(const QString &f, + const AudioRenderingParams& params) : + file_(f), + params_(params) +{ + Q_ASSERT(params_.is_valid()); +} + +WaveOutput::~WaveOutput() +{ + close(); +} + +bool WaveOutput::open() +{ + data_length_ = 0; + + if (file_.open(QFile::WriteOnly)) { + // RIFF header + file_.write("RIFF"); + + // Total file size minus RIFF and this integer (minus 8 bytes, filled in later) + write_int(&file_, 0); + + // File type header + file_.write("WAVE"); + + // Begin format descriptor chunk + file_.write("fmt "); + + // Format chunk size + write_int(&file_, 16); + + // Type of format + switch (params_.format()) { + case SAMPLE_FMT_U8: + case SAMPLE_FMT_S16: + case SAMPLE_FMT_S32: + case SAMPLE_FMT_S64: + write_int(&file_, kWAVIntegerFormat); + break; + case SAMPLE_FMT_FLT: + case SAMPLE_FMT_DBL: + write_int(&file_, kWAVFloatFormat); + break; + case SAMPLE_FMT_INVALID: + case SAMPLE_FMT_COUNT: + qWarning() << "Invalid sample format for WAVE audio"; + file_.close(); + return false; + } + + // Number of channels + write_int(&file_, static_cast(params_.channel_count())); + + // Sample rate + write_int(&file_, params_.sample_rate()); + + // Bytes per second + write_int(&file_, params_.samples_to_bytes(params_.sample_rate())); + + // Bytes per sample + write_int(&file_, static_cast(params_.samples_to_bytes(1))); + + // Bits per sample per channel + write_int(&file_, static_cast(params_.bits_per_sample())); + + // Data chunk header + file_.write("data"); + + // Size of data chunk (filled in later) + write_int(&file_, 0); + + return true; + } + + return false; +} + +void WaveOutput::write(const QByteArray &bytes) +{ + if (file_.isOpen()) { + file_.write(bytes); + + data_length_ += bytes.size(); + } +} + +void WaveOutput::write(const char *bytes, int length) +{ + if (file_.isOpen()) { + file_.write(bytes, length); + + data_length_ += length; + } +} + +void WaveOutput::close() +{ + if (file_.isOpen()) { + + // Write file sizes + file_.seek(4); + write_int(&file_, data_length_ + 36); + + file_.seek(40); + write_int(&file_, data_length_); + + file_.close(); + } +} + +void WaveOutput::switch_endianness(QByteArray& array) +{ + int half_sz = array.size()/2; + + for (int i=0;i +void WaveOutput::write_int(QFile *file, T integer) +{ + QByteArray bytes; + bytes.resize(sizeof(T)); + memcpy(bytes.data(), &integer, static_cast(bytes.size())); + + // WAV expects little-endian, so if the integer is big endian we need to switch + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + switch_endianness(bytes); + } + + file->write(bytes); +} diff --git a/app/decoder/waveoutput.h b/app/decoder/waveoutput.h new file mode 100644 index 000000000..8f8d8ccb2 --- /dev/null +++ b/app/decoder/waveoutput.h @@ -0,0 +1,41 @@ +#ifndef WAVEAUDIO_H +#define WAVEAUDIO_H + +#include +#include + +#include "audio/sampleformat.h" +#include "render/audioparams.h" + +class WaveOutput +{ +public: + WaveOutput(const QString& f, + const AudioRenderingParams& params); + + ~WaveOutput(); + + Q_DISABLE_COPY_MOVE(WaveOutput) + + bool open(); + + void write(const QByteArray& bytes); + void write(const char* bytes, int length); + + void close(); + +private: + template + void write_int(QFile* file, T integer); + + void switch_endianness(QByteArray &array); + + QFile file_; + + AudioRenderingParams params_; + + int data_length_; + +}; + +#endif // WAVEAUDIO_H diff --git a/app/render/backend/audio/audioworker.cpp b/app/render/backend/audio/audioworker.cpp new file mode 100644 index 000000000..125e249b8 --- /dev/null +++ b/app/render/backend/audio/audioworker.cpp @@ -0,0 +1,6 @@ +#include "audioworker.h" + +AudioWorker::AudioWorker() +{ + +} diff --git a/app/render/backend/audio/audioworker.h b/app/render/backend/audio/audioworker.h new file mode 100644 index 000000000..76cea9f30 --- /dev/null +++ b/app/render/backend/audio/audioworker.h @@ -0,0 +1,11 @@ +#ifndef AUDIOWORKER_H +#define AUDIOWORKER_H + + +class AudioWorker +{ +public: + AudioWorker(); +}; + +#endif // AUDIOWORKER_H