diff --git a/doc/CHANGES-staging/func_volume.txt b/doc/CHANGES-staging/func_volume.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e73295b99a0e221c74dcfa64635ddeb31a89cc83
--- /dev/null
+++ b/doc/CHANGES-staging/func_volume.txt
@@ -0,0 +1,3 @@
+Subject: func_volume
+
+Accept decimal number as argument.
diff --git a/funcs/func_volume.c b/funcs/func_volume.c
index 533eb552f6510f10c46ec8858342116dd5ae8dae..85874794b40355e420415ae78bf6acc569f6912a 100644
--- a/funcs/func_volume.c
+++ b/funcs/func_volume.c
@@ -70,8 +70,8 @@
 
 struct volume_information {
 	struct ast_audiohook audiohook;
-	int tx_gain;
-	int rx_gain;
+	float tx_gain;
+	float rx_gain;
 	unsigned int flags;
 };
 
@@ -107,7 +107,7 @@ static int volume_callback(struct ast_audiohook *audiohook, struct ast_channel *
 {
 	struct ast_datastore *datastore = NULL;
 	struct volume_information *vi = NULL;
-	int *gain = NULL;
+	float *gain = NULL;
 
 	/* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
 	if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
@@ -141,7 +141,7 @@ static int volume_callback(struct ast_audiohook *audiohook, struct ast_channel *
 		if (!(gain = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? &vi->rx_gain : &vi->tx_gain) || !*gain)
 			return 0;
 		/* Apply gain to frame... easy as pi */
-		ast_frame_adjust_volume(frame, *gain);
+		ast_frame_adjust_volume_float(frame, *gain);
 	}
 
 	return 0;
@@ -193,9 +193,9 @@ static int volume_write(struct ast_channel *chan, const char *cmd, char *data, c
 	}
 
 	if (!strcasecmp(args.direction, "tx")) {
-		vi->tx_gain = atoi(value);
+		vi->tx_gain = atof(value);
 	} else if (!strcasecmp(args.direction, "rx")) {
-		vi->rx_gain = atoi(value);
+		vi->rx_gain = atof(value);
 	} else {
 		ast_log(LOG_ERROR, "Direction must be either RX or TX\n");
 	}
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index 780cba3e66866af1d9c63f4cd2fc221e0fd5dc22..0ead3b81c5dff8d7f1d7f3cb8722d6ced6b1beed 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -596,6 +596,14 @@ struct ast_frame *ast_frame_enqueue(struct ast_frame *head, struct ast_frame *f,
  */
 int ast_frame_adjust_volume(struct ast_frame *f, int adjustment);
 
+/*!
+  \brief Adjusts the volume of the audio samples contained in a frame.
+  \param f The frame containing the samples (must be AST_FRAME_VOICE and AST_FORMAT_SLINEAR)
+  \param adjustment The number of dB to adjust up or down.
+  \return 0 for success, non-zero for an error
+ */
+int ast_frame_adjust_volume_float(struct ast_frame *f, float adjustment);
+
 /*!
   \brief Sums two frames of audio samples.
   \param f1 The first frame (which will contain the result)
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index 2b332a51f04b7b0ca1d1fcd2528ed70011a5b443..10dfe83c953d0e10dbfc4591a67d089742a19373 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -375,11 +375,35 @@ static force_inline void ast_slinear_saturated_multiply(short *input, short *val
 		*input = (short) res;
 }
 
+static force_inline void ast_slinear_saturated_multiply_float(short *input, float *value)
+{
+	float res;
+
+	res = (float) *input * *value;
+	if (res > 32767)
+		*input = 32767;
+	else if (res < -32768)
+		*input = -32768;
+	else
+		*input = (short) res;
+}
+
 static force_inline void ast_slinear_saturated_divide(short *input, short *value)
 {
 	*input /= *value;
 }
 
+static force_inline void ast_slinear_saturated_divide_float(short *input, float *value)
+{
+	float res = (float) *input / *value;
+	if (res > 32767)
+		*input = 32767;
+	else if (res < -32768)
+		*input = -32768;
+	else
+		*input = (short) res;
+}
+
 #ifdef localtime_r
 #undef localtime_r
 #endif
diff --git a/main/frame.c b/main/frame.c
index f7a5222b07af513f51f689dfe47ed6e6fe31cada..4ae732b056035794dac6c823c071ec9b894b7a0a 100644
--- a/main/frame.c
+++ b/main/frame.c
@@ -43,6 +43,8 @@
 #include "asterisk/dsp.h"
 #include "asterisk/file.h"
 
+#include <math.h>
+
 #if (defined(LOW_MEMORY) || defined(MALLOC_DEBUG)) && !defined(NO_FRAME_CACHE)
 #define NO_FRAME_CACHE
 #endif
@@ -706,6 +708,31 @@ int ast_frame_adjust_volume(struct ast_frame *f, int adjustment)
 	return 0;
 }
 
+int ast_frame_adjust_volume_float(struct ast_frame *f, float adjustment)
+{
+	int count;
+	short *fdata = f->data.ptr;
+	float adjust_value = fabs(adjustment);
+
+	if ((f->frametype != AST_FRAME_VOICE) || !(ast_format_cache_is_slinear(f->subclass.format))) {
+		return -1;
+	}
+
+	if (!adjustment) {
+		return 0;
+	}
+
+	for (count = 0; count < f->samples; count++) {
+		if (adjustment > 0) {
+			ast_slinear_saturated_multiply_float(&fdata[count], &adjust_value);
+		} else if (adjustment < 0) {
+			ast_slinear_saturated_divide_float(&fdata[count], &adjust_value);
+		}
+	}
+
+	return 0;
+}
+
 int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2)
 {
 	int count;