summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2013-04-23 03:20:49 -0400
committerMichael Sevakis <jethead71@rockbox.org>2013-04-27 06:59:27 +0200
commit08199cd6cb1e2c600eb16ce077cc308fee82de33 (patch)
tree5c33c39092a5f5aa65f6ac104f3fee54c5041519
parent370ed6de7c7596b2a1f6a2f99c8070bd179b4abd (diff)
downloadrockbox-08199cd6cb1e2c600eb16ce077cc308fee82de33.tar.gz
rockbox-08199cd6cb1e2c600eb16ce077cc308fee82de33.zip
Provide high resolution volume and prescaler to hosted targets.
HAVE_SW_VOLUME_CONTROL is required and at this time only affects the SDL targets using pcm-sdl.c. Enables balance control in SDL targets, unless mono volume is in use. Compiles software volume control as unbuffered when PCM_SW_VOLUME_UNBUFFERED is defined. This avoids the overhead and extra latency introduced by the double buffer when it is not needed. Use this config when the target's PCM driver is buffered and sufficient latency exists to perform safely the volume scaling. Simulated targets that are double-buffered when made as native targets remain so in the sim in order to run the same code. Change-Id: Ifa77d2d3ae7376c65afecdfc785a084478cb5ffb Reviewed-on: http://gerrit.rockbox.org/457 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested-by: Michael Sevakis <jethead71@rockbox.org>
-rw-r--r--firmware/drivers/audio/sdl.c47
-rw-r--r--firmware/export/audiohw.h5
-rw-r--r--firmware/export/config.h18
-rw-r--r--firmware/export/hosted_codec.h5
-rw-r--r--firmware/export/pcm-internal.h39
-rw-r--r--firmware/export/pcm_sw_volume.h6
-rw-r--r--firmware/pcm.c7
-rw-r--r--firmware/pcm_sw_volume.c121
-rw-r--r--firmware/target/hosted/sdl/pcm-sdl.c23
9 files changed, 177 insertions, 94 deletions
diff --git a/firmware/drivers/audio/sdl.c b/firmware/drivers/audio/sdl.c
index 9bb399b910..ae66380d16 100644
--- a/firmware/drivers/audio/sdl.c
+++ b/firmware/drivers/audio/sdl.c
@@ -23,36 +23,48 @@
23#include "config.h" 23#include "config.h"
24#include "sound.h" 24#include "sound.h"
25#include "pcm_sampr.h" 25#include "pcm_sampr.h"
26#ifdef HAVE_SW_VOLUME_CONTROL
27#include "pcm_sw_volume.h"
28#include "fixedpoint.h"
29#endif
26 30
27/** 31/**
28 * Audio Hardware api. Make them do nothing as we cannot properly simulate with 32 * Audio Hardware api. Make some of them do nothing as we cannot properly
29 * SDL. if we used DSP we would run code that doesn't actually run on the target 33 * simulate with SDL. if we used DSP we would run code that doesn't actually
34 * run on the target
30 **/ 35 **/
31
32#ifdef HAVE_SW_VOLUME_CONTROL 36#ifdef HAVE_SW_VOLUME_CONTROL
33#include "pcm_sw_volume.h" 37static int sdl_volume_level(int volume)
34
35void audiohw_set_volume(int vol_l, int vol_r)
36{ 38{
37 pcm_set_master_volume(vol_l, vol_r); 39 int shift = (1 - sound_numdecimals(SOUND_VOLUME)) << 16;
40 int minvol = fp_mul(sound_min(SOUND_VOLUME), fp_exp10(shift, 16), 16);
41 return volume <= minvol ? INT_MIN : volume;
38} 42}
43#endif /* HAVE_SW_VOLUME_CONTROL */
39 44
40#else /* ndef HAVE_SW_VOLUME_CONTROL */ 45#if defined(AUDIOHW_HAVE_MONO_VOLUME)
41
42
43void audiohw_set_volume(int volume) 46void audiohw_set_volume(int volume)
44{ 47{
45#if CONFIG_CODEC == SWCODEC 48#ifdef HAVE_SW_VOLUME_CONTROL
49 volume = sdl_volume_level(volume);
50 pcm_set_master_volume(volume, volume);
51#elif CONFIG_CODEC == SWCODEC
46 extern void pcm_set_mixer_volume(int volume); 52 extern void pcm_set_mixer_volume(int volume);
47 pcm_set_mixer_volume(volume); 53 pcm_set_mixer_volume(volume);
48#endif 54#endif
49 (void)volume; 55 (void)volume;
50} 56}
51#endif /* HAVE_SW_VOLUME_CONTROL */ 57#else /* !AUDIOHW_HAVE_MONO_VOLUME */
52 58void audiohw_set_volume(int vol_l, int vol_r)
53/** 59{
54 * stubs here, for the simulator 60#ifdef HAVE_SW_VOLUME_CONTROL
55 **/ 61 vol_l = sdl_volume_level(vol_l);
62 vol_r = sdl_volume_level(vol_r);
63 pcm_set_master_volume(vol_l, vol_r);
64#endif
65 (void)vol_l; (void)vol_r;
66}
67#endif /* AUDIOHW_HAVE_MONO_VOLUME */
56 68
57#if defined(AUDIOHW_HAVE_PRESCALER) 69#if defined(AUDIOHW_HAVE_PRESCALER)
58void audiohw_set_prescaler(int value) 70void audiohw_set_prescaler(int value)
@@ -62,7 +74,8 @@ void audiohw_set_prescaler(int value)
62#endif 74#endif
63 (void)value; 75 (void)value;
64} 76}
65#endif 77#endif /* AUDIOHW_HAVE_PRESCALER */
78
66#if defined(AUDIOHW_HAVE_BALANCE) 79#if defined(AUDIOHW_HAVE_BALANCE)
67void audiohw_set_balance(int value) { (void)value; } 80void audiohw_set_balance(int value) { (void)value; }
68#endif 81#endif
diff --git a/firmware/export/audiohw.h b/firmware/export/audiohw.h
index 843ac0c0c4..4379c20a79 100644
--- a/firmware/export/audiohw.h
+++ b/firmware/export/audiohw.h
@@ -111,11 +111,6 @@ struct sound_settings_info
111#include "hosted_codec.h" 111#include "hosted_codec.h"
112#endif 112#endif
113 113
114#if defined(SIMULATOR) && !defined(HAVE_SW_VOLUME_CONTROL)
115/* For now, without software volume control, sim only supports mono control */
116#define AUDIOHW_HAVE_MONO_VOLUME
117#endif
118
119/* convert caps into defines */ 114/* convert caps into defines */
120#ifdef AUDIOHW_CAPS 115#ifdef AUDIOHW_CAPS
121/* Tone controls */ 116/* Tone controls */
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 6a4a4648c8..7d7a18cc23 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -1139,6 +1139,24 @@ Lyre prototype 1 */
1139#define ROCKBOX_HAS_LOGDISKF 1139#define ROCKBOX_HAS_LOGDISKF
1140#endif 1140#endif
1141 1141
1142#if defined(HAVE_SDL_AUDIO) \
1143 && !(CONFIG_PLATFORM & PLATFORM_MAEMO5) \
1144 && !defined(HAVE_SW_VOLUME_CONTROL) \
1145 && CONFIG_CODEC == SWCODEC
1146/* SW volume is needed for accurate control and no double buffering should be
1147 * required. If target uses SW volume, then its definitions are used instead
1148 * so things are as on target. */
1149#define HAVE_SW_VOLUME_CONTROL
1150#define PCM_SW_VOLUME_UNBUFFERED /* pcm driver itself is buffered */
1151#ifdef SIMULATOR
1152/* For sim, nice res for ~ -127dB..+36dB that so far covers all targets */
1153#define PCM_SW_VOLUME_FRACBITS (24)
1154#else
1155/* For app, use fractional-only setup for -79..+0, no large-integer math */
1156#define PCM_SW_VOLUME_FRACBITS (16)
1157#endif /* SIMULATOR */
1158#endif /* default SDL SW volume conditions */
1159
1142/* null audiohw setting macro for when codec header is included for reasons 1160/* null audiohw setting macro for when codec header is included for reasons
1143 other than audio support */ 1161 other than audio support */
1144#define AUDIOHW_SETTING(name, us, nd, st, minv, maxv, defv, expr...) 1162#define AUDIOHW_SETTING(name, us, nd, st, minv, maxv, defv, expr...)
diff --git a/firmware/export/hosted_codec.h b/firmware/export/hosted_codec.h
index 72495709e8..f5e92ed297 100644
--- a/firmware/export/hosted_codec.h
+++ b/firmware/export/hosted_codec.h
@@ -21,8 +21,13 @@
21#ifndef HOSTED_CODEC_H 21#ifndef HOSTED_CODEC_H
22#define HOSTED_CODEC_H 22#define HOSTED_CODEC_H
23 23
24#if defined(HAVE_SDL_AUDIO) \
25 && !(CONFIG_PLATFORM & PLATFORM_MAEMO5)
26AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -80, 0, 0)
27#else
24#define AUDIOHW_CAPS (MONO_VOL_CAP) 28#define AUDIOHW_CAPS (MONO_VOL_CAP)
25AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -99, 0, 0) 29AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -99, 0, 0)
30#endif /* CONFIG_PLATFORM & PLATFORM_SDL */
26 31
27#if (CONFIG_PLATFORM & PLATFORM_ANDROID) 32#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
28/* Bass and treble tone controls */ 33/* Bass and treble tone controls */
diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h
index 03e5c5e6e7..2b73f64ef6 100644
--- a/firmware/export/pcm-internal.h
+++ b/firmware/export/pcm-internal.h
@@ -27,13 +27,36 @@
27#ifdef HAVE_SW_VOLUME_CONTROL 27#ifdef HAVE_SW_VOLUME_CONTROL
28/* Default settings - architecture may have other optimal values */ 28/* Default settings - architecture may have other optimal values */
29 29
30#define PCM_FACTOR_BITS 15 /* Allows -73 to +6dB gain, sans 64-bit math */ 30#ifndef PCM_SW_VOLUME_FRACBITS
31/* Allows -73 to +6dB gain, sans large integer math */
32#define PCM_SW_VOLUME_FRACBITS (15)
33#endif
34
35/* Constants selected based on integer math overflow avoidance */
36#if PCM_SW_VOLUME_FRACBITS <= 16
37#define PCM_FACTOR_MAX 0x00010000u
38#define PCM_FACTOR_UNITY (1u << PCM_SW_VOLUME_FRACBITS)
39#elif PCM_SW_VOLUME_FRACBITS <= 31
40#define PCM_FACTOR_MAX 0x80000000u
41#define PCM_FACTOR_UNITY (1u << PCM_SW_VOLUME_FRACBITS)
42#endif /* PCM_SW_VOLUME_FRACBITS */
43
44#ifdef PCM_SW_VOLUME_UNBUFFERED
45/* Copies buffer with volume scaling applied */
46void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t size);
47#define pcm_copy_buffer pcm_sw_volume_copy_buffer
48#else /* !PCM_SW_VOLUME_UNBUFFERED */
49#ifdef HAVE_SDL_AUDIO
50#define pcm_copy_buffer memcpy
51#endif
52#ifndef PCM_PLAY_DBL_BUF_SAMPLES
31#define PCM_PLAY_DBL_BUF_SAMPLES 1024 /* Max 4KByte chunks */ 53#define PCM_PLAY_DBL_BUF_SAMPLES 1024 /* Max 4KByte chunks */
54#endif
55#ifndef PCM_DBL_BUF_BSS
32#define PCM_DBL_BUF_BSS /* In DRAM, uncached may be better */ 56#define PCM_DBL_BUF_BSS /* In DRAM, uncached may be better */
33#define PCM_FACTOR_MIN 0x00000 /* Minimum final factor */ 57#endif
34#define PCM_FACTOR_MAX 0x10000 /* Maximum final factor */ 58#endif /* PCM_SW_VOLUME_UNBUFFERED */
35 59
36#define PCM_FACTOR_UNITY (1 << PCM_FACTOR_BITS)
37#endif /* HAVE_SW_VOLUME_CONTROL */ 60#endif /* HAVE_SW_VOLUME_CONTROL */
38 61
39#define PCM_SAMPLE_SIZE (2 * sizeof (int16_t)) 62#define PCM_SAMPLE_SIZE (2 * sizeof (int16_t))
@@ -84,22 +107,22 @@ static FORCE_INLINE enum pcm_dma_status pcm_play_call_status_cb(
84static FORCE_INLINE enum pcm_dma_status 107static FORCE_INLINE enum pcm_dma_status
85pcm_play_dma_status_callback(enum pcm_dma_status status) 108pcm_play_dma_status_callback(enum pcm_dma_status status)
86{ 109{
87#ifdef HAVE_SW_VOLUME_CONTROL 110#if defined(HAVE_SW_VOLUME_CONTROL) && !defined(PCM_SW_VOLUME_UNBUFFERED)
88 extern enum pcm_dma_status 111 extern enum pcm_dma_status
89 pcm_play_dma_status_callback_int(enum pcm_dma_status status); 112 pcm_play_dma_status_callback_int(enum pcm_dma_status status);
90 return pcm_play_dma_status_callback_int(status); 113 return pcm_play_dma_status_callback_int(status);
91#else 114#else
92 return pcm_play_call_status_cb(status); 115 return pcm_play_call_status_cb(status);
93#endif /* HAVE_SW_VOLUME_CONTROL */ 116#endif /* HAVE_SW_VOLUME_CONTROL && !PCM_SW_VOLUME_UNBUFFERED */
94} 117}
95 118
96#ifdef HAVE_SW_VOLUME_CONTROL 119#if defined(HAVE_SW_VOLUME_CONTROL) && !defined(PCM_SW_VOLUME_UNBUFFERED)
97void pcm_play_dma_start_int(const void *addr, size_t size); 120void pcm_play_dma_start_int(const void *addr, size_t size);
98void pcm_play_dma_pause_int(bool pause); 121void pcm_play_dma_pause_int(bool pause);
99void pcm_play_dma_stop_int(void); 122void pcm_play_dma_stop_int(void);
100void pcm_play_stop_int(void); 123void pcm_play_stop_int(void);
101const void *pcm_play_dma_get_peak_buffer_int(int *count); 124const void *pcm_play_dma_get_peak_buffer_int(int *count);
102#endif /* HAVE_SW_VOLUME_CONTROL */ 125#endif /* HAVE_SW_VOLUME_CONTROL && !PCM_SW_VOLUME_UNBUFFERED */
103 126
104/* Called by the bottom layer ISR when more data is needed. Returns true 127/* Called by the bottom layer ISR when more data is needed. Returns true
105 * if a new buffer is available, false otherwise. */ 128 * if a new buffer is available, false otherwise. */
diff --git a/firmware/export/pcm_sw_volume.h b/firmware/export/pcm_sw_volume.h
index b5d70654a1..5088eadeb1 100644
--- a/firmware/export/pcm_sw_volume.h
+++ b/firmware/export/pcm_sw_volume.h
@@ -21,6 +21,12 @@
21#ifndef PCM_SW_VOLUME_H 21#ifndef PCM_SW_VOLUME_H
22#define PCM_SW_VOLUME_H 22#define PCM_SW_VOLUME_H
23 23
24/***
25 ** Note: Only PCM drivers that are themselves buffered should use the
26 ** PCM_SW_VOLUME_UNBUFFERED configuration. This may be part of the platform,
27 ** the library or a hardware necessity. Normally, it shouldn't be used and
28 ** only the port developer can properly decide.
29 **/
24#ifdef HAVE_SW_VOLUME_CONTROL 30#ifdef HAVE_SW_VOLUME_CONTROL
25 31
26#include <audiohw.h> 32#include <audiohw.h>
diff --git a/firmware/pcm.c b/firmware/pcm.c
index 6bf0e12c8d..e095ab2cea 100644
--- a/firmware/pcm.c
+++ b/firmware/pcm.c
@@ -106,8 +106,9 @@ static void pcm_play_data_start_int(const void *addr, size_t size);
106static void pcm_play_pause_int(bool play); 106static void pcm_play_pause_int(bool play);
107void pcm_play_stop_int(void); 107void pcm_play_stop_int(void);
108 108
109#ifndef HAVE_SW_VOLUME_CONTROL 109#if !defined(HAVE_SW_VOLUME_CONTROL) || defined(PCM_SW_VOLUME_UNBUFFERED)
110/** Standard hw volume control functions - otherwise, see pcm_sw_volume.c **/ 110/** Standard hw volume/unbuffered control functions - otherwise, see
111 ** pcm_sw_volume.c **/
111static inline void pcm_play_dma_start_int(const void *addr, size_t size) 112static inline void pcm_play_dma_start_int(const void *addr, size_t size)
112{ 113{
113 pcm_play_dma_start(addr, size); 114 pcm_play_dma_start(addr, size);
@@ -150,7 +151,7 @@ bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
150 pcm_play_stop_int(); 151 pcm_play_stop_int();
151 return false; 152 return false;
152} 153}
153#endif /* ndef HAVE_SW_VOLUME_CONTROL */ 154#endif /* !HAVE_SW_VOLUME_CONTROL || PCM_SW_VOLUME_UNBUFFERED */
154 155
155static void pcm_play_data_start_int(const void *addr, size_t size) 156static void pcm_play_data_start_int(const void *addr, size_t size)
156{ 157{
diff --git a/firmware/pcm_sw_volume.c b/firmware/pcm_sw_volume.c
index 473e63c7cb..eb77fe0c6b 100644
--- a/firmware/pcm_sw_volume.c
+++ b/firmware/pcm_sw_volume.c
@@ -26,63 +26,91 @@
26#include "fixedpoint.h" 26#include "fixedpoint.h"
27#include "pcm_sw_volume.h" 27#include "pcm_sw_volume.h"
28 28
29/* source buffer from client */ 29/* volume factors set by pcm_set_master_volume */
30static const void * volatile src_buf_addr = NULL; 30static uint32_t vol_factor_l = 0, vol_factor_r = 0;
31static size_t volatile src_buf_rem = 0;
32 31
33#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_SAMPLE_SIZE)
34
35/* double buffer and frame length control */
36static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2]
37 PCM_DBL_BUF_BSS MEM_ALIGN_ATTR;
38static size_t pcm_dbl_buf_size[2];
39static int pcm_dbl_buf_num = 0;
40static size_t frame_size;
41static unsigned int frame_count, frame_err, frame_frac;
42
43static int32_t vol_factor_l = 0, vol_factor_r = 0;
44#ifdef AUDIOHW_HAVE_PRESCALER 32#ifdef AUDIOHW_HAVE_PRESCALER
45static int32_t prescale_factor = PCM_FACTOR_UNITY; 33/* prescale factor set by pcm_set_prescaler */
34static uint32_t prescale_factor = PCM_FACTOR_UNITY;
46#endif /* AUDIOHW_HAVE_PRESCALER */ 35#endif /* AUDIOHW_HAVE_PRESCALER */
47 36
48/* pcm scaling factors */ 37/* final pcm scaling factors */
49static int32_t pcm_factor_l = 0, pcm_factor_r = 0; 38static uint32_t pcm_factor_l = 0, pcm_factor_r = 0;
50
51#define PCM_FACTOR_CLIP(f) \
52 MAX(MIN((f), PCM_FACTOR_MAX), PCM_FACTOR_MIN)
53#define PCM_SCALE_SAMPLE(f, s) \
54 (((f) * (s) + PCM_FACTOR_UNITY/2) >> PCM_FACTOR_BITS)
55 39
40/***
41 ** Volume scaling routine
42 ** If unbuffered, called externally by pcm driver
43 **/
56 44
57/* TODO: #include CPU-optimized routines and move this to /firmware/asm */ 45/* TODO: #include CPU-optimized routines and move this to /firmware/asm */
58static inline void pcm_copy_buffer(int16_t *dst, const int16_t *src, 46
59 size_t size) 47#if PCM_SW_VOLUME_FRACBITS <= 16
48#define PCM_F_T int32_t
49#else
50#define PCM_F_T int64_t /* Requires large integer math */
51#endif /* PCM_SW_VOLUME_FRACBITS */
52
53static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s)
60{ 54{
61 int32_t factor_l = pcm_factor_l; 55 return (f * s + (PCM_F_T)PCM_FACTOR_UNITY/2) >> PCM_SW_VOLUME_FRACBITS;
62 int32_t factor_r = pcm_factor_r; 56}
63 57
64 if (LIKELY(factor_l <= PCM_FACTOR_UNITY && factor_r <= PCM_FACTOR_UNITY)) 58/* Copies buffer with volume scaling applied */
59#ifndef PCM_SW_VOLUME_UNBUFFERED
60static inline
61#endif
62void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t size)
63{
64 int16_t *d = dst;
65 const int16_t *s = src;
66 uint32_t factor_l = pcm_factor_l;
67 uint32_t factor_r = pcm_factor_r;
68
69 if (factor_l == PCM_FACTOR_UNITY && factor_r == PCM_FACTOR_UNITY)
70 {
71 /* Both unity */
72 memcpy(dst, src, size);
73 }
74 else if (LIKELY(factor_l <= PCM_FACTOR_UNITY &&
75 factor_r <= PCM_FACTOR_UNITY))
65 { 76 {
66 /* All cut or unity */ 77 /* Either cut, both <= UNITY */
67 while (size) 78 while (size)
68 { 79 {
69 *dst++ = PCM_SCALE_SAMPLE(factor_l, *src++); 80 *d++ = pcm_scale_sample(factor_l, *s++);
70 *dst++ = PCM_SCALE_SAMPLE(factor_r, *src++); 81 *d++ = pcm_scale_sample(factor_r, *s++);
71 size -= PCM_SAMPLE_SIZE; 82 size -= PCM_SAMPLE_SIZE;
72 } 83 }
73 } 84 }
74 else 85 else
75 { 86 {
76 /* Any positive gain requires clipping */ 87 /* Either positive gain, requires clipping */
77 while (size) 88 while (size)
78 { 89 {
79 *dst++ = clip_sample_16(PCM_SCALE_SAMPLE(factor_l, *src++)); 90 *d++ = clip_sample_16(pcm_scale_sample(factor_l, *s++));
80 *dst++ = clip_sample_16(PCM_SCALE_SAMPLE(factor_r, *src++)); 91 *d++ = clip_sample_16(pcm_scale_sample(factor_r, *s++));
81 size -= PCM_SAMPLE_SIZE; 92 size -= PCM_SAMPLE_SIZE;
82 } 93 }
83 } 94 }
84} 95}
85 96
97#ifndef PCM_SW_VOLUME_UNBUFFERED
98/* source buffer from client */
99static const void * volatile src_buf_addr = NULL;
100static size_t volatile src_buf_rem = 0;
101
102#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_SAMPLE_SIZE)
103
104/* double buffer and frame length control */
105static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2]
106 PCM_DBL_BUF_BSS MEM_ALIGN_ATTR;
107static size_t pcm_dbl_buf_size[2];
108static int pcm_dbl_buf_num = 0;
109static size_t frame_size;
110static unsigned int frame_count, frame_err, frame_frac;
111
112/** Overrides of certain functions in pcm.c and pcm-internal.h **/
113
86bool pcm_play_dma_complete_callback(enum pcm_dma_status status, 114bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
87 const void **addr, size_t *size) 115 const void **addr, size_t *size)
88{ 116{
@@ -155,7 +183,7 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status)
155 183
156 pcm_dbl_buf_num ^= 1; 184 pcm_dbl_buf_num ^= 1;
157 pcm_dbl_buf_size[pcm_dbl_buf_num] = size; 185 pcm_dbl_buf_size[pcm_dbl_buf_num] = size;
158 pcm_copy_buffer(pcm_dbl_buf[pcm_dbl_buf_num], addr, size); 186 pcm_sw_volume_copy_buffer(pcm_dbl_buf[pcm_dbl_buf_num], addr, size);
159 187
160 return PCM_DMAST_OK; 188 return PCM_DMAST_OK;
161} 189}
@@ -216,27 +244,36 @@ const void * pcm_play_dma_get_peak_buffer_int(int *count)
216 return NULL; 244 return NULL;
217} 245}
218 246
247#endif /* PCM_SW_VOLUME_UNBUFFERED */
248
249
250/** Internal **/
251
219/* Return the scale factor corresponding to the centibel level */ 252/* Return the scale factor corresponding to the centibel level */
220static int32_t pcm_centibels_to_factor(int volume) 253static uint32_t pcm_centibels_to_factor(int volume)
221{ 254{
222 if (volume == PCM_MUTE_LEVEL) 255 if (volume == PCM_MUTE_LEVEL)
223 return 0; /* mute */ 256 return 0; /* mute */
224 257
225 /* Centibels -> fixedpoint */ 258 /* Centibels -> fixedpoint */
226 return fp_factor(fp_div(volume, 10, PCM_FACTOR_BITS), PCM_FACTOR_BITS); 259 return (uint32_t)fp_factor(fp_div(volume, 10, PCM_SW_VOLUME_FRACBITS),
260 PCM_SW_VOLUME_FRACBITS);
227} 261}
228 262
263
264/** Public functions **/
265
229/* Produce final pcm scale factor */ 266/* Produce final pcm scale factor */
230static void pcm_sync_prescaler(void) 267static void pcm_sync_prescaler(void)
231{ 268{
232 int32_t factor_l = vol_factor_l; 269 uint32_t factor_l = vol_factor_l;
233 int32_t factor_r = vol_factor_r; 270 uint32_t factor_r = vol_factor_r;
234#ifdef AUDIOHW_HAVE_PRESCALER 271#ifdef AUDIOHW_HAVE_PRESCALER
235 factor_l = fp_mul(prescale_factor, factor_l, PCM_FACTOR_BITS); 272 factor_l = fp_mul(prescale_factor, factor_l, PCM_SW_VOLUME_FRACBITS);
236 factor_r = fp_mul(prescale_factor, factor_r, PCM_FACTOR_BITS); 273 factor_r = fp_mul(prescale_factor, factor_r, PCM_SW_VOLUME_FRACBITS);
237#endif 274#endif
238 pcm_factor_l = PCM_FACTOR_CLIP(factor_l); 275 pcm_factor_l = MIN(factor_l, PCM_FACTOR_MAX);
239 pcm_factor_r = PCM_FACTOR_CLIP(factor_r); 276 pcm_factor_r = MIN(factor_r, PCM_FACTOR_MAX);
240} 277}
241 278
242#ifdef AUDIOHW_HAVE_PRESCALER 279#ifdef AUDIOHW_HAVE_PRESCALER
diff --git a/firmware/target/hosted/sdl/pcm-sdl.c b/firmware/target/hosted/sdl/pcm-sdl.c
index beefc7818c..290dffbb95 100644
--- a/firmware/target/hosted/sdl/pcm-sdl.c
+++ b/firmware/target/hosted/sdl/pcm-sdl.c
@@ -51,12 +51,6 @@
51extern bool debug_audio; 51extern bool debug_audio;
52#endif 52#endif
53 53
54#ifdef HAVE_SW_VOLUME_CONTROL
55static int sim_volume = SDL_MIX_MAXVOLUME;
56#else
57static int sim_volume = 0;
58#endif
59
60#if CONFIG_CODEC == SWCODEC 54#if CONFIG_CODEC == SWCODEC
61static int cvt_status = -1; 55static int cvt_status = -1;
62 56
@@ -177,10 +171,10 @@ static void write_to_soundcard(struct pcm_udata *udata)
177 cvt.len = rd * pcm_sample_bytes; 171 cvt.len = rd * pcm_sample_bytes;
178 cvt.buf = (Uint8 *) malloc(cvt.len * cvt.len_mult); 172 cvt.buf = (Uint8 *) malloc(cvt.len * cvt.len_mult);
179 173
180 memcpy(cvt.buf, pcm_data, cvt.len); 174 pcm_copy_buffer(cvt.buf, pcm_data, cvt.len);
181 175
182 SDL_ConvertAudio(&cvt); 176 SDL_ConvertAudio(&cvt);
183 SDL_MixAudio(udata->stream, cvt.buf, cvt.len_cvt, sim_volume); 177 memcpy(udata->stream, cvt.buf, cvt.len_cvt);
184 178
185 udata->num_in = cvt.len / pcm_sample_bytes; 179 udata->num_in = cvt.len / pcm_sample_bytes;
186 udata->num_out = cvt.len_cvt / pcm_sample_bytes; 180 udata->num_out = cvt.len_cvt / pcm_sample_bytes;
@@ -223,8 +217,8 @@ static void write_to_soundcard(struct pcm_udata *udata)
223 } 217 }
224 } else { 218 } else {
225 udata->num_in = udata->num_out = MIN(udata->num_in, udata->num_out); 219 udata->num_in = udata->num_out = MIN(udata->num_in, udata->num_out);
226 SDL_MixAudio(udata->stream, pcm_data, 220 pcm_copy_buffer(udata->stream, pcm_data,
227 udata->num_out * pcm_sample_bytes, sim_volume); 221 udata->num_out * pcm_sample_bytes);
228#ifdef DEBUG 222#ifdef DEBUG
229 if (udata->debug != NULL) { 223 if (udata->debug != NULL) {
230 fwrite(pcm_data, sizeof(Uint8), udata->num_out * pcm_sample_bytes, 224 fwrite(pcm_data, sizeof(Uint8), udata->num_out * pcm_sample_bytes,
@@ -418,13 +412,4 @@ void pcm_play_dma_postinit(void)
418{ 412{
419} 413}
420 414
421#ifndef HAVE_SW_VOLUME_CONTROL
422void pcm_set_mixer_volume(int volume)
423{
424 int minvol = sound_min(SOUND_VOLUME);
425 int volrange = sound_max(SOUND_VOLUME) - minvol;
426 sim_volume = SDL_MIX_MAXVOLUME * (volume / 10 - minvol) / volrange;
427}
428#endif /* HAVE_SW_VOLUME_CONTROL */
429
430#endif /* CONFIG_CODEC == SWCODEC */ 415#endif /* CONFIG_CODEC == SWCODEC */