Audio System + Drivers (#562)
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <tactility/freertos/freertos.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
struct Device;
|
||||
|
||||
/**
|
||||
* @brief Direction of an audio codec operation or capability.
|
||||
*/
|
||||
enum AudioCodecDirection {
|
||||
AUDIO_CODEC_DIR_INPUT = 1,
|
||||
AUDIO_CODEC_DIR_OUTPUT = 2,
|
||||
AUDIO_CODEC_DIR_BOTH = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stream configuration used to open a codec for reading or writing.
|
||||
*/
|
||||
struct AudioCodecStreamConfig {
|
||||
uint32_t sample_rate;
|
||||
uint8_t bits_per_sample; // 16, 24, 32
|
||||
uint8_t channels;
|
||||
enum AudioCodecDirection direction;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief API for audio codec drivers (e.g. ES8388, ES7210, AW88298).
|
||||
*
|
||||
* Implementations typically wrap a vendor codec library (e.g. esp_codec_dev) and
|
||||
* the underlying I2S/I2C/GPIO controllers. This API is intentionally low-level and
|
||||
* codec-agnostic: it does not perform resampling or mixing. Most apps should use
|
||||
* the higher-level audio_stream API (see audio_stream.h) instead.
|
||||
*/
|
||||
struct AudioCodecApi {
|
||||
/**
|
||||
* @brief Opens the codec for streaming in the given direction(s).
|
||||
* @param[in] device the audio codec device
|
||||
* @param[in] config the stream configuration
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if the requested direction is not supported
|
||||
*/
|
||||
error_t (*open)(struct Device* device, const struct AudioCodecStreamConfig* config);
|
||||
|
||||
/**
|
||||
* @brief Closes the codec, stopping any active streams.
|
||||
* @param[in] device the audio codec device
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t (*close)(struct Device* device);
|
||||
|
||||
/**
|
||||
* @brief Reads recorded audio data from the codec. Only valid for codecs opened with input direction.
|
||||
* @param[in] device the audio codec device
|
||||
* @param[out] data the buffer to store the read data
|
||||
* @param[in] data_size the number of bytes to read
|
||||
* @param[out] bytes_read the number of bytes actually read
|
||||
* @param[in] timeout the maximum time to wait for the operation to complete
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_TIMEOUT when the operation timed out
|
||||
*/
|
||||
error_t (*read)(struct Device* device, void* data, size_t data_size, size_t* bytes_read, TickType_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Writes audio data for playback to the codec. Only valid for codecs opened with output direction.
|
||||
* @param[in] device the audio codec device
|
||||
* @param[in] data the buffer containing the data to write
|
||||
* @param[in] data_size the number of bytes to write
|
||||
* @param[out] bytes_written the number of bytes actually written
|
||||
* @param[in] timeout the maximum time to wait for the operation to complete
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_TIMEOUT when the operation timed out
|
||||
*/
|
||||
error_t (*write)(struct Device* device, const void* data, size_t data_size, size_t* bytes_written, TickType_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Sets the volume for the given direction.
|
||||
* @param[in] device the audio codec device
|
||||
* @param[in] direction AUDIO_CODEC_DIR_INPUT or AUDIO_CODEC_DIR_OUTPUT
|
||||
* @param[in] volume_percent volume in the range 0..100
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if the direction is not supported by this codec
|
||||
*/
|
||||
error_t (*set_volume)(struct Device* device, enum AudioCodecDirection direction, float volume_percent);
|
||||
|
||||
/**
|
||||
* @brief Gets the volume for the given direction.
|
||||
* @param[in] device the audio codec device
|
||||
* @param[in] direction AUDIO_CODEC_DIR_INPUT or AUDIO_CODEC_DIR_OUTPUT
|
||||
* @param[out] volume_percent volume in the range 0..100
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if the direction is not supported by this codec
|
||||
*/
|
||||
error_t (*get_volume)(struct Device* device, enum AudioCodecDirection direction, float* volume_percent);
|
||||
|
||||
/**
|
||||
* @brief Mutes or unmutes the given direction.
|
||||
* @param[in] device the audio codec device
|
||||
* @param[in] direction AUDIO_CODEC_DIR_INPUT or AUDIO_CODEC_DIR_OUTPUT
|
||||
* @param[in] muted true to mute, false to unmute
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if the direction is not supported by this codec
|
||||
*/
|
||||
error_t (*set_mute)(struct Device* device, enum AudioCodecDirection direction, bool muted);
|
||||
|
||||
/**
|
||||
* @brief Gets the mute state for the given direction.
|
||||
* @param[in] device the audio codec device
|
||||
* @param[in] direction AUDIO_CODEC_DIR_INPUT or AUDIO_CODEC_DIR_OUTPUT
|
||||
* @param[out] muted true if muted, false otherwise
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if the direction is not supported by this codec
|
||||
*/
|
||||
error_t (*get_mute)(struct Device* device, enum AudioCodecDirection direction, bool* muted);
|
||||
|
||||
/**
|
||||
* @brief Gets the native sample rate the codec hardware runs at for the given direction.
|
||||
* @param[in] device the audio codec device
|
||||
* @param[in] direction AUDIO_CODEC_DIR_INPUT or AUDIO_CODEC_DIR_OUTPUT
|
||||
* @param[out] rate_hz the native sample rate in Hz
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if the direction is not supported by this codec
|
||||
*/
|
||||
error_t (*get_native_sample_rate)(struct Device* device, enum AudioCodecDirection direction, uint32_t* rate_hz);
|
||||
|
||||
/**
|
||||
* @brief Gets the number of channels the codec hardware natively expects/produces for the
|
||||
* given direction (e.g. a 4-slot TDM microphone ADC reports 4 here even if an app only
|
||||
* wants 1). Callers that need a different channel count must downmix/upmix themselves --
|
||||
* opening the codec with a channel count that doesn't match its native layout can produce
|
||||
* corrupted audio (e.g. ES7210 in TDM mode silently halves its configured bit depth when
|
||||
* asked for <= 2 channels).
|
||||
* @param[in] device the audio codec device
|
||||
* @param[in] direction AUDIO_CODEC_DIR_INPUT or AUDIO_CODEC_DIR_OUTPUT
|
||||
* @param[out] channels the native channel count
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if the direction is not supported by this codec
|
||||
*/
|
||||
error_t (*get_native_channels)(struct Device* device, enum AudioCodecDirection direction, uint8_t* channels);
|
||||
|
||||
/**
|
||||
* @brief Gets the directions supported by this codec.
|
||||
* @param[in] device the audio codec device
|
||||
* @param[out] supported_directions AUDIO_CODEC_DIR_INPUT, AUDIO_CODEC_DIR_OUTPUT, or AUDIO_CODEC_DIR_BOTH
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t (*get_capabilities)(struct Device* device, enum AudioCodecDirection* supported_directions);
|
||||
|
||||
/**
|
||||
* @brief Gets a fixed digital gain multiplier to apply to this codec's input samples,
|
||||
* on top of whatever audio_codec_set_volume() controls. Compensates for mic capsules
|
||||
* that are quiet even at full hardware gain (e.g. small PDM/MEMS mics), or codecs with
|
||||
* no working hardware gain control at all (e.g. a dummy PDM codec). Set from the
|
||||
* codec's own devicetree config; 1.0 (no boost) if this field is NULL or the codec
|
||||
* doesn't implement it.
|
||||
* @param[in] device the audio codec device
|
||||
* @param[out] gain the multiplier to apply (1.0 = no change)
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if the direction is not supported by this codec
|
||||
*/
|
||||
error_t (*get_input_gain_multiplier)(struct Device* device, float* gain);
|
||||
};
|
||||
|
||||
/** @brief See AudioCodecApi::open */
|
||||
error_t audio_codec_open(struct Device* device, const struct AudioCodecStreamConfig* config);
|
||||
|
||||
/** @brief See AudioCodecApi::close */
|
||||
error_t audio_codec_close(struct Device* device);
|
||||
|
||||
/** @brief See AudioCodecApi::read */
|
||||
error_t audio_codec_read(struct Device* device, void* data, size_t data_size, size_t* bytes_read, TickType_t timeout);
|
||||
|
||||
/** @brief See AudioCodecApi::write */
|
||||
error_t audio_codec_write(struct Device* device, const void* data, size_t data_size, size_t* bytes_written, TickType_t timeout);
|
||||
|
||||
/** @brief See AudioCodecApi::set_volume */
|
||||
error_t audio_codec_set_volume(struct Device* device, enum AudioCodecDirection direction, float volume_percent);
|
||||
|
||||
/** @brief See AudioCodecApi::get_volume */
|
||||
error_t audio_codec_get_volume(struct Device* device, enum AudioCodecDirection direction, float* volume_percent);
|
||||
|
||||
/** @brief See AudioCodecApi::set_mute */
|
||||
error_t audio_codec_set_mute(struct Device* device, enum AudioCodecDirection direction, bool muted);
|
||||
|
||||
/** @brief See AudioCodecApi::get_mute */
|
||||
error_t audio_codec_get_mute(struct Device* device, enum AudioCodecDirection direction, bool* muted);
|
||||
|
||||
/** @brief See AudioCodecApi::get_native_sample_rate */
|
||||
error_t audio_codec_get_native_sample_rate(struct Device* device, enum AudioCodecDirection direction, uint32_t* rate_hz);
|
||||
|
||||
/** @brief See AudioCodecApi::get_native_channels */
|
||||
error_t audio_codec_get_native_channels(struct Device* device, enum AudioCodecDirection direction, uint8_t* channels);
|
||||
|
||||
/** @brief See AudioCodecApi::get_capabilities */
|
||||
error_t audio_codec_get_capabilities(struct Device* device, enum AudioCodecDirection* supported_directions);
|
||||
|
||||
/**
|
||||
* @brief See AudioCodecApi::get_input_gain_multiplier. Returns 1.0 (no change) if the
|
||||
* codec doesn't implement this field, rather than ERROR_NOT_SUPPORTED -- callers can treat
|
||||
* the multiplier as always present.
|
||||
*/
|
||||
error_t audio_codec_get_input_gain_multiplier(struct Device* device, float* gain);
|
||||
|
||||
extern const struct DeviceType AUDIO_CODEC_TYPE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,211 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <tactility/freertos/freertos.h>
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/drivers/audio_codec.h>
|
||||
|
||||
struct Device;
|
||||
|
||||
/**
|
||||
* @brief Handle to an open audio stream (see audio_stream_open_input/open_output).
|
||||
*
|
||||
* The `device` field identifies the owning audio stream device so that
|
||||
* audio_stream_read/write/close can dispatch to the correct driver; implementations
|
||||
* embed this as the first member of a larger private struct and may store additional
|
||||
* state after it.
|
||||
*/
|
||||
struct AudioStreamHandleData {
|
||||
struct Device* device;
|
||||
};
|
||||
|
||||
typedef struct AudioStreamHandleData* AudioStreamHandle;
|
||||
|
||||
/**
|
||||
* @brief Stream configuration requested by the caller.
|
||||
*
|
||||
* The sample_rate is the rate the caller wants to read/write at; the audio-stream
|
||||
* device transparently resamples to/from the bound codec's native rate, so the same
|
||||
* app code works regardless of which codec hardware is present.
|
||||
*/
|
||||
struct AudioStreamConfig {
|
||||
uint32_t sample_rate; // e.g. 16000, 44100, 48000
|
||||
uint8_t bits_per_sample; // 16, 24, 32
|
||||
uint8_t channels;
|
||||
};
|
||||
|
||||
/** @brief Identifies which cached field changed in an AudioStreamChangeCallback. */
|
||||
enum AudioStreamChange {
|
||||
AUDIO_STREAM_CHANGE_VOLUME,
|
||||
AUDIO_STREAM_CHANGE_MUTE,
|
||||
AUDIO_STREAM_CHANGE_ENABLED,
|
||||
};
|
||||
|
||||
typedef void (*AudioStreamChangeCallback)(struct Device* device, enum AudioCodecDirection direction, enum AudioStreamChange change, void* user_data);
|
||||
|
||||
/**
|
||||
* @brief API for the high-level full-duplex audio stream device.
|
||||
*
|
||||
* Implementations bind to one input-capable and/or one output-capable AUDIO_CODEC_TYPE
|
||||
* device, and provide resampling plus shared volume/mute/enable state on top. This is
|
||||
* the API apps (including ELF side-loaded apps) and AudioService should use — most code
|
||||
* should not talk to AUDIO_CODEC_TYPE devices directly.
|
||||
*
|
||||
* read/write are blocking and must be called from the caller's own task, never from the
|
||||
* main/LVGL thread.
|
||||
*/
|
||||
struct AudioStreamApi {
|
||||
/**
|
||||
* @brief Opens an input (recording) stream at the requested configuration.
|
||||
* @param[in] device the audio stream device
|
||||
* @param[in] config the requested stream configuration
|
||||
* @param[out] out_handle receives the opened stream handle
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if no input-capable codec is bound
|
||||
* @retval ERROR_INVALID_STATE if an input stream is already open
|
||||
*/
|
||||
error_t (*open_input)(struct Device* device, const struct AudioStreamConfig* config, AudioStreamHandle* out_handle);
|
||||
|
||||
/**
|
||||
* @brief Opens an output (playback) stream at the requested configuration.
|
||||
* @param[in] device the audio stream device
|
||||
* @param[in] config the requested stream configuration
|
||||
* @param[out] out_handle receives the opened stream handle
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if no output-capable codec is bound
|
||||
* @retval ERROR_INVALID_STATE if an output stream is already open
|
||||
*/
|
||||
error_t (*open_output)(struct Device* device, const struct AudioStreamConfig* config, AudioStreamHandle* out_handle);
|
||||
|
||||
/**
|
||||
* @brief Reads recorded audio data from an input stream, resampled to the requested rate.
|
||||
* @param[in] handle the stream handle returned by open_input
|
||||
* @param[out] data the buffer to store the read data
|
||||
* @param[in] data_size the number of bytes to read
|
||||
* @param[out] bytes_read the number of bytes actually read
|
||||
* @param[in] timeout the maximum time to wait for the operation to complete
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_TIMEOUT when the operation timed out
|
||||
*/
|
||||
error_t (*read)(AudioStreamHandle handle, void* data, size_t data_size, size_t* bytes_read, TickType_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Writes audio data for playback to an output stream, resampled from the requested rate.
|
||||
* @param[in] handle the stream handle returned by open_output
|
||||
* @param[in] data the buffer containing the data to write
|
||||
* @param[in] data_size the number of bytes to write
|
||||
* @param[out] bytes_written the number of bytes actually written
|
||||
* @param[in] timeout the maximum time to wait for the operation to complete
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_TIMEOUT when the operation timed out
|
||||
*/
|
||||
error_t (*write)(AudioStreamHandle handle, const void* data, size_t data_size, size_t* bytes_written, TickType_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Closes a stream opened with open_input or open_output.
|
||||
* @param[in] handle the stream handle to close
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t (*close)(AudioStreamHandle handle);
|
||||
|
||||
/**
|
||||
* @brief Sets the shared volume for the given direction. Visible to all consumers
|
||||
* (other apps, AudioService, Settings UI) since the state lives on the bound codec.
|
||||
* @param[in] device the audio stream device
|
||||
* @param[in] direction AUDIO_CODEC_DIR_INPUT or AUDIO_CODEC_DIR_OUTPUT
|
||||
* @param[in] volume_percent volume in the range 0..100
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if no codec is bound for the given direction
|
||||
*/
|
||||
error_t (*set_volume)(struct Device* device, enum AudioCodecDirection direction, float volume_percent);
|
||||
|
||||
/** @brief Gets the shared volume for the given direction. See set_volume. */
|
||||
error_t (*get_volume)(struct Device* device, enum AudioCodecDirection direction, float* volume_percent);
|
||||
|
||||
/** @brief Sets the shared mute state for the given direction. See set_volume. */
|
||||
error_t (*set_mute)(struct Device* device, enum AudioCodecDirection direction, bool muted);
|
||||
|
||||
/** @brief Gets the shared mute state for the given direction. See set_volume. */
|
||||
error_t (*get_mute)(struct Device* device, enum AudioCodecDirection direction, bool* muted);
|
||||
|
||||
/**
|
||||
* @brief Enables or disables the given direction. A disabled direction cannot be
|
||||
* opened (open_input/open_output return ERROR_NOT_ALLOWED) and any open stream in
|
||||
* that direction is closed. Used by Settings UI / AudioService to govern access.
|
||||
* @param[in] device the audio stream device
|
||||
* @param[in] direction AUDIO_CODEC_DIR_INPUT or AUDIO_CODEC_DIR_OUTPUT
|
||||
* @param[in] enabled true to enable, false to disable
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_SUPPORTED if no codec is bound for the given direction
|
||||
*/
|
||||
error_t (*set_enabled)(struct Device* device, enum AudioCodecDirection direction, bool enabled);
|
||||
|
||||
/** @brief Gets the enabled state for the given direction. See set_enabled. */
|
||||
error_t (*get_enabled)(struct Device* device, enum AudioCodecDirection direction, bool* enabled);
|
||||
|
||||
/**
|
||||
* @brief Checks whether a codec supporting the given direction is bound.
|
||||
* @param[in] device the audio stream device
|
||||
* @param[in] direction AUDIO_CODEC_DIR_INPUT or AUDIO_CODEC_DIR_OUTPUT
|
||||
* @param[out] supported true if a codec for this direction is bound, false otherwise
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t (*is_supported)(struct Device* device, enum AudioCodecDirection direction, bool* supported);
|
||||
|
||||
/** @brief See audio_stream_set_change_callback. */
|
||||
error_t (*set_change_callback)(struct Device* device, AudioStreamChangeCallback callback, void* user_data);
|
||||
};
|
||||
|
||||
/** @brief See AudioStreamApi::open_input */
|
||||
error_t audio_stream_open_input(struct Device* device, const struct AudioStreamConfig* config, AudioStreamHandle* out_handle);
|
||||
|
||||
/** @brief See AudioStreamApi::open_output */
|
||||
error_t audio_stream_open_output(struct Device* device, const struct AudioStreamConfig* config, AudioStreamHandle* out_handle);
|
||||
|
||||
/** @brief See AudioStreamApi::read */
|
||||
error_t audio_stream_read(AudioStreamHandle handle, void* data, size_t data_size, size_t* bytes_read, TickType_t timeout);
|
||||
|
||||
/** @brief See AudioStreamApi::write */
|
||||
error_t audio_stream_write(AudioStreamHandle handle, const void* data, size_t data_size, size_t* bytes_written, TickType_t timeout);
|
||||
|
||||
/** @brief See AudioStreamApi::close */
|
||||
error_t audio_stream_close(AudioStreamHandle handle);
|
||||
|
||||
/** @brief See AudioStreamApi::set_volume */
|
||||
error_t audio_stream_set_volume(struct Device* device, enum AudioCodecDirection direction, float volume_percent);
|
||||
|
||||
/** @brief See AudioStreamApi::get_volume */
|
||||
error_t audio_stream_get_volume(struct Device* device, enum AudioCodecDirection direction, float* volume_percent);
|
||||
|
||||
/** @brief See AudioStreamApi::set_mute */
|
||||
error_t audio_stream_set_mute(struct Device* device, enum AudioCodecDirection direction, bool muted);
|
||||
|
||||
/** @brief See AudioStreamApi::get_mute */
|
||||
error_t audio_stream_get_mute(struct Device* device, enum AudioCodecDirection direction, bool* muted);
|
||||
|
||||
/** @brief See AudioStreamApi::set_enabled */
|
||||
error_t audio_stream_set_enabled(struct Device* device, enum AudioCodecDirection direction, bool enabled);
|
||||
|
||||
/** @brief See AudioStreamApi::get_enabled */
|
||||
error_t audio_stream_get_enabled(struct Device* device, enum AudioCodecDirection direction, bool* enabled);
|
||||
|
||||
/** @brief See AudioStreamApi::is_supported */
|
||||
error_t audio_stream_is_supported(struct Device* device, enum AudioCodecDirection direction, bool* supported);
|
||||
|
||||
/** @brief See AudioStreamApi::set_change_callback */
|
||||
error_t audio_stream_set_change_callback(struct Device* device, AudioStreamChangeCallback callback, void* user_data);
|
||||
|
||||
extern const struct DeviceType AUDIO_STREAM_TYPE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -40,6 +40,16 @@ struct I2sTdmRxConfig {
|
||||
uint8_t slot_bit_width; // bit width of each TDM slot (0 = auto, matches bits_per_sample)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief I2S PDM RX config (e.g. for a PDM MEMS microphone such as the SPM1423).
|
||||
* Only supported on I2S controller 0 on ESP32 targets -- PDM RX is hardware-restricted
|
||||
* to that controller. PDM only supports 16-bit samples and mono or stereo.
|
||||
*/
|
||||
struct I2sPdmRxConfig {
|
||||
uint32_t sample_rate_hz;
|
||||
bool stereo; // false = mono (single PDM mic), true = stereo (two PDM mics)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief I2S Config
|
||||
*/
|
||||
@@ -104,13 +114,40 @@ struct I2sControllerApi {
|
||||
|
||||
/**
|
||||
* @brief Reconfigures the RX channel to TDM mode (e.g. for ES7210).
|
||||
* Must be called after set_config() which creates the channel handles.
|
||||
* Does not require set_config() to be called first -- TDM allocates its own RX
|
||||
* channel handle independently of standard mode (calling set_config() first would
|
||||
* leave its TX/RX channel pair bound to the shared BCLK/WS/MCLK pins, which then
|
||||
* blocks this call from claiming them; callers should pick TDM or standard mode
|
||||
* up front, not call both in sequence).
|
||||
* @param[in] device the I2S controller device
|
||||
* @param[in] config TDM parameters
|
||||
* @retval ERROR_NONE when the operation was successful
|
||||
* @retval ERROR_NOT_SUPPORTED if the driver does not implement TDM
|
||||
*/
|
||||
error_t (*set_rx_tdm_config)(struct Device* device, const struct I2sTdmRxConfig* config);
|
||||
|
||||
/**
|
||||
* @brief Reconfigures the RX channel to PDM mode (e.g. for a PDM MEMS microphone).
|
||||
* Does not require set_config() to be called first -- PDM RX allocates its own
|
||||
* channel handle independently of standard/TDM mode.
|
||||
* @param[in] device the I2S controller device
|
||||
* @param[in] config PDM parameters
|
||||
* @retval ERROR_NONE when the operation was successful
|
||||
* @retval ERROR_NOT_SUPPORTED if the driver/controller does not support PDM RX
|
||||
*/
|
||||
error_t (*set_rx_pdm_config)(struct Device* device, const struct I2sPdmRxConfig* config);
|
||||
|
||||
/**
|
||||
* @brief Disables and releases only the TX or only the RX channel, leaving the other
|
||||
* direction (if active) running. Unlike reset(), which tears down both directions --
|
||||
* unsuitable when a single controller carries an independent TX user (e.g. a speaker
|
||||
* amp) and RX user (e.g. a PDM mic) that must be able to close without affecting each
|
||||
* other.
|
||||
* @param[in] device the I2S controller device
|
||||
* @param[in] is_input true to disable the RX channel, false to disable the TX channel
|
||||
* @retval ERROR_NONE when the operation was successful
|
||||
*/
|
||||
error_t (*disable_direction)(struct Device* device, bool is_input);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -160,7 +197,9 @@ error_t i2s_controller_reset(struct Device* device);
|
||||
|
||||
/**
|
||||
* @brief Reconfigures the RX channel to TDM mode (e.g. for ES7210 4-slot mic ADC).
|
||||
* Must be called after i2s_controller_set_config() which creates the channel handles.
|
||||
* Does not require i2s_controller_set_config() to be called first -- see
|
||||
* I2sControllerApi::set_rx_tdm_config for why standard mode and TDM mode are
|
||||
* alternative setup paths rather than sequential steps.
|
||||
* @param[in] device the I2S controller device
|
||||
* @param[in] config TDM parameters
|
||||
* @retval ERROR_NONE when the operation was successful
|
||||
@@ -168,6 +207,24 @@ error_t i2s_controller_reset(struct Device* device);
|
||||
*/
|
||||
error_t i2s_controller_set_rx_tdm_config(struct Device* device, const struct I2sTdmRxConfig* config);
|
||||
|
||||
/**
|
||||
* @brief Reconfigures the RX channel to PDM mode (e.g. for a PDM MEMS microphone).
|
||||
* @param[in] device the I2S controller device
|
||||
* @param[in] config PDM parameters
|
||||
* @retval ERROR_NONE when the operation was successful
|
||||
* @retval ERROR_NOT_SUPPORTED if the driver/controller does not support PDM RX
|
||||
*/
|
||||
error_t i2s_controller_set_rx_pdm_config(struct Device* device, const struct I2sPdmRxConfig* config);
|
||||
|
||||
/**
|
||||
* @brief See I2sControllerApi::disable_direction. Falls back to the full reset() if the
|
||||
* driver doesn't implement direction-aware teardown.
|
||||
* @param[in] device the I2S controller device
|
||||
* @param[in] is_input true to disable the RX channel, false to disable the TX channel
|
||||
* @retval ERROR_NONE when the operation was successful
|
||||
*/
|
||||
error_t i2s_controller_disable_direction(struct Device* device, bool is_input);
|
||||
|
||||
extern const struct DeviceType I2S_CONTROLLER_TYPE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <tactility/drivers/audio_codec.h>
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/device.h>
|
||||
|
||||
#define AUDIO_CODEC_DRIVER_API(driver) ((struct AudioCodecApi*)driver->api)
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t audio_codec_open(Device* device, const struct AudioCodecStreamConfig* config) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->open(device, config);
|
||||
}
|
||||
|
||||
error_t audio_codec_close(Device* device) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->close(device);
|
||||
}
|
||||
|
||||
error_t audio_codec_read(Device* device, void* data, size_t data_size, size_t* bytes_read, TickType_t timeout) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->read(device, data, data_size, bytes_read, timeout);
|
||||
}
|
||||
|
||||
error_t audio_codec_write(Device* device, const void* data, size_t data_size, size_t* bytes_written, TickType_t timeout) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->write(device, data, data_size, bytes_written, timeout);
|
||||
}
|
||||
|
||||
error_t audio_codec_set_volume(Device* device, AudioCodecDirection direction, float volume_percent) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->set_volume(device, direction, volume_percent);
|
||||
}
|
||||
|
||||
error_t audio_codec_get_volume(Device* device, AudioCodecDirection direction, float* volume_percent) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->get_volume(device, direction, volume_percent);
|
||||
}
|
||||
|
||||
error_t audio_codec_set_mute(Device* device, AudioCodecDirection direction, bool muted) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->set_mute(device, direction, muted);
|
||||
}
|
||||
|
||||
error_t audio_codec_get_mute(Device* device, AudioCodecDirection direction, bool* muted) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->get_mute(device, direction, muted);
|
||||
}
|
||||
|
||||
error_t audio_codec_get_native_sample_rate(Device* device, AudioCodecDirection direction, uint32_t* rate_hz) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->get_native_sample_rate(device, direction, rate_hz);
|
||||
}
|
||||
|
||||
error_t audio_codec_get_native_channels(Device* device, AudioCodecDirection direction, uint8_t* channels) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->get_native_channels(device, direction, channels);
|
||||
}
|
||||
|
||||
error_t audio_codec_get_capabilities(Device* device, AudioCodecDirection* supported_directions) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->get_capabilities(device, supported_directions);
|
||||
}
|
||||
|
||||
error_t audio_codec_get_input_gain_multiplier(Device* device, float* gain) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
if (AUDIO_CODEC_DRIVER_API(driver)->get_input_gain_multiplier == nullptr) {
|
||||
*gain = 1.0f;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
return AUDIO_CODEC_DRIVER_API(driver)->get_input_gain_multiplier(device, gain);
|
||||
}
|
||||
|
||||
const struct DeviceType AUDIO_CODEC_TYPE {
|
||||
.name = "audio-codec"
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <tactility/drivers/audio_stream.h>
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/device.h>
|
||||
|
||||
#define AUDIO_STREAM_DRIVER_API(driver) ((struct AudioStreamApi*)driver->api)
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t audio_stream_open_input(Device* device, const struct AudioStreamConfig* config, AudioStreamHandle* outHandle) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->open_input(device, config, outHandle);
|
||||
}
|
||||
|
||||
error_t audio_stream_open_output(Device* device, const struct AudioStreamConfig* config, AudioStreamHandle* outHandle) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->open_output(device, config, outHandle);
|
||||
}
|
||||
|
||||
error_t audio_stream_read(AudioStreamHandle handle, void* data, size_t data_size, size_t* bytes_read, TickType_t timeout) {
|
||||
const auto* driver = device_get_driver(handle->device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->read(handle, data, data_size, bytes_read, timeout);
|
||||
}
|
||||
|
||||
error_t audio_stream_write(AudioStreamHandle handle, const void* data, size_t data_size, size_t* bytes_written, TickType_t timeout) {
|
||||
const auto* driver = device_get_driver(handle->device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->write(handle, data, data_size, bytes_written, timeout);
|
||||
}
|
||||
|
||||
error_t audio_stream_close(AudioStreamHandle handle) {
|
||||
const auto* driver = device_get_driver(handle->device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->close(handle);
|
||||
}
|
||||
|
||||
error_t audio_stream_set_volume(Device* device, AudioCodecDirection direction, float volume_percent) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->set_volume(device, direction, volume_percent);
|
||||
}
|
||||
|
||||
error_t audio_stream_get_volume(Device* device, AudioCodecDirection direction, float* volume_percent) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->get_volume(device, direction, volume_percent);
|
||||
}
|
||||
|
||||
error_t audio_stream_set_mute(Device* device, AudioCodecDirection direction, bool muted) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->set_mute(device, direction, muted);
|
||||
}
|
||||
|
||||
error_t audio_stream_get_mute(Device* device, AudioCodecDirection direction, bool* muted) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->get_mute(device, direction, muted);
|
||||
}
|
||||
|
||||
error_t audio_stream_set_enabled(Device* device, AudioCodecDirection direction, bool enabled) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->set_enabled(device, direction, enabled);
|
||||
}
|
||||
|
||||
error_t audio_stream_get_enabled(Device* device, AudioCodecDirection direction, bool* enabled) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->get_enabled(device, direction, enabled);
|
||||
}
|
||||
|
||||
error_t audio_stream_is_supported(Device* device, AudioCodecDirection direction, bool* supported) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->is_supported(device, direction, supported);
|
||||
}
|
||||
|
||||
error_t audio_stream_set_change_callback(Device* device, AudioStreamChangeCallback callback, void* user_data) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return AUDIO_STREAM_DRIVER_API(driver)->set_change_callback(device, callback, user_data);
|
||||
}
|
||||
|
||||
const struct DeviceType AUDIO_STREAM_TYPE {
|
||||
.name = "audio-stream"
|
||||
};
|
||||
|
||||
}
|
||||
@@ -38,6 +38,20 @@ error_t i2s_controller_set_rx_tdm_config(struct Device* device, const struct I2s
|
||||
return I2S_DRIVER_API(driver)->set_rx_tdm_config(device, config);
|
||||
}
|
||||
|
||||
error_t i2s_controller_set_rx_pdm_config(struct Device* device, const struct I2sPdmRxConfig* config) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
if (!I2S_DRIVER_API(driver)->set_rx_pdm_config) return ERROR_NOT_SUPPORTED;
|
||||
return I2S_DRIVER_API(driver)->set_rx_pdm_config(device, config);
|
||||
}
|
||||
|
||||
error_t i2s_controller_disable_direction(struct Device* device, bool isInput) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
if (!I2S_DRIVER_API(driver)->disable_direction) {
|
||||
return I2S_DRIVER_API(driver)->reset(device);
|
||||
}
|
||||
return I2S_DRIVER_API(driver)->disable_direction(device, isInput);
|
||||
}
|
||||
|
||||
const struct DeviceType I2S_CONTROLLER_TYPE {
|
||||
.name = "i2s-controller"
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <tactility/concurrent/timer.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/drivers/audio_codec.h>
|
||||
#include <tactility/drivers/audio_stream.h>
|
||||
#include <tactility/drivers/bluetooth.h>
|
||||
#include <tactility/drivers/bluetooth_serial.h>
|
||||
#include <tactility/drivers/bluetooth_midi.h>
|
||||
@@ -80,6 +82,32 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = {
|
||||
DEFINE_MODULE_SYMBOL(driver_is_compatible),
|
||||
DEFINE_MODULE_SYMBOL(driver_find_compatible),
|
||||
DEFINE_MODULE_SYMBOL(driver_get_device_type),
|
||||
// drivers/audio_codec
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_open),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_close),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_read),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_write),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_set_volume),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_get_volume),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_set_mute),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_get_mute),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_get_native_sample_rate),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_get_native_channels),
|
||||
DEFINE_MODULE_SYMBOL(audio_codec_get_capabilities),
|
||||
DEFINE_MODULE_SYMBOL(AUDIO_CODEC_TYPE),
|
||||
// drivers/audio_stream
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_open_input),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_open_output),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_read),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_write),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_close),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_set_volume),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_get_volume),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_set_mute),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_get_mute),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_set_enabled),
|
||||
DEFINE_MODULE_SYMBOL(audio_stream_get_enabled),
|
||||
DEFINE_MODULE_SYMBOL(AUDIO_STREAM_TYPE),
|
||||
// drivers/gpio_controller
|
||||
DEFINE_MODULE_SYMBOL(gpio_descriptor_acquire),
|
||||
DEFINE_MODULE_SYMBOL(gpio_descriptor_release),
|
||||
|
||||
Reference in New Issue
Block a user