mirror of https://github.com/ARMmbed/mbed-os.git
390 lines
10 KiB
C++
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
|