Skip to content
mxmxmx edited this page May 22, 2016 · 79 revisions

... hacking

  • NB #1: certain bits of the pjrc audio API have been modified/customized (mainly, to reduce the latency when streaming from SD); notably the AudioPlaySdWav class will behave somewhat differently from the official/off-the-shelf version. for details, see here and below. in particular, rather than simply do play(const char *filename) (which closes, opens, parses, and starts streaming a .wav file) the modded play_sd_wav class splits up that functionality:

    • bool open_and_parse(const char *filename);
    • bool seek(uint32_t pos);
    • void close(void);
  • NB #2: because of the OLED (to drive it somewhat efficiently), the API has to include i2c_t3.h, not Wire.h

  • because the module is fairly similar/identical to the PJRC audio adapter board otherwise (in terms of pin out / pin usage), most of the teensy audio library examples can be easily adapted ... unlike the SGTL5000 part, the pcm5102a DAC has no inputs, so there's no audio inputs (via i2s), evidently.

  • extending the API with custom objects etc isn't difficult either. but using the API isn't a must, of course. here is a basic/alternative i2s library, for instance.

basic operation (of the eurotrash mk2 firmware).

  • each channel (L, R) is an instance of the struct audioChannel:

    struct audioChannel *audioChannels[CHANNELS];

  • each channel keeps two instances of the .wav respectively .raw file currently selected (file #: uint16_t file_wav, instance #: uint16_t swap). So that makes four files in total (for both channels):

    *wav[4] = {&wav1, &wav2, &wav3, &wav4};

    and ditto for .raw (= the stuff that goes onto the flash chip):

    *raw[4] = {&raw1, &raw2, &raw3, &raw4};

  • the instances are streamed alternatingly, ping-pong style. that allows for some basic cross-fading thing / smoother operation when re-triggering a file:

  • so, on each incoming clock, the .wav (or .raw) object is pointed back to the current file-start position (seek); then we fade in the one instance, and, if the sample is still playing, fade out the other instance. most of this happens in void _play(struct audioChannel* _channel).

  • NB: unlike the pjrc audio API, opening and parsing as well as closing the files are separate operations from actually triggering the files to play ( = bool AudioPlaySdWav::seek(uint32_t pos)). files are opened/closed only if/when a new/different file is selected (via the encoder/switch):

    bool AudioPlaySdWav::open_and_parse(const char *filename).

  • that's to avoid the delay involved in accessing the uSD card and opening the file; adding CV over file selection, while easy to do, thus comes with a penalty (= increased latency).

  • a new 'pre-fetch' buffer (512 bytes = 1 SD sector = 2 sample blocks) is acquired every time and only if the file start-position changes. the purpose of keeping the pre-fetch buffer also is to minimize the trigger-to-audio latency; conversely, modulating the start-position increases the latency, because the pre-fetch buffer needs to be updated to correspond to the new/altered start-position (theoretically, by 2.9ms = 1 sample block).

  • channel L is hard-panned left (mixL), channel R hard-panned right (mixR). if you'd prefer stereo output, that'll require a different mixing set-up (see here).