buffered, tick-based audio generation

portability/boost
zetaPRIME 2018-12-17 16:27:02 -05:00
parent c1e73b922a
commit 96c3c3bd19
2 changed files with 42 additions and 30 deletions

View File

@ -64,6 +64,8 @@ void AudioEngine::play(std::shared_ptr<Project> p) {
output->setBufferSize(sampleRate*4*(10/1000)); // 10ms
output->start(this);
for (auto& b : buffer) b.reserve(static_cast<size_t>(sampleRate/4));
mode = Playing;
}, Qt::QueuedConnection);
}
@ -77,39 +79,44 @@ void AudioEngine::stop() {
}
qint64 AudioEngine::readData(char *data, qint64 maxlen) {
static double time = 0;
const constexpr qint64 smp = 2;
const constexpr qint64 stride = smp*2;
qint64 sr = maxlen;
while (sr >= stride) {
if (bufPos >= buffer[0].size()) nextTick(); // process next tick when end of buffer reached
if (bufPos >= buffer[0].size()) break; // if held up still, let the event loop run another cycle
int16_t* l = reinterpret_cast<int16_t*>(data);
int16_t* r = reinterpret_cast<int16_t*>(data+smp);
data += stride;
*l = static_cast<int16_t>(buffer[0][bufPos] * 32767);
*r = static_cast<int16_t>(buffer[1][bufPos] * 32767);
bufPos++;
sr -= stride;
}
return maxlen - sr;
}
void AudioEngine::nextTick() {
bufPos = 0;
buffer[0].clear();
buffer[1].clear();
buffer[0].resize(480);
buffer[1].resize(480);
static double time = 0;
const double PI = std::atan(1)*4;
const double SEMI = std::pow(2.0, 1.0/12.0);
double vol = std::pow(.5, 4);
while (sr >= 4) {
sr -= 4;
int16_t* l = reinterpret_cast<int16_t*>(data);
int16_t* r = reinterpret_cast<int16_t*>(data+2);
vol = std::pow(.5 + std::sin(time * PI*2) * .15, 4);
/**l = static_cast<int16_t>(std::sin(time * PI*2 * 440 * std::pow(SEMI, 4)) * 32767 * vol);
*r = static_cast<int16_t>(std::sin(time * PI*2 * 440) * 32767 * vol);
*l += static_cast<int16_t>(std::sin(time * PI*2 * 440 * std::pow(SEMI, 11.9)) * 32767 * vol);
*r += static_cast<int16_t>(std::sin(time * PI*2 * 440 * std::pow(SEMI, 7)) * 32767 * vol);*/
*l = 0;
//*l += static_cast<int16_t>(std::sin(time * PI*2 * 440 * std::pow(SEMI, 1)) * 32767 * vol);
//*l += static_cast<int16_t>(std::sin(time * PI*2 * 440 * std::pow(SEMI, 1.1)) * 32767 * vol);
*l += static_cast<int16_t>(std::clamp(std::sin(time * PI*2 * 440 * std::pow(SEMI, time - 48)) * 3, -1.0, 1.0) * 32767 * vol);
*r = *l;
for (size_t i = 0; i < buffer[0].size(); i++) {
buffer[0][i] = static_cast<float>(std::sin(time * PI*2 * 440 * std::pow(SEMI, 0)) * .25);
buffer[1][i] = static_cast<float>(std::sin(time * PI*2 * 440 * std::pow(SEMI, 3)) * .25);
time += 1.0/sampleRate;
data += 4;
}
while (sr > 0) {
sr--;
*data = 0;
++data;
}
//qDebug() << "audio engine requested:" << maxlen;
return maxlen;
}

View File

@ -27,9 +27,14 @@ namespace Xybrid::Audio {
std::unique_ptr<QAudioOutput> output;
int sampleRate = 48000;
std::vector<float> buffer[2];
size_t bufPos = 0;
PlaybackMode mode = Stopped;
std::shared_ptr<Data::Project> project;
void postInit();
void nextTick();
public:
static void init();
inline constexpr PlaybackMode playbackMode() const { return mode; }
@ -38,9 +43,9 @@ namespace Xybrid::Audio {
void stop();
// QIODevice functions
qint64 readData(char* data, qint64 maxlen) override;// {return 0;}
qint64 readData(char* data, qint64 maxlen) override;
qint64 writeData(const char*, qint64) override { return 0; }
qint64 bytesAvailable() const override { return 1166; }
qint64 bytesAvailable() const override { return 0; } // not actually used by QAudioOutput
signals:
void playbackModeChanged(PlaybackMode);