audioringbuffer: Add support for DSD data

Code is partially based on the DSD of Robert Tiemann <rtie@gmx.de>:
https://gitlab.freedesktop.org/rtiemann/gstreamer/-/tree/dsd

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3901>
This commit is contained in:
Carlos Rafael Giani 2023-05-19 15:57:46 +02:00 committed by GStreamer Marge Bot
parent 4cce9a77c9
commit b65eab915a
2 changed files with 87 additions and 9 deletions

View file

@ -40,6 +40,7 @@
#include <string.h>
#include <gst/audio/audio.h>
#include <gst/audio/gstdsd.h>
#include "gstaudioringbuffer.h"
GST_DEBUG_CATEGORY_STATIC (gst_audio_ring_buffer_debug);
@ -313,6 +314,60 @@ gst_audio_ring_buffer_parse_caps (GstAudioRingBufferSpec * spec, GstCaps * caps)
gst_structure_get_int (structure, "channels", &info.channels);
spec->type = GST_AUDIO_RING_BUFFER_FORMAT_TYPE_FLAC;
info.bpf = 1;
} else if (g_str_equal (mimetype, GST_DSD_MEDIA_TYPE)) {
/* Notes about what the "rate" means in DSD:
*
* In DSD, "sample formats" don't actually exist. There is only the DSD bit;
* this is what could be considered the closest equivalent to a "sample format".
* But since it is impractical to deal with individual bits in software, the
* bits are typically grouped into words (8/16/32 bit words). These are the
* DSDU8, DSDU16LE etc. "grouping formats".
*
* The "rate" in DSD information refers to the number of DSD _bytes_ per second
* (not bits per second, because, as said, per-bit handling in software does
* not usually make sense). The way the GstAudioRingBuffer works however requires
* the rate to be interpreted as the number of DSD _words_ per minute. This is
* in part because that's how ALSA uses the rate.
*
* If the word format is DSDU8, then there's no difference to just using the
* original byte rate. But if for example it is DSDU16LE, then the ringbuffer's
* rate needs to be half of the rate from GstDsdInfo. For this reason, it is
* essential to divide the rate from the DSD info by the word length (in bytes).
*
* Furthermore, the BPF is set to the stride (= format width * num channels).
* The GstAudioRingBuffer can only handle interleaved DSD. This means that
* there is a "stride", that is, the DSD word of channel #1 is stored first,
* followed by the DSD word of channel #2 etc. and then again we get a DSD
* word from channel #1, and so forth. This is similar to how interleaved
* PCM works. The stride is then the size (in bytes) of the DSD words for
* each channel that are played at the same time. Using this as the BPF is
* very important. Otherweise, timestamp and duration figures can be off,
* the segment sizes may not be an integer multiple of the DSD stride, etc.
*/
GstDsdInfo dsd_info;
guint format_width;
if (!gst_dsd_info_from_caps (&dsd_info, caps))
goto parse_error;
format_width = gst_dsd_format_get_width (dsd_info.format);
info.rate = dsd_info.rate / format_width;
info.channels = dsd_info.channels;
info.bpf = format_width * dsd_info.channels;
GST_INFO ("using DSD word rate %d instead of DSD byte rate %d "
"for ringbuffer", info.rate, dsd_info.rate);
memcpy (info.position, dsd_info.positions,
sizeof (GstAudioChannelPosition) * dsd_info.channels);
GST_AUDIO_RING_BUFFER_SPEC_DSD_FORMAT (spec) =
GST_DSD_INFO_FORMAT (&dsd_info);
spec->type = GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DSD;
} else {
goto parse_error;
}
@ -655,13 +710,19 @@ gst_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
g_free (buf->empty_seg);
buf->empty_seg = g_malloc (segsize);
if (buf->spec.type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW) {
gst_audio_format_info_fill_silence (buf->spec.info.finfo, buf->empty_seg,
segsize);
} else {
/* FIXME, non-raw formats get 0 as the empty sample */
memset (buf->empty_seg, 0, segsize);
switch (buf->spec.type) {
case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW:
gst_audio_format_info_fill_silence (buf->spec.info.finfo, buf->empty_seg,
segsize);
break;
case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DSD:
memset (buf->empty_seg, GST_DSD_SILENCE_PATTERN_BYTE, segsize);
break;
default:
/* FIXME, non-raw formats get 0 as the empty sample */
memset (buf->empty_seg, 0, segsize);
}
GST_DEBUG_OBJECT (buf, "acquired device");
done:

View file

@ -27,6 +27,8 @@
#ifndef __GST_AUDIO_RING_BUFFER_H__
#define __GST_AUDIO_RING_BUFFER_H__
#include <gst/audio/gstdsdformat.h>
G_BEGIN_DECLS
#define GST_TYPE_AUDIO_RING_BUFFER (gst_audio_ring_buffer_get_type())
@ -88,6 +90,7 @@ typedef enum {
* @GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG2_AAC_RAW: samples in MPEG-2 AAC raw format (Since: 1.12)
* @GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG4_AAC_RAW: samples in MPEG-4 AAC raw format (Since: 1.12)
* @GST_AUDIO_RING_BUFFER_FORMAT_TYPE_FLAC: samples in FLAC format (Since: 1.12)
* @GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DSD: samples in DSD format (Since: 1.24)
*
* The format of the samples in the ringbuffer.
*/
@ -107,7 +110,8 @@ typedef enum
GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG4_AAC,
GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG2_AAC_RAW,
GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG4_AAC_RAW,
GST_AUDIO_RING_BUFFER_FORMAT_TYPE_FLAC
GST_AUDIO_RING_BUFFER_FORMAT_TYPE_FLAC,
GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DSD
} GstAudioRingBufferFormatType;
/**
@ -121,8 +125,13 @@ typedef enum
* @segtotal: the total number of segments
* @seglatency: number of segments queued in the lower level device,
* defaults to segtotal
* @dsd_format: the #GstDsdFormat (Since: 1.24)
*
* The structure containing the format specification of the ringbuffer.
*
* When @type is GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DSD, the @dsd_format
* is valid (otherwise it is unused). Also, when DSD is the sample type,
* only the rate, channels, position, and bpf fields in @info are populated.
*/
struct _GstAudioRingBufferSpec
{
@ -152,8 +161,15 @@ struct _GstAudioRingBufferSpec
gint seglatency; /* number of segments queued in the lower
* level device, defaults to segtotal. */
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
/* Union preserves padded struct size for backwards compat
* Consumer code should use the accessor macros for fields */
union {
struct { /* < skip > */
GstDsdFormat dsd_format;
} abi;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
} ABI;
};
#define GST_AUDIO_RING_BUFFER_SPEC_FORMAT_TYPE(spec) ((spec)->type)
@ -163,6 +179,7 @@ struct _GstAudioRingBufferSpec
#define GST_AUDIO_RING_BUFFER_SPEC_SEGSIZE(spec) ((spec)->segsize)
#define GST_AUDIO_RING_BUFFER_SPEC_SEGTOTAL(spec) ((spec)->segtotal)
#define GST_AUDIO_RING_BUFFER_SPEC_SEGLATENCY(spec) ((spec)->seglatency)
#define GST_AUDIO_RING_BUFFER_SPEC_DSD_FORMAT(spec) ((spec)->ABI.abi.dsd_format)
#define GST_AUDIO_RING_BUFFER_GET_COND(buf) (&(((GstAudioRingBuffer *)buf)->cond))
#define GST_AUDIO_RING_BUFFER_WAIT(buf) (g_cond_wait (GST_AUDIO_RING_BUFFER_GET_COND (buf), GST_OBJECT_GET_LOCK (buf)))