PageRenderTime 33ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/kdenetwork-4.8.97/kopete/protocols/jabber/jingle/alsaio.cpp

#
C++ | 557 lines | 424 code | 80 blank | 53 comment | 81 complexity | 4623cca6a1945dec4214d7ea34ba25a7 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0
  1. /*
  2. * alsaio.cpp - An alsa I/O manager (works in Capture or in Playback mode but not both at a time.)
  3. *
  4. * Copyright (c) 2008 by Detlev Casanova <detlev.casanova@gmail.com>
  5. *
  6. * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
  7. *
  8. * *************************************************************************
  9. * * *
  10. * * This program is free software; you can redistribute it and/or modify *
  11. * * it under the terms of the GNU General Public License as published by *
  12. * * the Free Software Foundation; either version 2 of the License, or *
  13. * * (at your option) any later version. *
  14. * * *
  15. * *************************************************************************
  16. */
  17. #include "alsaio.h"
  18. #include <alsa/asoundlib.h>
  19. #include <QObject>
  20. #include <QSocketNotifier>
  21. #include <QStringList>
  22. #include <KDebug>
  23. // taken from netinterface_unix (changed the split to KeepEmptyParts)
  24. static QStringList read_proc_as_lines(const char *procfile)
  25. {
  26. QStringList out;
  27. FILE *f = fopen(procfile, "r");
  28. if(!f)
  29. return out;
  30. QByteArray buf;
  31. while(!feof(f))
  32. {
  33. // max read on a proc is 4K
  34. QByteArray block(4096, 0);
  35. int ret = fread(block.data(), 1, block.size(), f);
  36. if(ret <= 0)
  37. break;
  38. block.resize(ret);
  39. buf += block;
  40. }
  41. fclose(f);
  42. QString str = QString::fromLocal8Bit(buf);
  43. out = str.split('\n', QString::KeepEmptyParts);
  44. return out;
  45. }
  46. QList<Item> getAlsaItems()
  47. {
  48. #ifdef Q_OS_LINUX
  49. QList<Item> out;
  50. QList<AlsaItem> items;
  51. QStringList devices_lines = read_proc_as_lines("/proc/asound/devices");
  52. foreach(const QString &line, devices_lines)
  53. {
  54. // get the fields we care about
  55. QString devbracket, devtype;
  56. int x = line.indexOf(": ");
  57. if(x == -1)
  58. continue;
  59. QString sub = line.mid(x + 2);
  60. x = sub.indexOf(": ");
  61. if(x == -1)
  62. continue;
  63. devbracket = sub.mid(0, x);
  64. devtype = sub.mid(x + 2);
  65. // skip all but playback and capture
  66. bool input;
  67. if(devtype == "digital audio playback")
  68. input = false;
  69. else if(devtype == "digital audio capture")
  70. input = true;
  71. else
  72. continue;
  73. // skip what isn't asked for
  74. /*if(!(type & DIR_INPUT) && input)
  75. continue;
  76. if(!(type & DIR_OUTPUT) && !input)
  77. continue;
  78. */
  79. // hack off brackets
  80. if(devbracket[0] != '[' || devbracket[devbracket.length()-1] != ']')
  81. continue;
  82. devbracket = devbracket.mid(1, devbracket.length() - 2);
  83. QString cardstr, devstr;
  84. x = devbracket.indexOf('-');
  85. if(x == -1)
  86. continue;
  87. cardstr = devbracket.mid(0, x);
  88. devstr = devbracket.mid(x + 1);
  89. AlsaItem ai;
  90. bool ok;
  91. ai.card = cardstr.toInt(&ok);
  92. if(!ok)
  93. continue;
  94. ai.dev = devstr.toInt(&ok);
  95. if(!ok)
  96. continue;
  97. ai.input = input;
  98. ai.name.sprintf("ALSA Card %d, Device %d", ai.card, ai.dev);
  99. items += ai;
  100. }
  101. // try to get the friendly names
  102. QStringList pcm_lines = read_proc_as_lines("/proc/asound/pcm");
  103. foreach(const QString &line, pcm_lines)
  104. {
  105. QString devnumbers, devname;
  106. int x = line.indexOf(": ");
  107. if(x == -1)
  108. continue;
  109. devnumbers = line.mid(0, x);
  110. devname = line.mid(x + 2);
  111. x = devname.indexOf(" :");
  112. if(x != -1)
  113. devname = devname.mid(0, x);
  114. else
  115. devname = devname.trimmed();
  116. QString cardstr, devstr;
  117. x = devnumbers.indexOf('-');
  118. if(x == -1)
  119. continue;
  120. cardstr = devnumbers.mid(0, x);
  121. devstr = devnumbers.mid(x + 1);
  122. bool ok;
  123. int cardnum = cardstr.toInt(&ok);
  124. if(!ok)
  125. continue;
  126. int devnum = devstr.toInt(&ok);
  127. if(!ok)
  128. continue;
  129. for(int n = 0; n < items.count(); ++n)
  130. {
  131. AlsaItem &ai = items[n];
  132. if(ai.card == cardnum && ai.dev == devnum)
  133. ai.name = devname;
  134. }
  135. }
  136. for(int n = 0; n < items.count(); ++n)
  137. {
  138. AlsaItem &ai = items[n];
  139. // make an item for both hw and plughw
  140. Item i;
  141. i.type = Item::Audio;
  142. if(ai.input)
  143. i.dir = Item::Input;
  144. else
  145. i.dir = Item::Output;
  146. i.name = ai.name;
  147. i.driver = "alsa";
  148. i.id = QString().sprintf("plughw:%d,%d", ai.card, ai.dev);
  149. out += i;
  150. i.name = ai.name + " (Direct)";
  151. i.id = QString().sprintf("hw:%d,%d", ai.card, ai.dev);
  152. out += i;
  153. }
  154. Item outDef, inDef;
  155. outDef.type = Item::Audio;
  156. outDef.dir = Item::Output;
  157. outDef.name = "Default device";
  158. outDef.id = "default";
  159. outDef.driver = "alsa";
  160. inDef.type = Item::Audio;
  161. inDef.dir = Item::Input;
  162. inDef.name = "Default device";
  163. inDef.id = "default";
  164. inDef.driver = "alsa";
  165. out << outDef << inDef;
  166. return out;
  167. #else
  168. // return empty list if non-linux
  169. return QList<Item>();
  170. #endif
  171. }
  172. AlsaIO::AlsaIO(StreamType t, QString device, Format f)
  173. : m_type(t)
  174. {
  175. times = 0;
  176. ready = false;
  177. written = 0;
  178. notifier = 0;
  179. bufferizing = true;
  180. int err;
  181. if ((err = snd_pcm_open(&handle, device.toUtf8().data(), m_type == Capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0)
  182. {
  183. kDebug() << "cannot open audio device" << device;
  184. kDebug() << "trying default";
  185. if ((err = snd_pcm_open(&handle, "default", m_type == Capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0)
  186. {
  187. kDebug() << "cannot open audio device default";
  188. return;
  189. }
  190. }
  191. if ((err = snd_pcm_hw_params_malloc(&hwParams)) < 0)
  192. {
  193. kDebug() << "cannot allocate hardware parameter structure" ;
  194. return;
  195. }
  196. if ((err = snd_pcm_hw_params_any(handle, hwParams)) < 0)
  197. {
  198. kDebug() << "cannot initialize hardware parameter structure" ;
  199. return;
  200. }
  201. if ((err = snd_pcm_hw_params_set_access(handle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
  202. {
  203. kDebug() << "cannot set access type" ;
  204. return;
  205. }
  206. snd_pcm_format_t fmt = static_cast<snd_pcm_format_t>(f);
  207. if ((err = snd_pcm_hw_params_set_format(handle, hwParams, fmt)) < 0)
  208. {
  209. kDebug() << "cannot set sample format";
  210. kDebug() << "Setting first format...";
  211. if ((err = snd_pcm_hw_params_set_format_first(handle, hwParams, &fmt)) < 0)
  212. {
  213. kDebug() << "cannot set first sample format !";
  214. return;
  215. }
  216. }
  217. snd_pcm_hw_params_get_format(hwParams, &fmt);
  218. m_format = static_cast<Format>(fmt);
  219. unsigned int p = 20000;
  220. if ((err = snd_pcm_hw_params_set_period_time_near(handle, hwParams, &p, 0)) < 0)
  221. {
  222. kDebug() << "cannot set period time near to 20 ms";
  223. return;
  224. }
  225. samplingRate = 8000;
  226. if ((err = snd_pcm_hw_params_set_rate_near(handle, hwParams, &samplingRate, 0)) < 0)
  227. {
  228. kDebug() << "cannot set sample rate";
  229. //Don't return now, could work without that.
  230. //return;
  231. }
  232. if ((err = snd_pcm_hw_params_set_channels(handle, hwParams, 1)) < 0)
  233. {
  234. kDebug() << "cannot set channel 1";
  235. return;
  236. }
  237. if ((err = snd_pcm_hw_params(handle, hwParams)) < 0)
  238. {
  239. kDebug() << "cannot set parameters";
  240. return;
  241. }
  242. snd_pcm_hw_params_get_period_size(hwParams, &pSize, 0);
  243. kDebug() << "Period size =" << pSize;
  244. snd_pcm_hw_params_get_period_time(hwParams, &pTime, 0);
  245. kDebug() << "Period time =" << pTime;
  246. snd_pcm_hw_params_get_rate (hwParams, &samplingRate, 0);
  247. kDebug() << "Sampling rate =" << samplingRate;
  248. pSizeBytes = snd_pcm_frames_to_bytes(handle, pSize);
  249. kDebug() << pSizeBytes;
  250. ready = true;
  251. }
  252. AlsaIO::~AlsaIO()
  253. {
  254. if (notifier)
  255. {
  256. close(notifier->socket());
  257. delete notifier;
  258. }
  259. if (ready)
  260. {
  261. snd_pcm_drain(handle);
  262. snd_pcm_close(handle);
  263. }
  264. kDebug() << "DESTROYED";
  265. }
  266. AlsaIO::StreamType AlsaIO::type() const
  267. {
  268. return m_type;
  269. }
  270. bool AlsaIO::start()
  271. {
  272. kDebug() << "start()";
  273. if (ready)
  274. {
  275. //This is done here so we can modify parameters before starting.
  276. snd_pcm_hw_params_free(hwParams);
  277. if (snd_pcm_prepare(handle) < 0)
  278. {
  279. kDebug() << "cannot prepare audio interface for use" ;
  280. ready = false;
  281. }
  282. }
  283. if (!ready)
  284. {
  285. if (m_type == Capture)
  286. {
  287. kDebug() << "Device is not ready, no packet will be sent.";
  288. return false;
  289. }
  290. else if (m_type == Playback)
  291. {
  292. kDebug() << "Device is not ready, we will simply drop packets. --> NO PLAYBACK";
  293. return false;
  294. }
  295. }
  296. fdCount = snd_pcm_poll_descriptors_count(handle);
  297. if (fdCount <= 0)
  298. {
  299. kDebug() << "No poll fd... WEIRD!";
  300. return false;
  301. }
  302. ufds = new pollfd[fdCount];
  303. int err = snd_pcm_poll_descriptors(handle, ufds, fdCount);
  304. if (err < 0)
  305. {
  306. kDebug() << "Error retrieving fd.";
  307. return false;
  308. }
  309. kDebug() << "Retrieved" << fdCount << "file descriptors.";
  310. if (m_type == Capture)
  311. {
  312. kDebug() << "Setting up Capture";
  313. //Always use the first pollfd
  314. notifier = new QSocketNotifier(ufds[0].fd, QSocketNotifier::Read, this);
  315. notifier->setEnabled(true);
  316. connect(notifier, SIGNAL(activated(int)), this, SLOT(slotReadyRead(int)));
  317. snd_pcm_start(handle);
  318. }
  319. else if (m_type == Playback)
  320. {
  321. kDebug() << "Setting up Playback";
  322. //Always use the first pollfd
  323. QSocketNotifier::Type type;
  324. switch (ufds[0].events & (POLLIN | POLLPRI | POLLOUT))
  325. {
  326. case POLLIN:
  327. kDebug() << "QSocketNotifier::Read";
  328. type = QSocketNotifier::Read;
  329. break;
  330. case POLLOUT:
  331. kDebug() << "QSocketNotifier::Write";
  332. type = QSocketNotifier::Write;
  333. break;
  334. default:
  335. kDebug() << "Unsupported poll events";
  336. return false;
  337. }
  338. notifier = new QSocketNotifier(ufds[0].fd, type);
  339. notifier->setEnabled(false); //Will be activated as soon as data comes in
  340. connect(notifier, SIGNAL(activated(int)), this, SLOT(slotReadyWrite(int)));
  341. kDebug() << "Time stamp =" << timeStamp();
  342. }
  343. kDebug() << "started.";
  344. return true;
  345. }
  346. void AlsaIO::write(const QByteArray& data)
  347. {
  348. if (!ready || m_type != Playback)
  349. {
  350. //kDebug() << "Packet dropped";
  351. return;
  352. }
  353. buf.append(data);
  354. // Bufferize for 150 ms before playing.
  355. if (bufferizing && buf.size() >= pSizeBytes * 75)
  356. {
  357. bufferizing = false;
  358. notifier->setEnabled(true);
  359. }
  360. // Rebuffer if there is only 50 ms left in the buffer.
  361. if (buf.size() < pSizeBytes * 25)
  362. {
  363. bufferizing = true;
  364. notifier->setEnabled(false);
  365. }
  366. if (!bufferizing && notifier && !notifier->isEnabled())
  367. {
  368. //kDebug() << "Reactivating notifier.";
  369. notifier->setEnabled(true);
  370. }
  371. }
  372. bool AlsaIO::isReady()
  373. {
  374. return ready;
  375. }
  376. /**
  377. * @return period time in milisecond
  378. */
  379. unsigned int AlsaIO::periodTime() const
  380. {
  381. return pTime / 1000;
  382. }
  383. /**
  384. * @return sampling rate.
  385. */
  386. unsigned int AlsaIO::sRate() const
  387. {
  388. return samplingRate;
  389. }
  390. QByteArray AlsaIO::data()
  391. {
  392. //QByteArray data = buf;
  393. //kDebug() << "data.size() =" << data.size();
  394. //buf.clear();
  395. return buf;
  396. }
  397. unsigned int AlsaIO::timeStamp()
  398. {
  399. unsigned int wps = sRate()/8; // Bytes per second
  400. kDebug() << "Bytes per second =" << wps;
  401. unsigned int wpms = wps/1000; // Bytes per milisecond
  402. kDebug() << "Bytes per millisecond =" << wpms;
  403. unsigned int ts = wpms * periodTime(); // Time stamp
  404. kDebug() << "Time stamp =" << ts;
  405. return ts;
  406. }
  407. void AlsaIO::slotReadyRead(int)
  408. {
  409. //kDebug() << "Data arrived. (Alsa told me !)";
  410. size_t size;
  411. buf.resize(pSizeBytes);
  412. size = snd_pcm_readi(handle, buf.data(), pSize);
  413. buf.resize(snd_pcm_frames_to_bytes(handle, size));
  414. //kDebug() << "Read" << buf.size() << "bytes";
  415. emit readyRead();
  416. }
  417. void AlsaIO::slotReadyWrite(int)
  418. {
  419. //kDebug() << "started since" << (times * periodTime()) / 1000 << "sec.";
  420. //times++;
  421. unsigned short revents;
  422. poll(ufds, fdCount, -1);
  423. snd_pcm_poll_descriptors_revents(handle, ufds, fdCount, &revents);
  424. if (revents & POLLOUT)
  425. writeData();
  426. else
  427. {
  428. notifier->setEnabled(false);
  429. kDebug() << "poll returned no event (" << revents << ", " << ufds[0].revents << ") ?";
  430. }
  431. }
  432. void AlsaIO::writeData()
  433. {
  434. if (buf.size() < pSizeBytes)
  435. {
  436. notifier->setEnabled(false);
  437. return;
  438. }
  439. //Write pSizeBytes from the buffer and remove it from the buffer.
  440. int size = snd_pcm_writei(handle, buf.left(pSizeBytes), snd_pcm_bytes_to_frames(handle, pSizeBytes));
  441. buf = buf.remove(0, pSizeBytes);
  442. if (size < 0)
  443. {
  444. if (size == -EPIPE)
  445. {
  446. kDebug() << "buffer underrun";
  447. prepare();
  448. return;
  449. }
  450. kDebug() << "An error occurred while writing data on the device. (" << snd_strerror(size) << ")";
  451. }
  452. }
  453. bool AlsaIO::prepare()
  454. {
  455. int err;
  456. kDebug() << "prepare()";
  457. if ((err = snd_pcm_prepare(handle)) < 0)
  458. {
  459. kDebug() << "cannot prepare audio interface for use" ;
  460. return false;
  461. }
  462. return true;
  463. }
  464. void AlsaIO::setFormat(Format f)
  465. {
  466. snd_pcm_format_t format = static_cast<snd_pcm_format_t>(f);
  467. if (snd_pcm_hw_params_set_format(handle, hwParams, format) < 0)
  468. {
  469. kDebug() << "cannot set sample format";
  470. return;
  471. }
  472. m_format = f;
  473. }
  474. int AlsaIO::frameSizeBytes()
  475. {
  476. return pSizeBytes;
  477. }