Source

asterisk-voicechanger / app_voicechanger.c

Full commit
J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 

J.A. Roberts Tun… 0c30de3 







J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… f954393 












J.A. Roberts Tun… 671e8c4 







J.A. Roberts Tun… 0c30de3 














J.A. Roberts Tun… 671e8c4 





J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 


J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 


J.A. Roberts Tun… 0c30de3 

J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 






J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 





J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 











J.A. Roberts Tun… 0c30de3 

J.A. Roberts Tun… 671e8c4 


J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 



J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 


J.A. Roberts Tun… c154acd 




J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… c154acd 
J.A. Roberts Tun… 0c30de3 




J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… c154acd 
J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 

J.A. Roberts Tun… 671e8c4 



J.A. Roberts Tun… 0c30de3 




J.A. Roberts Tun… 671e8c4 

J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 


J.A. Roberts Tun… 671e8c4 













J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 
J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 









J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 












J.A. Roberts Tun… 671e8c4 

J.A. Roberts Tun… c154acd 



J.A. Roberts Tun… 671e8c4 
J.A. Roberts Tun… 0c30de3 
























J.A. Roberts Tun… 671e8c4 





J.A. Roberts Tun… 0c30de3 


J.A. Roberts Tun… 671e8c4 










/*
 * Voice Changer for Asterisk 1.6+
 * Copyright (C) 2005-2010 J.A. Roberts Tunney
 *
 * J.A. Roberts Tunney <jtunney@lobstertech.com>
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License version 2.0.
 *
 */

#define AST_MODULE "app_voicechanger"

#include <asterisk.h>
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/audiohook.h>
#include <asterisk/pbx.h>
#include <asterisk/module.h>
#include <asterisk/lock.h>
#include <asterisk/cli.h>
#include <asterisk/options.h>
#include <asterisk/app.h>
#include <asterisk/linkedlists.h>
#include <asterisk/utils.h>

#include "voicechanger.h"

struct voicechanger {
	void *st8k;
	void *st16k;
	struct ast_audiohook ah[1];
};

static const char *app = "VoiceChanger";
static const char *synopsis = "Adjusts the pitch of your voice";
static const char *desc = ""
"  VoiceChanger(<pitch>)\n\n"
"Specify a pitch in semitones.  Like -5 for deeper and 5 for higher.\n"
"";

static const char *stop_app = "StopVoiceChanger";
static const char *stop_synopsis = "Removes voice changer";
static const char *stop_desc = ""
"  StopVoiceChanger()\n\n"
"Removes the voice change effect from a channel, if there is any.\n"
"";

static struct ast_datastore_info dsinfo[1];

static int audio_callback(struct ast_audiohook *audiohook,
			  struct ast_channel *chan,
			  struct ast_frame *frame,
			  enum ast_audiohook_direction direction)
{
	struct ast_datastore *ds;
	struct voicechanger *vc;
	void *st;

	if (!audiohook || !chan || !frame ||
	    direction != AST_AUDIOHOOK_DIRECTION_READ) {
		return 0;
	}

	ast_channel_lock(chan);

	if (!(ds = ast_channel_datastore_find(chan, dsinfo, app)) ||
	    !(vc = (struct voicechanger *)ds->data) ||
	    !vc->st8k || !vc->st16k) {
		ast_channel_unlock(chan);
		ast_log(LOG_WARNING, "where my data at\n");
		return 0;
	}

	if (frame->data.ptr == NULL || frame->samples == 0 ||
	    frame->frametype != AST_FRAME_VOICE) {
		ast_channel_unlock(chan);
		ast_log(LOG_WARNING, "got incompatible frame\n");
		return 0;
	}

	switch (frame->subclass) {
	case AST_FORMAT_SLINEAR:
		st = vc->st8k;
		break;
	case AST_FORMAT_SLINEAR16:
		st = vc->st16k;
		break;
	default:
		ast_channel_unlock(chan);
		ast_log(LOG_WARNING, "bad audio type: %s\n",
			ast_getformatname(frame->subclass));
		return 0;
	}

	float fbuf[frame->samples];
	vc_voice_change(st, fbuf, (int16_t *)frame->data.ptr,
			frame->samples, frame->datalen);

	ast_channel_unlock(chan);

	return 0;
}

static void voicechanger_free(void *data)
{
	struct voicechanger *vc;
	if (data) {
		vc = (struct voicechanger *)data;
		vc_soundtouch_free(vc->st8k); vc->st8k = NULL;
		vc_soundtouch_free(vc->st16k); vc->st16k = NULL;
		ast_audiohook_detach(vc->ah);
		ast_audiohook_destroy(vc->ah);
		ast_free(data);
	}
	ast_log(LOG_DEBUG, "freed voice changer resources\n");
}

static int install_vc(struct ast_channel *chan, float pitch)
{
	struct ast_datastore *ds;
	struct voicechanger *vc;

	ast_log(LOG_DEBUG, "pitch is %f\n", pitch);
	if (-0.1 < pitch && pitch < 0.1) {
		return 0;
	}

	/* create soundtouch object */
	vc = ast_calloc(1, sizeof(struct voicechanger));
	if (!(vc->st8k = vc_soundtouch_create(8000, pitch)) ||
	    !(vc->st16k = vc_soundtouch_create(16000, pitch))) {
		ast_log(LOG_ERROR, "failed to make soundtouch\n");
		return -1;
	}

	/* create audiohook */
	ast_log(LOG_DEBUG, "Creating AudioHook object...\n");
	if (ast_audiohook_init(vc->ah, AST_AUDIOHOOK_TYPE_MANIPULATE,
			       "VoiceChanger")) {
		voicechanger_free(vc);
		ast_log(LOG_WARNING, "failed to make audiohook\n");
		return -1;
	}

	ast_audiohook_lock(vc->ah);
	vc->ah->manipulate_callback = audio_callback;
	ast_set_flag(vc->ah, AST_AUDIOHOOK_WANTS_DTMF);
	ast_audiohook_unlock(vc->ah);

	/* glue audiohook to channel */
	if (ast_audiohook_attach(chan, vc->ah) == -1) {
		voicechanger_free(vc);
		ast_log(LOG_WARNING, "failed to attach hook\n");
		return -1;
	}

	/* glue our data thing to channel */
	ds = ast_datastore_alloc(dsinfo, app);
	ds->data = vc;
	ast_channel_lock(chan);
	ast_channel_datastore_add(chan, ds);
	ast_channel_unlock(chan);

	return 0;
}

static int voicechanger_exec(struct ast_channel *chan, void *data)
{
	int rc;
	struct ast_module_user *u;
	float pitch;
	if (ast_strlen_zero(data)) {
		ast_log(LOG_WARNING, "voicechanger() missing argument\n");
		return -1;
	}
	pitch = strtof(data, NULL);
	u = ast_module_user_add(chan);
	rc = install_vc(chan, pitch);
	ast_module_user_remove(u);
	return rc;
}

static int uninstall_vc(struct ast_channel *chan)
{
	struct ast_datastore *ds;
	ast_log(LOG_DEBUG, "Detaching Voice Changer from channel...\n");
	ast_channel_lock(chan);
	ds = ast_channel_datastore_find(chan, dsinfo, app);
	if (ds) {
		ast_channel_datastore_remove(chan, ds);
		ast_datastore_free(ds);
	}
	ast_channel_unlock(chan);
	return 0;
}

static int stop_voicechanger_exec(struct ast_channel *chan, void *data)
{
	int rc;
	struct ast_module_user *u;
	u = ast_module_user_add(chan);
	rc = uninstall_vc(chan);
	ast_module_user_remove(u);
	return rc;
}

static int unload_module(void)
{
	int res;
	res  = ast_unregister_application(app);
	res |= ast_unregister_application(stop_app);
	ast_module_user_hangup_all();
	return res;
}

static int load_module(void)
{
	int res;
	dsinfo->type = app;
	dsinfo->destroy = voicechanger_free;
	res = ast_register_application(
		app, voicechanger_exec, synopsis, desc);
	res |= ast_register_application(
		stop_app, stop_voicechanger_exec, stop_synopsis, stop_desc);
	return res;
}

AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Voice Changer");

/* For Emacs:
 * Local Variables:
 * indent-tabs-mode:t
 * tab-width:8
 * c-basic-offset:8
 * c-file-style: nil
 * End:
 * For VIM:
 * vim:set softtabstop=8 shiftwidth=8 tabstop=8:
 */