Skip to content

Commit

Permalink
Implementing TeensyJuce
Browse files Browse the repository at this point in the history
  • Loading branch information
cutlasses committed Oct 9, 2017
1 parent 791a0b1 commit 6eceb4d
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 66 deletions.
1 change: 1 addition & 0 deletions CompileSwitches.h
Expand Up @@ -3,4 +3,5 @@
//#define DEBUG_OUTPUT
//#define STANDALONE_AUDIO
#define PERF_CHECK
#define TARGET_TEENSY
//#define SET_TEMPO
20 changes: 14 additions & 6 deletions GlitchDelayEffect.h
@@ -1,6 +1,7 @@
#pragma once

#include <AudioStream.h>
#include "TeensyJuce.h"
#include "Util.h"

#define DELAY_BUFFER_SIZE_IN_BYTES 1024*240 // 240k

Expand Down Expand Up @@ -74,7 +75,7 @@ class DELAY_BUFFER
{
friend PLAY_HEAD;

byte m_buffer[DELAY_BUFFER_SIZE_IN_BYTES];
uint8_t m_buffer[DELAY_BUFFER_SIZE_IN_BYTES];
int m_buffer_size_in_samples;
int m_sample_size_in_bits;

Expand Down Expand Up @@ -115,11 +116,10 @@ class DELAY_BUFFER

////////////////////////////////////

class GLITCH_DELAY_EFFECT : public AudioStream
{
class GLITCH_DELAY_EFFECT : public TEENSY_AUDIO_STREAM_WRAPPER
{
static const int NUM_PLAY_HEADS = 3;

audio_block_t* m_input_queue_array[1];
DELAY_BUFFER m_delay_buffer;

PLAY_HEAD m_play_heads[NUM_PLAY_HEADS];
Expand All @@ -136,12 +136,20 @@ class GLITCH_DELAY_EFFECT : public AudioStream
int m_next_sample_size_in_bits;
bool m_next_loop_moving;
bool m_next_beat;

protected:

void process_audio_in_impl( int channel, const int16_t* sample_data, int num_samples ) override;
void process_audio_out_impl( int channel, int16_t* sample_data, int num_samples ) override;

public:

GLITCH_DELAY_EFFECT();

int num_input_channels() const override;
int num_output_channels() const override;

virtual void update();
void update() override;

void set_bit_depth( int sample_size_in_bits );
void set_speed( float speed );
Expand Down
140 changes: 80 additions & 60 deletions GlitchDelayEffect.ino
@@ -1,9 +1,12 @@
#ifdef TARGET_TEENSY
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#endif // TARGET_TEENSY

#include <string.h>
#include <Math.h>
#include "GlitchDelayEffect.h"
#include "CompileSwitches.h"
Expand All @@ -12,6 +15,11 @@
const float MIN_SPEED( 0.25f );
const float MAX_SPEED( 4.0f );

#ifdef TARGET_JUCE
const int AUDIO_BLOCK_SAMPLES( 512 ); // TODO need a value in JUCE, is it even constant?
const int AUDIO_SAMPLE_RATE( 44100 ); // TODO need a value in JUCE
#endif

const int FIXED_FADE_TIME_SAMPLES( (AUDIO_SAMPLE_RATE / 1000.0f ) * 4 ); // 4ms cross fade
const int MIN_LOOP_SIZE_IN_SAMPLES( (FIXED_FADE_TIME_SAMPLES * 2) + AUDIO_BLOCK_SAMPLES );
const int MAX_LOOP_SIZE_IN_SAMPLES( AUDIO_SAMPLE_RATE * 0.5f );
Expand Down Expand Up @@ -76,6 +84,11 @@ PLAY_HEAD::PLAY_HEAD( const DELAY_BUFFER& delay_buffer, float play_speed ) :
m_initial_loop_crossfade_complete(false)
{
set_loop_behind_write_head();

// set head immediately (don't want to crossfade initially)
m_current_play_head = m_destination_play_head;
m_initial_loop_crossfade_complete = true;
m_fade_samples_remaining = 0;
}

int PLAY_HEAD::current_position() const
Expand Down Expand Up @@ -389,21 +402,21 @@ void PLAY_HEAD::disable_loop()
#ifdef DEBUG_OUTPUT
void PLAY_HEAD::debug_output()
{
Serial.print("PLAY_HEAD current:");
Serial.print(m_current_play_head);
Serial.print(" destination:");
Serial.print(m_destination_play_head);
Serial.print(" loop start:");
Serial.print(m_loop_start);
Serial.print(" loop end:");
Serial.print(m_loop_end);
Serial.print(" fade samples:");
Serial.print(m_fade_samples_remaining);
DEBUG_TEXT("PLAY_HEAD current:");
DEBUG_TEXT(m_current_play_head);
DEBUG_TEXT(" destination:");
DEBUG_TEXT(m_destination_play_head);
DEBUG_TEXT(" loop start:");
DEBUG_TEXT(m_loop_start);
DEBUG_TEXT(" loop end:");
DEBUG_TEXT(m_loop_end);
DEBUG_TEXT(" fade samples:");
DEBUG_TEXT(m_fade_samples_remaining);
if( !m_initial_loop_crossfade_complete )
{
Serial.print(" INITIAL CF");
DEBUG_TEXT(" INITIAL CF");
}
Serial.print("\n");
DEBUG_TEXT("\n");
}
#endif

Expand Down Expand Up @@ -696,8 +709,6 @@ void DELAY_BUFFER::debug_output()
/////////////////////////////////////////////////////////////////////

GLITCH_DELAY_EFFECT::GLITCH_DELAY_EFFECT() :
AudioStream( 1, m_input_queue_array ),
m_input_queue_array(),
m_delay_buffer(),
m_play_heads( { PLAY_HEAD( m_delay_buffer, 0.5f ), PLAY_HEAD( m_delay_buffer, 1.0f ), PLAY_HEAD( m_delay_buffer, 2.0f ) } ),
m_speed_ratio(1.0f),
Expand All @@ -712,61 +723,70 @@ GLITCH_DELAY_EFFECT::GLITCH_DELAY_EFFECT() :

}

void GLITCH_DELAY_EFFECT::update()
{
m_delay_buffer.set_bit_depth( m_next_sample_size_in_bits );
m_loop_moving = m_next_loop_moving;
void GLITCH_DELAY_EFFECT::process_audio_in_impl( int channel, const int16_t* sample_data, int num_samples )
{
ASSERT_MSG( channel == 0, "Only mono input supported" );

m_delay_buffer.write_to_buffer( sample_data, num_samples );
}

for( int pi = 0; pi < NUM_PLAY_HEADS; ++pi )
{
if( m_loop_moving )
{
m_play_heads[pi].set_shift_speed( m_speed_ratio );
}
else
{
m_play_heads[pi].set_shift_speed( 0.0f );
m_play_heads[pi].set_jitter( m_speed_ratio );
}

m_play_heads[pi].set_loop_size( m_loop_size_ratio );

// check whether the write head is about to run over the read head, in which case cross fade read head to new position
if( m_play_heads[pi].position_inside_section( m_delay_buffer.write_head(), m_play_heads[pi].buffered_loop_start(), m_play_heads[pi].loop_end() ) )
{
m_play_heads[pi].set_loop_behind_write_head();
}
else if( m_next_beat && !m_play_heads[pi].crossfade_active() )
{
m_play_heads[pi].set_next_loop();
m_play_heads[pi].set_loop_behind_write_head();
}
}
m_next_beat = false;

void GLITCH_DELAY_EFFECT::process_audio_out_impl( int channel, int16_t* sample_data, int num_samples )
{

audio_block_t* read_block = receiveReadOnly();
ASSERT_MSG( !m_play_heads[channel].position_inside_next_read( m_delay_buffer.write_head(), num_samples ), "Non - reading over write buffer\n" ); // position after write head is OLD DATA
m_play_heads[channel].read_from_play_head( sample_data, num_samples );
}

if( read_block != nullptr )
{
m_delay_buffer.write_to_buffer( read_block->data, AUDIO_BLOCK_SAMPLES );
release( read_block );
int GLITCH_DELAY_EFFECT::num_input_channels() const
{
return 1;
}

int GLITCH_DELAY_EFFECT::num_output_channels() const
{
return 3;
}

void GLITCH_DELAY_EFFECT::update()
{
m_delay_buffer.set_bit_depth( m_next_sample_size_in_bits );
m_loop_moving = m_next_loop_moving;

for( int pi = 0; pi < NUM_PLAY_HEADS; ++pi )
{
audio_block_t* write_block = allocate();

if( write_block != nullptr )
{
ASSERT_MSG( !m_play_heads[pi].position_inside_next_read( m_delay_buffer.write_head(), AUDIO_BLOCK_SAMPLES ), "Non - reading over write buffer\n" ); // position after write head is OLD DATA
m_play_heads[pi].read_from_play_head( write_block->data, AUDIO_BLOCK_SAMPLES );
if( m_loop_moving )
{
m_play_heads[pi].set_shift_speed( m_speed_ratio );
}
else
{
m_play_heads[pi].set_shift_speed( 0.0f );
m_play_heads[pi].set_jitter( m_speed_ratio );
}

m_play_heads[pi].set_loop_size( m_loop_size_ratio );

// check whether the write head is about to run over the read head, in which case cross fade read head to new position
if( m_play_heads[pi].position_inside_section( m_delay_buffer.write_head(), m_play_heads[pi].buffered_loop_start(), m_play_heads[pi].loop_end() ) )
{
m_play_heads[pi].set_loop_behind_write_head();
}
else if( m_next_beat && !m_play_heads[pi].crossfade_active() )
{
m_play_heads[pi].set_next_loop();
m_play_heads[pi].set_loop_behind_write_head();
}
}
m_next_beat = false;

transmit( write_block, pi );
// read in on channel 0
process_audio_in( 0 );

release( write_block ); // note is this legal? may want to allocate a new block each time
}
// write out all the playheads TODO this uses more output than there are channels!!
for( int pi = 0; pi < NUM_PLAY_HEADS; ++pi )
{
process_audio_out( pi );
}
}
}

void GLITCH_DELAY_EFFECT::set_bit_depth( int sample_size_in_bits )
Expand Down
121 changes: 121 additions & 0 deletions TeensyJuce.h
@@ -0,0 +1,121 @@
//
// TeensyJuce.h
// TeensyJuce
//
// Created by Scott Pitkethly on 23/09/2017.
//
//

#ifndef TeensyJuce_h
#define TeensyJuce_h

#include <stdint.h>
#include "CompileSwitches.h"

#ifdef TARGET_TEENSY

#include <Audio.h>

class TEENSY_AUDIO_STREAM_WRAPPER : public AudioStream
{
audio_block_t* m_input_queue_array[1];

protected:

// these are the only functions that require bespoke Teensy code
bool process_audio_in( int channel )
{
audio_block_t* read_block = receiveReadOnly();

if( read_block != nullptr )
{
process_audio_in_impl( channel, read_block->data, AUDIO_BLOCK_SAMPLES );
release( read_block );

return true;
}

return false;
}

bool process_audio_out( int channel )
{
audio_block_t* write_block = allocate();

if( write_block != nullptr )
{
process_audio_out_impl( channel, write_block->data, AUDIO_BLOCK_SAMPLES );

transmit( write_block, channel );

release( write_block );

return true;
}

return false;
}

// add audio processing code in these 2 functions
virtual void process_audio_in_impl( int channel, const int16_t* sample_data, int num_samples ) = 0;
virtual void process_audio_out_impl( int channel, int16_t* sample_data, int num_samples ) = 0;

public:

TEENSY_AUDIO_STREAM_WRAPPER() :
AudioStream( 1, m_input_queue_array ),
m_input_queue_array()
{

}

virtual ~TEENSY_AUDIO_STREAM_WRAPPER() {;}

virtual int num_input_channels() const = 0;
virtual int num_output_channels() const = 0;
};

#endif // TARGET_TEENSY

#ifdef TARGET_JUCE

#include <vector>
#include "../JuceLibraryCode/JuceHeader.h"

class TEENSY_AUDIO_STREAM_WRAPPER
{
int m_num_input_channels;
int m_num_output_channels;

protected:

// store the 16-bit in/out buffers
typedef std::vector< int16_t > SAMPLE_BUFFER;
std::vector< SAMPLE_BUFFER > m_channel_buffers;

// these are the only functions that require bespoke JUCE code
bool process_audio_in( int channel );
bool process_audio_out( int channel );

// add audio processing code in these 2 functions
virtual void process_audio_in_impl( int channel, const int16_t* sample_data, int num_samples ) = 0;
virtual void process_audio_out_impl( int channel, int16_t* sample_data, int num_samples ) = 0;

public:

TEENSY_AUDIO_STREAM_WRAPPER();
virtual ~TEENSY_AUDIO_STREAM_WRAPPER();


void pre_process_audio( const AudioSampleBuffer& audio_in, int num_input_channels, int num_output_channels );
void post_process_audio( AudioSampleBuffer& audio_out );

virtual int num_input_channels() const = 0;
virtual int num_output_channels() const = 0;

virtual void update() = 0;
};

#endif // TARGET_JUCE

#endif /* TeensyJuce_h */

0 comments on commit 6eceb4d

Please sign in to comment.