[asterisk-commits] russell: branch russell/chan_console r49091 - in /team/russell/chan_console: ...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Mon Jan 1 11:38:31 MST 2007


Author: russell
Date: Mon Jan  1 12:38:29 2007
New Revision: 49091

URL: http://svn.digium.com/view/asterisk?view=rev&rev=49091
Log:
- Add support for the "autoanswer" option
- Add support for playing various sounds to the console for ringing, busy,
  congestion, etc.

Modified:
    team/russell/chan_console/channels/chan_console.c
    team/russell/chan_console/configs/console.conf.sample

Modified: team/russell/chan_console/channels/chan_console.c
URL: http://svn.digium.com/view/asterisk/team/russell/chan_console/channels/chan_console.c?view=diff&rev=49091&r1=49090&r2=49091
==============================================================================
--- team/russell/chan_console/channels/chan_console.c (original)
+++ team/russell/chan_console/channels/chan_console.c Mon Jan  1 12:38:29 2007
@@ -77,7 +77,14 @@
 /*! \brief Mono Output */
 #define OUTPUT_CHANNELS  1
 
-#define CONFIG_FILE      "console.conf"
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+static const char *config_file = "console.conf";
 
 /*!
  * Each sound is made of 'datalen' samples of sound, repeated as needed to
@@ -135,10 +142,14 @@
 	/*! The number of samples sent for the current sound, mod the total number
 	 *  of samples for the sound plus trailing silence. */
 	unsigned int sampsent;
+	/*! Running = 1, Not running = 0 */
+	unsigned int streamstate:1;
 	/*! On-hook = 0, Off-hook = 1 */
 	unsigned int hookstate:1;
 	/*! Unmuted = 0, Muted = 1 */
 	unsigned int muted:1;
+	/*! Automatically answer incoming calls */
+	unsigned int autoanswer:1;
 	/*! Ignore context in the console dial CLI command */
 	unsigned int overridecontext:1;
 	/*! The PortAudio callback is being executed */
@@ -204,6 +215,9 @@
 {
 	PaError res;
 
+	if (pvt.streamstate)
+		return 0;
+
 	res = Pa_OpenDefaultStream(&pvt.stream, INPUT_CHANNELS, OUTPUT_CHANNELS, 
 		paInt16, SAMPLE_RATE, NUM_SAMPLES, console_callback, NULL);
 	if (res != paNoError) {
@@ -219,6 +233,8 @@
 		return -1;
 	}
 
+	pvt.streamstate = 1;
+
 	return 0;
 }
 
@@ -227,10 +243,14 @@
  */
 static int stop_stream(void)
 {
+	if (!pvt.streamstate)
+		return 0;
+
 	ast_mutex_lock(&pvt.lock);
 	Pa_AbortStream(pvt.stream);
 	Pa_CloseStream(pvt.stream);
 	pvt.stream = NULL;
+	pvt.streamstate = 0;
 	ast_cond_signal(&pvt.cond);
 	ast_mutex_unlock(&pvt.lock);
 
@@ -270,11 +290,14 @@
 
 	ast_jb_configure(chan, &global_jbconf);
 
-	if (state != AST_STATE_DOWN && ast_pbx_start(chan)) {
-		ast_log(LOG_WARNING, "Unable to start PBX on %s\n", chan->name);
-		chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
-		ast_hangup(chan);
-		chan = NULL;
+	if (state != AST_STATE_DOWN) {
+		if (ast_pbx_start(chan)) {
+			ast_log(LOG_WARNING, "Unable to start PBX on %s\n", chan->name);
+			chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+			ast_hangup(chan);
+			chan = NULL;
+		} else
+			start_stream();
 	}
 
 	return chan;
@@ -323,14 +346,34 @@
 	return 0;
 }
 
+static void set_cursound(struct console_pvt *pvt, int sound)
+{
+	unsigned int i = 0;
+
+	if (sound == -1) {
+		pvt->cursound = -1;
+		ast_verbose(" --- <(\"<) --- Sound stopped --- (>\")> ---\n");
+		return;
+	}
+
+	for (; sounds[i].ind != -1; i++) {
+		if (sounds[i].ind == sound)
+			pvt->cursound = i;
+	}
+
+	if (pvt->cursound > -1)
+		ast_verbose(" --- <(\"<) --- Sound playing should be '%s' --- (>\")> ---\n", 
+			sounds[pvt->cursound].desc);
+	else
+		ast_verbose(" --- <(\"<) --- Unknown sound requested on the console! --- (>\")> ---\n");
+}
+
 static int console_hangup(struct ast_channel *c)
 {
-	int res = 0;
-
 	ast_verbose(" --- <(\"<) --- Hangup on Console --- (>\")> ---\n");
 
 	pvt.hookstate = 0;
-	pvt.cursound = -1;
+	set_cursound(&pvt, -1);
 	c->tech_pvt = NULL;
 	pvt.owner = NULL;
 
@@ -345,18 +388,18 @@
 
 	stop_stream();
 
-	return res;
+	return 0;
 }
 
 static int console_answer(struct ast_channel *c)
 {
-	int res = 0;
+	int res;
 
 	ast_verbose(" --- <(\"<) --- Call from Console has been Answered --- (>\")> ---\n");
 
 	ast_mutex_lock(&pvt.lock);
 	ast_setstate(c, AST_STATE_UP);
-	pvt.cursound = -1;
+	set_cursound(&pvt, -1);
 	res = start_stream();
 	ast_mutex_unlock(&pvt.lock);
 
@@ -373,19 +416,25 @@
 static int console_call(struct ast_channel *c, char *dest, int timeout)
 {
 	struct ast_frame f = { 0, };
-	int res = 0;
 
 	ast_verbose(" --- <(\"<) --- Call to device '%s' on console from '%s' <%s> --- (>\")> ---\n",
 		dest, c->cid.cid_name, c->cid.cid_num);
-	ast_verbose(" --- <(\"<) --- Auto-answered --- (>\")> --- \n");
-	pvt.hookstate = 1;
-	f.frametype = AST_FRAME_CONTROL;
-	f.subclass = AST_CONTROL_ANSWER;
-	ast_queue_frame(c, &f);
-
-	res = start_stream();
-
-	return res;
+
+	if (pvt.autoanswer) {
+		ast_verbose(" --- <(\"<) --- Auto-answered --- (>\")> --- \n");
+		pvt.hookstate = 1;
+		f.frametype = AST_FRAME_CONTROL;
+		f.subclass = AST_CONTROL_ANSWER;
+		ast_queue_frame(c, &f);
+	} else {
+		ast_verbose(" --- <(\"<) --- Type 'answer' to answer, or use 'autoanswer' for future calls --- (>\")> ---\n");
+		f.frametype = AST_FRAME_CONTROL;
+		f.subclass = AST_CONTROL_RINGING;
+		ast_queue_frame(c, &f);
+		set_cursound(&pvt, AST_CONTROL_RING);
+	}
+
+	return start_stream();
 }
 
 static int console_write(struct ast_channel *chan, struct ast_frame *f)
@@ -404,18 +453,21 @@
 
 static int console_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
 {
+	struct console_pvt *pvt = chan->tech_pvt;
+
 	switch (cond) {
 	case AST_CONTROL_BUSY:
 	case AST_CONTROL_CONGESTION:
 	case AST_CONTROL_RINGING:
-		/* \todo Play appropriate sound to console */
+		set_cursound(pvt, cond);
 		break;
 	case AST_CONTROL_VIDUPDATE:
 	case -1:
+		set_cursound(pvt, -1);
 		break;
 	case AST_CONTROL_HOLD:
 		ast_verbose(" --- <(\"<) --- Console Has Been Placed on Hold --- (>\")> --- \n");
-		ast_moh_start(chan, data, pvt.mohinterpret);
+		ast_moh_start(chan, data, pvt->mohinterpret);
 		break;
 	case AST_CONTROL_UNHOLD:
 		ast_verbose(" --- <(\"<) --- Console Has Been Retrieved from Hold --- (>\")> --- \n");
@@ -438,60 +490,121 @@
 	return 0;
 }
 
-static int handle_output(void *output, unsigned long frame_count)
+/*!
+ * \brief Play the current sound
+ *
+ * Builds a frame from the high level description of the sounds,
+ * and passes it to the audio device.
+ * The actual sound is made of 1 or more sequences of sound samples
+ * (s->datalen, repeated to make s->samplen samples) followed by
+ * s->silencelen samples of silence. The position in the sequence is stored
+ * in pvt->sampsent, which goes between 0 .. s->samplen+s->silencelen.
+ * In case we fail to write a frame, don't update pvt->sampsent.
+ */
+static void send_sound(struct console_pvt *pvt, void *output, unsigned long frame_count)
+{
+	int ofs, l, start;
+	int l_sampsent = pvt->sampsent;
+	struct sound *s;
+
+	if (pvt->cursound < 0)		/* no sound to send */
+		return;
+
+	s = &sounds[pvt->cursound];
+
+	for (ofs = 0; ofs < frame_count; ofs += l) {
+		l = s->samplen - l_sampsent;	/* # of available samples */
+		if (l > 0) {
+			start = l_sampsent % s->datalen;	/* source offset */
+			l = MIN(l, frame_count - ofs);	/* don't overflow the frame */
+			l = MIN(l, s->datalen - start);	/* don't overflow the source */
+			bcopy(s->data + start, output + ofs, l * 2);
+			if (0)
+				ast_log(LOG_WARNING, "send_sound sound %d/%d of %d into %d\n", l_sampsent, l, s->samplen, ofs);
+			l_sampsent += l;
+		} else {				/* end of samples, maybe some silence */
+			static const short silence[NUM_SAMPLES] = { 0, };
+
+			l += s->silencelen;
+			if (l > 0) {
+				l = MIN(l, frame_count - ofs);
+				bcopy(silence, output + ofs, l * 2);
+				l_sampsent += l;
+			} else {			/* silence is over, restart sound if loop */
+				if (s->repeat == 0) {	/* last block */
+					pvt->cursound = -1;
+					if (ofs < frame_count)	/* pad with silence */
+						bcopy(silence, output + ofs, (frame_count - ofs) * 2);
+				}
+				l_sampsent = 0;
+			}
+		}
+	}
+	if (l > 0)
+		pvt->sampsent = l_sampsent;	/* update status */
+}
+static int handle_output(struct console_pvt *pvt, void *output, unsigned long frame_count)
 {
 	unsigned int src = 0;
 	unsigned int num_samples;
 	struct ast_frame *fr;
 
-	ast_mutex_lock(&pvt.lock);
-	if (pvt.write_dst) {
-		num_samples = (pvt.write_dst * 2) > frame_count ? pvt.write_dst * 2 : frame_count;
-		memcpy(output, &pvt.write_buf, num_samples * 2);
-		pvt.write_dst -= num_samples * 2;
+	ast_mutex_lock(&pvt->lock);
+	/* Fill the buffer with the current sound if one is set */
+	if (pvt->cursound != -1) {
+		send_sound(pvt, output, frame_count);
+		frame_count = 0;
+	}
+	/* Empty the write buffer */
+	if (frame_count && pvt->write_dst) {
+		num_samples = MIN(pvt->write_dst * 2, frame_count);
+		memcpy(output, pvt->write_buf, num_samples * 2);
+		pvt->write_dst -= num_samples * 2;
 		src += num_samples * 2;
 		frame_count -= num_samples;
 	}
+	/* Get the rest from queued frames */
 	while (frame_count) { 
-		while (!(fr = AST_LIST_REMOVE_HEAD(&pvt.frames, frame_list))) {
-			ast_cond_wait(&pvt.cond, &pvt.lock);
-			if (!pvt.hookstate) {
+		while (!(fr = AST_LIST_REMOVE_HEAD(&pvt->frames, frame_list))) {
+			ast_cond_wait(&pvt->cond, &pvt->lock);
+			if (!pvt->hookstate) {
 				if (fr)
 					ast_frfree(fr);
-				ast_mutex_unlock(&pvt.lock);
-				pvt.incallback = 0;
+				ast_mutex_unlock(&pvt->lock);
+				pvt->incallback = 0;
 				return paAbort;	
 			}
 		}
-		num_samples = fr->samples > frame_count ? frame_count : fr->samples;
+		num_samples = MIN(fr->samples, frame_count);
 		memcpy(output + src, fr->data, num_samples * 2);
 		frame_count -= num_samples;
+		/* Write what is left over to the write buffer */
 		if (num_samples != fr->samples) {
-			memcpy(&pvt.write_buf + pvt.write_dst, fr->data + num_samples * 2, (fr->samples - num_samples) * 2);
-			pvt.write_dst += (fr->samples - num_samples) * 2;
+			memcpy(&pvt->write_buf + pvt->write_dst, fr->data + num_samples * 2, (fr->samples - num_samples) * 2);
+			pvt->write_dst += (fr->samples - num_samples) * 2;
 		}
 		ast_frfree(fr);
 	}
-	ast_mutex_unlock(&pvt.lock);
+	ast_mutex_unlock(&pvt->lock);
 
 	return paContinue;
 }
 
-static int handle_input(const void *input, unsigned long frame_count)
-{
-	struct ast_frame *fr = &pvt.fr;
+static int handle_input(struct console_pvt *pvt, const void *input, unsigned long frame_count)
+{
+	struct ast_frame *fr = &pvt->fr;
 	unsigned int count = 0;
 
 	memset(fr, 0, sizeof(*fr));
-	memset(pvt.read_buf, 0, NUM_SAMPLES * 2 + AST_FRIENDLY_OFFSET);
-	fr->data = pvt.read_buf + AST_FRIENDLY_OFFSET;
+	memset(pvt->read_buf, 0, frame_count * 2 + AST_FRIENDLY_OFFSET);
+	fr->data = pvt->read_buf + AST_FRIENDLY_OFFSET;
 	fr->offset = AST_FRIENDLY_OFFSET;
 	fr->frametype = AST_FRAME_VOICE;
 	fr->subclass = AST_FORMAT_SLINEAR;
-	fr->samples = NUM_SAMPLES;
-	fr->datalen = NUM_SAMPLES * 2;
-	if (!pvt.muted) /* If muted, just leave the data all zeros */
-		memcpy(fr->data, input, NUM_SAMPLES * 2);
+	fr->samples = frame_count;
+	fr->datalen = frame_count * 2;
+	if (!pvt->muted) /* If muted, just leave the data all zeros */
+		memcpy(fr->data, input, frame_count * 2);
 
 	/* If the channel is in the process of being hung up, then it will be
 	 * locked while calling the console_hangup callback, and that could be
@@ -499,16 +612,16 @@
 	 * function returns.  Thus, locking the channel could result in a
 	 * deadlock.  So, we use the fact that the locks are recursive to test
 	 * locking the channel before queueing the frame. */
-	while (ast_channel_trylock(pvt.owner)) {
+	while (ast_channel_trylock(pvt->owner)) {
 		if (++count == 100) {
 			ast_log(LOG_DEBUG, "Dropping frame\n");
-			pvt.incallback = 0;
+			pvt->incallback = 0;
 			return paContinue;	
 		}
 		usleep(10);
 	}
-	ast_queue_frame(pvt.owner, fr);
-	ast_channel_unlock(pvt.owner);
+	ast_queue_frame(pvt->owner, fr);
+	ast_channel_unlock(pvt->owner);
 
 	return paContinue;
 }
@@ -518,15 +631,16 @@
 	PaStreamCallbackFlags flags, void *userData)
 {
 	int res = paContinue;
-
-	pvt.incallback = 1;
+	struct console_pvt *p = &pvt;
+
+	p->incallback = 1;
 
 	if (output)
-		res = handle_output(output, frame_count);
+		res = handle_output(p, output, frame_count);
 	if (res != paAbort && input)
-		res = handle_input(input, frame_count);
-
-	pvt.incallback = 0;
+		res = handle_input(p, input, frame_count);
+
+	p->incallback = 0;
 
 	return res;
 }
@@ -632,7 +746,7 @@
 		return CLI_FAILURE;
 	}
 	pvt.hookstate = 0;
-	pvt.cursound = -1;
+	set_cursound(&pvt, -1);
 	if (pvt.owner)
 		ast_queue_hangup(pvt.owner);
 
@@ -734,9 +848,10 @@
 	pvt.cid_num[0] = '\0';
 	pvt.cid_name[0] = '\0';
 	pvt.overridecontext = 0;
-
-	if (!(cfg = ast_config_load(CONFIG_FILE))) {
-		ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", CONFIG_FILE);
+	pvt.autoanswer = 0;
+
+	if (!(cfg = ast_config_load(config_file))) {
+		ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", config_file);
 		return -1;
 	}
 	v = ast_variable_browse(cfg, "general");
@@ -749,17 +864,18 @@
 			ast_copy_string(pvt.exten, v->value, sizeof(pvt.exten));
 		else if (!strcasecmp(v->name, "mohinterpret"))
 			ast_copy_string(pvt.mohinterpret, v->value, sizeof(pvt.mohinterpret));
+		else if (!strcasecmp(v->name, "language"))
+			ast_copy_string(pvt.language, v->value, sizeof(pvt.language));
+		else if (!strcasecmp(v->name, "callerid"))
+			ast_callerid_split(v->value, pvt.cid_name, sizeof(pvt.cid_name), 
+				pvt.cid_num, sizeof(pvt.cid_num));
 		else if (!strcasecmp(v->name, "overridecontext"))
 			pvt.overridecontext = ast_true(v->value) ? 1 : 0;
-		else if (!strcasecmp(v->name, "language"))
-			ast_copy_string(pvt.language, v->value, sizeof(pvt.language));
-		else if (!strcasecmp(v->name, "callerid")) {
-			ast_callerid_split(v->value, pvt.cid_name, sizeof(pvt.cid_name), 
-				pvt.cid_num, sizeof(pvt.cid_num));
-		} else {
+		else if (!strcasecmp(v->name, "autoanswer"))
+			pvt.autoanswer = ast_true(v->value) ? 1 : 0;
+		else
 			ast_log(LOG_WARNING, "Unknown option '%s' on line '%d' of '%s'!\n",
-				v->name, v->lineno, CONFIG_FILE);
-		}
+				v->name, v->lineno, config_file);
 	}
 	ast_config_destroy(cfg);
 

Modified: team/russell/chan_console/configs/console.conf.sample
URL: http://svn.digium.com/view/asterisk/team/russell/chan_console/configs/console.conf.sample?view=diff&rev=49091&r1=49090&r2=49091
==============================================================================
--- team/russell/chan_console/configs/console.conf.sample (original)
+++ team/russell/chan_console/configs/console.conf.sample Mon Jan  1 12:38:29 2007
@@ -4,27 +4,34 @@
 
 [general]
 
+; Set this option to "yes" to enable automatically answering calls on the
+; console.  This is very useful if the console is used as an intercom. 
+; The default value is "no".
+;
+; autoanswer = no
+
 ; Set the default context to use for outgoing calls.  This can be overridden by
 ; dialing some extension at context, unless the overridecontext option is enabled.
+; The default is "default".
 ;
-; context=default
+; context = default
 
 ; Set the default extension to use for outgoing calls.  The default is "s".
 ;
-; extension=s
+; extension = s
 
 ; Set the default CallerID for created channels.
 ; 
-; callerid=MyName Here <(256) 428-6000>
+; callerid = MyName Here <(256) 428-6000>
 
 ; Set the default language for created channels.
 ;
-; language=en
+; language = en
 
 ; If you set overridecontext to 'yes', then the whole dial string
 ; will be interpreted as an extension, which is extremely useful
 ; to dial SIP, IAX and other extensions which use the '@' character.
-; The default is 'no'.
+; The default is "no".
 ;
 ; overridecontext = no	; if 'no', the last @ will start the context
 			; if 'yes' the whole string is an extension.



More information about the asterisk-commits mailing list