]> code.ossystems Code Review - meta-freescale.git/commitdiff
tinycompress: Add recipe
authorTom Hochstein <tom.hochstein@nxp.com>
Wed, 1 Sep 2021 18:21:24 +0000 (13:21 -0500)
committerDaiane Angolini <daiane.angolini@foundries.io>
Sat, 11 Sep 2021 16:35:14 +0000 (13:35 -0300)
Add tinycompress, a library to handle compressed formats like MP3.

Signed-off-by: Tom Hochstein <tom.hochstein@nxp.com>
recipes-multimedia/tinycompress/tinycompress/0001-tinycompress-Add-id3-decoding.patch [new file with mode: 0755]
recipes-multimedia/tinycompress/tinycompress/0002-cplay-Support-wave-file.patch [new file with mode: 0755]
recipes-multimedia/tinycompress/tinycompress/0003-cplay-Add-pause-feature.patch [new file with mode: 0755]
recipes-multimedia/tinycompress/tinycompress/0004-tinycompress-pass-NULL-buffer-with-0-size-to-driver.patch [new file with mode: 0755]
recipes-multimedia/tinycompress/tinycompress/0005-cplay-Support-aac-streams.patch [new file with mode: 0755]
recipes-multimedia/tinycompress/tinycompress_1.1.6.bb [new file with mode: 0644]

diff --git a/recipes-multimedia/tinycompress/tinycompress/0001-tinycompress-Add-id3-decoding.patch b/recipes-multimedia/tinycompress/tinycompress/0001-tinycompress-Add-id3-decoding.patch
new file mode 100755 (executable)
index 0000000..f578148
--- /dev/null
@@ -0,0 +1,1001 @@
+From 16f6b7a5baec41f18fde75fd311fb988e3c31810 Mon Sep 17 00:00:00 2001
+From: Shengjiu Wang <shengjiu.wang@nxp.com>
+Date: Fri, 13 Jul 2018 18:13:24 +0800
+Subject: [PATCH] tinycompress: Add id3 decoding
+
+Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
+---
+ include/tinycompress/id3_tag_decode.h | 198 +++++++++++
+ src/utils/Makefile.am                 |   2 +-
+ src/utils/cplay.c                     |  88 +++++
+ src/utils/id3_tag_decode.c            | 642 ++++++++++++++++++++++++++++++++++
+ 4 files changed, 929 insertions(+), 1 deletion(-)
+ create mode 100644 include/tinycompress/id3_tag_decode.h
+ create mode 100644 src/utils/id3_tag_decode.c
+
+diff --git a/include/tinycompress/id3_tag_decode.h b/include/tinycompress/id3_tag_decode.h
+new file mode 100644
+index 0000000..1a911d7
+--- /dev/null
++++ b/include/tinycompress/id3_tag_decode.h
+@@ -0,0 +1,198 @@
++/*
++ * Copyright (c) 2006-2017 Cadence Design Systems, Inc.
++ * Copyright 2018 NXP
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included
++ * in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
++ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
++ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
++ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++/******************************************************************
++ *  file name       : id3_tag_decode.h
++ *
++ *  description     : stores typedefs of structures specific to MP3 tag
++ *
++ *  revision history:
++ *       29 04 2004       DK     creation
++ *****************************************************************/
++
++#ifndef ID3_TAG_DECODE_H
++#define ID3_TAG_DECODE_H
++
++typedef signed char             WORD8;
++typedef signed char *           pWORD8;
++typedef unsigned char           UWORD8;
++typedef unsigned char *         pUWORD8;
++
++typedef signed short            WORD16;
++typedef signed short *          pWORD16;
++typedef unsigned short          UWORD16;
++typedef unsigned short *        pUWORD16;
++
++typedef signed int              WORD24;
++typedef signed int *            pWORD24;
++typedef unsigned int            UWORD24;
++typedef unsigned int *          pUWORD24;
++
++typedef signed int              WORD32;
++typedef signed int *            pWORD32;
++typedef unsigned int            UWORD32;
++typedef unsigned int *          pUWORD32;
++
++typedef void                    VOID;
++typedef void *                  pVOID;
++
++typedef signed int              BOOL;
++typedef unsigned int            UBOOL;
++typedef signed int              FLAG;
++typedef unsigned int            UFLAG;
++typedef signed int              LOOPIDX;
++typedef unsigned int            ULOOPIDX;
++typedef signed int              WORD;
++typedef unsigned int            UWORD;
++
++#define MAX_TAG_FRAME_SIZE            100
++
++#define ID3V1         (0x544147)      /* 0x544147 is TAG in WORD8 */
++
++#define ID3V2         (0x494433)      /* 0x494433 is ID3 in WORD8 */
++
++/*
++ * structure corresponding to ID3 tag v1 header.
++ * this structure has all the field corresponding to ID3 tag v1 header.
++ */
++
++typedef struct {
++      WORD32 tag;                             // 3 bytes
++
++      WORD16 version;                 // 2 bytes
++
++      WORD8 flag;                             //1 byte
++
++      WORD32  size;           //4 bytes
++
++} id3_v2_header_struct;
++
++/* structure which will store the frame data and
++ * also put a limit max data to be stored
++ */
++typedef struct {
++      WORD8 frame_data[MAX_TAG_FRAME_SIZE];
++
++      WORD32  max_size;               //4 bytes
++
++      WORD16 tag_present;
++
++      WORD16 exceeds_buffer_size;
++
++} id3_v2_frame_struct;
++
++/*
++ * structure corresponding to ID3 tag v2.
++ * this structure has some of the  field corresponding to ID3 tag v2.
++ * if user wants to read some more tag information from
++ * the MP3 file, he can add that field in this structure and pass address
++ * of that element to get_inf function in id3_tag_decode.c under the
++ * corresponding field frame header. few fields which are needed are already
++ * populated by reading from the TAG header.
++ */
++typedef struct {
++      id3_v2_frame_struct  album_movie_show_title;
++
++      id3_v2_frame_struct  composer_name;
++
++      id3_v2_frame_struct  content_type;
++
++      id3_v2_frame_struct  encoded_by;
++
++      id3_v2_frame_struct  lyricist_text_writer;
++
++      id3_v2_frame_struct  content_group_description;
++
++      id3_v2_frame_struct  title_songname_content_description;
++
++      id3_v2_frame_struct  medxa_type;
++
++      id3_v2_frame_struct  original_album_movie_show_title;
++
++      id3_v2_frame_struct  original_filename;
++
++      id3_v2_frame_struct  original_lyricist_text_writer;
++
++      id3_v2_frame_struct  original_artist_performer;
++
++      id3_v2_frame_struct  file_owner_licensee;
++
++      id3_v2_frame_struct  lead_performer_soloist;
++
++      id3_v2_frame_struct  publisher;
++
++      id3_v2_frame_struct  private_frame;
++
++      id3_v2_frame_struct  other_info;
++
++      id3_v2_header_struct id3_v2_header;
++
++      WORD32 header_end;
++
++      WORD32 bytes_consumed;
++
++} id3v2_struct;
++
++/*
++ * structure corresponding to ID3 tag v1.
++ * this structure has all the field corresponding to ID3 tag v1.
++ */
++typedef struct {
++      WORD8 song_title[30]; //30 word8acters
++
++      WORD8 artist[30]; //30 word8acters
++
++      WORD8 album[30]; //30 word8acters
++
++      WORD8 year[4]; //4 word8acters
++
++      WORD8 comment[30]; //30 word8acters
++
++      WORD8 genre[1]; //1 byte
++
++} id3v1_struct;
++
++WORD32 get_info(const char *inp_buffer,
++              unsigned int         avail_inp,
++              WORD32               tag_size,
++              id3_v2_frame_struct *dest);
++
++WORD32 search_id3_v2(UWORD8 *buffer);
++
++WORD32 decode_id3_v2(const char *const buffer,
++                   id3v2_struct *id3v2,
++                   WORD32        continue_flag,
++                   WORD32        insize);
++
++WORD32 get_id3_v2_bytes(UWORD8 *buffer);
++
++WORD32 get_v1_info(UWORD8 *buffer, id3v1_struct *id3v1);
++
++WORD32 search_id3_v1(UWORD8 *buffer);
++
++WORD32 decode_id3_v1(UWORD8 *buffer, id3v1_struct *id3v1);
++
++void init_id3v2_field(id3v2_struct *id3v2);
++
++#endif
+diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
+index 1b996d4..e813689 100644
+--- a/src/utils/Makefile.am
++++ b/src/utils/Makefile.am
+@@ -1,6 +1,6 @@
+ bin_PROGRAMS = cplay crecord
+-cplay_SOURCES = cplay.c
++cplay_SOURCES = cplay.c id3_tag_decode.c
+ crecord_SOURCES = crecord.c
+ cplay_CFLAGS = -I$(top_srcdir)/include
+diff --git a/src/utils/cplay.c b/src/utils/cplay.c
+index 87863a3..2a52b52 100644
+--- a/src/utils/cplay.c
++++ b/src/utils/cplay.c
+@@ -72,6 +72,7 @@
+ #include "sound/compress_params.h"
+ #include "tinycompress/tinycompress.h"
+ #include "tinycompress/tinymp3.h"
++#include "tinycompress/id3_tag_decode.h"
+ static int verbose;
+ static const unsigned int DEFAULT_CODEC_ID = SND_AUDIOCODEC_PCM;
+@@ -245,12 +246,97 @@ int main(int argc, char **argv)
+       exit(EXIT_SUCCESS);
+ }
++void shift_buffer(char *buf, int buf_size, int bytes_consumed)
++{
++      int i;
++
++      if (bytes_consumed <= 0)
++              return;
++
++      for (i = 0; i < buf_size - bytes_consumed; i++)
++              buf[i] = buf[i + bytes_consumed];
++}
++
++void parse_id3(FILE *file, int *offset) {
++      /* ID3 tag specific declarations */
++      unsigned char id3_buf[128];
++      unsigned char id3v2_buf[4096];
++      signed int id3_v1_found = 0, id3_v1_decoded = 0;
++      signed int id3_v2_found = 0, id3_v2_complete = 0;
++      signed int i_bytes_consumed = 0;
++      signed int i_fread_bytes;
++      id3v1_struct id3v1;
++      id3v2_struct id3v2;
++
++      {
++              fseek(file, -128, SEEK_END);
++              fread(id3_buf, 1, 128, file);
++
++              /* search for ID3V1 */
++              id3_v1_found = search_id3_v1(id3_buf + 0);
++              if (id3_v1_found) {
++                      /* if ID3V1 is found, decode ID3V1 */
++                      decode_id3_v1(id3_buf + 3, &id3v1);
++                      id3_v1_decoded = 1;
++              }
++              fseek(file, 0, SEEK_SET);
++      }
++
++      {
++              signed int flag = 0;
++              signed int continue_flag = 0;
++
++              i_fread_bytes = fread(id3v2_buf,
++                              sizeof(char), 0x1000, file);
++
++              /* search for ID3V2 */
++              id3_v2_found =
++                      search_id3_v2(id3v2_buf);
++
++              if (id3_v2_found) {
++                      /* initialise the max fields */
++                      init_id3v2_field(&id3v2);
++
++                      while (!id3_v2_complete && id3_v2_found) {
++                              /* if ID3V2 is found, decode ID3V2 */
++                              id3_v2_complete = decode_id3_v2((const char *const)id3v2_buf,
++                                                      &id3v2, continue_flag, i_fread_bytes);
++
++                              if (!id3_v2_complete) {
++                                      continue_flag = 1;
++                                      i_bytes_consumed = id3v2.bytes_consumed;
++
++                                      fseek(file, i_bytes_consumed, SEEK_SET);
++
++                                        i_fread_bytes = fread(id3v2_buf,
++                                                              sizeof(unsigned char), 0x1000, file);
++                                        if (i_fread_bytes <= 0) {
++                                              return;
++                                        }
++                              }
++                      }
++
++                      if (id3_v2_complete) {
++                              i_bytes_consumed = id3v2.bytes_consumed;
++                              fseek(file, i_bytes_consumed, SEEK_SET);
++                        }
++                }
++      }
++
++      *offset = i_bytes_consumed;
++}
++
+ void get_codec_mp3(FILE *file, struct compr_config *config,
+               struct snd_codec *codec)
+ {
+       size_t read;
+       struct mp3_header header;
+       unsigned int channels, rate, bits;
++      int offset = 0;
++
++      parse_id3(file, &offset);
++
++      fseek(file, offset, SEEK_SET);
+       read = fread(&header, 1, sizeof(header), file);
+       if (read != sizeof(header)) {
+@@ -279,6 +365,8 @@ void get_codec_mp3(FILE *file, struct compr_config *config,
+       codec->level = 0;
+       codec->ch_mode = 0;
+       codec->format = 0;
++
++      fseek(file, offset, SEEK_SET);
+ }
+ void get_codec_iec(FILE *file, struct compr_config *config,
+diff --git a/src/utils/id3_tag_decode.c b/src/utils/id3_tag_decode.c
+new file mode 100644
+index 0000000..393967a
+--- /dev/null
++++ b/src/utils/id3_tag_decode.c
+@@ -0,0 +1,642 @@
++/*
++ * Copyright (c) 2006-2017 Cadence Design Systems, Inc.
++ * Copyright 2018 NXP
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included
++ * in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
++ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
++ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
++ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++#include <ctype.h>
++#include "tinycompress/id3_tag_decode.h"
++
++#define CHAR4(c1, c2, c3, c4) \
++      (int)(((unsigned char)(c1) << 24) | \
++              ((unsigned char)(c2) << 16) | \
++              ((unsigned char)(c3) << 8) | \
++              ((unsigned char)c4))
++
++#ifndef MSVC_BUILD
++unsigned int umin(unsigned int a, unsigned int b)
++{
++      return (a < b ? a : b);
++}
++
++#else
++unsigned int umin(unsigned int a, unsigned int b)
++{
++      return (a < b ? a : b);
++}
++#endif
++
++/***********************************************************
++ *  function name   : display
++ *
++ *  description     : display ID3 tag contents.
++ *
++ *  arguments       : input parameters
++ *
++ *  values returned : 0
++ ***********************************************************/
++
++static void display2(const id3_v2_frame_struct * const src,
++                   int size,
++                   const char * const disp)
++{
++      int j;
++
++
++      for (j = 0; j < size; j++) {
++              int c = src->frame_data[j];
++
++              if (c) {
++                      if (!isprint(c))
++                              break;
++              }
++      }
++}
++
++static VOID display1(WORD8 src[], WORD32 size, WORD8 disp[])
++{
++      WORD32 j;
++
++      for (j = 0; j < size ; j++) {
++              int c = src[j];
++
++              if (c) {
++                      if (!isprint(c))
++                              break;
++              }
++      }
++}
++
++/*****************************************************************
++ *  function name   : init_id3_header
++ *
++ *  description     : initialise the max filed size of teh farem.
++ *
++ *  arguments       : input parameters
++ *
++ *  values returned : 0
++ ****************************************************************/
++
++VOID init_id3v2_field(id3v2_struct *id3v2)
++{
++      id3v2->album_movie_show_title.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->composer_name.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->content_type.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->encoded_by.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->lyricist_text_writer.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->content_group_description.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->title_songname_content_description.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->medxa_type.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->original_album_movie_show_title.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->original_filename.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->original_lyricist_text_writer.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->original_artist_performer.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->file_owner_licensee.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->lead_performer_soloist.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->publisher.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->private_frame.max_size = MAX_TAG_FRAME_SIZE;
++      id3v2->other_info.max_size = MAX_TAG_FRAME_SIZE;
++
++      /* resetting the flag to indicate presese of frame */
++      id3v2->album_movie_show_title.tag_present = 0;
++      id3v2->composer_name.tag_present = 0;
++      id3v2->content_type.tag_present = 0;
++      id3v2->encoded_by.tag_present = 0;
++      id3v2->lyricist_text_writer.tag_present = 0;
++      id3v2->content_group_description.tag_present = 0;
++      id3v2->title_songname_content_description.tag_present = 0;
++      id3v2->medxa_type.tag_present = 0;
++      id3v2->original_album_movie_show_title.tag_present = 0;
++      id3v2->original_filename.tag_present = 0;
++      id3v2->original_lyricist_text_writer.tag_present = 0;
++      id3v2->original_artist_performer.tag_present = 0;
++      id3v2->file_owner_licensee.tag_present = 0;
++      id3v2->lead_performer_soloist.tag_present = 0;
++      id3v2->publisher.tag_present = 0;
++      id3v2->private_frame.tag_present = 0;
++      id3v2->other_info.tag_present = 0;
++
++      /* resetting the flag which indicates that size of the frame has
++       * exceeded the max buffer size
++       */
++      id3v2->album_movie_show_title.exceeds_buffer_size = 0;
++      id3v2->composer_name.exceeds_buffer_size = 0;
++      id3v2->content_type.exceeds_buffer_size = 0;
++      id3v2->encoded_by.exceeds_buffer_size = 0;
++      id3v2->lyricist_text_writer.exceeds_buffer_size = 0;
++      id3v2->content_group_description.exceeds_buffer_size = 0;
++      id3v2->title_songname_content_description.exceeds_buffer_size = 0;
++      id3v2->medxa_type.exceeds_buffer_size = 0;
++      id3v2->original_album_movie_show_title.exceeds_buffer_size = 0;
++      id3v2->original_filename.exceeds_buffer_size = 0;
++      id3v2->original_lyricist_text_writer.exceeds_buffer_size = 0;
++      id3v2->original_artist_performer.exceeds_buffer_size = 0;
++      id3v2->file_owner_licensee.exceeds_buffer_size = 0;
++      id3v2->lead_performer_soloist.exceeds_buffer_size = 0;
++      id3v2->publisher.exceeds_buffer_size = 0;
++      id3v2->private_frame.exceeds_buffer_size = 0;
++      id3v2->other_info.exceeds_buffer_size = 0;
++
++      id3v2->bytes_consumed = 0;
++      id3v2->header_end = 0;
++}
++
++/***************************************************************
++ *  function name   : search_id3_v2
++ *
++ *  description     : finds if ID3V2 starts at the start of given buffer.
++ *
++ *  arguments       : input parameters
++ *                   buffer          input buffer
++ *
++ *  values returned : FLAG   1: ID3 found   0: ID3 not found
++ ***************************************************************/
++WORD32 search_id3_v2(UWORD8 *buffer)
++{
++      UWORD32 temp;
++
++      temp  = buffer[0] << 16;
++      temp |= buffer[1] <<  8;
++      temp |= buffer[2];
++
++      if (temp == ID3V2)
++              return 1;   /* ID3 found */
++
++      return 0;       /* ID3 not found */
++}
++
++/**************************************************************
++ *  function name   : search_id3_v1
++ *
++ *  description     : finds if ID3V1 starts at the start of given buffer.
++ *
++ *  arguments       : input parameters
++ *                   buffer          input buffer
++ *
++ *  values returned : FLAG   1: ID3 found   0: ID3 not found
++ **************************************************************/
++WORD32 search_id3_v1(UWORD8 *buffer)
++{
++      UWORD32 temp;
++
++      temp  = buffer[0] << 16;
++      temp |= buffer[1] <<  8;
++      temp |= buffer[2];
++
++      if (temp == ID3V1)
++              return 1;   /* ID3 found */
++
++      return 0;       /* ID3 not found */
++}
++
++/***************************************************************
++ *  function name   : decode_id3_v1
++ *
++ *  description     : decodes ID3V1 tag.
++ *
++ *  arguments       : input parameters
++ *                   buffer              input buffer
++ *                   id3v1               structure
++ *
++ *  values returned : bytes consumed
++ **************************************************************/
++WORD32 decode_id3_v1(UWORD8 *buffer, id3v1_struct *id3v1)
++{
++      WORD32 bytes_consumed = 0;
++      short tag_type;
++
++      /* setting the tag type */
++      tag_type = 1;
++
++      bytes_consumed = get_v1_info(buffer, id3v1);
++
++      return bytes_consumed;
++}
++
++/***********************************************************
++ *  function name   : get_v1_info
++ *
++ *  description     : gets ID3V1 information fields.
++ *
++ *  arguments       : input parameters
++ *                   buffer              input buffer
++ *                   id3v1               structure
++ *
++ *  values returned : bytes consumed
++ ***********************************************************/
++WORD32 get_v1_info(UWORD8 *buffer, id3v1_struct *id3v1)
++{
++      WORD32 i;
++      WORD32 bytes_consumed = 0;
++
++      /* get song_title */
++      for (i = 0; i < 30; i++)
++              id3v1->song_title[i] = buffer[i];
++
++      buffer += 30;
++      bytes_consumed += 30;
++      display1(id3v1->song_title, 30, (WORD8 *)"song_title : ");
++
++      /* get artist */
++      for (i = 0; i < 30; i++)
++              id3v1->artist[i] = buffer[i];
++
++      buffer += 30;
++      bytes_consumed += 30;
++      display1(id3v1->artist, 30, (WORD8 *)"artist : ");
++
++      /* get album */
++      for (i = 0; i < 30; i++)
++              id3v1->album[i] = buffer[i];
++
++      buffer += 30;
++      bytes_consumed += 30;
++      display1(id3v1->album, 30, (WORD8 *)"album : ");
++
++      /* get year */
++      for (i = 0; i < 4; i++)
++              id3v1->year[i] = buffer[i];
++
++      buffer += 4;
++      bytes_consumed += 4;
++      display1(id3v1->year, 4, (WORD8 *)"year : ");
++
++      /* get comment */
++      for (i = 0; i < 30; i++)
++              id3v1->comment[i] = buffer[i];
++
++      buffer += 30;
++      bytes_consumed += 30;
++      display1(id3v1->comment, 30, (WORD8 *)"comment : ");
++
++      /* get genre */
++      for (i = 0; i < 1; i++)
++              id3v1->genre[i] = buffer[i];
++
++      buffer += 1;
++      bytes_consumed += 1;
++
++      return bytes_consumed;
++}
++
++/*****************************************************
++ *  function name   : decode_id3_v2
++ *
++ *  description     : decodes ID3V2 tag.
++ *
++ *  arguments       : input parameters
++ *                   buffer              input buffer
++ *                   id3v2               structure
++ *                   continue_flag       FLAG to indicate whether
++ *                                       it is first call or not
++ *                   insize              input buffer size
++ *
++ *  values returned : bytes consumed
++ ******************************************************/
++WORD32 decode_id3_v2(const char *const buffer,
++                   id3v2_struct *const id3v2,
++                   WORD32  continue_flag,
++                   WORD32  insize)
++{
++      UWORD32 size = 0, flag;
++      WORD32 i, buf_update_val;
++      UWORD8 buf[4], frame_header[10], id3_buffer[10];
++      WORD8 *bitstream_ptr;
++      short tag_type;
++
++      WORD32 bytes_consumed = 0;
++
++      if (id3v2->header_end == 1) {
++              id3v2->bytes_consumed += insize;
++              if (id3v2->bytes_consumed < id3v2->id3_v2_header.size)
++                      return 0;
++
++              id3v2->bytes_consumed = (id3v2->id3_v2_header.size + 10);
++              return 1;
++      }
++
++      bitstream_ptr = (WORD8 *)id3_buffer;
++
++      if (!continue_flag) {
++              bytes_consumed += 3;
++              /* setting the tag type */
++              tag_type = 2;
++              id3v2->id3_v2_header.version  = buffer[bytes_consumed + 0] << 8;
++              id3v2->id3_v2_header.version |= buffer[bytes_consumed + 1];
++              id3v2->id3_v2_header.flag = buffer[bytes_consumed + 2];
++
++              /* making the msb of each byte zero */
++              buf[0] = buffer[bytes_consumed + 6] & 0x7f;
++              buf[1] = buffer[bytes_consumed + 5] & 0x7f;
++              buf[2] = buffer[bytes_consumed + 4] & 0x7f;
++              buf[3] = buffer[bytes_consumed + 3] & 0x7f;
++
++              bytes_consumed += 7;
++
++              /* concatenation the bytes after making
++               * 7th bit zero to get 28 bits size
++               */
++              size  = buf[0];
++              size |= (buf[1] << 7);
++              size |= (buf[2] << 14);
++              size |= (buf[3] << 21);
++              /* storing the size */
++              id3v2->id3_v2_header.size = size;
++
++              /* check for extended header */
++              if (id3v2->id3_v2_header.flag & 0x20) {
++                      for (i = 0; i < 10; i++)
++                              bitstream_ptr[i] = buffer[bytes_consumed + i];
++
++                      i = 0;
++                      bytes_consumed += 10;
++
++                      size = bitstream_ptr[i++]  << 24;
++                      size |= bitstream_ptr[i++] << 16;
++                      size |= bitstream_ptr[i++] << 8;
++                      size |= bitstream_ptr[i++];
++
++                      /* two bytes for flag */
++                      i += 2;
++                      {
++                              UWORD32 padding_size;
++
++                              padding_size   = bitstream_ptr[i++]  << 24;
++                              padding_size   |= bitstream_ptr[i++] << 16;
++                              padding_size   |= bitstream_ptr[i++] << 8;
++                              padding_size   |= bitstream_ptr[i++];
++
++                              /* skipping the padding and frame size
++                               * number of bytes
++                               */
++                              bytes_consumed += (padding_size + size);
++                      }
++              }
++      }
++
++      while (id3v2->header_end != 1) {
++              char *key;
++              id3_v2_frame_struct *value;
++              unsigned int avail_inp;
++
++              /* reading the 10 bytes to get the frame header */
++
++              for (i = 0; i < 10; i++)
++                      frame_header[i] = buffer[bytes_consumed + i];
++              bytes_consumed += 10;
++
++              /* getting the size from the header */
++              size = frame_header[4]  << 24;
++              size |= frame_header[5] << 16;
++              size |= frame_header[6] << 8;
++              size |= frame_header[7];
++
++              /* decoding the flag, currently not used */
++              flag = frame_header[8] << 8;
++              flag |= frame_header[9];
++
++              avail_inp = insize - bytes_consumed;
++
++              /* switching to the frame type */
++              switch (CHAR4(frame_header[0],
++                            frame_header[1],
++                            frame_header[2],
++                            frame_header[3])) {
++              case CHAR4('A', 'E', 'N', 'C'):
++              case CHAR4('A', 'P', 'I', 'C'):
++              case CHAR4('C', 'O', 'M', 'M'):
++              case CHAR4('C', 'O', 'M', 'R'):
++              case CHAR4('E', 'N', 'C', 'R'):
++              case CHAR4('E', 'Q', 'U', 'A'):
++              case CHAR4('E', 'T', 'C', 'O'):
++              case CHAR4('G', 'E', 'O', 'B'):
++              case CHAR4('G', 'R', 'I', 'D'):
++              case CHAR4('I', 'P', 'L', 'S'):
++              case CHAR4('L', 'I', 'N', 'K'):
++              case CHAR4('M', 'C', 'D', 'I'):
++              case CHAR4('M', 'L', 'L', 'T'):
++              case CHAR4('O', 'W', 'N', 'E'):
++              case CHAR4('P', 'C', 'N', 'T'):
++              case CHAR4('P', 'O', 'P', 'M'):
++              case CHAR4('P', 'O', 'S', 'S'):
++              case CHAR4('R', 'B', 'U', 'F'):
++              case CHAR4('R', 'V', 'A', 'D'):
++              case CHAR4('R', 'V', 'R', 'B'):
++              case CHAR4('S', 'Y', 'L', 'T'):
++              case CHAR4('S', 'Y', 'T', 'C'):
++              case CHAR4('T', 'B', 'P', 'M'):
++              case CHAR4('T', 'C', 'O', 'P'):
++              case CHAR4('T', 'D', 'A', 'T'):
++              case CHAR4('T', 'D', 'L', 'Y'):
++              case CHAR4('T', 'F', 'L', 'T'):
++              case CHAR4('T', 'I', 'M', 'E'):
++              case CHAR4('T', 'K', 'E', 'Y'):
++              case CHAR4('T', 'L', 'A', 'N'):
++              case CHAR4('T', 'L', 'E', 'N'):
++              case CHAR4('T', 'M', 'E', 'D'):
++              case CHAR4('T', 'O', 'F', 'N'):
++              case CHAR4('T', 'O', 'L', 'Y'):
++              case CHAR4('T', 'O', 'R', 'Y'):
++              case CHAR4('T', 'P', 'E', '2'):
++              case CHAR4('T', 'P', 'E', '3'):
++              case CHAR4('T', 'P', 'E', '4'):
++              case CHAR4('T', 'P', 'O', 'S'):
++              case CHAR4('T', 'R', 'C', 'K'):
++              case CHAR4('T', 'R', 'D', 'A'):
++              case CHAR4('T', 'R', 'S', 'N'):
++              case CHAR4('T', 'R', 'S', 'O'):
++              case CHAR4('T', 'S', 'I', 'Z'):
++              case CHAR4('T', 'S', 'R', 'C'):
++              case CHAR4('T', 'S', 'S', 'E'):
++              case CHAR4('T', 'Y', 'E', 'R'):
++              case CHAR4('T', 'X', 'X', 'X'):
++              case CHAR4('U', 'F', 'I', 'D'):
++              case CHAR4('U', 'S', 'E', 'R'):
++              case CHAR4('U', 'S', 'L', 'T'):
++              case CHAR4('W', 'C', 'O', 'M'):
++              case CHAR4('W', 'C', 'O', 'P'):
++              case CHAR4('W', 'O', 'A', 'F'):
++              case CHAR4('W', 'O', 'A', 'R'):
++              case CHAR4('W', 'O', 'A', 'S'):
++              case CHAR4('W', 'O', 'R', 'S'):
++              case CHAR4('W', 'P', 'A', 'Y'):
++              case CHAR4('W', 'P', 'U', 'B'):
++              case CHAR4('W', 'X', 'X', 'X'):
++              case CHAR4('T', 'I', 'T', '3'):
++                      key = "other_info : ";
++                      value = &id3v2->other_info;
++                      break;
++              case CHAR4('P', 'R', 'I', 'V'):
++                      key = "private_frame : ";
++                      value = &id3v2->private_frame;
++                      break;
++              case CHAR4('T', 'A', 'L', 'B'):
++                      key = "album_movie_show_title : ";
++                      value = &id3v2->album_movie_show_title;
++                      break;
++              case CHAR4('T', 'C', 'O', 'M'):
++                      key = "composer_name : ";
++                      value = &id3v2->composer_name;
++                      break;
++              case CHAR4('T', 'C', 'O', 'N'):
++                      key = "content_type : ";
++                      value = &id3v2->content_type;
++                      break;
++              case CHAR4('T', 'E', 'N', 'C'):
++                      key = "encoded_by : ";
++                      value = &id3v2->encoded_by;
++                      break;
++              case CHAR4('T', 'E', 'X', 'T'):
++                      key = "lyricist_text_writer : ";
++                      value = &id3v2->lyricist_text_writer;
++                      break;
++              case CHAR4('T', 'I', 'T', '1'):
++                      key = "content_group_description : ";
++                      value = &id3v2->content_group_description;
++                      break;
++              case CHAR4('T', 'I', 'T', '2'):
++                      key = "title_songname_content_description : ";
++                      value = &id3v2->title_songname_content_description;
++                      break;
++              case CHAR4('T', 'O', 'A', 'L'):
++                      key = "original_album_movie_show_title : ";
++                      value = &id3v2->original_album_movie_show_title;
++                      break;
++              case CHAR4('T', 'O', 'P', 'E'):
++                      key = "original_artist_performer : ";
++                      value = &id3v2->original_artist_performer;
++                      break;
++              case CHAR4('T', 'O', 'W', 'N'):
++                      key = "file_owner_licensee : ";
++                      value = &id3v2->file_owner_licensee;
++                      break;
++              case CHAR4('T', 'P', 'E', '1'):
++                      key = "lead_performer_soloist : ";
++                      value = &id3v2->lead_performer_soloist;
++                      break;
++              case CHAR4('T', 'P', 'U', 'B'):
++                      key =  "publisher : ";
++                      value = &id3v2->publisher;
++                      break;
++              default:
++                      /* skipping the read 10 bytes */
++                      buf_update_val = -10;
++                      id3v2->header_end = 1;
++                      value = 0;
++                      key = 0;
++                      break;
++              }
++
++              if (value != 0)
++                      buf_update_val = get_info(&buffer[bytes_consumed],
++                                                avail_inp, size, value);
++
++              /* Negative value for buf_update_val means one of two things:
++               * 1. The default case happened and we're done with ID3V2 tag
++               *    frames, or
++               * 2. get_info() returned -1 to indicate that more input is
++               *    required to decode this frame of the tag.
++               */
++              if (buf_update_val >= 0)
++                      display2(value,
++                               umin(value->max_size, buf_update_val), key);
++
++              if (buf_update_val == -1) {
++                      id3v2->bytes_consumed += bytes_consumed;
++                      return 1;
++              }
++
++              bytes_consumed += buf_update_val;
++
++              /* Is there enough input left (10 bytes) to begin
++               * decoding another frame? If not, bag out temporarily
++               * now. The caller will refill our input buffer and
++               * call us again with continue_flag == 1.
++               */
++              if (insize - bytes_consumed < 10) {
++                      id3v2->bytes_consumed += bytes_consumed;
++                      return 0; /* not completely decoded */
++              }
++      }
++
++      id3v2->bytes_consumed += bytes_consumed;
++      if ((id3v2->bytes_consumed + 10) < id3v2->id3_v2_header.size)
++              return 0; /* not completely decoded */
++
++      return 1; /* completely decoded */
++}
++
++/*******************************************************
++ *  function name   : get_id3_v2_bytes
++ *
++ *  description     : tells the size of ID3V2 tag.
++ *
++ *  arguments       : input parameters
++ *                   buffer          input buffer
++ *
++ *  values returned : bytes consumed
++ ********************************************************/
++WORD32 get_id3_v2_bytes(UWORD8 *buffer)
++{
++      WORD32 size;
++
++      /* making the msb of each byte zero */
++      size  =  (buffer[9] & 0x7f);
++      size |= ((buffer[8] & 0x7f) << 7);
++      size |= ((buffer[7] & 0x7f) << 14);
++      size |= ((buffer[6] & 0x7f) << 21);
++
++      return (size + 10);
++}
++
++/****************************************************
++ *  function name   : get_info
++ *
++ *  description     : read the frame information from the input buffer.
++ *
++ *  arguments       : input parameters
++ *
++ *  values returned : update value for buffer
++ ****************************************************/
++WORD32 get_info(const char *inp_buffer,
++              unsigned int         avail_inp,
++              WORD32               tag_size,
++              id3_v2_frame_struct *dest)
++{
++      WORD32 j;
++
++      /* setting the tag to indicate the presence of frame */
++      dest->tag_present = 1;
++      /* If there isn't enough input available, we punt back to the top
++       * level and ask for more.
++       */
++      if (avail_inp < umin(tag_size, dest->max_size))
++              return -1;
++
++      if (dest->max_size >= tag_size) {
++              for (j = 0; j < tag_size ; j++)
++                      dest->frame_data[j] = inp_buffer[j];
++      } else {
++              dest->exceeds_buffer_size = 1;
++              for (j = 0; j < dest->max_size ; j++)
++                      dest->frame_data[j] = inp_buffer[j];
++      }
++      return tag_size;
++}
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/tinycompress/tinycompress/0002-cplay-Support-wave-file.patch b/recipes-multimedia/tinycompress/tinycompress/0002-cplay-Support-wave-file.patch
new file mode 100755 (executable)
index 0000000..9189bc0
--- /dev/null
@@ -0,0 +1,221 @@
+From 4d4bc0a958fe254531920095fbabc241aad88113 Mon Sep 17 00:00:00 2001
+From: Shengjiu Wang <shengjiu.wang@nxp.com>
+Date: Tue, 28 Jul 2020 13:00:36 +0800
+Subject: [PATCH] cplay: Support wave file
+
+The supported format is mono/stereo, S16_LE/S32_LE, 8kHz-192kHz.
+Command is:
+cplay  -c x -I PCM test.wav
+
+Upstream-Status: Inappropriate [i.MX specific]
+Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
+---
+ include/tinycompress/wave_formats.h |  51 +++++++++++++
+ src/utils/cplay.c                   | 107 ++++++++++++++++++++++++++++
+ 2 files changed, 158 insertions(+)
+ create mode 100644 include/tinycompress/wave_formats.h
+
+diff --git a/include/tinycompress/wave_formats.h b/include/tinycompress/wave_formats.h
+new file mode 100644
+index 000000000000..4e2e009206cf
+--- /dev/null
++++ b/include/tinycompress/wave_formats.h
+@@ -0,0 +1,51 @@
++#ifndef WAVE_FORMATS_H
++#define WAVE_FORMATS_H                1
++
++#define COMPOSE_ID(a,b,c,d)   ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
++
++#define WAV_RIFF              COMPOSE_ID('R','I','F','F')
++#define WAV_RIFX              COMPOSE_ID('R','I','F','X')
++#define WAV_WAVE              COMPOSE_ID('W','A','V','E')
++#define WAV_FMT                       COMPOSE_ID('f','m','t',' ')
++#define WAV_DATA              COMPOSE_ID('d','a','t','a')
++
++/* WAVE fmt block constants from Microsoft mmreg.h header */
++#define WAV_FMT_PCM             0x0001
++#define WAV_FMT_IEEE_FLOAT      0x0003
++#define WAV_FMT_DOLBY_AC3_SPDIF 0x0092
++#define WAV_FMT_EXTENSIBLE      0xfffe
++
++/* Used with WAV_FMT_EXTENSIBLE format */
++#define WAV_GUID_TAG          "\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71"
++
++typedef struct {
++      u_int magic;            /* 'RIFF' */
++      u_int length;           /* filelen */
++      u_int type;             /* 'WAVE' */
++} WaveHeader;
++
++typedef struct {
++      u_short format;         /* see WAV_FMT_* */
++      u_short channels;
++      u_int sample_fq;        /* frequence of sample */
++      u_int byte_p_sec;
++      u_short byte_p_spl;     /* samplesize; 1 or 2 bytes */
++      u_short bit_p_spl;      /* 8, 12 or 16 bit */
++} WaveFmtBody;
++
++typedef struct {
++      WaveFmtBody format;
++      u_short ext_size;
++      u_short bit_p_spl;
++      u_int channel_mask;
++      u_short guid_format;    /* WAV_FMT_* */
++      u_char guid_tag[14];    /* WAV_GUID_TAG */
++} WaveFmtExtensibleBody;
++
++typedef struct {
++      u_int type;             /* 'data' */
++      u_int length;           /* samplecount */
++} WaveChunkHeader;
++
++
++#endif                                /* FORMATS */
+diff --git a/src/utils/cplay.c b/src/utils/cplay.c
+index 5b749419e731..8882f4d9746d 100644
+--- a/src/utils/cplay.c
++++ b/src/utils/cplay.c
+@@ -1,4 +1,6 @@
+ /*
++ * Copyright 2020 NXP
++ *
+  * This file is provided under a dual BSD/LGPLv2.1 license.  When using or
+  * redistributing this file, you may do so under either license.
+  *
+@@ -73,6 +75,8 @@
+ #include "tinycompress/tinycompress.h"
+ #include "tinycompress/tinymp3.h"
+ #include "tinycompress/id3_tag_decode.h"
++#include "tinycompress/wave_formats.h"
++#include <alsa/asoundlib.h>
+ static int verbose;
+ static const unsigned int DEFAULT_CODEC_ID = SND_AUDIOCODEC_PCM;
+@@ -166,6 +170,77 @@ static int parse_mp3_header(struct mp3_header *header, unsigned int *num_channel
+       return 0;
+ }
++static int parse_wav_header(FILE *file, unsigned int *num_channels, unsigned int *sample_rate,
++                    unsigned int *format) {
++      WaveHeader wave_header;
++      WaveChunkHeader chunk_header;
++      WaveFmtBody fmt_body;
++      int more_chunks = 1;
++
++      fread(&wave_header, sizeof(WaveHeader), 1, file);
++      if ((wave_header.magic != WAV_RIFF) ||
++          (wave_header.type != WAV_WAVE)) {
++              fprintf(stderr, "Error: it is not a riff/wave file\n");
++              return -1;
++      }
++
++      do {
++              fread(&chunk_header, sizeof(WaveChunkHeader), 1, file);
++              switch (chunk_header.type) {
++              case WAV_FMT:
++                      fread(&fmt_body, sizeof(WaveFmtBody), 1, file);
++                      /* If the format header is larger, skip the rest */
++                      if (chunk_header.length > sizeof(WaveFmtBody))
++                              fseek(file, chunk_header.length - sizeof(WaveFmtBody), SEEK_CUR);
++
++                      *num_channels = fmt_body.channels;
++                      *sample_rate = fmt_body.sample_fq;
++
++                      switch (fmt_body.bit_p_spl) {
++                      case 8:
++                              *format = SND_PCM_FORMAT_U8;
++                              break;
++                      case 16:
++                              *format = SND_PCM_FORMAT_S16_LE;
++                              break;
++                      case 24:
++                              switch (fmt_body.byte_p_spl / fmt_body.channels) {
++                              case 3:
++                                      *format = SND_PCM_FORMAT_S24_3LE;
++                                      break;
++                              case 4:
++                                      *format = SND_PCM_FORMAT_S24_LE;
++                                      break;
++                              default:
++                                      fprintf(stderr, "format error\n");
++                                      return -1;
++                              }
++                              break;
++                      case 32:
++                              if (fmt_body.format == WAV_FMT_PCM) {
++                                      *format = SND_PCM_FORMAT_S32_LE;
++                              } else if (fmt_body.format == WAV_FMT_IEEE_FLOAT) {
++                                      *format = SND_PCM_FORMAT_FLOAT_LE;
++                              }
++                              break;
++                      default:
++                              fprintf(stderr, "format error\n");
++                              return -1;
++                      }
++                      break;
++              case WAV_DATA:
++                      /* Stop looking for chunks */
++                      more_chunks = 0;
++                      break;
++              default:
++                      /* Unknown chunk, skip bytes */
++                      fseek(file, chunk_header.length, SEEK_CUR);
++              }
++      } while (more_chunks);
++
++      return 0;
++}
++
+ static int print_time(struct compress *compress)
+ {
+       unsigned int avail;
+@@ -385,6 +460,35 @@ void get_codec_iec(FILE *file, struct compr_config *config,
+       codec->format = 0;
+ }
++void get_codec_pcm(FILE *file, struct compr_config *config,
++              struct snd_codec *codec)
++{
++      unsigned int channels, rate, format;
++
++      if (parse_wav_header(file, &channels, &rate, &format) == -1) {
++              fclose(file);
++              exit(EXIT_FAILURE);
++      }
++
++      if (channels > 2 || (format != SND_PCM_FORMAT_S16_LE && format != SND_PCM_FORMAT_S32_LE) ||
++          rate > 192000) {
++              fprintf(stderr, "unsupported wave file\n");
++              fclose(file);
++              exit(EXIT_FAILURE);
++      }
++
++      codec->id = SND_AUDIOCODEC_PCM;
++      codec->ch_in = channels;
++      codec->ch_out = channels;
++      codec->sample_rate = rate;
++      codec->bit_rate = 0;
++      codec->rate_control = 0;
++      codec->profile = SND_AUDIOPROFILE_PCM;
++      codec->level = 0;
++      codec->ch_mode = 0;
++      codec->format = format;
++}
++
+ void play_samples(char *name, unsigned int card, unsigned int device,
+               unsigned long buffer_size, unsigned int frag,
+               unsigned long codec_id)
+@@ -411,6 +515,9 @@ void play_samples(char *name, unsigned int card, unsigned int device,
+       case SND_AUDIOCODEC_IEC61937:
+               get_codec_iec(file, &config, &codec);
+               break;
++      case SND_AUDIOCODEC_PCM:
++              get_codec_pcm(file, &config, &codec);
++              break;
+       default:
+               fprintf(stderr, "codec ID %ld is not supported\n", codec_id);
+               exit(EXIT_FAILURE);
+-- 
+2.27.0
+
diff --git a/recipes-multimedia/tinycompress/tinycompress/0003-cplay-Add-pause-feature.patch b/recipes-multimedia/tinycompress/tinycompress/0003-cplay-Add-pause-feature.patch
new file mode 100755 (executable)
index 0000000..7d8492b
--- /dev/null
@@ -0,0 +1,146 @@
+From 6f778c21ee357a662cdd758cff578a3e4b85eedf Mon Sep 17 00:00:00 2001
+From: Zhang Peng <peng.zhang_8@nxp.com>
+Date: Tue, 4 Aug 2020 15:29:29 +0800
+Subject: [PATCH] cplay: Add pause feature
+
+Add option: -p pause
+
+Upstream-Status: Inappropriate [i.MX specific]
+Signed-off-by: Zhang Peng <peng.zhang_8@nxp.com>
+---
+ src/utils/cplay.c | 56 +++++++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 52 insertions(+), 4 deletions(-)
+
+diff --git a/src/utils/cplay.c b/src/utils/cplay.c
+index 8882f4d..8e3dcbb 100644
+--- a/src/utils/cplay.c
++++ b/src/utils/cplay.c
+@@ -117,6 +117,9 @@ static void usage(void)
+               "-f\tfragments\n\n"
+               "-v\tverbose mode\n"
+               "-h\tPrints this help list\n\n"
++              "-p\tpause\n"
++              "-m\tpause blocks\n"
++              "-n\tpause time duration\n"
+               "Example:\n"
+               "\tcplay -c 1 -d 2 test.mp3\n"
+               "\tcplay -f 5 test.mp3\n\n"
+@@ -133,7 +136,8 @@ static void usage(void)
+ void play_samples(char *name, unsigned int card, unsigned int device,
+               unsigned long buffer_size, unsigned int frag,
+-              unsigned long codec_id);
++              unsigned long codec_id, int pause_count, int pause_block,
++              int pause_duration);
+ struct mp3_header {
+       uint16_t sync;
+@@ -262,12 +266,15 @@ int main(int argc, char **argv)
+       int c, i;
+       unsigned int card = 0, device = 0, frag = 0;
+       unsigned int codec_id = SND_AUDIOCODEC_MP3;
++      int pause_count = 0;
++      int pause_block = 6;
++      int pause_duration = 10;
+       if (argc < 2)
+               usage();
+       verbose = 0;
+-      while ((c = getopt(argc, argv, "hvb:f:c:d:I:")) != -1) {
++      while ((c = getopt(argc, argv, "hvb:f:c:d:I:p:m:n:")) != -1) {
+               switch (c) {
+               case 'h':
+                       usage();
+@@ -306,6 +313,23 @@ int main(int argc, char **argv)
+               case 'v':
+                       verbose = 1;
+                       break;
++              case 'p':
++                      pause_count = strtol(optarg, NULL, 10);
++                      break;
++              case 'm':
++                      pause_block = strtol(optarg, NULL, 10);
++                      if (pause_duration < 0) {
++                              printf("Set wrong paramter! Set duration default 6.\n");
++                              pause_duration = 6;
++                      }
++                      break;
++              case 'n':
++                      pause_duration = strtol(optarg, NULL, 10);
++                      if (pause_duration < 0) {
++                              printf("Set wrong paramter! Set duration default 10.\n");
++                              pause_duration = 10;
++                      }
++                      break;
+               default:
+                       exit(EXIT_FAILURE);
+               }
+@@ -315,7 +339,7 @@ int main(int argc, char **argv)
+       file = argv[optind];
+-      play_samples(file, card, device, buffer_size, frag, codec_id);
++      play_samples(file, card, device, buffer_size, frag, codec_id, pause_count, pause_block, pause_duration);
+       fprintf(stderr, "Finish Playing.... Close Normally\n");
+       exit(EXIT_SUCCESS);
+@@ -491,7 +515,8 @@ void get_codec_pcm(FILE *file, struct compr_config *config,
+ void play_samples(char *name, unsigned int card, unsigned int device,
+               unsigned long buffer_size, unsigned int frag,
+-              unsigned long codec_id)
++              unsigned long codec_id, int pause_count, int pause_block,
++              int pause_duration)
+ {
+       struct compr_config config;
+       struct snd_codec codec;
+@@ -499,6 +524,7 @@ void play_samples(char *name, unsigned int card, unsigned int device,
+       FILE *file;
+       char *buffer;
+       int size, num_read, wrote;
++      int write_count = 0;
+       if (verbose)
+               printf("%s: entry\n", __func__);
+@@ -574,6 +600,13 @@ void play_samples(char *name, unsigned int card, unsigned int device,
+       if (verbose)
+               printf("%s: You should hear audio NOW!!!\n", __func__);
++      if (pause_count > 0) {
++              printf("sleep...\n");
++              compress_pause(compress);
++              sleep(pause_duration);
++              compress_resume(compress);
++      }
++
+       do {
+               num_read = fread(buffer, 1, size, file);
+               if (num_read > 0) {
+@@ -592,8 +625,23 @@ void play_samples(char *name, unsigned int card, unsigned int device,
+                               printf("%s: wrote %d\n", __func__, wrote);
+                       }
+               }
++              write_count++;
++              if ((pause_count > 0) && (write_count % pause_block == 0)) {
++                              printf("pause...\n");
++                              compress_pause(compress);
++                              sleep(pause_duration);
++                              printf("pause release...\n");
++                              compress_resume(compress);
++                              pause_count--;
++              }
+       } while (num_read > 0);
++      if (pause_count > 0) {
++              compress_pause(compress);
++              sleep(5);
++              compress_resume(compress);
++      }
++
+       if (verbose)
+               printf("%s: exit success\n", __func__);
+       /* issue drain if it supports */
+-- 
+2.17.1
+
diff --git a/recipes-multimedia/tinycompress/tinycompress/0004-tinycompress-pass-NULL-buffer-with-0-size-to-driver.patch b/recipes-multimedia/tinycompress/tinycompress/0004-tinycompress-pass-NULL-buffer-with-0-size-to-driver.patch
new file mode 100755 (executable)
index 0000000..dfedd18
--- /dev/null
@@ -0,0 +1,40 @@
+From a2892bf5db7520689fa9cb1d1589fa804bd9dc1a Mon Sep 17 00:00:00 2001
+From: Bing Song <bing.song@nxp.com>
+Date: Tue, 18 Aug 2020 15:26:51 +0800
+Subject: [PATCH] tinycompress: pass NULL buffer with 0 size to driver.
+
+The NULL buffer with 0 size to indecate driver drain input data with
+non-block mode. The defaul drain is block mode.
+
+Upstream-Status: Inappropriate [i.MX specific]
+Signed-off-by: Bing Song <bing.song@nxp.com>
+---
+ src/lib/compress.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/lib/compress.c b/src/lib/compress.c
+index bba4fcf..d66df0b 100644
+--- a/src/lib/compress.c
++++ b/src/lib/compress.c
+@@ -315,7 +315,8 @@ int compress_write(struct compress *compress, const void *buf, unsigned int size
+       fds.events = POLLOUT;
+       /*TODO: treat auto start here first */
+-      while (size) {
++      /* NULL buffer with 0 size for non-block drain */
++      do {
+               if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail))
+                       return oops(compress, errno, "cannot get avail");
+@@ -357,7 +358,7 @@ int compress_write(struct compress *compress, const void *buf, unsigned int size
+               size -= written;
+               cbuf += written;
+               total += written;
+-      }
++      } while (size);
+       return total;
+ }
+-- 
+2.17.1
+
diff --git a/recipes-multimedia/tinycompress/tinycompress/0005-cplay-Support-aac-streams.patch b/recipes-multimedia/tinycompress/tinycompress/0005-cplay-Support-aac-streams.patch
new file mode 100755 (executable)
index 0000000..2f36551
--- /dev/null
@@ -0,0 +1,251 @@
+From 2912f8573cea25fbd38ac7a8b68af2ea6a05e599 Mon Sep 17 00:00:00 2001
+From: Zhang Peng <peng.zhang_8@nxp.com>
+Date: Wed, 28 Oct 2020 19:08:53 +0800
+Subject: [PATCH] cplay: Support aac streams
+
+Support run aac format streams for cplay.
+
+Upstream-Status: Inappropriate [i.MX specific]
+Signed-off-by: Zhang Peng <peng.zhang_8@nxp.com>
+---
+ src/utils/cplay.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 210 insertions(+)
+
+diff --git a/src/utils/cplay.c b/src/utils/cplay.c
+index 8e3dcbb..2a1464a 100644
+--- a/src/utils/cplay.c
++++ b/src/utils/cplay.c
+@@ -245,6 +245,190 @@ static int parse_wav_header(FILE *file, unsigned int *num_channels, unsigned int
+       return 0;
+ }
++int find_adts_header(FILE *file, unsigned int *num_channels, unsigned int *sample_rate, unsigned int *format)
++{
++      int ret;
++      unsigned char buf[5];
++
++      ret = fread(buf, sizeof(buf), 1, file);
++      if (ret < 0) {
++              fprintf(stderr, "open file error: %d\n", ret);
++              return 0;
++      }
++      fseek(file, 0, SEEK_SET);
++
++      if ((buf[0] != 0xff) || (buf[1] & 0xf0 != 0xf0))
++              return 0;
++      /* mpeg id */
++      switch (buf[1]>>3 & 0x1) {
++      case 0x0:
++              *format = SND_AUDIOSTREAMFORMAT_MP4ADTS;
++              break;
++      case 0x1:
++              *format = SND_AUDIOSTREAMFORMAT_MP2ADTS;
++              break;
++      default:
++              fprintf(stderr, "can't find stream format\n");
++              break;
++      }
++      /* sample_rate */
++      switch (buf[2]>>2 & 0xf) {
++      case 0x0:
++              *sample_rate = 96000;
++              break;
++      case 0x1:
++              *sample_rate = 88200;
++              break;
++      case 0x2:
++              *sample_rate = 64000;
++              break;
++      case 0x3:
++              *sample_rate = 48000;
++              break;
++      case 0x4:
++              *sample_rate = 44100;
++              break;
++      case 0x5:
++              *sample_rate = 32000;
++              break;
++      case 0x6:
++              *sample_rate = 24000;
++              break;
++      case 0x7:
++              *sample_rate = 22050;
++              break;
++      case 0x8:
++              *sample_rate = 16000;
++              break;
++      case 0x9:
++              *sample_rate = 12000;
++              break;
++      case 0xa:
++              *sample_rate = 11025;
++              break;
++      case 0xb:
++              *sample_rate = 8000;
++              break;
++      case 0xc:
++              *sample_rate = 7350;
++              break;
++      default:
++              break;
++      }
++      /* channel */
++      switch (((buf[2]&0x1) << 2) | (buf[3]>>6)) {
++      case 1:
++              *num_channels = 1;
++              break;
++      case 2:
++              *num_channels = 2;
++              break;
++      case 3:
++              *num_channels = 3;
++              break;
++      case 4:
++              *num_channels = 4;
++              break;
++      case 5:
++              *num_channels = 5;
++              break;
++      case 6:
++              *num_channels = 6;
++              break;
++      case 7:
++              *num_channels = 7;
++              break;
++      default:
++              break;
++      }
++      return 1;
++}
++
++static const int aac_sample_rates[] = { 96000, 88200, 64000, 48000, 44100,
++  32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
++};
++
++#define MAX_SR_NUM sizeof(aac_sample_rates)/sizeof(aac_sample_rates[0])
++
++static int get_sample_rate_from_index(int sr_index)
++{
++      if (sr_index >= 0 && sr_index < MAX_SR_NUM)
++              return aac_sample_rates[sr_index];
++
++      return 0;
++}
++
++int find_adif_header(FILE *file, unsigned int *num_channels, unsigned int *sample_rate, unsigned int *format)
++{
++      int ret;
++      unsigned char adif_id[4];
++      unsigned char adif_header[20];
++      int bitstream_type;
++      int bitrate;
++      int object_type;
++      int sr_index;
++      int skip_size = 0;
++
++      ret = fread(adif_id, sizeof(unsigned char), 4, file);
++      if (ret < 0) {
++              fprintf(stderr, "read data from file err: %d\n", ret);
++              return 0;
++      }
++      /* adif id */
++      if ((adif_id[0] != 0x41) || (adif_id[1] != 0x44) ||
++              (adif_id[2] != 0x49) || (adif_id[3] != 0x46))
++              return 0;
++
++      fread(adif_header, sizeof(unsigned char), 20, file);
++
++      /* copyright string */
++      if (adif_header[0] & 0x80)
++              skip_size = 9;
++
++      bitstream_type = adif_header[0 + skip_size] & 0x10;
++      bitrate =
++               ((unsigned int) (adif_header[0 + skip_size] & 0x0f) << 19) |
++               ((unsigned int) adif_header[1 + skip_size] << 11) |
++               ((unsigned int) adif_header[2 + skip_size] << 3) |
++               ((unsigned int) adif_header[3 + skip_size] & 0xe0);
++
++      if (bitstream_type == 0) {
++              object_type = ((adif_header[6 + skip_size] & 0x01) << 1) |
++                      ((adif_header[7 + skip_size] & 0x80) >> 7);
++              sr_index = (adif_header[7 + skip_size] & 0x78) >> 3;
++      }
++      /* VBR */
++      else {
++              object_type = (adif_header[4 + skip_size] & 0x18) >> 3;
++              sr_index = ((adif_header[4 + skip_size] & 0x07) << 1) |
++                      ((adif_header[5 + skip_size] & 0x80) >> 7);
++      }
++
++      /* sample rate */
++      *sample_rate = get_sample_rate_from_index(sr_index);
++
++      /* FIXME: assume channels is 2 */
++      *num_channels = 2;
++
++      *format = SND_AUDIOSTREAMFORMAT_ADIF;
++      fseek(file, 0, SEEK_SET);
++      return 1;
++}
++
++static int parse_aac_header(FILE *file, unsigned int *num_channels, unsigned int *sample_rate, unsigned int *format)
++{
++      if (find_adts_header(file, num_channels, sample_rate, format))
++              return 1;
++      else if (find_adif_header(file, num_channels, sample_rate, format))
++              return 1;
++      else {
++              fprintf(stderr, "can't find streams format\n");
++              return 0;
++      }
++
++      return 1;
++}
++
+ static int print_time(struct compress *compress)
+ {
+       unsigned int avail;
+@@ -513,6 +697,29 @@ void get_codec_pcm(FILE *file, struct compr_config *config,
+       codec->format = format;
+ }
++void get_codec_aac(FILE *file, struct compr_config *config,
++              struct snd_codec *codec)
++{
++      unsigned int channels, rate, format;
++
++      if (parse_aac_header(file, &channels, &rate, &format) == 0) {
++              fclose(file);
++              exit(EXIT_FAILURE);
++      };
++      fseek(file, 0, SEEK_SET);
++
++      codec->id = SND_AUDIOCODEC_AAC;
++      codec->ch_in = channels;
++      codec->ch_out = channels;
++      codec->sample_rate = rate;
++      codec->bit_rate = 0;
++      codec->rate_control = 0;
++      codec->profile = SND_AUDIOPROFILE_AAC;
++      codec->level = 0;
++      codec->ch_mode = 0;
++      codec->format = format;
++
++}
+ void play_samples(char *name, unsigned int card, unsigned int device,
+               unsigned long buffer_size, unsigned int frag,
+               unsigned long codec_id, int pause_count, int pause_block,
+@@ -544,6 +751,9 @@ void play_samples(char *name, unsigned int card, unsigned int device,
+       case SND_AUDIOCODEC_PCM:
+               get_codec_pcm(file, &config, &codec);
+               break;
++      case SND_AUDIOCODEC_AAC:
++              get_codec_aac(file, &config, &codec);
++              break;
+       default:
+               fprintf(stderr, "codec ID %ld is not supported\n", codec_id);
+               exit(EXIT_FAILURE);
+-- 
+2.17.1
+
diff --git a/recipes-multimedia/tinycompress/tinycompress_1.1.6.bb b/recipes-multimedia/tinycompress/tinycompress_1.1.6.bb
new file mode 100644 (file)
index 0000000..6c0fc72
--- /dev/null
@@ -0,0 +1,16 @@
+DESCRIPTION = "A library to handle compressed formats like MP3 etc."
+LICENSE = "LGPLv2.1 | BSD-3-Clause"
+LIC_FILES_CHKSUM = "file://COPYING;md5=cf9105c1a2d4405cbe04bbe3367373a0"
+DEPENDS = "alsa-lib"
+
+SRC_URI = "git://git.alsa-project.org/tinycompress.git;protocol=git;branch=master \
+           file://0001-tinycompress-Add-id3-decoding.patch \
+           file://0002-cplay-Support-wave-file.patch \
+           file://0003-cplay-Add-pause-feature.patch \
+           file://0004-tinycompress-pass-NULL-buffer-with-0-size-to-driver.patch \
+           file://0005-cplay-Support-aac-streams.patch \
+"
+SRCREV = "995f2ed91045dad8c20485ab1a64727d22cd92e5"
+S = "${WORKDIR}/git"
+
+inherit autotools pkgconfig