aRts audio server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

audioiomas.cc 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. /*
  2. Copyright (C) 2001-2003 Stefan Westerfeld
  3. stefan@space.twc.de
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public License
  13. along with this library; see the file COPYING.LIB. If not, write to
  14. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. Boston, MA 02110-1301, USA.
  16. */
  17. #ifdef HAVE_CONFIG_H
  18. #include <config.h>
  19. #endif
  20. /*
  21. * Only compile this AudioIO class if we have MAS
  22. */
  23. #ifdef HAVE_LIBMAS
  24. extern "C" {
  25. #include <mas/mas.h>
  26. #include <mas/mas_getset.h>
  27. #include <mas/mas_source.h>
  28. }
  29. #include <sys/types.h>
  30. #include <sys/ioctl.h>
  31. #include <sys/time.h>
  32. #include <sys/stat.h>
  33. #include <assert.h>
  34. #include <errno.h>
  35. #include <fcntl.h>
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <unistd.h>
  39. #include <iostream>
  40. #include <algorithm>
  41. #include "debug.h"
  42. #include "audioio.h"
  43. #include "audiosubsys.h"
  44. #include "iomanager.h"
  45. #include "dispatcher.h"
  46. namespace Arts {
  47. class AudioIOMAS : public AudioIO, public TimeNotify {
  48. protected:
  49. mas_channel_t audio_channel;
  50. mas_port_t mix_sink;
  51. mas_port_t srate_source, srate_sink;
  52. mas_port_t audio_source, audio_sink;
  53. mas_port_t endian_sink, endian_source;
  54. mas_port_t sbuf_source, sbuf_sink;
  55. mas_port_t squant_sink, squant_source;
  56. mas_port_t open_source; /* (!) */
  57. mas_device_t endian;
  58. mas_device_t srate;
  59. mas_device_t squant;
  60. mas_device_t sbuf;
  61. mas_data *data;
  62. mas_package package;
  63. int32 mas_error;
  64. std::list<mas_channel_t> allocated_channels;
  65. std::list<mas_port_t> allocated_ports;
  66. std::list<mas_device_t> allocated_devices;
  67. double lastUpdate, bytesPerSec;
  68. int readBufferAvailable;
  69. int writeBufferAvailable;
  70. double currentTime();
  71. void updateBufferSizes();
  72. #ifdef WORDS_BIGENDIAN
  73. static const int defaultFormat = 17;
  74. #else
  75. static const int defaultFormat = 16;
  76. #endif
  77. bool close_with_error(const std::string& text);
  78. public:
  79. AudioIOMAS();
  80. // Timer callback
  81. void notifyTime();
  82. void setParam(AudioParam param, int& value);
  83. int getParam(AudioParam param);
  84. bool open();
  85. void close();
  86. int read(void *buffer, int size);
  87. int write(void *buffer, int size);
  88. };
  89. REGISTER_AUDIO_IO(AudioIOMAS,"mas","MAS Audio Input/Output");
  90. };
  91. using namespace std;
  92. using namespace Arts;
  93. AudioIOMAS::AudioIOMAS()
  94. {
  95. /*
  96. * default parameters
  97. */
  98. param(samplingRate) = 44100;
  99. paramStr(deviceName) = ""; // TODO
  100. param(fragmentSize) = 4096;
  101. param(fragmentCount) = 7;
  102. param(channels) = 2;
  103. param(direction) = 2;
  104. param(format) = defaultFormat;
  105. }
  106. namespace {
  107. int masInitCount = 0;
  108. }
  109. // Opens the audio device
  110. bool AudioIOMAS::open()
  111. {
  112. string& _error = paramStr(lastError);
  113. string& _deviceName = paramStr(deviceName);
  114. int& _channels = param(channels);
  115. int& _fragmentSize = param(fragmentSize);
  116. int& _fragmentCount = param(fragmentCount);
  117. int& _samplingRate = param(samplingRate);
  118. int& _format = param(format);
  119. /* FIXME: do we need to free what we allocate with mas_init() in close() */
  120. if (!masInitCount)
  121. {
  122. mas_error = mas_init();
  123. if (mas_error < 0)
  124. return close_with_error("error connecting to MAS server");
  125. }
  126. masInitCount++;
  127. if (param(direction) != 2)
  128. {
  129. _error = "unsupported direction (currently no full duplex support)";
  130. return false;
  131. }
  132. /*
  133. * data path
  134. *
  135. * audio_sink
  136. * audio_channel: data_channel ("artsd")
  137. * audio_source
  138. * |
  139. * V
  140. * endian_sink
  141. * endian: instantiate_device ("endian")
  142. * open_source = endian_source
  143. * |
  144. * V
  145. * [squant_sink]
  146. * [squant]
  147. * [squant_source]
  148. * |
  149. * V
  150. * [srate_sink]
  151. * [srate]
  152. * [srate_source]
  153. * |
  154. * V
  155. * sbuf_sink
  156. * sbuf
  157. * sbuf_source
  158. * |
  159. * V
  160. * mix_sink: port ("default_mix_sink")
  161. */
  162. // audio_channel, source & sink
  163. mas_error = mas_make_data_channel("artsd", &audio_channel, &audio_source, &audio_sink);
  164. if (mas_error < 0)
  165. return close_with_error("error initializing MAS data channel");
  166. allocated_channels.push_back(audio_channel);
  167. allocated_ports.push_back(audio_source);
  168. allocated_ports.push_back(audio_sink);
  169. // endian, source & sink
  170. mas_error = mas_asm_instantiate_device( "endian", 0, 0, &endian );
  171. if ( mas_error < 0 )
  172. return close_with_error("error initantiating MAS endian device");
  173. allocated_devices.push_back(endian);
  174. mas_error = mas_asm_get_port_by_name( endian, "sink", &endian_sink );
  175. if ( mas_error < 0 )
  176. return close_with_error("error getting MAS endian device sink port");
  177. allocated_ports.push_back(endian_sink);
  178. mas_error = mas_asm_get_port_by_name( endian, "source", &endian_source );
  179. if ( mas_error < 0 )
  180. return close_with_error("error getting MAS endian device source port");
  181. allocated_ports.push_back(endian_source);
  182. char ratestring[16], resolutionstring[16];
  183. sprintf (ratestring, "%u", _samplingRate);
  184. sprintf (resolutionstring, "%u", _format);
  185. mas_data_characteristic* dc;
  186. dc = (mas_data_characteristic *)MAS_NEW( dc );
  187. masc_setup_dc( dc, 6 );
  188. masc_append_dc_key_value( dc, "format", (_format==8) ? "ulinear":"linear" );
  189. masc_append_dc_key_value( dc, "resolution", resolutionstring );
  190. masc_append_dc_key_value( dc, "sampling rate", ratestring );
  191. masc_append_dc_key_value( dc, "channels", "2" );
  192. masc_append_dc_key_value( dc, "endian", "little" );
  193. mas_error = mas_asm_connect_source_sink( audio_source, endian_sink, dc );
  194. if ( mas_error < 0 )
  195. return close_with_error("error connecting MAS net audio source to endian sink");
  196. /* The next device is 'if needed' only. After the following if()
  197. statement, open_source will contain the current unconnected
  198. source in the path (will be either endian_source or
  199. squant_source in this case)
  200. */
  201. open_source = endian_source;
  202. if ( _format != 16 )
  203. {
  204. arts_debug("MAS output: Sample resolution is not 16 bit/sample, instantiating squant device.");
  205. // squant, source & sink
  206. mas_error = mas_asm_instantiate_device( "squant", 0, 0, &squant );
  207. if ( mas_error < 0 )
  208. return close_with_error("error creating MAS squant device");
  209. allocated_devices.push_back(squant);
  210. mas_error = mas_asm_get_port_by_name( squant, "sink", &squant_sink );
  211. if ( mas_error < 0 )
  212. return close_with_error("error getting MAS squant device sink port");
  213. allocated_ports.push_back(squant_sink);
  214. mas_error = mas_asm_get_port_by_name( squant, "source", &squant_source );
  215. if ( mas_error < 0 )
  216. return close_with_error("error getting MAS squant device source port");
  217. allocated_ports.push_back(squant_source);
  218. arts_debug( "MAS output: Connecting endian -> squant.");
  219. masc_strike_dc( dc );
  220. masc_setup_dc( dc, 6 );
  221. masc_append_dc_key_value( dc,"format",(_format==8) ? "ulinear":"linear" );
  222. masc_append_dc_key_value( dc, "resolution", resolutionstring );
  223. masc_append_dc_key_value( dc, "sampling rate", ratestring );
  224. masc_append_dc_key_value( dc, "channels", "2" );
  225. masc_append_dc_key_value( dc, "endian", "host" );
  226. mas_error = mas_asm_connect_source_sink( endian_source, squant_sink, dc );
  227. if ( mas_error < 0 )
  228. return close_with_error("error connecting MAS endian output to squant device");
  229. /* sneaky: the squant device is optional -> pretend it isn't there */
  230. open_source = squant_source;
  231. }
  232. /* Another 'if necessary' device, as above */
  233. if ( _samplingRate != 44100 )
  234. {
  235. arts_debug ("MAS output: Sample rate is not 44100, instantiating srate device.");
  236. // srate, source & sink
  237. mas_error = mas_asm_instantiate_device( "srate", 0, 0, &srate );
  238. if ( mas_error < 0 )
  239. return close_with_error("error initantiating MAS srate device");
  240. allocated_devices.push_back(srate);
  241. mas_error = mas_asm_get_port_by_name( srate, "sink", &srate_sink );
  242. if ( mas_error < 0 )
  243. return close_with_error("error getting MAS srate sink port");
  244. allocated_ports.push_back(srate_sink);
  245. mas_error = mas_asm_get_port_by_name( srate, "source", &srate_source );
  246. if ( mas_error < 0 )
  247. return close_with_error("error getting MAS srate source port");
  248. allocated_ports.push_back(srate_source);
  249. arts_debug( "MAS output: Connecting to srate.");
  250. masc_strike_dc( dc );
  251. masc_setup_dc( dc, 6 );
  252. masc_append_dc_key_value( dc, "format", "linear" );
  253. masc_append_dc_key_value( dc, "resolution", "16" );
  254. masc_append_dc_key_value( dc, "sampling rate", ratestring );
  255. masc_append_dc_key_value( dc, "channels", "2" );
  256. masc_append_dc_key_value( dc, "endian", "host" );
  257. mas_error = mas_asm_connect_source_sink( open_source, srate_sink, dc );
  258. if ( mas_error < 0 )
  259. return close_with_error("error connecting to MAS srate device");
  260. open_source = srate_source;
  261. }
  262. // sbuf, source & sink
  263. mas_error = mas_asm_instantiate_device( "sbuf", 0, 0, &sbuf );
  264. if ( mas_error < 0 )
  265. return close_with_error("error initantiating MAS sbuf device");
  266. allocated_devices.push_back(sbuf);
  267. mas_error = mas_asm_get_port_by_name( sbuf, "sink", &sbuf_sink );
  268. if ( mas_error < 0 )
  269. return close_with_error("error getting MAS sbuf device sink port");
  270. allocated_ports.push_back(sbuf_sink);
  271. mas_error = mas_asm_get_port_by_name( sbuf, "source", &sbuf_source );
  272. if ( mas_error < 0 )
  273. return close_with_error("error getting MAS sbuf device source port");
  274. allocated_ports.push_back(sbuf_source);
  275. masc_strike_dc( dc );
  276. masc_setup_dc( dc, 6 );
  277. masc_append_dc_key_value( dc, "format", "linear" );
  278. masc_append_dc_key_value( dc, "resolution", "16" );
  279. masc_append_dc_key_value( dc, "sampling rate", "44100" );
  280. masc_append_dc_key_value( dc, "channels", "2" );
  281. masc_append_dc_key_value( dc, "endian", "host" );
  282. arts_debug("MAS output: Connecting to sbuf.");
  283. mas_error = mas_asm_connect_source_sink( open_source, sbuf_sink, dc );
  284. if ( mas_error < 0 )
  285. return close_with_error("error connecting to MAS mixer device");
  286. /* configure sbuf */
  287. float BUFTIME_MS = _fragmentSize * _fragmentCount;
  288. BUFTIME_MS *= 1000.0;
  289. BUFTIME_MS /= (float)_channels;
  290. if (_format > 8)
  291. BUFTIME_MS /= 2.0;
  292. BUFTIME_MS /= (float)_samplingRate;
  293. arts_debug("MAS output: BUFTIME_MS = %f", BUFTIME_MS);
  294. masc_setup_package( &package, NULL, 0, 0 );
  295. masc_pushk_uint32( &package, "buftime_ms", (uint32) BUFTIME_MS );
  296. masc_finalize_package( &package );
  297. mas_set( sbuf, "buftime_ms", &package );
  298. masc_strike_package( &package );
  299. masc_setup_package( &package, NULL, 0, 0 );
  300. masc_pushk_int32( &package, "mc_clkid", 9 );
  301. masc_finalize_package( &package );
  302. mas_set( sbuf, "mc_clkid", &package );
  303. masc_strike_package( &package );
  304. mas_source_play( sbuf );
  305. // mix_sink
  306. mas_error = mas_asm_get_port_by_name( 0, "default_mix_sink", &mix_sink );
  307. if (mas_error < 0)
  308. return close_with_error("error finding MAS default sink");
  309. allocated_ports.push_back(mix_sink);
  310. arts_debug("MAS output: Connecting sbuf to mix_sink.");
  311. mas_error = mas_asm_connect_source_sink( sbuf_source, mix_sink, dc );
  312. if ( mas_error < 0 )
  313. return close_with_error("error connecting to MAS mixer device");
  314. data = (mas_data *)MAS_NEW( data );
  315. masc_setup_data( data, _fragmentSize ); /* we can reuse this */
  316. data->length = _fragmentSize;
  317. data->allocated_length = data->length;
  318. data->header.type = 10;
  319. arts_debug("MAS output: playing.");
  320. // Install the timer
  321. Dispatcher::the()->ioManager()->addTimer(10, this);
  322. bytesPerSec = _channels * _samplingRate;
  323. if (_format > 8)
  324. bytesPerSec *= 2;
  325. lastUpdate = 0;
  326. return true;
  327. }
  328. double AudioIOMAS::currentTime()
  329. {
  330. timeval tv;
  331. gettimeofday(&tv,0);
  332. return (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
  333. }
  334. bool AudioIOMAS::close_with_error(const string& text)
  335. {
  336. string& error = paramStr(lastError);
  337. error = text;
  338. error += masc_strmerror (mas_error);
  339. return false;
  340. }
  341. void AudioIOMAS::close()
  342. {
  343. list<mas_port_t>::iterator pi;
  344. for (pi = allocated_ports.begin(); pi != allocated_ports.end(); pi++)
  345. mas_free_port (*pi);
  346. allocated_ports.clear();
  347. list<mas_channel_t>::iterator ci;
  348. for (ci = allocated_channels.begin(); ci != allocated_channels.end(); ci++)
  349. mas_free_channel (*ci);
  350. allocated_channels.clear();
  351. list<mas_device_t>::iterator di;
  352. for (di = allocated_devices.begin(); di != allocated_devices.end(); di++)
  353. {
  354. mas_device_t device = *di;
  355. mas_error = mas_asm_terminate_device_instance(device, 0);
  356. if (mas_error < 0)
  357. arts_warning ("MAS output: error while closing device: %s", masc_strmerror(mas_error));
  358. mas_free_device(device);
  359. }
  360. allocated_devices.clear();
  361. Dispatcher::the()->ioManager()->removeTimer(this);
  362. }
  363. void AudioIOMAS::updateBufferSizes()
  364. {
  365. double time = currentTime();
  366. double waterMark = param(fragmentSize);
  367. waterMark *= 1.3;
  368. if ((time - lastUpdate) * bytesPerSec < waterMark)
  369. return;
  370. lastUpdate = time;
  371. uint32 inbuf_ms;
  372. int32 mas_error;
  373. mas_error = mas_get( sbuf, "inbuf_ms", 0 , &package );
  374. if ( mas_error < 0 )
  375. arts_fatal ("MAS output: error getting size of buffer: %s", masc_strmerror(mas_error));
  376. masc_pull_uint32( &package, &inbuf_ms );
  377. masc_strike_package( &package );
  378. //arts_debug(" inbuf_ms = %u", inbuf_ms);
  379. float bytes = inbuf_ms;
  380. bytes /= 1000.0;
  381. bytes *= param(samplingRate);
  382. bytes *= param(channels);
  383. if(param(format) > 8)
  384. bytes *= 2;
  385. int bytesFree = param(fragmentSize) * param(fragmentCount) - (int)bytes;
  386. if (bytesFree < param(fragmentSize))
  387. bytesFree = 0;
  388. writeBufferAvailable = bytesFree;
  389. arts_debug ("MAS output buffer: %6d / %6d bytes used => %6d bytes free",
  390. (int)bytes, param(fragmentSize) * param(fragmentCount), writeBufferAvailable);
  391. }
  392. // This is called on each timer tick
  393. void AudioIOMAS::notifyTime()
  394. {
  395. updateBufferSizes();
  396. int& _direction = param(direction);
  397. int& _fragmentSize = param(fragmentSize);
  398. for (;;) {
  399. int todo = 0;
  400. if ((_direction & directionRead) && (getParam(canRead) >= _fragmentSize))
  401. todo |= AudioSubSystem::ioRead;
  402. if ((_direction & directionWrite) && (getParam(canWrite) >= _fragmentSize))
  403. todo |= AudioSubSystem::ioWrite;
  404. if (!todo)
  405. return;
  406. AudioSubSystem::the()->handleIO(todo);
  407. }
  408. }
  409. void AudioIOMAS::setParam(AudioParam p, int& value)
  410. {
  411. switch(p) {
  412. #if 0
  413. case fragmentSize:
  414. param(p) = requestedFragmentSize = value;
  415. break;
  416. case fragmentCount:
  417. param(p) = requestedFragmentCount = value;
  418. break;
  419. #endif
  420. default:
  421. param(p) = value;
  422. break;
  423. }
  424. }
  425. int AudioIOMAS::getParam(AudioParam p)
  426. {
  427. int bytes;
  428. int count;
  429. switch(p)
  430. {
  431. #if 0
  432. case canRead:
  433. if (ioctl(audio_fd, AUDIO_GETINFO, &auinfo) < 0)
  434. return (0);
  435. bytes = (auinfo.record.samples * bytesPerSample) - bytesRead;
  436. if (bytes < 0) {
  437. printf("Error: bytes %d < 0, samples=%u, bytesRead=%u\n",
  438. bytes, auinfo.record.samples, bytesRead);
  439. bytes = 0;
  440. }
  441. return bytes;
  442. case canWrite:
  443. if (ioctl(audio_fd, AUDIO_GETINFO, &auinfo) < 0)
  444. return (0);
  445. count = SUN_MAX_BUFFER_SIZE -
  446. (bytesWritten - (auinfo.play.samples * bytesPerSample));
  447. return count;
  448. #endif
  449. case canWrite:
  450. return writeBufferAvailable;
  451. case autoDetect:
  452. /*
  453. * Fairly small priority, for we haven't tested this a lot
  454. */
  455. return 3;
  456. default:
  457. return param(p);
  458. }
  459. }
  460. int AudioIOMAS::read(void *buffer, int size)
  461. {
  462. #if 0
  463. size = ::read(audio_fd, buffer, size);
  464. if (size < 0)
  465. return 0;
  466. bytesRead += size;
  467. return size;
  468. #endif
  469. return 0;
  470. }
  471. int AudioIOMAS::write(void *buffer, int size)
  472. {
  473. static int ts = 0;
  474. static int seq = 0;
  475. data->header.sequence = seq++;
  476. data->header.media_timestamp = ts;
  477. ts += size / 4;
  478. assert(size == data->length);
  479. memcpy(data->segment, buffer, size);
  480. int32 mas_error = mas_send( audio_channel , data );
  481. if (mas_error < 0)
  482. arts_fatal ("MAS output: problem during mas_send: %s", masc_strmerror(mas_error));
  483. writeBufferAvailable -= size;
  484. return size;
  485. }
  486. #endif /* HAVE_LIBMAS */