Files
rtphone/src/engine/audio/Audio_WavFile.cpp

432 lines
9.9 KiB
C++

/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Audio_WavFile.h"
#include "helper/HL_Exception.h"
#include "helper/HL_String.h"
#include "helper/HL_Log.h"
#include "../engine_config.h"
#include <memory.h>
#include <assert.h>
#ifndef WORD
# define WORD unsigned short
#endif
#ifndef DWORD
# define DWORD unsigned int
#endif
typedef struct {
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
WORD wBitsPerSample;
WORD cbSize;
}
WaveFormatEx;
#define WAVE_FORMAT_PCM 1
#define LOG_SUBSYSTEM "WavFileReader"
#define LOCK std::unique_lock<std::recursive_mutex> lock(mFileMtx);
using namespace Audio;
// ---------------------- WavFileReader -------------------------
WavFileReader::WavFileReader()
:mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
{
mDataOffset = 0;
}
WavFileReader::~WavFileReader()
{
}
#define THROW_READERROR throw Exception(ERR_WAVFILE_FAILED);
std::string WavFileReader::readChunk()
{
char name[5] = {0};
readBuffer(name, 4);
std::string result = name;
uint32_t size = 0;
readBuffer(&size, 4);
if (result == "fact")
{
uint32_t dataLength = 0;
readBuffer(&dataLength, sizeof dataLength);
mDataLength = dataLength;
}
else
if (result != "data")
mInput->seekg(size, std::ios_base::beg);
else
mDataLength = size;
return result;
}
void WavFileReader::readBuffer(void* buffer, size_t sz)
{
auto p = mInput->tellg();
mInput->read(reinterpret_cast<char*>(buffer), sz);
if (mInput->tellg() - p != sz)
throw Exception(ERR_WAVFILE_FAILED);
}
size_t WavFileReader::tryReadBuffer(void* buffer, size_t sz)
{
auto p = mInput->tellg();
mInput->read(reinterpret_cast<char*>(buffer), sz);
return mInput->tellg() - p;
}
bool WavFileReader::open(const std::filesystem::path& p)
{
LOCK;
try
{
mPath = p;
mInput = std::make_unique<std::ifstream>(p, std::ios::binary | std::ios::in);
if (!mInput->is_open())
{
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
mLastError = errno;
#endif
#if defined(TARGET_WIN)
mLastError = GetLastError();
#endif
return false;
}
mLastError = 0;
// Read the .WAV header
char riff[4];
readBuffer(riff, sizeof riff);
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
THROW_READERROR;
// Read the file size
uint32_t filesize = 0;
readBuffer(&filesize, sizeof(filesize));
char wavefmt[9] = {0};
readBuffer(wavefmt, 8);
if (strcmp(wavefmt, "WAVEfmt ") != 0)
THROW_READERROR;
uint32_t fmtSize = 0;
readBuffer(&fmtSize, sizeof(fmtSize));
auto fmtStart = mInput->tellg();
uint16_t formattag = 0;
readBuffer(&formattag, sizeof(formattag));
if (formattag != 1/*WAVE_FORMAT_PCM*/)
THROW_READERROR;
mChannels = 0;
readBuffer(&mChannels, sizeof(mChannels));
mSamplerate = 0;
readBuffer(&mSamplerate, sizeof(mSamplerate));
uint32_t avgbytespersec = 0;
readBuffer(&avgbytespersec, sizeof(avgbytespersec));
uint16_t blockalign = 0;
readBuffer(&blockalign, sizeof(blockalign));
mBits = 0;
readBuffer(&mBits, sizeof(mBits));
if (mBits !=8 && mBits != 16)
THROW_READERROR;
// Look for the chunk 'data'
mInput->seekg(fmtStart + std::streampos(fmtSize));
mDataLength = 0;
while (readChunk() != "data")
;
mDataOffset = mInput->tellg();
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
}
catch(...)
{
mInput.reset();
mLastError = static_cast<unsigned>(-1);
}
return isOpened();
}
void WavFileReader::close()
{
LOCK;
mInput.reset();
}
int WavFileReader::samplerate() const
{
return mSamplerate;
}
int WavFileReader::channels() const
{
return mChannels;
}
size_t WavFileReader::read(void* buffer, size_t bytes)
{
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
}
size_t WavFileReader::readRaw(void* buffer, size_t bytes)
{
return readRaw((short*)buffer, bytes / channels() / sizeof(short)) * channels() * sizeof(short);
}
size_t WavFileReader::read(short* buffer, size_t samples)
{
LOCK;
if (!mInput)
return 0;
// Get number of samples that must be read from source file
size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
bool useHeap = requiredBytes > sizeof mTempBuffer;
void* temp;
if (useHeap)
temp = malloc(requiredBytes);
else
temp = mTempBuffer;
memset(temp, 0, requiredBytes);
// Find required size of input buffer
if (mDataLength)
{
auto filePosition = mInput->tellg();
// Check how much data we can read
size_t fileAvailable = mDataLength + mDataOffset - filePosition;
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
}
size_t readBytes = tryReadBuffer(temp, requiredBytes);
size_t processedBytes = 0;
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes,
buffer, samples * 2 * AUDIO_CHANNELS);
if (useHeap)
free(temp);
return result / 2 / AUDIO_CHANNELS;
}
size_t WavFileReader::readRaw(short* buffer, size_t samples)
{
LOCK;
if (!mInput)
return 0;
// Get number of samples that must be read from source file
size_t requiredBytes = samples * channels() * sizeof(short);
// Find required size of input buffer
if (mDataLength)
{
auto filePosition = mInput->tellg();
// Check how much data we can read
size_t fileAvailable = mDataLength + mDataOffset - filePosition;
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
}
size_t readBytes = tryReadBuffer(buffer, requiredBytes);
return readBytes / channels() / sizeof(short);
}
bool WavFileReader::isOpened()
{
LOCK;
if (!mInput)
return false;
return mInput->is_open();
}
void WavFileReader::rewind()
{
LOCK;
if (mInput)
mInput->seekg(mDataOffset);
}
std::filesystem::path WavFileReader::path() const
{
LOCK;
return mPath;
}
size_t WavFileReader::size() const
{
LOCK;
return mDataLength;
}
unsigned WavFileReader::lastError() const
{
return mLastError;
}
// ------------------------- WavFileWriter -------------------------
#define LOG_SUBSYTEM "WavFileWriter"
#define BITS_PER_CHANNEL 16
WavFileWriter::WavFileWriter()
:mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
{}
WavFileWriter::~WavFileWriter()
{
close();
}
void WavFileWriter::checkWriteResult(int result)
{
if (result < 1)
throw Exception(ERR_WAVFILE_FAILED, errno);
}
void WavFileWriter::writeBuffer(const void* buffer, size_t sz)
{
if (!mOutput)
return;
auto p = mOutput->tellp();
mOutput->write(reinterpret_cast<const char*>(buffer), sz);
if (mOutput->tellp() - p != sz)
throw Exception(ERR_WAVFILE_FAILED);
}
bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int channels)
{
LOCK;
close();
mSamplerate = samplerate;
mChannels = channels;
mOutput = std::make_unique<std::ofstream>(p, std::ios::binary | std::ios::trunc);
if (!mOutput)
{
int errorcode = errno;
ICELogError(<< "Failed to create .wav file: filename = " << p << " , error = " << errorcode);
return false;
}
// Write the .WAV header
const char* riff = "RIFF";
writeBuffer(riff, 4);
// Write the file size
uint32_t filesize = 0;
writeBuffer(&filesize, sizeof filesize);
const char* wavefmt = "WAVEfmt ";
writeBuffer(wavefmt, 8);
// Set the format description
uint32_t dwFmtSize = 16; /*= 16L*/;
writeBuffer(&dwFmtSize, sizeof(dwFmtSize));
WaveFormatEx format;
format.wFormatTag = WAVE_FORMAT_PCM;
writeBuffer(&format.wFormatTag, sizeof(format.wFormatTag));
format.nChannels = mChannels;
writeBuffer(&format.nChannels, sizeof(format.nChannels));
format.nSamplesPerSec = mSamplerate;
writeBuffer(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec));
format.nAvgBytesPerSec = mSamplerate * 2 * mChannels;
writeBuffer(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec));
format.nBlockAlign = 2 * mChannels;
writeBuffer(&format.nBlockAlign, sizeof(format.nBlockAlign));
format.wBitsPerSample = BITS_PER_CHANNEL;
writeBuffer(&format.wBitsPerSample, sizeof(format.wBitsPerSample));
const char* data = "data";
writeBuffer(data, 4);
mPath = p;
mWritten = 0;
mLengthOffset = mOutput->tellp();
writeBuffer(&mWritten, sizeof mWritten);
return isOpened();
}
void WavFileWriter::close()
{
LOCK;
mOutput.reset();
}
size_t WavFileWriter::write(const void* buffer, size_t bytes)
{
LOCK;
if (!mOutput)
return 0;
// Seek the end of file - here new data will be written
mOutput->seekp(0, std::ios_base::end);
mWritten += bytes;
// Write the data
writeBuffer(buffer, bytes);
// Write file length
mOutput->seekp(4, std::ios_base::beg);
uint32_t fl = mWritten + 36;
writeBuffer(&fl, sizeof(fl));
// Write data length
mOutput->seekp(mLengthOffset, std::ios_base::beg);
writeBuffer(&mWritten, sizeof(mWritten));
return bytes;
}
bool WavFileWriter::isOpened() const
{
LOCK;
return mOutput.get();
}
std::filesystem::path WavFileWriter::path() const
{
LOCK;
return mPath;
}