sintonia/library/ecasound-2.7.2/libecasound/audioio-rtnull.cpp

261 lines
6.4 KiB
C++

// ------------------------------------------------------------------------
// audioio-rtnull.cpp: Null audio object with realtime behaviour
// Copyright (C) 1999,2002,2005 Kai Vehmanen
//
// Attributes:
// eca-style-version: 3
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// ------------------------------------------------------------------------
#include <cmath>
#include <iostream>
#include <string>
#include <sys/time.h> /* gettimeofday() */
#include <kvu_dbc.h>
#include <kvu_utils.h>
#include "audioio-device.h"
#include "audioio-rtnull.h"
#include "eca-error.h"
#include "eca-logger.h"
using std::cerr;
using std::endl;
/**
* Definitions from glibc's sys/time.h (LGPL 2.1)
*/
#ifndef timerclear
#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
#endif
#ifndef timeradd
#define timeradd(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
if ((result)->tv_usec >= 1000000) \
{ \
++(result)->tv_sec; \
(result)->tv_usec -= 1000000; \
} \
} while (0)
#endif
#ifndef timercmp
#define timercmp(a, b, CMP) \
(((a)->tv_sec == (b)->tv_sec) ? \
((a)->tv_usec CMP (b)->tv_usec) : \
((a)->tv_sec CMP (b)->tv_sec))
#endif
#ifndef timersub
#define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
#endif
/**
* Function definitions
*/
REALTIME_NULL::REALTIME_NULL(const std::string& name)
{
xruns_rep = 0;
}
REALTIME_NULL::~REALTIME_NULL(void)
{
if (is_open() == true && is_running()) stop();
if (is_open() == true) {
close();
}
if (xruns_rep != 0) {
cerr << "(audioio-rtnull) WARNING! There were " << xruns_rep << " xruns while ";
if (io_mode() != io_read) {
cerr << "writing.\n";
}
else {
cerr << "reading.\n";
}
}
}
void REALTIME_NULL::open(void) throw (AUDIO_IO::SETUP_ERROR &)
{
ECA_LOG_MSG(ECA_LOGGER::user_objects, "open");
double t = static_cast<double>(buffersize()) / samples_per_second();
buffer_length_rep.tv_sec = static_cast<time_t>(floor(t));
buffer_length_rep.tv_usec = static_cast<long>((t - buffer_length_rep.tv_sec) * 1000000.0);
total_buffers_rep = 3;
if (max_buffers() == true) {
total_buffers_rep = 8;
}
total_buffer_length_rep.tv_sec = total_buffers_rep * buffer_length_rep.tv_sec;
total_buffer_length_rep.tv_usec = total_buffers_rep * buffer_length_rep.tv_usec;
AUDIO_IO_DEVICE::open();
}
void REALTIME_NULL::close(void)
{
AUDIO_IO_DEVICE::close();
}
void REALTIME_NULL::prepare(void)
{
ECA_LOG_MSG(ECA_LOGGER::user_objects, "prepare");
timerclear(&data_processed_rep);
AUDIO_IO_DEVICE::prepare();
}
void REALTIME_NULL::start(void)
{
ECA_LOG_MSG(ECA_LOGGER::user_objects, "start");
gettimeofday(&start_time_rep, NULL);
AUDIO_IO_DEVICE::start();
}
void REALTIME_NULL::stop(void)
{
ECA_LOG_MSG(ECA_LOGGER::user_objects, "stop");
AUDIO_IO_DEVICE::stop();
}
/**
* Calculates 'time_since_start_rep'.
*/
void REALTIME_NULL::calculate_device_position(void)
{
struct timeval now;
gettimeofday(&now, NULL);
timersub(&now, &start_time_rep, &time_since_start_rep);
}
/**
* Calculates 'avail_data_rep'.
*/
void REALTIME_NULL::calculate_available_data(void) const
{
if (io_mode() == io_read) {
/* capture: device is always ahead */
timersub(&time_since_start_rep, &data_processed_rep, &avail_data_rep);
}
else {
/* playback: device is always behind */
struct timeval diff;
timersub(&data_processed_rep, &time_since_start_rep, &diff);
timersub(&total_buffer_length_rep, &diff, &avail_data_rep);
}
if (timercmp(&avail_data_rep, &total_buffer_length_rep, >)) {
++xruns_rep;
// cerr << "(audioio-rtnull) xrun occured!" << endl;
}
}
void REALTIME_NULL::block_until_data_available(void)
{
calculate_device_position();
calculate_available_data();
while (timercmp(&avail_data_rep, &buffer_length_rep, <)) {
struct timeval delay;
timersub(&buffer_length_rep, &avail_data_rep, &delay);
// cerr << "(audioio-rtnull) sleeping for: " << ndelay.tv_sec << " sec, " << ndelay.tv_nsec << " nanoseconds.\n";
kvu_sleep(delay.tv_sec, delay.tv_usec * 1000);
calculate_device_position();
calculate_available_data();
}
}
long int REALTIME_NULL::read_samples(void* target_buffer,
long int samples)
{
DBC_CHECK(is_running() == true);
for(int n = 0; n < samples * frame_size(); n++) ((char*)target_buffer)[n] = 0;
block_until_data_available();
/* read one buffer of audio */
timeradd(&data_processed_rep, &buffer_length_rep, &data_processed_rep);
return buffersize();
}
void REALTIME_NULL::write_samples(void* target_buffer,
long int samples)
{
if (is_running() != true) {
/* prefill phase */
/* write one buffer of audio */
timeradd(&data_processed_rep, &buffer_length_rep, &data_processed_rep);
}
else {
/* block until write space available */
block_until_data_available();
/* write one buffer of audio */
timeradd(&data_processed_rep, &buffer_length_rep, &data_processed_rep);
}
}
long int REALTIME_NULL::prefill_space(void) const
{
if (io_mode() != io_read) return total_buffers_rep * buffersize();
return 0;
}
long int REALTIME_NULL::delay(void) const
{
long int delay = 0;
if (is_running() == true) {
calculate_available_data();
double time = avail_data_rep.tv_sec * 1000000.0 + avail_data_rep.tv_usec;
delay = static_cast<SAMPLE_SPECS::sample_pos_t>
(time * samples_per_second() / 1000000.0);
}
DBC_CHECK(delay >= 0);
return delay;
}