222 lines
6.1 KiB
Diff
Executable File
222 lines
6.1 KiB
Diff
Executable File
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
|
|
|