Source Code: media/file/voicechanger/0.9/app_voicechanger.c
/* * 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: */