1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
|
// ------------------------------------------------------------------------
// audioio.cpp: Routines common for all audio IO-devices.
// Copyright (C) 1999-2004,2008,2009,2010 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 <string>
#include <kvu_dbc.h>
#include <kvu_message_item.h>
#include <kvu_numtostr.h>
#include "eca-error.h"
#include "audioio.h"
#include "eca-logger.h"
/**
* FIXME notes (last update 2008-03-11)
*
* - Modify default implementation of set_label()/label() so
* that is mapped directly to set/get_parameter(1, ...).
*/
const string& AUDIO_IO::SETUP_ERROR::message(void) const { return message_rep; }
AUDIO_IO::SETUP_ERROR::Error_type AUDIO_IO::SETUP_ERROR::type(void) const { return type_rep; }
AUDIO_IO::SETUP_ERROR::SETUP_ERROR(AUDIO_IO::SETUP_ERROR::Error_type type,
const string& message)
: type_rep(type), message_rep(message) { }
// ===================================================================
// Constructors and destructors
AUDIO_IO::~AUDIO_IO(void)
{
if (is_open() == true)
close();
}
AUDIO_IO::AUDIO_IO(const string& name,
int mode)
{
set_label(name);
set_io_mode(mode);
nonblocking_rep = false;
open_rep = false;
}
// ===================================================================
// Attributes
/**
* Returns info about supported I/O modes (bitwise-OR)
*
* Note that return value may change after device is
* opened (some objects will refine their attributes after
* external resources are acquired).
*
* By default, all I/O modes are supported.
*/
int AUDIO_IO::supported_io_modes(void) const { return (io_read | io_readwrite | io_write); }
/**
* Whether device supports non-blocking I/O mode.
*
* Note that return value may change after device is
* opened (some objects will refine their attributes after
* external resources are acquired).
*
* By default, nonblocking mode is not supported.
*/
bool AUDIO_IO::supports_nonblocking_mode(void) const { return false; }
/**
* Whether device supports seeking.
*
* Note that return value may change after device is
* opened (some objects will refine their attributes after
* external resources are acquired).
*
* By default, seeking is supported.
*
* @see supports_seeking_sample_accurate()
*/
bool AUDIO_IO::supports_seeking(void) const { return true; }
/**
* Whether device supports sample accurate seeking.
*
* @see supports_seeking()
*
* Note that return value may change after device is
* opened (some objects will refine their attributes after
* external resources are acquired).
*
* By default, sample accurate seeking is supported if
* supports_seeking() returns 'true'.
*/
bool AUDIO_IO::supports_seeking_sample_accurate(void) const { return AUDIO_IO::supports_seeking(); }
/**
* Whether audio stream has a distinct length. It's important
* to note the difference between this attribute and
* 'supports_seeking()'. For example, a file read through
* a pipe mechanism is not seekable and its length is not
* known until 'finished() becomes true, but still, it is
* of finite length. A sine oscillator on the other hand
* can go on producing a signal forever, and is thus infinite.
*
* This attributes directly affects how 'finished()' should
* to be interpreted. @see finished().
*
* Note that return value may change after device is
* opened (some objects will refine their attributes after
* external resources are acquired).
*
* By default, audio streams are finite length.
*/
bool AUDIO_IO::finite_length_stream(void) const { return true; }
/**
* Whether audio format is fully or partially locked. If this is true,
* audio object has a known audio format, and may not allow to
* override some or all of the values.
*
* Example 1: When working with existing audio files (reading
* or updating), the audio parameters are locked to values
* specified in the file header.
*
* Example 2: File format (or audio subsystem interface specification)
* may hardcode some of the parameters (e.g. only support
* float samples). In these cases locked_audio_format()
* will return true, although some of the parameters may
* still be freely set. Client should check after open()
* which values were set according to its wishes, and which
* were limited by the audio object implementation.
*
* By default, audio format is not locked.
*/
bool AUDIO_IO::locked_audio_format(void) const { return false; }
// ===================================================================
// Configuration (setting and getting configuration parameters)
/**
* Returns info about the current I/O mode.
*/
int AUDIO_IO::io_mode(void) const { return io_mode_rep; }
/**
* Set object input/output-mode. If the requested mode isn't
* supported, the nearest supported mode is used. Because
* of this, it's wise to afterwards check whether the requested
* mode was accepted.
*
* require:
* is_open() != true
*/
void AUDIO_IO::set_io_mode(int mode) { io_mode_rep = mode; }
/**
* Sets the object label. Label is used to identify the object instance.
* Unlike ECA_OBJECT::name(), which is typically the same for all
* instances of the class, label() is instance specific. Still it
* is not guaranteed to be unique for each object. Device and file
* names are typical label values.
*
* require:
* is_open() != true
*/
void AUDIO_IO::set_label(const string& id_label) { id_label_rep = id_label; }
/**
* Enable/disbale nonblocking mode.
*
* require:
* is_open() != true
*/
void AUDIO_IO::toggle_nonblocking_mode(bool value)
{
nonblocking_rep = value;
}
/**
* Returns the current label. See documentation for
* label(const string&).
*/
const string& AUDIO_IO::label(void) const { return id_label_rep; }
/**
* Returns a string containing info about sample format parameters.
*/
string AUDIO_IO::format_info(void) const
{
MESSAGE_ITEM otemp;
if (locked_audio_format() == true && is_open() != true) {
otemp << "Using audio format specified in file header data (file not yet opened).";
} else {
otemp << "Format: " << format_string();
otemp << ", channels " << channels();
otemp << ", srate " << samples_per_second();
if (interleaved_channels() == true)
otemp << ", interleaved";
else
otemp << ", noninterleaved";
if (locked_audio_format() == true)
otemp << " (locked params).";
else
otemp << ".";
}
return otemp.to_string();
}
void AUDIO_IO::set_parameter(int param,
string value)
{
ECA_LOG_MSG(ECA_LOGGER::user_objects,
AUDIO_IO::parameter_set_to_string(param, value));
if (param == 1) set_label(value);
}
string AUDIO_IO::get_parameter(int param) const
{
ECA_LOG_MSG(ECA_LOGGER::system_objects,
AUDIO_IO::parameter_get_to_string(param));
if (param == 1) return label();
return "";
}
/**
* Returns a debugging string for a parameter value change.
*/
string AUDIO_IO::parameter_get_to_string(int param) const
{
return string("get param ")
+ kvu_numtostr(param) + " of \"" + label() + "\": \""
+ kvu_numtostr(param) + "\"";
}
/**
* Returns a debugging string for a parameter value change.
*/
string AUDIO_IO::parameter_set_to_string(int param, string value) const
{
return string("set param ")
+ kvu_numtostr(param) + " of \""
+ label() + "\" to \"" + value + "\"";
}
// ===================================================================
// Main functionality
void AUDIO_IO::open(void) throw (AUDIO_IO::SETUP_ERROR &)
{
DBC_REQUIRE(is_open() != true);
DBC_CHECK(channels() > 0);
DBC_CHECK(sample_format() != ECA_AUDIO_FORMAT::sfmt_none);
DBC_CHECK(samples_per_second() > 0);
open_rep = true;
if (supports_seeking() == true)
seek_position(position_in_samples());
else
/* note: if seeking is not supported, object always starts
* at position 0 when opened */
set_position_in_samples(0);
}
void AUDIO_IO::close(void)
{
DBC_REQUIRE(is_open() == true);
open_rep = false;
}
// ===================================================================
// Runtime information
/**
* If applicable, returns total length of the audio data stored
* into current audio object. In many situations it's impossible
* enquire the whole length of the object. For instance, if the
* object is streaming a finite length audio stream audio object
* from other applications using some type of standard IPC,
* the actual length won't be known until the whole stream has
* been read. As a general rule, if 'supports_seeking() == true',
* length can be known right after initialization. Then again,
* if 'finite_length_stream() == true', the whole stream must
* be processed before we know the actual length. In other
* cases, length is unknown or infinite.
*/
ECA_AUDIO_TIME AUDIO_IO::length(void) const
{
return ECA_AUDIO_TIME(length_in_samples(), samples_per_second());
}
/**
* Returns the current position.
*/
ECA_AUDIO_TIME AUDIO_IO::position(void) const
{
return ECA_AUDIO_TIME(position_in_samples(), samples_per_second());
}
/**
* Is nonblocking mode is enabled?
*/
bool AUDIO_IO::nonblocking_mode(void) const { return nonblocking_rep; }
/**
* Is the audio object ready for reading?
*/
bool AUDIO_IO::readable(void) const { return is_open() && io_mode() != io_write; }
/**
* Is the audio object ready for writing?
*/
bool AUDIO_IO::writable(void) const { return is_open() && io_mode() != io_read; }
/**
* Sets the total length of audio object data.
*/
void AUDIO_IO::length(const ECA_AUDIO_TIME& v)
{
set_length_in_samples(v.samples());
}
/**
* Sets the current position.
*/
void AUDIO_IO::position(const ECA_AUDIO_TIME& v)
{
if (v.samples_per_second() == samples_per_second())
set_position_in_samples(v.samples());
else {
ECA_AUDIO_TIME modified (v);
modified.set_samples_per_second_keeptime(samples_per_second());
set_position_in_samples(modified.samples());
}
}
/**
* Optional status string
*
* An unformatted text string describing the state and status of
* the current object.
*/
string AUDIO_IO::status(void) const {
MESSAGE_ITEM mitem;
mitem.setprecision(3);
mitem << "position (" << position_in_seconds_exact() << "/";
if (finite_length_stream() == true)
mitem << length_in_seconds_exact();
else
mitem << "inf";
mitem << ") seconds.\n -> ";
if (is_open() == true)
mitem << "open, ";
else
mitem << "closed";
if (locked_audio_format() == true &&
is_open() != true) {
mitem << ", audio format not available until object is opened.";
}
else {
mitem << ", " << format_string() << "/" << channels() << "ch/" << samples_per_second();
mitem << "Hz, buffer " << buffersize() << ".";
}
return mitem.to_string();
}
/**
* Overrides the non-virtaul function
* ECA_SAMPLERATE_AWARE::samples_per_second(), that is
* present (through inheritance) in both ECA_AUDIO_FORMAT
* and ECA_AUDIO_POSITION.
*/
SAMPLE_SPECS::sample_rate_t AUDIO_IO::samples_per_second(void) const
{
DBC_CHECK(ECA_AUDIO_FORMAT::samples_per_second() ==
ECA_AUDIO_POSITION::samples_per_second());
return ECA_AUDIO_FORMAT::samples_per_second();
}
void AUDIO_IO::set_samples_per_second(SAMPLE_SPECS::sample_rate_t v)
{
ECA_AUDIO_FORMAT::set_samples_per_second(v);
ECA_AUDIO_POSITION::set_samples_per_second(v);
ECA_LOG_MSG(ECA_LOGGER::system_objects,
"set srate, aobj \"" +
name() + ":" + label() +
"\" to " +
kvu_numtostr(v) + ".");
}
void AUDIO_IO::set_audio_format(const ECA_AUDIO_FORMAT& f_str)
{
ECA_AUDIO_FORMAT::set_audio_format(f_str);
ECA_AUDIO_POSITION::set_samples_per_second(f_str.samples_per_second());
}
SAMPLE_SPECS::sample_pos_t AUDIO_IO::seek_position(SAMPLE_SPECS::sample_pos_t pos)
{
if (supports_seeking() != true &&
pos != 0) {
ECA_LOG_MSG(ECA_LOGGER::info,
"WARNING: seeking not supported by audio objects of type \"" +
name() + "\".");
return position_in_samples();
}
return pos;
}
|