summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
Diffstat (limited to 'audio')
-rw-r--r--audio/audio_hw.c112
1 files changed, 92 insertions, 20 deletions
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
index b781b29..54ee3c8 100644
--- a/audio/audio_hw.c
+++ b/audio/audio_hw.c
@@ -125,8 +125,10 @@
#undef PLAYBACK_MMAP // was #define
/* short period (aka low latency) in milliseconds */
#define SHORT_PERIOD_MS 4 // was 22
-/* long period (aka low power or deep buffer) in milliseconds */
-#define LONG_PERIOD_MS 308
+/* deep buffer short period (screen on) in milliseconds */
+#define DEEP_BUFFER_SHORT_PERIOD_MS 22
+/* deep buffer long period (screen off) in milliseconds */
+#define DEEP_BUFFER_LONG_PERIOD_MS 308
/* Constraint imposed by ABE: for playback, all period sizes must be multiples of 24 frames
* = 500 us at 48 kHz. It seems to be either 48 or 96 for capture, or maybe it is because the
@@ -143,12 +145,22 @@
/* number of frames per short period (low latency) */
#define SHORT_PERIOD_SIZE (ABE_BASE_FRAME_COUNT * SHORT_PERIOD_MULTIPLIER)
-/* number of short periods in a long period */
-#define LONG_PERIOD_MULTIPLIER (LONG_PERIOD_MS / SHORT_PERIOD_MS)
-/* number of frames per long period (low power) */
-#define LONG_PERIOD_SIZE (SHORT_PERIOD_SIZE * LONG_PERIOD_MULTIPLIER)
-/* number of periods for low power playback */
-#define PLAYBACK_LONG_PERIOD_COUNT 2
+/* number of base blocks in a short deep buffer period (screen on) */
+#define DEEP_BUFFER_SHORT_PERIOD_MULTIPLIER (DEEP_BUFFER_SHORT_PERIOD_MS * MULTIPLIER_FACTOR)
+/* number of frames per short deep buffer period (screen on) */
+#define DEEP_BUFFER_SHORT_PERIOD_SIZE (ABE_BASE_FRAME_COUNT * DEEP_BUFFER_SHORT_PERIOD_MULTIPLIER)
+/* number of periods for deep buffer playback (screen on) */
+#define PLAYBACK_DEEP_BUFFER_SHORT_PERIOD_COUNT 4
+
+/* number of short deep buffer periods in a long period */
+#define DEEP_BUFFER_LONG_PERIOD_MULTIPLIER \
+ (DEEP_BUFFER_LONG_PERIOD_MS / DEEP_BUFFER_SHORT_PERIOD_MS)
+/* number of frames per long deep buffer period (screen off) */
+#define DEEP_BUFFER_LONG_PERIOD_SIZE \
+ (DEEP_BUFFER_SHORT_PERIOD_SIZE * DEEP_BUFFER_LONG_PERIOD_MULTIPLIER)
+/* number of periods for deep buffer playback (screen off) */
+#define PLAYBACK_DEEP_BUFFER_LONG_PERIOD_COUNT 2
+
/* Number of pseudo periods for low latency playback.
* These are called "pseudo" periods in that they are not known as periods by ALSA.
* Formerly, ALSA was configured in MMAP mode with 2 large periods, and this
@@ -276,11 +288,11 @@ enum tty_modes {
struct pcm_config pcm_config_mm = {
.channels = 2,
.rate = MM_FULL_POWER_SAMPLING_RATE,
- .period_size = LONG_PERIOD_SIZE,
- .period_count = PLAYBACK_LONG_PERIOD_COUNT,
+ .period_size = DEEP_BUFFER_LONG_PERIOD_SIZE,
+ .period_count = PLAYBACK_DEEP_BUFFER_LONG_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
- .start_threshold = LONG_PERIOD_SIZE,
- .avail_min = LONG_PERIOD_SIZE,
+ .start_threshold = DEEP_BUFFER_SHORT_PERIOD_SIZE * 2,
+ .avail_min = DEEP_BUFFER_LONG_PERIOD_SIZE,
};
struct pcm_config pcm_config_tones = {
@@ -616,6 +628,7 @@ struct tuna_audio_device {
bool bluetooth_nrec;
bool device_is_toro;
int wb_amr;
+ bool screen_off;
/* RIL */
struct ril_handle ril;
@@ -639,6 +652,9 @@ struct tuna_stream_out {
size_t buffer_frames;
int standby;
struct echo_reference_itfe *echo_reference;
+ int write_threshold;
+ bool use_long_periods;
+
struct tuna_audio_device *dev;
};
@@ -1387,6 +1403,9 @@ static int start_output_stream_deep_buffer(struct tuna_stream_out *out)
select_output_device(adev);
}
+ out->write_threshold = PLAYBACK_DEEP_BUFFER_LONG_PERIOD_COUNT * DEEP_BUFFER_LONG_PERIOD_SIZE;
+ out->use_long_periods = true;
+
out->config[PCM_NORMAL] = pcm_config_mm;
out->config[PCM_NORMAL].rate = MM_FULL_POWER_SAMPLING_RATE;
out->pcm[PCM_NORMAL] = pcm_open(CARD_TUNA_DEFAULT, PORT_MM,
@@ -1397,7 +1416,7 @@ static int start_output_stream_deep_buffer(struct tuna_stream_out *out)
out->pcm[PCM_NORMAL] = NULL;
return -ENOMEM;
}
- out->buffer_frames = pcm_config_mm.period_size * 2;
+ out->buffer_frames = DEEP_BUFFER_SHORT_PERIOD_SIZE * 2;
if (out->buffer == NULL)
out->buffer = malloc(out->buffer_frames * audio_stream_frame_size(&out->stream.common));
@@ -1574,7 +1593,8 @@ static size_t out_get_buffer_size_deep_buffer(const struct audio_stream *stream)
multiple of 16 frames, as audioflinger expects audio buffers to
be a multiple of 16 frames. Note: we use the default rate here
from pcm_config_mm.rate. */
- size_t size = (LONG_PERIOD_SIZE * DEFAULT_OUT_SAMPLING_RATE) / pcm_config_mm.rate;
+ size_t size = (DEEP_BUFFER_SHORT_PERIOD_SIZE * DEFAULT_OUT_SAMPLING_RATE) /
+ pcm_config_mm.rate;
size = ((size + 15) / 16) * 16;
return size * audio_stream_frame_size((struct audio_stream *)stream);
}
@@ -1738,7 +1758,8 @@ static uint32_t out_get_latency_deep_buffer(const struct audio_stream_out *strea
struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
/* Note: we use the default rate here from pcm_config_mm.rate */
- return (LONG_PERIOD_SIZE * PLAYBACK_LONG_PERIOD_COUNT * 1000) / pcm_config_mm.rate;
+ return (DEEP_BUFFER_LONG_PERIOD_SIZE * PLAYBACK_DEEP_BUFFER_LONG_PERIOD_COUNT * 1000) /
+ pcm_config_mm.rate;
}
static int out_set_volume(struct audio_stream_out *stream, float left,
@@ -1847,6 +1868,10 @@ static ssize_t out_write_deep_buffer(struct audio_stream_out *stream, const void
struct tuna_audio_device *adev = out->dev;
size_t frame_size = audio_stream_frame_size(&out->stream.common);
size_t in_frames = bytes / frame_size;
+ size_t out_frames;
+ bool use_long_periods;
+ int kernel_frames;
+ void *buf;
/* acquiring hw device mutex systematically is useful if a low priority thread is waiting
* on the output stream mutex - e.g. executing select_mode() while holding the hw device
@@ -1862,20 +1887,59 @@ static ssize_t out_write_deep_buffer(struct audio_stream_out *stream, const void
}
out->standby = 0;
}
+ use_long_periods = adev->screen_off && !adev->active_input;
pthread_mutex_unlock(&adev->lock);
+ if (use_long_periods != out->use_long_periods) {
+ size_t period_size;
+ size_t period_count;
+
+ if (use_long_periods) {
+ period_size = DEEP_BUFFER_LONG_PERIOD_SIZE;
+ period_count = PLAYBACK_DEEP_BUFFER_LONG_PERIOD_COUNT;
+ } else {
+ period_size = DEEP_BUFFER_SHORT_PERIOD_SIZE;
+ period_count = PLAYBACK_DEEP_BUFFER_SHORT_PERIOD_COUNT;
+ }
+ out->write_threshold = period_size * period_count;
+ pcm_set_avail_min(out->pcm[PCM_NORMAL], period_size);
+ out->use_long_periods = use_long_periods;
+ }
+
/* only use resampler if required */
if (out->config[PCM_NORMAL].rate != DEFAULT_OUT_SAMPLING_RATE) {
- size_t out_frames = out->buffer_frames;
-
+ out_frames = out->buffer_frames;
out->resampler->resample_from_input(out->resampler,
(int16_t *)buffer,
&in_frames,
(int16_t *)out->buffer,
&out_frames);
- ret = pcm_mmap_write(out->pcm[PCM_NORMAL], (void *)out->buffer, out_frames * frame_size);
- } else
- ret = pcm_mmap_write(out->pcm[PCM_NORMAL], (void *)buffer, bytes);
+ buf = (void *)out->buffer;
+ } else {
+ out_frames = in_frames;
+ buf = (void *)buffer;
+ }
+
+ /* do not allow more than out->write_threshold frames in kernel pcm driver buffer */
+ do {
+ struct timespec time_stamp;
+
+ if (pcm_get_htimestamp(out->pcm[PCM_NORMAL],
+ (unsigned int *)&kernel_frames, &time_stamp) < 0)
+ break;
+ kernel_frames = pcm_get_buffer_size(out->pcm[PCM_NORMAL]) - kernel_frames;
+
+ if (kernel_frames > out->write_threshold) {
+ unsigned long time = (unsigned long)
+ (((int64_t)(kernel_frames - out->write_threshold) * 1000000) /
+ MM_FULL_POWER_SAMPLING_RATE);
+ if (time < MIN_WRITE_SLEEP_US)
+ time = MIN_WRITE_SLEEP_US;
+ usleep(time);
+ }
+ } while (kernel_frames > out->write_threshold);
+
+ ret = pcm_mmap_write(out->pcm[PCM_NORMAL], buf, out_frames * frame_size);
exit:
pthread_mutex_unlock(&out->lock);
@@ -3075,6 +3139,14 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
adev->bluetooth_nrec = false;
}
+ ret = str_parms_get_str(parms, "screen_state", value, sizeof(value));
+ if (ret >= 0) {
+ if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
+ adev->screen_off = false;
+ else
+ adev->screen_off = true;
+ }
+
str_parms_destroy(parms);
return ret;
}