#include "avfsincludes.h"
#define WAV_MAX_SAMPLE_BLOCK_SIZE 256
struct AvfsWavFile final:
AvfsMediaFile_
{
int references = 1;
Synther_* avs = nullptr;
uint64_t startSample;
uint64_t sampleCount;
union {
WaveHeader wave;
Wave64Header wave64;
} hdr;
uint16_t sampleBlockSize;
size_t hdrSize;
uint64_t dataSize;
AvfsWavFile(
Synther_* avs,
bool isFloat,
uint16_t sampleBlockSize,
uint64_t sampleCount,
uint64_t startSample,
uint64_t maxFileSize,
bool forceWave64);
~AvfsWavFile(void);
void AddRef(void);
void Release(void);
bool ReadMedia(
AvfsLog_* log,
uint64_t fileOffset,
void* buffer,
size_t requestedSize);
};
AvfsWavFile::AvfsWavFile(
Synther_ *inAvs,
bool isFloat,
uint16_t inSampleBlockSize,
uint64_t totalSampleCount,
uint64_t inStartSample,
uint64_t maxFileSize,
bool forceWave64)
{
avs = inAvs;
avs->AddRef();
startSample = inStartSample;
sampleCount = totalSampleCount - inStartSample;
sampleBlockSize = inSampleBlockSize;
const VideoInfoAdapter &vi = avs->GetVideoInfo();
uint64_t maxSampleCount = (maxFileSize-sizeof(hdr.wave)) / sampleBlockSize;
bool wave64 = forceWave64;
if (maxFileSize > maxWaveFileSize || wave64) {
maxSampleCount = (maxFileSize - sizeof(hdr.wave64)) / sampleBlockSize;
}
if (sampleCount > maxSampleCount) {
sampleCount = maxSampleCount;
}
dataSize = sampleCount * sampleBlockSize;
if (sizeof(hdr.wave) + dataSize > maxWaveFileSize)
wave64 = true;
if (wave64) { CreateWave64Header(hdr.wave64, isFloat, vi.BitsPerChannelSample(), vi.SamplesPerSecond(), vi.ChannelLayout(), sampleCount);
hdrSize = sizeof(hdr.wave64);
} else {
CreateWaveHeader(hdr.wave, isFloat, vi.BitsPerChannelSample(), vi.SamplesPerSecond(), vi.ChannelLayout(), sampleCount);
hdrSize = sizeof(hdr.wave);
}
}
AvfsWavFile::~AvfsWavFile(void)
{
ASSERT(!references);
avs->Release();
}
void AvfsWavFile::AddRef(void)
{
ASSERT(references);
references ++;
}
void AvfsWavFile::Release(void)
{
ASSERT(references);
if(!--references)
{
delete this;
}
}
bool AvfsWavFile::ReadMedia(
AvfsLog_* log,
uint64_t inFileOffset,
void* inBuffer,
size_t inRequestedSize)
{
ASSERT(inRequestedSize);
ASSERT(inFileOffset < hdrSize+dataSize);
ASSERT(inFileOffset+inRequestedSize <= hdrSize+dataSize);
bool success = true;
uint64_t offset = inFileOffset;
size_t remainingSize = inRequestedSize;
uint8_t* buffer = static_cast<uint8_t*>(inBuffer);
uint8_t temp[WAV_MAX_SAMPLE_BLOCK_SIZE];
if (remainingSize && offset < hdrSize) {
size_t partSize = remainingSize;
if (offset+partSize > hdrSize) {
partSize = hdrSize-size_t(offset);
}
memcpy(buffer, reinterpret_cast<uint8_t*>(&hdr)+size_t(offset), partSize);
buffer += partSize;
offset += partSize;
remainingSize -= partSize;
}
offset -= hdrSize;
size_t extraOffset = size_t(offset%sampleBlockSize);
if (remainingSize && extraOffset) {
size_t partSize = remainingSize;
if (extraOffset+partSize > sampleBlockSize) {
partSize = sampleBlockSize-extraOffset;
}
success = success && avs->GetAudio(log, temp, offset/sampleBlockSize, 1);
memcpy(buffer, temp+extraOffset, partSize);
buffer += partSize;
offset += partSize;
remainingSize -= partSize;
}
if (remainingSize > sampleBlockSize) {
ASSERT(offset%sampleBlockSize == 0);
size_t partSize = remainingSize-remainingSize%sampleBlockSize;
success = success && avs->GetAudio(log, buffer, offset/sampleBlockSize,
int(partSize/sampleBlockSize));
buffer += partSize;
offset += partSize;
remainingSize -= partSize;
}
if (remainingSize) {
ASSERT(remainingSize < sampleBlockSize);
success = success && avs->GetAudio(log, temp, offset/sampleBlockSize, 1);
memcpy(buffer, temp, remainingSize);
}
return success;
}
void AvfsWavMediaInit(
AvfsLog_* log,
Synther_* avs,
AvfsVolume_* volume)
{
ASSERT(log && avs && volume);
AvfsWavFile* file;
const VideoInfoAdapter &vi = avs->GetVideoInfo();
size_t bytesPerOutputSample = (vi.BitsPerChannelSample() + 7) / 8;
uint16_t sampleBlockSize = uint16_t(bytesPerOutputSample * vi.AudioChannels());
uint64_t sampleCount = static_cast<uint64_t>(vi.num_audio_samples);
uint64_t position = 0;
uint64_t endPosition;
unsigned fileNumber = 0;
static const size_t maxFileNameChars = 300;
wchar_t fileName[maxFileNameChars];
if (!vi.HasAudio()) {
log->Printf(L"AvfsWavMediaInit: Clip has no audio.\r\n");
} else if (sampleBlockSize > WAV_MAX_SAMPLE_BLOCK_SIZE) {
log->Printf(
L"AvfsWavMediaInit: Unsupported BitsPerChannelSample(%i)"
L"or SampleType(%i).\n",sampleBlockSize, vi.BitsPerChannelSample());
} else {
file = new(std::nothrow) AvfsWavFile(avs, vi.AudioIsFloat(),sampleBlockSize,
sampleCount,0,maxWaveFileSize,false);
if (file) {
if (file->sampleCount == sampleCount) {
ssformat(fileName,maxFileNameChars,L"%s.wav",volume->GetMediaName());
volume->CreateMediaFile(file,fileName,file->hdrSize+file->dataSize);
}
file->Release();
}
file = new(std::nothrow) AvfsWavFile(avs, vi.AudioIsFloat(),sampleBlockSize,
sampleCount,0,UINT64_MAX,true);
if (file) {
ssformat(fileName,maxFileNameChars,L"%s.w64",volume->GetMediaName());
volume->CreateMediaFile(file,fileName,file->hdrSize+file->dataSize);
file->Release();
}
while (position < sampleCount) {
file = new(std::nothrow) AvfsWavFile(avs, vi.AudioIsFloat(),sampleBlockSize,
sampleCount,position,maxCompatWaveFileSize,false);
if(!file) {
position = UINT64_MAX;
}
else {
endPosition = position+file->sampleCount;
ssformat(fileName,maxFileNameChars,L"%s.%02u.wav",
volume->GetMediaName(),fileNumber);
volume->CreateMediaFile(file,fileName,file->hdrSize+file->dataSize);
file->Release();
position = endPosition;
}
fileNumber ++;
}
}
}