From cfa76b52781f33c566effce9aecc63572b6c128a Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 22 Jul 2014 19:23:05 -0600 Subject: [PATCH] Flesh out a few more PCM methods, still incomplete. Re-vision PCM structure definition --- audio/audio.c | 5 +- audio/pcm_decode.c | 395 ++++++++++++++++++++++++++++++-------- drivers/audio/wm8904.c | 2 +- include/nuttx/audio/pcm.h | 59 ++++-- 4 files changed, 354 insertions(+), 107 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index 32e067bb6f..f5f815431f 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -386,9 +386,10 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) FAR struct audio_caps_s *caps = (FAR struct audio_caps_s*)((uintptr_t)arg); DEBUGASSERT(lower->ops->getcaps != NULL); - audvdbg("AUDIOIOC_GETCAPS: Device=%d", caps->ac_type); + audvdbg("AUDIOIOC_GETCAPS: Device=%d\n", caps->ac_type); /* Call the lower-half driver capabilities handler */ + ret = lower->ops->getcaps(lower, caps->ac_type, caps); } break; @@ -399,7 +400,7 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) (FAR const struct audio_caps_desc_s*)((uintptr_t)arg); DEBUGASSERT(lower->ops->configure != NULL); - audvdbg("AUDIOIOC_INITIALIZE: Device=%d", caps->caps.ac_type); + audvdbg("AUDIOIOC_INITIALIZE: Device=%d\n", caps->caps.ac_type); /* Call the lower-half driver configure handler */ diff --git a/audio/pcm_decode.c b/audio/pcm_decode.c index 6cf7327fce..56cf7079b1 100644 --- a/audio/pcm_decode.c +++ b/audio/pcm_decode.c @@ -64,6 +64,7 @@ ****************************************************************************/ /* Configuration ************************************************************/ +#define CONFIG_PCM_DEBUG 1 /* For now */ /**************************************************************************** * Private Types @@ -93,16 +94,41 @@ struct pcm_decode_s */ FAR struct audio_lowerhalf_s *lower; + + /* This is a copy of the WAV file header, in host endian order */ + + struct wav_header_s wav; + + /* Set to true once we have parse a valid header and have begun stream + * audio. + */ + + bool streaming; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ +/* Helper functions *********************************************************/ + #ifdef CONFIG_PCM_DEBUG -static void pcm_dump(FAR const struct wav_header_s *wav) +static void pcm_dump(FAR const struct wav_header_s *wav); +#else +# define pcm_dump(w) #endif +#ifdef CONFIG_ENDIAN_BIG +static uint16_t pcm_leuint16(uint16_t value); +static uint16_t pcm_leuint32(uint32_t value); +#else +# define pcm_leuint16(v) (v) +# define pcm_leuint32(v) (v) +#endif + +static inline bool pcm_validwav(FAR const struct wav_header_s *wav); +static bool pcm_parsewav(FAR struct pcm_decode_s *priv, uint8_t *data); + /* struct audio_lowerhalf_s methods *****************************************/ static int pcm_getcaps(FAR struct audio_lowerhalf_s *dev, int type, @@ -185,27 +211,63 @@ static int pcm_release(FAR struct audio_lowerhalf_s *dev); * Name: pcm_dump * * Description: - * Dump a WAV file header. + * Dump a WAV file header. * ****************************************************************************/ #ifdef CONFIG_PCM_DEBUG static void pcm_dump(FAR const struct wav_header_s *wav) { - printf( "Wave file header\n"); - printf( " Chunk ID: 0x%08x\n", wav->chkid); - printf( " Chunk Size: %u\n", wav->chklen); - printf( " Format: 0x%08x\n", wav->format); - printf( " SubChunk ID: 0x%08x\n", wav->subchkid1); - printf( " Subchunk1 Size: %u\n", wav->subchklen1); - printf( " Audio Format: 0x%04x\n", wav->compression); - printf( " Num. Channels: %d\n", wav->nchannels); - printf( " Sample Rate: %u\n", wav->samprate); - printf( " Byte Rate: %u\n", wav->byterate); - printf( " Block Align: %d\n", wav->align); - printf( " Bits Per Sample: %d\n", wav->bpsamp); - printf( " Subchunk2 ID: 0x%08x\n", wav->subchkid2); - printf( " Subchunk2 Size: %u\n", wav->subchklen2); + dbg( "Wave file header\n"); + dbg( " Chunk ID: 0x%08x\n", wav->hdr.chunkid); + dbg( " Chunk Size: %u\n", wav->hdr.chunklen); + dbg( " Format: 0x%08x\n", wav->hdr.format); + dbg( " SubChunk ID: 0x%08x\n", wav->fmt.chunkid); + dbg( " Subchunk1 Size: %u\n", wav->fmt.chunklen); + dbg( " Audio Format: 0x%04x\n", wav->fmt.format); + dbg( " Num. Channels: %d\n", wav->fmt.nchannels); + dbg( " Sample Rate: %u\n", wav->fmt.samprate); + dbg( " Byte Rate: %u\n", wav->fmt.byterate); + dbg( " Block Align: %d\n", wav->fmt.align); + dbg( " Bits Per Sample: %d\n", wav->fmt.bpsamp); + dbg( " Subchunk2 ID: 0x%08x\n", wav->data.chunkid); + dbg( " Subchunk2 Size: %u\n", wav->data.chunklen); +} +#endif + +/**************************************************************************** + * Name: pcm_leuint16 + * + * Description: + * Get a 16-bit value stored in little endian order for a big-endian + * machine. + * + ****************************************************************************/ + +#ifdef CONFIG_ENDIAN_BIG +static uint16_t pcm_leuint16(uint16_t value) +{ + return (((value & 0x00ff) << 8) | + ((value >> 8) & 0x00ff)); +} +#endif + +/**************************************************************************** + * Name: pcm_leuint16 + * + * Description: + * Get a 16-bit value stored in little endian order for a big-endian + * machine. + * + ****************************************************************************/ + +#ifdef CONFIG_ENDIAN_BIG +static uint16_t pcm_leuint32(uint32_t value) +{ + return (((value & 0x000000ff) << 24) | + ((value & 0x0000ff00) << 8) | + ((value & 0x00ff0000) >> 8) | + ((value & 0xff000000) >> 24)); } #endif @@ -213,27 +275,71 @@ static void pcm_dump(FAR const struct wav_header_s *wav) * Name: pcm_validwav * * Description: - * Return true if this is a valid WAV file header + * Return true if this is a valid WAV file header * ****************************************************************************/ static inline bool pcm_validwav(FAR const struct wav_header_s *wav) { - return (wav->chkid == WAV_CHUNKID && - wav->format == WAV_FORMAT && - wav->subchklen1 == WAV_SUBCHKLEN1); + return (wav->hdr.chunkid == WAV_HDR_CHUNKID && + wav->hdr.format == WAV_HDR_FORMAT && + wav->fmt.chunkid == WAV_FMT_CHUNKID && + wav->fmt.chunklen == WAV_FMT_CHUNKLEN && + wav->fmt.format == WAV_FMT_FORMAT && + wav->data.chunkid == WAV_DATA_CHUNKID); +} + +/**************************************************************************** + * Name: pcm_parsewav + * + * Description: + * Parse and verify the WAV file header. + * + ****************************************************************************/ + +static bool pcm_parsewav(FAR struct pcm_decode_s *priv, uint8_t *data) +{ + FAR const struct wav_header_s *wav = (FAR const struct wav_header_s *)data; + + /* Transfer the purported WAV file header into our private storage, + * correcting for endian issues as needed. + */ + + priv->wav.hdr.chunkid = pcm_leuint32(wav->hdr.chunkid); + priv->wav.hdr.chunklen = pcm_leuint32(wav->hdr.chunklen); + priv->wav.hdr.format = pcm_leuint32(wav->hdr.format); + + priv->wav.fmt.chunkid = pcm_leuint32(wav->fmt.chunkid); + priv->wav.fmt.chunklen = pcm_leuint32(wav->fmt.chunklen); + priv->wav.fmt.format = pcm_leuint16(wav->fmt.format); + priv->wav.fmt.nchannels = pcm_leuint16(wav->fmt.nchannels); + priv->wav.fmt.samprate = pcm_leuint32(wav->fmt.samprate); + priv->wav.fmt.byterate = pcm_leuint32(wav->fmt.byterate); + priv->wav.fmt.align = pcm_leuint16(wav->fmt.align); + priv->wav.fmt.bpsamp = pcm_leuint16(wav->fmt.bpsamp); + + priv->wav.data.chunkid = pcm_leuint32(wav->data.chunkid); + priv->wav.data.chunklen = pcm_leuint32(wav->data.chunklen); + + /* Dump the converted wave header information */ + + pcm_dump(&priv->wav); + + /* And return true if the the file is a valid WAV header file */ + + return pcm_validwav(&priv->wav); } /**************************************************************************** * Name: pcm_getcaps * * Description: - * This method is called to retrieve the lower-half device capabilities. - * It will be called with device type AUDIO_TYPE_QUERY to request the - * overall capabilities, such as to determine the types of devices supported - * audio formats supported, etc. Then it may be called once or more with - * reported supported device types to determine the specific capabilities - * of that device type (such as MP3 encoder, WMA encoder, PCM output, etc.). + * This method is called to retrieve the lower-half device capabilities. + * It will be called with device type AUDIO_TYPE_QUERY to request the + * overall capabilities, such as to determine the types of devices supported + * audio formats supported, etc. Then it may be called once or more with + * reported supported device types to determine the specific capabilities + * of that device type (such as MP3 encoder, WMA encoder, PCM output, etc.). * ****************************************************************************/ @@ -241,20 +347,48 @@ static int pcm_getcaps(FAR struct audio_lowerhalf_s *dev, int type, FAR struct audio_caps_s *caps) { FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev; -#warning Missing logic - return -ENOSYS; + FAR struct audio_lowerhalf_s *lower; + int ret; + + DEBUGASSERT(priv); + + /* Defer the operation to the lower device driver */ + + lower = priv->lower; + DEBUGASSERT(lower && lower->ops->getcaps); + + /* Get the capabilities of the lower-level driver */ + + ret = lower->ops->getcaps(lower, type, caps); + if (ret < 0) + { + auddbg("Lower getcaps() failed: %d\n", ret); + return ret; + } + + /* Modify the capabilities reported by the lower driver: PCM is the only + * supported format that we will report, regardless of what the lower driver + * reported. + */ + + if (caps->ac_subtype == AUDIO_TYPE_QUERY) + { + *((uint16_t *)&caps->ac_format[0]) = (1 << (AUDIO_FMT_PCM - 1)); + } + + return caps->ac_len; } /**************************************************************************** * Name: pcm_configure * * Description: - * This method is called to bind the lower-level driver to the upper-level - * driver and to configure the driver for a specific mode of - * operation defined by the parameters selected in supplied device caps - * structure. The lower-level device should perform any initialization - * needed to prepare for operations in the specified mode. It should not, - * however, process any audio data until the start method is called. + * This method is called to bind the lower-level driver to the upper-level + * driver and to configure the driver for a specific mode of + * operation defined by the parameters selected in supplied device caps + * structure. The lower-level device should perform any initialization + * needed to prepare for operations in the specified mode. It should not, + * however, process any audio data until the start method is called. * ****************************************************************************/ @@ -275,13 +409,13 @@ static int pcm_configure(FAR struct audio_lowerhalf_s *dev, * Name: pcm_shutdown * * Description: - * This method is called when the driver is closed. The lower half driver - * should stop processing audio data, including terminating any active - * output generation. It should also disable the audio hardware and put - * it into the lowest possible power usage state. + * This method is called when the driver is closed. The lower half driver + * should stop processing audio data, including terminating any active + * output generation. It should also disable the audio hardware and put + * it into the lowest possible power usage state. * - * Any enqueued Audio Pipeline Buffers that have not been processed / dequeued - * should be dequeued by this function. + * Any enqueued Audio Pipeline Buffers that have not been processed / dequeued + * should be dequeued by this function. * ****************************************************************************/ @@ -296,10 +430,10 @@ static int pcm_shutdown(FAR struct audio_lowerhalf_s *dev) * Name: pcm_start * * Description: - * Start audio streaming in the configured mode. For input and synthesis - * devices, this means it should begin sending streaming audio data. For output - * or processing type device, it means it should begin processing of any enqueued - * Audio Pipeline Buffers. + * Start audio streaming in the configured mode. For input and synthesis + * devices, this means it should begin sending streaming audio data. For output + * or processing type device, it means it should begin processing of any enqueued + * Audio Pipeline Buffers. * ****************************************************************************/ @@ -318,8 +452,8 @@ static int pcm_start(FAR struct audio_lowerhalf_s *dev) * Name: pcm_stop * * Description: - * Stop audio streaming and/or processing of enqueued Audio Pipeline - * Buffers + * Stop audio streaming and/or processing of enqueued Audio Pipeline + * Buffers * ****************************************************************************/ @@ -340,8 +474,8 @@ static int pcm_stop(FAR struct audio_lowerhalf_s *dev) * Name: pcm_pause * * Description: - * Pause the audio stream. Should keep current playback context active - * in case a resume is issued. Could be called and then followed by a stop. + * Pause the audio stream. Should keep current playback context active + * in case a resume is issued. Could be called and then followed by a stop. * ****************************************************************************/ @@ -361,7 +495,7 @@ static int pcm_pause(FAR struct audio_lowerhalf_s *dev) * Name: pcm_resume * * Description: - * Resumes audio streaming after a pause. + * Resumes audio streaming after a pause. * ****************************************************************************/ @@ -381,12 +515,12 @@ static int pcm_resume(FAR struct audio_lowerhalf_s *dev) * Name: pcm_allocbuffer * * Description: - * Allocate an audio pipeline buffer. This routine provides the - * lower-half driver with the opportunity to perform special buffer - * allocation if needed, such as allocating from a specific memory - * region (DMA-able, etc.). If not supplied, then the top-half - * driver will perform a standard kumalloc using normal user-space - * memory region. + * Allocate an audio pipeline buffer. This routine provides the + * lower-half driver with the opportunity to perform special buffer + * allocation if needed, such as allocating from a specific memory + * region (DMA-able, etc.). If not supplied, then the top-half + * driver will perform a standard kumalloc using normal user-space + * memory region. * ****************************************************************************/ @@ -402,6 +536,7 @@ static int pcm_allocbuffer(FAR struct audio_lowerhalf_s *dev, lower = priv->lower; DEBUGASSERT(lower && lower->ops->allocbuffer); + return lower->ops->allocbuffer(lower, apb); } @@ -409,9 +544,9 @@ static int pcm_allocbuffer(FAR struct audio_lowerhalf_s *dev, * Name: pcm_freebuffer * * Description: - * Free an audio pipeline buffer. If the lower-level driver - * provides an allocbuffer routine, it should also provide the - * freebuffer routine to perform the free operation. + * Free an audio pipeline buffer. If the lower-level driver provides an + * allocbuffer routine, it should also provide the freebuffer routine to + * perform the free operation. * ****************************************************************************/ @@ -427,6 +562,7 @@ static int pcm_freebuffer(FAR struct audio_lowerhalf_s *dev, lower = priv->lower; DEBUGASSERT(lower && lower->ops->freebuffer); + return lower->ops->freebuffer(lower, apb); } @@ -434,16 +570,18 @@ static int pcm_freebuffer(FAR struct audio_lowerhalf_s *dev, * Name: pcm_enqueuebuffer * * Description: - * Enqueue a buffer for processing. This is a non-blocking enqueue operation. - * If the lower-half driver's buffer queue is full, then it should return an - * error code of -ENOMEM, and the upper-half driver can decide to either block - * the calling thread or deal with it in a non-blocking manner. + * Enqueue a buffer for processing. This is a non-blocking enqueue + * operation. If the lower-half driver's buffer queue is full, then it + * should return an error code of -ENOMEM, and the upper-half driver can + * decide to either block the calling thread or deal with it in a non- + * blocking manner. * - * For each call to enqueuebuffer, the lower-half driver must call - * audio_dequeuebuffer when it is finished processing the bufferr, passing the - * previously enqueued apb and a dequeue status so that the upper-half driver - * can decide if a waiting thread needs to be release, if the dequeued buffer - * should be passed to the next block in the Audio Pipeline, etc. + * For each call to enqueuebuffer, the lower-half driver must call + * audio_dequeuebuffer when it is finished processing the bufferr, passing + * the previously enqueued apb and a dequeue status so that the upper-half + * driver can decide if a waiting thread needs to be release, if the + * dequeued buffer should be passed to the next block in the Audio + * Pipeline, etc. * ****************************************************************************/ @@ -451,15 +589,62 @@ static int pcm_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb) { FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev; -#warning Missing logic - return -ENOSYS; + FAR struct audio_lowerhalf_s *lower; + apb_samp_t bytesleft; + + DEBUGASSERT(priv); + audvdbg("Received buffer %p, streaming=%d\n", apb, priv->streaming); + + lower = priv->lower; + DEBUGASSERT(lower && lower->ops->enqueuebuffer); + + /* Are we streaming yet? */ + + if (priv->streaming) + { + /* Yes, just give the buffer to the lower driver */ + + return lower->ops->enqueuebuffer(lower, apb); + } + + /* No.. then this must be the first buffer that we have seen (since we + * will error out out if the first buffer is smaller than the WAV file + * header. There is no attempt to reconstruct the full header from + * fragments in multiple, tiny audio buffers). + */ + + bytesleft = apb->nbytes - apb->curbyte; + audvdbg("curbyte=%d nbytes=%d nmaxbytes=%d bytesleft=%d\n", + apb->curbyte, apb->nbytes, apb->nmaxbytes, bytesleft); + + if (bytesleft >= sizeof(struct wav_header_s)) + { + /* Parse and verify the candidate WAV file header */ + + if (pcm_parsewav(priv, &apb->samp[apb->curbyte])) + { + /* Now we are streaming */ + + priv->streaming = true; + + /* Bump up the data offset and pass the buffer to the lower level */ + + apb->curbyte += sizeof(struct wav_header_s); + return lower->ops->enqueuebuffer(lower, apb); + } + } + + /* This is not a WAV file! */ + + auddbg("ERROR: Invalid WAV file\n"); + return -EINVAL; } /**************************************************************************** * Name: pcm_cancelbuffer * * Description: - * Cancel a previously enqueued buffer. + * Cancel a previously enqueued buffer. * ****************************************************************************/ @@ -475,27 +660,35 @@ static int pcm_cancelbuffer(FAR struct audio_lowerhalf_s *dev, * Name: pcm_ioctl * * Description: - * Lower-half logic may support platform-specific ioctl commands. + * Lower-half logic may support platform-specific ioctl commands. * ****************************************************************************/ -static int pcm_ioctl(FAR struct audio_lowerhalf_s *dev, - int cmd, unsigned long arg) +static int pcm_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, + unsigned long arg) { FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev; -#warning Missing logic - return -ENOSYS; + FAR struct audio_lowerhalf_s *lower; + + DEBUGASSERT(priv); + + /* Defer the operation to the lower device driver */ + + lower = priv->lower; + DEBUGASSERT(lower && lower->ops->ioctl); + + return lower->ops->ioctl(lower, cmd, arg); } /**************************************************************************** * Name: pcm_reserve * * Description: - * Reserve a session (may only be one per device or may be multiple) for - * use by a client. Client software can open audio devices and issue - * AUDIOIOC_GETCAPS calls freely, but other operations require a - * reservation. A session reservation will assign a context that must - * be passed with + * Reserve a session (may only be one per device or may be multiple) for + * use by a client. Client software can open audio devices and issue + * AUDIOIOC_GETCAPS calls freely, but other operations require a + * reservation. A session reservation will assign a context that must + * be passed with * ****************************************************************************/ @@ -506,15 +699,32 @@ static int pcm_reserve(FAR struct audio_lowerhalf_s *dev) #endif { FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev; -#warning Missing logic - return -ENOSYS; + FAR struct audio_lowerhalf_s *lower; + + DEBUGASSERT(priv); + + /* It is not necessary to reserve the upper half. What we really need to + * do is to reserved the lower device driver for exclusive use by the PCM + * decoder. That effectively reserves the upper PCM decoder along with + * the lower driver (which is then not available for use by other + * decoders). + */ + + lower = priv->lower; + DEBUGASSERT(lower && lower->ops->reserve); + +#ifdef CONFIG_AUDIO_MULTI_SESSION + return lower->ops->reserve(lower, session); +#else + return lower->ops->reserve(lower); +#endif } /**************************************************************************** * Name: pcm_release * * Description: - * Release a session. + * Release a session. * ****************************************************************************/ @@ -525,8 +735,23 @@ static int pcm_release(FAR struct audio_lowerhalf_s *dev) #endif { FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev; -#warning Missing logic - return -ENOSYS; + FAR struct audio_lowerhalf_s *lower; + + DEBUGASSERT(priv); + + /* Release the lower driver.. it is then available for use by other + * decoders (and we cannot use the lower driver wither unless we re- + * reserve it). + */ + + lower = priv->lower; + DEBUGASSERT(lower && lower->ops->release); + +#ifdef CONFIG_AUDIO_MULTI_SESSION + return lower->ops->release(lower, session); +#else + return lower->ops->release(lower); +#endif } /**************************************************************************** diff --git a/drivers/audio/wm8904.c b/drivers/audio/wm8904.c index f09910732e..d37ef116c8 100644 --- a/drivers/audio/wm8904.c +++ b/drivers/audio/wm8904.c @@ -1500,7 +1500,7 @@ static int wm8904_reserve(FAR struct audio_lowerhalf_s *dev) } else { - /* Initialize the session context. We don't really use it. */ + /* Initialize the session context */ #ifdef CONFIG_AUDIO_MULTI_SESSION *session = NULL; diff --git a/include/nuttx/audio/pcm.h b/include/nuttx/audio/pcm.h index 33c5de0dee..9b2ffad741 100644 --- a/include/nuttx/audio/pcm.h +++ b/include/nuttx/audio/pcm.h @@ -68,38 +68,59 @@ /* Default configuration values */ /* WAVE Header Definitions **************************************************/ -/* All values are little endian */ +/* All values are little 32-bit or 16-bit endian */ -#define WAV_CHUNKID 0x46464952 /* "RIFF" */ -#define WAV_FORMAT 0x45564157 /* "WAVE" */ -#define WAV_SUBCHKID1 0x20746d66 /* "fmt " */ -#define WAV_SUBCHKLEN1 16 /* Size of a PCM subchunk */ -#define WAV_COMPRESSION 1 /* Linear quantization */ -#define WAV_MONO 1 /* nchannels=1 */ -#define WAV_STEREO 2 /* nchannels=2 */ -#define WAV_DATA 0x61746164 /* "data" +#define WAV_HDR_CHUNKID 0x46464952 /* "RIFF" */ +#define WAV_HDR_FORMAT 0x45564157 /* "WAVE" */ +#define WAV_FMT_CHUNKID 0x20746d66 /* "fmt " */ +#define WAV_FMT_CHUNKLEN 16 /* Size of a PCM subchunk */ +#define WAV_FMT_FORMAT 1 /* Linear quantization */ +#define WAV_FMT_MONO 1 /* nchannels=1 */ +#define WAV_FMT_STEREO 2 /* nchannels=2 */ +#define WAV_DATA_CHUNKID 0x61746164 /* "data" */ /**************************************************************************** * Public Types ****************************************************************************/ +/* The standard WAV header consist of three chunks + * + * 1. A WAV header chunk, + * 2. A format chunk, and + * 3. A data chunk. + */ -/* Standard WAV file header format */ - -struct wav_header_s +struct wav_hdrchunk_s { - uint32_t chkid; /* Contains the letters "RIFF" in ASCII form. */ - uint32_t chklen; /* Size of the rest of the following chunk */ + uint32_t chunkid; /* Contains the letters "RIFF" in ASCII form. */ + uint32_t chunklen; /* Size of the rest of the following chunk */ uint32_t format; /* Contains the letters "WAVE" */ - uint32_t subchkid1; /* Contains the letters "fmt " */ - uint32_t subchklen1; /* Size of the following subchunk (16 for PCM) */ - uint16_t compression; /* PCM=1 (i.e. Linear quantization) */ +}; + +struct wav_formatchunk_s +{ + uint32_t chunkid; /* Contains the letters "fmt " */ + uint32_t chunklen; /* Size of the following chunk (16 for PCM) */ + uint16_t format; /* PCM=1 (i.e. Linear quantization) */ uint16_t nchannels; /* Mono=1, Stereo=2 */ uint32_t samprate; /* 8000, 44100, ... */ uint32_t byterate; /* samprate * nchannels * bpsamp / 8 */ uint16_t align; /* nchannels * bpsamp / 8 */ uint16_t bpsamp; /* Bits per sample: 8 bits = 8, 16 bits = 16 */ - uint32_t subchkid2; /* Contains the letters "data" */ - uint32_t subchklen2; /* Number of bytes in the data */ +}; + +struct wav_datachunk_s +{ + uint32_t chunkid; /* Contains the letters "data" */ + uint32_t chunklen; /* Number of bytes in the data */ +}; + +/* The standard WAV file header format is then these three chunks */ + +struct wav_header_s +{ + struct wav_hdrchunk_s hdr; + struct wav_formatchunk_s fmt; + struct wav_datachunk_s data; }; /****************************************************************************