mbed-os/drivers/USBAudio.h

390 lines
10 KiB
C++

/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef USBAudio_H
#define USBAudio_H
/* These headers are included for child class. */
#include "USBDescriptor.h"
#include "USBDevice_Types.h"
#include "USBDevice.h"
#include "Callback.h"
#include "OperationList.h"
#include "ByteBuffer.h"
#include "rtos/EventFlags.h"
/** \defgroup drivers-public-api-usb USB
* \ingroup drivers-public-api
*/
/**
* \defgroup drivers_USBAudio USBAudio class
* \ingroup drivers-public-api-usb
* @{
*/
/**
* USBAudio example
*
* @code
* #include "mbed.h"
* #include "USBAudio.h"
*
* // Audio loopback example use:
* // 1. Select "Mbed Audio" as your sound device
* // 2. Play a song or audio file
* // 3. Record the output using a program such as Audacity
*
* int main() {
*
* USBAudio audio(true, 44100, 2, 44100, 2);
*
* printf("Looping audio\r\n");
* static uint8_t buf[128];
* while (true) {
* if (!audio.read(buf, sizeof(buf))) {
* memset(buf, 0, sizeof(buf));
* }
* audio.write(buf, sizeof(buf));
* }
* }
* @endcode
*/
class USBAudio: protected USBDevice {
public:
enum AudioEvent {
Start,
Transfer,
End
};
/**
* Basic constructor
*
* Construct this object optionally connecting.
*
* @note Do not use this constructor in derived classes.
*
* @param connect Call connect on initialization
* @param frequency_rx frequency in Hz (default: 48000)
* @param channel_count_rx channel number (1 or 2) (default: 1)
* @param frequency_tx frequency in Hz (default: 8000)
* @param channel_count_tx channel number (1 or 2) (default: 1)
* @param buffer_ms time audio can be buffered without overflowing in milliseconds
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBAudio(bool connect = true, uint32_t frequency_rx = 48000, uint8_t channel_count_rx = 1, uint32_t frequency_tx = 8000, uint8_t channel_count_tx = 1, uint32_t buffer_ms = 10, uint16_t vendor_id = 0x7bb8, uint16_t product_id = 0x1111, uint16_t product_release = 0x0100);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param frequency_rx frequency in Hz (default: 48000)
* @param channel_count_rx channel number (1 or 2) (default: 1)
* @param frequency_tx frequency in Hz (default: 8000)
* @param channel_count_tx channel number (1 or 2) (default: 1)
* @param buffer_ms time audio can be buffered without overflowing in milliseconds
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBAudio(USBPhy *phy, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBAudio();
/**
* Connect USBAudio
*/
void connect();
/**
* Disconnect USBAudio
*
* This unblocks all calls to read_ready and write_ready.
*/
void disconnect();
/**
* Read audio data
*
* @param buf pointer on a buffer which will be filled with audio data
* @param size size to read
*
* @returns true if successful
*/
bool read(uint8_t *buf, uint32_t size);
/**
* Nonblocking audio data read
*
* Read the available audio data.
*
* @param buf pointer on a buffer which will be filled with audio data
* @param size size to read
* @param actual size actually read
* @note This function is safe to call from USBAudio callbacks.
*/
void read_nb(uint8_t *buf, uint32_t size, uint32_t *actual);
/**
* Return the number read packets dropped due to overflow
*
* @param clear Reset the overflow count back to 0
* @return Number of packets dropped due to overflow
*/
uint32_t read_overflows(bool clear = false);
/**
* Check if the audio read channel is open
*
* @return true if the audio read channel open, false otherwise
*/
bool read_ready();
/**
* Wait until the audio read channel is open
*/
void read_wait_ready();
/**
* Write audio data
*
* @param buf pointer to audio data to write
* @param size size to write
*
* @returns true if successful
*/
bool write(uint8_t *buf, uint32_t size);
/**
* Nonblocking audio data write
*
* Write the available audio data.
*
* @param buf pointer to audio data to write
* @param size size to write
* @param actual actual size written
* @note This function is safe to call from USBAudio callbacks.
*/
void write_nb(uint8_t *buf, uint32_t size, uint32_t *actual);
/**
* Return the number write packets not sent due to underflow
*
* @param clear Reset the underflow count back to 0
* @return Number of packets that should have been
* sent but weren't due to overflow
*/
uint32_t write_underflows(bool clear = false);
/**
* Check if the audio write channel is open
*
* @return true if the audio write channel open, false otherwise
*/
bool write_ready();
/**
* Wait until the audio write channel is open
*/
void write_wait_ready();
/**
* Get current volume between 0.0 and 1.0
*
* @returns volume
*/
float get_volume();
/** Attach a Callback to update the volume
*
* @param cb Callback to attach
*
*/
void attach(mbed::Callback<void()> &cb);
/** attach a Callback to Tx Done
*
* @param cb Callback to attach
*
*/
void attach_tx(mbed::Callback<void(AudioEvent)> &cb);
/** attach a Callback to Rx Done
*
* @param cb Callback to attach
*
*/
void attach_rx(mbed::Callback<void(AudioEvent)> &cb);
protected:
virtual void callback_state_change(DeviceState new_state);
virtual void callback_request(const setup_packet_t *setup);
virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted);
virtual void callback_set_configuration(uint8_t configuration);
virtual void callback_set_interface(uint16_t interface, uint8_t alternate);
virtual const uint8_t *string_iproduct_desc();
virtual const uint8_t *string_iinterface_desc();
virtual const uint8_t *configuration_desc(uint8_t index);
private:
class AsyncWrite;
class AsyncRead;
enum ChannelState {
Powerdown,
Closed,
Opened
};
void _init(uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms);
/*
* Call to rebuild the configuration descriptor
*
* This function should be called on creation or when any
* value that is part of the configuration descriptor
* changes.
* @note This function uses ~200 bytes of stack so
* make sure your stack is big enough for it.
*/
void _build_configuration_desc();
void _receive_change(ChannelState new_state);
void _receive_isr();
void _send_change(ChannelState new_state);
void _send_isr_start();
void _send_isr_next_sync();
void _send_isr();
// has connect been called
bool _connected;
// audio volume
float _volume;
// mute state
uint8_t _mute;
// Volume Current Value
uint16_t _vol_cur;
// Volume Minimum Value
uint16_t _vol_min;
// Volume Maximum Value
uint16_t _vol_max;
// Volume Resolution
uint16_t _vol_res;
// callback to update volume
mbed::Callback<void()> _update_vol;
// callback transmit Done
mbed::Callback<void(AudioEvent)> _tx_done;
// callback receive Done
mbed::Callback<void(AudioEvent)> _rx_done;
// Number of times data was dropped due to an overflow
uint32_t _rx_overflow;
// Number of times data was not sent due to an underflow
uint32_t _tx_underflow;
// frequency in Hz
uint32_t _tx_freq;
uint32_t _rx_freq;
// mono, stereo,...
uint8_t _rx_channel_count;
uint8_t _tx_channel_count;
bool _tx_idle;
uint16_t _tx_frame_fract;
uint16_t _tx_whole_frames_per_xfer;
uint16_t _tx_fract_frames_per_xfer;
// size of the maximum packet for the isochronous endpoint
uint16_t _tx_packet_size_max;
uint16_t _rx_packet_size_max;
// Buffer used for the isochronous transfer
uint8_t *_tx_packet_buf;
uint8_t *_rx_packet_buf;
// Holding buffer
ByteBuffer _tx_queue;
ByteBuffer _rx_queue;
// State of the audio channels
ChannelState _tx_state;
ChannelState _rx_state;
// sample - a single PCM audio sample
// frame - a group of samples from each channel
// packet - a group of frames sent over USB in one transfer
// Blocking primitives
OperationList<AsyncWrite> _write_list;
OperationList<AsyncRead> _read_list;
rtos::EventFlags _flags;
// endpoint numbers
usb_ep_t _episo_out; // rx endpoint
usb_ep_t _episo_in; // tx endpoint
// channel config in the configuration descriptor: master, left, right
uint16_t _channel_config_rx;
uint16_t _channel_config_tx;
// configuration descriptor
uint8_t _config_descriptor[183];
// buffer for control requests
uint8_t _control_receive[2];
};
/** @}*/
#endif