1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/include/dac_dds.h
Benjamin Valentin ceb3f8443a drivers/dac_dds: add module to play sample buffer over a DAC
This adds an API function to play a buffer of Audio samples using a DAC.

Double buffered operation is supported by specifying a callback that will
be called when the next buffer can be queued with dac_dds_play().
2020-12-04 23:12:32 +01:00

165 lines
5.3 KiB
C

/*
* Copyright (C) 2020 Beuth Hochschule für Technik Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_dac_dds DAC Direct Digital Synthesis
* @ingroup drivers_periph_dac
*
* @{
* @file
* @brief Use a DAC to play a buffer of samples
*
* A buffer of (audio) samples can be played on a DAC with
* a given sample rate.
* To supply a steady stream of samples, a double buffer is
* used.
* While buffer A is played on the DAC the user can fill buffer
* B with the next batch of samples by calling @ref dac_dds_play
* again. Once buffer A has finished playing, buffer B will
* automatically be used instead.
*
* A callback can be registered that signals when the next buffer
* is ready to be filled.
* Do not do the actual buffer filling inside the callback as this
* is called from the timer context that is used to play the samples.
* Instead, use it to wake a thread that then provides the samples
* and calls @ref dac_dds_play.
* If the next sample buffer is already prepared, you can also call
* `dac_dds_play` within the callback, just don't do any I/O or
* heavy processing.
*
* @author Benjamin Valentin <benpicco@beuth-hochschule.de>
*/
#ifndef DAC_DDS_H
#define DAC_DDS_H
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#include "periph/dac.h"
#include "periph/timer.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The callback that will be called when the end of the current sample buffer
* has been reached.
* Should be used to start filling the next sample buffer with @ref dac_dds_play.
*
* @note Will be called in interrupt context. Only use the callback to signal a
* thread. Don't directly fill the sample buffer in the callback.
*/
typedef void (*dac_dds_cb_t)(void *arg);
/**
* @brief Configuration struct for a DAC DDS channel
* @{
*/
typedef struct {
dac_t dac; /**< The DAC used for playing the sample */
tim_t timer; /**< Timer used for periodic DAC events */
uint32_t timer_hz; /**< Timer frequency, must be at least 2x sample rate */
} dac_dds_params_t;
/** @} */
/**
* @brief Index of the DAC DDS channel
*/
typedef uint8_t dac_dds_t;
/**
* @brief A sample has a resolution of 8 bit
*/
#ifndef DAC_FLAG_8BIT
#define DAC_FLAG_8BIT (0x0)
#endif
/**
* @brief A sample has a resolution of 16 bit
*/
#ifndef DAC_FLAG_16BIT
#define DAC_FLAG_16BIT (0x1)
#endif
/**
* @brief Initialize a DAC for playing audio samples
* A user defined callback can be provided that will be called when
* the next buffer can be queued.
* @experimental
*
* @param[in] dac The DAC to initialize
* @param[in] sample_rate The sample rate in Hz
* @param[in] flags Optional flags (@ref DAC_FLAG_16BIT)
* @param[in] cb Will be called when the next buffer can be queued
* @param[in] cb_arg Callback argument
*/
void dac_dds_init(dac_dds_t dac, uint16_t sample_rate, uint8_t flags,
dac_dds_cb_t cb, void *cb_arg);
/**
* @brief Change the 'buffer done' callback.
* A user defined callback can be provided that will be called when
* the next buffer can be queued.
* This function can be used to change the callback on the fly.
*
* Passing in a @p cb of `NULL` can be used to only update the arg
* without updating the @p cb. Conversely, to clear the callback, both
* @p cb and @p cb_arg need to be passed in as NULL.
* @experimental
*
* @param[in] dac The DAC to configure
* @param[in] cb Called when the played buffer is done
* @param[in] cb_arg Callback argument
*/
void dac_dds_set_cb(dac_dds_t dac, dac_dds_cb_t cb, void *cb_arg);
/**
* @brief Play a buffer of (audio) samples on a DAC
*
* If this function is called while another buffer is already
* being played, the new `buf` will be played when the current
* buffer has finished playing.
*
* The DAC implementations allows one buffer to be queued
* (double buffering).
*
* Whenever a new buffer can be queued, the @ref dac_dds_cb_t
* callback function will be executed.
*
* @experimental
*
* @param[in] dac The DAC to play the sample on
* @param[in] buf A buffer with (audio) samples
* @param[in] len Number of bytes to be played
*
* @return `true` if the buffer was queued while another
* buffer is currently playing.
* `false` if the new buffer is played immediately.
* That means playing was just started or an underrun
* occurred.
*/
bool dac_dds_play(dac_dds_t dac, const void *buf, size_t len);
/**
* @brief Stop playback of the current sample buffer
*
* @param[in] dac The DAC to stop
*/
void dac_dds_stop(dac_dds_t dac);
#ifdef __cplusplus
}
#endif
#endif /* DAC_DDS_H */
/** @} */