symb_mda_dev.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195
  1. /*
  2. * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  3. * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. */
  19. #include <pjmedia-audiodev/audiodev_imp.h>
  20. #include <pjmedia-audiodev/errno.h>
  21. #include <pjmedia/alaw_ulaw.h>
  22. #include <pj/assert.h>
  23. #include <pj/log.h>
  24. #include <pj/math.h>
  25. #include <pj/os.h>
  26. #include <pj/string.h>
  27. #if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
  28. /*
  29. * This file provides sound implementation for Symbian Audio Streaming
  30. * device. Application using this sound abstraction must link with:
  31. * - mediaclientaudiostream.lib, and
  32. * - mediaclientaudioinputstream.lib
  33. */
  34. #include <mda/common/audio.h>
  35. #include <mdaaudiooutputstream.h>
  36. #include <mdaaudioinputstream.h>
  37. #define THIS_FILE "symb_mda_dev.c"
  38. #define BITS_PER_SAMPLE 16
  39. #define BYTES_PER_SAMPLE (BITS_PER_SAMPLE/8)
  40. #if 1
  41. # define TRACE_(st) PJ_LOG(3, st)
  42. #else
  43. # define TRACE_(st)
  44. #endif
  45. /* MDA factory */
  46. struct mda_factory
  47. {
  48. pjmedia_aud_dev_factory base;
  49. pj_pool_t *pool;
  50. pj_pool_factory *pf;
  51. pjmedia_aud_dev_info dev_info;
  52. };
  53. /* Forward declaration of internal engine. */
  54. class CPjAudioInputEngine;
  55. class CPjAudioOutputEngine;
  56. /* MDA stream. */
  57. struct mda_stream
  58. {
  59. // Base
  60. pjmedia_aud_stream base; /**< Base class. */
  61. // Pool
  62. pj_pool_t *pool; /**< Memory pool. */
  63. // Common settings.
  64. pjmedia_aud_param param; /**< Stream param. */
  65. // Audio engine
  66. CPjAudioInputEngine *in_engine; /**< Record engine. */
  67. CPjAudioOutputEngine *out_engine; /**< Playback engine. */
  68. };
  69. /* Prototypes */
  70. static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
  71. static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
  72. static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
  73. static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
  74. static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
  75. unsigned index,
  76. pjmedia_aud_dev_info *info);
  77. static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
  78. unsigned index,
  79. pjmedia_aud_param *param);
  80. static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
  81. const pjmedia_aud_param *param,
  82. pjmedia_aud_rec_cb rec_cb,
  83. pjmedia_aud_play_cb play_cb,
  84. void *user_data,
  85. pjmedia_aud_stream **p_aud_strm);
  86. static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
  87. pjmedia_aud_param *param);
  88. static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
  89. pjmedia_aud_dev_cap cap,
  90. void *value);
  91. static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
  92. pjmedia_aud_dev_cap cap,
  93. const void *value);
  94. static pj_status_t stream_start(pjmedia_aud_stream *strm);
  95. static pj_status_t stream_stop(pjmedia_aud_stream *strm);
  96. static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
  97. /* Operations */
  98. static pjmedia_aud_dev_factory_op factory_op =
  99. {
  100. &factory_init,
  101. &factory_destroy,
  102. &factory_get_dev_count,
  103. &factory_get_dev_info,
  104. &factory_default_param,
  105. &factory_create_stream,
  106. &factory_refresh
  107. };
  108. static pjmedia_aud_stream_op stream_op =
  109. {
  110. &stream_get_param,
  111. &stream_get_cap,
  112. &stream_set_cap,
  113. &stream_start,
  114. &stream_stop,
  115. &stream_destroy
  116. };
  117. /*
  118. * Convert clock rate to Symbian's TMdaAudioDataSettings capability.
  119. */
  120. static TInt get_clock_rate_cap(unsigned clock_rate)
  121. {
  122. switch (clock_rate) {
  123. case 8000: return TMdaAudioDataSettings::ESampleRate8000Hz;
  124. case 11025: return TMdaAudioDataSettings::ESampleRate11025Hz;
  125. case 12000: return TMdaAudioDataSettings::ESampleRate12000Hz;
  126. case 16000: return TMdaAudioDataSettings::ESampleRate16000Hz;
  127. case 22050: return TMdaAudioDataSettings::ESampleRate22050Hz;
  128. case 24000: return TMdaAudioDataSettings::ESampleRate24000Hz;
  129. case 32000: return TMdaAudioDataSettings::ESampleRate32000Hz;
  130. case 44100: return TMdaAudioDataSettings::ESampleRate44100Hz;
  131. case 48000: return TMdaAudioDataSettings::ESampleRate48000Hz;
  132. case 64000: return TMdaAudioDataSettings::ESampleRate64000Hz;
  133. case 96000: return TMdaAudioDataSettings::ESampleRate96000Hz;
  134. default:
  135. return 0;
  136. }
  137. }
  138. /*
  139. * Convert number of channels into Symbian's TMdaAudioDataSettings capability.
  140. */
  141. static TInt get_channel_cap(unsigned channel_count)
  142. {
  143. switch (channel_count) {
  144. case 1: return TMdaAudioDataSettings::EChannelsMono;
  145. case 2: return TMdaAudioDataSettings::EChannelsStereo;
  146. default:
  147. return 0;
  148. }
  149. }
  150. /*
  151. * Utility: print sound device error
  152. */
  153. static void snd_perror(const char *title, TInt rc)
  154. {
  155. PJ_LOG(1,(THIS_FILE, "%s: error code %d", title, rc));
  156. }
  157. //////////////////////////////////////////////////////////////////////////////
  158. //
  159. /*
  160. * Implementation: Symbian Input Stream.
  161. */
  162. class CPjAudioInputEngine : public CBase, MMdaAudioInputStreamCallback
  163. {
  164. public:
  165. enum State
  166. {
  167. STATE_INACTIVE,
  168. STATE_ACTIVE,
  169. };
  170. ~CPjAudioInputEngine();
  171. static CPjAudioInputEngine *NewL(struct mda_stream *parent_strm,
  172. pjmedia_aud_rec_cb rec_cb,
  173. void *user_data);
  174. static CPjAudioInputEngine *NewLC(struct mda_stream *parent_strm,
  175. pjmedia_aud_rec_cb rec_cb,
  176. void *user_data);
  177. pj_status_t StartRecord();
  178. void Stop();
  179. pj_status_t SetGain(TInt gain) {
  180. if (iInputStream_) {
  181. iInputStream_->SetGain(gain);
  182. return PJ_SUCCESS;
  183. } else
  184. return PJ_EINVALIDOP;
  185. }
  186. TInt GetGain() {
  187. if (iInputStream_) {
  188. return iInputStream_->Gain();
  189. } else
  190. return PJ_EINVALIDOP;
  191. }
  192. TInt GetMaxGain() {
  193. if (iInputStream_) {
  194. return iInputStream_->MaxGain();
  195. } else
  196. return PJ_EINVALIDOP;
  197. }
  198. private:
  199. State state_;
  200. struct mda_stream *parentStrm_;
  201. pjmedia_aud_rec_cb recCb_;
  202. void *userData_;
  203. CMdaAudioInputStream *iInputStream_;
  204. HBufC8 *iStreamBuffer_;
  205. TPtr8 iFramePtr_;
  206. TInt lastError_;
  207. pj_uint32_t timeStamp_;
  208. CActiveSchedulerWait startAsw_;
  209. // cache variable
  210. // to avoid calculating frame length repeatedly
  211. TInt frameLen_;
  212. // sometimes recorded size != requested framesize, so let's
  213. // provide a buffer to make sure the rec callback returning
  214. // framesize as requested.
  215. TUint8 *frameRecBuf_;
  216. TInt frameRecBufLen_;
  217. CPjAudioInputEngine(struct mda_stream *parent_strm,
  218. pjmedia_aud_rec_cb rec_cb,
  219. void *user_data);
  220. void ConstructL();
  221. TPtr8 & GetFrame();
  222. public:
  223. virtual void MaiscOpenComplete(TInt aError);
  224. virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer);
  225. virtual void MaiscRecordComplete(TInt aError);
  226. };
  227. CPjAudioInputEngine::CPjAudioInputEngine(struct mda_stream *parent_strm,
  228. pjmedia_aud_rec_cb rec_cb,
  229. void *user_data)
  230. : state_(STATE_INACTIVE), parentStrm_(parent_strm),
  231. recCb_(rec_cb), userData_(user_data),
  232. iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0),
  233. lastError_(KErrNone), timeStamp_(0),
  234. frameLen_(parent_strm->param.samples_per_frame *
  235. BYTES_PER_SAMPLE),
  236. frameRecBuf_(NULL), frameRecBufLen_(0)
  237. {
  238. }
  239. CPjAudioInputEngine::~CPjAudioInputEngine()
  240. {
  241. Stop();
  242. delete iStreamBuffer_;
  243. iStreamBuffer_ = NULL;
  244. delete [] frameRecBuf_;
  245. frameRecBuf_ = NULL;
  246. frameRecBufLen_ = 0;
  247. }
  248. void CPjAudioInputEngine::ConstructL()
  249. {
  250. iStreamBuffer_ = HBufC8::NewL(frameLen_);
  251. CleanupStack::PushL(iStreamBuffer_);
  252. frameRecBuf_ = new TUint8[frameLen_*2];
  253. CleanupStack::PushL(frameRecBuf_);
  254. }
  255. CPjAudioInputEngine *CPjAudioInputEngine::NewLC(struct mda_stream *parent,
  256. pjmedia_aud_rec_cb rec_cb,
  257. void *user_data)
  258. {
  259. CPjAudioInputEngine* self = new (ELeave) CPjAudioInputEngine(parent,
  260. rec_cb,
  261. user_data);
  262. CleanupStack::PushL(self);
  263. self->ConstructL();
  264. return self;
  265. }
  266. CPjAudioInputEngine *CPjAudioInputEngine::NewL(struct mda_stream *parent,
  267. pjmedia_aud_rec_cb rec_cb,
  268. void *user_data)
  269. {
  270. CPjAudioInputEngine *self = NewLC(parent, rec_cb, user_data);
  271. CleanupStack::Pop(self->frameRecBuf_);
  272. CleanupStack::Pop(self->iStreamBuffer_);
  273. CleanupStack::Pop(self);
  274. return self;
  275. }
  276. pj_status_t CPjAudioInputEngine::StartRecord()
  277. {
  278. // Ignore command if recording is in progress.
  279. if (state_ == STATE_ACTIVE)
  280. return PJ_SUCCESS;
  281. // According to Nokia's AudioStream example, some 2nd Edition, FP2 devices
  282. // (such as Nokia 6630) require the stream to be reconstructed each time
  283. // before calling Open() - otherwise the callback never gets called.
  284. // For uniform behavior, lets just delete/re-create the stream for all
  285. // devices.
  286. // Destroy existing stream.
  287. if (iInputStream_) delete iInputStream_;
  288. iInputStream_ = NULL;
  289. // Create the stream.
  290. TRAPD(err, iInputStream_ = CMdaAudioInputStream::NewL(*this));
  291. if (err != KErrNone)
  292. return PJ_RETURN_OS_ERROR(err);
  293. // Initialize settings.
  294. TMdaAudioDataSettings iStreamSettings;
  295. iStreamSettings.iChannels =
  296. get_channel_cap(parentStrm_->param.channel_count);
  297. iStreamSettings.iSampleRate =
  298. get_clock_rate_cap(parentStrm_->param.clock_rate);
  299. pj_assert(iStreamSettings.iChannels != 0 &&
  300. iStreamSettings.iSampleRate != 0);
  301. PJ_LOG(4,(THIS_FILE, "Opening sound device for capture, "
  302. "clock rate=%d, channel count=%d..",
  303. parentStrm_->param.clock_rate,
  304. parentStrm_->param.channel_count));
  305. // Open stream.
  306. lastError_ = KRequestPending;
  307. iInputStream_->Open(&iStreamSettings);
  308. #if defined(PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START) && \
  309. PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START != 0
  310. startAsw_.Start();
  311. #endif
  312. // Success
  313. PJ_LOG(4,(THIS_FILE, "Sound capture started."));
  314. return PJ_SUCCESS;
  315. }
  316. void CPjAudioInputEngine::Stop()
  317. {
  318. // If capture is in progress, stop it.
  319. if (iInputStream_ && state_ == STATE_ACTIVE) {
  320. lastError_ = KRequestPending;
  321. iInputStream_->Stop();
  322. // Wait until it's actually stopped
  323. while (lastError_ == KRequestPending)
  324. pj_symbianos_poll(-1, 100);
  325. }
  326. if (iInputStream_) {
  327. delete iInputStream_;
  328. iInputStream_ = NULL;
  329. }
  330. if (startAsw_.IsStarted()) {
  331. startAsw_.AsyncStop();
  332. }
  333. state_ = STATE_INACTIVE;
  334. }
  335. TPtr8 & CPjAudioInputEngine::GetFrame()
  336. {
  337. //iStreamBuffer_->Des().FillZ(frameLen_);
  338. iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), frameLen_, frameLen_);
  339. return iFramePtr_;
  340. }
  341. void CPjAudioInputEngine::MaiscOpenComplete(TInt aError)
  342. {
  343. if (startAsw_.IsStarted()) {
  344. startAsw_.AsyncStop();
  345. }
  346. lastError_ = aError;
  347. if (aError != KErrNone) {
  348. snd_perror("Error in MaiscOpenComplete()", aError);
  349. return;
  350. }
  351. /* Apply input volume setting if specified */
  352. if (parentStrm_->param.flags &
  353. PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING)
  354. {
  355. stream_set_cap(&parentStrm_->base,
  356. PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
  357. &parentStrm_->param.input_vol);
  358. }
  359. // set stream priority to normal and time sensitive
  360. iInputStream_->SetPriority(EPriorityNormal,
  361. EMdaPriorityPreferenceTime);
  362. // Read the first frame.
  363. TPtr8 & frm = GetFrame();
  364. TRAPD(err2, iInputStream_->ReadL(frm));
  365. if (err2) {
  366. PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
  367. lastError_ = err2;
  368. return;
  369. }
  370. // input stream opened succesfully, set status to Active
  371. state_ = STATE_ACTIVE;
  372. }
  373. void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
  374. const TDesC8 &aBuffer)
  375. {
  376. lastError_ = aError;
  377. if (aError != KErrNone) {
  378. snd_perror("Error in MaiscBufferCopied()", aError);
  379. return;
  380. }
  381. if (frameRecBufLen_ || aBuffer.Length() < frameLen_) {
  382. pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Length());
  383. frameRecBufLen_ += aBuffer.Length();
  384. }
  385. if (frameRecBufLen_) {
  386. while (frameRecBufLen_ >= frameLen_) {
  387. pjmedia_frame f;
  388. f.type = PJMEDIA_FRAME_TYPE_AUDIO;
  389. f.buf = frameRecBuf_;
  390. f.size = frameLen_;
  391. f.timestamp.u32.lo = timeStamp_;
  392. f.bit_info = 0;
  393. // Call the callback.
  394. recCb_(userData_, &f);
  395. // Increment timestamp.
  396. timeStamp_ += parentStrm_->param.samples_per_frame;
  397. frameRecBufLen_ -= frameLen_;
  398. pj_memmove(frameRecBuf_, frameRecBuf_+frameLen_, frameRecBufLen_);
  399. }
  400. } else {
  401. pjmedia_frame f;
  402. f.type = PJMEDIA_FRAME_TYPE_AUDIO;
  403. f.buf = (void*)aBuffer.Ptr();
  404. f.size = aBuffer.Length();
  405. f.timestamp.u32.lo = timeStamp_;
  406. f.bit_info = 0;
  407. // Call the callback.
  408. recCb_(userData_, &f);
  409. // Increment timestamp.
  410. timeStamp_ += parentStrm_->param.samples_per_frame;
  411. }
  412. // Record next frame
  413. TPtr8 & frm = GetFrame();
  414. TRAPD(err2, iInputStream_->ReadL(frm));
  415. if (err2) {
  416. PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
  417. }
  418. }
  419. void CPjAudioInputEngine::MaiscRecordComplete(TInt aError)
  420. {
  421. lastError_ = aError;
  422. state_ = STATE_INACTIVE;
  423. if (aError != KErrNone && aError != KErrCancel) {
  424. snd_perror("Error in MaiscRecordComplete()", aError);
  425. }
  426. }
  427. //////////////////////////////////////////////////////////////////////////////
  428. //
  429. /*
  430. * Implementation: Symbian Output Stream.
  431. */
  432. class CPjAudioOutputEngine : public CBase, MMdaAudioOutputStreamCallback
  433. {
  434. public:
  435. enum State
  436. {
  437. STATE_INACTIVE,
  438. STATE_ACTIVE,
  439. };
  440. ~CPjAudioOutputEngine();
  441. static CPjAudioOutputEngine *NewL(struct mda_stream *parent_strm,
  442. pjmedia_aud_play_cb play_cb,
  443. void *user_data);
  444. static CPjAudioOutputEngine *NewLC(struct mda_stream *parent_strm,
  445. pjmedia_aud_play_cb rec_cb,
  446. void *user_data);
  447. pj_status_t StartPlay();
  448. void Stop();
  449. pj_status_t SetVolume(TInt vol) {
  450. if (iOutputStream_) {
  451. iOutputStream_->SetVolume(vol);
  452. return PJ_SUCCESS;
  453. } else
  454. return PJ_EINVALIDOP;
  455. }
  456. TInt GetVolume() {
  457. if (iOutputStream_) {
  458. return iOutputStream_->Volume();
  459. } else
  460. return PJ_EINVALIDOP;
  461. }
  462. TInt GetMaxVolume() {
  463. if (iOutputStream_) {
  464. return iOutputStream_->MaxVolume();
  465. } else
  466. return PJ_EINVALIDOP;
  467. }
  468. private:
  469. State state_;
  470. struct mda_stream *parentStrm_;
  471. pjmedia_aud_play_cb playCb_;
  472. void *userData_;
  473. CMdaAudioOutputStream *iOutputStream_;
  474. TUint8 *frameBuf_;
  475. unsigned frameBufSize_;
  476. TPtrC8 frame_;
  477. TInt lastError_;
  478. unsigned timestamp_;
  479. CActiveSchedulerWait startAsw_;
  480. CPjAudioOutputEngine(struct mda_stream *parent_strm,
  481. pjmedia_aud_play_cb play_cb,
  482. void *user_data);
  483. void ConstructL();
  484. virtual void MaoscOpenComplete(TInt aError);
  485. virtual void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer);
  486. virtual void MaoscPlayComplete(TInt aError);
  487. };
  488. CPjAudioOutputEngine::CPjAudioOutputEngine(struct mda_stream *parent_strm,
  489. pjmedia_aud_play_cb play_cb,
  490. void *user_data)
  491. : state_(STATE_INACTIVE), parentStrm_(parent_strm), playCb_(play_cb),
  492. userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL),
  493. lastError_(KErrNone), timestamp_(0)
  494. {
  495. }
  496. void CPjAudioOutputEngine::ConstructL()
  497. {
  498. frameBufSize_ = parentStrm_->param.samples_per_frame *
  499. BYTES_PER_SAMPLE;
  500. frameBuf_ = new TUint8[frameBufSize_];
  501. }
  502. CPjAudioOutputEngine::~CPjAudioOutputEngine()
  503. {
  504. Stop();
  505. delete [] frameBuf_;
  506. }
  507. CPjAudioOutputEngine *
  508. CPjAudioOutputEngine::NewLC(struct mda_stream *parent_strm,
  509. pjmedia_aud_play_cb play_cb,
  510. void *user_data)
  511. {
  512. CPjAudioOutputEngine* self = new (ELeave) CPjAudioOutputEngine(parent_strm,
  513. play_cb,
  514. user_data);
  515. CleanupStack::PushL(self);
  516. self->ConstructL();
  517. return self;
  518. }
  519. CPjAudioOutputEngine *
  520. CPjAudioOutputEngine::NewL(struct mda_stream *parent_strm,
  521. pjmedia_aud_play_cb play_cb,
  522. void *user_data)
  523. {
  524. CPjAudioOutputEngine *self = NewLC(parent_strm, play_cb, user_data);
  525. CleanupStack::Pop(self);
  526. return self;
  527. }
  528. pj_status_t CPjAudioOutputEngine::StartPlay()
  529. {
  530. // Ignore command if playing is in progress.
  531. if (state_ == STATE_ACTIVE)
  532. return PJ_SUCCESS;
  533. // Destroy existing stream.
  534. if (iOutputStream_) delete iOutputStream_;
  535. iOutputStream_ = NULL;
  536. // Create the stream
  537. TRAPD(err, iOutputStream_ = CMdaAudioOutputStream::NewL(*this));
  538. if (err != KErrNone)
  539. return PJ_RETURN_OS_ERROR(err);
  540. // Initialize settings.
  541. TMdaAudioDataSettings iStreamSettings;
  542. iStreamSettings.iChannels =
  543. get_channel_cap(parentStrm_->param.channel_count);
  544. iStreamSettings.iSampleRate =
  545. get_clock_rate_cap(parentStrm_->param.clock_rate);
  546. pj_assert(iStreamSettings.iChannels != 0 &&
  547. iStreamSettings.iSampleRate != 0);
  548. PJ_LOG(4,(THIS_FILE, "Opening sound device for playback, "
  549. "clock rate=%d, channel count=%d..",
  550. parentStrm_->param.clock_rate,
  551. parentStrm_->param.channel_count));
  552. // Open stream.
  553. lastError_ = KRequestPending;
  554. iOutputStream_->Open(&iStreamSettings);
  555. #if defined(PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START) && \
  556. PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START != 0
  557. startAsw_.Start();
  558. #endif
  559. // Success
  560. PJ_LOG(4,(THIS_FILE, "Sound playback started"));
  561. return PJ_SUCCESS;
  562. }
  563. void CPjAudioOutputEngine::Stop()
  564. {
  565. // Stop stream if it's playing
  566. if (iOutputStream_ && state_ != STATE_INACTIVE) {
  567. lastError_ = KRequestPending;
  568. iOutputStream_->Stop();
  569. // Wait until it's actually stopped
  570. while (lastError_ == KRequestPending)
  571. pj_symbianos_poll(-1, 100);
  572. }
  573. if (iOutputStream_) {
  574. delete iOutputStream_;
  575. iOutputStream_ = NULL;
  576. }
  577. if (startAsw_.IsStarted()) {
  578. startAsw_.AsyncStop();
  579. }
  580. state_ = STATE_INACTIVE;
  581. }
  582. void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError)
  583. {
  584. if (startAsw_.IsStarted()) {
  585. startAsw_.AsyncStop();
  586. }
  587. lastError_ = aError;
  588. if (aError==KErrNone) {
  589. // set stream properties, 16bit 8KHz mono
  590. TMdaAudioDataSettings iSettings;
  591. iSettings.iChannels =
  592. get_channel_cap(parentStrm_->param.channel_count);
  593. iSettings.iSampleRate =
  594. get_clock_rate_cap(parentStrm_->param.clock_rate);
  595. iOutputStream_->SetAudioPropertiesL(iSettings.iSampleRate,
  596. iSettings.iChannels);
  597. /* Apply output volume setting if specified */
  598. if (parentStrm_->param.flags &
  599. PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING)
  600. {
  601. stream_set_cap(&parentStrm_->base,
  602. PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
  603. &parentStrm_->param.output_vol);
  604. } else {
  605. // set volume to 1/2th of stream max volume
  606. iOutputStream_->SetVolume(iOutputStream_->MaxVolume()/2);
  607. }
  608. // set stream priority to normal and time sensitive
  609. iOutputStream_->SetPriority(EPriorityNormal,
  610. EMdaPriorityPreferenceTime);
  611. // Call callback to retrieve frame from upstream.
  612. pjmedia_frame f;
  613. pj_status_t status;
  614. f.type = PJMEDIA_FRAME_TYPE_AUDIO;
  615. f.buf = frameBuf_;
  616. f.size = frameBufSize_;
  617. f.timestamp.u32.lo = timestamp_;
  618. f.bit_info = 0;
  619. status = playCb_(this->userData_, &f);
  620. if (status != PJ_SUCCESS) {
  621. this->Stop();
  622. return;
  623. }
  624. if (f.type != PJMEDIA_FRAME_TYPE_AUDIO)
  625. pj_bzero(frameBuf_, frameBufSize_);
  626. // Increment timestamp.
  627. timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
  628. // issue WriteL() to write the first audio data block,
  629. // subsequent calls to WriteL() will be issued in
  630. // MMdaAudioOutputStreamCallback::MaoscBufferCopied()
  631. // until whole data buffer is written.
  632. frame_.Set(frameBuf_, frameBufSize_);
  633. iOutputStream_->WriteL(frame_);
  634. // output stream opened succesfully, set status to Active
  635. state_ = STATE_ACTIVE;
  636. } else {
  637. snd_perror("Error in MaoscOpenComplete()", aError);
  638. }
  639. }
  640. void CPjAudioOutputEngine::MaoscBufferCopied(TInt aError,
  641. const TDesC8& aBuffer)
  642. {
  643. PJ_UNUSED_ARG(aBuffer);
  644. if (aError==KErrNone) {
  645. // Buffer successfully written, feed another one.
  646. // Call callback to retrieve frame from upstream.
  647. pjmedia_frame f;
  648. pj_status_t status;
  649. f.type = PJMEDIA_FRAME_TYPE_AUDIO;
  650. f.buf = frameBuf_;
  651. f.size = frameBufSize_;
  652. f.timestamp.u32.lo = timestamp_;
  653. f.bit_info = 0;
  654. status = playCb_(this->userData_, &f);
  655. if (status != PJ_SUCCESS) {
  656. this->Stop();
  657. return;
  658. }
  659. if (f.type != PJMEDIA_FRAME_TYPE_AUDIO)
  660. pj_bzero(frameBuf_, frameBufSize_);
  661. // Increment timestamp.
  662. timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
  663. // Write to playback stream.
  664. frame_.Set(frameBuf_, frameBufSize_);
  665. iOutputStream_->WriteL(frame_);
  666. } else if (aError==KErrAbort) {
  667. // playing was aborted, due to call to CMdaAudioOutputStream::Stop()
  668. state_ = STATE_INACTIVE;
  669. } else {
  670. // error writing data to output
  671. lastError_ = aError;
  672. state_ = STATE_INACTIVE;
  673. snd_perror("Error in MaoscBufferCopied()", aError);
  674. }
  675. }
  676. void CPjAudioOutputEngine::MaoscPlayComplete(TInt aError)
  677. {
  678. lastError_ = aError;
  679. state_ = STATE_INACTIVE;
  680. if (aError != KErrNone && aError != KErrCancel) {
  681. snd_perror("Error in MaoscPlayComplete()", aError);
  682. }
  683. }
  684. /****************************************************************************
  685. * Factory operations
  686. */
  687. /*
  688. * C compatible declaration of MDA factory.
  689. */
  690. PJ_BEGIN_DECL
  691. PJ_DECL(pjmedia_aud_dev_factory*) pjmedia_symb_mda_factory(pj_pool_factory *pf);
  692. PJ_END_DECL
  693. /*
  694. * Init Symbian audio driver.
  695. */
  696. pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf)
  697. {
  698. struct mda_factory *f;
  699. pj_pool_t *pool;
  700. pool = pj_pool_create(pf, "symb_aud", 1000, 1000, NULL);
  701. f = PJ_POOL_ZALLOC_T(pool, struct mda_factory);
  702. f->pf = pf;
  703. f->pool = pool;
  704. f->base.op = &factory_op;
  705. return &f->base;
  706. }
  707. /* API: init factory */
  708. static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
  709. {
  710. struct mda_factory *af = (struct mda_factory*)f;
  711. pj_ansi_strcpy(af->dev_info.name, "Symbian Audio");
  712. af->dev_info.default_samples_per_sec = 8000;
  713. af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
  714. PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
  715. af->dev_info.input_count = 1;
  716. af->dev_info.output_count = 1;
  717. PJ_LOG(4, (THIS_FILE, "Symb Mda initialized"));
  718. return PJ_SUCCESS;
  719. }
  720. /* API: destroy factory */
  721. static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
  722. {
  723. struct mda_factory *af = (struct mda_factory*)f;
  724. pj_pool_t *pool = af->pool;
  725. af->pool = NULL;
  726. pj_pool_release(pool);
  727. PJ_LOG(4, (THIS_FILE, "Symbian Mda destroyed"));
  728. return PJ_SUCCESS;
  729. }
  730. /* API: refresh the device list */
  731. static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
  732. {
  733. PJ_UNUSED_ARG(f);
  734. return PJ_ENOTSUP;
  735. }
  736. /* API: get number of devices */
  737. static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
  738. {
  739. PJ_UNUSED_ARG(f);
  740. return 1;
  741. }
  742. /* API: get device info */
  743. static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
  744. unsigned index,
  745. pjmedia_aud_dev_info *info)
  746. {
  747. struct mda_factory *af = (struct mda_factory*)f;
  748. PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
  749. pj_memcpy(info, &af->dev_info, sizeof(*info));
  750. return PJ_SUCCESS;
  751. }
  752. /* API: create default device parameter */
  753. static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
  754. unsigned index,
  755. pjmedia_aud_param *param)
  756. {
  757. struct mda_factory *af = (struct mda_factory*)f;
  758. PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
  759. pj_bzero(param, sizeof(*param));
  760. param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
  761. param->rec_id = index;
  762. param->play_id = index;
  763. param->clock_rate = af->dev_info.default_samples_per_sec;
  764. param->channel_count = 1;
  765. param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
  766. param->bits_per_sample = BITS_PER_SAMPLE;
  767. // Don't set the flags without specifying the flags value.
  768. //param->flags = af->dev_info.caps;
  769. return PJ_SUCCESS;
  770. }
  771. /* API: create stream */
  772. static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
  773. const pjmedia_aud_param *param,
  774. pjmedia_aud_rec_cb rec_cb,
  775. pjmedia_aud_play_cb play_cb,
  776. void *user_data,
  777. pjmedia_aud_stream **p_aud_strm)
  778. {
  779. struct mda_factory *mf = (struct mda_factory*)f;
  780. pj_pool_t *pool;
  781. struct mda_stream *strm;
  782. /* Can only support 16bits per sample raw PCM format. */
  783. PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
  784. PJ_ASSERT_RETURN((param->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT)==0 ||
  785. param->ext_fmt.id == PJMEDIA_FORMAT_L16,
  786. PJ_ENOTSUP);
  787. /* It seems that MDA recorder only supports for mono channel. */
  788. PJ_ASSERT_RETURN(param->channel_count == 1, PJ_EINVAL);
  789. /* Create and Initialize stream descriptor */
  790. pool = pj_pool_create(mf->pf, "symb_aud_dev", 1000, 1000, NULL);
  791. PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
  792. strm = PJ_POOL_ZALLOC_T(pool, struct mda_stream);
  793. strm->pool = pool;
  794. strm->param = *param;
  795. // Create the output stream.
  796. if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
  797. TRAPD(err, strm->out_engine = CPjAudioOutputEngine::NewL(strm, play_cb,
  798. user_data));
  799. if (err != KErrNone) {
  800. pj_pool_release(pool);
  801. return PJ_RETURN_OS_ERROR(err);
  802. }
  803. }
  804. // Create the input stream.
  805. if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
  806. TRAPD(err, strm->in_engine = CPjAudioInputEngine::NewL(strm, rec_cb,
  807. user_data));
  808. if (err != KErrNone) {
  809. strm->in_engine = NULL;
  810. delete strm->out_engine;
  811. strm->out_engine = NULL;
  812. pj_pool_release(pool);
  813. return PJ_RETURN_OS_ERROR(err);
  814. }
  815. }
  816. /* Done */
  817. strm->base.op = &stream_op;
  818. *p_aud_strm = &strm->base;
  819. return PJ_SUCCESS;
  820. }
  821. /* API: Get stream info. */
  822. static pj_status_t stream_get_param(pjmedia_aud_stream *s,
  823. pjmedia_aud_param *pi)
  824. {
  825. struct mda_stream *strm = (struct mda_stream*)s;
  826. PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
  827. pj_memcpy(pi, &strm->param, sizeof(*pi));
  828. /* Update the output volume setting */
  829. if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
  830. &pi->output_vol) == PJ_SUCCESS)
  831. {
  832. pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
  833. }
  834. /* Update the input volume setting */
  835. if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
  836. &pi->input_vol) == PJ_SUCCESS)
  837. {
  838. pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING;
  839. }
  840. return PJ_SUCCESS;
  841. }
  842. /* API: get capability */
  843. static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
  844. pjmedia_aud_dev_cap cap,
  845. void *pval)
  846. {
  847. struct mda_stream *strm = (struct mda_stream*)s;
  848. pj_status_t status = PJ_ENOTSUP;
  849. PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
  850. switch (cap) {
  851. case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
  852. if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
  853. PJ_ASSERT_RETURN(strm->in_engine, PJ_EINVAL);
  854. TInt max_gain = strm->in_engine->GetMaxGain();
  855. TInt gain = strm->in_engine->GetGain();
  856. if (max_gain > 0 && gain >= 0) {
  857. *(unsigned*)pval = gain * 100 / max_gain;
  858. status = PJ_SUCCESS;
  859. } else {
  860. status = PJMEDIA_EAUD_NOTREADY;
  861. }
  862. }
  863. break;
  864. case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
  865. if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
  866. PJ_ASSERT_RETURN(strm->out_engine, PJ_EINVAL);
  867. TInt max_vol = strm->out_engine->GetMaxVolume();
  868. TInt vol = strm->out_engine->GetVolume();
  869. if (max_vol > 0 && vol >= 0) {
  870. *(unsigned*)pval = vol * 100 / max_vol;
  871. status = PJ_SUCCESS;
  872. } else {
  873. status = PJMEDIA_EAUD_NOTREADY;
  874. }
  875. }
  876. break;
  877. default:
  878. break;
  879. }
  880. return status;
  881. }
  882. /* API: set capability */
  883. static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
  884. pjmedia_aud_dev_cap cap,
  885. const void *pval)
  886. {
  887. struct mda_stream *strm = (struct mda_stream*)s;
  888. pj_status_t status = PJ_ENOTSUP;
  889. PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
  890. switch (cap) {
  891. case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
  892. if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
  893. PJ_ASSERT_RETURN(strm->in_engine, PJ_EINVAL);
  894. TInt max_gain = strm->in_engine->GetMaxGain();
  895. if (max_gain > 0) {
  896. TInt gain;
  897. gain = *(unsigned*)pval * max_gain / 100;
  898. status = strm->in_engine->SetGain(gain);
  899. } else {
  900. status = PJMEDIA_EAUD_NOTREADY;
  901. }
  902. }
  903. break;
  904. case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
  905. if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
  906. PJ_ASSERT_RETURN(strm->out_engine, PJ_EINVAL);
  907. TInt max_vol = strm->out_engine->GetMaxVolume();
  908. if (max_vol > 0) {
  909. TInt vol;
  910. vol = *(unsigned*)pval * max_vol / 100;
  911. status = strm->out_engine->SetVolume(vol);
  912. } else {
  913. status = PJMEDIA_EAUD_NOTREADY;
  914. }
  915. }
  916. break;
  917. default:
  918. break;
  919. }
  920. return status;
  921. }
  922. /* API: Start stream. */
  923. static pj_status_t stream_start(pjmedia_aud_stream *strm)
  924. {
  925. struct mda_stream *stream = (struct mda_stream*)strm;
  926. PJ_ASSERT_RETURN(stream, PJ_EINVAL);
  927. if (stream->out_engine) {
  928. pj_status_t status;
  929. status = stream->out_engine->StartPlay();
  930. if (status != PJ_SUCCESS)
  931. return status;
  932. }
  933. if (stream->in_engine) {
  934. pj_status_t status;
  935. status = stream->in_engine->StartRecord();
  936. if (status != PJ_SUCCESS)
  937. return status;
  938. }
  939. return PJ_SUCCESS;
  940. }
  941. /* API: Stop stream. */
  942. static pj_status_t stream_stop(pjmedia_aud_stream *strm)
  943. {
  944. struct mda_stream *stream = (struct mda_stream*)strm;
  945. PJ_ASSERT_RETURN(stream, PJ_EINVAL);
  946. if (stream->in_engine) {
  947. stream->in_engine->Stop();
  948. }
  949. if (stream->out_engine) {
  950. stream->out_engine->Stop();
  951. }
  952. return PJ_SUCCESS;
  953. }
  954. /* API: Destroy stream. */
  955. static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
  956. {
  957. struct mda_stream *stream = (struct mda_stream*)strm;
  958. PJ_ASSERT_RETURN(stream, PJ_EINVAL);
  959. stream_stop(strm);
  960. delete stream->in_engine;
  961. stream->in_engine = NULL;
  962. delete stream->out_engine;
  963. stream->out_engine = NULL;
  964. pj_pool_t *pool;
  965. pool = stream->pool;
  966. if (pool) {
  967. stream->pool = NULL;
  968. pj_pool_release(pool);
  969. }
  970. return PJ_SUCCESS;
  971. }
  972. #endif /* PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA */