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

Thread: Batdetector: Time expansion (slow down replay) using granular-effect

  1. #1
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    107

    Batdetector: Time expansion (slow down replay) using granular-effect

    Hi,

    I am still working on the Teensy Batdetector (original idea by Frank DD4WH). I have managed to get most of the things of the original setup working so its time to enhance the detector with new functionality.

    One of the things I would like to be able to do is to listen to a live signal but with time-expansion (slow down replay). Time expansion for raw recorded signals was allready part of the setup of the initial batdetector and that works fine. The slowdown replay needs to be 10-20 times slower than the incoming samplerate ( 192 to 281 kbps ) to get the recorded signals into the normal audio frequencies. Time expansion calls reveal a lot more detail of the ultrasonic signal a bat "shouts" out.

    So my idea was to let the detector react to an incoming high-frequency signal by starting a recording to memory and than replay this slowed down. The capture should be instantaneous (or continuous in a ringbuffer) as bat-calls are in general short in duration (often milliseconds). I studied the audio library and it looks as if the granular-effect could be a good option for this kind of feature. The granular-effect seems to work like a buffer that gets filled as soon as the user calls "beginPitchShift". When the buffer is full it will start replaying at a different speed using "setSpeed" and after the replay record again and play again etc etc. Ive implemented this setup and it does seem to work (using artificial bat calls).

    But I wonder if this is the most optimal way to do this. Anybody with comments on this ?

    regards
    Cor

  2. #2
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    107
    Ive created routines inside effect_granular that work as planned. I can now listen to ultrasonic sounds that come in as small timeblocks (I am using a granular_memory_size of 30000 which is around 100ms of data at a samplerate of 281K). Next up will be to chain this to active detection of ultrasonic sounds and only use then use time-expansion.

    Ive tested an earlier version yesterday in my garden with Common Pipistrelle bats around and I did hear some calls time-expanded.

    I am moving the further development of these routines back to the batdetector thread. https://forum.pjrc.com/threads/38988-Bat-detector





    in effect_granular.h

    Code:
    void beginTimeExpansion(float grain_length) {
    		if (grain_length <= 0.0) return;
    		beginTimeExpansion_int(grain_length);
    	}
    in effect_granular.cpp
    Code:
    void AudioEffectGranular::beginTimeExpansion_int(int grain_samples)
    {
    	__disable_irq();
    	grain_mode = 3;
    	if (allow_len_change) {
    		if (grain_samples > max_sample_len) {
    		grain_samples = max_sample_len;
    	     } 
    		glitch_len = grain_samples;
    	}
    	sample_loaded = false;
    	write_en = false;
    	sample_req = true;
    	__enable_irq();
    }

    and inside routine void AudioEffectGranular::update(void)
    Code:
    else if (grain_mode == 3) {
    		//TIME EXPANSION
    		for (int k = 0; k < AUDIO_BLOCK_SAMPLES; k++) 
    		 { 
    			// wait with recording until a crossing zero point passes
    			if (sample_req) {
    				int16_t current_input = block->data[k];
    				if ((current_input < 0 && prev_input >= 0) ||
    				  (current_input >= 0 && prev_input < 0)) {
    					write_en = true; //start collecting a sample
    				} else {
    					prev_input = current_input;
    				}
    			}
    
            //active collecting of all incoming data
    			if (write_en) {
    				sample_req = false;
    				allow_len_change = true; 
    				// collect until glitch_len samples are available in the sample_bank
    				if (write_head >= glitch_len) {
    					write_head = 0;
    					accumulator= 0;
    					
    					sample_loaded = true; //sample_bank is full 
    					write_en = false;
    					allow_len_change = false;
    				}
    				sample_bank[write_head]= block->data[k];
    				write_head++; // next sample
    			   
    			}
    
                            //playback_rate is 65536 for sample_ratio 1 (no decimation)
    			//accumulator holds next position of the sample*65535 
    			// read_head is the position of the next sample to use in the sample_bank
    			// if sample_ration <1 the sample_value will be pushed into the bank several times (time expansion)
    
    			accumulator += playpack_rate; // sample 
    			read_head = (accumulator >> 16); //rightshift 16 == div by 65535 
    
    			if (read_head >= glitch_len) {
    				read_head = 0;
    				accumulator = 0;
                                    sample_req = true; //we can have a new sample coming in
    				sample_loaded = false;
    			}
             
    			block->data[k] = sample_bank[read_head];
    			 
    			
    		}
    	}

Posting Permissions

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