~ubuntu-branches/ubuntu/vivid/soundscaperenderer/vivid

« back to all changes in this revision

Viewing changes to src/audioplayer.cpp

  • Committer: Package Import Robot
  • Author(s): IOhannes m zmölnig (Debian/GNU)
  • Date: 2014-05-08 16:58:09 UTC
  • Revision ID: package-import@ubuntu.com-20140508165809-7tz9dhu5pvo5wy25
Tags: upstream-0.4.1~dfsg
Import upstream version 0.4.1~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************************************
 
2
 * Copyright © 2012-2014 Institut für Nachrichtentechnik, Universität Rostock *
 
3
 * Copyright © 2006-2012 Quality & Usability Lab,                             *
 
4
 *                       Telekom Innovation Laboratories, TU Berlin           *
 
5
 *                                                                            *
 
6
 * This file is part of the SoundScape Renderer (SSR).                        *
 
7
 *                                                                            *
 
8
 * The SSR is free software:  you can redistribute it and/or modify it  under *
 
9
 * the terms of the  GNU  General  Public  License  as published by the  Free *
 
10
 * Software Foundation, either version 3 of the License,  or (at your option) *
 
11
 * any later version.                                                         *
 
12
 *                                                                            *
 
13
 * The SSR is distributed in the hope that it will be useful, but WITHOUT ANY *
 
14
 * WARRANTY;  without even the implied warranty of MERCHANTABILITY or FITNESS *
 
15
 * FOR A PARTICULAR PURPOSE.                                                  *
 
16
 * See the GNU General Public License for more details.                       *
 
17
 *                                                                            *
 
18
 * You should  have received a copy  of the GNU General Public License  along *
 
19
 * with this program.  If not, see <http://www.gnu.org/licenses/>.            *
 
20
 *                                                                            *
 
21
 * The SSR is a tool  for  real-time  spatial audio reproduction  providing a *
 
22
 * variety of rendering algorithms.                                           *
 
23
 *                                                                            *
 
24
 * http://spatialaudio.net/ssr                           ssr@spatialaudio.net *
 
25
 ******************************************************************************/
 
26
 
 
27
/// @file
 
28
/// Audio player using ecasound (implementation).
 
29
 
 
30
#include <jack/jack.h> // for jack_client_name_size()
 
31
#include <algorithm>
 
32
 
 
33
#include "audioplayer.h"
 
34
#include "maptools.h"
 
35
#include "ssr_global.h"
 
36
#include "apf/stringtools.h"
 
37
#include "posixpathtools.h"
 
38
 
 
39
using maptools::get_item;
 
40
 
 
41
/// delete the file map.
 
42
AudioPlayer::~AudioPlayer()
 
43
{
 
44
  if (!_file_map.empty())
 
45
  {
 
46
    maptools::purge(_file_map);
 
47
    VERBOSE2("AudioPlayer dtor: file map deleted.");
 
48
  }
 
49
  else
 
50
  {
 
51
    VERBOSE2("AudioPlayer dtor.");
 
52
  }
 
53
}
 
54
 
 
55
/** _.
 
56
 * If the file is already opened, an existing instance of ecasound is used.
 
57
 * @param audio_file_name name of the audio file, what did you think?
 
58
 * @param channel select a channel of a multichannel file (starting with 1)
 
59
 * @param loop temporary solution for a loop mode
 
60
 * @return Name of the JACK port.
 
61
 * @warning If @a audio_file_name uses symbolic links or such things it can
 
62
 * happen that one file is opened several times.
 
63
 **/
 
64
std::string AudioPlayer::get_port_name(const std::string& audio_file_name,
 
65
    int channel, bool loop)
 
66
{
 
67
  assert(channel >= 0);
 
68
 
 
69
  auto registered_file = get_item(_file_map, audio_file_name);
 
70
  if (registered_file != nullptr)
 
71
  {
 
72
    VERBOSE2("AudioPlayer: Input file '" + audio_file_name
 
73
        + "' already registered.");
 
74
    if (channel > registered_file->get_channels())
 
75
    {
 
76
      ERROR("AudioPlayer: Channel " << channel << " doesn't exist in '"
 
77
          + audio_file_name + "'!");
 
78
      return "";
 
79
    }
 
80
  }
 
81
  else // file not yet registered
 
82
  {
 
83
    auto temp = Soundfile::create(audio_file_name, loop);
 
84
    if (!temp)
 
85
    {
 
86
      WARNING("AudioPlayer: Initialization of soundfile '" + audio_file_name
 
87
          + "' failed!");
 
88
      return "";
 
89
    }
 
90
    if (channel > temp->get_channels())
 
91
    {
 
92
      ERROR("AudioPlayer: Channel " << channel << " doesn't exist in '"
 
93
          + audio_file_name + "'!");
 
94
      // if wrong channel is requested, audiofile is not registered.
 
95
      return "";
 
96
    }
 
97
    registered_file = temp.get();
 
98
    _file_map[audio_file_name] = temp.release();
 
99
  }
 
100
  return registered_file->get_client_name() + ":"
 
101
    + registered_file->output_prefix + "_" + apf::str::A2S(channel);
 
102
}
 
103
 
 
104
/** _.
 
105
 * @param audio_file_name the audio file you want to know the length of.
 
106
 * @warning If the file wasn't loaded before, 0 is returned!
 
107
 **/
 
108
long int AudioPlayer::get_file_length(const std::string& audio_file_name) const
 
109
{
 
110
  const Soundfile* const file = get_item(_file_map, audio_file_name);
 
111
  return file ? file->get_length() : 0;
 
112
}
 
113
 
 
114
std::string
 
115
AudioPlayer::Soundfile::get_format(const std::string& filename
 
116
    , size_t& channels, size_t& sample_rate)
 
117
{
 
118
  ECA_CONTROL_INTERFACE eca;
 
119
  eca.command("cs-add dummy_chainsetup");
 
120
  eca.command("c-add dummy_chain");
 
121
 
 
122
  eca.command("ai-add sndfile,"
 
123
      + posixpathtools::get_escaped_filename(filename));
 
124
  eca.command("ao-add null");
 
125
  eca.command("cs-connect");
 
126
  if (eca.error())
 
127
  {
 
128
    throw soundfile_error("get_format(): " + eca.last_error());
 
129
  }
 
130
  eca.command("ai-index-select 1");
 
131
  eca.command("ai-get-format");
 
132
  std::string str = eca.last_string();
 
133
  eca.command("cs-disconnect");
 
134
  eca.command("c-remove");
 
135
  eca.command("cs-remove");
 
136
 
 
137
  std::replace(str.begin(), str.end(), ',', ' ');
 
138
  std::istringstream iss(str);
 
139
  std::string format;
 
140
  iss >> format >> channels >> sample_rate;
 
141
  if (iss.fail())
 
142
  {
 
143
    throw soundfile_error("Couldn't convert format string!");
 
144
  }
 
145
  assert(sample_rate >= 1);
 
146
 
 
147
  return format;
 
148
}
 
149
 
 
150
size_t
 
151
AudioPlayer::Soundfile::_get_jack_sample_rate()
 
152
{
 
153
  ECA_CONTROL_INTERFACE eca;
 
154
  eca.command("cs-add dummy_chainsetup");
 
155
  eca.command("c-add dummy_chain");
 
156
 
 
157
  eca.command("ai-add jack");
 
158
  eca.command("ao-add null");
 
159
  eca.command("cs-connect");
 
160
  if (eca.error())
 
161
  {
 
162
    throw soundfile_error("_get_jack_sample_rate(): " + eca.last_error());
 
163
  }
 
164
  eca.command("ai-get-format");
 
165
  std::string str = eca.last_string();
 
166
  eca.command("cs-disconnect");
 
167
  eca.command("c-remove");
 
168
  eca.command("cs-remove");
 
169
 
 
170
  std::replace(str.begin(), str.end(), ',', ' ');
 
171
  std::istringstream iss(str);
 
172
  std::string format;
 
173
  size_t channels, sample_rate;
 
174
  iss >> format >> channels >> sample_rate;
 
175
  if (iss.fail())
 
176
  {
 
177
    throw soundfile_error("Couldn't convert string for getting sample rate!");
 
178
  }
 
179
  assert(sample_rate >= 1);
 
180
 
 
181
  return sample_rate;
 
182
}
 
183
 
 
184
/** ctor.
 
185
 * @param filename name of soundfile
 
186
 * @param loop enable loop mode
 
187
 * @param prefix prefix used for channel names
 
188
 * @throw soundfile_error
 
189
 * @attention The soundfile must have the same sampling rate as the JACK server.
 
190
 * @warning If the name of the soundfile (including path) is longer than the
 
191
 * maximum allowed JACK client-name, it gets truncated. But if another file is
 
192
 * truncated to the same client-name, ecasound probably wants to add "_2" to
 
193
 * distinguish them but this will then be longer than the allowed length.
 
194
 * \par
 
195
 * UPDATE: This is already taken care of, but only up to "_99". So if you have
 
196
 * more than 99 equal client-names, you will get a problem. Hopefully, you
 
197
 * don't have that many ...
 
198
 **/
 
199
AudioPlayer::Soundfile::Soundfile(const std::string& filename, bool loop,
 
200
    const std::string& prefix) throw (soundfile_error) :
 
201
  output_prefix(prefix),
 
202
  _filename(filename),
 
203
  _client_name(""),
 
204
  _channels(0)
 
205
{
 
206
  _sample_format = get_format(_filename, _channels, _sample_rate);
 
207
  size_t jack_sample_rate = _get_jack_sample_rate();
 
208
 
 
209
  _eca.command("cs-add real_chainsetup");
 
210
  _eca.command("c-add real_chain");
 
211
  _eca.command("cs-set-audio-format ,"
 
212
      + apf::str::A2S(_channels) + "," + apf::str::A2S(jack_sample_rate));
 
213
 
 
214
  std::string ai_add = "ai-add ";
 
215
  if (loop)
 
216
  {
 
217
    ai_add += "audioloop,";
 
218
  }
 
219
 
 
220
  if (_sample_rate != jack_sample_rate)
 
221
  {
 
222
    WARNING("'" + _filename
 
223
        + "' has a different sample rate than JACK! ("
 
224
        + apf::str::A2S(_sample_rate) + " vs. "
 
225
        + apf::str::A2S(jack_sample_rate) + ")");
 
226
    ai_add += "resample-hq,auto,";
 
227
  }
 
228
 
 
229
  ai_add += "sndfile,";
 
230
  ai_add += posixpathtools::get_escaped_filename(filename);
 
231
 
 
232
  _eca.command(ai_add);
 
233
  _eca.command("ao-add jack_generic," + this->output_prefix);
 
234
 
 
235
  // check if filename is too long for a JACK portname.
 
236
  // if yes, truncate filename (keep the end)
 
237
  int max_size = jack_client_name_size();  // #include <jack/jack.h>
 
238
#ifdef __APPLE__
 
239
  // TODO this is a workaround, and might not work on every system or with every version of jack.
 
240
  // on OS X jack_client_name_size() returns 64, but 52 seems to be the max supported size.
 
241
  //_client_name = "a123456789a123456789a123456789a123456789a123456789a"; // length = 51 + \0 = 52
 
242
  max_size = 52;
 
243
#endif
 
244
  max_size--; // max_size includes the terminating \0 character!
 
245
  max_size -= 3; // to allow ecasound to append a number up to "_99"
 
246
  max_size--; // we will add a special character at the beginning (maybe '['?)
 
247
  _client_name = _filename;
 
248
  assert(max_size >= 0);
 
249
  if (_filename.size() > static_cast<size_t>(max_size))
 
250
  {
 
251
    _client_name = _filename.substr(_filename.size() - max_size);
 
252
    // to visualize the truncation
 
253
    _client_name[0] = '<';
 
254
  }
 
255
  // to group the inputs in an alphabetic list of clients (e.g. in qjackctl)
 
256
  _client_name.insert(0,"["); // see max_size-- above!
 
257
  std::replace(_client_name.begin(), _client_name.end(), '/', '_');
 
258
 
 
259
  // set the name of the JACK client (using the truncated audio-filename)
 
260
  // do not send, only receive transport msg.
 
261
  // this must be done AFTER adding a chain
 
262
  _eca.command("-G:jack," + _client_name + ",recv");
 
263
 
 
264
  _eca.command("cs-connect");
 
265
  if (_eca.error())
 
266
  {
 
267
    throw soundfile_error("AudioPlayer::Soundfile: " + _eca.last_error());
 
268
  }
 
269
 
 
270
  // after cs-connect, get information about the input file:
 
271
  //_eca.command("ai-get-length");
 
272
  //_length = _eca.last_float();
 
273
  _eca.command("ai-get-length-samples");
 
274
  _length_samples = _eca.last_long_integer();
 
275
 
 
276
  _eca.command("engine-launch");
 
277
  if (_eca.error())
 
278
  {
 
279
    _eca.command("cs-disconnect");
 
280
    throw soundfile_error("AudioPlayer::Soundfile: " + _eca.last_error());
 
281
  }
 
282
  // It takes a little time until the client is available
 
283
  // This is a little ugly, but I don't know a better way to do it.
 
284
  // If you know one, tell me, please!
 
285
  usleep(ssr::usleeptime);
 
286
  VERBOSE2("Added '" + _filename
 
287
      + "', format: '" + apf::str::A2S(_sample_format)
 
288
      + "', channels: " + apf::str::A2S(_channels)
 
289
      + ", sample rate: " + apf::str::A2S(_sample_rate) + ".");
 
290
}
 
291
 
 
292
/// disconnects from ecasound.
 
293
AudioPlayer::Soundfile::~Soundfile()
 
294
{
 
295
  // TODO: check if ecasound is really running.
 
296
  _eca.command("cs-disconnect"); // implies "stop" and "engine-halt"
 
297
  VERBOSE2("AudioPlayer::Soundfile: '" + _filename + "' disconnected.");
 
298
}
 
299
 
 
300
AudioPlayer::Soundfile::ptr_t AudioPlayer::Soundfile::create(
 
301
    const std::string& filename, bool loop)
 
302
{
 
303
  ptr_t temp; // temp = NULL
 
304
  try
 
305
  {
 
306
    temp.reset(new Soundfile(filename, loop));
 
307
  }
 
308
  catch(soundfile_error& e)
 
309
  {
 
310
    ERROR(e.what());
 
311
  }
 
312
  return temp;
 
313
}
 
314
 
 
315
int AudioPlayer::Soundfile::get_channels() const
 
316
{
 
317
  return _channels;
 
318
}
 
319
 
 
320
std::string AudioPlayer::Soundfile::get_client_name() const
 
321
{
 
322
  return _client_name;
 
323
}
 
324
 
 
325
long int AudioPlayer::Soundfile::get_length() const
 
326
{
 
327
  return _length_samples;
 
328
}
 
329
 
 
330
// Settings for Vim (http://www.vim.org/), please do not remove:
 
331
// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
 
332
// vim:fdm=expr:foldexpr=getline(v\:lnum)=~'/\\*\\*'&&getline(v\:lnum)!~'\\*\\*/'?'a1'\:getline(v\:lnum)=~'\\*\\*/'&&getline(v\:lnum)!~'/\\*\\*'?'s1'\:'='