CUGL 2.3
Cornell University Game Library
|
#include <CUAudioResampler.h>
Public Member Functions | |
AudioResampler () | |
~AudioResampler () | |
virtual bool | init () override |
virtual bool | init (Uint8 channels, Uint32 rate) override |
bool | init (const std::shared_ptr< AudioNode > &input, Uint32 rate) |
virtual void | dispose () override |
bool | attach (const std::shared_ptr< AudioNode > &node) |
std::shared_ptr< AudioNode > | detach () |
std::shared_ptr< AudioNode > | getInput () const |
virtual void | setReadSize (Uint32 size) override |
Uint32 | getInputRate () const |
void | setInputRate (Uint32 value) |
float | getStopband () const |
void | setStopband (float value) |
Uint32 | getBitPrecision () const |
void | setBitPrecision (Uint32 value) |
Uint32 | getZeroCrossings () const |
void | setZeroCrossings (Uint32 value) |
virtual bool | completed () override |
virtual Uint32 | read (float *buffer, Uint32 frames) override |
virtual bool | mark () override |
virtual bool | unmark () override |
virtual bool | reset () override |
virtual Sint64 | advance (Uint32 frames) override |
virtual Sint64 | getPosition () const override |
virtual Sint64 | setPosition (Uint32 position) override |
virtual double | getElapsed () const override |
virtual double | setElapsed (double time) override |
virtual double | getRemaining () const override |
virtual double | setRemaining (double time) override |
Public Member Functions inherited from cugl::audio::AudioNode | |
AudioNode () | |
virtual | ~AudioNode () |
virtual bool | init () |
virtual bool | init (Uint8 channels, Uint32 rate) |
virtual void | dispose () |
Uint8 | getChannels () const |
Uint32 | getRate () const |
float | getGain () |
virtual void | setGain (float gain) |
Uint32 | getReadSize () const |
virtual void | setReadSize (Uint32 size) |
const std::string | getClassName () const |
const std::string | getName () const |
void | setName (const std::string name) |
Sint32 | getTag () const |
void | setTag (Sint32 tag) |
virtual std::string | toString (bool verbose=false) const |
operator std::string () const | |
Callback | getCallback () |
void | setCallback (Callback callback) |
virtual bool | isPaused () |
virtual bool | pause () |
virtual bool | resume () |
virtual bool | completed () |
virtual Uint32 | read (float *buffer, Uint32 frames) |
virtual bool | mark () |
virtual bool | unmark () |
virtual bool | reset () |
virtual Sint64 | advance (Uint32 frames) |
virtual Sint64 | getPosition () const |
virtual Sint64 | setPosition (Uint32 position) |
virtual double | getElapsed () const |
virtual double | setElapsed (double time) |
virtual double | getRemaining () const |
virtual double | setRemaining (double time) |
Static Public Member Functions | |
static std::shared_ptr< AudioResampler > | alloc () |
static std::shared_ptr< AudioResampler > | alloc (Uint8 channels, Uint32 rate) |
static std::shared_ptr< AudioResampler > | alloc (const std::shared_ptr< AudioNode > &input, Uint32 rate) |
Additional Inherited Members | |
Public Types inherited from cugl::audio::AudioNode | |
enum | Action : int { COMPLETE = 0 , INTERRUPT = 1 , FADE_OUT = 2 , FADE_IN = 3 , FADE_DIP = 4 , LOOPBACK = 5 } |
typedef std::function< void(const std::shared_ptr< AudioNode > &node, Action type)> | Callback |
Static Public Attributes inherited from cugl::audio::AudioNode | |
static const Uint32 | DEFAULT_CHANNELS |
static const Uint32 | DEFAULT_SAMPLING |
Protected Member Functions inherited from cugl::audio::AudioNode | |
void | notify (const std::shared_ptr< AudioNode > &node, Action action) |
Protected Attributes inherited from cugl::audio::AudioNode | |
Uint8 | _channels |
Uint32 | _sampling |
bool | _booted |
std::atomic< float > | _ndgain |
std::atomic< bool > | _paused |
std::atomic< bool > | _polling |
Callback | _callback |
std::atomic< bool > | _calling |
Sint32 | _tag |
Uint32 | _readsize |
std::string | _localname |
std::string | _classname |
size_t | _hashOfName |
bool | _locked |
This class provides a graph node for converting from one sample rate to another.
The node uses a kaiser-windowed sinc filter to perform continuous resampling on a potentially infinite audio stream. This is is necessary for cross-platform reasons as iPhones are very stubborn about delivering any requested sampling rates other than 48000.
The filter is configurable. You can set the number of zero crossings, as well as the attentionuation factor in decibels. Details behind the filter design of this resampler can be found here
https://tomroelandts.com/articles/how-to-create-a-configurable-filter-using-a-kaiser-window
This is a dynamic resampler. While the output sampling rate is fixed, the input is not. It will readjust the conversion filter to match the sampling rate of the input node whenever the input node changes.
The audio graph should only be accessed in the main thread. In addition, no methods marked as AUDIO THREAD ONLY should ever be accessed by the user.
This class does not support any actions for the AudioNode#setCallback
.
cugl::audio::AudioResampler::AudioResampler | ( | ) |
Creates a degenerate audio resampler.
The node has not been initialized, so it is not active. The node must be initialized to be used.
|
inline |
Deletes the audio resampler, disposing of all resources
|
overridevirtual |
Advances the stream by the given number of frames.
DELEGATED METHOD: This method delegates its call to the input node. It returns -1 if there is no input node or if this method is unsupported in that node
This method only advances the read position, it does not actually read data into a buffer. This method is generally not supported for nodes with real-time input like AudioInput
.
frames | The number of frames to advace |
Reimplemented from cugl::audio::AudioNode.
|
inlinestatic |
Returns a newly allocated resampler with 2 channels at 48000 Hz.
This sample rate of the output of this node is 48000 Hz, but the input sample rate depends on the input node, which can change over time. However, the input node must agree with number of channels, which is fixed.
|
inlinestatic |
Returns a newly allocated resampler with the given input node and sample rate.
This node acquires the channels of the input, but will use the given sample rate as its output rate. If input is nullptr, this method will fail.
input | The audio node to resample |
rate | The output sample rate (frequency) in Hz |
|
inlinestatic |
Returns a newly allocated resampler with the given channels and sample rate.
This sample rate is the output rate of this node. The input same rate depends on the input node, which can change over time. However, the input node must agree with number of channels, which is fixed.
channels | The number of audio channels |
rate | The output sample rate (frequency) in HZ |
bool cugl::audio::AudioResampler::attach | ( | const std::shared_ptr< AudioNode > & | node | ) |
Attaches an audio node to this resampler.
This method will reset the resampler stream if the input has a different rate than the previous input value (and is not the same rate as the output). It will fail if the input does not have the same number of channels as this resampler.
node | The audio node to resample |
|
overridevirtual |
Returns true if this resampler has no more data.
An audio node is typically completed if it return 0 (no frames read) on subsequent calls to read()
. However, for infinite-running audio threads, it is possible for this method to return true even when data can still be read; in that case the node is notifying that it should be shut down.
Reimplemented from cugl::audio::AudioNode.
std::shared_ptr< AudioNode > cugl::audio::AudioResampler::detach | ( | ) |
Detaches an audio node from this resampler.
If the method succeeds, it returns the audio node that was removed. This method will not automatically reset the sampling stream.
|
overridevirtual |
Disposes any resources allocated for this resampler.
The state of the node is reset to that of an uninitialized constructor. Unlike the destructor, this method allows the node to be reinitialized.
Reimplemented from cugl::audio::AudioNode.
|
inline |
Returns the bit precision for audio sent to this filter.
Even though CUGL processes all audio data as floats, that does not mean that the audio on this platform is guaranteed to have 32 bit precision. Indeed, on Android, most audio is processed at 16 bit precision, and many audio files are recorded at this level of precision as well. Hence this filter assumes 16 bit precision by default.
This is relevant for the size of the filter to process the audio. Each additional bit doubles the size of the filter table used for the convolution. A 16 bit filter uses a very reasonable 512 entries per zero crossing. On the other hand, a 32 bit filter would require 131072 entries per zero crossing. Given the limitations of real-time resampling, it typically does not make much sense to assume more than 16 bits.
|
overridevirtual |
Returns the elapsed time in seconds.
DELEGATED METHOD: This method delegates its call to the input node. It returns -1 if there is no input node or if this method is unsupported in that node
In some nodes like AudioInput
, this method is only supported if mark()
is set. In that case, the times will be the number of seconds since the mark. Other nodes like AudioPlayer
measure from the start of the stream.
Reimplemented from cugl::audio::AudioNode.
|
inline |
Returns the input node of this resampler.
|
inline |
Returns the input sample rate of this filter.
This value is distinct from AudioNode#getRate()
, which is the output sample rate of this node. Instead, this value is the sample rate of any audio node connected to this one via the attach
method.
Normally this value is assigned when a new audio node is attached. However, changing this value requires that the underlying read buffer be resized. Hence, by setting this value ahead of time (and making sure that all attached input nodes match this sample rate), you can improve the performance of this filter.
Assigning this value while there is still an attached audio node has undefined behavior.
|
overridevirtual |
Returns the current frame position of this audio node
DELEGATED METHOD: This method delegates its call to the input node. It returns -1 if there is no input node or if this method is unsupported in that node
In some nodes like AudioInput
, this method is only supported if mark()
is set. In that case, the position will be the number of frames since the mark. Other nodes like AudioPlayer
measure from the start of the stream.
Reimplemented from cugl::audio::AudioNode.
|
overridevirtual |
Returns the remaining time in seconds.
DELEGATED METHOD: This method delegates its call to the input node. It returns -1 if there is no input node or if this method is unsupported in that node
In some nodes like AudioInput
, this method is only supported if setRemaining()
has been called. In that case, the node will be marked as completed after the given number of seconds. This may or may not actually move the read head. For example, in AudioPlayer
it will skip to the end of the sample. However, in AudioInput
it will simply time out after the given time.
Reimplemented from cugl::audio::AudioNode.
|
inline |
Returns the stopband attentuation for this filter in dB
This value is described in more detail here:
https://tomroelandts.com/articles/how-to-create-a-configurable-filter-using-a-kaiser-window
By default, this value is 80.0.
|
inline |
Returns the number of zero-crossings of this filter.
The zero-crossings of a sinc filter are relevant because the determine the number of coefficients in a single filter convolution. For X zero-crossings, a single output sample requires 2*(X-1) input computations. Increasing this value can give some increased value in filter. However, the droppoff for sinc filters is large enough that eventually that large enough values will have no discernable effect.
The default number of zero crossing is 5, meaning that this filter roughly causes an 8x-10x decrease in performance when processing audio (when taking all the relevant overhead into account). This value is that one recommended by this tutorial website:
https://www.dsprelated.com/freebooks/pasp/Windowed_Sinc_Interpolation.html
|
overridevirtual |
Initializes a resampler with 2 channels at 48000 Hz.
This sample rate of the output of this node is 48000 Hz, but the input sample rate depends on the input node, which can change over time. However, the input node must agree with number of channels, which is fixed.
Reimplemented from cugl::audio::AudioNode.
bool cugl::audio::AudioResampler::init | ( | const std::shared_ptr< AudioNode > & | input, |
Uint32 | rate | ||
) |
Initializes a resampler with the given input node and sample rate.
This node acquires the channels of the input, but will use the given sample rate as its output rate. If input is nullptr, this method will fail.
input | The audio node to resample |
rate | The output sample rate (frequency) in Hz |
|
overridevirtual |
Initializes a resampler with the given channels and sample rate.
This sample rate is the output rate of this node. The input same rate depends on the input node, which can change over time. However, the input node must agree with number of channels, which is fixed.
channels | The number of audio channels |
rate | The output sample rate (frequency) in Hz |
Reimplemented from cugl::audio::AudioNode.
|
overridevirtual |
Marks the current read position in the audio steam.
DELEGATED METHOD: This method delegates its call to the input node. It returns false if there is no input node or if this method is unsupported in that node
This method is typically used by reset()
to determine where to restore the read position. For some nodes (like AudioInput
), this method may start recording data to a buffer, which will continue until reset()
is called.
It is possible for reset()
to be supported even if this method is not.
Reimplemented from cugl::audio::AudioNode.
|
overridevirtual |
Reads up to the specified number of frames into the given buffer
AUDIO THREAD ONLY: Users should never access this method directly. The only exception is when the user needs to create a custom subclass of this AudioOutput.
The buffer should have enough room to store frames * channels elements. The channels are interleaved into the output buffer.
This method will always forward the read position.
buffer | The read buffer to store the results |
frames | The maximum number of frames to read |
Reimplemented from cugl::audio::AudioNode.
|
overridevirtual |
Resets the read position to the marked position of the audio stream.
DELEGATED METHOD: This method delegates its call to the input node. It returns false if there is no input node or if this method is unsupported in that node
When no mark()
is set, the result of this method is node dependent. Some nodes (such as AudioPlayer
) will reset to the beginning of the stream, while others (like AudioInput
) only support a rest when a mark is set. Pay attention to the return value of this method to see if the call is successful.
Reimplemented from cugl::audio::AudioNode.
void cugl::audio::AudioResampler::setBitPrecision | ( | Uint32 | value | ) |
Sets the bit precision for audio sent to this filter.
Even though CUGL processes all audio data as floats, that does not mean that the audio on this platform is guaranteed to have 32 bit precision. Indeed, on Android, most audio is processed at 16 bit precision, and many audio files are recorded at this level of precision as well. Hence this filter assumes 16 bit precision by default.
This is relevant for the size of the filter to process the audio. Each additional bit doubles the size of the filter table used for the convolution. A 16 bit filter uses a very reasonable 512 entries per zero crossing. On the other hand, a 32 bit filter would require 131072 entries per zero crossing. Given the limitations of real-time resampling, it typically does not make much sense to assume more than 16 bits.
value | The bit precision for audio sent to this filter. |
|
overridevirtual |
Sets the read position to the elapsed time in seconds.
DELEGATED METHOD: This method delegates its call to the input node. It returns -1 if there is no input node or if this method is unsupported in that node
In some nodes like AudioInput
, this method is only supported if mark()
is set. In that case, the new time will be meaured from the mark. Other nodes like AudioPlayer
measure from the start of the stream.
time | The elapsed time in seconds. |
Reimplemented from cugl::audio::AudioNode.
void cugl::audio::AudioResampler::setInputRate | ( | Uint32 | value | ) |
Sets the input sample rate of this filter.
This value is distinct from AudioNode#getRate()
, which is the output sample rate of this node. Instead, this value is the sample rate of any audio node connected to this one via the attach
method.
Normally this value is assigned when a new audio node is attached. However, changing this value requires that the underlying read buffer be resized. Hence, by setting this value ahead of time (and making sure that all attached input nodes match this sample rate), you can improve the performance of this filter.
Assigning this value while there is still an attached audio node has undefined behavior.
value | The input sample rate of this filter. |
|
overridevirtual |
Sets the current frame position of this audio node.
DELEGATED METHOD: This method delegates its call to the input node. It returns -1 if there is no input node or if this method is unsupported in that node
In some nodes like AudioInput
, this method is only supported if mark()
is set. In that case, the position will be the number of frames since the mark. Other nodes like AudioPlayer
measure from the start of the stream.
position | the current frame position of this audio node. |
Reimplemented from cugl::audio::AudioNode.
|
overridevirtual |
Sets the typical read size of this node.
Some audio nodes need an internal buffer for operations like mixing or resampling. In that case, it helps to know the requested read
size ahead of time. The capacity is the minimal required read amount of the AudioEngine
and corresponds to AudioEngine#getReadSize
.
It is not actually necessary to set this size. However for nodes with internal buffer, setting this value can optimize performance.
This method is not synchronized because it is assumed that this value will never change while the audio engine in running. The average user should never call this method explicitly. You should always call AudioEngine#setReadSize
instead.
size | The typical read size of this node. |
Reimplemented from cugl::audio::AudioNode.
|
overridevirtual |
Sets the remaining time in seconds.
DELEGATED METHOD: This method delegates its call to the input node. It returns -1 if there is no input node or if this method is unsupported in that node
If this method is supported, then the node will be marked as completed after the given number of seconds. This may or may not actually move the read head. For example, in AudioPlayer
it will skip to the end of the sample. However, in AudioInput
it will simply time out after the given time.
time | The remaining time in seconds. |
Reimplemented from cugl::audio::AudioNode.
void cugl::audio::AudioResampler::setStopband | ( | float | value | ) |
Sets the stopband attentuation for this filter in dB
This value is described in more detail here:
https://tomroelandts.com/articles/how-to-create-a-configurable-filter-using-a-kaiser-window
By default, this value is 80.0.
value | The ripple factor for this filter in dB |
void cugl::audio::AudioResampler::setZeroCrossings | ( | Uint32 | value | ) |
Sets the number of zero-crossings of this filter.
The zero-crossings of a sinc filter are relevant because the determine the number of coefficients in a single filter convolution. For X zero-crossings, a single output sample requires 2*(X-1) input computations. Increasing this value can give some increased value in filter. However, the droppoff for sinc filters is large enough that eventually that large enough values will have no discernable effect.
The default number of zero crossing is 5, meaning that this filter roughly causes an 8x-10x decrease in performance when processing audio (when taking all the relevant overhead into account). This value is that one recommended by this tutorial website:
https://www.dsprelated.com/freebooks/pasp/Windowed_Sinc_Interpolation.html
value | The number of zero-crossings of this filter. |
|
overridevirtual |
Clears the current marked position.
DELEGATED METHOD: This method delegates its call to the input node. It returns false if there is no input node or if this method is unsupported in that node
If the method mark()
started recording to a buffer (such as with AudioInput
), this method will stop recording and release the buffer. When the mark is cleared, reset()
may or may not work depending upon the specific node.
Reimplemented from cugl::audio::AudioNode.