-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
applications: nrf5340_audio: Module to read LC3 files from SD card
Reads and parses LC3 files from the SD card, and provides a function to read the LC3 file one frame at a time. Uses the existing SD card module. OCT-3031 Signed-off-by: Andreas Vibeto <andreas.vibeto@nordicsemi.no>
- Loading branch information
Showing
12 changed files
with
793 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
/* | ||
* Copyright (c) 2024 Nordic Semiconductor ASA | ||
* | ||
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause | ||
*/ | ||
|
||
#include "lc3_file.h" | ||
#include "sd_card.h" | ||
|
||
#include <zephyr/logging/log.h> | ||
LOG_MODULE_REGISTER(sd_card_lc3_file, CONFIG_MODULE_SD_CARD_LC3_FILE_LOG_LEVEL); | ||
|
||
#define LC3_FILE_ID 0xCC1C | ||
|
||
int lc3_file_frame_get(struct lc3_file_ctx *file, uint8_t *buffer, size_t buffer_size) | ||
{ | ||
int ret; | ||
|
||
if ((file == NULL) || (buffer == NULL)) { | ||
LOG_ERR("Nullptr received"); | ||
return -EINVAL; | ||
} | ||
|
||
/* Read frame header */ | ||
uint16_t frame_header; | ||
size_t frame_header_size = sizeof(frame_header); | ||
|
||
ret = sd_card_read((char *)&frame_header, &frame_header_size, &file->file_object); | ||
if (ret) { | ||
LOG_ERR("Failed to read frame header: %d", ret); | ||
return ret; | ||
} | ||
|
||
if ((frame_header_size == 0) || (frame_header == 0)) { | ||
LOG_ERR("No more frames to read"); | ||
return -ENODATA; | ||
} | ||
|
||
LOG_DBG("Size of frame is %d", frame_header); | ||
|
||
if (buffer_size < frame_header) { | ||
LOG_ERR("Buffer size too small: %d < %d", buffer_size, frame_header); | ||
return -ENOMEM; | ||
} | ||
|
||
/* Read frame data */ | ||
size_t frame_size = frame_header; | ||
|
||
ret = sd_card_read((char *)buffer, &frame_size, &file->file_object); | ||
if (ret) { | ||
LOG_ERR("Failed to read frame data: %d", ret); | ||
return ret; | ||
} | ||
|
||
if (frame_size != frame_header) { | ||
LOG_ERR("Frame size mismatch: %d != %d", frame_size, frame_header); | ||
return -EIO; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
int lc3_file_open(struct lc3_file_ctx *file, const char *file_name) | ||
{ | ||
int ret; | ||
size_t size = sizeof(file->lc3_header); | ||
|
||
if ((file == NULL) || (file_name == NULL)) { | ||
LOG_ERR("Nullptr received"); | ||
return -EINVAL; | ||
} | ||
|
||
ret = sd_card_open(file_name, &file->file_object); | ||
if (ret) { | ||
LOG_ERR("Failed to open file: %d", ret); | ||
return ret; | ||
} | ||
|
||
/* Read LC3 header and store in struct */ | ||
ret = sd_card_read((char *)&file->lc3_header, &size, &file->file_object); | ||
if (ret) { | ||
LOG_ERR("Failed to read the LC3 header: %d", ret); | ||
return ret; | ||
} | ||
|
||
if (file->lc3_header.file_id != LC3_FILE_ID) { | ||
LOG_ERR("Invalid file ID: 0x%04x", file->lc3_header.file_id); | ||
return -EINVAL; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
int lc3_file_close(struct lc3_file_ctx *file) | ||
{ | ||
int ret; | ||
|
||
if (file == NULL) { | ||
LOG_ERR("Nullptr received"); | ||
return -EINVAL; | ||
} | ||
|
||
ret = sd_card_close(&file->file_object); | ||
if (ret) { | ||
LOG_ERR("Failed to close file: %d", ret); | ||
return ret; | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
int lc3_file_init(void) | ||
{ | ||
int ret; | ||
|
||
ret = sd_card_init(); | ||
if (ret) { | ||
LOG_ERR("Failed to initialize SD card: %d", ret); | ||
return ret; | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright (c) 2024 Nordic Semiconductor ASA | ||
* | ||
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause | ||
*/ | ||
|
||
#ifndef LC3_FILE_H__ | ||
#define LC3_FILE_H__ | ||
|
||
#include <stddef.h> | ||
#include <stdint.h> | ||
|
||
#include <zephyr/fs/fs.h> | ||
#include <zephyr/sys/util.h> | ||
|
||
struct lc3_file_header { | ||
uint16_t file_id; /* Constant value, 0xCC1C */ | ||
uint16_t hdr_size; /* Header size, 0x0012 */ | ||
uint16_t sample_rate; /* Sample frequency / 100 */ | ||
uint16_t bit_rate; /* Bit rate / 100 (total for all channels) */ | ||
uint16_t channels; /* Number of channels */ | ||
uint16_t frame_duration; /* Frame duration in ms * 100 */ | ||
uint16_t rfu; /* Reserved for future use */ | ||
uint16_t signal_len_lsb; /* Number of samples in signal, 16 LSB */ | ||
uint16_t signal_len_msb; /* Number of samples in signal, 16 MSB (>> 16) */ | ||
} __packed; | ||
|
||
struct lc3_file_ctx { | ||
struct fs_file_t file_object; | ||
struct lc3_file_header lc3_header; | ||
uint32_t number_of_samples; | ||
}; | ||
|
||
/** | ||
* @brief Get the next LC3 frame from the file. | ||
* | ||
* @param[in] file Pointer to the file context. | ||
* @param[out] buffer Pointer to the buffer to store the frame. | ||
* @param[in] buffer_size Size of the buffer. | ||
* | ||
* @retval -ENODATA No more frames to read. | ||
* @retval 0 Success. | ||
*/ | ||
int lc3_file_frame_get(struct lc3_file_ctx *file, uint8_t *buffer, size_t buffer_size); | ||
|
||
/** | ||
* @brief Open a LC3 file for reading | ||
* | ||
* @details Opens the file and reads the LC3 header. | ||
* | ||
* @param[in] file Pointer to the file context. | ||
* @param[in] file_name Name of the file to open. | ||
* | ||
* @retval -ENODEV SD card init failed. SD card likely not inserted. | ||
* @retval 0 Success. | ||
*/ | ||
int lc3_file_open(struct lc3_file_ctx *file, const char *file_name); | ||
|
||
/** | ||
* @brief Close a LC3 file. | ||
* | ||
* @param[in] file Pointer to the file context. | ||
* | ||
* @retval -EPERM SD card operation is not ongoing. | ||
* @retval 0 Success. | ||
*/ | ||
int lc3_file_close(struct lc3_file_ctx *file); | ||
|
||
/** | ||
* @brief Initialize the LC3 file module. | ||
* | ||
* Initializes the SD card and mounts the file system. | ||
* | ||
* @retval -ENODEV SD card init failed. SD card likely not inserted. | ||
* @retval 0 Success. | ||
*/ | ||
int lc3_file_init(void); | ||
|
||
#endif /* LC3_FILE_H__ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
* Copyright (c) 2024 Nordic Semiconductor ASA | ||
* | ||
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause | ||
*/ | ||
|
||
#include <zephyr/sys/util.h> | ||
|
||
#include "lc3_file_data.h" | ||
|
||
/******************* | ||
* VALID DATASET 1 * | ||
*******************/ | ||
const uint8_t lc3_file_dataset1_valid[] = { | ||
0x1c, 0xcc, 0x12, 0x00, 0xe0, 0x01, 0x40, 0x01, 0x01, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x80, | ||
0x07, 0x00, 0x00, 0x28, 0x00, 0x50, 0x46, 0x07, 0x52, 0x03, 0x2c, 0x8f, 0xe1, 0xe9, 0x21, | ||
0x73, 0x81, 0x25, 0x82, 0x9f, 0x94, 0x7a, 0xa4, 0xa3, 0xa2, 0xc9, 0x50, 0xa2, 0xc3, 0xeb, | ||
0x43, 0xe0, 0xff, 0xf0, 0x14, 0x20, 0x86, 0xdd, 0x2f, 0xda, 0x48, 0xe2, 0x39, 0x92, 0xec, | ||
0x28, 0x00, 0x3f, 0x48, 0x6d, 0xc7, 0x7f, 0xae, 0x83, 0xad, 0x95, 0xd7, 0x66, 0xa1, 0x9b, | ||
0xcf, 0x3e, 0xdb, 0x1b, 0x41, 0xb4, 0xc8, 0x80, 0x6d, 0xf9, 0x13, 0x7b, 0x43, 0x36, 0x2f, | ||
0xc6, 0xe1, 0xaa, 0x11, 0xd2, 0xce, 0xe9, 0x01, 0x62, 0x0b, 0x94, 0xfc, 0x28, 0x00, 0x82, | ||
0x8a, 0x0f, 0x07, 0x74, 0x19, 0x24, 0x41, 0xbc, 0xcd, 0x38, 0x73, 0xba, 0x0a, 0x51, 0xde, | ||
0x2c, 0x9c, 0x77, 0x7b, 0xc9, 0x03, 0x7a, 0xee, 0x29, 0x1b, 0x0a, 0x02, 0xc7, 0x03, 0x7c, | ||
0xd6, 0xd0, 0xb6, 0xbb, 0x2d, 0x63, 0xbe, 0xb4, 0x54, 0x28, 0x00, 0x1e, 0xc2, 0x47, 0x7e, | ||
0xc7, 0x1a, 0xf8, 0x46, 0xde, 0x60, 0xbb, 0xbf, 0x7e, 0x67, 0x94, 0xbd, 0x96, 0x44, 0x36, | ||
0xbc, 0xcf, 0x59, 0x3f, 0xa0, 0xfd, 0x8f, 0x2c, 0x57, 0x89, 0x2a, 0xb3, 0x78, 0xdd, 0xe0, | ||
0xdc, 0xbb, 0x65, 0x0f, 0x54, 0x5c, 0x28, 0x00, 0xff, 0xed, 0x1b, 0x0e, 0x0b, 0x34, 0xdc, | ||
0xad, 0xe1, 0x34, 0xad, 0xba, 0xb9, 0xfc, 0x85, 0x72, 0x60, 0x9c, 0xac, 0x00, 0x00, 0x03, | ||
0xf2, 0x14, 0xc2, 0xf6, 0xc7, 0x5a, 0xd3, 0xd8, 0x94, 0x10, 0xce, 0x1c, 0xb8, 0x32, 0xb1, | ||
0xbd, 0xa3, 0x3c}; | ||
const size_t lc3_file_dataset1_valid_size = ARRAY_SIZE(lc3_file_dataset1_valid); | ||
|
||
const uint8_t lc3_file_dataset1_valid_frame1[] = { | ||
0x50, 0x46, 0x07, 0x52, 0x03, 0x2c, 0x8f, 0xe1, 0xe9, 0x21, 0x73, 0x81, 0x25, 0x82, | ||
0x9f, 0x94, 0x7a, 0xa4, 0xa3, 0xa2, 0xc9, 0x50, 0xa2, 0xc3, 0xeb, 0x43, 0xe0, 0xff, | ||
0xf0, 0x14, 0x20, 0x86, 0xdd, 0x2f, 0xda, 0x48, 0xe2, 0x39, 0x92, 0xec}; | ||
const size_t lc3_file_dataset1_valid_frame1_size = ARRAY_SIZE(lc3_file_dataset1_valid_frame1); | ||
|
||
const uint8_t lc3_file_dataset1_valid_frame2[] = { | ||
0x3f, 0x48, 0x6d, 0xc7, 0x7f, 0xae, 0x83, 0xad, 0x95, 0xd7, 0x66, 0xa1, 0x9b, 0xcf, | ||
0x3e, 0xdb, 0x1b, 0x41, 0xb4, 0xc8, 0x80, 0x6d, 0xf9, 0x13, 0x7b, 0x43, 0x36, 0x2f, | ||
0xc6, 0xe1, 0xaa, 0x11, 0xd2, 0xce, 0xe9, 0x01, 0x62, 0x0b, 0x94, 0xfc}; | ||
const size_t lc3_file_dataset1_valid_frame2_size = ARRAY_SIZE(lc3_file_dataset1_valid_frame2); | ||
|
||
const uint8_t lc3_file_dataset1_valid_frame3[] = { | ||
0x82, 0x8a, 0x0f, 0x07, 0x74, 0x19, 0x24, 0x41, 0xbc, 0xcd, 0x38, 0x73, 0xba, 0x0a, | ||
0x51, 0xde, 0x2c, 0x9c, 0x77, 0x7b, 0xc9, 0x03, 0x7a, 0xee, 0x29, 0x1b, 0x0a, 0x02, | ||
0xc7, 0x03, 0x7c, 0xd6, 0xd0, 0xb6, 0xbb, 0x2d, 0x63, 0xbe, 0xb4, 0x54}; | ||
const size_t lc3_file_dataset1_valid_frame3_size = ARRAY_SIZE(lc3_file_dataset1_valid_frame3); | ||
|
||
const uint8_t lc3_file_dataset1_valid_frame4[] = { | ||
0x1e, 0xc2, 0x47, 0x7e, 0xc7, 0x1a, 0xf8, 0x46, 0xde, 0x60, 0xbb, 0xbf, 0x7e, 0x67, | ||
0x94, 0xbd, 0x96, 0x44, 0x36, 0xbc, 0xcf, 0x59, 0x3f, 0xa0, 0xfd, 0x8f, 0x2c, 0x57, | ||
0x89, 0x2a, 0xb3, 0x78, 0xdd, 0xe0, 0xdc, 0xbb, 0x65, 0x0f, 0x54, 0x5c}; | ||
const size_t lc3_file_dataset1_valid_frame4_size = ARRAY_SIZE(lc3_file_dataset1_valid_frame4); | ||
|
||
const uint8_t lc3_file_dataset1_valid_frame5[] = { | ||
0xff, 0xed, 0x1b, 0x0e, 0x0b, 0x34, 0xdc, 0xad, 0xe1, 0x34, 0xad, 0xba, 0xb9, 0xfc, | ||
0x85, 0x72, 0x60, 0x9c, 0xac, 0x00, 0x00, 0x03, 0xf2, 0x14, 0xc2, 0xf6, 0xc7, 0x5a, | ||
0xd3, 0xd8, 0x94, 0x10, 0xce, 0x1c, 0xb8, 0x32, 0xb1, 0xbd, 0xa3, 0x3c}; | ||
const size_t lc3_file_dataset1_valid_frame5_size = ARRAY_SIZE(lc3_file_dataset1_valid_frame5); | ||
|
||
/************************* | ||
* INVALID FRAME DATASET * | ||
*************************/ | ||
const uint8_t lc3_file_dataset_invalid_frame[] = { | ||
0x1c, 0xcc, 0x12, 0x00, 0xe0, 0x01, 0x40, 0x01, 0x01, 0x00, 0xe8, 0x03, | ||
0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x28, 0x00, 0x50, 0x46, 0x07, 0x52, | ||
0x03, 0x2c, 0x8f, 0xe1, 0xe9, 0x21, 0x73, 0x81, 0x25, 0x82, 0x9f, 0x94, | ||
0x7a, 0xa4, 0xa3, 0xa2, 0xc9, 0x50, 0xa2, 0xc3, 0xeb, 0x43}; | ||
const size_t lc3_file_dataset_invalid_frame_size = ARRAY_SIZE(lc3_file_dataset_invalid_frame); | ||
|
||
/************************** | ||
* INVALID HEADER DATASET * | ||
**************************/ | ||
const uint8_t lc3_file_dataset_invalid_header[] = { | ||
0x1c, 0xFF, /* Invalid file id, is normally 0xcc1c */ | ||
0x12, 0x00, /* hdr_size: 0x0012 */ | ||
0x40, 0x01, /* sample_rate: 0x0140 */ | ||
0xe0, 0x01, /* bit_rate: 0x01e0 */ | ||
0x01, 0x00, /* channels: 0x0001 */ | ||
0xe8, 0x03, /* frame_duration: 0x03e8 */ | ||
0x00, 0x00, /* rfu: 0x0000 */ | ||
0x6c, 0x00, /* signal_len_lsb: 0x006c */ | ||
0x42, 0x00 /* signal_len_msb: 0x0042 */ | ||
}; | ||
const size_t lc3_file_dataset_invalid_header_size = ARRAY_SIZE(lc3_file_dataset_invalid_header); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* Copyright (c) 2024 Nordic Semiconductor ASA | ||
* | ||
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause | ||
*/ | ||
|
||
#ifndef LC3_FILE_DATA_H__ | ||
#define LC3_FILE_DATA_H__ | ||
|
||
#include <zephyr/types.h> | ||
|
||
/******************* | ||
* VALID DATASET 1 * | ||
*******************/ | ||
extern const uint8_t lc3_file_dataset1_valid[]; | ||
extern const size_t lc3_file_dataset1_valid_size; | ||
|
||
extern const uint8_t lc3_file_dataset1_valid_frame1[]; | ||
extern const size_t lc3_file_dataset1_valid_frame1_size; | ||
|
||
extern const uint8_t lc3_file_dataset1_valid_frame2[]; | ||
extern const size_t lc3_file_dataset1_valid_frame2_size; | ||
|
||
extern const uint8_t lc3_file_dataset1_valid_frame3[]; | ||
extern const size_t lc3_file_dataset1_valid_frame3_size; | ||
|
||
extern const uint8_t lc3_file_dataset1_valid_frame4[]; | ||
extern const size_t lc3_file_dataset1_valid_frame4_size; | ||
|
||
extern const uint8_t lc3_file_dataset1_valid_frame5[]; | ||
extern const size_t lc3_file_dataset1_valid_frame5_size; | ||
|
||
/************************* | ||
* INVALID FRAME DATASET * | ||
*************************/ | ||
/* LC3 dataset where the header is valid, but the frame contains less data than the frame header | ||
* specifies | ||
*/ | ||
extern const uint8_t lc3_file_dataset_invalid_frame[]; | ||
extern const size_t lc3_file_dataset_invalid_frame_size; | ||
|
||
/************************** | ||
* INVALID HEADER DATASET * | ||
**************************/ | ||
/* LC3 dataset header where the header ID is invalid */ | ||
extern const uint8_t lc3_file_dataset_invalid_header[]; | ||
extern const size_t lc3_file_dataset_invalid_header_size; | ||
|
||
#endif /* LC3_FILE_DATA_H__ */ |
Oops, something went wrong.