Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: compress: add support for gapless playback

this add new API for sound compress to support gapless playback.
As noted in Documentation change, we add API to send metadata of encoder and
padding delay to DSP. Also add API for indicating EOF and switching to
subsequent track

Also bump the compress API version

Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Jeeja KP and committed by
Takashi Iwai
9727b490 8be69efa

+180 -1
+46
Documentation/sound/alsa/compress_offload.txt
··· 145 145 - Addition of encoding options when required (derived from OpenMAX IL) 146 146 - Addition of rateControlSupported (missing in OpenMAX AL) 147 147 148 + Gapless Playback 149 + ================ 150 + When playing thru an album, the decoders have the ability to skip the encoder 151 + delay and padding and directly move from one track content to another. The end 152 + user can perceive this as gapless playback as we dont have silence while 153 + switching from one track to another 154 + 155 + Also, there might be low-intensity noises due to encoding. Perfect gapless is 156 + difficult to reach with all types of compressed data, but works fine with most 157 + music content. The decoder needs to know the encoder delay and encoder padding. 158 + So we need to pass this to DSP. This metadata is extracted from ID3/MP4 headers 159 + and are not present by default in the bitstream, hence the need for a new 160 + interface to pass this information to the DSP. Also DSP and userspace needs to 161 + switch from one track to another and start using data for second track. 162 + 163 + The main additions are: 164 + 165 + - set_metadata 166 + This routine sets the encoder delay and encoder padding. This can be used by 167 + decoder to strip the silence. This needs to be set before the data in the track 168 + is written. 169 + 170 + - set_next_track 171 + This routine tells DSP that metadata and write operation sent after this would 172 + correspond to subsequent track 173 + 174 + - partial drain 175 + This is called when end of file is reached. The userspace can inform DSP that 176 + EOF is reached and now DSP can start skipping padding delay. Also next write 177 + data would belong to next track 178 + 179 + Sequence flow for gapless would be: 180 + - Open 181 + - Get caps / codec caps 182 + - Set params 183 + - Set metadata of the first track 184 + - Fill data of the first track 185 + - Trigger start 186 + - User-space finished sending all, 187 + - Indicaite next track data by sending set_next_track 188 + - Set metadata of the next track 189 + - then call partial_drain to flush most of buffer in DSP 190 + - Fill data of the next track 191 + - DSP switches to second track 192 + (note: order for partial_drain and write for next track can be reversed as well) 193 + 148 194 Not supported: 149 195 150 196 - Support for VoIP/circuit-switched calls is not the target of this
+8
include/sound/compress_driver.h
··· 71 71 * @runtime: pointer to runtime structure 72 72 * @device: device pointer 73 73 * @direction: stream direction, playback/recording 74 + * @metadata_set: metadata set flag, true when set 75 + * @next_track: has userspace signall next track transistion, true when set 74 76 * @private_data: pointer to DSP private data 75 77 */ 76 78 struct snd_compr_stream { ··· 81 79 struct snd_compr_runtime *runtime; 82 80 struct snd_compr *device; 83 81 enum snd_compr_direction direction; 82 + bool metadata_set; 83 + bool next_track; 84 84 void *private_data; 85 85 }; 86 86 ··· 114 110 struct snd_compr_params *params); 115 111 int (*get_params)(struct snd_compr_stream *stream, 116 112 struct snd_codec *params); 113 + int (*set_metadata)(struct snd_compr_stream *stream, 114 + struct snd_compr_metadata *metadata); 115 + int (*get_metadata)(struct snd_compr_stream *stream, 116 + struct snd_compr_metadata *metadata); 117 117 int (*trigger)(struct snd_compr_stream *stream, int cmd); 118 118 int (*pointer)(struct snd_compr_stream *stream, 119 119 struct snd_compr_tstamp *tstamp);
+30 -1
include/uapi/sound/compress_offload.h
··· 30 30 #include <sound/compress_params.h> 31 31 32 32 33 - #define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 0) 33 + #define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 1) 34 34 /** 35 35 * struct snd_compressed_buffer: compressed buffer 36 36 * @fragment_size: size of buffer fragment in bytes ··· 122 122 }; 123 123 124 124 /** 125 + * @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the 126 + * end of the track 127 + * @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the 128 + * beginning of the track 129 + */ 130 + enum { 131 + SNDRV_COMPRESS_ENCODER_PADDING = 1, 132 + SNDRV_COMPRESS_ENCODER_DELAY = 2, 133 + }; 134 + 135 + /** 136 + * struct snd_compr_metadata: compressed stream metadata 137 + * @key: key id 138 + * @value: key value 139 + */ 140 + struct snd_compr_metadata { 141 + __u32 key; 142 + __u32 value[8]; 143 + }; 144 + 145 + /** 125 146 * compress path ioctl definitions 126 147 * SNDRV_COMPRESS_GET_CAPS: Query capability of DSP 127 148 * SNDRV_COMPRESS_GET_CODEC_CAPS: Query capability of a codec ··· 166 145 struct snd_compr_codec_caps) 167 146 #define SNDRV_COMPRESS_SET_PARAMS _IOW('C', 0x12, struct snd_compr_params) 168 147 #define SNDRV_COMPRESS_GET_PARAMS _IOR('C', 0x13, struct snd_codec) 148 + #define SNDRV_COMPRESS_SET_METADATA _IOW('C', 0x14,\ 149 + struct snd_compr_metadata) 150 + #define SNDRV_COMPRESS_GET_METADATA _IOWR('C', 0x15,\ 151 + struct snd_compr_metadata) 169 152 #define SNDRV_COMPRESS_TSTAMP _IOR('C', 0x20, struct snd_compr_tstamp) 170 153 #define SNDRV_COMPRESS_AVAIL _IOR('C', 0x21, struct snd_compr_avail) 171 154 #define SNDRV_COMPRESS_PAUSE _IO('C', 0x30) ··· 177 152 #define SNDRV_COMPRESS_START _IO('C', 0x32) 178 153 #define SNDRV_COMPRESS_STOP _IO('C', 0x33) 179 154 #define SNDRV_COMPRESS_DRAIN _IO('C', 0x34) 155 + #define SNDRV_COMPRESS_NEXT_TRACK _IO('C', 0x35) 156 + #define SNDRV_COMPRESS_PARTIAL_DRAIN _IO('C', 0x36) 180 157 /* 181 158 * TODO 182 159 * 1. add mmap support 183 160 * 184 161 */ 185 162 #define SND_COMPR_TRIGGER_DRAIN 7 /*FIXME move this to pcm.h */ 163 + #define SND_COMPR_TRIGGER_NEXT_TRACK 8 164 + #define SND_COMPR_TRIGGER_PARTIAL_DRAIN 9 186 165 #endif
+96
sound/core/compress_offload.c
··· 486 486 if (retval) 487 487 goto out; 488 488 stream->runtime->state = SNDRV_PCM_STATE_SETUP; 489 + stream->metadata_set = false; 490 + stream->next_track = false; 489 491 } else { 490 492 return -EPERM; 491 493 } ··· 516 514 517 515 out: 518 516 kfree(params); 517 + return retval; 518 + } 519 + 520 + static int 521 + snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) 522 + { 523 + struct snd_compr_metadata metadata; 524 + int retval; 525 + 526 + if (!stream->ops->get_metadata) 527 + return -ENXIO; 528 + 529 + if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 530 + return -EFAULT; 531 + 532 + retval = stream->ops->get_metadata(stream, &metadata); 533 + if (retval != 0) 534 + return retval; 535 + 536 + if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) 537 + return -EFAULT; 538 + 539 + return 0; 540 + } 541 + 542 + static int 543 + snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) 544 + { 545 + struct snd_compr_metadata metadata; 546 + int retval; 547 + 548 + if (!stream->ops->set_metadata) 549 + return -ENXIO; 550 + /* 551 + * we should allow parameter change only when stream has been 552 + * opened not in other cases 553 + */ 554 + if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) 555 + return -EFAULT; 556 + 557 + retval = stream->ops->set_metadata(stream, &metadata); 558 + stream->metadata_set = true; 559 + 519 560 return retval; 520 561 } 521 562 ··· 645 600 return retval; 646 601 } 647 602 603 + static int snd_compr_next_track(struct snd_compr_stream *stream) 604 + { 605 + int retval; 606 + 607 + /* only a running stream can transition to next track */ 608 + if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) 609 + return -EPERM; 610 + 611 + /* you can signal next track isf this is intended to be a gapless stream 612 + * and current track metadata is set 613 + */ 614 + if (stream->metadata_set == false) 615 + return -EPERM; 616 + 617 + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); 618 + if (retval != 0) 619 + return retval; 620 + stream->metadata_set = false; 621 + stream->next_track = true; 622 + return 0; 623 + } 624 + 625 + static int snd_compr_partial_drain(struct snd_compr_stream *stream) 626 + { 627 + int retval; 628 + if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || 629 + stream->runtime->state == SNDRV_PCM_STATE_SETUP) 630 + return -EPERM; 631 + /* stream can be drained only when next track has been signalled */ 632 + if (stream->next_track == false) 633 + return -EPERM; 634 + 635 + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); 636 + 637 + stream->next_track = false; 638 + return retval; 639 + } 640 + 648 641 static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 649 642 { 650 643 struct snd_compr_file *data = f->private_data; ··· 712 629 case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): 713 630 retval = snd_compr_get_params(stream, arg); 714 631 break; 632 + case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): 633 + retval = snd_compr_set_metadata(stream, arg); 634 + break; 635 + case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): 636 + retval = snd_compr_get_metadata(stream, arg); 637 + break; 715 638 case _IOC_NR(SNDRV_COMPRESS_TSTAMP): 716 639 retval = snd_compr_tstamp(stream, arg); 717 640 break; ··· 739 650 case _IOC_NR(SNDRV_COMPRESS_DRAIN): 740 651 retval = snd_compr_drain(stream); 741 652 break; 653 + case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): 654 + retval = snd_compr_partial_drain(stream); 655 + break; 656 + case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): 657 + retval = snd_compr_next_track(stream); 658 + break; 659 + 742 660 } 743 661 mutex_unlock(&stream->device->lock); 744 662 return retval;