/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define THIS_FILE "app_main.cpp" #define CLOCK_RATE 8000 #define CHANNEL_COUNT 1 #define PTIME 20 #define SAMPLES_PER_FRAME (CLOCK_RATE*PTIME/1000) #define BITS_PER_SAMPLE 16 extern CConsoleBase* console; static pj_caching_pool cp; static pjmedia_aud_stream *strm; static unsigned rec_cnt, play_cnt; static pj_time_val t_start; static pjmedia_aud_param param; static pj_pool_t *pool; static pjmedia_delay_buf *delaybuf; static char frame_buf[256]; static void copy_frame_ext(pjmedia_frame_ext *f_dst, const pjmedia_frame_ext *f_src) { pj_bzero(f_dst, sizeof(*f_dst)); if (f_src->subframe_cnt) { f_dst->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; for (unsigned i = 0; i < f_src->subframe_cnt; ++i) { pjmedia_frame_ext_subframe *sf; sf = pjmedia_frame_ext_get_subframe(f_src, i); pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen, param.samples_per_frame); } } else { f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE; } } /* Logging callback */ static void log_writer(int level, const char *buf, unsigned len) { static wchar_t buf16[PJ_LOG_MAX_SIZE]; PJ_UNUSED_ARG(level); pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16)); TPtrC16 aBuf((const TUint16*)buf16, (TInt)len); console->Write(aBuf); } /* perror util */ static void app_perror(const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, "Error: %s: %s", title, errmsg)); } /* Application init */ static pj_status_t app_init() { unsigned i, count; pj_status_t status; /* Redirect log */ pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer); pj_log_set_decor(PJ_LOG_HAS_NEWLINE); pj_log_set_level(3); /* Init pjlib */ status = pj_init(); if (status != PJ_SUCCESS) { app_perror("pj_init()", status); return status; } pj_caching_pool_init(&cp, NULL, 0); /* Init sound subsystem */ status = pjmedia_aud_subsys_init(&cp.factory); if (status != PJ_SUCCESS) { app_perror("pjmedia_snd_init()", status); pj_caching_pool_destroy(&cp); pj_shutdown(); return status; } count = pjmedia_aud_dev_count(); PJ_LOG(3,(THIS_FILE, "Device count: %d", count)); for (i=0; ibuf); if (frame->size != SAMPLES_PER_FRAME*2) { PJ_LOG(3, (THIS_FILE, "Size captured = %u", frame->size)); } } else { pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frame; pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame_buf; copy_frame_ext(f_dst, f_src); } ++rec_cnt; return PJ_SUCCESS; } /* Play cb */ static pj_status_t play_cb(void *user_data, pjmedia_frame *frame) { PJ_UNUSED_ARG(user_data); if (param.ext_fmt.id == PJMEDIA_FORMAT_PCM) { pjmedia_delay_buf_get(delaybuf, (pj_int16_t*)frame->buf); frame->size = SAMPLES_PER_FRAME*2; frame->type = PJMEDIA_FRAME_TYPE_AUDIO; } else { pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frame_buf; pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame; copy_frame_ext(f_dst, f_src); } ++play_cnt; return PJ_SUCCESS; } /* Start sound */ static pj_status_t snd_start(unsigned flag) { pj_status_t status; if (strm != NULL) { app_perror("snd already open", PJ_EINVALIDOP); return PJ_EINVALIDOP; } pjmedia_aud_dev_default_param(0, ¶m); param.channel_count = CHANNEL_COUNT; param.clock_rate = CLOCK_RATE; param.samples_per_frame = SAMPLES_PER_FRAME; param.dir = (pjmedia_dir) flag; param.ext_fmt.id = PJMEDIA_FORMAT_AMR; param.ext_fmt.bitrate = 12200; param.output_route = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER; status = pjmedia_aud_stream_create(¶m, &rec_cb, &play_cb, NULL, &strm); if (status != PJ_SUCCESS) { app_perror("snd open", status); return status; } rec_cnt = play_cnt = 0; pj_gettimeofday(&t_start); pjmedia_delay_buf_reset(delaybuf); status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { app_perror("snd start", status); pjmedia_aud_stream_destroy(strm); strm = NULL; return status; } return PJ_SUCCESS; } /* Stop sound */ static pj_status_t snd_stop() { pj_time_val now; pj_status_t status; if (strm == NULL) { app_perror("snd not open", PJ_EINVALIDOP); return PJ_EINVALIDOP; } status = pjmedia_aud_stream_stop(strm); if (status != PJ_SUCCESS) { app_perror("snd failed to stop", status); } status = pjmedia_aud_stream_destroy(strm); strm = NULL; pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, t_start); PJ_LOG(3,(THIS_FILE, "Duration: %d.%03d", now.sec, now.msec)); PJ_LOG(3,(THIS_FILE, "Captured: %d", rec_cnt)); PJ_LOG(3,(THIS_FILE, "Played: %d", play_cnt)); return status; } /* Shutdown application */ static void app_fini() { if (strm) snd_stop(); pjmedia_aud_subsys_shutdown(); pjmedia_delay_buf_destroy(delaybuf); pj_pool_release(pool); pj_caching_pool_destroy(&cp); pj_shutdown(); } //////////////////////////////////////////////////////////////////////////// /* * The interractive console UI */ #include class ConsoleUI : public CActive { public: ConsoleUI(CConsoleBase *con); // Run console UI void Run(); // Stop void Stop(); protected: // Cancel asynchronous read. void DoCancel(); // Implementation: called when read has completed. void RunL(); private: CConsoleBase *con_; }; ConsoleUI::ConsoleUI(CConsoleBase *con) : CActive(EPriorityUserInput), con_(con) { CActiveScheduler::Add(this); } // Run console UI void ConsoleUI::Run() { con_->Read(iStatus); SetActive(); } // Stop console UI void ConsoleUI::Stop() { DoCancel(); } // Cancel asynchronous read. void ConsoleUI::DoCancel() { con_->ReadCancel(); } static void PrintMenu() { PJ_LOG(3, (THIS_FILE, "\n\n" "Menu:\n" " a Start bidir sound\n" " t Start recorder\n" " p Start player\n" " d Stop & close sound\n" " w Quit\n")); } // Implementation: called when read has completed. void ConsoleUI::RunL() { TKeyCode kc = con_->KeyCode(); pj_bool_t reschedule = PJ_TRUE; switch (kc) { case 'w': snd_stop(); CActiveScheduler::Stop(); reschedule = PJ_FALSE; break; case 'a': snd_start(PJMEDIA_DIR_CAPTURE_PLAYBACK); break; case 't': snd_start(PJMEDIA_DIR_CAPTURE); break; case 'p': snd_start(PJMEDIA_DIR_PLAYBACK); break; case 'd': snd_stop(); break; default: PJ_LOG(3,(THIS_FILE, "Keycode '%c' (%d) is pressed", kc, kc)); break; } PrintMenu(); if (reschedule) Run(); } //////////////////////////////////////////////////////////////////////////// int app_main() { if (app_init() != PJ_SUCCESS) return -1; // Run the UI ConsoleUI *con = new ConsoleUI(console); con->Run(); PrintMenu(); CActiveScheduler::Start(); delete con; app_fini(); return 0; }