123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- /*
- This file is part of QTau
- Copyright (C) 2013-2018 Tobias "Tomoko" Platen <tplaten@posteo.de>
- Copyright (C) 2013 digited <https://github.com/digited>
- Copyright (C) 2010-2013 HAL@ShurabaP <https://github.com/haruneko>
- QTau 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 <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: GPL-3.0+
- */
- #include "audio/outputbuffer.h"
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdint.h>
- #include <sys/time.h>
- #define __devloglevel__ 4
- #define debugperf 0
- #define debugperf2 0
- uint32_t getTime() {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- uint32_t ret = static_cast<uint32_t>(tv.tv_usec / 1000 + tv.tv_sec * 1000);
- return ret;
- }
- OutputBuffer::OutputBuffer(JackAudio* jack) {
- _buffersize = 64 * 1024;
- _jackSamplerate = jack->getSamplerate();
- _ringbuffer = jack_ringbuffer_create(_buffersize);
- _sndfile = nullptr;
- _datacount = 0;
- start();
- }
- void OutputBuffer::scheduleSynth(ISynth* synth) {
- if (_scheduledSynth) return;
- _datacount = 0;
- _runtime = 0;
- jack_ringbuffer_reset(_ringbuffer);
- open("/tmp/test.wav", 44100);
- _scheduledSynth = synth;
- }
- void OutputBuffer::run() {
- int data_size = 1024 * 2;
- float* data = new float[data_size];
- uint32_t lasttime = getTime();
- while (1) {
- if (_scheduledSynth) {
- int end = _scheduledSynth->readData(data, data_size);
- if (end == 0) {
- _scheduledSynth->readData(nullptr, 0);
- _scheduledSynth = nullptr;
- close();
- continue;
- }
- int writespace = jack_ringbuffer_write_space(_ringbuffer);
- int size2 = data_size * sizeof(float);
- int sleepcount = 0;
- while (writespace < 2 * size2) {
- if (!_playbackIsStable) emit startPlayback();
- _playbackIsStable = true;
- usleep(1000);
- sleepcount++;
- writespace = jack_ringbuffer_write_space(_ringbuffer);
- }
- uint32_t currenttime = getTime();
- uint32_t timedelta = currenttime - lasttime;
- lasttime = currenttime;
- if (debugperf)
- DEVLOG_DEBUG("sleepcount " + STR(sleepcount) + " writespace " +
- STR(writespace) + " timedelta " + STR(timedelta));
- _runtime += timedelta;
- uint32_t timedelta_max = (int)(data_size * 1000 / 44100);
- if (debugperf2)
- if (timedelta > timedelta_max)
- DEVLOG_DEBUG("timedelta_max " + STR(timedelta_max) + " timedelta " +
- STR(timedelta - sleepcount));
- jack_ringbuffer_write(_ringbuffer, (char*)data, size2);
- sf_write_float(_sndfile, data, data_size);
- } else {
- usleep(20000); // nothing to do
- }
- }
- }
- // on synth start
- void OutputBuffer::open(QString fileName, int samplerate) {
- if (_sndfile) sf_close(_sndfile);
- SF_INFO info;
- memset(&info, 0, sizeof(info));
- info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
- info.samplerate = samplerate;
- info.channels = 1;
- _sndfile = sf_open(fileName.toUtf8().data(), SFM_WRITE, &info);
- if (_jackSamplerate != samplerate) {
- DEVLOG_ERROR("secret rabbit code is not implemented yet\n");
- }
- }
- void OutputBuffer::openReadFile(QString fileName) {
- SF_INFO info;
- memset(&info, 0, sizeof(info));
- _sndfile = sf_open(fileName.toUtf8().data(), SFM_READ, &info);
- _offline = true;
- }
- // on synth end
- void OutputBuffer::close() {
- if (_sndfile) sf_close(_sndfile);
- _sndfile = nullptr;
- jack_ringbuffer_reset(_ringbuffer);
- _datacount = 0;
- }
- // other thread -- copy engine
- void OutputBuffer::readData(float* data, int size) {
- if (_offline && _sndfile) {
- int count = sf_readf_float(_sndfile, data, size);
- if (count == 0) {
- DEVLOG_DEBUG("offline mode: stop playback");
- emit stopPlayback();
- close();
- }
- return;
- }
- // if(!_playbackIsStable) return; //do nothing
- unsigned int data_size = (unsigned int)size * sizeof(float);
- memset(data, 0, data_size);
- unsigned int readspace = jack_ringbuffer_read_space(_ringbuffer);
- float filled = readspace * 1.0 / _buffersize;
- if (filled < 0.5 && debugperf) DEVLOG_DEBUG("reading data " + STR(filled));
- if (!_playbackIsStable) return;
- if (readspace >= data_size) {
- jack_ringbuffer_read(_ringbuffer, (char*)data, data_size);
- } else {
- if (_scheduledSynth) {
- if (debugperf) DEVLOG_DEBUG("XRUN - cannot read from buffer");
- _xruncount++;
- } else {
- if (_playbackIsStable) {
- DEVLOG_DEBUG("end reached xruncount=" + STR(_xruncount) +
- " cputime=" + STR(_runtime));
- stopPlayback();
- _playbackIsStable = false;
- }
- }
- }
- }
- void OutputBuffer::reset() {
- jack_ringbuffer_reset(_ringbuffer);
- _datacount = 0;
- }
|