Version 0.2.0: CAPI 2.0, ALSA, threading, low-latency.

Changes in short:
  * Re-work of session handling to use sound and ISDN threads, which
    greatly reduces latency and CPU usage
  * Complete re-work of ISDN subsystem to use CAPI 2.0
  * Complete re-work of sound subsystem to use ALSA
  * Re-work of recording subsystem to produce better results
  * Re-work of debug and error message handling (centralized)
  * Added --sleep and --wakeup remote commands to facilitate release
    and re-acquire of CAPI connection
  * Code documentation (partial)

Reduced latency:
----------------
We are now using separate threads to handle ISDN input (and send it to audio
output) and audio input (which again sends it to ISDN). This means, we are
not dependent on GTK GUI reaction time. This enables very good sound quality
with low latency and low CPU consumption (normally not measurable).

Tested with 3x parallel "while true; do true; done" and "find /". No sound
problems, even though the threads don't run with real time priority (which
can be implemented now relatively easily, though).

ISDN system:
------------
We now support CAPI 2.0. This means, any card supported by CAPI 2.0 should
work and also ant-phone should be able to coexist with other ISDN
applications.

New CAPI code was tested with Fritz!Card, both with fcpci driver and with
mISDN driver.

There are also remote-CAPI implementations which forward local CAPI requests
to a remote host (e.g., a Fritz!Box router). They should be also usable with
new CAPI subsystem (not tested).

Sound system:
-------------
Since we are now using ALSA, it's much easier to coexist with other sound
applications. However, sound device specification has been changed. You
should now type in ALSA names (normally 'default') instead of raw devices.

Recording:
----------
Recording subsystem now uses ring buffers and the buffers are flushed in GTK
thread on timeout. Although it would be possible to have special thread for
this purpose, it doesn't really make sense, since we buffer about 2 seconds.
In 2 seconds, the application better responds... Anyway, in the worst case,
there will be some samples skipped. Also, recording tries to stitch together
incoming samples on local as well as remote channels as it best fits based on
time code. Certain jitter is compensated for automatically, bigger jitter may
produce some cracks. However, in tests there was no such problem.

Debug and error message handling:
---------------------------------
All debug and error messages should now go through macros dbgprintf() and
errprintf(). This allows for implementing of debug log viewer later.

For new/rewritten code, all debug and error messages start with "COMPONENT: "
prefix. This allows for easier identification in logs.

Additionally, there are further debug levels, up to 4. Debug level 3 and
higher prints very noisy information about each audio packet processed.

Sleep and wakeup commands:
--------------------------
Some ISDN card drivers need to be unloaded before suspending the machine to
disk and loaded again after resume. This is not possible, as long as the
application is holding the CAPI connection. Therefore, two new command-line
commands have been implemented, which allow turning off and on the CAPI
connection (--sleep and --wakeup, respectively). They can be integrated into
suspend and resume scripts to take care of stopping and resuming CAPI.

Code documentation:
-------------------
Documentation for new code and for big rewritten parts has been changed to
Doxygen format. Instead of plain comments, you'll see comments in form
/*! ... */ with tags in form @tag, which allow Doxygen to generate
pretty-printed source code reference documentation. Doxygen script file
has not been actually produced. The rest of the files should be changed
as well to use Doxygen format tags in documentation of functions and
structures.
This commit is contained in:
Ivan Schreter 2007-11-24 18:55:27 +01:00
parent 1f787304be
commit 720bd885eb
35 changed files with 4013 additions and 2630 deletions

View File

@ -6,6 +6,8 @@ Lars Volkhardt <Lars.Volkhardt@uni-konstanz.de> (save callerid list to file)
Mario Andrés Pagella <mapagella@fitzroy-is.com.ar> (project logo)
Ivan Schreter <schreter@gmx.net> (CAPI 2.0 support, ALSA support, low-latency)
Translators:
FACORAT Fabrice <f.faber-pro@ifrance.com> (French)
Roel Koelewijn <rl.koelewijn@home.nl> (Dutch)

23
README
View File

@ -6,20 +6,20 @@ Copyright 2002, 2003, 2004, 2005 Roland Stigge
WHAT IT IS
==========
ANT is a telephone application written for GNU/Linux, I4L (ISDN4Linux,
integrated in the kernel), GTK+ (GIMP Toolkit) and OSS (Open Sound System).
ANT is a telephone application written for GNU/Linux, CAPI 2.0 (ISDN
interface), GTK+ (GIMP Toolkit) and ALSA (sound system).
ABOUT ANT
=========
It directly interfaces OSS and ISDN devices, so there is no need to install
It directly interfaces ALSA and CAPI devices, so there is no need to install
extra software or hardware like PBX (Private Branch Exchange) or telephony
cards, if you've got direct access to an audio capable ISDN Card and full
cards, if you've got direct access to an audio capable ISDN card and full
duplex (or multiple) soundcard.
For the soundcard interface you can use headphones (or speakers) and a
microphone, or a headset instead of all this.
microphone, or a headset.
Currently, there is no direct mixer integration planned for ANT for several
reasons. For GNU/Linux, there are quite a lot of good mixer programs available
@ -36,16 +36,16 @@ WHAT YOU WILL NEED
to run:
* a computer
* an ISDN card with audio support for Linux (ISDN4Linux driver)
(teles or HiSax drivers are known to work)
* an ISDN card with CAPI 2.0 telephony support for Linux
* GNU/Linux (as far as nobody has tried it on another platform yet)
* OSS support
* a full duplex sound card or two sound devices
(one for input, another for output)
* a microphone (or your sound source of choice)
* speakers, or better: headphones
* instead of the last two points, you could also use a headset
* GTK+ 2.x
* libcapi20 (CAPI 2.0 support)
* libasound (ALSA support)
* libsndfile
to compile:
@ -53,8 +53,9 @@ to compile:
* GNU make
* GCC
* GTK+ 2.x development files
* OSS development files (actually, it's mostly just one header file)
* libsndfile development files
* libcapi20 development files (CAPI 2.0 support)
* libasound development files (ALSA support)
* libsndfile development files (for recording)
WHAT YOU MIGHT NEED
@ -76,7 +77,7 @@ FEATURES
* Setting outgoing (identifying) MSN (Multiple Subscriber Number) and
MSNs to listen on
* Line Level Checker
* Works with ALSA (OSS emulation)
* Works with ALSA
* Saved config file
* Saved Caller ID history
* Option to run an external command on incoming call (useful for external

20
TODO
View File

@ -1,17 +1,22 @@
Bugs:
=====
* Recording chopped up (only recorded file)
* Incoming delay
* Kernel cpu consumption in conversation mode. This is a kernel-internal
problem. While reading blocks from kernel OSS devices and ttyI, select(s)
seems to consume lot's of cpu time in kernel space while NOT BLOCKING!
-> not a big problem for now, just less idle tasks ;)
-> The problem doesn't seem to appear with ALSA
* Due to unsynchronized ISDN and ALSA clocks, there are still some issues with
audio overruns/unterruns, which may cause slight distortions in the sound
quality. This can only by addressed by stretching/contracting sound segments.
Currently it is handled by duplicating small frames (~20ms) on underruns or
skipping small frames (~20ms) on overruns. In some rare cases, however,
it happens frequently for up to a minute. Hanging up and redialing is one
option how to get rid of this problem.
* Audio device choosing is still done via textbox. With ALSA now, it should be
done via combobox, as device names can be queried.
* Surely some new ones after rewrite of large parts of the code...
Feature requests:
=================
* Real time support
=> not really necessary anymore, but can be easily implemented now
* client/server architecture (ttyI network forward) (Sven Geggus <sven@gegg.us>, Arne Börs <Arne.Boers@gmx.de>, martin@stigge.org)
=> can be now handled by remote CAPI, no need for special code
* Makeln (Joerg Brueggemann <jb@neviges.net>)
* database connection (caller id, times of incoming / outgoing, ...)
@ -51,6 +56,7 @@ Daniel N
Philipp Thomas <pthomas@suse.de>:
* CAPI support, native ALSA support (both in Linux 2.6) (needed for SuSE 9.1 prepared for 02/2004)
=> both done
wolfgang@rohdewald.de:
* "execute on recorded message" option

View File

@ -1,6 +1,6 @@
# Process this file with autoconf to produce a configure script.
AC_INIT(ant-phone)
AM_INIT_AUTOMAKE(ant-phone, "0.1.13", ant-phone-devel@nongnu.org)
AM_INIT_AUTOMAKE(ant-phone, "0.2.0", ant-phone-devel@nongnu.org)
AC_PREREQ(2.53)
AC_CONFIG_SRCDIR([config.h.in])
AM_CONFIG_HEADER([config.h])
@ -53,7 +53,7 @@ AC_SUBST(DEPS_LIBS)
#
AC_MSG_CHECKING(to see if we can add '-Wall -W' to CFLAGS)
if test x$GCC != x ; then
CFLAGS="$CFLAGS -D_U_=\"__attribute__((unused))\" -Wall -W -D_GNU_SOURCE -O3"
CFLAGS="$CFLAGS -D_U_=\"__attribute__((unused))\" -Wall -W -D_GNU_SOURCE"
AC_MSG_RESULT(yes)
else
CFLAGS="-D_U_=\"\" $CFLAGS"

View File

@ -27,7 +27,9 @@ ant_phone_SOURCES = \
server.c \
client.c \
recording.c \
isdntree.c
isdntree.c \
thread.c \
globals.c
noinst_HEADERS = \
callerid.h \
@ -49,7 +51,8 @@ noinst_HEADERS = \
globals.h \
gettext.h \
isdnlexer.h \
isdntree.h
isdntree.h \
thread.h
EXTRA_DIST = \
pickup.xpm \
@ -73,7 +76,7 @@ datadir = @datadir@
localedir = $(datadir)/locale
DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
LIBS = @LIBINTL@ @LIBS@
LIBS = @LIBINTL@ @LIBS@ -lgthread-2.0 -lasound -lcapi20
INCLUDES = -I../intl -I$(top_srcdir)/intl @DEPS_CFLAGS@
AM_CFLAGS = -DVERSION='"@VERSION@"' -DPACKAGE='"@PACKAGE@"' @CFLAGS@

View File

@ -102,9 +102,11 @@ int main(int argc, char *argv[]) {
{"msn", required_argument, 0, 'm'},
{"msns", required_argument, 0, 'l'},
{"call", required_argument, 0, 'c'},
{"sleep", no_argument, 0, 's'},
{"wakeup", no_argument, 0, 'w'},
{0, 0, 0, 0}
};
char *short_options = "hvrd::i:o:m:l:c:";
char *short_options = "hvrswd::i:o:m:l:c:";
int option_index = 0;
int c;
@ -126,11 +128,11 @@ int main(int argc, char *argv[]) {
#ifdef ENABLE_NLS
setlocale(LC_ALL, "");
if (!bindtextdomain(PACKAGE, LOCALEDIR)) {
fprintf(stderr, "Error setting directory for textdomain (i18n).\n");
errprintf("Error setting directory for textdomain (i18n).\n");
}
output_codeset_save();
if (!textdomain(PACKAGE)) {
fprintf(stderr, "Error setting domainname for gettext() "
errprintf("Error setting domainname for gettext() "
"(internationalization).\n");
}
#endif
@ -186,6 +188,9 @@ Options:\n\
-l, --msns=MSNS MSNs to listen on, semicolon-separated list or '*'\n\
default: *\n\
-c, --call=NUMBER Call specified number\n\
-s, --sleep Put ISDN thread to sleep (to be able to remove CAPI\n\
modules before suspending the computer).\n\
-w, --wakeup Restart ISDN thread after sleep.\n\
\n\
Note: If arguments of --soundin and --soundout are equal, a full duplex\n\
sound device is needed.\n"), argv[0]);
@ -219,7 +224,25 @@ Note: If arguments of --soundin and --soundout are equal, a full duplex\n\
break;
case 'c':
printf(_("Calling %s... "), optarg);
if (client_make_call(optarg)) {
if (client_make_call(LOCAL_MSG_CALL, optarg)) {
printf("\nAn error occured while calling a running " PACKAGE ".\n");
} else {
printf(_("successful.\n"));
}
exit(0);
break;
case 's':
printf(_("Suspending ISDN thread... "));
if (client_make_call(LOCAL_MSG_SUSPEND, "")) {
printf("\nAn error occured while calling a running " PACKAGE ".\n");
} else {
printf(_("successful.\n"));
}
exit(0);
break;
case 'w':
printf(_("Waking up ISDN thread... "));
if (client_make_call(LOCAL_MSG_WAKEUP, "")) {
printf("\nAn error occured while calling a running " PACKAGE ".\n");
} else {
printf(_("successful.\n"));
@ -237,22 +260,20 @@ Note: If arguments of --soundin and --soundout are equal, a full duplex\n\
if (session_init(&session, audio_device_name_in, audio_device_name_out,
msn, msns))
{
fprintf(stderr, "Error at session init.\n");
errprintf("Error at session init.\n");
exit(1);
} else {
if (debug)
fprintf(stderr, "Init OK.\n");
dbgprintf(1, "Init OK.\n");
}
/* gtk stuff, main loop */
gtk_result = main_gtk(&session);
if (session_deinit(&session)) {
fprintf(stderr, "Error at session exit\n");
errprintf("Error at session exit\n");
exit(1);
} else {
if (debug)
fprintf(stderr, "Quit OK.\n");
dbgprintf(1, "Quit OK.\n");
}
output_codeset_set(NULL); /* restore saved codeset */

View File

@ -69,7 +69,7 @@ static gpointer cid_row_new(void) {
*/
static void cid_row_destroy(gpointer rowdata) {
if (debug > 1)
fprintf(stderr, "debug: destroying rowdata at %p.\n", rowdata);
errprintf("debug: destroying rowdata at %p.\n", rowdata);
free(rowdata);
}
@ -192,6 +192,25 @@ static void cid_playback(GtkWidget *widget _U_, gpointer data, guint row) {
session_set_state(session, STATE_PLAYBACK);
}
/*
* Callback: called on call request
*/
static void cid_call(GtkWidget *widget _U_, gpointer data, guint row) {
session_t *session = (session_t *) data;
char *typestr;
char *numberstr;
gtk_clist_get_pixtext(GTK_CLIST(session->cid_list), row,
CID_COL_TYPE, &typestr, NULL, NULL, NULL);
gtk_clist_get_text(GTK_CLIST(session->cid_list), row,
strcmp(typestr, "IN") == 0 ?
CID_COL_FROM : CID_COL_TO, &numberstr);
gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(session->dial_number_box)->entry), numberstr);
}
/*
* Callback: called on "OK" click in "Save as..." file selection
*/
@ -225,26 +244,24 @@ static void cid_save_as_filename_cb(GtkWidget *fs) {
}
} while (just_read);
if (close(fd_out)) {
fprintf(stderr, "Error on closing destination file.\n");
errprintf("Error on closing destination file.\n");
}
} else {
fprintf(stderr,
"Error on destination file (%s) open().\n", destination);
errprintf("Error on destination file (%s) open().\n", destination);
}
if (close(fd_in)) {
fprintf(stderr, "Error on source file.\n");
errprintf("Error on source file.\n");
}
} else {
fprintf(stderr,
"Error on source file (%s) open().\n", sourcename);
errprintf("Error on source file (%s) open().\n", sourcename);
}
free(buffer);
} else {
fprintf(stderr, "Error on malloc().\n");
errprintf("Error on malloc().\n");
}
free(destination);
} else {
fprintf(stderr, "Error on asprintf().\n");
errprintf("Error on asprintf().\n");
}
}
free(sourcename);
@ -266,7 +283,7 @@ static void cid_save_as(GtkWidget *widget _U_, gpointer data, guint row) {
if (0 > asprintf(&title, _("Enter the base filename for %s file"),
extension))
{
fprintf(stderr, "Error on asprintf().\n");
errprintf("Error on asprintf().\n");
return;
}
fs = gtk_file_selection_new(title);
@ -279,7 +296,7 @@ static void cid_save_as(GtkWidget *widget _U_, gpointer data, guint row) {
"clicked", G_CALLBACK(cid_save_as_filename_cb), fs);
gtk_widget_show(fs);
} else {
fprintf(stderr, "Error: no filename extension found.\n");
errprintf("Error: no filename extension found.\n");
}
}
@ -333,6 +350,7 @@ static gint cid_mouse_cb(GtkWidget *widget _U_,
GtkItemFactoryEntry menu_items[] = {
/*path accel. callb. cb param. kind extra */
{_("/_Call"), NULL, cid_call, row, "", NULL},
{_("/_Playback"), NULL, cid_playback, row, "", NULL},
{_("/_Save as..."), NULL, cid_save_as, row, "", NULL},
{_("/Delete _Recording"),NULL, cid_delete_rec,row, "", NULL},
@ -345,6 +363,7 @@ static gint cid_mouse_cb(GtkWidget *widget _U_,
gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
GtkWidget *call_item;
GtkWidget *playback_item;
GtkWidget *save_as_item;
GtkWidget *delete_record_item;
@ -358,21 +377,25 @@ static gint cid_mouse_cb(GtkWidget *widget _U_,
gtk_item_factory_create_items_ac(item_factory, nmenu_items, menu_items,
session, 2);
if (!(call_item = gtk_item_factory_get_item(item_factory,
temp = stripchr(_("/_Call"), '_'))))
errprintf("Error getting call_item.\n");
free(temp);
if (!(playback_item = gtk_item_factory_get_item(item_factory,
temp = stripchr(_("/_Playback"), '_'))))
fprintf(stderr, "Error getting playback_item.\n");
errprintf("Error getting playback_item.\n");
free(temp);
if (!(save_as_item = gtk_item_factory_get_item(item_factory,
temp = stripchr(_("/_Save as..."), '_'))))
fprintf(stderr, "Error getting save_as_item.\n");
errprintf("Error getting save_as_item.\n");
free(temp);
if (!(delete_record_item = gtk_item_factory_get_item(item_factory,
temp = stripchr(_("/Delete _Recording"), '_'))))
fprintf(stderr, "Error getting delete_record_item_item.\n");
errprintf("Error getting delete_record_item_item.\n");
free(temp);
if (!(delete_item = gtk_item_factory_get_item(item_factory,
temp = stripchr(_("/_Delete Row"), '_'))))
fprintf(stderr, "Error getting delete_item.\n");
errprintf("Error getting delete_item.\n");
free(temp);
if (!(session->state == STATE_READY &&
@ -383,6 +406,9 @@ static gint cid_mouse_cb(GtkWidget *widget _U_,
} else {
free(fn);
}
if (session->state != STATE_READY) {
gtk_widget_set_sensitive(call_item, FALSE);
}
menu = GTK_MENU(gtk_item_factory_get_widget(item_factory, "<popup>"));
gtk_menu_set_accel_group(menu, accel_group);
@ -528,7 +554,7 @@ static char *cid_timestring(time_t t) {
date[0] = '\1';
len = strftime(date, 20, "%Y-%m-%d %H:%M:%S", localtime(&t));
if (len == 0 && date[0] != '\0') {
fprintf(stderr, "cid: Error calculating time with strftime.\n");
errprintf("cid: Error calculating time with strftime.\n");
return NULL;
}
@ -635,7 +661,7 @@ void cid_set_duration(session_t *session, gchar *message) {
void cid_add_saved_line(session_t *session, char *date, char *type,
char *from, char *to, char *duration) {
if (debug > 1)
fprintf(stderr, "Caller ID add:\n"
errprintf("Caller ID add:\n"
"Date: |%s|, Type: |%s|, From: |%s|, To: |%s|, Dur: |%s|\n",
date, type, from, to, duration);
@ -683,13 +709,11 @@ void cid_calls_merge(session_t *session) {
/* try to find isdnlog data file */
calls_filename = isdn_get_calls_filename();
if (calls_filename && (f = fopen(calls_filename, "r"))) {
if (debug) {
fprintf(stderr, "Using %s as source for isdnlog data.\n", calls_filename);
}
dbgprintf(1, "Using %s as source for isdnlog data.\n", calls_filename);
if (session->option_calls_merge_max_days) {
/* binary search on the file for the desired starting time if needed */
if (debug >= 3) {
fprintf(stderr, "Binary search in calls file...\n");
errprintf("Binary search in calls file...\n");
}
low = 0;
fseek(f, 0, SEEK_END);
@ -697,7 +721,7 @@ void cid_calls_merge(session_t *session) {
while (high - low > 200) {
if (debug >= 3) {
fprintf(stderr, "low = %d, high = %d\n", low, high);
errprintf("low = %d, high = %d\n", low, high);
}
mid = (low + high) / 2;
fseek(f, mid, SEEK_SET);
@ -726,7 +750,7 @@ void cid_calls_merge(session_t *session) {
if (sscanf(line,
"%*40[^|]|%40[^|]|%40[^|]|%d|%*40[^|]|%d|%*40[^|]|%1c",
from, to, &duration, &date, type) != 5) {
fprintf(stderr, "Warning: Incomplete data input from calls file.\n");
errprintf("Warning: Incomplete data input from calls file.\n");
break;
}
if ((temp = strchr(from, ' '))) *temp = '\0';
@ -791,9 +815,9 @@ void cid_calls_merge(session_t *session) {
cid_normalize(session);
if (fclose(f))
fprintf(stderr, "Error closing %s.\n", calls_filename);
errprintf("Error closing %s.\n", calls_filename);
} else { /* error on fopen() */
fprintf(stderr,
errprintf(
"Warning: Couldn't open isdnlog calls logfile. Proceeding without it.\n");
}
free(line);
@ -876,13 +900,13 @@ char* cid_get_record_filename(session_t* session, int row) {
timestr = cid_purify_timestring(timestr);
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
errprintf("Warning: Couldn't get home dir.\n");
return NULL;
}
if (asprintf(&pattern, "%s/." PACKAGE "/recordings/%s.*",
homedir, timestr) < 0) {
fprintf(stderr, "Warning: "
errprintf("Warning: "
"Couldn't allocate memory for filename globbing pattern.\n");
return NULL;
}
@ -895,7 +919,7 @@ char* cid_get_record_filename(session_t* session, int row) {
result = NULL;
break;
default:
fprintf(stderr, "Warning: "
errprintf("Warning: "
"globbing error while looking up recorded conversation.\n");
return NULL;
}

View File

@ -64,7 +64,7 @@ line : '\n'
free($1); free($3); free($5); free($7); free($9); }
| error '\n'
{ if (debug)
fprintf(stderr,
errprintf(
"Warning: Parsing callerid history file:%d, "
"recovering after error.\n", @1.last_line);
}
@ -79,7 +79,7 @@ line : '\n'
*/
void callerid_error(const char *message) {
if (debug)
fprintf(stderr,
errprintf(
"Warning: Parsing callerid history file line %d: %s.\n",
callerid_lloc.first_line, message);
}

View File

@ -42,7 +42,7 @@
*
* returns 0 on success, -1 otherwise
*/
int client_make_call(char *number) {
int client_make_call(char message, char *number) {
int sock;
struct sockaddr_un local_name;
size_t size;
@ -67,8 +67,8 @@ int client_make_call(char *number) {
return -1;
}
if (asprintf(&msg, "%c%s", LOCAL_MSG_CALL, number) < 0) {
fprintf(stderr, "asprintf error");
if (asprintf(&msg, "%c%s", message, number) < 0) {
errprintf("asprintf error");
return -1;
}
bytes = write(sock, msg, 1 + strlen(number) + 1);

View File

@ -22,4 +22,4 @@
*
*/
int client_make_call(char *number);
int client_make_call(char message, char *number);

View File

@ -252,34 +252,19 @@ static void controlpad_mute_cb(GtkWidget *button, gpointer data) {
*/
static void controlpad_record_cb(GtkWidget *button, gpointer data) {
session_t *session = (session_t *) data;
char *digits = NULL;
if (button == session->record_checkbutton) {
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) { /* record! */
session->option_record = 1;
if (session->state == STATE_CONVERSATION) {
if (recording_open(session->recorder,
digits = util_digitstime(&session->vcon_time),
session->option_recording_format))
{
fprintf(stderr, "Error opening audio file.\n");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
session->record_checkbutton), FALSE);
return;
}
free(digits);
cid_row_mark_record(session, session->cid_num - 1);
if (session_start_recording(session) == 0) {
session->option_record = 1;
cid_row_mark_record(session, session->cid_num - 1);
}
}
} else { /* don't record! */
session->option_record = 0;
if (session->state == STATE_CONVERSATION) {
recording_write(session->recorder, session->rec_buf_local,
session->rec_buf_local_index, RECORDING_LOCAL);
recording_write(session->recorder, session->rec_buf_remote,
session->rec_buf_remote_index, RECORDING_REMOTE);
recording_close(session->recorder);
session->rec_buf_local_index = 0;
session->rec_buf_remote_index = 0;
}
}
gtk_widget_set_sensitive(session->record_checkbutton_local,

View File

@ -136,8 +136,11 @@ unsigned char fxgenerate(session_t *session, enum effect_t effect,
(int)((sin(seconds * 2 * M_PI * f1) + sin(seconds * 2 * M_PI * f2))
/ 2 * 127.5 * 0.7 + 127.5)];
break;
case EFFECT_EMPTY:
x = session->audio_LUT_generate[128];
break;
default:
fprintf(stderr, "fxgenerate: Unknown effect.\n");
errprintf("fxgenerate: Unknown effect %d.\n", effect);
x = 0;
}

View File

@ -4,6 +4,7 @@
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2002, 2003 Roland Stigge
* Copyright 2007 Ivan Schreter
*
* ANT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -34,4 +35,19 @@
extern int debug;
/*!
* @brief Output a message.
*
* @param level message level (0=error, 1..n=debug).
* @param format printf-like format for following arguments.
*/
void msgprintf(int level, const char *format, ...)
__attribute__ ((format (printf, 2, 3)));
#define dbgprintf(level, ...) \
if (level <= debug) msgprintf(level, __VA_ARGS__)
#define errprintf(...) \
msgprintf(0, __VA_ARGS__)
#endif /* globals.h */

View File

@ -82,6 +82,7 @@ static void quit(GtkWidget *widget _U_, gpointer data, guint action _U_) {
settings_callerid_write(session); /* write callerid history */
gtk_handle_hang_up_button(NULL, data); /* simulate hang_up_button */
session_io_handlers_stop(session); /* make sure GTK handlers are stopped */
/* some (GUI) de-initialization, not directly session related */
llcheck_bar_deinit(session->llcheck_in);
@ -117,15 +118,13 @@ gint timeout_callback(gpointer data) {
if (interrupted) {
switch(interrupted) {
case SIGINT:
if (debug)
fprintf(stderr, "Ctrl-C caught.\n");
dbgprintf(1, "Ctrl-C caught.\n");
break;
case SIGTERM:
if (debug)
fprintf(stderr, "SIGTERM caught.\n");
dbgprintf(1, "SIGTERM caught.\n");
break;
default:
fprintf(stderr, "Warning: Unknown signal caught.\n");
errprintf("Warning: Unknown signal caught.\n");
}
quit(NULL, data, 0);
}
@ -136,7 +135,7 @@ gint timeout_callback(gpointer data) {
if (0 > asprintf(&buf, "%s %s",
state_data[session->state].status_bar, timediff))
fprintf(stderr, "Warning: timeout_callback: asprintf error.\n");
errprintf("Warning: timeout_callback: asprintf error.\n");
gtk_statusbar_pop(GTK_STATUSBAR(session->status_bar),
session->phone_context_id);
@ -154,7 +153,7 @@ gint timeout_callback(gpointer data) {
if (0 > asprintf(&buf, "%s %s",
state_data[session->state].status_bar, timediff))
fprintf(stderr, "Warning: timeout_callback: asprintf error.\n");
errprintf("Warning: timeout_callback: asprintf error.\n");
gtk_statusbar_pop(GTK_STATUSBAR(session->status_bar),
session->phone_context_id);
@ -277,10 +276,10 @@ static void cb_info_window(GtkWidget *widget _U_, gpointer data,
{ N_("Output channels:"), inactive ? strdup(_("[inactive]")) : ltostr(1) },
{ "", strdup("") },
{ N_("ISDN device:"), strdup(session->isdn_device_name) },
/* { N_("ISDN device:"), strdup(session->isdn_device_name) }, */
{ N_("ISDN speed (samples):"), ltostr(8000) },
{ N_("ISDN sample size (bits):"), ltostr(8) },
{ N_("ISDN fragment size (bytes):"), ltostr(255) }
/* { N_("ISDN fragment size (bytes):"), ltostr(255) } */
};
unsigned int i;

View File

@ -54,7 +54,7 @@ static int gtksettings_try(GtkWidget *widget) {
free(session->exec_on_incoming);
session->exec_on_incoming = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
} else
fprintf(stderr, "gtksettings_cb_ok: Error getting exec_on_incoming.\n");
errprintf("gtksettings_cb_ok: Error getting exec_on_incoming.\n");
/* popup checkbutton */
button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(widget),
@ -63,7 +63,7 @@ static int gtksettings_try(GtkWidget *widget) {
session->option_popup =
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
else
fprintf(stderr, "gtksettings_cb_ok: Error getting popup state.\n");
errprintf("gtksettings_cb_ok: Error getting popup state.\n");
/* recording_format */
list = (GSList *) gtk_object_get_data(GTK_OBJECT(widget), "recording_format");
@ -76,21 +76,21 @@ static int gtksettings_try(GtkWidget *widget) {
list = list->next;
}
} else
fprintf(stderr, "gtksettings_cb_ok: Error getting recording_format.\n");
errprintf("gtksettings_cb_ok: Error getting recording_format.\n");
/* msn */
entry = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(widget), "msn_entry");
if (entry)
session->msn = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
else
fprintf(stderr, "gtksettings_cb_ok: Error getting msn.\n");
errprintf("gtksettings_cb_ok: Error getting msn.\n");
/* msns */
entry = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(widget), "msns_entry");
if (entry)
session->msns = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
else
fprintf(stderr, "gtksettings_cb_ok: Error getting msns.\n");
errprintf("gtksettings_cb_ok: Error getting msns.\n");
/* history_entry */
entry = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(widget),
@ -99,7 +99,7 @@ static int gtksettings_try(GtkWidget *widget) {
session->dial_number_history_maxlen =
strtol(gtk_entry_get_text(GTK_ENTRY(entry)), NULL, 0);
else
fprintf(stderr, "gtksettings_cb_ok: Error getting history.\n");
errprintf("gtksettings_cb_ok: Error getting history.\n");
session->dial_number_history_pointer = 0;
/* cid_max_entry */
@ -109,7 +109,7 @@ static int gtksettings_try(GtkWidget *widget) {
session->cid_num_max =
strtol(gtk_entry_get_text(GTK_ENTRY(entry)), NULL, 0);
else
fprintf(stderr, "gtksettings_cb_ok: "
errprintf("gtksettings_cb_ok: "
"Error getting caller id maximum rows.\n");
/* cid_calls_merge_checkbutton */
@ -119,7 +119,7 @@ static int gtksettings_try(GtkWidget *widget) {
session->option_calls_merge =
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
else
fprintf(stderr,
errprintf(
"gtksettings_cb_ok: Error getting isdnlog calls_merge state.\n");
/* cid_calls_merge_max_entry */
entry = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(widget),
@ -128,7 +128,7 @@ static int gtksettings_try(GtkWidget *widget) {
session->option_calls_merge_max_days =
strtol(gtk_entry_get_text(GTK_ENTRY(entry)), NULL, 0);
else
fprintf(stderr, "gtksettings_cb_ok: "
errprintf("gtksettings_cb_ok: "
"Error getting maximum number of days for isdnlog retrieval.\n");
/* save checkbutton */
@ -138,7 +138,7 @@ static int gtksettings_try(GtkWidget *widget) {
session->option_save_options =
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
else
fprintf(stderr,
errprintf(
"gtksettings_cb_ok: Error getting save_options state.\n");
/*
@ -147,8 +147,7 @@ static int gtksettings_try(GtkWidget *widget) {
if (session->audio_device_name_in) { /* shut down if defined */
session_io_handlers_stop(session);
if (!session->option_release_devices) /* audio_close if normal mode */
session_audio_deinit(session);
session_set_audio_state(session, AUDIO_DISCONNECTED);
free(session->audio_device_name_in);
free(session->audio_device_name_out);
}
@ -160,7 +159,7 @@ static int gtksettings_try(GtkWidget *widget) {
session->audio_device_name_in =
strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
else
fprintf(stderr,"gtksettings_cb_ok: Error getting audio_device_name_in.\n");
errprintf("gtksettings_cb_ok: Error getting audio_device_name_in.\n");
/* audio_device_name_out */
entry = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(widget),
@ -169,7 +168,7 @@ static int gtksettings_try(GtkWidget *widget) {
session->audio_device_name_out =
strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
else
fprintf(stderr,
errprintf(
"gtksettings_cb_ok: Error getting audio_device_name_out.\n");
/* release checkbutton */
@ -179,11 +178,11 @@ static int gtksettings_try(GtkWidget *widget) {
session->option_release_devices =
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
else
fprintf(stderr,
errprintf(
"gtksettings_cb_ok: Error getting release_devices state.\n");
if (!session->option_release_devices) {
if (session_audio_init(session)) {
if (session_set_audio_state(session, AUDIO_IDLE) < 0) {
successful = 0;
free(session->audio_device_name_in);
free(session->audio_device_name_out);
@ -196,21 +195,23 @@ static int gtksettings_try(GtkWidget *widget) {
session_set_state(session, STATE_READY); /* update everything */
}
/* try to apply msn settings */
if (isdn_setMSN(session->isdn_fd, session->msn) ||
isdn_setMSNs(session->isdn_fd, session->msns)) {
/* got some problem */
free(session->msn);
free(session->msns);
session->msn = old_msn;
session->msns = old_msns;
isdn_setMSN(session->isdn_fd, session->msn);
isdn_setMSNs(session->isdn_fd, session->msns);
successful = 0;
} else {
/* everything's fine */
free(old_msn);
free(old_msns);
if (session->state == STATE_READY) {
/* try to apply msn settings */
if (isdn_setMSN(&session->isdn, session->msn) ||
isdn_setMSNs(&session->isdn, session->msns)) {
/* got some problem */
free(session->msn);
free(session->msns);
session->msn = old_msn;
session->msns = old_msns;
isdn_setMSN(&session->isdn, session->msn);
isdn_setMSNs(&session->isdn, session->msns);
successful = 0;
} else {
/* everything's fine */
free(old_msn);
free(old_msns);
}
}
return !!successful - 1;

1583
src/isdn.c

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2002, 2003 Roland Stigge
* Copyright 2007 Ivan Schreter
*
* ANT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,47 +22,259 @@
*
*/
#ifndef _ANT_ISDN_H
#define _ANT_ISDN_H
#include "config.h"
#include "thread.h"
#include "util.h"
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#define LOCK_PATH "/var/lock"
#define DEFAULT_ISDNBUF_SIZE 255
#define ISDN_COMMAND_TIMEOUT 1
/* 0 (master MSN) or MSN to use as identification on outgoing calls */
/*!
* @brief 0 (master MSN) or MSN to use as identification on outgoing calls.
*/
#define DEFAULT_MSN "0"
/* "*" (wildcard) or comma-separated list of MSNs */
/*!
* @brief Comma-separated set of MSNs to listen on.
*
* "*" (wildcard) to listen on all MSNs
*/
#define DEFAULT_MSNS "*"
#define ISDN_SPEED 8000
#define ETX 0x03
#define DC4 0x14
#define DLE 0x10
/*!
* @brief Fragment size to send to ISDN device.
*/
#define ISDN_FRAGMENT_SIZE 128
#define ISDN_CONFIG_FILENAME "/etc/isdn/isdn.conf"
extern char* isdn_calls_filename_from_config;
int open_isdn_device(char **isdn_device_name, char **isdn_lockfile_name);
int init_isdn_device(int isdn_fd, struct termios *backup);
int deinit_isdn_device(int isdn_fd, struct termios *backup);
int isdn_setMSN(int isdn_fd, char *msn);
int isdn_setMSNs(int isdn_fd, char *msns);
int isdn_stop_audio(int isdn_fd, int self_hangup);
int isdn_hangup(int isdn_fd);
int isdn_blockmode(int isdn_fd, int flag);
int close_isdn_device(int isdn_fd, char *isdn_device_name,
char *isdn_lockfile_name);
int isdn_dial(int isdn_fd, char *number, int timeout);
int isdn_set_full_duplex(int isdn_fd);
int tty_read(int fd, char *s, int timeout, char **got);
int tty_write(int fd, char *s);
int tty_clear(int fd);
/*!
* @brief ISDN callbacks to the application.
*
* The callbacks described in this structure run within the context
* of the calling thread. They need to send messages to other threads
* properly synchronized.
*/
typedef struct {
/*!
* @brief Callback when connection established.
*
* @param context context given at initialization time.
* @param number remote party number (may be NULL).
*/
void (*info_connected)(void *context, char *number);
/*!
* @brief Callback when ISDN data received.
*
* @param context context given at initialization time.
* @param data pointer to received data.
* @param length length of received data (in bytes).
*/
void (*info_data)(void *context, void *data, unsigned int length);
/*!
* @brief Callback when connection disconnected.
*
* @param context context given at initialization time.
*/
void (*info_disconnected)(void *context);
/*!
* @brief Callback when connection attempt fails with error.
*
* @param context context given at initialization time.
* @param error CAPI error number.
*/
void (*info_error)(void *context, unsigned int error);
/*!
* @brief Callback called on RING from other side.
*
* @param context context given at initialization time.
* @param callee remote party number (may be NULL).
* @param called called (our) number (may be NULL).
*/
void (*info_ring)(void *context, char *callee, char *called);
} isdn_callback_t;
/*!
* @brief Speed measurmenets.
*/
typedef struct {
unsigned long samples;/*!< total sample count (after first frame) */
uint64_t delta; /*!< how long did it take to send/receive samples */
uint64_t start; /*!< input start time in microseconds (for delta computation) */
uint64_t debug; /*!< debug timepoint */
} isdn_speed_t;
/*!
* @brief ISDN connection state.
*/
typedef enum {
ISDN_IDLE = 0, /*!< no connection active */
ISDN_CONNECT_REQ, /*!< connection request has been sent */
ISDN_CONNECT_WAIT, /*!< connection request acknowledged, waiting for connect */
ISDN_CONNECT_ACTIVE, /*!< connection active, no data channel (but requested) */
ISDN_CONNECT_B3_WAIT, /*!< data channel request confirmed, waiting for connect */
ISDN_CONNECTED, /*!< connection is completely established */
ISDN_DISCONNECT_B3_REQ, /*!< data disconnect has been requested */
ISDN_DISCONNECT_B3_WAIT, /*!< data disconnect confirmed, waiting for actual disconnect */
ISDN_DISCONNECT_ACTIVE, /*!< data disconnect done, sent physical channel disconnect req */
ISDN_DISCONNECT_WAIT, /*!< channel disconnect req confirmed, waiting for actual disconnect */
ISDN_RINGING, /*!< ringing */
ISDN_INCOMING_WAIT, /*!< waiting for incoming connect indication */
ISDN_MAXSTATE
} isdn_state_t;
/*!
* @brief ISDN handle wrapping CAPI interface.
*/
typedef struct {
/* NOTE: all parts private! Do not access them directly! */
unsigned int appl_id; /*!< CAPI application ID */
unsigned int msg_no; /*!< CAPI message serial number */
isdn_state_t state; /*!< current connection state */
unsigned int ctrl_count; /*!< controller count */
char *own_msn; /*!< own MSN (for originating calls) */
unsigned int info_mask; /*!< info mask for received info from CAPI */
unsigned int cip_mask; /*!< CIP mask for listening on services */
unsigned int active_plci; /*!< active physical connection PLCI (if off-hook) */
unsigned int active_ncci; /*!< active logical connection NCCI (if off-hook) */
char *remote_number; /*!< remote party number */
char *local_number; /*!< local number (currently only for ring) */
GMutex *lock; /*!< lock protecting this structure */
thread_t reply_thread; /*!< thread for processing ISDN replies */
isdn_callback_t *callback;/*!< set of callbacks for ISDN replies */
void *cb_context; /*!< context to use for callbacks */
GMutex *data_lock; /*!< ISDN request/reply lock */
isdn_speed_t in_speed; /*!< ISDN data input speed */
} isdn_t;
/*!
* @brief Open ISDN device via CAPI interface.
*
* @param isdn handle to fill in.
* @param callbacks ISDN callbacks to call for various ISDN events.
* @param context context for ISDN callbacks.
* @return 0 on success, less than 0 on error.
*/
int open_isdn_device(isdn_t *isdn, isdn_callback_t *callbacks, void *context);
/*!
* @brief Close ISDN device.
*
* @param isdn handle to close.
* @return 0 on success, less than 0 on error.
*/
int close_isdn_device(isdn_t *isdn);
/*!
* @brief Activate/deactivate ISDN connection.
*
* @param isdn device handle.
* @param activate if nonzero, activate ISDN, otherwise deactivate.
* @return 0 on success, -1 otherwise (e.g., can't open ISDN device).
*/
int activate_isdn_device(isdn_t *isdn, unsigned int active);
/*!
* @brief Initiate voice call on ISDN device.
*
* @param isdn device handle.
* @param controller ISDN controller number or 0 to use first available.
* @param number number to call.
* @return 0 on success, less than 0 on error.
*/
int isdn_dial(isdn_t *isdn, unsigned int controller, char *number);
/*!
* @brief Hang up current call on ISDN device.
*
* @param isdn device handle.
* @return 0 on success, less than 0 on error.
*/
int isdn_hangup(isdn_t *isdn);
/*!
* @brief Pick up pending call on ISDN device.
*
* @param isdn device handle.
* @return 0 on success, less than 0 on error.
*/
int isdn_pickup(isdn_t *isdn);
/*!
* @brief Send data over ISDN connection, after it's established.
*
* @param isdn device handle.
* @param data pointer to data.
* @param datalen data length.
*/
int isdn_send_data(isdn_t *isdn, unsigned char *data, unsigned int datalen);
/*!
* @brief Sets originating MSN for the specified ISDN device.
*
* @param isdn device handle.
* @param msn MSN to set ('0' for default).
* @return 0 on success, -1 otherwise.
*/
int isdn_setMSN(isdn_t *isdn, char *msn);
/*!
* @brief Sets MSNs to listen on for the specified ISDN device.
*
* @param isdn device handle.
* @param msns comma-separated set of MSNs to listen on ('*' for any).
* @return 0 on success, -1 otherwise.
*/
int isdn_setMSNs(isdn_t *isdn, char *msns);
/*!
* @brief Get the name of the calls file from isdnlog.
*
* @return file name or NULL on error.
*/
char* isdn_get_calls_filename(void);
/*!
* @brief Initialize speed measurement structure.
*
* @param speed structure to initialize.
*/
void isdn_speed_init(isdn_speed_t *speed);
/*!
* @brief Add bytes sent/received to measurement structure.
*
* @param speed structure to modify.
* @param samples sample count.
*/
void isdn_speed_addsamples(isdn_speed_t *speed, unsigned int samples);
/*!
* @brief Debug message for speed.
*
* @param speed structure to print out.
* @param level debug level.
* @param prefix prefix for message.
*/
void isdn_speed_debug(isdn_speed_t *speed, int level, char *prefix);
#endif /* _ANT_ISDN_H */

View File

@ -107,10 +107,10 @@ INCLUDE([ \t]|"\\\n")*"(" {
/* ignoring non-existent included file */
if (debug) {
if (loop_detected)
fprintf(stderr, "Loop detected ");
errprintf("Loop detected ");
else
fprintf(stderr, "Error ");
fprintf(stderr,
errprintf("Error ");
errprintf(
"reading ISDN options file %s "
"from %s. Ignoring.\n",
isdn_filename, temp->filename);
@ -124,7 +124,7 @@ INCLUDE([ \t]|"\\\n")*"(" {
/* include file OK */
isdn_in = temp_in;
if (debug)
fprintf(stderr,
errprintf(
"Reading options file %s "
"from %s ...\n",
isdn_filename, temp->filename);
@ -144,7 +144,7 @@ INCLUDE([ \t]|"\\\n")*"(" {
isdn_include_t* temp = isdn_include_list;
if (debug)
fprintf(stderr, "Returning to %s ...\n",
errprintf("Returning to %s ...\n",
temp->filename);
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_switch_to_buffer(temp->state);
@ -216,7 +216,7 @@ INCLUDE([ \t]|"\\\n")*"(" {
. { /* eat up rest */
isdn_locate();
if (debug) {
fprintf(stderr,
errprintf(
"isdnlexer: Unrecognized character: %c "
"at %d:%d\n",
*isdn_text,
@ -261,9 +261,9 @@ void isdn_lexer_init(char* filename) {
isdn_lloc.last_column = 1;
isdn_include_list = NULL;
if (debug)
fprintf(stderr, "Reading options file: %s ...\n", filename);
errprintf("Reading options file: %s ...\n", filename);
if (!(isdn_in = fopen(filename, "r"))) {
fprintf(stderr, "Error opening options file %s.\n", filename);
errprintf("Error opening options file %s.\n", filename);
} else {
isdn_filename = strdup(filename);
}
@ -274,7 +274,7 @@ void isdn_lexer_init(char* filename) {
*/
void isdn_lexer_deinit() {
if (fclose(isdn_in) == EOF) {
fprintf(stderr, "Warning: Couldn't close options file.\n");
errprintf("Warning: Couldn't close options file.\n");
}
free(isdn_filename);
}

View File

@ -97,7 +97,7 @@ section : '[' ISDN_TOKEN_NAME ']' entries
if (!($$ =
(isdn_tree_node_t*) malloc(sizeof(isdn_tree_node_t))))
{
fprintf(stderr, "Error: Out of memory.\n");
errprintf("Error: Out of memory.\n");
exit(1);
}
$$->type = ISDN_NODE_TYPE_SECTION;
@ -122,7 +122,7 @@ entries : { $$.list = NULL; $$.last = NULL; }
}
| entries error
{
fprintf(stderr, "Error region from %d:%d up to %d:%d.\n",
errprintf("Error region from %d:%d up to %d:%d.\n",
@2.first_line, @2.first_column,
@2.last_line, @2.last_column);
$$ = $1;
@ -134,7 +134,7 @@ entry : ISDN_TOKEN_NAME '=' value
if (!($$ =
(isdn_tree_node_t*) malloc(sizeof(isdn_tree_node_t))))
{
fprintf(stderr, "Error: Out of memory.\n");
errprintf("Error: Out of memory.\n");
exit(1);
}
$$->type = ISDN_NODE_TYPE_ENTRY;
@ -147,7 +147,7 @@ entry : ISDN_TOKEN_NAME '=' value
if (!($$ =
(isdn_tree_node_t*) malloc(sizeof(isdn_tree_node_t))))
{
fprintf(stderr, "Error: Out of memory.\n");
errprintf("Error: Out of memory.\n");
exit(1);
}
$$->type = ISDN_NODE_TYPE_SUBSECTION;
@ -171,7 +171,7 @@ value : ISDN_TOKEN_VALUE
*/
void isdn_error(const char *message) {
if (debug)
fprintf(stderr,
errprintf(
"Warning: Parsing isdn options file: %d:%d: %s.\n",
isdn_lloc.first_line, isdn_lloc.first_column, message);
}

View File

@ -27,6 +27,7 @@
/* own headers */
#include "isdntree.h"
#include "globals.h"
isdn_tree_node_t* isdn_tree;
@ -51,7 +52,7 @@ static void isdn_tree_dump_list(isdn_tree_node_t* list, int indent) {
isdn_tree_dump_list(list->content.subsection, indent + 2);
break;
default:
fprintf(stderr, "Unknown ISDN_NODE_TYPE\n");
errprintf("Unknown ISDN_NODE_TYPE\n");
}
list = list->next;
@ -87,7 +88,7 @@ static void isdn_tree_free_list(isdn_tree_node_t* list) {
isdn_tree_free_list(list->content.subsection);
break;
default:
fprintf(stderr, "Unknown ISDN_NODE_TYPE\n");
errprintf("Unknown ISDN_NODE_TYPE\n");
}
temp = list->next;

View File

@ -4,6 +4,7 @@
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2002, 2003 Roland Stigge
* Copyright 2007 Ivan Schreter
*
* ANT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -127,7 +128,9 @@ static void llcheck_quit(GtkObject *window) {
GtkWidget *bar = gtk_object_get_data(window, "bar");
/* remove own and restore old handlers */
#ifdef USE_GTK_LOOP
gtk_input_remove(session->gtk_audio_input_tag);
#endif
session_io_handlers_start(session);
/* stop effect */
@ -139,95 +142,16 @@ static void llcheck_quit(GtkObject *window) {
session_set_state(session, STATE_READY);
}
/*
* gdk callback on audio input
*
* input: widget: the llcheck_bar
*/
static void llcheck_handle_audio_input(gpointer widget, gint fd _U_,
GdkInputCondition condition _U_) {
session_t *session = gtk_object_get_data(widget, "session");
int i, got;
int max = 0;
unsigned char sample;
got = read(session->audio_fd_in, session->audio_inbuf,
session->fragment_size_in);
if (got != -1) {
for (i = 0; i < got;
i += session->audio_sample_size_in) {
if (session->audio_sample_size_in == 1) {
sample = session->audio_LUT_analyze[
session->audio_LUT_out[(int)(session->audio_inbuf[i])]];
} else { /* audio_sample_size == 2 */
/* multiple byte samples are used "little endian" in int
to look up in LUT (see mediation_makeLUT) */
sample = session->audio_LUT_analyze[
session->audio_LUT_out[(int)(session->audio_inbuf[i]) |
(int)(session->audio_inbuf[i+1]) << 8]];
}
if (abs((int)sample - 128) > max)
max = abs((int)sample - 128);
}
llcheck_bar_set(widget, (double)max / 128);
} else {
switch (errno) {
case EAGAIN:
fprintf(stderr,
"llcheck_handle_audio_input: "
"EAGAIN - no data immediately available.\n");
break;
case EBADF:
fprintf(stderr,
"llcheck_handle_audio_input: "
"EBADF - invalid file descriptor.\n");
break;
case EINTR:
fprintf(stderr,
"llcheck_handle_audio_input: EINTR - interrupted by signal.\n");
break;
case EIO:
fprintf(stderr,
"llcheck_handle_audio_input: EIO - hardware error.\n");
break;
}
}
}
/*
* called when sound is requested in level check window
*
* input: widget: (play) button
* data: session pointer
*/
static void llcheck_play(GtkWidget *widget, gpointer data) {
static void llcheck_play(GtkWidget *widget _U_, gpointer data) {
session_t *session = (session_t *)data;
GtkWidget *bar = gtk_object_get_data(GTK_OBJECT(widget), "bar");
if (session->effect != EFFECT_NONE) { /* already playing -> stop feeding */
gtk_input_remove(session->effect_tag);
}
/* carefully reset audio */
gtk_input_remove(session->gtk_audio_input_tag);
session_reset_audio(session);
session->gtk_audio_input_tag = gtk_input_add_full(session->audio_fd_in,
GDK_INPUT_READ,
llcheck_handle_audio_input,
NULL,
(gpointer) bar,
NULL);
/* start recording (again) */
read(session->audio_fd_in, session->audio_inbuf,
session->fragment_size_in);
/* finally, start playing */
/* start playing effect */
session_effect_start(session, EFFECT_TEST);
}
@ -313,7 +237,7 @@ void llcheck_bar_set(GtkWidget *bar, double max) {
(GtkWidget *)gtk_object_get_data(GTK_OBJECT(bar), "pbar2");
struct history_t *history =
(struct history_t *)gtk_object_get_data(GTK_OBJECT(bar), "history");
double maxmax = history_append(&history, max);
double maxmax = history ? history_append(&history, max) : max;
int width = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(bar), "width"));
/* saving additional width
(bar->allocation.width is valid only with shown widgets) */
@ -432,17 +356,8 @@ void llcheck(GtkWidget *widget _U_, gpointer data, guint action _U_) {
gtk_window_set_modal(GTK_WINDOW(window), TRUE);
/* remove old and set up own audio input handler */
session_io_handlers_stop(session);
session->gtk_audio_input_tag = gtk_input_add_full(session->audio_fd_in,
GDK_INPUT_READ,
llcheck_handle_audio_input,
NULL,
(gpointer) bar,
NULL);
read(session->audio_fd_in, session->audio_inbuf, /* start recording */
session->fragment_size_in);
/* start empty effect */
session_effect_start(session, EFFECT_EMPTY);
/* show everything */
gtk_widget_show(window);

View File

@ -4,6 +4,7 @@
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2002, 2003 Roland Stigge
* Copyright 2007 Ivan Schreter
*
* ANT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -4,6 +4,7 @@
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2002, 2003 Roland Stigge
* Copyright 2007 Ivan Schreter
*
* ANT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,9 +22,6 @@
*
*
*
* NOTE:
* * for performance reasons, separate recording buffers are filled while
* mediating
*/
#include "config.h"
@ -55,25 +53,41 @@
#include "fxgenerator.h"
#include "recording.h"
/*
* allocate memory and build look-up-tables for audio <-> isdn conversion
/*!
* @brief Invert bits in a byte.
*
* input:
* format_in, LUT_in: used audio format and pointer to look-up-table
* for conversion of isdn -> audio
* format_out, LUT_out: the same for audio -> isdn
* LUT_generate: table for conversion of 8 bit unsigned -> isdn
* LUT_analyze: table for conversion of isdn -> 8 bit unsigned
* For the fun of it, ISDN doesn't use plain A-law, but instead uses
* bit-inverse A-law. I.e., instead of bit order 01234567, it sends
* 76543210. This function converts it to bit-inverse.
*
* return: 0 on success, -1 otherwise
*
* NOTE: the caller has to free the memory of LUT_* itself
* @param c byte to invert.
* @return inverted byte.
*/
static unsigned char bitinverse(unsigned char c);
/*--------------------------------------------------------------------------*/
static unsigned char bitinverse(unsigned char c)
{
return
((c >> 7) & 0x1) |
((c >> 5) & 0x2) |
((c >> 3) & 0x4) |
((c >> 1) & 0x8) |
((c << 1) & 0x10) |
((c << 3) & 0x20) |
((c << 5) & 0x40) |
((c << 7) & 0x80);
}
/*--------------------------------------------------------------------------*/
int mediation_makeLUT(int format_in, unsigned char **LUT_in,
int format_out, unsigned char **LUT_out,
unsigned char **LUT_generate,
unsigned char **LUT_analyze,
short **LUT_ulaw2short) {
short **LUT_alaw2short) {
int sample_size_in;
int sample_size_out;
int buf_size_in;
@ -99,368 +113,278 @@ int mediation_makeLUT(int format_in, unsigned char **LUT_in,
return -1;
if (!(*LUT_analyze = (unsigned char*) malloc (256)))
return -1;
if (!(*LUT_ulaw2short = (short*) malloc (256*sizeof(short))))
if (!(*LUT_alaw2short = (short*) malloc (256*sizeof(short))))
return -1;
/* Calculation */
for (i = 0; i < buf_size_in; i += sample_size_in) { /* isdn -> audio */
switch(format_in) {
case AFMT_U8:
(*LUT_in)[i] = (unsigned char)((ulaw2linear((unsigned char)i) / 256 &
case SND_PCM_FORMAT_U8:
(*LUT_in)[i] = (unsigned char)((alaw2linear((unsigned char)i) / 256 &
0xff) ^ 0x80);
break;
case AFMT_S8:
(*LUT_in)[i] = (unsigned char)(ulaw2linear((unsigned char)i) / 256 &
case SND_PCM_FORMAT_S8:
(*LUT_in)[i] = (unsigned char)(alaw2linear((unsigned char)i) / 256 &
0xff);
break;
case AFMT_MU_LAW:
case SND_PCM_FORMAT_MU_LAW:
(*LUT_in)[i] = linear2ulaw(alaw2linear((unsigned char)i));
break;
case SND_PCM_FORMAT_A_LAW:
(*LUT_in)[i] = (unsigned char)i;
break;
case AFMT_S16_LE:
sample = ulaw2linear((unsigned char)(i / 2));
case SND_PCM_FORMAT_S16_LE:
sample = alaw2linear((unsigned char)(i / 2));
(*LUT_in)[i] = (unsigned char)(sample & 0xff);
(*LUT_in)[i+1] = (unsigned char)(sample >> 8 & 0xff);
break;
case AFMT_S16_BE:
sample = ulaw2linear((unsigned char)(i / 2));
case SND_PCM_FORMAT_S16_BE:
sample = alaw2linear((unsigned char)(i / 2));
(*LUT_in)[i+1] = (unsigned char)(sample & 0xff);
(*LUT_in)[i] = (unsigned char)(sample >> 8 & 0xff);
break;
case AFMT_U16_LE:
sample = ulaw2linear((unsigned char)(i / 2));
case SND_PCM_FORMAT_U16_LE:
sample = alaw2linear((unsigned char)(i / 2));
(*LUT_in)[i] = (unsigned char)(sample & 0xff);
(*LUT_in)[i+1] = (unsigned char)((sample >> 8 & 0xff) ^ 0x80);
break;
case AFMT_U16_BE:
sample = ulaw2linear((unsigned char)(i / 2));
case SND_PCM_FORMAT_U16_BE:
sample = alaw2linear((unsigned char)(i / 2));
(*LUT_in)[i+1] = (unsigned char)(sample & 0xff);
(*LUT_in)[i] = (unsigned char)((sample >> 8 & 0xff) ^ 0x80);
break;
default:
fprintf(stderr,
"Error: "
"Unsupported format appeared while building input LUT.\n");
errprintf("MEDIATION: "
"Unsupported in format %d appeared while building input LUT.\n",
format_in);
return -1;
}
}
for (i = 0; i < buf_size_out; i++) { /* audio -> isdn */
switch(format_out) {
case AFMT_U8:
(*LUT_out)[i] = linear2ulaw((i - 128) * 256);
case SND_PCM_FORMAT_U8:
(*LUT_out)[i] = linear2alaw((i - 128) * 256);
break;
case AFMT_S8:
(*LUT_out)[i] = linear2ulaw(i * 256);
case SND_PCM_FORMAT_S8:
(*LUT_out)[i] = linear2alaw(i * 256);
break;
case AFMT_MU_LAW:
case SND_PCM_FORMAT_MU_LAW:
(*LUT_out)[i] = linear2alaw(ulaw2linear((unsigned char)i));
break;
case SND_PCM_FORMAT_A_LAW:
(*LUT_out)[i] = (unsigned char)i;
break;
/* next 4 cases:
input int i stores first buffer byte in low byte */
case AFMT_S16_LE:
(*LUT_out)[i] = linear2ulaw((int)(signed char)(i >> 8) << 8 |
case SND_PCM_FORMAT_S16_LE:
(*LUT_out)[i] = linear2alaw((int)(signed char)(i >> 8) << 8 |
(int)(i & 0xff));
break;
case AFMT_S16_BE:
(*LUT_out)[i] = linear2ulaw((int)(signed char)(i & 0xff) << 8 |
case SND_PCM_FORMAT_S16_BE:
(*LUT_out)[i] = linear2alaw((int)(signed char)(i & 0xff) << 8 |
(int)(i >> 8));
break;
case AFMT_U16_LE:
(*LUT_out)[i] = linear2ulaw(i - 32768);
case SND_PCM_FORMAT_U16_LE:
(*LUT_out)[i] = linear2alaw(i - 32768);
break;
case AFMT_U16_BE:
(*LUT_out)[i] = linear2ulaw(((i & 0xff) << 8 | i >> 8) - 32768);
case SND_PCM_FORMAT_U16_BE:
(*LUT_out)[i] = linear2alaw(((i & 0xff) << 8 | i >> 8) - 32768);
break;
default:
fprintf(stderr,
"Error: "
"Unsupported format appeared while building output LUT.\n");
errprintf("MEDIATION: "
"Unsupported out format %d appeared while building output LUT.\n",
format_out);
return -1;
}
}
for (i = 0; i < 256; i++) { /* 8 bit unsigned -> isdn -> 8 bit unsigned */
(*LUT_generate)[i] = linear2ulaw((i - 128) * 256);
(*LUT_ulaw2short)[i] = s = ulaw2linear((unsigned char)i); /* ulaw->short */
(*LUT_generate)[i] = linear2alaw((i - 128) * 256);
(*LUT_alaw2short)[i] = s = alaw2linear((unsigned char)i); /* alaw->short */
(*LUT_analyze)[i] = (unsigned char)((s / 256 & 0xff) ^ 0x80);
}
return 0;
}
/*
* writes buffer carefully out to file (ttyI / audio device)
*
* returns 0 on success, -1 otherwise (write error)
*/
int write_buf(int fd, unsigned char *outbuf, int outbuf_size) {
int towrite = outbuf_size;
int written = 0;
/* write until everything has been written */
while (towrite && (written != -1 || errno == EAGAIN)) {
written = write(fd, &outbuf[outbuf_size - towrite], towrite);
if (debug >= 2)
fprintf(stderr, "Wrote %d bytes to device.\n", written);
if (written != -1)
towrite -= written;
else
if (errno == EAGAIN) {
if (debug)
fprintf(stderr, "write_buf: EAGAIN\n");
ant_sleep(SHORT_INTERVAL);
}
}
if (written == -1) {
perror("write_buf");
return -1;
}
return 0;
}
/*--------------------------------------------------------------------------*/
/* XXX: smooth samples when converting speeds in next 2 functions */
/*
* process isdn input from ttyI to sound device
*
* to be called after select found block to read in isdn file descriptor
*/
void process_isdn_source(session_t *session) {
int got, i, j;
void convert_isdn_to_audio(session_t *session,
unsigned char *isdn_buf,
unsigned int isdn_size,
unsigned char *audio_buf,
unsigned int *audio_size,
short *rec_buf,
unsigned int inverse) {
unsigned int i, j;
unsigned char inbyte; /* byte read from ttyI */
int to_process; /* number of samples to process
unsigned int to_process; /* number of samples to process
(according to ratio / ratio_support_count) */
unsigned int outptr; /* output sample pointer */
unsigned char sample; /* 8 bit unsigned sample */
double llratio; /* line level falloff ratio */
int max = 0; /* for llcheck */
short s; /* libsndfile sample data */
got = read(session->isdn_fd, session->isdn_inbuf,
session->isdn_inbuf_size);
outptr = 0;
if (debug >= 2)
fprintf(stderr, "From isdn: got %d bytes.\n", got);
dbgprintf(3, "MEDIATION: From isdn: got %d bytes.\n", isdn_size);
if (got != -1) {
for (i = 0; i < got; i++) {
inbyte = session->isdn_inbuf[i];
if (!session->escape == (inbyte != DLE)) {
/* normal mode or last byte was an escape in DLE mode */
/* input line level check */
sample = session->audio_LUT_analyze[inbyte];
if (abs((int)sample - 128) > max)
max = abs((int)sample - 128);
for (i = 0; i < isdn_size; i++) {
inbyte = isdn_buf[i];
if (inverse)
inbyte = bitinverse(inbyte);
/* recording */
if (session->option_record) {
if (session->option_record_remote)
s = session->audio_LUT_ulaw2short[inbyte];
else
s = 0;
session->rec_buf_remote[session->rec_buf_remote_index++] = s;
if (session->rec_buf_remote_index >= session->rec_buf_remote_size) {
if (recording_write(session->recorder, session->rec_buf_remote,
session->rec_buf_remote_size, RECORDING_REMOTE))
fprintf(stderr, "Warning: Recording (remote) error.\n");
session->rec_buf_remote_index = 0;
}
}
/* store sample for recording */
rec_buf[i] = session->option_record_remote ?
session->audio_LUT_alaw2short[inbyte] : 0;
/* touchtone to audio: after llcheck to monitor other end */
if (session->touchtone_countdown_audio > 0) {
inbyte = fxgenerate(session, EFFECT_TOUCHTONE,
session->touchtone_index,
(double)session->touchtone_countdown_audio /
ISDN_SPEED); /* playing reverse is ok */
session->touchtone_countdown_audio--;
}
/* input line level check */
sample = session->audio_LUT_analyze[inbyte];
if (abs((int)sample - 128) > max)
max = abs((int)sample - 128);
/* mediation */
to_process = (int)floor((double)(session->samples_in + 1) *
session->ratio_in) -
(int)floor((double)session->samples_in *
session->ratio_in);
/* printf("isdn -> audio: to_process == %d\n", to_process); */
for (j = 0; j < to_process; j++) {
if (session->audio_sample_size_out == 1) {
session->audio_outbuf[session->audio_outbuf_index++] =
session->audio_LUT_in[(int)inbyte];
} else { /* audio_sample_size == 2 */
session->audio_outbuf[session->audio_outbuf_index++] =
session->audio_LUT_in[(int)inbyte * 2];
session->audio_outbuf[session->audio_outbuf_index++] =
session->audio_LUT_in[(int)inbyte * 2 + 1];
}
if (session->audio_outbuf_index >= session->fragment_size_out) {
if (write_buf(session->audio_fd_out, session->audio_outbuf,
session->fragment_size_out))
session->aborted = 1;
session->audio_outbuf_index = 0;
}
}
session->samples_in++;
if (session->escape) {
session->escape = 0;
if (debug) fprintf(stderr, "debug: escape mode off after 2x DLE.\n");
}
} else if (!session->escape && inbyte == DLE) { /* new escape: DLE */
session->escape = 1;
if (debug)
fprintf(stderr, "debug: ttyI DLE escape mode on.\n");
} else /* i.e. if (*escape) */ {
if (inbyte == DC4 || inbyte == ETX) {
session->hangup = 1;
} else {/* else: must be a touchtone: ignored */
if (debug) {
if ((inbyte >= '0' && inbyte <= '9') || inbyte == '#' ||
inbyte =='*' || (inbyte >= 'A' && inbyte <= 'D'))
fprintf(stderr, "Touchtone %c received.\n", inbyte);
else
fprintf(stderr, "Warning: Unknown escape sequence received.\n");
}
}
session->escape = 0;
if (debug) fprintf(stderr,
"debug: escape mode off after special char.\n");
}
/* touchtone to audio: after llcheck to monitor other end */
if (session->touchtone_countdown_audio > 0) {
inbyte = fxgenerate(session, EFFECT_TOUCHTONE,
session->touchtone_index,
(double)session->touchtone_countdown_audio /
ISDN_SPEED); /* playing reverse is ok */
session->touchtone_countdown_audio--;
}
llcheck_bar_set(session->llcheck_in, (double)max / 128);
} else {
fprintf(stderr, "process_isdn_source: read error (return -1).\n");
/* mediation */
to_process = (int)floor((double)(i + 1) * session->ratio_in) -
(int)floor((double)i * session->ratio_in);
/* printf("isdn -> audio: to_process == %d\n", to_process); */
for (j = 0; j < to_process; j++) {
if (session->audio_sample_size_out == 1) {
audio_buf[outptr++] =
session->audio_LUT_in[(int)inbyte];
} else { /* audio_sample_size == 2 */
audio_buf[outptr++] =
session->audio_LUT_in[(int)inbyte * 2];
audio_buf[outptr++] =
session->audio_LUT_in[(int)inbyte * 2 + 1];
}
}
}
if (session->option_record && inverse) {
recording_write(session->recorder, rec_buf, isdn_size, RECORDING_REMOTE);
}
llratio = isdn_size / 400.0;
if (llratio > 1.0)
llratio = 1.0;
session->llcheck_in_state =
session->llcheck_in_state * (1.0 - llratio) +
((double)max / 128) * llratio;
dbgprintf(4, "MEDIATION: Audio out gain: %.3f\n", session->llcheck_in_state);
*audio_size = outptr;
}
/*
* process audio input from sound device to isdn tty
*
* to be called after select found fragment(s) to read from
*/
void process_audio_source(session_t *session) {
int i, j, got, n;
unsigned char sample; /* the ulaw sample */
short s = 0; /* libsndfile sample data */
/* the ulaw sample when muted: */
/*--------------------------------------------------------------------------*/
void convert_audio_to_isdn(session_t *session,
unsigned char *audio_buf,
unsigned int audio_size,
unsigned char *isdn_buf,
unsigned int *isdn_size,
short *rec_buf) {
unsigned int i, j;
unsigned int outptr; /* output sample pointer */
unsigned char sample; /* the alaw sample */
/* the alaw sample when muted: */
unsigned char zero = session->audio_LUT_generate[128];
int to_process; /* number of samples to process
unsigned int to_process; /* number of samples to process
(according to ratio / ratio_support_count) */
unsigned char sampleu8; /* 8 bit unsigned sample */
double llratio; /* line level falloff ratio */
int max = 0; /* for llcheck */
got = read(session->audio_fd_in, session->audio_inbuf,
session->fragment_size_in);
if (debug >= 2)
fprintf(stderr, "From audio: got %d bytes.\n", got);
outptr = 0;
if (got != -1) {
for (i = 0; i < got;
i += session->audio_sample_size_in, session->samples_out++) {
to_process = (int)floor((double)(session->samples_out + 1)
* session->ratio_out) -
(int)floor((double)session->samples_out
* session->ratio_out);
/* printf("audio -> isdn: to_process == %d\n", to_process); */
for (j = 0; j < to_process; j++) {
if (session->audio_sample_size_in == 1) {
sample = session->audio_LUT_out[(int)(session->audio_inbuf[i])];
} else { /* audio_sample_size == 2 */
/* multiple byte samples are used "little endian" in int
to look up in LUT (see mediation_makeLUT) */
sample = session->audio_LUT_out[(int)(session->audio_inbuf[i]) |
(int)(session->audio_inbuf[i+1])
<< 8];
}
/* touchtone to isdn: before llcheck to monitor it */
if (session->touchtone_countdown_isdn > 0) {
sample = fxgenerate(session, EFFECT_TOUCHTONE,
session->touchtone_index,
(double)session->touchtone_countdown_isdn /
ISDN_SPEED /* playing reverse is ok */ );
session->touchtone_countdown_isdn--;
}
dbgprintf(3, "MEDIATION: From audio: got %d bytes.\n", audio_size);
if (session->option_muted) /* zero if muted */
sample = zero;
for (i = 0; i < audio_size;
i += session->audio_sample_size_in) {
/* input line level check */
sampleu8 = session->audio_LUT_analyze[sample];
if (abs((int)sampleu8 - 128) > max)
max = abs((int)sampleu8 - 128);
/* recording */
if (session->option_record) {
if (session->option_record_local)
s = session->audio_LUT_ulaw2short[sample];
else
s = 0;
session->rec_buf_local[session->rec_buf_local_index++] = s;
if (session->rec_buf_local_index >= session->rec_buf_local_size) {
if (recording_write(session->recorder, session->rec_buf_local,
session->rec_buf_local_size, RECORDING_LOCAL))
fprintf(stderr, "Warning: Recording (local) error.\n");
session->rec_buf_local_index = 0;
}
}
n = (sample == DLE) ? 2 : 1; /* again if DLE escape */
while (n > 0) {
session->isdn_outbuf[session->isdn_outbuf_index++] = sample;
if (session->isdn_outbuf_index >= session->isdn_outbuf_size) {
/* write outbuf out */
if (write_buf(session->isdn_fd, session->isdn_outbuf,
session->isdn_outbuf_size))
session->aborted = 1;
session->isdn_outbuf_index = 0;
}
n--;
}
to_process = (int)floor((double)(outptr + 1)
* session->ratio_out) -
(int)floor((double)outptr
* session->ratio_out);
/* printf("audio -> isdn: to_process == %d\n", to_process); */
for (j = 0; j < to_process; j++) {
if (session->audio_sample_size_in == 1) {
sample = session->audio_LUT_out[(int)(audio_buf[i])];
} else { /* audio_sample_size == 2 */
/* multiple byte samples are used "little endian" in int
to look up in LUT (see mediation_makeLUT) */
sample = session->audio_LUT_out[(int)(audio_buf[i]) |
((int)(audio_buf[i+1]) << 8)];
}
/* touchtone to isdn: before llcheck to monitor it */
if (session->touchtone_countdown_isdn > 0) {
sample = fxgenerate(session, EFFECT_TOUCHTONE,
session->touchtone_index,
(double)session->touchtone_countdown_isdn /
ISDN_SPEED /* playing reverse is ok */ );
session->touchtone_countdown_isdn--;
}
if (session->option_muted) /* zero if muted */
sample = zero;
/* input line level check */
sampleu8 = session->audio_LUT_analyze[sample];
if (abs((int)sampleu8 - 128) > max)
max = abs((int)sampleu8 - 128);
/* store sample for recording */
rec_buf[outptr] = session->option_record_local ?
session->audio_LUT_alaw2short[sample] : 0;
isdn_buf[outptr++] = bitinverse(sample);
}
llcheck_bar_set(session->llcheck_out, (double)max / 128);
} else {
switch (errno) {
case EAGAIN:
if (debug)
fprintf(stderr,
"process_audio_source: "
"EAGAIN - no data immediately available (that's ok).\n");
break;
case EBADF:
fprintf(stderr,
"process_audio_source: EBADF - invalid file descriptor.\n");
break;
case EINTR:
fprintf(stderr,
"process_audio_source: EINTR - interrupted by signal.\n");
break;
case EIO:
fprintf(stderr,
"process_audio_source: EIO - hardware error.\n");
break;
}
}
if (session->option_record) {
recording_write(session->recorder, rec_buf, outptr, RECORDING_LOCAL);
}
llratio = outptr / 400.0;
if (llratio > 1.0)
llratio = 1.0;
session->llcheck_out_state =
session->llcheck_out_state * (1.0 - llratio) +
((double)max / 128) * llratio;
dbgprintf(4, "MEDIATION: Audio in gain: %.3f\n", session->llcheck_out_state);
*isdn_size = outptr;
}
/*--------------------------------------------------------------------------*/

View File

@ -4,6 +4,7 @@
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2002, 2003 Roland Stigge
* Copyright 2007 Ivan Schreter
*
* ANT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,17 +24,60 @@
#include "session.h"
/* mediation internal recording buffer (number of shorts): */
#define MEDIATION_RECBUFSIZE 16384
extern int finished;
extern int hangup;
/*!
* @brief Generate audio sample conversion tables.
*
*
* @note The caller is responsible to free the memory allocated for
* conversion tables!
*
* @param format_in audio input format.
* @param LUT_in conversion table to convert from A-law to audio.
* @param format_out audio output format.
* @param LUT_out conversion table to convert from audio to A-law.
* @param LUT_generate conversion table from signed 8-bit to A-law.
* @param LUT_analyze conversion table from A-law to signed 8-bit.
* @param LUT_alaw2short conversion table from A-law to short.
* @return 0 on success, -1 otherwise.
*/
int mediation_makeLUT(int format_in, unsigned char **LUT_in,
int format_out, unsigned char **LUT_out,
unsigned char **LUT_generate,
unsigned char **LUT_analyze,
short **LUT_ulaw2short);
int write_buf(int fd, unsigned char *outbuf, int outbuf_size);
void process_isdn_source(session_t *session);
void process_audio_source(session_t *session);
short **LUT_alaw2short);
/*!
* @brief Convert ISDN data to audio data.
*
* @param session current session.
* @param isdn_buf ISDN data buffer (A-law or bit-inverse A-law).
* @param isdn_size number of samples in ISDN buffer.
* @param audio_buf destination buffer for audio data.
* @param audio_size filled with size of audio data in bytes.
* @param rec_buf recording buffer as temporary to hold at least isdn_size shorts.
* @param bitinverse if true, ISDN data are bit-inverse A-law, otherwise A-law.
*/
void convert_isdn_to_audio(session_t *session,
unsigned char *isdn_buf,
unsigned int isdn_size,
unsigned char *audio_buf,
unsigned int *audio_size,
short *rec_buf,
unsigned int bitinverse);
/*!
* @brief Convert audio data to ISDN data.
*
* @param session current session.
* @param audio_buf buffer with audio data.
* @param audio_size size of audio data in bytes.
* @param isdn_buf destination ISDN data buffer (bit-inverse A-law).
* @param isdn_size filled with number of samples written to ISDN buffer.
* @param rec_buf recording buffer as temporary to hold at least isdn_size shorts.
*/
void convert_audio_to_isdn(session_t *session,
unsigned char *audio_buf,
unsigned int audio_size,
unsigned char *isdn_buf,
unsigned int *isdn_size,
short *rec_buf);

View File

@ -4,6 +4,7 @@
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2003 Roland Stigge
* Copyright 2007 Ivan Schreter
*
* ANT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -27,6 +28,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* sndfile audio file reading/writing library */
#include <sndfile.h>
@ -37,25 +39,19 @@
#include "recording.h"
#include "util.h"
/* recorder internal buffer size (number of items, 1 item = 2 shorts): */
#define RECORDING_BUFSIZE 16384
/*--------------------------------------------------------------------------*/
int recording_init(struct recorder_t *recorder)
{
memset(recorder, 0, sizeof(struct recorder_t));
return 0;
}
/*--------------------------------------------------------------------------*/
/*
* Carefully opens a file and prepares recorder
* * when the file exists, new data will be appended
*
* File format:
*
* input:
* recorder: struct to be filled with recorder state for recording session
* until recording_close()
* filename: the base file name. It will be expanded
* with full path and extension
*
* returns: 0 on success, -1 otherwise
*/
int recording_open(struct recorder_t *recorder, char *filename,
enum recording_format_t format) {
enum recording_format_t format)
{
SF_INFO sfinfo;
char *homedir;
char *fn;
@ -64,26 +60,24 @@ int recording_open(struct recorder_t *recorder, char *filename,
touch_dotdir();
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
errprintf("RECORD: Couldn't get home dir.\n");
return -1;
}
if (asprintf(&fn, "%s/." PACKAGE "/recordings", homedir) < 0) {
fprintf(stderr, "Warning: "
errprintf("RECORD: "
"recording_open: Couldn't allocate memory for directory name.\n");
return -1;
}
if (touch_dir(fn) < 0) {
if (debug)
fprintf(stderr,
"Warning: recording_open: Can't reach directory %s.\n", fn);
errprintf("RECORD: recording_open: Can't reach directory %s.\n", fn);
return -1;
}
free(fn);
if (asprintf(&fn, "%s/." PACKAGE "/recordings/%s.%s", homedir, filename, format & RECORDING_FORMAT_WAV ? "wav" : "aiff")
< 0) {
fprintf(stderr, "Warning: "
"recording_open: Couldn't allocate memory for directory name.\n");
errprintf("RECORD: "
"recording_open: Couldn't allocate memory for file name.\n");
return -1;
}
@ -94,146 +88,187 @@ int recording_open(struct recorder_t *recorder, char *filename,
sfinfo.channels = 2;
sfinfo.samplerate = ISDN_SPEED;
if (!(recorder->sf = sf_open(fn, SFM_WRITE, &sfinfo))) {
fprintf(stderr, "recording_open: sf_open (file creation) error.\n");
errprintf("RECORD: recording_open: sf_open (file creation) error.\n");
return -1;
}
} else { /* file already exists */
sfinfo.format = 0;
if (!(recorder->sf = sf_open(fn, SFM_RDWR, &sfinfo))) {
fprintf(stderr, "recording_open: sf_open (reopen) error.\n");
errprintf("RECORD: recording_open: sf_open (reopen) error.\n");
return -1;
}
if (sf_seek(recorder->sf, 0, SEEK_END) == -1) {
fprintf(stderr, "recording_open: sf_seek error.\n");
errprintf("RECORD: recording_open: sf_seek error.\n");
return -1;
}
}
recorder->filename = fn;
if (!(recorder->queue =
(struct recqueue_t *) malloc (sizeof(struct recqueue_t)))) {
fprintf(stderr, "recording_open: recorder->queue malloc error.\n");
return -1;
}
if (!(recorder->queue->buf =
(short *) malloc (RECORDING_BUFSIZE * sizeof(short) * 2))) {
fprintf(stderr, "recording_open: recorder->queue->buf malloc error.\n");
return -1;
}
recorder->queue->next = NULL;
recorder->current_local = recorder->current_remote = recorder->queue;
recorder->localindex = 0;
recorder->remoteindex = 0;
/* initialize streaming buffers */
recorder->last_write = 0;
memset(&recorder->channel_local, 0, sizeof(rec_channel_t));
memset(&recorder->channel_remote, 0, sizeof(rec_channel_t));
/* NOTE: this has to be the last assignment, as it starts recording */
recorder->start_time = microsec_time();
return 0;
}
/*
* writes specified number of shorts to file
*
* input:
* recorder: struct with sound file state
* buf, size: the buffer of shorts with its size (number of shorts)
* channel: RECORDING_LOCAL or RECORDING_REMOTE
*
* returns: 0 on success, -1 otherwise
*/
/*--------------------------------------------------------------------------*/
int recording_write(struct recorder_t *recorder, short *buf, int size,
enum recording_channel_t channel) {
int i;
struct recqueue_t **link_this; /* alias for current_local or current_remote */
int *buf_index_this; /* alias for localindex or remoteindex */
struct recqueue_t *temp;
if (channel == RECORDING_LOCAL) {
link_this = &recorder->current_local;
buf_index_this = &recorder->localindex;
enum recording_channel_t channel)
{
int64_t start = recorder->start_time;
int64_t current, startpos, position;
int bufpos, split, delta;
rec_channel_t *buffer;
if (start == 0)
return 0; /* not enabled */
if (size < 1) {
errprintf("RECORD: recording_write: Trying to record with wrong size %d\n",
size);
return -1;
}
/* determine channel */
switch (channel)
{
case RECORDING_LOCAL:
buffer = &recorder->channel_local;
break;
case RECORDING_REMOTE:
buffer = &recorder->channel_remote;
break;
default:
errprintf("RECORD: recording_write: Recording to unknown channel %d requested\n",
(int) channel);
return -1;
}
/* compute position where to start write */
current = microsec_time() - start;
if (current < 0)
return 0; /* should never happen! */
int64_t endpos = current * ISDN_SPEED / 1000000LL;
startpos = endpos - size;
position = buffer->position;
if (startpos >= position - RECORDING_JITTER && startpos <= position + RECORDING_JITTER) {
/* position falls within recording jitter, adjust it to prevent cracks in recording */
startpos = position;
endpos = position + size;
}
if (startpos < position) {
/* should not happen, but to be sure, skip samples at the beginning */
delta = (int) position - startpos;
startpos = position;
buf += delta;
size -= delta;
if (size <= 0)
return 0; /* skipping too much, no data left */
}
bufpos = startpos % RECORDING_BUFSIZE;
dbgprintf(3, "RECORD: recording_write: data 0x%lx+%d to channel %d, pos %lld(%d)\n",
(long) buf, size, (int) channel, (long long) startpos, bufpos);
/* copy data into buffer */
if (bufpos + size <= RECORDING_BUFSIZE) {
/* all data can be copied at once */
memcpy(buffer->buf + bufpos, buf,
size * sizeof(short));
} else {
link_this = &recorder->current_remote;
buf_index_this = &recorder->remoteindex;
/* must split to two copies */
split = RECORDING_BUFSIZE - bufpos;
memcpy(buffer->buf + bufpos, buf,
split * sizeof(short));
buf += split;
size -= split;
memcpy(buffer->buf, buf, size * sizeof(short));
}
for (i = 0; i < size; i++) {
(*link_this)->buf[(*buf_index_this)++ * 2 + channel] = buf[i];
if (*buf_index_this >= RECORDING_BUFSIZE) { /* one buffer full */
if ((*link_this)->next == NULL) { /* expand list */
if (!((*link_this)->next =
(struct recqueue_t *) malloc (sizeof(struct recqueue_t)))) {
fprintf(stderr, "recording_write: buffer allocation error.\n");
return -1;
}
if (!((*link_this)->next->buf =
(short *) malloc (RECORDING_BUFSIZE * sizeof(short) * 2))) {
fprintf(stderr, "recording_write: buffer allocation error.\n");
return -1;
}
(*link_this)->next->next = NULL;
}
*link_this = (*link_this)->next; /* go further in list */
*buf_index_this = 0;
if (recorder->queue != recorder->current_local &&
recorder->queue != recorder->current_remote) {
/* write out buffer */
sf_writef_short(recorder->sf, recorder->queue->buf, RECORDING_BUFSIZE);
temp = recorder->queue;
recorder->queue = recorder->queue->next;
free(temp->buf);
free(temp);
}
}
}
/* mark buffer last position */
buffer->position = endpos;
return 0;
}
/*
* finishes recording to file, writes remaining data from queue to file
* (eventually filling a dangling channeln with silence)
*
* input:
* recorder: struct with sound file state
*
* returns: 0 on success, -1 otherwise
*/
int recording_close(struct recorder_t *recorder) {
struct recqueue_t **link_this; /* aliases for the links and their buffer - */
struct recqueue_t **link_other;
int *buf_index_this; /* indices to traverse */
int *buf_index_other;
enum recording_channel_t channel;
struct recqueue_t *temp;
/* set last, unavailable samples to zero */
if (recorder->queue == recorder->current_local) { /* traverse local samples */
link_this = &recorder->current_local;
link_other = &recorder->current_remote;
buf_index_this = &recorder->localindex;
buf_index_other = &recorder->remoteindex;
channel = RECORDING_LOCAL;
} else { /* traverse remote samples */
link_this = &recorder->current_remote;
link_other = &recorder->current_local;
buf_index_this = &recorder->remoteindex;
buf_index_other = &recorder->localindex;
channel = RECORDING_REMOTE;
/*--------------------------------------------------------------------------*/
int recording_flush(struct recorder_t *recorder, unsigned int last)
{
/* compute position to write up to */
int64_t maxposition = recorder->channel_local.position;
int64_t tmp = recorder->channel_remote.position;
int64_t startposition = recorder->last_write;
short recbuf[RECORDING_BUFSIZE * 2]; /* sample buffer */
int srcptr, dstptr, size;
if (recorder->start_time == 0)
return 0; /* recording not active */
if (tmp > maxposition)
maxposition = tmp;
if (startposition + (RECORDING_BUFSIZE * 7 / 8) < maxposition) {
/* underflow, skip samples */
dbgprintf(2, "RECORD: recording_flush: underflow detected, start %lld, current %lld\n",
(long long) startposition, (long long) maxposition);
startposition = maxposition - (RECORDING_BUFSIZE * 7 / 8);
}
while (*link_this != NULL) { /* traverse all chain links */
while ((*link_this != *link_other && *buf_index_this < RECORDING_BUFSIZE)
|| *buf_index_this < *buf_index_other) { /* all remaining samples */
(*link_this)->buf[(*buf_index_this)++ * 2 + channel] = 0;
}
sf_writef_short(recorder->sf, (*link_this)->buf, *buf_index_this);
*buf_index_this = 0;
temp = *link_this;
*link_this = (*link_this)->next;
free(temp->buf);
free(temp);
if (!last) {
/* skip last 1/8th of the buffer as jitter buffer */
maxposition -= RECORDING_BUFSIZE / 8;
}
if (!sf_close(recorder->sf)) return -1;
free(recorder->filename);
size = (int) (maxposition - startposition);
if (maxposition <= 0 || startposition >= maxposition ||
(!last && size < RECORDING_BUFSIZE / 8))
return 0; /* not enough data yet */
/* write samples between startposition and maxposition */
dstptr = 0;
srcptr = startposition % RECORDING_BUFSIZE;
while (--size) {
recbuf[dstptr++] = recorder->channel_local.buf[srcptr];
recorder->channel_local.buf[srcptr] = 0;
recbuf[dstptr++] = recorder->channel_remote.buf[srcptr];
recorder->channel_remote.buf[srcptr] = 0;
if (++srcptr >= RECORDING_BUFSIZE)
srcptr = 0;
}
sf_writef_short(recorder->sf, recbuf, dstptr / 2);
/* update last sample pointer */
recorder->last_write = maxposition;
return 0;
}
/*--------------------------------------------------------------------------*/
int recording_close(struct recorder_t *recorder)
{
int result = 0;
if (recorder->start_time) {
/* flush outstanding data and disable recording */
if (recording_flush(recorder, 1) < 0)
result = -1;
recorder->start_time = 0;
if (recorder->filename) {
free(recorder->filename);
recorder->filename = 0;
}
/* close the recorder */
if (sf_close(recorder->sf) != 0)
result = -1;
}
return result;
}
/*--------------------------------------------------------------------------*/

View File

@ -4,6 +4,7 @@
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2003 Roland Stigge
* Copyright 2007 Ivan Schreter
*
* ANT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -32,6 +33,19 @@
/* sndfile audio file reading/writing library */
#include <sndfile.h>
/*!
* @brief Recorder internal buffer size (number of items, 1 item = 2 shorts).
*/
#define RECORDING_BUFSIZE 32768
/*!
* @brief Recording jitter to compensate, about 25ms.
*/
#define RECORDING_JITTER 200
/*!
* @brief Recording formats.
*/
enum recording_format_t {
RECORDING_FORMAT_WAV = 0x10,
RECORDING_FORMAT_AIFF = 0x20,
@ -43,30 +57,111 @@ enum recording_format_t {
RECORDING_FORMAT_MINOR = 0x0F
};
/*!
* @brief Recording channels.
*/
enum recording_channel_t {
RECORDING_LOCAL = 0,
RECORDING_REMOTE = 1
RECORDING_LOCAL = 0, /*!< local channel */
RECORDING_REMOTE = 1 /*!< remote channel */
};
struct recqueue_t {
struct recqueue_t *next;
short *buf;
};
/*!
* @brief Recording channel structure (cyclic buffer).
*
* Each recording data buffer computes its start and end positions based on
* sample count, current microtime, start microtime and ISDN speed. Then,
* it will write itself at appropriate position in recording buffer and update
* the position.
*
* Consumer of recording buffer will take last written position and minimum of
* the positions in buffers and write out the samples in between. The written
* samples will be zeroed out, to prevent repeating data. The consumer will
* be repeatedly called by main thread (via GTK timeout).
*
* Opening of recording device will set a flag to start writing to recording
* buffers. Closing of recording device will turn off recording flag and flush
* all not yet written data.
*
* In this way, it's possible to more or less guarantee real-time properties
* of ISDN and audio threads (as they will not wait for any disk writes) and
* also be reasonably sure the samples do make it to the disk. If they don't,
* because of CPU load, there will be skipping of samples on disk. So what...
*/
typedef struct {
int64_t position; /*!< one past last sample (absolute position) */
short buf[RECORDING_BUFSIZE]; /*!< buffer with samples to record */
} rec_channel_t;
/*!
* @brief Recorder state.
*/
struct recorder_t {
SNDFILE *sf; /* sndfile state */
char *filename; /* audio file name */
struct recqueue_t *queue; /* buffer queue for communication with libsndfile */
struct recqueue_t *current_local; /* current queue element for local input */
struct recqueue_t *current_remote;/* current queue element for remote input */
int localindex; /* indices into current block in queue */
int remoteindex;
SNDFILE *sf; /*!< sndfile state */
char *filename; /*!< audio file name */
int64_t start_time; /*!< recording start time (0=disabled) */
rec_channel_t channel_local; /*!< recoding data channel for local data */
rec_channel_t channel_remote; /*!< recoding data channel for remote data */
int64_t last_write; /*!< position of last known write */
};
/*!
* @brief Initialize recorder structure.
*
* @param recorder struct to be filled with recorder state for recording
* session until recording_close().
* @return 0 on success, -1 otherwise.
*/
int recording_init(struct recorder_t *recorder);
/*!
* @brief Opens a file and prepares recorder.
*
* If the file already exists, new data will be appended at the end.
*
* @param recorder struct to be filled with recorder state for recording
* session until recording_close().
* @param filename the base file name. It will be expanded with full path
* and extension.
* @return 0 on success, -1 otherwise.
*/
int recording_open(struct recorder_t *recorder, char *filename,
enum recording_format_t format);
/*!
* @brief Writes specified number of shorts to recording channel.
*
* @param recorder struct with sound file state.
* @param buf buffer to write.
* @param size buffer size.
* @param channel channel to write to (RECORDING_LOCAL or RECORDING_REMOTE).
* @return 0 on success, -1 otherwise.
*/
int recording_write(struct recorder_t *recorder, short *buf, int size,
enum recording_channel_t channel);
enum recording_channel_t channel);
/*!
* @brief Flushes current record buffer to the file.
*
* This function is to be called periodically by main thread to write
* outstanding sound samples to the sound file and make space in cyclic
* buffer for data pro
*
* @param recorder struct with sound file state.
* @param last if nonzero, flush all samples, otherwise account for jitter.
* @return 0 on success, -1 otherwise.
*/
int recording_flush(struct recorder_t *recorder, unsigned int last);
/*!
* @brief Finishes recording to file.
*
* This function disables recording and writes remaining data from queue
* to file (eventually filling the lagging channel with silence).
*
* @param recorder struct with sound file state.
* @return 0 on success, -1 otherwise.
*/
int recording_close(struct recorder_t *recorder);
#endif /* recording.h */

View File

@ -47,7 +47,7 @@ char *server_local_socket_name(void) {
if (asprintf(&filename, "%s/." PACKAGE "/%s",
get_homedir(), SERVER_LOCAL_SOCKET_NAME) < 0) {
fprintf(stderr,
errprintf(
"Warning: Couldn't allocate memory for history filename.\n");
return NULL;
}
@ -83,7 +83,7 @@ int server_init(session_t *session) {
/* bind (file) name to socket */
if (bind(local_sock, (struct sockaddr *) &local_name, size)) {
perror("local bind");
fprintf(stderr,
errprintf(
"This program was possibly killed by accident on last run.\n"
"In this case, try running it with option -r.\n");
return -1;
@ -118,15 +118,25 @@ void server_handle_local_input(gpointer data, gint fd _U_,
bytes = read(sock, buffer, SERVER_INBUF_SIZE);
if (bytes > 0) { /* got message */
if (buffer[0] == LOCAL_MSG_CALL) {
if (debug) fprintf(stderr, "Request (%d bytes): call %s.\n",
bytes, &buffer[1]);
session_make_call(session, &buffer[1]);
switch (buffer[0]) {
case LOCAL_MSG_CALL:
dbgprintf(1, "Request (%d bytes): call %s.\n",
bytes, &buffer[1]);
session_make_call(session, &buffer[1]);
break;
case LOCAL_MSG_SUSPEND:
dbgprintf(1, "Request (%d bytes): sleep ISDN.\n", bytes);
session_activate_isdn(session, 0);
break;
case LOCAL_MSG_WAKEUP:
dbgprintf(1, "Request (%d bytes): wake up ISDN.\n", bytes);
session_activate_isdn(session, 1);
break;
}
} else if (bytes < 0) { /* error */
perror("local read");
} else { /* EOF */
fprintf(stderr, "local read: EOF");
errprintf("local read: EOF");
}
close(sock);

View File

@ -29,8 +29,13 @@
#define SERVER_CONNECTIONS_MAX_PENDING 5
#define SERVER_INBUF_SIZE 1024
/*!
* @brief Messages passed from client to server.
*/
enum local_msg_t {
LOCAL_MSG_CALL
LOCAL_MSG_CALL,
LOCAL_MSG_SUSPEND,
LOCAL_MSG_WAKEUP
};
char *server_local_socket_name(void);

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2002, 2003 Roland Stigge
* Copyright 2007 Ivan Schreter
*
* ANT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,149 +36,160 @@
/* GTK */
#include <gtk/gtk.h>
#include <alsa/asoundlib.h>
/* own header files */
#include "recording.h"
#include "isdn.h"
#include "thread.h"
#define SESSION_PRESET_SIZE 4
/*!
* @brief Session states.
*/
enum state_t {
STATE_READY, /* completely idle */
STATE_RINGING, /* somebody's calling */
STATE_RINGING_QUIET, /* same as above, audio off (device blocked) */
STATE_DIALING, /* we are dialing out */
STATE_CONVERSATION, /* we are talking */
STATE_SERVICE, /* special mode (llcheck) */
STATE_PLAYBACK, /* sound playback, usually recorded conversation */
STATE_READY, /*!< completely idle */
STATE_RINGING, /*!< somebody's calling */
STATE_RINGING_QUIET, /*!< same as above, audio off (device blocked) */
STATE_DIALING, /*!< we are dialing out */
STATE_CONVERSATION, /*!< we are talking */
STATE_SERVICE, /*!< special mode (llcheck) */
STATE_PLAYBACK, /*!< sound playback, usually recorded conversation */
STATE_NUMBER /* dummy to calculate size */
STATE_NUMBER /*!< dummy to calculate size */
};
/*!
* @brief Known audio effects.
*/
enum effect_t {
EFFECT_NONE, /* nothing is played currently */
EFFECT_RING, /* somebody's calling */
EFFECT_RINGING, /* waiting for the other end to pick up the phone */
EFFECT_TEST, /* play test sound (e.g. line level check) */
EFFECT_TOUCHTONE,/* play a touchtone */
EFFECT_SOUNDFILE /* play sound from file */
EFFECT_NONE, /*!< nothing is played currently */
EFFECT_RING, /*!< somebody's calling */
EFFECT_RINGING, /*!< waiting for the other end to pick up the phone */
EFFECT_TEST, /*!< play test sound (e.g. line level check) */
EFFECT_TOUCHTONE,/*!< play a touchtone */
EFFECT_EMPTY, /*!< don't play anything */
EFFECT_SOUNDFILE /*!< play sound from file */
};
/*
* Data needed for setting the session state (the state is the index)
/*!
* @brief Audio states.
*/
enum audio_t {
AUDIO_DISCONNECTED, /*!< audio is disconnected */
AUDIO_IDLE, /*!< audio is connected, but idle */
AUDIO_EFFECT, /*!< audio is playing an effect */
AUDIO_CONVERSATION /*!< audio is used for conversation */
};
/*!
* @brief Data needed for setting the session state (the state is the index).
*/
struct state_data_t {
char *status_bar;
char *pick_up_label;
int pick_up_state;
char *hang_up_label;
int hang_up_state;
char *status_bar; /*!< what to display in status bar */
char *pick_up_label; /*!< label for pick-up button */
int pick_up_state; /*!< state for pick-up button */
char *hang_up_label; /*!< label for hang-up button */
int hang_up_state; /*!< state for hang-up button */
};
struct state_data_t state_data[STATE_NUMBER];
/*!
* @brief GUI state data for various session states.
*/
extern struct state_data_t state_data[STATE_NUMBER];
/*
* session data
/*!
* @brief Session data.
*/
typedef struct {
/* audio device data */
char *audio_device_name_in; /* allocated memory! */
char *audio_device_name_out; /* allocated memory! */
int audio_fd_in; /* audio device file descriptors */
int audio_fd_out;
unsigned int audio_speed_in; /* sound device recording speed */
unsigned int audio_speed_out; /* sound device playback speed */
int fragment_size_in; /* sound device buffer fragment sizes in bytes */
int fragment_size_out;
int *format_priorities; /* 0-terminated sorted list of preferenced formats */
int audio_format_in; /* the actual formats */
int audio_format_out;
int audio_sample_size_in; /* number of bytes of a sample */
int audio_sample_size_out;
unsigned char *audio_inbuf; /* buffer: should have size fragment_size_in */
unsigned char *audio_outbuf; /* buffer: should have size fragment_size_out */
int audio_outbuf_index; /* needed for conversation mode output memory */
char *audio_device_name_in; /*!< name of input audio device */
char *audio_device_name_out; /*!< name of output audio device */
snd_pcm_t *audio_in; /*!< input audio device PCM handle */
snd_pcm_t *audio_out; /*!< output audio device PCM handle */
enum audio_t audio_state; /*!< current audio state */
unsigned int audio_speed_in; /*!< audio device recording speed */
unsigned int audio_speed_out; /*!< audio device playback speed */
int fragment_size_in; /*!< audio input fragment sizes in bytes */
int fragment_size_out; /*!< audio output fragment sizes in bytes */
int *format_priorities; /*!< 0-terminated sorted list of preferred audio formats (ALSA constants) */
int audio_format_in; /*!< used audio in format */
int audio_format_out; /*!< used audio out format */
int audio_sample_size_in; /*!< number of bytes of an input audio sample */
int audio_sample_size_out; /*!< number of bytes of an output audio sample */
isdn_speed_t audio_out_speed; /*!< actual audio out speed */
isdn_speed_t audio_in_speed; /*!< actual audio in speed */
thread_t thread_audio_input; /*!< audio data input thread */
/* isdn data */
char* isdn_device_name; /* "/dev/ttyIxx", xx == 0 .. 63 */
int isdn_fd;
char* isdn_lockfile_name;
int isdn_inbuf_size;
int isdn_outbuf_size;
unsigned char *isdn_inbuf; /* 8 bit ulaw */
unsigned char *isdn_outbuf;
int isdn_inbuf_len; /* index of '\0' in audio_inbuf in command mode,
if char at this index != 0 -> reading line */
int isdn_outbuf_index; /* needed for conversation mode output memory */
int escape; /* escape mode/state RECEIVING isdn data */
int no_input; /* after this many select calls without isdn input
get back from blockmode */
struct termios isdn_backup; /* saved state to restore on exit */
/* ISDN data */
isdn_t isdn; /*!< ISDN handle */
unsigned int isdn_active; /*!< flag for active CAPI connection */
char *from; /* caller's number */
char *to; /* callee's number */
char *from; /*!< caller's number */
char *to; /*!< callee's number */
char *hangup_reason; /*!< reason for hangup */
/* mediation data */
/* Look-up-tables for audio <-> isdn conversion: */
unsigned char *audio_LUT_in; /* isdn -> audio */
unsigned char *audio_LUT_out; /* audio -> isdn */
unsigned char *audio_LUT_generate; /* 8 bit unsigned -> isdn */
unsigned char *audio_LUT_analyze; /* isdn -> 8 bit unsigned */
short *audio_LUT_ulaw2short; /* unsigned char (ulaw) -> short */
double ratio_in; /* ratio: audio output rate / isdn input rate */
double ratio_out; /* ratio: isdn output rate / audio input rate */
unsigned int samples_in; /* ring counter of samples: from isdn */
unsigned int samples_out; /* ... ... : from sound device */
unsigned char *audio_LUT_in; /*!< lookup table ISDN -> audio */
unsigned char *audio_LUT_out; /*!< lookup table audio -> ISDN */
unsigned char *audio_LUT_generate; /*!< lookup table 8 bit unsigned -> ISDN */
unsigned char *audio_LUT_analyze; /*!< lookup table ISDN -> 8 bit unsigned */
short *audio_LUT_alaw2short; /*!< lookup table unsigned char (alaw) -> short */
double ratio_in; /*!< ratio: audio output rate / ISDN input rate */
double ratio_out; /*!< ratio: ISDN output rate / audio input rate */
/* recording data */
short *rec_buf_local; /* pointers to recording buffers */
short *rec_buf_remote;
int rec_buf_local_index; /* number of shorts in local recording buffer */
int rec_buf_remote_index; /* number of shorts in local recording buffer */
int rec_buf_local_size; /* recording buffer sizes */
int rec_buf_remote_size;
struct recorder_t *recorder; /* recorder internal data */
struct recorder_t *recorder; /*!< recorder internal data */
/* level check data */
double llcheck_in_state; /*!< current input value for level check */
double llcheck_out_state; /*!< current output value for level check */
guint gtk_updater_timer_tag; /*!< GTK timer tag for updating levels */
remote_call_port_t rem_port; /*!< remote call port to call functions in session thread */
/* GUI elements in this session (GTK specific) */
GtkWidget *main_window; /* the main window (with style ...) */
GtkWidget *pick_up_button; /* the pick up button to enable / disable */
GtkWidget *pick_up_label; /* the label on the pick up button */
GtkWidget *hang_up_button; /* the hang up button to enable / disable */
GtkWidget *hang_up_label; /* the label on the hang up button */
GtkWidget *dial_number_box; /* the dial number combo box */
GList *dial_number_history; /* the last called numbers */
unsigned int dial_number_history_maxlen; /* how many numbers to remember */
unsigned int dial_number_history_pointer; /* which one to use next if req */
GtkWidget *status_bar; /* the status bar */
gint phone_context_id; /* a context for the status bar */
GtkWidget *audio_warning; /* inside status bar */
gint audio_context_id; /* a context for audio_warning */
GtkWidget *main_window; /*!< the main window (with style ...) */
GtkWidget *pick_up_button; /*!< the pick up button to enable / disable */
GtkWidget *pick_up_label; /*!< the label on the pick up button */
GtkWidget *hang_up_button; /*!< the hang up button to enable / disable */
GtkWidget *hang_up_label; /*!< the label on the hang up button */
GtkWidget *dial_number_box; /*!< the dial number combo box */
GList *dial_number_history; /*!< the last called numbers */
unsigned int dial_number_history_maxlen; /*!< how many numbers to remember */
unsigned int dial_number_history_pointer; /*!< which one to use next if req */
GtkWidget *status_bar; /*!< the status bar */
gint phone_context_id; /*!< a context for the status bar */
GtkWidget *audio_warning; /*!< inside status bar */
gint audio_context_id; /*!< a context for audio_warning */
GtkWidget *llcheck; /* line level check widget inside status bar */
GtkWidget *llcheck_in; /* input level meter */
GtkWidget *llcheck_out; /* output level meter */
GtkWidget *llcheck_check_menu_item; /* state of line levels (status bar) */
GtkWidget *llcheck; /*!< line level check widget inside status bar */
GtkWidget *llcheck_in; /*!< input level meter */
GtkWidget *llcheck_out; /*!< output level meter */
GtkWidget *llcheck_check_menu_item; /*!< state of line levels (status bar) */
guint gtk_isdn_input_tag; /* these tags are saved to later remove handlers */
guint gtk_audio_input_tag;
GtkWidget *controlpad; /* key pad etc. */
GtkWidget *controlpad_check_menu_item; /* display state of control pad */
GtkWidget *mute_button; /* toggle button */
GtkWidget *muted_warning; /* show in status bar if muted */
gint muted_context_id; /* a context for the status bar */
GtkWidget *controlpad; /*!< key pad etc. */
GtkWidget *controlpad_check_menu_item; /*!< display state of control pad */
GtkWidget *mute_button; /*!< mute toggle button */
GtkWidget *muted_warning; /*!< show in status bar if muted */
gint muted_context_id; /*!< a context for mute in the status bar */
GtkWidget *record_checkbutton; /* recording checkbutton */
GtkWidget *record_checkbutton_local; /* local recording checkbutton */
GtkWidget *record_checkbutton_remote; /* remote recording checkbutton */
GtkWidget *record_checkbutton; /*!< recording checkbutton */
GtkWidget *record_checkbutton_local; /*!< local recording checkbutton */
GtkWidget *record_checkbutton_remote; /*!< remote recording checkbutton */
/* caller id related */
GtkWidget *cid; /* the caller id widget itself (to show/hide) */
GtkWidget *cid_check_menu_item; /* to handle state of cid monitor (show?) */
GtkWidget *cid_list; /* the list to hold the individual call data */
GtkWidget *cid_scrolled_window; /* the home of the clist with adjustments */
gint cid_num; /* number of rows in list */
gint cid_num_max; /* maximum number of rows in list */
time_t vcon_time; /* the start of conversation mode (for duration calc.) */
time_t ring_time; /* the first sign of the conversation (dial/ring) */
GtkWidget *cid; /*!< the caller id widget itself (to show/hide) */
GtkWidget *cid_check_menu_item; /*!< to handle state of cid monitor (show?) */
GtkWidget *cid_list; /*!< the list to hold the individual call data */
GtkWidget *cid_scrolled_window; /*!< the home of the clist with adjustments */
gint cid_num; /*!< number of rows in list */
gint cid_num_max; /*!< maximum number of rows in list */
time_t vcon_time; /*!< the start of conversation mode (for duration calc.) */
time_t ring_time; /*!< the first sign of the conversation (dial/ring) */
/* the symbols for the CList */
GdkPixmap *symbol_in_pixmap;
GdkBitmap *symbol_in_bitmap;
@ -186,87 +198,228 @@ typedef struct {
GdkPixmap *symbol_record_pixmap;
GdkBitmap *symbol_record_bitmap;
GtkWidget *menuitem_settings; /* Menu items to select / deselect */
GtkWidget *menuitem_settings; /*!< Menu items to select / deselect */
GtkWidget *menuitem_line_check;
/* ringing etc. */
guint effect_tag; /* remove this callback after e.g. ringing */
enum effect_t effect; /* which effect is currently been played? */
unsigned int effect_pos; /* sample position in effect */
char* effect_filename; /* the file to playback */
SNDFILE* effect_sndfile; /* the handle */
SF_INFO effect_sfinfo; /* info struct about effect_sndfile */
short* effect_sndfile_buffer;/*temporary buffer to construct playback output*/
int effect_sndfile_buffer_frames;/*number of frames of effect_sndfile_buffer*/
time_t effect_playback_start_time; /* start time of playback */
int touchtone_countdown_isdn; /* number of samples yet to play */
int touchtone_countdown_audio;
int touchtone_index; /* which touchtone */
thread_t thread_effect; /*!< effect thread, e.g., for ringing */
enum effect_t effect; /*!< which effect is currently been played? */
unsigned int effect_pos; /*!< sample position in effect */
char* effect_filename; /*!< the file to play back */
SNDFILE* effect_sndfile; /*!< sound file handle */
SF_INFO effect_sfinfo; /*!< info struct about effect_sndfile */
time_t effect_playback_start_time; /*!< start time of playback */
int touchtone_countdown_isdn; /*!< number of samples yet to play */
int touchtone_countdown_audio; /*!< number of samples yet to play */
int touchtone_index; /*!< which touchtone */
/* phone specific */
enum state_t state; /* which state we are currently in */
int hangup; /* remote hangup */
int aborted; /* i/o error (isdn or audio) */
enum state_t state; /*!< which state we are currently in */
char* msn; /* originating msn, allocated memory! */
char* msns; /* comma-separated list of msns to listen on, allocated memory!*/
char* msn; /*!< originating msn, allocated memory! */
char* msns; /*!< comma-separated list of msns to listen on, allocated memory!*/
int unanswered; /* unanswered calls for this session */
int unanswered; /*!< unanswered calls for this session */
/* some options (useful for options file handling) */
int option_save_options; /* save options on exit */
int option_release_devices; /* close sound devices while not needed */
int option_show_llcheck; /* show line level checks in main window */
int option_show_callerid; /* show callerid part in main window */
int option_show_controlpad; /* show control pad (key pad etc.) */
int option_muted; /* mute microphone (other party gets zeros) */
int option_record; /* record to file */
int option_record_local; /* record local channel */
int option_record_remote; /* record remote channel */
enum recording_format_t option_recording_format; /* recording file format */
int option_save_options; /*!< save options on exit */
int option_release_devices; /*!< close sound devices while not needed */
int option_show_llcheck; /*!< show line level checks in main window */
int option_show_callerid; /*!< show callerid part in main window */
int option_show_controlpad; /*!< show control pad (key pad etc.) */
int option_muted; /*!< mute microphone (other party gets zeros) */
int option_record; /*!< record to file */
int option_record_local; /*!< record local channel */
int option_record_remote; /*!< record remote channel */
enum recording_format_t option_recording_format; /*!< recording file format */
int option_calls_merge; /* merge isdnlog */
int option_calls_merge; /*!< merge isdnlog */
int option_calls_merge_max_days;
char *exec_on_incoming; /* string with command to execute on incoming call */
int option_popup; /* push main window to foreground on incoming call */
char *exec_on_incoming; /*!< string with command to execute on incoming call */
int option_popup; /*!< push main window to foreground on incoming call */
char* preset_names[SESSION_PRESET_SIZE]; /* names and numbers for */
char* preset_numbers[SESSION_PRESET_SIZE]; /* preset buttons */
char* preset_names[SESSION_PRESET_SIZE]; /*!< names for preset buttons */
char* preset_numbers[SESSION_PRESET_SIZE]; /*!< numbers for preset buttons */
int local_sock; /*!< unix domain socket for local server functionality */
guint gtk_local_input_tag; /*!< GTK tag for GTK main loop select */
int local_sock; /* unix domain socket for local server functionality */
guint gtk_local_input_tag; /* gtk tag for gtk main loop select */
} session_t;
/*!
* @brief Default session.
*/
extern session_t session;
int session_set_state(session_t *session, enum state_t state);
void session_io_handlers_start(session_t *session);
void session_io_handlers_stop(session_t *session);
int session_reset_audio(session_t *session);
int session_audio_open(session_t *session);
int session_audio_close(session_t *session);
int session_audio_init(session_t *session);
int session_audio_deinit(session_t *session);
/*!
* @brief Sets new state in session and GUI (also handles audio state).
*
* @param session session to use.
* @param state new session state (@see state_t).
* @return 0 on success, -1 otherwise (e.g., can't open audio device).
*/
int session_set_state(session_t *session, enum state_t state);
/*!
* @brief Activate/deactivate ISDN connection.
*
* @param session session to use.
* @param activate if nonzero, activate ISDN, otherwise deactivate.
* @return 0 on success, -1 otherwise (e.g., can't open ISDN device).
*/
int session_activate_isdn(session_t *session, unsigned int activate);
/*!
* @brief Set up GTK I/O handlers.
*
* @param session session to use.
*/
void session_io_handlers_start(session_t *session);
/*!
* @brief Remove GTK handlers.
*
* @param session session to use.
*/
void session_io_handlers_stop(session_t *session);
/*!
* @brief Set audio device state in session.
*
* @param session session.
* @param state requested audio state (@see audio_t).
* @return 0 on success, -1 otherwise.
*/
int session_set_audio_state(session_t *session, enum audio_t state);
/*!
* @brief Resets audio devices by closing and reopening.
*
* @param session session.
* @return 0 on success, -1 otherwise.
*
* @note Stop I/O handlers first!
* Only resets currently used device(s). To use another device,
* use session_set_audio_state() with AUDIO_DISCONNECTED to
* disconnect the old device first.
*/
int session_reset_audio(session_t *session);
/*!
* @brief Initialize a session (ISDN and audio devices) and read options file.
*
* @param session session, empty, to be filled.
* @param audio_device_name_in default name of input audio device.
* @param audio_device_name_out default name of output audio device.
* @param msn default MSN to use.
* @param msns default set of MSNs to listen on.
* @return 0 on success, -1 otherwise.
*
* @note The latter 4 parameters are only the defaults. They are normally
* overridden by the options file.
*/
int session_init(session_t *session,
char *audio_device_name_in,
char *audio_device_name_out,
char *msn, char *msns);
void session_audio_free(session_t *session);
/*!
* @brief Clean up a session (ISDN and audio devices).
*
* @param session session to clean up.
* @return 0 on success, -1 otherwise.
*/
int session_deinit(session_t *session);
/*!
* @brief Start an effect (effect_t) playing on the sound device.
*
* If kind == EFFECT_SOUNDFILE, session->effect_filename should be
* initialized. session_effect_stop() will free() it afterwards.
*
* @param session session.
* @param kind effect kind.
*/
void session_effect_start(session_t *session, enum effect_t kind);
/*!
* @brief Stop playing an effect.
*
* @param session session.
*/
void session_effect_stop(session_t *session);
void gtk_handle_isdn_input(gpointer data, gint fd,
GdkInputCondition condition);
void gtk_handle_audio_input(gpointer data, gint fd,
GdkInputCondition condition);
/*!
* @brief Start recording on a session.
*
* @param session session on which to record.
* @return 0 on success, -1 otherwise.
*/
int session_start_recording(session_t *session);
/*!
* @brief Initiates dialing to specified number.
*
* Changes contents of dial entry and simulates pick up button.
*
* @param session session.
* @param number number to dial.
*/
void session_make_call(session_t *session, char *number);
/*!
* @brief Callback from GTK on pick up button clicked.
*
* @param widget the button.
* @param data session.
*/
void gtk_handle_pick_up_button(GtkWidget *widget, gpointer data);
/*!
* @brief Callback from GTK on hang up button clicked.
*
* @note Also called on exit.
*
* @param widget the button, NULL when called directly (on exit).
* @param data session.
*/
void gtk_handle_hang_up_button(GtkWidget *widget, gpointer data);
/*!
* @brief Add line to history of dial number combo box as first row.
*
* Also check maximum size of history.
*
* @param session session.
* @param number number to add (will be copied).
*/
void session_history_add(session_t *session, const char *number);
/*!
* @brief Add line to history of dial number combo box as last row.
*
* Also check maximum size of history.
*
* @param session session.
* @param number number to add (will be copied).
*/
void session_history_append(session_t *session, char *number);
#endif /* session.h */

View File

@ -202,7 +202,7 @@ void settings_options_read(session_t *session) {
/* read isdn4linux config */
isdn_lexer_init(ISDN_CONFIG_FILENAME);
if (!isdn_in) {
fprintf(stderr, "Warning: Couldn't read ISDN config file.\n");
errprintf("Warning: Couldn't read ISDN config file.\n");
} else {
isdn_tree_node_t* node;
@ -238,7 +238,7 @@ void settings_options_read(session_t *session) {
}
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
errprintf("Warning: Couldn't get home dir.\n");
return;
}
@ -246,14 +246,14 @@ void settings_options_read(session_t *session) {
if (asprintf(&filename, "%s/." PACKAGE "/%s",
homedir, SETTINGS_OPTIONS_FILENAME) < 0)
{
fprintf(stderr,
errprintf(
"Warning: Couldn't allocate memory for options filename.\n");
return;
}
isdn_lexer_init(filename);
if (!isdn_in) {
fprintf(stderr, "Warning: No options file available.\n");
errprintf("Warning: No options file available.\n");
} else {
isdn_tree_node_t* node;
@ -268,20 +268,18 @@ void settings_options_read(session_t *session) {
while (node != NULL) {
switch (node->type) {
case ISDN_NODE_TYPE_ENTRY:
if (debug)
printf("Setting \"%s\" to \"%s\"...\n",
dbgprintf(1, "Setting \"%s\" to \"%s\"...\n",
node->name, node->content.value);
settings_option_set(session, node->name, node->content.value);
break;
case ISDN_NODE_TYPE_SECTION:
fprintf(stderr, "Warning: Unexpected section \"%s\".\n", node->name);
errprintf("Warning: Unexpected section \"%s\".\n", node->name);
break;
case ISDN_NODE_TYPE_SUBSECTION:
fprintf(stderr,
"Warning: Unexpected subsection \"%s\".\n", node->name);
errprintf("Warning: Unexpected subsection \"%s\".\n", node->name);
break;
default:
fprintf(stderr, "Unknown ISDN_NODE_TYPE\n");
errprintf("Unknown ISDN_NODE_TYPE\n");
}
node = node->next;
@ -302,7 +300,7 @@ void settings_options_write(session_t *session) {
FILE *f;
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
errprintf("Warning: Couldn't get home dir.\n");
return;
}
@ -311,7 +309,7 @@ void settings_options_write(session_t *session) {
if (asprintf(&filename, "%s/." PACKAGE "/%s",
homedir, SETTINGS_OPTIONS_FILENAME) < 0) {
fprintf(stderr,
errprintf(
"Warning: Couldn't allocate memory for options filename.\n");
return;
}
@ -405,10 +403,10 @@ void settings_options_write(session_t *session) {
session->option_calls_merge_max_days);
if (fclose(f) == EOF) {
fprintf(stderr, "Warning: Couldn't close options file.\n");
errprintf("Warning: Couldn't close options file.\n");
}
} else if (debug) {
fprintf(stderr, "Warning: Can't write to options file.\n");
} else {
dbgprintf(1, "Warning: Can't write to options file.\n");
}
free(filename);
}
@ -423,19 +421,18 @@ void settings_history_read(session_t *session) {
ssize_t got;
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
errprintf("Warning: Couldn't get home dir.\n");
return;
}
if (asprintf(&filename, "%s/." PACKAGE "/%s",
homedir, SETTINGS_HISTORY_FILENAME) < 0) {
fprintf(stderr,
errprintf(
"Warning: Couldn't allocate memory for history filename.\n");
return;
}
if (debug)
fprintf(stdout, "Info: History Filename: %s.\n", filename);
dbgprintf(1, "Info: History Filename: %s.\n", filename);
if ((f = fopen(filename, "r"))) {
do {
got = getline(&lineptr, &linesize, f);
@ -444,16 +441,15 @@ void settings_history_read(session_t *session) {
}
if (got > 0 && strlen(lineptr) > 0) {
session_history_append(session, lineptr);
if (debug)
fprintf(stdout, "Info: History Number: %s.\n", lineptr);
dbgprintf(1, "Info: History Number: %s.\n", lineptr);
}
} while (got > 0);
if (fclose(f) == EOF) {
fprintf(stderr, "Warning: Couldn't close history file.\n");
errprintf("Warning: Couldn't close history file.\n");
}
} else if (debug) {
fprintf(stderr, "Warning: No history file available.\n");
} else {
dbgprintf(1, "Warning: No history file available.\n");
}
free(filename);
@ -480,7 +476,7 @@ void settings_history_write(session_t *session) {
FILE *f;
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
errprintf("Warning: Couldn't get home dir.\n");
return;
}
@ -489,7 +485,7 @@ void settings_history_write(session_t *session) {
if (asprintf(&filename, "%s/." PACKAGE "/%s",
homedir, SETTINGS_HISTORY_FILENAME) < 0) {
fprintf(stderr,
errprintf(
"Warning: Couldn't allocate memory for history filename.\n");
return;
}
@ -499,10 +495,10 @@ void settings_history_write(session_t *session) {
settings_history_write_line, f);
if (fclose(f) == EOF) {
fprintf(stderr, "Warning: Couldn't close history file.\n");
errprintf("Warning: Couldn't close history file.\n");
}
} else if (debug) {
fprintf(stderr, "Warning: Can't write to history file.\n");
} else {
dbgprintf(1, "Warning: Can't write to history file.\n");
}
free(filename);
@ -514,13 +510,13 @@ void settings_callerid_read(session_t *session) {
char *filename;
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
errprintf("Warning: Couldn't get home dir.\n");
return;
}
if (asprintf(&filename, "%s/." PACKAGE "/%s",
homedir, SETTINGS_CALLERID_HISTORY_FILENAME) < 0) {
fprintf(stderr, "Warning: "
errprintf("Warning: "
"Couldn't allocate memory for caller id history filename.\n");
return;
}
@ -528,10 +524,10 @@ void settings_callerid_read(session_t *session) {
if ((callerid_in = fopen(filename, "r"))) {
callerid_parse(session);
if (fclose(callerid_in) == EOF) {
fprintf(stderr, "Warning: Couldn't close callerid history file.\n");
errprintf("Warning: Couldn't close callerid history file.\n");
}
} else if (debug) {
fprintf(stderr, "Warning: No caller id history file available.\n");
} else {
dbgprintf(1, "Warning: No caller id history file available.\n");
}
free(filename);
@ -552,7 +548,7 @@ void settings_callerid_write(session_t *session) {
gchar *duration;
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
errprintf("Warning: Couldn't get home dir.\n");
return;
}
@ -561,7 +557,7 @@ void settings_callerid_write(session_t *session) {
if (asprintf(&filename, "%s/." PACKAGE "/%s",
homedir, SETTINGS_CALLERID_HISTORY_FILENAME) < 0) {
fprintf(stderr, "Warning: "
errprintf("Warning: "
"Couldn't allocate memory for callerid history filename.\n");
return;
}
@ -583,16 +579,16 @@ void settings_callerid_write(session_t *session) {
fprintf (f, "%-19s|%-3s|%-20s|%-20s|%-10s\n",
date, type, from, to, duration);
if (debug > 1)
fprintf(stderr, "%-19s|%-3s|%-20s|%-20s|%-10s\n",
errprintf("%-19s|%-3s|%-20s|%-20s|%-10s\n",
date, type, from, to, duration);
}
if (fclose(f) == EOF) {
fprintf(stderr, "Warning: Couldn't close callerid history file.\n");
errprintf("Warning: Couldn't close callerid history file.\n");
}
} else if (debug) {
fprintf(stderr, "Warning: Can't write to callerid history file.\n");
} else {
dbgprintf(1, "Warning: Can't write to callerid history file.\n");
}
free(filename);

View File

@ -46,363 +46,300 @@
#include "globals.h"
#include "sound.h"
int default_audio_priorities[] = {AFMT_S16_LE,/* try formats in this order */ \
AFMT_S16_BE,\
AFMT_U16_LE,\
AFMT_U16_BE,\
AFMT_U8,\
AFMT_S8,\
/* ulaw at last because no native soundcard support assumed */ \
AFMT_MU_LAW,\
0}; /* end of list */
/*
* common initialization for a specific audio device
/* try formats in this order */
int default_audio_priorities[] = {SND_PCM_FORMAT_S16_LE,
SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_U16_LE,
SND_PCM_FORMAT_U16_BE,
SND_PCM_FORMAT_U8,
SND_PCM_FORMAT_S8,
/* alaw/ulaw at last because no native soundcard support assumed */
SND_PCM_FORMAT_A_LAW,
SND_PCM_FORMAT_MU_LAW,
0}; /* end of list */
/*--------------------------------------------------------------------------*/
/*!
* @brief Common initialization for a specific audio device
*
* NOTE: Assumes opened device, but leaves it in a state not able for further
* use when not successful. The device has to be closed and opened again.
* @param audio PCM descriptor of an audio device.
* @param format PCM format.
* @param channels number of PCM channels.
* @param speed requested/actual sampling rate (in/out).
* @param fragment_size requested/actual fragment size (in/out).
*
* input: audio_fd: valid file descriptor of an audio device,
* format, number of channels, requested sampling rate
* requested fragment sizes
*
* returns: 0 if successful, non-zero otherwise
*
* output: speed: actually sampling rate
* fragment_size: actually fragment size for this device
*
* @return 0 if successful, non-zero otherwise.
*/
int init_audio_device(int audio_fd, int format, int channels, int *speed,
int *fragment_size) {
int temp_format;
int temp_channels;
int temp_speed;
int formats_mask;
int fragment_arg;
static int init_audio_device(snd_pcm_t *audio,
int format,
int channels,
unsigned int *speed,
int *fragment_size)
{
int err;
unsigned int rspeed;
int dir;
unsigned int buffer_time, period_time;
snd_pcm_uframes_t period_size;
/* set fragment size */
fragment_arg = 0x7FFF0000 + logb(*fragment_size);
if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &fragment_arg)) {
perror("SNDCTL_DSP_SETFRAGMENT");
return(-1);
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);
if ((err = snd_pcm_hw_params_any(audio, hwparams)) < 0) {
errprintf("AUDIO: AUDIO: No audio configurations available: %s\n",
snd_strerror(err));
return err;
}
/* query supported audio formats */
if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &formats_mask) == -1) {
perror("SNDCTL_DSP_GETFMTS");
return(-1);
}
if (!(formats_mask & format)) { /* requested format not supported */
return(-1); /* caller should try another one */
if ((err = snd_pcm_hw_params_set_format(audio, hwparams, format)) < 0) {
errprintf("AUDIO: Audio sample format (%d) not available: %s\n",
format, snd_strerror(err));
return err;
}
/* audio format */
temp_format = format;
if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &temp_format) == -1) {
perror("SNDCTL_DSP_SETFMT");
return(-1);
if ((err = snd_pcm_hw_params_set_channels(audio, hwparams, channels)) < 0) {
errprintf("AUDIO: Audio channels count (%d) not available: %s\n",
channels, snd_strerror(err));
return err;
}
/* number of channels */
temp_channels = channels;
if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &temp_channels) == -1) {
perror("SNDCTL_DSP_CHANNELS");
return(-1);
rspeed = *speed;
if ((err = snd_pcm_hw_params_set_rate_near(audio, hwparams, &rspeed, 0)) < 0) {
errprintf("AUDIO: Audio speed %dHz not available: %s\n",
*speed, snd_strerror(err));
return err;
}
if (temp_channels != channels) {
fprintf(stderr, "Error: %d not supported as number of channels", channels);
return(-1);
if (rspeed != *speed) {
errprintf("AUDIO: Audio speed doesn't match (requested %dHz, got %dHz)\n",
*speed, rspeed);
return -EINVAL;
}
/* sampling rate */
temp_speed = *speed;
if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &temp_speed) == -1) {
perror("SNDCTL_DSP_SPEED");
/* rarely returned because of oss' liberal use of other possible speeds */
return(-1);
buffer_time = 150000;
if ((err = snd_pcm_hw_params_set_buffer_time_near(audio, hwparams, &buffer_time, &dir)) < 0) {
errprintf("AUDIO: Unable to set audio buffer time %d: %s\n",
buffer_time, snd_strerror(err));
return err;
}
*speed = temp_speed; /* actually used speed */
/* "verify" fragment size:
* let the driver actually calculate the fragment size
*/
if (ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &fragment_arg)) {
perror("SNDCTL_DSP_GETBLKSIZE");
return(-1);
period_time = 25000;
if ((err = snd_pcm_hw_params_set_period_time_near(audio, hwparams, &period_time, &dir)) < 0) {
errprintf("AUDIO: Unable to set audio period time %d: %s\n",
period_time, snd_strerror(err));
return err;
}
if (fragment_arg != *fragment_size)
fprintf(stderr, "Note: Using non-default fragment size %d.\n",
fragment_arg);
*fragment_size = fragment_arg;
if ((err = snd_pcm_hw_params_get_period_size_min(hwparams, &period_size, &dir)) < 0) {
errprintf("AUDIO: Unable to get audio period time: %s\n",
snd_strerror(err));
return err;
}
*fragment_size = period_size;
if ((err = snd_pcm_hw_params_set_access(audio, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
errprintf("AUDIO: Unable to set audio access mode: %s\n",
snd_strerror(err));
return err;
}
if ((err = snd_pcm_hw_params(audio, hwparams)) < 0) {
errprintf("AUDIO: Unable to set hw params for audio: %s\n",
snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params_current(audio, swparams)) < 0) {
errprintf("AUDIO: Unable to determine current swparams for audio: %s\n",
snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params_set_start_threshold(audio, swparams, period_size)) < 0) {
errprintf("AUDIO: Unable to set start threshold: %s\n",
snd_strerror(err));
return err;
}
/*
if ((err = snd_pcm_sw_params_set_sleep_min(audio, swparams, 0)) < 0) {
errprintf("AUDIO: Unable to set minimum sleep time: %s\n",
snd_strerror(err));
return err;
}
*/
if ((err = snd_pcm_sw_params_set_xfer_align(audio, swparams, 1)) < 0) {
errprintf("AUDIO: Unable to set transfer alignment: %s\n",
snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params_set_silence_size(audio, swparams, period_size * 2)) < 0) {
errprintf("AUDIO: Unable to set silence threshold: %s\n",
snd_strerror(err));
return err;
}
/*
if ((err = snd_pcm_sw_params_set_stop_threshold(audio, swparams, 0)) < 0) {
errprintf("AUDIO: Unable to set stop threshold: %s\n",
snd_strerror(err));
return err;
}
*/
//snd_pcm_sw_params_set_avail_min(Handle,swparams,Frames);
if ((err = snd_pcm_sw_params(audio, swparams)) < 0) {
printf("Unable to set sw params for audio: %s\n", snd_strerror(err));
return err;
}
#if 0
if ((err = snd_pcm_prepare(audio)) < 0) {
errprintf("AUDIO: Cannot prepare audio: %s\n", snd_strerror(err));
return -1;
}
#endif
return 0;
}
/*
* opens the audio device(s). if in_audio_device_name and out_audio_device_name
* are equal, full duplex mode is assumed
*
* input: in_audio_device_name, out_audio_device_name: strings
* channels: requestes number of channels (1 / 2)
* format_priorities: list of sorted integers with valid sound formats
* (e.g. AFMT_U8). the first working one will be used
* speed_in, speed_out: requested speeds (equal for full duplex)
*
* returns: 0 if successful
* -1 if not successful
*
* output:
* audio_fd_in, audio_fd_out: file descriptors
* fragment_size_in, fragment_size_out: device buffer fragment sizes
* format_in, format_out: actually used formats
* speed_in, speed_out: actually used speeds
*/
/*--------------------------------------------------------------------------*/
int open_audio_devices(char *in_audio_device_name,
char *out_audio_device_name,
int channels, int *format_priorities,
int *audio_fd_in, int *audio_fd_out,
snd_pcm_t **audio_in, snd_pcm_t **audio_out,
int *fragment_size_in, int *fragment_size_out,
int *format_in, int *format_out,
int *speed_in, int *speed_out) {
int audio_fd;
unsigned int *speed_in, unsigned int *speed_out)
{
int err;
int initresult;
int capabilities;
int *priority;
/* try to open the sound device */
if (strcmp(in_audio_device_name, out_audio_device_name) == 0) {
/* same device for input and output: full duplex */
for (initresult = -1, priority = format_priorities;
priority && initresult; priority++) { /* try different formats */
if ((audio_fd = open(in_audio_device_name, O_RDWR | O_NONBLOCK, 0))
== -1) {
perror(in_audio_device_name);
return(-1);
}
/* device is in non-blocking mode now */
*audio_fd_in = audio_fd;
*audio_fd_out = audio_fd;
/* set device to full duplex mode */
if (ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0)) {
perror("SNDCTL_DSP_SETDUPLEX");
return(-1);
}
if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &capabilities)) {
perror("SNDCTL_DSP_GETCAPS");
return(-1);
}
if (!(capabilities & DSP_CAP_DUPLEX)) {
fprintf(stderr, "Error: device doesn't support full duplex mode\n");
return(-1);
}
/* *speed_in assumed to be equal to *speed_out */
*format_in = *priority; /* get new format to try */
initresult = init_audio_device(audio_fd, *format_in, channels, speed_in,
fragment_size_in);
if (initresult) /* let's retry (and re-open) */
close_audio_devices(*audio_fd_in, *audio_fd_out);
}
*format_out = *format_in;
*speed_out = *speed_in;
*fragment_size_out = *fragment_size_in;
return initresult;
} else {
/* different devices for input and output */
for (initresult = -1, priority = format_priorities;
initresult && priority; priority++) {
if ((audio_fd = open(in_audio_device_name, O_RDONLY | O_NONBLOCK, 0)) ==
-1) {
perror(in_audio_device_name);
return(-1);
}
/* device is in non-blocking mode now */
*audio_fd_in = audio_fd;
*format_in = *priority;
initresult = init_audio_device(audio_fd, *format_in, channels, speed_in,
fragment_size_in);
if (initresult) /* let's retry (and re-open) */
if (close(audio_fd)) {
perror("close audio in device");
return -1;
}
}
if (initresult) return initresult; /* no chance */
for (initresult = -1, priority = format_priorities;
initresult && priority; priority++) {
if ((audio_fd = open(out_audio_device_name, O_WRONLY | O_NONBLOCK, 0))
== -1) {
perror(out_audio_device_name);
if (close(*audio_fd_in)) { /* error -> close audio_in device */
perror("close audio in device");
}
return(-1);
}
/* device is in non-blocking mode now */
*audio_fd_out = audio_fd;
*format_out = *priority;
initresult = init_audio_device(audio_fd, *format_out, channels,
speed_out, fragment_size_out);
if (initresult) /* let's retry (and re-open) */
if (close(audio_fd)) {
perror("close audio out device");
return -1;
}
}
return initresult;
}
}
/*
* stops audio playback and recording on specified file descriptors
*/
int audio_stop(int audio_fd_in, int audio_fd_out) {
/* Instead of ioctl SNDCTL_DSP_RESET, OSS Programmer's Guide recommends
* closing and reopening the devices (-> close/open_audio_devices).
* Here, we flush the input buffer and use SNDCTL_DSP_RESET
* to clean up for further recording, but on restart, old input comes again.
*/
int result;
int frag_size;
char *buf; /* temporary allocated buffer */
struct timeval tv;
fd_set fds;
int num; /* number of file descriptors with data (0/1) */
/* flush input buffer */
if (ioctl(audio_fd_in, SNDCTL_DSP_GETBLKSIZE, &frag_size))
fprintf(stderr, "Error obtaining audio input fragment size "
"with SNDCTL_DSP_GETBLKSIZE in audio_stop().\n");
else {
buf = (char *) malloc(frag_size);
tv.tv_sec = 0; /* return immediately */
tv.tv_usec = 0;
do {
FD_ZERO(&fds);
FD_SET(audio_fd_in, &fds);
num = select(FD_SETSIZE, &fds, 0, 0, &tv); /* return immediately */
if (num > 0)
read(audio_fd_in, buf, frag_size);
} while (num > 0);
if (num == -1)
perror("select at audio_stop");
free(buf);
}
/* stop device */
if (audio_fd_in == audio_fd_out)
result = ioctl(audio_fd_in, SNDCTL_DSP_RESET, 0);
else
result = ioctl(audio_fd_in, SNDCTL_DSP_RESET, 0) |
ioctl(audio_fd_out, SNDCTL_DSP_RESET, 0);
return result;
}
/*
* Closes specified input/output file descriptors
* returns 0 on success, -1 on error
*/
int close_audio_devices(int audio_fd_in, int audio_fd_out) {
audio_stop(audio_fd_in, audio_fd_out);
if (close(audio_fd_in) == -1) {
perror("close audio device");
return -1;
}
if (audio_fd_in != audio_fd_out)
if (close(audio_fd_out) == -1) {
perror("close audio device");
if ((err = snd_pcm_open(audio_in, in_audio_device_name, SND_PCM_STREAM_CAPTURE, 0/*SND_PCM_NONBLOCK*/)) < 0) {
errprintf("AUDIO: Audio recording device '%s' open error: %s, trying default\n",
in_audio_device_name, snd_strerror(err));
if ((err = snd_pcm_open(audio_in, "default", SND_PCM_STREAM_CAPTURE, 0/*SND_PCM_NONBLOCK*/)) < 0) {
errprintf("AUDIO: Audio recording device 'default' open error: %s\n",
snd_strerror(err));
return -1;
}
return 0;
}
if ((err = snd_pcm_open(audio_out, out_audio_device_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
errprintf("AUDIO: Audio playback device '%s' open error: %s, trying default\n",
out_audio_device_name, snd_strerror(err));
if ((err = snd_pcm_open(audio_out, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
errprintf("AUDIO: Audio playback device 'default' open error: %s\n",
snd_strerror(err));
return -1;
}
}
/* set format and sampling rate */
for (initresult = -1, priority = format_priorities;
initresult && priority; priority++) {
*format_in = *priority;
initresult = init_audio_device(*audio_in, *format_in, channels, speed_in, fragment_size_in);
}
if (initresult)
return initresult; /* no chance */
for (initresult = -1, priority = format_priorities;
initresult && priority; priority++) {
*format_out = *priority;
initresult = init_audio_device(*audio_out, *format_out, channels, speed_out, fragment_size_out);
}
#if 0
/* not implemented in ALSA */
if ((err = snd_pcm_link(*audio_out, *audio_in)) < 0) {
errprintf("AUDIO: Error linking in/out devices: %s\n",
snd_strerror(err));
return -1;
}
#endif
if (debug > 2) {
/* TODO: redirect output to dbgprintf */
snd_output_t *output;
snd_output_stdio_attach(&output, stderr, 0);
dbgprintf(2, "AUDIO: Dump of IN PCM:");
snd_pcm_dump(*audio_in, output);
dbgprintf(2, "AUDIO: Dump of OUT PCM:");
snd_pcm_dump(*audio_out, output);
}
return initresult;
}
/*
* returns number of bytes per sample for the specified
* OSS format (e.g. AFMT_U8)
*
* returns >= 1 on success, 0 otherwise (when format not supported)
*/
int sample_size_from_format(int format) {
int audio_stop(snd_pcm_t *audio_in, snd_pcm_t *audio_out) {
int err, err0;
err = 0;
if ((err0 = snd_pcm_drop(audio_in)) < 0) {
errprintf("AUDIO: Unable to reset audio capture: %s\n", snd_strerror(err0));
err = -1;
}
if ((err0 = snd_pcm_drop(audio_out)) < 0) {
errprintf("AUDIO: Unable to reset audio playback: %s\n", snd_strerror(err0));
err = -1;
}
return err;
}
/*--------------------------------------------------------------------------*/
int close_audio_devices(snd_pcm_t *audio_in, snd_pcm_t *audio_out)
{
int err, err0;
audio_stop(audio_in, audio_out);
err = 0;
if ((err0 = snd_pcm_close(audio_in)) < 0) {
errprintf("AUDIO: Unable to close audio capture: %s\n", snd_strerror(err0));
err = -1;
}
if ((err0 = snd_pcm_close(audio_out)) < 0) {
errprintf("AUDIO: Unable to close audio playback: %s\n", snd_strerror(err0));
err = -1;
}
return err;
}
/*--------------------------------------------------------------------------*/
int sample_size_from_format(int format)
{
switch(format) {
case AFMT_U8:
case AFMT_S8:
case AFMT_MU_LAW:
return 1;
case AFMT_S16_LE:
case AFMT_S16_BE:
case AFMT_U16_LE:
case AFMT_U16_BE:
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_S16_BE:
case SND_PCM_FORMAT_U16_LE:
case SND_PCM_FORMAT_U16_BE:
return 2;
case SND_PCM_FORMAT_U8:
case SND_PCM_FORMAT_S8:
case SND_PCM_FORMAT_MU_LAW:
case SND_PCM_FORMAT_A_LAW:
return 1;
default:
return 0;
}
}
/*
* returns the number of available fragents that can be read immediately
* from specified sound device file descriptor fd
*/
int audio_get_read_fragments_number(int fd) {
audio_buf_info info;
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) {
perror("SNDCTL_DSP_GETISPACE");
}
return info.fragments;
}
/*
* returns the total number of fragents available for OSS at
* specified sound device file descriptor fd for reading
*/
int audio_get_read_fragments_total(int fd) {
audio_buf_info info;
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) {
perror("SNDCTL_DSP_GETISPACE");
}
return info.fragstotal;
}
/*
* returns the number of fragents that can be written (non-blocking) to
* specified sound device file descriptor fd
*/
int audio_get_write_fragments_number(int fd) {
audio_buf_info info;
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == -1) {
perror("SNDCTL_DSP_GETOSPACE");
}
return info.fragments;
}
/*
* returns the total number of fragents available for OSS at
* specified sound device file descriptor fd for writing
*/
int audio_get_write_fragments_total(int fd) {
audio_buf_info info;
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == -1) {
perror("SNDCTL_DSP_GETOSPACE");
}
return info.fragstotal;
}
/*--------------------------------------------------------------------------*/

View File

@ -21,27 +21,60 @@
*
*/
#include <sys/soundcard.h>
#include <alsa/asoundlib.h>
#define DEFAULT_FRAGMENT_SIZE 1024
#define DEFAULT_AUDIO_DEVICE_NAME_IN "/dev/dsp"
#define DEFAULT_AUDIO_DEVICE_NAME_OUT "/dev/dsp"
#define DEFAULT_FRAGMENT_SIZE 128
#define DEFAULT_AUDIO_DEVICE_NAME_IN "default"
#define DEFAULT_AUDIO_DEVICE_NAME_OUT "default"
extern int default_audio_priorities[];
int init_audio_device(int audio_fd, int format, int channels, int *speed,
int *fragment_size);
/*!
* @brief Opens the audio device(s).
*
* @param in_audio_device_name name of input device.
* @param out_audio_device_name name of output device.
* @param channels requestes number of channels (1/2).
* @param format_priorities list of sorted integers with valid sound formats
* (e.g. SND_PCM_FORMAT_U8). the first working one will be used.
* @param audio_in filled with input PCM handle.
* @param audio_out filled with output PCM handle.
* @param fragment_size_in in/out fragment size for input.
* @param fragment_size_out in/out fragment size for output.
* @param speed_in in/out requested/actual input speed.
* @param speed_out in/out requested/actual output speed.
* @return 0 if successful, -1 on error.
*/
int open_audio_devices(char *in_audio_device_name,
char *out_audio_device_name,
int channels, int *format_priorities,
int *audio_fd_in, int *audio_fd_out,
snd_pcm_t **audio_in, snd_pcm_t **audio_out,
int *fragment_size_in, int *fragment_size_out,
int *format_in, int *format_out,
int *speed_in, int *speed_out);
int close_audio_devices(int audio_fd_in, int audio_fd_out);
int audio_stop(int audio_fd_in, int audio_fd_out);
unsigned int *speed_in, unsigned int *speed_out);
/*!
* @brief Close audio devices..
*
* @param audio_in input device to close.
* @param audio_out output device to close.
* @return 0 if successful, -1 on error.
*/
int close_audio_devices(snd_pcm_t *audio_in, snd_pcm_t *audio_out);
/*!
* @brief Stops audio playback and recording on specified devices.
*
* @param audio_in input device to stop.
* @param audio_out output device to stop.
* @return 0 if successful, -1 on error.
*/
int audio_stop(snd_pcm_t *audio_in, snd_pcm_t *audio_out);
/*!
* @brief Get number of bytes per sample for the specified format.
*
* @param format ALSA format (e.g. SND_PCM_FORMAT_U8).
* @return >= 1 on success, 0 otherwise (when format not supported).
*/
int sample_size_from_format(int format);
int audio_get_read_fragments_number(int fd);
int audio_get_read_fragments_total(int fd);
int audio_get_write_fragments_number(int fd);
int audio_get_write_fragments_total(int fd);

View File

@ -171,10 +171,10 @@ void execute(char *command) {
char *argv[] = {"sh", "-c", command, NULL};
if (result == -1) { /* error */
fprintf(stderr, "Fork error.\n");
errprintf("Fork error.\n");
} else if (result == 0) { /* we are the child */
if (execvp("sh", argv)) {
fprintf(stderr, "Exec error.\n");
errprintf("Exec error.\n");
exit(1);
}
}
@ -225,18 +225,16 @@ int touch_dotdir(void) {
char *filename;
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
errprintf("Warning: Couldn't get home dir.\n");
return -1;
}
if (asprintf(&filename, "%s/." PACKAGE, homedir) < 0) {
fprintf(stderr,
"Warning: Couldn't allocate memory for configuration directory.\n");
errprintf("Warning: Couldn't allocate memory for configuration directory.\n");
return -1;
}
if (touch_dir(filename) < 0) {
if (debug)
fprintf(stderr, "Warning: Can't reach configuration directory.\n");
dbgprintf(1, "Warning: Can't reach configuration directory.\n");
return -1;
}
free(filename);
@ -357,9 +355,20 @@ int output_codeset_set(char* codeset) {
if (!codeset)
codeset = output_codeset;
if (!bind_textdomain_codeset(PACKAGE, codeset)) {
fprintf(stderr, "Error setting gettext output codeset to %s.\n", codeset);
errprintf("Error setting gettext output codeset to %s.\n", codeset);
return -1;
}
return 0;
}
/*--------------------------------------------------------------------------*/
uint64_t microsec_time()
{
/* TODO: optimize this using HR timer, since gettimeofday is expensive */
struct timeval tv;
gettimeofday(&tv, 0);
return tv.tv_sec * ((uint64_t) 1000000) + tv.tv_usec;
}
/*--------------------------------------------------------------------------*/

View File

@ -32,6 +32,8 @@
#endif
#include <time.h>
#include <stdint.h>
#define SHORT_INTERVAL 10000
/* 10 milliseconds */
@ -53,4 +55,11 @@ char* filename_extension(char* fn);
int output_codeset_save(void);
int output_codeset_set(char* codeset);
/*!
* @brief Get time in microsecond.
*
* @return time in microseconds since arbitrary start point.
*/
uint64_t microsec_time();
#endif /* util.h */