/* $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 #define THIS_FILE "auddemo.c" #define MAX_DEVICES 64 #define WAV_FILE "auddemo.wav" static unsigned dev_count; static unsigned playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; static unsigned capture_lat = PJMEDIA_SND_DEFAULT_REC_LATENCY; static void app_perror(const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); printf( "%s: %s (err=%d)\n", title, errmsg, status); } static void list_devices(void) { unsigned i; pj_status_t status; dev_count = pjmedia_aud_dev_count(); if (dev_count == 0) { PJ_LOG(3,(THIS_FILE, "No devices found")); return; } PJ_LOG(3,(THIS_FILE, "Found %d devices:", dev_count)); for (i=0; i= dev_count) { PJ_LOG(1,(THIS_FILE, "Error: invalid index %u", index)); return; } status = pjmedia_aud_dev_get_info(index, &info); if (status != PJ_SUCCESS) { app_perror("pjmedia_aud_dev_get_info() error", status); return; } PJ_LOG(3, (THIS_FILE, "Device at index %u:", index)); PJ_LOG(3, (THIS_FILE, "-------------------------")); PJ_LOG(3, (THIS_FILE, H": %u (0x%x)", "ID", index, index)); PJ_LOG(3, (THIS_FILE, H": %s", "Name", info.name)); PJ_LOG(3, (THIS_FILE, H": %s", "Driver", info.driver)); PJ_LOG(3, (THIS_FILE, H": %u", "Input channels", info.input_count)); PJ_LOG(3, (THIS_FILE, H": %u", "Output channels", info.output_count)); PJ_LOG(3, (THIS_FILE, H": %s", "Capabilities", decode_caps(info.caps))); formats[0] = '\0'; if (info.caps & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) { unsigned i; for (i=0; i=0 ? "faster" : "slower"; unsigned drift = result.rec_drift_per_sec>=0 ? result.rec_drift_per_sec : -result.rec_drift_per_sec; PJ_LOG(3,(THIS_FILE, " Clock drifts detected. Capture device " "is running %d samples per second %s " "than the playback device", drift, which)); } } } static pj_status_t wav_rec_cb(void *user_data, pjmedia_frame *frame) { return pjmedia_port_put_frame((pjmedia_port*)user_data, frame); } static void record(unsigned rec_index, const char *filename) { pj_pool_t *pool = NULL; pjmedia_port *wav = NULL; pjmedia_aud_param param; pjmedia_aud_stream *strm = NULL; char line[10], *dummy; pj_status_t status; if (filename == NULL) filename = WAV_FILE; pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav", 1000, 1000, NULL); status = pjmedia_wav_writer_port_create(pool, filename, 16000, 1, 320, 16, 0, 0, &wav); if (status != PJ_SUCCESS) { app_perror("Error creating WAV file", status); goto on_return; } status = pjmedia_aud_dev_default_param(rec_index, ¶m); if (status != PJ_SUCCESS) { app_perror("pjmedia_aud_dev_default_param()", status); goto on_return; } param.dir = PJMEDIA_DIR_CAPTURE; param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info); param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info); param.channel_count = PJMEDIA_PIA_CCNT(&wav->info); param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info); status = pjmedia_aud_stream_create(¶m, &wav_rec_cb, NULL, wav, &strm); if (status != PJ_SUCCESS) { app_perror("Error opening the sound device", status); goto on_return; } status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { app_perror("Error starting the sound device", status); goto on_return; } PJ_LOG(3,(THIS_FILE, "Recording started, press ENTER to stop")); dummy = fgets(line, sizeof(line), stdin); PJ_UNUSED_ARG(dummy); on_return: if (strm) { pjmedia_aud_stream_stop(strm); pjmedia_aud_stream_destroy(strm); } if (wav) pjmedia_port_destroy(wav); if (pool) pj_pool_release(pool); } static pj_status_t wav_play_cb(void *user_data, pjmedia_frame *frame) { return pjmedia_port_get_frame((pjmedia_port*)user_data, frame); } static void play_file(unsigned play_index, const char *filename) { pj_pool_t *pool = NULL; pjmedia_port *wav = NULL; pjmedia_aud_param param; pjmedia_aud_stream *strm = NULL; char line[10], *dummy; pj_status_t status; if (filename == NULL) filename = WAV_FILE; pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav", 1000, 1000, NULL); status = pjmedia_wav_player_port_create(pool, filename, 20, 0, 0, &wav); if (status != PJ_SUCCESS) { app_perror("Error opening WAV file", status); goto on_return; } status = pjmedia_aud_dev_default_param(play_index, ¶m); if (status != PJ_SUCCESS) { app_perror("pjmedia_aud_dev_default_param()", status); goto on_return; } param.dir = PJMEDIA_DIR_PLAYBACK; param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info); param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info); param.channel_count = PJMEDIA_PIA_CCNT(&wav->info); param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info); status = pjmedia_aud_stream_create(¶m, NULL, &wav_play_cb, wav, &strm); if (status != PJ_SUCCESS) { app_perror("Error opening the sound device", status); goto on_return; } status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { app_perror("Error starting the sound device", status); goto on_return; } PJ_LOG(3,(THIS_FILE, "Playback started, press ENTER to stop")); dummy = fgets(line, sizeof(line), stdin); PJ_UNUSED_ARG(dummy); on_return: if (strm) { pjmedia_aud_stream_stop(strm); pjmedia_aud_stream_destroy(strm); } if (wav) pjmedia_port_destroy(wav); if (pool) pj_pool_release(pool); } static void print_menu(void) { puts(""); puts("Audio demo menu:"); puts("-------------------------------"); puts(" l List devices"); puts(" R Refresh devices"); puts(" i ID Show device info for device ID"); puts(" t RID PID CR PTIM [CH] Perform test on the device:"); puts(" RID: record device ID (-1 for no)"); puts(" PID: playback device ID (-1 for no)"); puts(" CR: clock rate"); puts(" PTIM: ptime in ms"); puts(" CH: # of channels"); puts(" r RID [FILE] Record capture device RID to WAV file"); puts(" p PID [FILE] Playback WAV file to device ID PID"); puts(" d [RLAT PLAT] Get/set sound device latencies (in ms):"); puts(" Specify no param to get current latencies setting"); puts(" RLAT: record latency (-1 for default)"); puts(" PLAT: playback latency (-1 for default)"); puts(" v Toggle log verbosity"); puts(" q Quit"); puts(""); printf("Enter selection: "); fflush(stdout); } int main() { pj_caching_pool cp; pj_bool_t done = PJ_FALSE; pj_status_t status; /* Init pjlib */ status = pj_init(); PJ_ASSERT_RETURN(status==PJ_SUCCESS, 1); pj_log_set_decor(PJ_LOG_HAS_NEWLINE); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); status = pjmedia_aud_subsys_init(&cp.factory); if (status != PJ_SUCCESS) { app_perror("pjmedia_aud_subsys_init()", status); pj_caching_pool_destroy(&cp); pj_shutdown(); return 1; } list_devices(); while (!done) { char line[80]; print_menu(); if (fgets(line, sizeof(line), stdin)==NULL) break; switch (line[0]) { case 'l': list_devices(); break; case 'R': pjmedia_aud_dev_refresh(); puts("Audio device list refreshed."); break; case 'i': { unsigned dev_index; if (sscanf(line+2, "%u", &dev_index) != 1) { puts("error: device ID required"); break; } show_dev_info(dev_index); } break; case 't': { pjmedia_dir dir; int rec_id, play_id; unsigned clock_rate, ptime, chnum; int cnt; cnt = sscanf(line+2, "%d %d %u %u %u", &rec_id, &play_id, &clock_rate, &ptime, &chnum); if (cnt < 4) { puts("error: not enough parameters"); break; } if (clock_rate < 8000 || clock_rate > 128000) { puts("error: invalid clock rate"); break; } if (ptime < 10 || ptime > 500) { puts("error: invalid ptime"); break; } if (cnt==5) { if (chnum < 1 || chnum > 4) { puts("error: invalid number of channels"); break; } } else { chnum = 1; } if (rec_id >= 0 && rec_id < (int)dev_count) { if (play_id >= 0 && play_id < (int)dev_count) dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; else dir = PJMEDIA_DIR_CAPTURE; } else if (play_id >= 0 && play_id < (int)dev_count) { dir = PJMEDIA_DIR_PLAYBACK; } else { puts("error: at least one valid device index required"); break; } test_device(dir, rec_id, play_id, clock_rate, ptime, chnum); } break; case 'r': /* record */ { int index; char filename[80]; int count; count = sscanf(line+2, "%d %s", &index, filename); if (count==1) record(index, NULL); else if (count==2) record(index, filename); else puts("error: invalid command syntax"); } break; case 'p': /* playback */ { int index; char filename[80]; int count; count = sscanf(line+2, "%d %s", &index, filename); if (count==1) play_file(index, NULL); else if (count==2) play_file(index, filename); else puts("error: invalid command syntax"); } break; case 'd': /* latencies */ { int rec_lat, play_lat; if (sscanf(line+2, "%d %d", &rec_lat, &play_lat) == 2) { capture_lat = (unsigned) (rec_lat>=0? rec_lat:PJMEDIA_SND_DEFAULT_REC_LATENCY); playback_lat = (unsigned) (play_lat >= 0? play_lat : PJMEDIA_SND_DEFAULT_PLAY_LATENCY); printf("Recording latency=%ums, playback latency=%ums", capture_lat, playback_lat); } else { printf("Current latencies: record=%ums, playback=%ums", capture_lat, playback_lat); } puts(""); } break; case 'v': if (pj_log_get_level() <= 3) { pj_log_set_level(5); puts("Logging set to detail"); } else { pj_log_set_level(3); puts("Logging set to quiet"); } break; case 'q': done = PJ_TRUE; break; } } pj_caching_pool_destroy(&cp); pj_shutdown(); return 0; }