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

Thread: AudioPlaySerialflashRaw with pitch control, CPU usage issue

  1. #1
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    55

    AudioPlaySerialflashRaw with pitch control, CPU usage issue

    Hi there,
    I've adapted the ResamplingSdReader from @Moo to the serial flash raw player,
    it works but with 8 players running I'm getting very high values when measuring the CPU with AudioProcessorUsageMax(), using teensy 4.1 btw,
    any suggestion about how to reduce the CPU load?

    here's the code:
    play_serialflash_raw2.h
    Code:
    #ifndef play_serial_raw2_h_
    #define play_serial_raw2_h_
    
    #include "Arduino.h"
    #include <AudioStream.h>
    #include <SerialFlash.h>
    
    #include "stdint.h"
    #include "ResamplingFlashReader.h"
    
    class AudioPlaySerialflashRaw2 : public AudioStream
    {
    public:
    	AudioPlaySerialflashRaw2(void) : 
    		AudioStream(0, NULL),
    		rawReader()
    		{ 
    			begin(); 
    		}
    
    	void begin(void);
    	bool play(const char *filename);
    	void stop(void);
    	bool isPlaying(void) { return playing; }
    	uint32_t positionMillis(void);
    	uint32_t lengthMillis(void);
    	virtual void update(void);
    
    	void setPlaybackRate(float f) {
            rawReader.setPlaybackRate(f);
        }
    
        float playbackRate(float f) {
            return rawReader.playbackRate();
        }
    
        bool interpolationEnabled() {
            return rawReader.interpolationEnabled();
        }
    
        void setInterpolationEnabled(bool enableInterpolation) {
            rawReader.setInterpolationEnabled(enableInterpolation);
        }
    
    private:
    
    	uint32_t file_size;
    	volatile uint32_t file_offset;
    	volatile bool playing;
    
    	ResamplingFlashReader rawReader;
    };
    
    #endif
    play_serialflash_raw2.cpp
    Code:
    #include <Arduino.h>
    #include "play_serialflash_raw2.h"
    #include "spi_interrupt.h"
    
    
    void AudioPlaySerialflashRaw2::begin(void)
    {
    	playing = false;
    	file_offset = 0;
    	file_size = 0;
    }
    
    bool AudioPlaySerialflashRaw2::play(const char *filename)
    {
    	stop();
        playing = rawReader.play(filename);
        return playing;
    }
    
    void AudioPlaySerialflashRaw2::stop(void)
    {
    	rawReader.stop();
    }
    
    void AudioPlaySerialflashRaw2::update(void)
    {
    	unsigned int i, n;
    	audio_block_t *block;
    
    	// only update if we're playing
    	if (!playing) return;
    
    	// allocate the audio blocks to transmit
    	block = allocate();
    	if (block == NULL) return;
    
    	if (rawReader.available()) {
    		// we can read more data from the file...
    		n = rawReader.read(block->data, AUDIO_BLOCK_SAMPLES*2);
    		file_offset += n;
    		for (i=n/2; i < AUDIO_BLOCK_SAMPLES; i++) {
    			block->data[i] = 0;
    		}
    		transmit(block);
    	} else {
    		rawReader.close();
    		AudioStopUsingSPI();
    		playing = false;
    		//Serial.println("Finished playing sample");		//TODO
    	}
    	release(block);
    }
    
    #define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592
    
    uint32_t AudioPlaySerialflashRaw2::positionMillis(void)
    {
    	return ((uint64_t)file_offset * B2M) >> 32;
    }
    
    uint32_t AudioPlaySerialflashRaw2::lengthMillis(void)
    {
    	return ((uint64_t)file_size * B2M) >> 32;
    }
    resamplingFlashReader.h
    Code:
    #ifndef TEENSYAUDIOLIBRARY_RESAMPLINGFLASHREADER_H
    #define TEENSYAUDIOLIBRARY_RESAMPLINGFLASHREADER_H
    
    #include "stdint.h"
    #include <SD.h>
    #include <SerialFlash.h>
    #include <AudioStream.h>
    
    #include "../spi_interrupt.h"
    
    const unsigned int ResamplingFlashReader_NUM_BUFFERS = 4;
    
    class ResamplingFlashReader {
    public:
        ResamplingFlashReader() {
    
            for (uint i=0; i<ResamplingFlashReader_NUM_BUFFERS; i++ ) {
                _buffers[i] = NULL;
            }
        }
        void begin(void);
        bool play(const char *filename);
        void stop(void);
        bool isPlaying(void) { return _playing; }
    
        int read(void *buf, uint16_t nbyte);
        bool readNextValue(int16_t *value);
    
        void setPlaybackRate(double f) {
            _playbackRate = f;
        }
    
        float playbackRate() {
            return _playbackRate;
        }
    
        int available(void);
    
        void close(void);
    
        bool interpolationEnabled() {
            return _enable_interpolation;
        }
    
        void setInterpolationEnabled(bool enableInterpolation) {
            _enable_interpolation = enableInterpolation;
        }
    
    private:
        volatile bool _playing = false;
        volatile uint32_t _file_offset;
        volatile int32_t _last_read_offset = 0;
    
        bool _enable_interpolation = true;
        uint32_t _file_size;
        double _playbackRate = 1.0;
        double _remainder = 0.0;
    
        uint _bufferPosition = 0;
    
        int16_t *_buffers[AUDIO_BLOCK_SAMPLES];
        unsigned int _bufferLength[ResamplingFlashReader_NUM_BUFFERS];
        bool bufferIsAvailableForRead[ResamplingFlashReader_NUM_BUFFERS];
    
        volatile signed char _readBuffer = -1;
        volatile signed char _playBuffer = -1;
        signed char _numBuffers = 0;
        SerialFlashFile _file;
    
        void updateBuffers(void);
        bool isInRange(void);
    
    };
    
    
    #endif //TEENSYAUDIOLIBRARY_RESAMPLINGSDREADER_H
    resamplingFlashReader.cpp
    Code:
    #include "ResamplingFlashReader.h"
    
    int ResamplingFlashReader::read(void *buf, uint16_t nbyte) {
        if (!_playing) return 0;
    
        unsigned int count = 0;
        int16_t *index = (int16_t*)buf;
        for (int i=0; i< nbyte/2; i++) {
    
            if (readNextValue(index))
                count+=2;
            else
                return count;
    
            index++;
        }
        return count;
    }
    
    bool ResamplingFlashReader::readNextValue(int16_t *value) {
    
        if (_bufferLength[_playBuffer] == 0)
            return false;
    
        if (_playbackRate > 0 ) {
            //forward playback
    
            if (_numBuffers == 1 && _last_read_offset + _bufferPosition > _file_size)
                return false;
    
            //Serial.printf("readNextValue: %d/%d \n", _bufferPosition, _bufferLength[_playBuffer]);
    
            if (_bufferPosition >= _bufferLength[_playBuffer]) {
                bufferIsAvailableForRead[_playBuffer] = true;
                //Serial.printf("\ndiscard buffer: %d\n", _playBuffer);
    
                if (_numBuffers ==1 && _file_offset >= _file_size)
                    return false;
    
                _numBuffers--;
                //Serial.printf("num  buffers: %d\n", _numBuffers);
    
                if (_playBuffer < _numBuffers) {
                    _readBuffer = _playBuffer;
                    //Serial.printf("switch read buffer to: %d\n", _readBuffer);
                }
    
                _bufferPosition -= _bufferLength[_playBuffer];
    
                _playBuffer++;
                if (_playBuffer >= ResamplingFlashReader_NUM_BUFFERS) {
                    _playBuffer = 0;
                }
                //Serial.printf("switch play buffer to: %d\n", _playBuffer);
                updateBuffers();
            }
        } else if (_playbackRate < 0) {
    
            if (_numBuffers == 1)
                if (_file_offset + (AUDIO_BLOCK_SAMPLES * 2) + _bufferPosition < 0)
                    return false;
    
            // reverse playback
            if (_bufferPosition < 0) {
    
                if (_numBuffers == 0)
                    return false;
    
                bufferIsAvailableForRead[_playBuffer] = true;
                _readBuffer = _playBuffer;
    
                _numBuffers--;
    
                _playBuffer ++;
                if (_playBuffer >= ResamplingFlashReader_NUM_BUFFERS) {
                    _playBuffer = 0;
                }
    
                _bufferPosition += _bufferLength[_playBuffer];
                updateBuffers();
    
            } else if (_bufferPosition > (AUDIO_BLOCK_SAMPLES-1) * 2) {
                _bufferPosition = (AUDIO_BLOCK_SAMPLES-1) * 2;
            }
        }
        int16_t *_buffer = _buffers[_playBuffer];
        int16_t result = _buffer[_bufferPosition/2];
        //Serial.printf("r: %d,", result);
    
        if (_enable_interpolation) {
            if (_remainder != 0.0) {
                if (_playbackRate > 0.0) {
                    int16_t next;
                    if (_bufferPosition < _bufferLength[_playBuffer]-2) {
                        next =_buffer[(_bufferPosition/2)+1];
                    } else {
                        int nextPlayBuffer = _playBuffer == ResamplingFlashReader_NUM_BUFFERS - 1? 0 : _playBuffer+1;
    
                        if (_last_read_offset + _bufferPosition < _file_size-2) // second interpolation point is before the end of the file, don't int
                            next = _buffers[nextPlayBuffer][0];
                        else
                            next = result; // dont interpolate past the end of the file
                    }
                    int16_t interpolated = static_cast<int16_t>( (((1-_remainder) * 1000.0 * result) + (_remainder * 1000.0 * next)) / 1000.0 );
                    result = interpolated;
                } else if (_playbackRate < 0.0) {
                    int16_t prev;
                    if (_bufferPosition >= 2) {
                        prev = _buffer[(_bufferPosition/2)-1];
                    } else {
                        if (_file_offset > 2) {
                            int nextPlayBuffer = _playBuffer == ResamplingFlashReader_NUM_BUFFERS - 1 ? 0 : _playBuffer + 1;
                            int nextBufLength = _bufferLength[nextPlayBuffer] / 2;
                            prev = _buffers[nextPlayBuffer][nextBufLength - 1];
                        } else
                            prev = result;
                    }
                    int16_t interpolated = static_cast<int16_t>( (((1 + _remainder) * 1000.0 * result) + (-_remainder * 1000.0 * prev)) / 1000.0 );
                    result = interpolated;
                }
            }
        }
    
        _remainder += _playbackRate;
    
        auto delta = static_cast<signed int>(_remainder);
        _remainder -= static_cast<double>(delta);
    
        _bufferPosition += 2 * delta;
        *value = result;
        return true;
    }
    
    void ResamplingFlashReader::begin(void)
    {
        _playing = false;
        _file_offset = 0;
        _file_size = 0;
    }
    
    bool ResamplingFlashReader::play(const char *filename)
    {
        stop();
    
        AudioStartUsingSPI();
        __disable_irq();
        _file = SerialFlash.open(filename);
        __enable_irq();
    
        if (!_file) {
            AudioStopUsingSPI();
            return false;
        }
    
        __disable_irq();
        _file_size = _file.size();
        __enable_irq();
    
        if (_playbackRate < 0) {
            // reverse playback - forward _file_offset to last audio block in file
            _file_offset = _file_size - AUDIO_BLOCK_SAMPLES * 2;
        } else {
            // forward playabck - set _file_offset to first audio block in file
            _file_offset = 0;
        }
    
    
        for (char i=0; i<ResamplingFlashReader_NUM_BUFFERS; i++ ) {
            _buffers[i] = (int16_t*)malloc( AUDIO_BLOCK_SAMPLES * 2);
            bufferIsAvailableForRead[i] = true;
    
        }
        _readBuffer = 0;
        _numBuffers = 0;
        updateBuffers();
        _playBuffer = 0;
        if (_numBuffers == 0)
            return false;
    
        _playing = true;
    
        if (_playbackRate < 0) {
            _bufferPosition = _bufferLength[_playBuffer]-2;
        }
    
        return true;
    }
    
    void ResamplingFlashReader::stop()
    {
        __disable_irq();
        if (_playing) {
            _playing = false;
            __enable_irq();
            _file.close();
            AudioStopUsingSPI();
            //StopUsingSPI();
    
            for (uint i=0; i<ResamplingFlashReader_NUM_BUFFERS; i++ ) {
                if (_buffers[i] != NULL) {
                    free(_buffers[i]);
                    _buffers[i] = NULL;
                }
            }
        } else {
            __enable_irq();
        }
    }
    
    int ResamplingFlashReader::available(void) {
        return _file.available() / _playbackRate;
    }
    
    void ResamplingFlashReader::close(void) {
        if (_playing)
            stop();
        _file.close();
    }
    
    void ResamplingFlashReader::updateBuffers() {
    
        bool forward = (_playbackRate >= 0.0);
    
        while (_readBuffer >= 0
               && isInRange()
               && bufferIsAvailableForRead[_readBuffer]) {
    
            //Serial.printf("read buffer: %d (_numBuffers:%d) \n",_readBuffer, _numBuffers);
            int16_t *buffer = _buffers[_readBuffer];
            bufferIsAvailableForRead[_readBuffer] = false;
    
            _last_read_offset = _file_offset;
            uint16_t numberOfBytesToRead = AUDIO_BLOCK_SAMPLES * 2;
            if (!forward) {
                if (_file_offset < 0) {
                    // reverse playback, last buffer, only read partial remaining buffer that hasn't already played
                    numberOfBytesToRead = _file_offset + AUDIO_BLOCK_SAMPLES * 2;
                }
                _file.seek(_file_offset < 0? 0 : _file_offset);
            }
            //Serial.printf("\nreading %d bytes, starting at:%d (into readbuff %d) - _file_offset:%d\n", numberOfBytesToRead, _file.position(), _readBuffer, _file_offset);
            int numRead = _file.read(buffer, numberOfBytesToRead);
            _bufferLength[_readBuffer] = numRead;
            //Serial.printf("read %d bytes\n", numRead);
    
            if (_playbackRate < 0) {
                _file_offset -= numRead;
            } else
                _file_offset += numRead;
    
            if (numRead > 0) {
                _readBuffer ++;
                if (_readBuffer >= ResamplingFlashReader_NUM_BUFFERS)
                    _readBuffer = -1;
    
                _numBuffers++;
            } else {
                _readBuffer = -1;
                //Serial.printf("\n Not able to read anymore buffers for the moment...\n", numRead);
            }
        }
    }
    
    bool ResamplingFlashReader::isInRange() {
        if (_playbackRate >= 0.0) {
            return _file_offset < _file_size;
        } else {
            return _file_offset + AUDIO_BLOCK_SAMPLES * 2 > 0;
        }
    }

  2. #2
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    8,277
    Serial flash is - compared to other memory - incredible slow.
    I don't think there is a way to make it much faster.

  3. #3
    Senior Member
    Join Date
    Jul 2020
    Posts
    964
    And resampling is not trivial processing either - resample offline if possible.

  4. #4
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    55
    Hi Frank, thanks for the input but when using the original flash raw player the AudioProcessorUsageMax() was like 31% and with the resampled version is 102%
    also, that 31% isn't only about the 8 flash players, there are lots of filters, effects, mixers,
    indeed the CPU when using the AudioPlayMemory is 19%, so using using the raw flash player, instead the memory one, increases the whole thing about +12% and then using the resampled version gives +71%
    that is too much difference, isn't it?

  5. #5
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    55
    Quote Originally Posted by MarkT View Post
    And resampling is not trivial processing either - resample offline if possible.
    the idea was real time control over pitch, so offline process does not solve it

  6. #6
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    55
    I've also try to set the interpolation off, it does not affect too much the CPU usage...and sounds worse

    pitching down the samples has a small impact in the CPU, looks like the issue comes when pitching the samples up,
    which makes sense because much more samples has to be acquired from the flash chip

Posting Permissions

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