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

CorBee

Well-known member
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
 
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];
			 
			
		}
	}
 
It seems that using the term Time Expansion for this technique is not appreciated by one of the developers of Bat-detectors. We have therefore renamed this technique to Granular Stretch as that is what it does using the granular_effect library.

regards
Cor
 
Back
Top