aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/pcm_volume.c41
1 files changed, 40 insertions, 1 deletions
diff --git a/src/pcm_volume.c b/src/pcm_volume.c
index 2dff14ef5..cb1d82b00 100644
--- a/src/pcm_volume.c
+++ b/src/pcm_volume.c
@@ -58,16 +58,55 @@ pcm_volume_change_16(int16_t *buffer, unsigned num_samples, int volume)
}
}
+#if __i386__
+/**
+ * Optimized volume function for i386. Use the EDX:EAX 2*32 bit
+ * multiplication result instead of emulating 64 bit multiplication.
+ */
+static inline int32_t
+pcm_volume_sample_24(int32_t sample, int32_t volume, int32_t dither)
+{
+ int32_t result;
+
+ asm(/* edx:eax = sample * volume */
+ "imul %2\n"
+
+ /* "add %3, %1\n" dithering disabled for now, because we
+ have no overflow check - is dithering really important
+ here? */
+
+ /* eax = edx:eax / PCM_VOLUME_1 */
+ "sal $22, %%edx\n"
+ "shr $10, %1\n"
+ "or %%edx, %1\n"
+
+ : "=a"(result)
+ : "0"(sample), "r"(volume) /* , "r"(dither) */
+ : "edx"
+ );
+
+ return result;
+}
+#endif
+
static void
pcm_volume_change_24(int32_t *buffer, unsigned num_samples, int volume)
{
while (num_samples > 0) {
+#if __i386__
+ /* assembly version for i386 */
+ int32_t sample = *buffer;
+
+ sample = pcm_volume_sample_24(sample, volume,
+ pcm_volume_dither());
+#else
+ /* portable version */
int64_t sample = *buffer;
sample = (sample * volume + pcm_volume_dither() +
PCM_VOLUME_1 / 2)
/ PCM_VOLUME_1;
-
+#endif
*buffer++ = pcm_range(sample, 24);
--num_samples;
}