diff options
-rw-r--r-- | firmware/drivers/audio/sdl.c | 47 | ||||
-rw-r--r-- | firmware/export/audiohw.h | 5 | ||||
-rw-r--r-- | firmware/export/config.h | 18 | ||||
-rw-r--r-- | firmware/export/hosted_codec.h | 5 | ||||
-rw-r--r-- | firmware/export/pcm-internal.h | 39 | ||||
-rw-r--r-- | firmware/export/pcm_sw_volume.h | 6 | ||||
-rw-r--r-- | firmware/pcm.c | 7 | ||||
-rw-r--r-- | firmware/pcm_sw_volume.c | 121 | ||||
-rw-r--r-- | firmware/target/hosted/sdl/pcm-sdl.c | 23 |
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" | 37 | static int sdl_volume_level(int volume) |
34 | |||
35 | void 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 | |||
43 | void audiohw_set_volume(int volume) | 46 | void 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 | 58 | void 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) |
58 | void audiohw_set_prescaler(int value) | 70 | void 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) |
67 | void audiohw_set_balance(int value) { (void)value; } | 80 | void 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) | ||
26 | AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -80, 0, 0) | ||
27 | #else | ||
24 | #define AUDIOHW_CAPS (MONO_VOL_CAP) | 28 | #define AUDIOHW_CAPS (MONO_VOL_CAP) |
25 | AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -99, 0, 0) | 29 | AUDIOHW_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 */ | ||
46 | void 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( | |||
84 | static FORCE_INLINE enum pcm_dma_status | 107 | static FORCE_INLINE enum pcm_dma_status |
85 | pcm_play_dma_status_callback(enum pcm_dma_status status) | 108 | pcm_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) |
97 | void pcm_play_dma_start_int(const void *addr, size_t size); | 120 | void pcm_play_dma_start_int(const void *addr, size_t size); |
98 | void pcm_play_dma_pause_int(bool pause); | 121 | void pcm_play_dma_pause_int(bool pause); |
99 | void pcm_play_dma_stop_int(void); | 122 | void pcm_play_dma_stop_int(void); |
100 | void pcm_play_stop_int(void); | 123 | void pcm_play_stop_int(void); |
101 | const void *pcm_play_dma_get_peak_buffer_int(int *count); | 124 | const 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); | |||
106 | static void pcm_play_pause_int(bool play); | 106 | static void pcm_play_pause_int(bool play); |
107 | void pcm_play_stop_int(void); | 107 | void 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 **/ | ||
111 | static inline void pcm_play_dma_start_int(const void *addr, size_t size) | 112 | static 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 | ||
155 | static void pcm_play_data_start_int(const void *addr, size_t size) | 156 | static 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 */ |
30 | static const void * volatile src_buf_addr = NULL; | 30 | static uint32_t vol_factor_l = 0, vol_factor_r = 0; |
31 | static 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 */ | ||
36 | static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2] | ||
37 | PCM_DBL_BUF_BSS MEM_ALIGN_ATTR; | ||
38 | static size_t pcm_dbl_buf_size[2]; | ||
39 | static int pcm_dbl_buf_num = 0; | ||
40 | static size_t frame_size; | ||
41 | static unsigned int frame_count, frame_err, frame_frac; | ||
42 | |||
43 | static int32_t vol_factor_l = 0, vol_factor_r = 0; | ||
44 | #ifdef AUDIOHW_HAVE_PRESCALER | 32 | #ifdef AUDIOHW_HAVE_PRESCALER |
45 | static int32_t prescale_factor = PCM_FACTOR_UNITY; | 33 | /* prescale factor set by pcm_set_prescaler */ |
34 | static 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 */ |
49 | static int32_t pcm_factor_l = 0, pcm_factor_r = 0; | 38 | static 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 */ |
58 | static 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 | |||
53 | static 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 | ||
60 | static inline | ||
61 | #endif | ||
62 | void 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 */ | ||
99 | static const void * volatile src_buf_addr = NULL; | ||
100 | static 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 */ | ||
105 | static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2] | ||
106 | PCM_DBL_BUF_BSS MEM_ALIGN_ATTR; | ||
107 | static size_t pcm_dbl_buf_size[2]; | ||
108 | static int pcm_dbl_buf_num = 0; | ||
109 | static size_t frame_size; | ||
110 | static unsigned int frame_count, frame_err, frame_frac; | ||
111 | |||
112 | /** Overrides of certain functions in pcm.c and pcm-internal.h **/ | ||
113 | |||
86 | bool pcm_play_dma_complete_callback(enum pcm_dma_status status, | 114 | bool 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 */ |
220 | static int32_t pcm_centibels_to_factor(int volume) | 253 | static 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 */ |
230 | static void pcm_sync_prescaler(void) | 267 | static 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 @@ | |||
51 | extern bool debug_audio; | 51 | extern bool debug_audio; |
52 | #endif | 52 | #endif |
53 | 53 | ||
54 | #ifdef HAVE_SW_VOLUME_CONTROL | ||
55 | static int sim_volume = SDL_MIX_MAXVOLUME; | ||
56 | #else | ||
57 | static int sim_volume = 0; | ||
58 | #endif | ||
59 | |||
60 | #if CONFIG_CODEC == SWCODEC | 54 | #if CONFIG_CODEC == SWCODEC |
61 | static int cvt_status = -1; | 55 | static 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 | ||
422 | void 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 */ |