You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
noisicaa/noisicaa/audioproc/engine/backend_renderer.cpp

161 lines
4.5 KiB
C++

/*
* @begin:license
*
* Copyright (c) 2015-2019, Benjamin Niemann <pink@odahoda.de>
*
* 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 2 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* @end:license
*/
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>
extern "C" {
#include "libavutil/channel_layout.h"
}
#include "noisicaa/core/perf_stats.h"
#include "noisicaa/audioproc/engine/backend_renderer.h"
#include "noisicaa/host_system/host_system.h"
#include "noisicaa/audioproc/engine/realm.h"
namespace noisicaa {
RendererBackend::RendererBackend(HostSystem* host_system, const BackendSettings& settings)
: Backend(host_system, "noisicaa.audioproc.engine.backend.renderer", settings) {}
RendererBackend::~RendererBackend() {}
Status RendererBackend::setup(Realm* realm) {
Status status = Backend::setup(realm);
RETURN_IF_ERROR(status);
if (_settings.datastream_address.size() == 0) {
return ERROR_STATUS("datastream_address not set.");
}
_logger->info("Writing data stream to %s", _settings.datastream_address.c_str());
_datastream = open(_settings.datastream_address.c_str(), O_RDWR);
if (_datastream < 0) {
return OSERROR_STATUS("Failed to open %s", _settings.datastream_address.c_str());
}
for (int c = 0 ; c < 2 ; ++c) {
_samples[c].reset(new BufferData[_host_system->block_size() * sizeof(float)]);
}
_outbuf.reset(new float[2 * _host_system->block_size()]);
return Status::Ok();
}
void RendererBackend::cleanup() {
if (_datastream >= 0) {
close(_datastream);
_datastream = -1;
}
Backend::cleanup();
}
Status RendererBackend::begin_block(BlockContext* ctxt) {
assert(ctxt->perf->current_span_id() == 0);
ctxt->perf->start_span("frame");
for (int c = 0 ; c < 2 ; ++c) {
_channel_written[c] = false;
memset(_samples[c].get(), 0, _host_system->block_size() * sizeof(float));
}
return Status::Ok();
}
Status RendererBackend::end_block(BlockContext* ctxt) {
const float* left_in = (float*)_samples[0].get();
const float* right_in = (float*)_samples[1].get();
float* out = _outbuf.get();
int num_samples = 0;
SampleTime* stime = ctxt->time_map.get();
SampleTime* stime_end = ctxt->time_map.get() + _host_system->block_size();
while (stime < stime_end) {
if (stime->start_time >= MusicalTime(0)) {
*out++ = *left_in;
*out++ = *right_in;
++num_samples;
}
++left_in;
++right_in;
++stime;
}
if (num_samples > 0) {
assert(_datastream >= 0);
assert(num_samples <= (int)_host_system->block_size());
size_t bytes_left = 2 * num_samples * sizeof(float);
char* p = (char*)_outbuf.get();
while (bytes_left > 0) {
ssize_t bytes_written = write(_datastream, p, bytes_left);
if (bytes_written < 0) {
return OSERROR_STATUS("Failed to write to datastream");
}
bytes_left -= bytes_written;
p += bytes_written;
}
_total_samples_written += num_samples;
} else {
if (_total_samples_written > 0 && _datastream >= 0) {
// Signal the other end that we're done.
_logger->info("Closing datastream.");
::close(_datastream);
_datastream = -1;
}
// When we're not playing, sleep a bit, so we don't hog the CPU.
usleep(10000);
}
ctxt->perf->end_span();
assert(ctxt->perf->current_span_id() == 0);
return Status::Ok();
}
Status RendererBackend::output(BlockContext* ctxt, const string& channel, BufferPtr samples) {
int c;
if (channel == "left") {
c = 0;
} else if (channel == "right") {
c = 1;
} else {
return ERROR_STATUS("Invalid channel %s", channel.c_str());
}
if (_channel_written[c]) {
return ERROR_STATUS("Channel %s written multiple times.", channel.c_str());
}
_channel_written[c] = true;
memmove(_samples[c].get(), samples, _host_system->block_size() * sizeof(float));
return Status::Ok();
}
} // namespace noisicaa