PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/ptlib-2.10.2/src/ptlib/unix/vxaudio.cxx

#
C++ | 609 lines | 410 code | 161 blank | 38 comment | 81 complexity | d1e612a158cecfb68f5038ae89552b5a MD5 | raw file
  1. #pragma implementation "sound.h"
  2. #include <ptlib.h>
  3. #include <time.h>
  4. class SoundHandleEntry : public PObject {
  5. PCLASSINFO(SoundHandleEntry, PObject)
  6. public:
  7. SoundHandleEntry();
  8. int handle;
  9. int direction;
  10. unsigned numChannels;
  11. unsigned sampleRate;
  12. unsigned bitsPerSample;
  13. unsigned fragmentValue;
  14. PBoolean isInitialised;
  15. };
  16. PDICTIONARY(SoundHandleDict, PString, SoundHandleEntry);
  17. PMutex PSoundChannel::dictMutex;
  18. static SoundHandleDict & handleDict()
  19. {
  20. static SoundHandleDict dict;
  21. return dict;
  22. }
  23. #define LOOPBACK_BUFFER_SIZE 5000
  24. #define BYTESINBUF ((startptr<endptr)?(endptr-startptr):(LOOPBACK_BUFFER_SIZE+endptr-startptr))
  25. char soundbuffer[LOOPBACK_BUFFER_SIZE];
  26. int startptr, endptr;
  27. PSound::PSound(unsigned channels,
  28. unsigned samplesPerSecond,
  29. unsigned bitsPerSample,
  30. PINDEX bufferSize,
  31. const BYTE * buffer)
  32. {
  33. encoding = 0;
  34. numChannels = channels;
  35. sampleRate = samplesPerSecond;
  36. sampleSize = bitsPerSample;
  37. SetSize(bufferSize);
  38. if (buffer != NULL)
  39. memcpy(GetPointer(), buffer, bufferSize);
  40. }
  41. PSound::PSound(const PFilePath & filename)
  42. {
  43. encoding = 0;
  44. numChannels = 1;
  45. sampleRate = 8000;
  46. sampleSize = 16;
  47. Load(filename);
  48. }
  49. PSound & PSound::operator=(const PBYTEArray & data)
  50. {
  51. PBYTEArray::operator=(data);
  52. return *this;
  53. }
  54. void PSound::SetFormat(unsigned channels,
  55. unsigned samplesPerSecond,
  56. unsigned bitsPerSample)
  57. {
  58. encoding = 0;
  59. numChannels = channels;
  60. sampleRate = samplesPerSecond;
  61. sampleSize = bitsPerSample;
  62. formatInfo.SetSize(0);
  63. }
  64. PBoolean PSound::Load(const PFilePath & /*filename*/)
  65. {
  66. return PFalse;
  67. }
  68. PBoolean PSound::Save(const PFilePath & /*filename*/)
  69. {
  70. return PFalse;
  71. }
  72. ///////////////////////////////////////////////////////////////////////////////
  73. SoundHandleEntry::SoundHandleEntry()
  74. {
  75. handle = -1;
  76. direction = 0;
  77. }
  78. ///////////////////////////////////////////////////////////////////////////////
  79. PSoundChannel::PSoundChannel()
  80. {
  81. Construct();
  82. }
  83. PSoundChannel::PSoundChannel(const PString & device,
  84. Directions dir,
  85. unsigned numChannels,
  86. unsigned sampleRate,
  87. unsigned bitsPerSample)
  88. {
  89. Construct();
  90. Open(device, dir, numChannels, sampleRate, bitsPerSample);
  91. }
  92. void PSoundChannel::Construct()
  93. {
  94. os_handle = -1;
  95. }
  96. PSoundChannel::~PSoundChannel()
  97. {
  98. Close();
  99. }
  100. PStringArray PSoundChannel::GetDeviceNames(Directions /*dir*/)
  101. {
  102. static const char * const devices[] = {
  103. "loopback"
  104. };
  105. return PStringArray(PARRAYSIZE(devices), devices);
  106. }
  107. PString PSoundChannel::GetDefaultDevice(Directions /*dir*/)
  108. {
  109. return "loopback";
  110. }
  111. PBoolean PSoundChannel::Open(const PString & _device,
  112. Directions _dir,
  113. unsigned _numChannels,
  114. unsigned _sampleRate,
  115. unsigned _bitsPerSample)
  116. {
  117. Close();
  118. // lock the dictionary
  119. dictMutex.Wait();
  120. // make the direction value 1 or 2
  121. int dir = _dir + 1;
  122. // if this device in in the dictionary
  123. if (handleDict().Contains(_device)) {
  124. SoundHandleEntry & entry = handleDict()[_device];
  125. // see if the sound channel is already open in this direction
  126. if ((entry.direction & dir) != 0) {
  127. dictMutex.Signal();
  128. return PFalse;
  129. }
  130. // flag this entry as open in this direction
  131. entry.direction |= dir;
  132. os_handle = entry.handle;
  133. } else {
  134. // this is the first time this device has been used
  135. // open the device in read/write mode always
  136. if (_device == "loopback") {
  137. startptr = endptr = 0;
  138. os_handle = 0; // Use os_handle value 0 to indicate loopback, cannot ever be stdin!
  139. }
  140. else {
  141. PAssertAlways(PUnimplementedFunction);
  142. return PFalse;
  143. }
  144. // add the device to the dictionary
  145. SoundHandleEntry * entry = PNEW SoundHandleEntry;
  146. handleDict().SetAt(_device, entry);
  147. // save the information into the dictionary entry
  148. entry->handle = os_handle;
  149. entry->direction = dir;
  150. entry->numChannels = _numChannels;
  151. entry->sampleRate = _sampleRate;
  152. entry->bitsPerSample = _bitsPerSample;
  153. entry->isInitialised = PFalse;
  154. entry->fragmentValue = 0x7fff0008;
  155. }
  156. // unlock the dictionary
  157. dictMutex.Signal();
  158. // save the direction and device
  159. direction = _dir;
  160. device = _device;
  161. isInitialised = PFalse;
  162. return PTrue;
  163. }
  164. PBoolean PSoundChannel::Setup()
  165. {
  166. if (os_handle < 0)
  167. return PFalse;
  168. if (isInitialised)
  169. return PTrue;
  170. // lock the dictionary
  171. dictMutex.Wait();
  172. // the device must always be in the dictionary
  173. PAssertOS(handleDict().Contains(device));
  174. // get record for the device
  175. SoundHandleEntry & entry = handleDict()[device];
  176. PBoolean stat = PFalse;
  177. if (entry.isInitialised) {
  178. isInitialised = PTrue;
  179. stat = PTrue;
  180. } else if (device == "loopback")
  181. stat = PTrue;
  182. else {
  183. PAssertAlways(PUnimplementedFunction);
  184. }
  185. entry.isInitialised = PTrue;
  186. isInitialised = PTrue;
  187. dictMutex.Signal();
  188. return stat;
  189. }
  190. PBoolean PSoundChannel::Close()
  191. {
  192. // if the channel isn't open, do nothing
  193. if (os_handle < 0)
  194. return PTrue;
  195. if (os_handle == 0) {
  196. os_handle = -1;
  197. return PTrue;
  198. }
  199. // the device must be in the dictionary
  200. dictMutex.Wait();
  201. SoundHandleEntry * entry;
  202. PAssert((entry = handleDict().GetAt(device)) != NULL, "Unknown sound device \"" + device + "\" found");
  203. // modify the directions bit mask in the dictionary
  204. entry->direction ^= (direction+1);
  205. // if this is the last usage of this entry, then remove it
  206. if (entry->direction == 0) {
  207. handleDict().RemoveAt(device);
  208. dictMutex.Signal();
  209. return PChannel::Close();
  210. }
  211. // flag this channel as closed
  212. dictMutex.Signal();
  213. os_handle = -1;
  214. return PTrue;
  215. }
  216. PBoolean PSoundChannel::Write(const void * buf, PINDEX len)
  217. {
  218. static struct timespec ts = {0, 5000000};
  219. if (!Setup()) {
  220. return PFalse;
  221. }
  222. if (os_handle > 0) {
  223. while (!ConvertOSError(::write(os_handle, (char *)buf, len)))
  224. if (GetErrorCode() != Interrupted) {
  225. return PFalse;
  226. }
  227. return PTrue;
  228. }
  229. int index = 0;
  230. while (len > 0) {
  231. len--;
  232. soundbuffer[endptr++] = ((char *)buf)[index++];
  233. if (endptr == LOOPBACK_BUFFER_SIZE)
  234. endptr = 0;
  235. while (((startptr - 1) == endptr) || ((endptr==LOOPBACK_BUFFER_SIZE - 1) && (startptr==0))) {
  236. nanosleep(&ts, 0);
  237. }
  238. }
  239. return PTrue;
  240. }
  241. PBoolean PSoundChannel::Read(void * buf, PINDEX len)
  242. {
  243. static struct timespec ts = {0, 5000000};
  244. if (!Setup())
  245. return PFalse;
  246. if (os_handle > 0) {
  247. while (!ConvertOSError(::read(os_handle, (char *)buf, len)))
  248. if (GetErrorCode() != Interrupted)
  249. return PFalse;
  250. return PTrue;
  251. }
  252. int index = 0;
  253. while (len > 0) {
  254. int i = 0;
  255. while ((startptr == endptr) && (i < 30)){ // int i is a very dirty hack to get the
  256. // call-termination to work. Transmit thread
  257. // will otherwise not end! (channels.cxx line 706
  258. // while() will get stuck. This should be fixed.
  259. i++;
  260. nanosleep(&ts, 0);
  261. }
  262. len--;
  263. ((char *)buf)[index++]=soundbuffer[startptr++];
  264. if (startptr == LOOPBACK_BUFFER_SIZE)
  265. startptr = 0;
  266. }
  267. return PTrue;
  268. }
  269. PBoolean PSoundChannel::SetFormat(unsigned numChannels,
  270. unsigned sampleRate,
  271. unsigned bitsPerSample)
  272. {
  273. if (os_handle < 0) {
  274. return SetErrorValues(NotOpen, EBADF);
  275. }
  276. // check parameters
  277. PAssert((bitsPerSample == 8) || (bitsPerSample == 16), PInvalidParameter);
  278. PAssert(numChannels >= 1 && numChannels <= 2, PInvalidParameter);
  279. Abort();
  280. // lock the dictionary
  281. dictMutex.Wait();
  282. // the device must always be in the dictionary
  283. PAssertOS(handleDict().Contains(device));
  284. // get record for the device
  285. SoundHandleEntry & entry = handleDict()[device];
  286. entry.numChannels = numChannels;
  287. entry.sampleRate = sampleRate;
  288. entry.bitsPerSample = bitsPerSample;
  289. entry.isInitialised = PFalse;
  290. // unlock dictionary
  291. dictMutex.Signal();
  292. // mark this channel as uninitialised
  293. isInitialised = PFalse;
  294. return PTrue;
  295. }
  296. PBoolean PSoundChannel::SetBuffers(PINDEX size, PINDEX count)
  297. {
  298. if (os_handle < 0) {
  299. return SetErrorValues(NotOpen, EBADF);
  300. }
  301. Abort();
  302. PAssert(size > 0 && count > 0 && count < 65536, PInvalidParameter);
  303. int arg = 1;
  304. while (size > (PINDEX)(1 << arg))
  305. arg++;
  306. arg |= count << 16;
  307. // lock the dictionary
  308. dictMutex.Wait();
  309. // the device must always be in the dictionary
  310. PAssertOS(handleDict().Contains(device));
  311. // get record for the device
  312. SoundHandleEntry & entry = handleDict()[device];
  313. // set information in the common record
  314. entry.fragmentValue = arg;
  315. entry.isInitialised = PFalse;
  316. // flag this channel as not initialised
  317. isInitialised = PFalse;
  318. dictMutex.Signal();
  319. return PTrue;
  320. }
  321. PBoolean PSoundChannel::GetBuffers(PINDEX & size, PINDEX & count)
  322. {
  323. if (os_handle < 0) {
  324. return SetErrorValues(NotOpen, EBADF);
  325. }
  326. // lock the dictionary
  327. dictMutex.Wait();
  328. // the device must always be in the dictionary
  329. PAssertOS(handleDict().Contains(device));
  330. SoundHandleEntry & entry = handleDict()[device];
  331. int arg = entry.fragmentValue;
  332. dictMutex.Signal();
  333. count = arg >> 16;
  334. size = 1 << (arg&0xffff);
  335. return PTrue;
  336. }
  337. PBoolean PSoundChannel::PlaySound(const PSound & sound, PBoolean wait)
  338. {
  339. if (os_handle < 0) {
  340. return SetErrorValues(NotOpen, EBADF);
  341. }
  342. Abort();
  343. if (!Write((const BYTE *)sound, sound.GetSize()))
  344. return PFalse;
  345. if (wait)
  346. return WaitForPlayCompletion();
  347. return PTrue;
  348. }
  349. PBoolean PSoundChannel::PlayFile(const PFilePath & filename, PBoolean wait)
  350. {
  351. if (os_handle < 0) {
  352. return SetErrorValues(NotOpen, EBADF);
  353. }
  354. return PFalse;
  355. }
  356. PBoolean PSoundChannel::HasPlayCompleted()
  357. {
  358. if (os_handle < 0) {
  359. return SetErrorValues(NotOpen, EBADF);
  360. }
  361. if (os_handle == 0)
  362. return BYTESINBUF <= 0;
  363. PAssertAlways(PUnimplementedFunction);
  364. return PFalse;
  365. }
  366. PBoolean PSoundChannel::WaitForPlayCompletion()
  367. {
  368. static struct timespec ts = {0, 1000000};
  369. if (os_handle < 0) {
  370. return SetErrorValues(NotOpen, EBADF);
  371. }
  372. if (os_handle == 0) {
  373. while (BYTESINBUF > 0)
  374. nanosleep(&ts, 0);
  375. return PTrue;
  376. }
  377. PAssertAlways(PUnimplementedFunction);
  378. return PFalse;
  379. }
  380. PBoolean PSoundChannel::RecordSound(PSound & sound)
  381. {
  382. if (os_handle < 0) {
  383. return SetErrorValues(NotOpen, EBADF);
  384. }
  385. return PFalse;
  386. }
  387. PBoolean PSoundChannel::RecordFile(const PFilePath & filename)
  388. {
  389. if (os_handle < 0) {
  390. return SetErrorValues(NotOpen, EBADF);
  391. }
  392. return PFalse;
  393. }
  394. PBoolean PSoundChannel::StartRecording()
  395. {
  396. if (os_handle < 0) {
  397. return SetErrorValues(NotOpen, EBADF);
  398. }
  399. if (os_handle == 0)
  400. return PTrue;
  401. fd_set fds;
  402. FD_ZERO(&fds);
  403. FD_SET(os_handle, &fds);
  404. struct timeval timeout;
  405. memset(&timeout, 0, sizeof(timeout));
  406. return ConvertOSError(::select(1, &fds, NULL, NULL, &timeout));
  407. }
  408. PBoolean PSoundChannel::IsRecordBufferFull()
  409. {
  410. if (os_handle < 0) {
  411. return SetErrorValues(NotOpen, EBADF);
  412. }
  413. if (os_handle == 0)
  414. return (BYTESINBUF > 0);
  415. PAssertAlways(PUnimplementedFunction);
  416. return PFalse;
  417. }
  418. PBoolean PSoundChannel::AreAllRecordBuffersFull()
  419. {
  420. if (os_handle < 0) {
  421. return SetErrorValues(NotOpen, EBADF);
  422. }
  423. if (os_handle == 0)
  424. return (BYTESINBUF == LOOPBACK_BUFFER_SIZE);
  425. PAssertAlways(PUnimplementedFunction);
  426. return PFalse;
  427. }
  428. PBoolean PSoundChannel::WaitForRecordBufferFull()
  429. {
  430. if (os_handle < 0) {
  431. return SetErrorValues(NotOpen, EBADF);
  432. }
  433. return PXSetIOBlock(PXReadBlock, readTimeout);
  434. }
  435. PBoolean PSoundChannel::WaitForAllRecordBuffersFull()
  436. {
  437. return PFalse;
  438. }
  439. PBoolean PSoundChannel::Abort()
  440. {
  441. if (os_handle == 0) {
  442. startptr = endptr = 0;
  443. return PTrue;
  444. }
  445. PAssertAlways(PUnimplementedFunction);
  446. return PFalse;
  447. }
  448. // End of file