Index: channel.c
===================================================================
--- channel.c	(revision 13620)
+++ channel.c	(working copy)
@@ -972,6 +972,10 @@
 		chan->monitor->stop( chan, 0 );
 	}
 
+	if (chan->effect) {
+		ast_deactivate_effect(chan);
+	}
+
 	/* If there is native format music-on-hold state, free it */
 	if(chan->music_state)
 		ast_moh_cleanup(chan);
@@ -1363,6 +1367,7 @@
 		chan->generator->release(chan, chan->generatordata);
 	chan->generatordata = NULL;
 	chan->generator = NULL;
+
 	if (chan->cdr) {		/* End the CDR if it hasn't already */ 
 		ast_cdr_end(chan->cdr);
 		ast_cdr_detach(chan->cdr);	/* Post and Free the CDR */ 
@@ -1489,6 +1494,37 @@
 	return res;
 }
 
+void ast_deactivate_effect(struct ast_channel *chan)
+{
+	ast_mutex_lock(&chan->lock);
+	if (chan->effectdata) {
+		if (chan->effect && chan->effect->release) 
+			chan->effect->release(chan, chan->effectdata);
+	}
+	chan->effectdata = NULL;
+	chan->effect = NULL;
+
+	ast_mutex_unlock(&chan->lock);
+}
+
+int ast_activate_effect(struct ast_channel *chan, struct ast_effect *eff, void *params)
+{
+	int res = 0;
+ 	
+	ast_deactivate_effect (chan);
+ 
+ 	ast_mutex_lock(&chan->lock);
+ 
+ 	if (eff->alloc) {
+ 		chan->effectdata = eff->alloc(chan, params);
+ 	}
+ 	chan->effect = eff;
+ 	
+ 	ast_mutex_unlock(&chan->lock);
+ 
+ 	return res;
+}
+
 /*! \brief Wait for x amount of time on a file descriptor to have input.  */
 int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
 {
@@ -1968,8 +2004,17 @@
 						if (ast_writestream(chan->monitor->read_stream, f) < 0)
 							ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
 					}
+					
 				}
 
+				if (chan->effectdata) {
+					int res;
+					res = chan->effect->effect_read (chan, chan->effectdata, f);
+					if (res) {
+ 						ast_log(LOG_DEBUG, "Auto-deactivating effect\n");
+						ast_deactivate_effect(chan);
+	 				}
+			        }
 				if (chan->readtrans) {
 					if (!(f = ast_translate(chan->readtrans, f, 1)))
 						f = &ast_null_frame;
@@ -2333,7 +2378,14 @@
 							ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
 					}
 				}
-
+ 				if (f->frametype == AST_FRAME_VOICE && chan->effectdata) {
+ 					int res;
+ 				    	res = chan->effect->effect_write (chan, chan->effectdata, f);
+ 					if (res) {
+ 						ast_log(LOG_DEBUG, "Auto-deactivating effect\n");
+ 						ast_deactivate_effect(chan);
+ 					}
+				}
 				res = chan->tech->write(chan, f);
 			} else
 				res = 0;
@@ -2942,7 +2994,6 @@
 	free_translation(clone);
 	free_translation(original);
 
-
 	/* Unlink the masquerade */
 	original->masq = NULL;
 	clone->masqr = NULL;
Index: apps/app_pitch.c
===================================================================
--- apps/app_pitch.c	(revision 0)
+++ apps/app_pitch.c	(revision 0)
@@ -0,0 +1,331 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Pitch application -- scale pitch on voice channel
+ * 
+ * \ingroup applications
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION (__FILE__, "$Revision: 7221 $")
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+
+static char *tdesc = "Scale pitch on voice channel";
+
+static char *app = "Pitch";
+
+static char *synopsis = "Add effect to voice channel - scale pitch";
+
+static char *descrip =
+"  Pitch(read,write):  Add effect to voice channel.\n"
+" Parameters:\n"
+"  read      - scale channel read pitch by coefficient\n"
+"  write     - scale channel write pitch by coefficient\n";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+/*
+*	Sox part, taken from sox pitch plugin.
+*/
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#define PITCH_FLOAT double
+
+#define OCTAVA             ((PITCH_FLOAT)(1200.0e0))	/* in cents */
+#define THOUSAND           ((PITCH_FLOAT)(1000.0e0))	/* in units */
+#define HUNDRED            ((PITCH_FLOAT)(100.0e0))
+#define FOUR               ((PITCH_FLOAT)(4.0e0))
+#define TWO                ((PITCH_FLOAT)(2.0e0))
+#define ONE                ((PITCH_FLOAT)(1.0e0))
+#define HALF               ((PITCH_FLOAT)(0.5e0))
+#define QUARTER            ((PITCH_FLOAT)(0.25e0))
+#define ONESIXTH           ((PITCH_FLOAT)(1.0e0/6.0e0))
+#define ZERO               ((PITCH_FLOAT)(0.0e0))
+#define SAMPLE_RATE        8000
+
+typedef struct pitch_params {
+	float read_shift;
+	float write_shift;
+} pitch_params;
+
+typedef struct pitch_t {
+	PITCH_FLOAT read_rate;
+	PITCH_FLOAT write_rate;
+
+	int   step;
+	int   size;
+	int   overlap;
+
+	PITCH_FLOAT fade[160];
+	PITCH_FLOAT tmp [160];
+	PITCH_FLOAT acc [160];
+	PITCH_FLOAT buf [160];
+} pitch_t;
+
+static void *pitch_alloc (struct ast_channel *chan, void *p)
+{
+	pitch_t *pitch;
+	pitch_params *params = (pitch_params *)p;
+	int i;
+	register PITCH_FLOAT pi_2_step;
+
+	pitch = malloc (sizeof (pitch_t));
+
+	pitch->step = 160;
+	pitch->size = 160;
+	pitch->overlap = 2;
+
+        pitch->read_rate = pow(TWO, params->read_shift / OCTAVA);
+        pitch->write_rate = pow(TWO, params->write_shift / OCTAVA);
+
+        for (i=0; i<pitch->step; i++)
+            pitch->buf[i]  = 0.0;
+
+        pi_2_step = M_PI_2 / (pitch->step-1);
+        pitch->fade[0] = ONE; /* cos(0) == 1.0 */
+        for (i=1; i<pitch->step-1; i++)
+            pitch->fade[i]  = (PITCH_FLOAT) cos(pi_2_step*i);
+        pitch->fade[pitch->step-1] = ZERO; /* cos(PI/2) == 0.0 */
+
+	return (void *) pitch;
+}
+
+static void
+pitch_release (struct ast_channel *chan, void *data)
+{
+	free (data);
+	return;
+}
+
+static PITCH_FLOAT
+cub (PITCH_FLOAT fm1,
+     PITCH_FLOAT f0, PITCH_FLOAT f1, PITCH_FLOAT f2, PITCH_FLOAT x)
+{
+	/* a x^3 + b x^2 + c x + d */
+	register PITCH_FLOAT a, b, c, d;
+
+	d = f0;
+	b = HALF * (f1 + fm1) - f0;
+	a = ONESIXTH * (f2 - f1 + fm1 - f0 - FOUR * b);
+	c = f1 - a - b - d;
+
+	return ((a * x + b) * x + c) * x + d;
+}
+
+static void
+interpolation (pitch_t *pitch,
+	       PITCH_FLOAT * ibuf, int ilen,
+	       PITCH_FLOAT * out, int olen, PITCH_FLOAT rate)
+{
+	register int i, size;
+	register PITCH_FLOAT index;
+
+	size = pitch->step;
+
+	if (rate > 0) {	/* sweep forwards */
+		for (index = ZERO, i = 0; i < olen; i++, index += rate) {
+			register int ifl = (int) index;
+			register PITCH_FLOAT frac = index - ifl;
+
+			out[i] = cub (ibuf[ifl - 1], ibuf[ifl], ibuf[ifl + 1],
+				      ibuf[ifl + 2], frac);
+		}
+	} else { /* rate < 0, sweep backwards */
+		for (index = ilen - 1, i = olen - 1; i >= 0;
+		     i--, index += rate) {
+			register int ifl = (int) index;
+			register PITCH_FLOAT frac = index - ifl;
+
+			out[i] = cub (ibuf[ifl - 1], ibuf[ifl], ibuf[ifl + 1],
+				      ibuf[ifl + 2], frac);
+		}
+	}
+}
+
+/* from input buffer to acc
+ */
+static void
+process_buffer (pitch_t *pitch, PITCH_FLOAT rate)
+{
+	register int i;
+
+	/* forwards sweep */
+	interpolation (pitch,
+		       pitch->buf + pitch->overlap,
+		       pitch->step + pitch->overlap, 
+		       pitch->tmp, pitch->step,
+		       rate);
+
+	for (i = 0; i < pitch->step; i++)
+		pitch->acc[i] = pitch->fade[i] * pitch->tmp[i];
+
+	/* backwards sweep */
+	interpolation (pitch,
+		       pitch->buf, pitch->step + pitch->overlap,
+		       pitch->tmp, pitch->step, -rate);
+
+	for (i = 0; i < pitch->step; i++)
+		pitch->acc[i] +=
+			pitch->fade[pitch->step - i - 1] * pitch->tmp[i];
+}
+
+static int
+pitch_read (struct ast_channel *chan, void *data, struct ast_frame *f)
+{
+	pitch_t *pitch = (pitch_t *) data;
+	short *frame = (short *)f->data;
+	int i;
+	
+	if (f->datalen != 320) {
+		ast_log (LOG_ERROR, "Should get 160 samples in frame!\n");
+		return 1;
+	}
+	
+	for (i = 0; i < 160; i++)
+		    pitch->buf[i] = frame[i];
+	process_buffer (pitch, pitch->read_rate);
+	for (i = 0; i < 160; i++)
+		    frame[i] = pitch->acc[i];
+
+	return 0;
+}
+
+static int
+pitch_write (struct ast_channel *chan, void *data, struct ast_frame *f)
+{
+	pitch_t *pitch = (pitch_t *) data;
+	short *frame = (short *)f->data;
+	int i;
+	
+	if (f->datalen != 320) {
+		ast_log (LOG_ERROR, "Should get 160 samples in frame!\n");
+		return 1;
+	}
+	
+	for (i = 0; i < 160; i++)
+		    pitch->buf[i] = frame[i];
+	process_buffer (pitch, pitch->write_rate);
+	for (i = 0; i < 160; i++)
+		    frame[i] = pitch->acc[i];
+
+	return 0;
+}
+
+/*
+*	End of sox part
+*/
+
+static struct ast_effect pitch = {
+	alloc:pitch_alloc,
+	release:pitch_release,
+	effect_read:pitch_read,
+	effect_write:pitch_write,
+};
+
+static int
+pitch_exec (struct ast_channel *chan, void *data)
+{
+	struct localuser *u;
+	char *args;
+	pitch_params params = { 1.0, 1.0 };
+
+	if (!(args = ast_strdupa ((char *) data))) {
+		ast_log (LOG_ERROR, "Out of memory!\n");
+		return -1;
+	}
+
+	if (sscanf (args, "%g|%g", &params.read_shift, &params.write_shift) !=
+	    2) {
+		ast_log (LOG_ERROR,
+			 "Pitch scale coefficients should be specified!\n");
+		return -1;
+	}
+
+	LOCAL_USER_ADD (u);
+
+	ast_set_write_format (chan, AST_FORMAT_SLINEAR);
+	ast_set_read_format (chan, AST_FORMAT_SLINEAR);
+
+	if (ast_activate_effect (chan, &pitch, &params) < 0) {
+		ast_log (LOG_WARNING, "Failed to activate effect on '%s'\n",
+			 chan->name);
+		LOCAL_USER_REMOVE (u);
+		return -1;
+	}
+	LOCAL_USER_REMOVE (u);
+	return 0;
+}
+
+int
+unload_module (void)
+{
+	int res;
+
+	res = ast_unregister_application (app);
+
+	STANDARD_HANGUP_LOCALUSERS;
+
+	return res;
+}
+
+int
+load_module (void)
+{
+	return ast_register_application (app, pitch_exec, synopsis, descrip);
+}
+
+char *
+description (void)
+{
+	return tdesc;
+}
+
+int
+usecount (void)
+{
+	int res;
+	STANDARD_USECOUNT (res);
+	return res;
+}
+
+char *
+key ()
+{
+	return ASTERISK_GPL_KEY;
+}
Index: include/asterisk/channel.h
===================================================================
--- include/asterisk/channel.h	(revision 13620)
+++ include/asterisk/channel.h	(working copy)
@@ -144,6 +144,13 @@
 	int (*generate)(struct ast_channel *chan, void *data, int len, int samples);
 };
 
+struct ast_effect {
+	void *(*alloc)(struct ast_channel *chan, void *params);
+	void (*release)(struct ast_channel *chan, void *data);
+	int (*effect_read)(struct ast_channel *chan, void *data, struct ast_frame *f);
+	int (*effect_write)(struct ast_channel *chan, void *data, struct ast_frame *f);
+};
+
 /*! Structure for all kinds of caller ID identifications */
 struct ast_callerid {
 	/*! Malloc'd Dialed Number Identifier */
@@ -295,6 +302,10 @@
 	void *generatordata;
 	/*! Current active data generator */
 	struct ast_generator *generator;
+	/*! Current effect data if there is any */
+	void *effectdata;
+	/*! Current active data effect */
+	struct ast_effect *effect;
 
 	/*! Who are we bridged to, if we're bridged  Do not access directly,
 	    use ast_bridged_channel(chan) */
@@ -1037,6 +1048,12 @@
 /*! Deactive an active generator */
 void ast_deactivate_generator(struct ast_channel *chan);
 
+/*! Activate a given effect */
+int ast_activate_effect(struct ast_channel *chan, struct ast_effect *effect, void *params);
+
+/*! Deactive an active effect */
+void ast_deactivate_effect(struct ast_channel *chan);
+
 void ast_set_callerid(struct ast_channel *chan, const char *cidnum, const char *cidname, const char *ani);
 
 /*! Start a tone going */
Index: configs/extensions.conf.sample
===================================================================
--- configs/extensions.conf.sample	(revision 13620)
+++ configs/extensions.conf.sample	(working copy)
@@ -426,7 +426,16 @@
 exten => 600,n,Playback(demo-echodone)	; Let them know it's over
 exten => 600,n,Goto(s,6)		; Start over
 
+; Create an extension, 700, for pitch effect
 ;
+; 
+exten => 700,1,Pitch(1000, -1000)	; Shift 1000 for read, -1000 for write
+exten => 700,n,Playback(demo-echotest)	; Let them know what's going on
+exten => 700,n,Echo			; Do the echo test
+exten => 700,n,Playback(demo-echodone)	; Let them know it's over
+exten => 700,n,Goto(s,6)		; Start over
+
+;
 ;	You can use the Macro Page to intercom a individual user
 exten => 76245,1,Macro(page,SIP/Grandstream1)
 ; or if your peernames are the same as extensions
