func_volume.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2011, Digium, Inc.
  5. *
  6. * Joshua Colp <jcolp@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*! \file
  19. *
  20. * \brief Technology independent volume control
  21. *
  22. * \author Joshua Colp <jcolp@digium.com>
  23. *
  24. * \ingroup functions
  25. *
  26. */
  27. /*** MODULEINFO
  28. <support_level>core</support_level>
  29. ***/
  30. #include "asterisk.h"
  31. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  32. #include "asterisk/module.h"
  33. #include "asterisk/channel.h"
  34. #include "asterisk/pbx.h"
  35. #include "asterisk/utils.h"
  36. #include "asterisk/audiohook.h"
  37. #include "asterisk/app.h"
  38. /*** DOCUMENTATION
  39. <function name="VOLUME" language="en_US">
  40. <synopsis>
  41. Set the TX or RX volume of a channel.
  42. </synopsis>
  43. <syntax>
  44. <parameter name="direction" required="true">
  45. <para>Must be <literal>TX</literal> or <literal>RX</literal>.</para>
  46. </parameter>
  47. <parameter name="options">
  48. <optionlist>
  49. <option name="p">
  50. <para>Enable DTMF volume control</para>
  51. </option>
  52. </optionlist>
  53. </parameter>
  54. </syntax>
  55. <description>
  56. <para>The VOLUME function can be used to increase or decrease the <literal>tx</literal> or
  57. <literal>rx</literal> gain of any channel.</para>
  58. <para>For example:</para>
  59. <para>Set(VOLUME(TX)=3)</para>
  60. <para>Set(VOLUME(RX)=2)</para>
  61. <para>Set(VOLUME(TX,p)=3)</para>
  62. <para>Set(VOLUME(RX,p)=3)</para>
  63. </description>
  64. </function>
  65. ***/
  66. struct volume_information {
  67. struct ast_audiohook audiohook;
  68. int tx_gain;
  69. int rx_gain;
  70. unsigned int flags;
  71. };
  72. enum volume_flags {
  73. VOLUMEFLAG_CHANGE = (1 << 1),
  74. };
  75. AST_APP_OPTIONS(volume_opts, {
  76. AST_APP_OPTION('p', VOLUMEFLAG_CHANGE),
  77. });
  78. static void destroy_callback(void *data)
  79. {
  80. struct volume_information *vi = data;
  81. /* Destroy the audiohook, and destroy ourselves */
  82. ast_audiohook_lock(&vi->audiohook);
  83. ast_audiohook_detach(&vi->audiohook);
  84. ast_audiohook_unlock(&vi->audiohook);
  85. ast_audiohook_destroy(&vi->audiohook);
  86. ast_free(vi);
  87. return;
  88. }
  89. /*! \brief Static structure for datastore information */
  90. static const struct ast_datastore_info volume_datastore = {
  91. .type = "volume",
  92. .destroy = destroy_callback
  93. };
  94. static int volume_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
  95. {
  96. struct ast_datastore *datastore = NULL;
  97. struct volume_information *vi = NULL;
  98. int *gain = NULL;
  99. /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
  100. if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
  101. return 0;
  102. /* Grab datastore which contains our gain information */
  103. if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL)))
  104. return 0;
  105. vi = datastore->data;
  106. /* If this is DTMF then allow them to increase/decrease the gains */
  107. if (ast_test_flag(vi, VOLUMEFLAG_CHANGE)) {
  108. if (frame->frametype == AST_FRAME_DTMF) {
  109. /* Only use DTMF coming from the source... not going to it */
  110. if (direction != AST_AUDIOHOOK_DIRECTION_READ)
  111. return 0;
  112. if (frame->subclass.integer == '*') {
  113. vi->tx_gain += 1;
  114. vi->rx_gain += 1;
  115. } else if (frame->subclass.integer == '#') {
  116. vi->tx_gain -= 1;
  117. vi->rx_gain -= 1;
  118. }
  119. }
  120. }
  121. if (frame->frametype == AST_FRAME_VOICE) {
  122. /* Based on direction of frame grab the gain, and confirm it is applicable */
  123. if (!(gain = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? &vi->rx_gain : &vi->tx_gain) || !*gain)
  124. return 0;
  125. /* Apply gain to frame... easy as pi */
  126. ast_frame_adjust_volume(frame, *gain);
  127. }
  128. return 0;
  129. }
  130. static int volume_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
  131. {
  132. struct ast_datastore *datastore = NULL;
  133. struct volume_information *vi = NULL;
  134. int is_new = 0;
  135. /* Separate options from argument */
  136. AST_DECLARE_APP_ARGS(args,
  137. AST_APP_ARG(direction);
  138. AST_APP_ARG(options);
  139. );
  140. if (!chan) {
  141. ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
  142. return -1;
  143. }
  144. AST_STANDARD_APP_ARGS(args, data);
  145. ast_channel_lock(chan);
  146. if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL))) {
  147. ast_channel_unlock(chan);
  148. /* Allocate a new datastore to hold the reference to this volume and audiohook information */
  149. if (!(datastore = ast_datastore_alloc(&volume_datastore, NULL)))
  150. return 0;
  151. if (!(vi = ast_calloc(1, sizeof(*vi)))) {
  152. ast_datastore_free(datastore);
  153. return 0;
  154. }
  155. ast_audiohook_init(&vi->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
  156. vi->audiohook.manipulate_callback = volume_callback;
  157. ast_set_flag(&vi->audiohook, AST_AUDIOHOOK_WANTS_DTMF);
  158. is_new = 1;
  159. } else {
  160. ast_channel_unlock(chan);
  161. vi = datastore->data;
  162. }
  163. /* Adjust gain on volume information structure */
  164. if (ast_strlen_zero(args.direction)) {
  165. ast_log(LOG_ERROR, "Direction must be specified for VOLUME function\n");
  166. return -1;
  167. }
  168. if (!strcasecmp(args.direction, "tx")) {
  169. vi->tx_gain = atoi(value);
  170. } else if (!strcasecmp(args.direction, "rx")) {
  171. vi->rx_gain = atoi(value);
  172. } else {
  173. ast_log(LOG_ERROR, "Direction must be either RX or TX\n");
  174. }
  175. if (is_new) {
  176. datastore->data = vi;
  177. ast_channel_lock(chan);
  178. ast_channel_datastore_add(chan, datastore);
  179. ast_channel_unlock(chan);
  180. ast_audiohook_attach(chan, &vi->audiohook);
  181. }
  182. /* Add Option data to struct */
  183. if (!ast_strlen_zero(args.options)) {
  184. struct ast_flags flags = { 0 };
  185. ast_app_parse_options(volume_opts, &flags, NULL, args.options);
  186. vi->flags = flags.flags;
  187. } else {
  188. vi->flags = 0;
  189. }
  190. return 0;
  191. }
  192. static struct ast_custom_function volume_function = {
  193. .name = "VOLUME",
  194. .write = volume_write,
  195. };
  196. static int unload_module(void)
  197. {
  198. return ast_custom_function_unregister(&volume_function);
  199. }
  200. static int load_module(void)
  201. {
  202. return ast_custom_function_register(&volume_function);
  203. }
  204. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Technology independent volume control");