Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 3 of 3

Thread: Playing two (or more) RAW files

  1. #1
    Junior Member
    Join Date
    Feb 2016
    Posts
    7

    Playing two (or more) RAW files

    I'm having a bit of a hard time playing more than one (raw) file at a time. Even using the Part_2_02_Mixers.ino tutorial code shows the same problem. Most of the time SD.open() fails or when it works, the sound is garbled. Playing one file at a time (one from each instance of the SD raw file player) works fine. Only when playing with two simultaneously this happens.

    My project is a simple Sound Machine (noise machine), which plays an audio loop indefinitely along with an alarm clock. It works in conjunction with an ESP8266, which handles the TCP/IP stack for getting the time, integration with the rest of my automation system, and also handling the (clock) LED array (more on that (OTA) later on a different thread -- in fact, the reason I'm doing the bulk on the work on the ESP8266 is the ease of uploading updates).

    For that I created a new derivation of AudioStream, using AudioPlaySdRaw as a starting point.

    The main question: Is it really possible to play more than one file at a time? The SD header file has a comment on the open method that says otherwise:

    "Note that currently only one file can be open at a time."

    For what's worth, here is my AudioPlaySdRawLoop class, which plays mono or stereo RAW files and optionally loops them.

    Header:

    Code:
    #ifndef play_raw_h_
    #define play_raw_h_
    
    #include "AudioStream.h"
    #include "SD.h"
    
    class AudioPlaySdRawLoop : public AudioStream
    {
    public:
    
        AudioPlaySdRawLoop();
    
        void        begin           ();
        bool        play            (const char *filename, bool stereo = false, bool loop = true);
        void        stop            ();
        bool        isPlaying       () { return _playing; }
        uint32_t    positionMillis  ();
        uint32_t    lengthMillis    ();
        void        update          ();
        void        endLoop         () { _loop = false; }
        //-- For debugging
        uint32_t    rewindTime      () { return _rewindTime; }
    
    private:
        uint32_t   _rewind         (uint32_t n);
        void       _transmit       (audio_block_t* blockL, audio_block_t* blockR = NULL);
    
    private:
        bool                _stereo;
        bool                _loop;
        File                _rawfile;
        uint32_t            _read_size;
        uint32_t            _file_size;
        volatile uint32_t   _file_offset;
        volatile bool       _playing;
        uint8_t             _buffer[(AUDIO_BLOCK_SAMPLES * 2) * 2];
        volatile uint32_t   _rewindTime;
    };
    
    #endif
    And the implementation:

    Code:
    #include "soundmachine.h"
    #include "play_raw.h"
    #include "spi_interrupt.h"
    
    #define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT)
    
    AudioPlaySdRawLoop::AudioPlaySdRawLoop()
        : AudioStream(0, NULL)
    {
        begin();
    }
    
    //-----------------------------------------------------------------------------
    void
    AudioPlaySdRawLoop::begin()
    {
        _stereo = false;
        _playing = false;
        _file_offset = 0;
        _file_size = 0;
        _read_size = ((AUDIO_BLOCK_SAMPLES * 2));
        _rewindTime = 0;
    }
    
    //-----------------------------------------------------------------------------
    bool
    AudioPlaySdRawLoop::play(const char *filename, bool stereo, bool loop)
    {
        stop();
        Serial.print("Playing ");
        Serial.print(filename);
        if(stereo)
            Serial.println(" Stereo");
        else
            Serial.println(" Mono");
        AudioStartUsingSPI();
        __disable_irq();
        _rawfile = SD.open(filename);
        __enable_irq();
        if (!_rawfile) {
            AudioStopUsingSPI();
            return false;
        }
        _stereo         = stereo;
        _loop           = loop;
        if(_stereo)
            _read_size  = ((AUDIO_BLOCK_SAMPLES * 2) * 2);
        else
            _read_size  = (AUDIO_BLOCK_SAMPLES * 2);
        _file_size      = _rawfile.size();
        _file_offset    = 0;
        _rewindTime     = 0;
        _playing        = true;
        return true;
    }
    
    //-----------------------------------------------------------------------------
    void
    AudioPlaySdRawLoop::stop()
    {
        __disable_irq();
        if (_playing) {
            _playing = false;
            _rawfile.close();
            __enable_irq();
            AudioStopUsingSPI();
        } else {
            __enable_irq();
        }
    }
    
    //-----------------------------------------------------------------------------
    void
    AudioPlaySdRawLoop::update()
    {
        if (!_playing)
            return;
        bool close = false;
        audio_block_t* blockL = NULL;
        audio_block_t* blockR = NULL;
        blockL = allocate();
        if (!blockL)
            return;
        if(_stereo) {
            blockR = allocate();
            if (!blockR) {
                release(blockL);
                return;
            }
        }
        if (_rawfile.available()) {
            uint32_t n = _rawfile.read(_buffer, _read_size);
            _file_offset += n;
            if (_file_offset >= _file_size || n < _read_size) {
                if(_loop)
                    _rewind(n);
                else {
                    //-- Zero out rest of buffer and be done with it.
                    memset(&_buffer[n], 0, _read_size - n);
                    close = true;
                }
            }
            _transmit(blockL, blockR);
        } else {
            if(_loop) {
                if(_rewind(0))
                    _transmit(blockL, blockR);
            } else
                close = true;
        }
        release(blockL);
        if(blockR)
            release(blockR);
        if(close) {
            _rawfile.close();
            AudioStopUsingSPI();
            _playing = false;
        }
    }
    
    //-----------------------------------------------------------------------------
    uint32_t
    AudioPlaySdRawLoop::positionMillis()
    {
        if(_stereo)
            return ((uint64_t)_file_offset * B2M) >> 32;
        else
            return ((uint64_t)_file_offset * (B2M >> 1)) >> 32;
    }
    
    //-----------------------------------------------------------------------------
    uint32_t
    AudioPlaySdRawLoop::lengthMillis()
    {
        if(_stereo)
            return ((uint64_t)_file_size * B2M) >> 32;
        else
            return ((uint64_t)_file_size * (B2M >> 1)) >> 32;
    }
    
    //-----------------------------------------------------------------------------
    uint32_t
    AudioPlaySdRawLoop::_rewind(uint32_t n)
    {
        uint32_t t = micros();
        _rawfile.seek(0);
        _file_offset = 0;
        n = _rawfile.read(&_buffer[n], _read_size - n);
        _file_offset += n;
        _rewindTime = micros() - t;
        return n;
    }
    
    //-----------------------------------------------------------------------------
    void
    AudioPlaySdRawLoop::_transmit(audio_block_t* blockL, audio_block_t* blockR)
    {
        if(_stereo) {
            uint8_t  lsb, msb;
            uint8_t* ptr = _buffer;
            for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
                lsb = *ptr++;
                msb = *ptr++;
                blockL->data[i] = (msb << 8) | lsb;
                lsb = *ptr++;
                msb = *ptr++;
                blockR->data[i] = (msb << 8) | lsb;
            }
        } else {
            memcpy(blockL->data, _buffer, (AUDIO_BLOCK_SAMPLES * 2));
        }
        transmit(blockL, 0);
        if(_stereo)
            transmit(blockR, 1);
        else
            transmit(blockL, 1);
    }

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,927
    Try editing hardware/teensy/avr/libraries/SD/SD_t3.h. Find any uncomment this:

    Code:
    // This Teensy 3.x optimized version is a work-in-progress.
    // Uncomment this line to use the Teensy version.  Otherwise,
    // the normal SD library is used.
    //#define USE_TEENSY3_OPTIMIZED_CODE
    Please let me know if this solves the problem? As you can probably guess, this optimization work is still somewhat experimental. Its main limitation is you can't write at all to the SD card with it enabled.

  3. #3
    Junior Member
    Join Date
    Feb 2016
    Posts
    7
    Paul,

    Thanks for the quick response. It turned out to be the MicroSD card. I had one exactly like the one you suggested (an 8G SanDisk Ultra) and given that the tested seek times were within reason (around 800us), it didn't occur to me to test a different one. I found another, newer one (16G), formatted it and made sure to copy the files one at a time from the shell this time, so they would be contiguous (copying from the Finder ends up with multiple simultaneous copies and the writes are all over the place). The seek/read block time now is down to around 480us.

    The code is usually running one loop continuously (the file is about a minute long) and the second instance plays "alarms" and "announcements" (adjusting the mixer gains accordingly in the process). It now all works as expected.

    Thanks again for such a cool dev board!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •