/*
AudioFileSourceFunction
Audio output generator which can generate WAV file data from function
Copyright (C) 2021 Hideaki Tai
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "AudioFileSourceFunction.h"
AudioFileSourceFunction::AudioFileSourceFunction(float sec, uint16_t channels, uint32_t sample_per_sec, uint16_t bits_per_sample) {
uint32_t bytes_per_sec = sample_per_sec * channels * bits_per_sample / 8;
uint32_t len = uint32_t(sec * (float)bytes_per_sec);
// RIFF chunk
strncpy(wav_header.riff.chunk_id, "RIFF", 4);
wav_header.riff.chunk_size = 4 // size of riff chunk w/o chunk_id and chunk_size
+ 8 + 16 // size of format chunk
+ 8 + len; // size of data chunk
strncpy(wav_header.riff.format, "WAVE", 4);
// format chunk
strncpy(wav_header.format.chunk_id, "fmt ", 4);
wav_header.format.chunk_size = 16;
wav_header.format.format_tag = 0x0001; // PCM
wav_header.format.channels = channels;
wav_header.format.sample_per_sec = sample_per_sec;
wav_header.format.avg_bytes_per_sec = bytes_per_sec;
wav_header.format.block_align = channels * bits_per_sample / 8;
wav_header.format.bits_per_sample = bits_per_sample;
// data chunk
strncpy(wav_header.data.chunk_id, "data", 4);
wav_header.data.chunk_size = len;
funcs.reserve(channels);
pos = 0;
size = sizeof(WavHeader) + len;
is_ready = false;
is_unique = false;
}
AudioFileSourceFunction::~AudioFileSourceFunction() {
close();
}
uint32_t AudioFileSourceFunction::read(void* data, uint32_t len) {
// callback size must be 1 or equal to channels
if (!is_ready)
return 0;
uint8_t* d = reinterpret_cast(data);
uint32_t i = 0;
while (i < len) {
uint32_t p = pos + i;
if (p < sizeof(WavHeader)) {
// header bytes
d[i] = wav_header.bytes[p];
i += 1;
} else {
// data bytes
float time = (float)(p - sizeof(WavHeader)) / (float)wav_header.format.avg_bytes_per_sec;
float v = funcs[0](time);
for (size_t ch = 0; ch < wav_header.format.channels; ++ch) {
if (!is_unique && ch > 0)
v = funcs[ch](time);
switch (wav_header.format.bits_per_sample) {
case 8: {
Uint8AndInt8 vs {int8_t(v * (float)0x7F)};
d[i] = vs.u;
break;
}
case 32: {
Uint8AndInt32 vs {int32_t(v * (float)0x7FFFFFFF)};
d[i + 0] = vs.u[0];
d[i + 1] = vs.u[1];
d[i + 2] = vs.u[2];
d[i + 3] = vs.u[3];
break;
}
case 16:
default: {
Uint8AndInt16 vs {int16_t(v * (float)0x7FFF)};
d[i + 0] = vs.u[0];
d[i + 1] = vs.u[1];
break;
}
}
}
i += wav_header.format.block_align;
}
}
pos += i;
return (pos >= size) ? 0 : i;
}
bool AudioFileSourceFunction::seek(int32_t pos, int dir) {
if (dir == SEEK_SET) {
if (pos < 0 || (uint32_t)pos >= size)
return false;
this->pos = pos;
} else if (dir == SEEK_CUR) {
int32_t p = (int32_t)this->pos + pos;
if (p < 0 || (uint32_t)p >= size)
return false;
this->pos = p;
} else {
int32_t p = (int32_t)this->size + pos;
if (p < 0 || (uint32_t)p >= size)
return false;
this->pos = p;
}
return true;
}
bool AudioFileSourceFunction::close() {
funcs.clear();
pos = 0;
size = 0;
is_ready = false;
is_unique = false;
return true;
}
bool AudioFileSourceFunction::isOpen() {
return is_ready;
}
uint32_t AudioFileSourceFunction::getSize() {
return size;
}
uint32_t AudioFileSourceFunction::getPos() {
return pos;
}