|
@@ -27,6 +27,7 @@
|
|
|
struct audio_line {
|
|
|
struct audio_output *audio;
|
|
|
struct circlebuf buffer;
|
|
|
+ DARRAY(uint8_t) volume_buffer;
|
|
|
uint64_t base_timestamp;
|
|
|
uint64_t last_timestamp;
|
|
|
|
|
@@ -38,12 +39,14 @@ struct audio_line {
|
|
|
static inline void audio_line_destroy_data(struct audio_line *line)
|
|
|
{
|
|
|
circlebuf_free(&line->buffer);
|
|
|
+ da_free(line->volume_buffer);
|
|
|
bfree(line);
|
|
|
}
|
|
|
|
|
|
struct audio_output {
|
|
|
struct audio_info info;
|
|
|
size_t block_size;
|
|
|
+ size_t channels;
|
|
|
media_t media;
|
|
|
media_output_t output;
|
|
|
|
|
@@ -116,7 +119,8 @@ int audio_output_open(audio_t *audio, media_t media, struct audio_info *info)
|
|
|
memcpy(&out->info, info, sizeof(struct audio_info));
|
|
|
pthread_mutex_init_value(&out->line_mutex);
|
|
|
out->media = media;
|
|
|
- out->block_size = get_audio_channels(info->speakers) *
|
|
|
+ out->channels = get_audio_channels(info->speakers);
|
|
|
+ out->block_size = out->channels *
|
|
|
get_audio_bytes_per_channel(info->format);
|
|
|
|
|
|
if (pthread_mutex_init(&out->line_mutex, NULL) != 0)
|
|
@@ -198,19 +202,120 @@ static inline uint64_t convert_to_sample_offset(audio_t audio, uint64_t offset)
|
|
|
(1000000000.0 / (double)audio->info.samples_per_sec));
|
|
|
}
|
|
|
|
|
|
+static inline void mul_vol_u8bit(struct audio_line *line, float volume,
|
|
|
+ size_t total_num)
|
|
|
+{
|
|
|
+ uint8_t *vals = line->volume_buffer.array;
|
|
|
+ int16_t vol = (int16_t)(volume * 127.0f);
|
|
|
+
|
|
|
+ for (size_t i = 0; i < total_num; i++) {
|
|
|
+ int16_t val = (int16_t)(vals[i] ^ 0x80) << 8;
|
|
|
+ vals[i] = (uint8_t)((val * vol / 127) + 128);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static inline void mul_vol_16bit(struct audio_line *line, float volume,
|
|
|
+ size_t total_num)
|
|
|
+{
|
|
|
+ uint16_t *vals = (uint16_t*)line->volume_buffer.array;
|
|
|
+ int32_t vol = (int32_t)(volume * 32767.0f);
|
|
|
+
|
|
|
+ for (size_t i = 0; i < total_num; i++)
|
|
|
+ vals[i] = (int32_t)((int32_t)vals[i] * vol / 32767);
|
|
|
+}
|
|
|
+
|
|
|
+static inline float conv_24bit_to_float(uint8_t *vals)
|
|
|
+{
|
|
|
+ int32_t val = ((int32_t)vals[0]) |
|
|
|
+ ((int32_t)vals[1] << 8) |
|
|
|
+ ((int32_t)vals[2] << 16);
|
|
|
+
|
|
|
+ if ((val & 0x800000) != 0)
|
|
|
+ val |= 0xFF000000;
|
|
|
+
|
|
|
+ return (float)val / 8388607.0f;
|
|
|
+}
|
|
|
+
|
|
|
+static inline void conv_float_to_24bit(float fval, uint8_t *vals)
|
|
|
+{
|
|
|
+ int32_t val = (int32_t)(fval * 8388607.0f);
|
|
|
+ vals[0] = (val) & 0xFF;
|
|
|
+ vals[1] = (val >> 8) & 0xFF;
|
|
|
+ vals[2] = (val >> 16) & 0xFF;
|
|
|
+}
|
|
|
+
|
|
|
+static inline void mul_vol_24bit(struct audio_line *line, float volume,
|
|
|
+ size_t total_num)
|
|
|
+{
|
|
|
+ uint8_t *vals = line->volume_buffer.array;
|
|
|
+
|
|
|
+ for (size_t i = 0; i < total_num; i++) {
|
|
|
+ float val = conv_24bit_to_float(vals) * volume;
|
|
|
+ conv_float_to_24bit(val, vals);
|
|
|
+ vals += 3;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static inline void mul_vol_32bit(struct audio_line *line, float volume,
|
|
|
+ size_t total_num)
|
|
|
+{
|
|
|
+ int32_t *vals = (int32_t*)line->volume_buffer.array;
|
|
|
+
|
|
|
+ for (size_t i = 0; i < total_num; i++) {
|
|
|
+ float val = (float)vals[i] / 2147483647.0f;
|
|
|
+ vals[i] = (int32_t)(val * volume / 2147483647.0f);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static inline void mul_vol_float(struct audio_line *line, float volume,
|
|
|
+ size_t total_num)
|
|
|
+{
|
|
|
+ float *vals = (float*)line->volume_buffer.array;
|
|
|
+
|
|
|
+ for (size_t i = 0; i < total_num; i++)
|
|
|
+ vals[i] *= volume;
|
|
|
+}
|
|
|
+
|
|
|
+static void audio_line_place_data(struct audio_line *line,
|
|
|
+ const struct audio_data *data, size_t position)
|
|
|
+{
|
|
|
+ size_t total_size = data->frames * line->audio->block_size;
|
|
|
+ size_t total_num = data->frames * line->audio->channels;
|
|
|
+
|
|
|
+ da_copy_array(line->volume_buffer, data->data, total_size);
|
|
|
+
|
|
|
+ switch (line->audio->info.format) {
|
|
|
+ case AUDIO_FORMAT_U8BIT:
|
|
|
+ mul_vol_u8bit(line, data->volume, total_num);
|
|
|
+ break;
|
|
|
+ case AUDIO_FORMAT_16BIT:
|
|
|
+ mul_vol_16bit(line, data->volume, total_num);
|
|
|
+ break;
|
|
|
+ case AUDIO_FORMAT_32BIT:
|
|
|
+ mul_vol_32bit(line, data->volume, total_num);
|
|
|
+ break;
|
|
|
+ case AUDIO_FORMAT_FLOAT:
|
|
|
+ mul_vol_float(line, data->volume, total_num);
|
|
|
+ break;
|
|
|
+ case AUDIO_FORMAT_UNKNOWN:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ circlebuf_place(&line->buffer, position, line->volume_buffer.array,
|
|
|
+ total_size);
|
|
|
+}
|
|
|
+
|
|
|
void audio_line_output(audio_line_t line, const struct audio_data *data)
|
|
|
{
|
|
|
if (!line->buffer.size) {
|
|
|
line->base_timestamp = data->timestamp;
|
|
|
|
|
|
- circlebuf_push_back(&line->buffer, data->data,
|
|
|
- data->frames * line->audio->block_size);
|
|
|
+ audio_line_place_data(line, data, 0);
|
|
|
} else {
|
|
|
uint64_t position = data->timestamp - line->base_timestamp;
|
|
|
position = convert_to_sample_offset(line->audio, position);
|
|
|
position *= line->audio->block_size;
|
|
|
|
|
|
- circlebuf_place(&line->buffer, (size_t)position, data->data,
|
|
|
- data->frames * line->audio->block_size);
|
|
|
+ audio_line_place_data(line, data, (size_t)position);
|
|
|
}
|
|
|
}
|