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

Thread: Pitch changing for audio playback application

  1. #1
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25

    Pitch changing for audio playback application

    Hi all! I'd like to make a simple sampler/player based on Teensy; anyone knows if it's possible to change the pitch when playing an audio sample?
    Thanks!

  2. #2
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    I've found at least one post about this topic. Apparently ... it seems an easy task stretching or shortening a waveform from samples, if only there is time for some simple interpolations:
    [ATTACH = CONFIG] 13275 [/ ATTACH]
    I'm sure I'm saying banality ... For sure things are more complex than I belive...
    Thanks
    Sandro
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	SAMPLER.jpg 
Views:	19 
Size:	46.1 KB 
ID:	13275  

  3. #3
    Senior Member
    Join Date
    Apr 2014
    Posts
    264
    Read the GUI synth section,some extracts copied below....

    begin(waveform);
    Configure the waveform type to create.

    begin(level, frequency, waveform);
    Output a waveform, and set the amplitude and frequency.

    frequency(freq);
    Change the frequency

    ...........

  4. #4
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    Hi Teenfor3,
    thank you for your answer; I need to use one "play" item (playFlashRaw) not a synth item, because I want to play audio samples. I did some search on internet, the topic of pitch shifting seems so difficult... Just one hour ago found this paper from Iowa State University:
    dec1712.sd.ece.iastate.edu/uploads/1/0/3/1/103197054/wr_10_1712.pdf
    It says that in Iowa State University, professors Geiger and Chen and their students are (were) working on a "pitch shifting effect for the Teensy"... Maybe something new is coming?
    Bye!
    Sandro

  5. #5
    Senior Member
    Join Date
    Apr 2014
    Posts
    264
    Your first post showed waveforms of what looks like 1 cycle sample, so play it at what ever pitch you want using the procs in my earlier post.
    Wavforms like this can be extracted from any wav file sample.

    You would need to explain what you are trying to do, size of samples...??? Play once through and stop or play repeated...??

    must the processing be done on teensy or can sample be pre-process on other software for teensy to play..???

    etc...etc.....

  6. #6
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    Hi Teenfor3, I posted that waveform with the purpose to show how I intend the pitch changing, but maybe was misleading, sorry, because my sample is a .wav file containing a speech, or a piece of a song, or some noise, it is not periodic wave.
    What I want to make is an expander with a sinlge sample inside: would store one .wav file in W25Q128FV serial flash memoery, attach a midi keyboard to the Teensy with a simple Midi-in interface, and play the .wav at different pitch/speed, depending on the note number; and eventually use the pitch bend control of the keyboard. All this would be easy, I think, if AudioPlaySerialFlashRaw had a "pitch" function..
    Thanks a lot
    Sandro

  7. #7
    Senior Member
    Join Date
    Apr 2014
    Posts
    264
    Don't know much about expanders but the W25Q128FV has option 8.2.6 Read Data 03h in the datasheet to read continuously from memory location and can use variable clock to control rate so maybe this could be used.......similar to "old" audio projects where a counter was used to count up and overflow to scan and read out the total memory space sequentially.

  8. #8
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    I don't doubt that the solution you propose is working good. With this "hardware" approach, I belive there would be advantages (semplicity of sw design) but also shortcoming, the worst is that we would give up the awsome poliphony allowed by AudioPlaySerialFlashRaw function using one single W25Q128FV chip ... Poliphony would be obtained by putting toghether many W25Q128FV, one for each voice.. I really prefer a "software" approach, as long as it is possible. I will annoy professors Geiger and Chen, hopeing they are close to a solution!!
    Thank you

  9. #9
    Senior Member
    Join Date
    Feb 2017
    Posts
    156
    Take a look here:
    https://forum.pjrc.com/threads/46793...Pitch-Shifting

    I also wrote some code using this technique. I'll see if I can dig it up.

  10. #10
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    Quote Originally Posted by gfvalvo View Post
    Take a look here:
    https://forum.pjrc.com/threads/46793...Pitch-Shifting

    I also wrote some code using this technique. I'll see if I can dig it up.
    Hi gfvalvo, I've read that post; a bit complex method as I can understand.. It seems (to me) more efficient trying to modify the standard "play" objects (AudioPlaySerialFlashRaw, in my case)... I'm not so smart to do this!!
    Thanks!

  11. #11
    Junior Member
    Join Date
    Feb 2015
    Location
    NY Metro Area
    Posts
    12

    Dynamically adjust pitch of WAV audio

    Quote Originally Posted by gfvalvo View Post
    Take a look here:
    https://forum.pjrc.com/threads/46793...Pitch-Shifting

    I also wrote some code using this technique. I'll see if I can dig it up.
    I would be very interested in a solution for this. Here's my use case: I am building a sub simulator for my 9 year old:

    https://developer.ibm.com/recipes/tu...ine-simulator/

    I took a rough approach to simulating the engines revving up/down: I edited a wav sample of an engine and did a pitch change in adobe audition. I end up with 10+ WAV files which I play out depending on the speed of the boat. However there are problems with this - if I use very short wav files I end up with a lot of audio clipping between replays of the sample files. I gues I could load the right audio file into memory and play from there. I am curious if I can do a pitch change of a single sample file and adjust the pitch as desired.

    Tiran

  12. #12
    Senior Member
    Join Date
    Apr 2014
    Posts
    264
    Tiran wrote " I am curious if I can do a pitch change of a single sample file and adjust the pitch as desired."

    Yes, Arbitrary Waveform, play sample at what ever frequency, duration and amplitude you want, see the teensy audio examples and the GUI design tool

  13. #13
    Junior Member
    Join Date
    Feb 2015
    Location
    NY Metro Area
    Posts
    12

    Pitch changing for audio playback

    Quote Originally Posted by Teenfor3 View Post
    Tiran wrote " I am curious if I can do a pitch change of a single sample file and adjust the pitch as desired."

    Yes, Arbitrary Waveform, play sample at what ever frequency, duration and amplitude you want, see the teensy audio examples and the GUI design tool

    Teenfor3 thank you for replying. I looked at the tutorial and most of the examples (pouring through them) and could not find an example where an audio wav file is played at a different pitch than its original form. Can you point it out to me or share a link or page number? I have seen people try this on the teensy audio library by suggesting it requires a custom filter library but have not seen working code examples. See this thread:

    https://forum.pjrc.com/threads/26767...-pitch-control

  14. #14
    Senior Member
    Join Date
    Apr 2014
    Posts
    264
    Probably not much simpler than this. This is a sample from Audacity export sample data as text from wav file and play in teensy using interval timer. the rate in the sketch is varied by counting down a timer but could be controlled by buttons or pot also the loudness or amplitude could also be controlled.
    Attached Files Attached Files

  15. #15
    Junior Member
    Join Date
    Feb 2015
    Location
    NY Metro Area
    Posts
    12
    Teenfor3:

    I love the simplicity of your approach. Unfortunately I am doing this using the Audio library and the Audio card. I need to output through the card and have 2-3 other audio track playing. For instance the engine might be revved up high because we are in pursuit and a torpedo sound fires while the captain announces "we are at full speed!". I need the pitch modulation to occur within the audio library for the PJRC audio card therefor...

  16. #16
    Junior Member
    Join Date
    Feb 2015
    Location
    NY Metro Area
    Posts
    12
    Quote Originally Posted by tirandagan View Post
    Teenfor3:

    I love the simplicity of your approach. Unfortunately I am doing this using the Audio library and the Audio card. I need to output through the card and have 2-3 other audio track playing. For instance the engine might be revved up high because we are in pursuit and a torpedo sound fires while the captain announces "we are at full speed!". I need the pitch modulation to occur within the audio library for the PJRC audio card therefor...
    Moving my thread to a different thread:
    https://forum.pjrc.com/threads/50712...033#post174033

  17. #17
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    Dears, I'm trying to add a function "pitch" to playFlashRaw; of course I will fail, but I'll try, with the goal of learning at least something about C++ and Teensy.

    So... first, if someone is so patient to explain me, in this (I belive) crucial part of code: I understand that we read AUDIO_BLOCK_SAMPLES*2 samples and then we delete half of them.. I'm wrong?

    Code:
    if (rawfile.available()) {
    		// we can read more data from the file...
    		n = rawfile.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);
    }

  18. #18
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    Hi all,
    I'm trying to modify play_serialflash_raw.cpp in order to add the pitch functionality; I've developed a simple demo with fixed pitch 50% (the sample is played at half speed) just to check if I'm on a good road. This is the code (my insertion/modification are evidenced with comments starting with // ****** ):

    Code:
    /* Audio Library for Teensy 3.X
     * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
     * Modified to use SerialFlash instead of SD library by Wyatt Olson <wyatt@digitalcave.ca>
     *
     * Development of this audio library was funded by PJRC.COM, LLC by sales of
     * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
     * open source software by purchasing Teensy or other PJRC products.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice, development funding notice, and this permission
     * notice shall be included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    #include <Arduino.h>
    #include "play_serialflash_raw.h"
    #include "spi_interrupt.h"
    
    int16_t ride;   // ****** number of time update is called
    int16_t box[128]; // ****** sound samples, local vector
    int16_t first_sample; // ****** each ride, the last sample read is stored here
    unsigned long t, t_0; // ***** computational time, must be within 2500 micros
    
    void AudioPlaySerialflashRaw::begin(void)
    {
    	playing = false;
    	file_offset = 0;
    	file_size = 0;
    }
    
    
    bool AudioPlaySerialflashRaw::play(const char *filename)
    {
    	stop();
    	ride=0; // ***** my code
    	AudioStartUsingSPI();
    	rawfile = SerialFlash.open(filename);
    	if (!rawfile) {
    		//Serial.println("unable to open file");
    		AudioStopUsingSPI();
    		return false;
    	}
    	file_size = rawfile.size();
    	file_offset = 0;
    	//Serial.println("able to open file");
    	playing = true;
    	return true;
    }
    
    void AudioPlaySerialflashRaw::stop(void)
    {
    	__disable_irq();
    	if (playing) {
    		playing = false;
    		__enable_irq();
    		rawfile.close();
    		AudioStopUsingSPI();
    	} else {
    		__enable_irq();
    	}
    }
    
    
    void AudioPlaySerialflashRaw::update(void)
    {
    	
    	int16_t b[128]; // ******* temporay vector used to store caluculated values
    	unsigned int h, k; // ***** my code
    	
    	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 (rawfile.available()) {
    		
            // ***** my code starts...
    	    t_0=micros(); // ***** checking the computational time, must be within 2500 micros
    		
    		if (ride==0) {
    		// we can read more data from the file...
    		n = rawfile.read(block->data, 65*2); // ***** only for the first ride 65 samples are needed
    		file_offset += n;
    			for (i=n/2; i < 65; i++) {
    			block->data[i] = 0;
    			}	
    			for (i=0;i<128;i++) {
    				h=(floor((float)i/2.0)); // ***** lower sample needed for calculation 
    				k=(ceil((float)i/2.0));	 // ***** upper sample needed for calculation
    				b[i]=(block->data[h]+block->data[k])/2; // **** interpolated value
    			}
                first_sample=block->data[64]; // ***** last sample read from file will be used in the next ride
    		}
    		
    		else {
    		// we can read more data from the file...
    		n = rawfile.read(block->data, 64*2); // ***** from the second ride 64 samples are needed (the first comes from the previous ride)
    		file_offset += n;
    		for (i=n/2; i < 64; i++) {
    			block->data[i] = 0;
    		}	
    			b[0]=first_sample;
    			for (i=1;i<128;i++) {
    				h=(floor((float)i/2.0)); // ***** lower sample needed for calculation 
    				k=(ceil((float)i/2.0));	 // ***** upper sample needed for calculation
    				b[i]=(block->data[h]+block->data[k])/2;	// **** interpolated value			
    			}
                first_sample=block->data[63]; // ***** last sample read from file will be used in the next ride
    		}
    		
    		for (i=0;i<128;i++) { // ***** preparing samples to be sent out
    			block->data[i]=b[i];
    		}	
    		
    			
    		t=micros()-t_0; // ***** checking the computational time, must be within 2500 micros
    		ride ++;		
    		// ***** my code ends 
    
    		transmit(block);
    	}
    	
    	else {
    		rawfile.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 AudioPlaySerialflashRaw::positionMillis(void)
    {
    	return ((uint64_t)file_offset * B2M) >> 32;
    }
    
    uint32_t AudioPlaySerialflashRaw::lengthMillis(void)
    {
    	return ((uint64_t)file_size * B2M) >> 32;
    }
    
    
    int AudioPlaySerialflashRaw::time_lapse(void) // // ***** reporting the computational time, must be within 2500 micros
    {
    	return t;
    }
    The result is half good, I can listen the sample with pitch 50% but ther are glitches.. maybe there are some gross mistakes? The computational time for calculating each new block of 128 samples is always <800 microseconds, this should be fine for the correct behave of the object... Can anybody have a look and help me to find what's wrong?
    Thanks a lot!
    Last edited by Sandro; 06-03-2018 at 02:47 PM.

  19. #19
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    I found one bug, a mistake with indexes... Now my demo behaves much better, this is the updated code:

    Code:
    /* Audio Library for Teensy 3.X
     * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
     * Modified to use SerialFlash instead of SD library by Wyatt Olson <wyatt@digitalcave.ca>
     *
     * Development of this audio library was funded by PJRC.COM, LLC by sales of
     * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
     * open source software by purchasing Teensy or other PJRC products.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice, development funding notice, and this permission
     * notice shall be included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    #include <Arduino.h>
    #include "play_serialflash_raw.h"
    #include "spi_interrupt.h"
    
    int16_t ride;   // ****** number of time update is called
    int16_t box[128]; // ****** sound samples, local vector
    int16_t first_sample; // ****** each ride, the last sample read is stored here
    unsigned long t, t_0; // ***** t is the computational time, must be within 2500 microseconds
    
    void AudioPlaySerialflashRaw::begin(void)
    {
    	playing = false;
    	file_offset = 0;
    	file_size = 0;
    }
    
    
    bool AudioPlaySerialflashRaw::play(const char *filename)
    {
    	stop();
    	ride=0; // ***** my code
    	AudioStartUsingSPI();
    	rawfile = SerialFlash.open(filename);
    	if (!rawfile) {
    		//Serial.println("unable to open file");
    		AudioStopUsingSPI();
    		return false;
    	}
    	file_size = rawfile.size();
    	file_offset = 0;
    	//Serial.println("able to open file");
    	playing = true;
    	return true;
    }
    
    void AudioPlaySerialflashRaw::stop(void)
    {
    	__disable_irq();
    	if (playing) {
    		playing = false;
    		__enable_irq();
    		rawfile.close();
    		AudioStopUsingSPI();
    	} else {
    		__enable_irq();
    	}
    }
    
    
    void AudioPlaySerialflashRaw::update(void)
    {
    	
    	int16_t b[128]; // ******* temporay vector used to store caluculated values
    	unsigned int h, k; // ******* two variables used to stor indexes
    	
    	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 (rawfile.available()) {
    		
            // ***** main code starts here
    	    t_0=micros(); // ***** checking the computational time, must be within 2500 micros
    		
    		if (ride==0) {
    		// we can read more data from the file...
    		n = rawfile.read(block->data, 65*2); // ***** only for the first ride 65 samples are needed
    		file_offset += n;
    			for (i=n/2; i < 65; i++) {
    			block->data[i] = 0;
    			}	
    			for (i=0;i<128;i++) {
    				h=(floor((float)i/2.0)); // ***** lower sample needed for calculation 
    				k=(ceil((float)i/2.0));	 // ***** upper sample needed for calculation
    				b[i]=(block->data[h]+block->data[k])/2; // **** interpolated value
    			}
                first_sample=block->data[64]; // ***** last sample read from file will be used in the next ride
    		}
    		
    		else {
    		// we can read more data from the file...
    		n = rawfile.read(block->data, 64*2); // ***** from the second ride 64 samples are needed (the first comes from the previous ride)
    		file_offset += n;
    		for (i=n/2; i < 64; i++) {
    			block->data[i] = 0;
    		}	
    			for (i=0;i<128;i++) {
    				h=(floor((float)i/2.0))-1; // ***** lower sample needed for calculation 
    				k=(ceil((float)i/2.0))-1;	 // ***** upper sample needed for calculation
    				b[i]=(((h<0) ? first_sample: block->data[h])+((k<0)? first_sample: block->data[k]))/2;	// **** interpolated value
    			}
                first_sample=block->data[63]; // ***** last sample read from file will be used in the next ride
    		}
    
    		for (i=0;i<128;i++) { // ***** preparing samples to be sent out
    			block->data[i]=b[i];
    		}	
    		
    		t=micros()-t_0; // ***** checking the computational time, must be within 2500 micros
    		ride ++;		
    		// ***** main code ends here
    
    		transmit(block);
    	}
    	
    	else {
    		rawfile.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 AudioPlaySerialflashRaw::positionMillis(void)
    {
    	return ((uint64_t)file_offset * B2M) >> 32;
    }
    
    uint32_t AudioPlaySerialflashRaw::lengthMillis(void)
    {
    	return ((uint64_t)file_size * B2M) >> 32;
    }
    
    
    int AudioPlaySerialflashRaw::time_lapse(void) // // ***** reporting the computational time t, must be within 2500 microseconds
    {
    	return t;
    }
    Some spike noise is still present.. but with much lower volume than before.
    Last edited by Sandro; 06-04-2018 at 06:23 AM.

  20. #20
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    Hi all, i found a gross mistake with 2 declarations! Now the demo is working fine, and the audio file stored in flash memory is played at 50% speed!
    I'm proud to share this demo I wrote (needless to say that I'm a beginner, and my code is obviously "naive"... please forgive me!!):

    Code:
    /* Audio Library for Teensy 3.X
     * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
     * Modified to use SerialFlash instead of SD library by Wyatt Olson <wyatt@digitalcave.ca>
     *
     * Development of this audio library was funded by PJRC.COM, LLC by sales of
     * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
     * open source software by purchasing Teensy or other PJRC products.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice, development funding notice, and this permission
     * notice shall be included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    #include <Arduino.h>
    #include "play_serialflash_raw.h"
    #include "spi_interrupt.h"
    
    int16_t ride;   // ****** number of time update() is called
    int16_t first_sample; // ****** each ride, the last sample read is stored here
    unsigned long t, t_0; // ***** t is the computational time, must be within 2500 microseconds
    int16_t bytes_read;   // ****** number of valid bytes read each update()
    
    
    void AudioPlaySerialflashRaw::begin(void)
    {
    	playing = false;
    	file_offset = 0;
    	file_size = 0;
    }
    
    
    bool AudioPlaySerialflashRaw::play(const char *filename)
    {
    	stop();
    	ride=0; // ***** my code
    	AudioStartUsingSPI();
    	rawfile = SerialFlash.open(filename);
    	if (!rawfile) {
    		//Serial.println("unable to open file");
    		AudioStopUsingSPI();
    		return false;
    	}
    	file_size = rawfile.size();
    	file_offset = 0;
    	//Serial.println("able to open file");
    	playing = true;
    	return true;
    }
    
    void AudioPlaySerialflashRaw::stop(void)
    {
    	__disable_irq();
    	if (playing) {
    		playing = false;
    		__enable_irq();
    		rawfile.close();
    		AudioStopUsingSPI();
    	} else {
    		__enable_irq();
    	}
    }
    
    
    void AudioPlaySerialflashRaw::update(void)
    {
    	
    	int16_t b[128]; // ******* temporay vector used to store caluculated values
    	int16_t h, k; // ******* two variables used to store indexes
    	
    	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 (rawfile.available()) {
    		
            // ***** main code starts here
    	    t_0=micros(); // ***** checking the computational time, must be within 2500 micros
    		
    		if (ride==0) {
    		// we can read more data from the file...
    		n = rawfile.read(block->data, 65*2); // ***** only for the first ride 65 samples are needed
    		bytes_read=n;
    		file_offset += n;
    			for (i=n/2; i < 65; i++) {
    			block->data[i] = 0;
    			}	
    			for (i=0;i<128;i++) {
    				h=(floor((float)i/2.0)); // ***** lower sample needed for calculation 
    				k=(ceil((float)i/2.0));	 // ***** upper sample needed for calculation
    				b[i]=(block->data[h]+block->data[k])/2; // **** interpolated value
    			}
                first_sample=block->data[64]; // ***** last sample read from file will be used in the next ride
    		}
    		
    		else {
    		// we can read more data from the file...
    		n = rawfile.read(block->data, 64*2); // ***** from the second ride 64 samples are needed (the first comes from the previous ride)
    		bytes_read=n;
    		file_offset += n;
    		for (i=n/2; i < 64; i++) {
    			block->data[i] = 0;
    		}	
    			for (i=0;i<128;i++) {
    				h=(floor((float)i/2.0))-1; // ***** lower sample needed for calculation 
    				k=(ceil((float)i/2.0))-1;	 // ***** upper sample needed for calculation
    				b[i]=(((h<0) ? first_sample: block->data[h])+((k<0)? first_sample: block->data[k]))/2;	// **** interpolated value
    			}
                first_sample=block->data[63]; // ***** last sample read from file will be used in the next ride
    		}
    
    		for (i=0;i<128;i++) { // ***** preparing samples to be sent out
    			block->data[i]=b[i];
    		}	
    		
    		t=micros()-t_0; // ***** checking the computational time, must be within 2500 micros
    		ride ++;		
    		// ***** main code ends here
    
    		transmit(block);
    	}
    	
    	else {
    		rawfile.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 AudioPlaySerialflashRaw::positionMillis(void)
    {
    	return ((uint64_t)file_offset * B2M) >> 32;
    }
    
    uint32_t AudioPlaySerialflashRaw::lengthMillis(void)
    {
    	return ((uint64_t)file_size * B2M) >> 32;
    }
    
    
    int AudioPlaySerialflashRaw::time_lapse(void) // // ***** reporting the computational time t, must be within 2500 microseconds
    {
    	return t;
    }
    
    int AudioPlaySerialflashRaw::bytes(void) // // ***** reporting the numeber of bytes read, just for some verification
    {
    	return bytes_read;
    }
    Now it's time to create a generalized algorithm for different pitch values!

  21. #21
    Member
    Join Date
    Nov 2016
    Location
    Rimini - Italy
    Posts
    25
    Hi all, i'm proud to say that I completed the job of introducing the pitch functionality in AudioPlaySerialflashRaw (play_serialflash_raw.cpp, play_serialflash_raw.h)!!

    The modified code is proposed in these pull requests:
    https://github.com/PaulStoffregen/Audio/pull/258
    https://github.com/PaulStoffregen/Audio/pull/257

    Code is running fine on my Teensy 3.2+AudioAdaptor and W25Q128FV flash memory chip.

  22. #22
    Hi everyone,

    Thanks Sandro for digging into this problem!
    Because I can't test with serial flash memory, I've translated your code for the play_sd_raw.cpp and .h files.
    I've also added an externalised method called setPitch to dynamically adjust the pitch variable outside the play() method, implemented like this :
    Code:
    bool AudioPlaySdRawPitch::setPitch(float pitch_value)
    {  
      pitch = pitch_value; // ***** set pitch value
      return true;
    }
    Good news, It work well with fixed pitch. But the problem I'm facing with now is when I try to adjust it the loop() with this method. My point is to create a "tape wooble varispeed" effect :

    Code:
    float tapeSpeed(float _speed, float _depth, float _lp) {
    
      if (_depth > 0.0) {
    
        static float ws   = 0.0;
        static uint8_t w8 = 128;
    
        w8 += random(3) - 1;
        ws += ( ( ( ( (float) w8 / 256.0 ) - 0.5 ) * _depth ) - ws ) * _lp;                                                            
    
        return ws + _speed;
    
      } else {
        
        return _speed;
    
      }
    }
    
    void loop() {
        myCustomPlayer.setPitch( tapeSpeed( 1.0, 0.1, 0.001 ) );
    }
    The values generated by the tapeSpeed() method = a float number that constantly drift a little up and a little down around 1.0.

    This result in a gradually distorded sound that ends-up by a reboot of the teensy itself. So bad...

    Your code looks interesting but I think it need a drastic optimisation in the way you do the thing. It's quite hard to debug for me right now. I'm not a very good C++ coder too but I can help you to bring this thing stable and implement the realtime changing pitch.

    Can you clarify your algorithm approach?
    The quality of the resampling is very good to my ears (at fixed speed). I've seen you dont use any windowing or FIR filtering approach during resampling, Thats why I'm curious about how you build the interpolation.

    Cheerz
    Q

Posting Permissions

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