0 Writing your Custom Input&Output Class
Phil Schatzmann edited this page 2024-02-23 15:35:54 +01:00

To implement your custom input or output class you can create a subclass of AudioStream and implement the following methods:

  • begin() - add your logic to start the processing
  • end() - add your logic to end the processing
  • setAudioInfo() - optional: handle changes to the sample rate, channels or bits per sample.

Output:

  • availableForWrite() - report the bytes that we can write
  • write() - writes an array of audio data.

Input:

  • available() - return the bytes that are available to read
  • readBytes() - read the bytes

Here is a simple example for an environment where the input and output is automatically handled via a callback method. We just write the data to a buffer and let the callback handle the data from the buffer.

#pragma once;

#include "AudioTools/AudioStreams.h"

namespace audio_tools {

#include "AudioTools.h"

/**
 * @brief Custom Stream 
 */
class CustomStream : public AudioStream {
public:
  CustomStream() { self = this; };

  /// Starts the processing
  bool begin() {
    TRACEI();
    // add your start logic: e.g. start callbacks
    return true;
  }

  /// Stops the processing and releases the memory
  void end() {
    TRACEI();
    // add your stop logic
  }

  void setAudioInfo(AudioInfo info) override {
    TRACED();
    AudioStream::setAudioInfo(info);
    // add your custom logic for changing the sample rate, channels or bits_per_sample
  }


  /// Limit amount of data to be written
  int availableForWrite() { return 1024; }

  /// Write audio data to buffer
  size_t write(const uint8_t *buffer, size_t size) override {
    // write to buffer or use your custom logic
    return write_buffer.writeArray(buffer, size);
  }

  /// amount of data available
  int available() { return read_buffer.available(); }

  /// Read from audio data to buffer
  size_t readBytes(uint8_t *buffer, size_t size) override {
    // write to buffer or use your custom logic
    size_t result = read_buffer.readArray(buffer, size);
    return result;
  }

protected:
  // output buffer
  NBuffer<uint8_t> write_buffer{1024, 3};
  NBuffer<uint8_t> read_buffer{1024, 3};
  static CustomStream* self;

  // sometimes your IO needs to be handled via a callback
  static void AudioCallback(uint8_t *in, uint8_t *out, size_t size) {
      // get data from write buffer to fill out
      self->write_buffer.readArray(out, size);
      // fill read_buffer from in
      self->read_buffer.writeArray(in, size);
  }

};

CustomStream *CustomStream::self = nullptr;

} // namespace audio_tools

After this you can test it e.g. with the following sketch:

#include "AudioTools.h"

AudioInfo info(44100, 2, 16);
SineWaveGenerator<int16_t> sineWave(32000);                // subclass of SoundGenerator with max amplitude of 32000
GeneratedSoundStream<int16_t> sound(sineWave);             // Stream generated from sine wave
CustomStream out; 
StreamCopy copier(out, sound);                             // copies sound into i2s

// Arduino Setup
void setup(void) {  
  // Open Serial 
  Serial.begin(115200);
  AudioLogger::instance().begin(Serial, AudioLogger::Info);

  out.begin(info);
  sound.begin(info);

  // Setup sine wave
  sineWave.begin(info, N_B4);
}

// Arduino loop - copy sound to out 
void loop() {
  copier.copy();
}