Questions about sample start and endpoint manipulation

Status
Not open for further replies.

Granulka

Member
Hi
I recently started building sampler inspierd by Volca Sample using teensy 4.0 and audio shield and i can't figure out if i can implement change in sample length or even reverse it.
 
Hi!

I have modified the Audio Library AudioPlaySdRaw code to accomplish something like this (I assume you want to play samples from the SD card. Also, if you want to play .wav file you should modify the AudioPlaySdWav code in a similar fashion, I haven't done that already).

I've added the setSpeed(speed) function to set the playback speed. Here speed is a float ranging from -4.0 to -0.25 or from 0.25 to 4.0 (negative numbers reverse the sample playback). To play at normal speed you can call setSpeed(1), to reverse call setSpeed(-1).

I've also added the play(filename, startpoint, endpoint) function, to set the startpoint and endpoint of the playback in milliseconds (if you set a negative speed, the startpoint should be greater than the endpoint).

I've only tested this on a teensy 4.1 with class 10 SD card and i2s output. Here's a sample video https://youtu.be/PSEETCdL-Rs

Note: this is not optimized at all. Looking at this thread https://forum.pjrc.com/threads/44377-SD-Audio-playback-speed?highlight=change+playback+speed I see that other members have already done some work for the change in playback speed, and I think they also added a resampling feature. My code does not implement resampling. Also, I'm not an expert at all in programming, so my code may be full of bugs, be aware of that.

I'm glad if you could try this out and let me know how it works.

Here's the code
modified play_sd_raw.h:
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 *
 * 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.
 */

#ifndef play_sd_raw_h
#define play_sd_raw_h

#include "Arduino.h"
#include "AudioStream.h"
#include "SD.h"

class AudioPlaySdRaw : public AudioStream
{
public:
	AudioPlaySdRaw(void) : AudioStream(0, NULL) { begin(); setSpeed(1.0f);}
	void begin(void);
	void setSpeed(float speed)
	{
		if (speed < 0)
		{
			playback_direction = -1;
			speed = speed * -1;
		}
		else
		{
			playback_direction = 1;
		}
		if(speed < 0.25)
			speed = 0.25f;
		else if(speed > 4)
			speed = 4.0f;
		playback_speed = speed;
		playback_speed_inv = 1.0f / playback_speed; 
	}
	bool play(const char *filename);
	bool play(const char *filename, float startPoint, float endPoint);
	void stop(void);
	bool isPlaying(void) { return playing; }
	uint32_t positionMillis(void);
	uint32_t lengthMillis(void);
	virtual void update(void);
private:
	int32_t millisToByte(float time_ms);
	File rawfile;
	int32_t file_size;
	volatile int32_t file_offset;
	volatile bool playing;
	float playback_speed;
	float playback_speed_inv;
	int16_t playback_direction;
	int16_t calc_buffer[AUDIO_BLOCK_SAMPLES * 5];
	int32_t buffer_index;
	int32_t endpoint_sample;
};

#endif

modified play_sd_raw.cpp:
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 *
 * 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_sd_raw.h"
#include "spi_interrupt.h"


void AudioPlaySdRaw::begin(void)
{
	playing = false;
	file_offset = 0;
	file_size = 0;
	buffer_index = 0;
	endpoint_sample = 0;
}


bool AudioPlaySdRaw::play(const char *filename)
{
	stop();
#if defined(HAS_KINETIS_SDHC)
	if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStartUsingSPI();
#else
	AudioStartUsingSPI();
#endif
	__disable_irq();
	rawfile = SD.open(filename);
	__enable_irq();
	if (!rawfile) {
		//Serial.println("unable to open file");
		#if defined(HAS_KINETIS_SDHC)
			if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
		#else
			AudioStopUsingSPI();
		#endif
		return false;
	}
	file_size = rawfile.size();
	if(playback_direction > 0)
	{
		file_offset = 0;
		endpoint_sample = file_size;
	}
	else
	{
		file_offset = file_size;
		endpoint_sample = 0;
	}
	//Serial.println("able to open file");
	playing = true;
	return true;
}

bool AudioPlaySdRaw::play(const char *filename, float startPoint, float endPoint)
{
	stop();
#if defined(HAS_KINETIS_SDHC)
	if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStartUsingSPI();
#else
	AudioStartUsingSPI();
#endif
	__disable_irq();
	rawfile = SD.open(filename);
	__enable_irq();
	if (!rawfile) {
		//Serial.println("unable to open file");
		#if defined(HAS_KINETIS_SDHC)
			if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
		#else
			AudioStopUsingSPI();
		#endif
		return false;
	}
	file_size = rawfile.size();

	if(playback_direction > 0)
	{
		// endPoint should be greater than startPoint
		if(startPoint <= 0)
			file_offset = 0;
		else
		{
			file_offset = millisToByte(startPoint);
			if(file_offset >= file_size)
			{
				playing = false;
				return false;
			}
		}
		endpoint_sample = millisToByte(endPoint);
		if(endpoint_sample >= file_size)
		{
			endpoint_sample = file_size;
		}
		if(endpoint_sample <= file_offset)
		{
			playing = false;
		  return false;
		}
		rawfile.seek(file_offset);
	}
	else
	{
		//reverse startPoint should be greater than endPoint
		if(startPoint <= 0)
		{
			playing = false;
			return false;
		}
		else
		{
			file_offset = millisToByte(startPoint);
			if(file_offset >= file_size)
				file_offset = file_size;
		}
		endpoint_sample = millisToByte(endPoint);
		if(endpoint_sample < 0)
		{
			endpoint_sample = 0;
		}
		if(endpoint_sample >= file_offset)
		{
			playing = false;
		  return false;
		}
	}
	//Serial.println("able to open file");
	playing = true;
	return true;
}

void AudioPlaySdRaw::stop(void)
{
	__disable_irq();
	if (playing) {
		playing = false;
		__enable_irq();
		rawfile.close();
		#if defined(HAS_KINETIS_SDHC)
			if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
		#else
			AudioStopUsingSPI();
		#endif
	} else {
		__enable_irq();
	}
}


void AudioPlaySdRaw::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(playback_direction > 0)
	{
		if (rawfile.available() && file_offset < endpoint_sample)
		{
			//we must have at least AUDIO_BLOCK_SAMPLES samples in the buffer
			while(buffer_index < AUDIO_BLOCK_SAMPLES)
			{
				if (rawfile.available() && file_offset < endpoint_sample)
				{
					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;
					}
				}
				else
				{
					for (i = 0; i < AUDIO_BLOCK_SAMPLES; i++)
					{
						block->data[i] = 0;
					}
				}

				//here we calculate the buffer
				unsigned int samples_to_write = int((float)AUDIO_BLOCK_SAMPLES * playback_speed_inv);
				for(i = 0; i < samples_to_write; i++)
				{
					calc_buffer[i + buffer_index] = block->data[int((float)i * playback_speed)];
				}
				buffer_index += samples_to_write;
			}

			//read from buffer the samples to transmit and transmit
			for(i = 0; i < AUDIO_BLOCK_SAMPLES; i++)
			{
				block->data[i] = calc_buffer[i];
			}
			transmit(block);

			//shift the buffer and update buffer_index
			for(i = 0; i < (AUDIO_BLOCK_SAMPLES * 4); i++)
			{
				calc_buffer[i] = calc_buffer[i + AUDIO_BLOCK_SAMPLES];
			}
			buffer_index -= AUDIO_BLOCK_SAMPLES;
		}
		else 
		{
			rawfile.close();
			#if defined(HAS_KINETIS_SDHC)
				if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
			#else
				AudioStopUsingSPI();
			#endif
			playing = false;
		}
	}
	else
	{
		// reverse direction
		if (file_offset >= endpoint_sample)
		{
			//we must have at least AUDIO_BLOCK_SAMPLES samples in the buffer
			while(buffer_index < AUDIO_BLOCK_SAMPLES)
			{
				//we have to be sure not to read data before file zero-position
				int seek_point = file_offset - (AUDIO_BLOCK_SAMPLES * 2);
				if(file_offset >= endpoint_sample)
				{
					i = 0;
					while(seek_point < 0)
					{
						block->data[i] = 0;
						i++;
						seek_point++;
						seek_point++;
					}
					rawfile.seek(seek_point);
					if(i == 0)
						n = rawfile.read(block->data, AUDIO_BLOCK_SAMPLES*2);
					else
					{
						int16_t temp_buffer[AUDIO_BLOCK_SAMPLES];
						n = rawfile.read(temp_buffer, AUDIO_BLOCK_SAMPLES*2);
						for(int j = i; j < AUDIO_BLOCK_SAMPLES; j++)
							block->data[j] = temp_buffer[j - i];
					}
					file_offset -= AUDIO_BLOCK_SAMPLES * 2;
				}
				else
				{
					for(i = 0; i < AUDIO_BLOCK_SAMPLES; i++)
					{
						block->data[i] = 0;
					}
				}

				//here we calculate the buffer, also reversing the samples
				unsigned int samples_to_write = int((float)AUDIO_BLOCK_SAMPLES * playback_speed_inv);
				for(i = 0; i < samples_to_write; i++)
				{
					calc_buffer[i + buffer_index] = block->data[AUDIO_BLOCK_SAMPLES - int((float)i * playback_speed) - 1];
				}
				buffer_index += samples_to_write;
			}

			//read from buffer the samples to transmit and transmit
			for(i = 0; i < AUDIO_BLOCK_SAMPLES; i++)
			{
				block->data[i] = calc_buffer[i];
			}
			transmit(block);

			//shift the buffer and update buffer_index
			for(i = 0; i < (AUDIO_BLOCK_SAMPLES * 4); i++)
			{
				calc_buffer[i] = calc_buffer[i + AUDIO_BLOCK_SAMPLES];
			}
			buffer_index -= AUDIO_BLOCK_SAMPLES;
		}
		else 
		{
			rawfile.close();
			#if defined(HAS_KINETIS_SDHC)
				if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
			#else
				AudioStopUsingSPI();
			#endif
			playing = false;
		}
	}
	release(block);
}

#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592

uint32_t AudioPlaySdRaw::positionMillis(void)
{
	return ((uint64_t)file_offset * B2M) >> 32;
}

uint32_t AudioPlaySdRaw::lengthMillis(void)
{
	return ((uint64_t)file_size * B2M) >> 32;
}

int32_t AudioPlaySdRaw::millisToByte(float time_ms)
{
	return (AUDIO_SAMPLE_RATE_EXACT * time_ms * 0.002f);
}
 
Here's a little example for functions usage

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioPlaySdRaw           playSdRaw1;     //xy=266,236
AudioFilterStateVariable filter1;        //xy=440,245.55556106567383
AudioOutputI2S           i2s1;           //xy=659.1110992431641,234.0000057220459
AudioConnection          patchCord1(playSdRaw1, 0, filter1, 0);
AudioConnection          patchCord2(filter1, 0, i2s1, 0);
AudioConnection          patchCord3(filter1, 0, i2s1, 1);
// GUItool: end automatically generated code


//teensy 4.1 has builtin soundcard
//if using audio shield soundcard, look into the examples for the correct setup
#define SDCARD_CS_PIN    BUILTIN_SDCARD

void setup()
{
  AudioMemory(250);
  Serial.begin(9600);

  //add a filter in case playing at higher speed 
  filter1.frequency(15000);
  
  if (!(SD.begin(SDCARD_CS_PIN))) 
  {
    Serial.println("Unable to access SD card!");
  }
  else
  {
    Serial.println("Play from 1s to 5s at 1x speed");
    playSdRaw1.setSpeed(1.0);
    playSdRaw1.play("my_file.RAW", 1000.0, 5000.0);
    while (playSdRaw1.isPlaying())
    {
      //wait
    }

    delay(1000);
    
    Serial.println("Play from 5s to 1s at 1x speed");
    playSdRaw1.setSpeed(-1.0);
    playSdRaw1.play("my_file.RAW", 5000.0, 1000.0);
    while (playSdRaw1.isPlaying())
    {
      //wait
    }

    delay(1000);

    Serial.println("Play from 1s to 5s at 2x speed");
    playSdRaw1.setSpeed(2.0);
    playSdRaw1.play("my_file.RAW", 1000.0, 5000.0);
    while (playSdRaw1.isPlaying())
    {
      //wait
    }
  }
}

void loop()
{
  delay(1000);
}
 
Thanks for reply i will definitley check this out after i fix problem of Teensy freezeing after reciving too much MIDI messages (i think).

So after about 30 sec of using sequencer audio playback just stops and whole uC freezes and i can't pinpoint any culprit of the problem.

Im using Beatstep Pro as a main midi controller and i know that is outputs lot of additional messages while running its sequencer (lot of sync transmission).

Also I run into audio glitches if i use to much pads at the same time on beatstep.

Code:
//LIBS
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <MIDI.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>

//CLASSES
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 column and 2 rows
Encoder myEnc(4, 5);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);


// GUItool: begin automatically generated code
AudioPlaySdWav           playSdWav1;    
AudioPlaySdWav           playSdWav4;    
AudioPlaySdWav           playSdWav3;     
AudioPlaySdWav           playSdWav2;     
AudioPlaySdWav           playSdWav9;     
AudioPlaySdWav           playSdWav8;    
AudioPlaySdWav           playSdWav15;
AudioPlaySdWav           playSdWav6;     
AudioPlaySdWav           playSdWav5;     
AudioPlaySdWav           playSdWav7;    
AudioPlaySdWav           playSdWav14;   
AudioPlaySdWav           playSdWav16;    
AudioPlaySdWav           playSdWav11;    
AudioPlaySdWav           playSdWav12;    
AudioPlaySdWav           playSdWav13;    
AudioPlaySdWav           playSdWav10;    

AudioMixer4              mixer7; 
AudioMixer4              mixer3; 
AudioMixer4              mixer4; 
AudioMixer4              mixer6; 
AudioMixer4              mixer8;   
AudioMixer4              mixer2; 
AudioMixer4              mixer9; 
AudioMixer4              mixer1;         
AudioMixer4              mixer5; 
AudioMixer4              mixer10; 

AudioOutputI2S           i2s1;           

AudioConnection          patchCord1(playSdWav1, 0, mixer4, 0);
AudioConnection          patchCord2(playSdWav1, 1, mixer6, 0);
AudioConnection          patchCord3(playSdWav4, 0, mixer4, 3);
AudioConnection          patchCord4(playSdWav4, 1, mixer6, 3);
AudioConnection          patchCord5(playSdWav3, 0, mixer4, 2);
AudioConnection          patchCord6(playSdWav3, 1, mixer6, 2);
AudioConnection          patchCord7(playSdWav2, 0, mixer4, 1);
AudioConnection          patchCord8(playSdWav2, 1, mixer6, 1);
AudioConnection          patchCord9(playSdWav9, 0, mixer2, 0);
AudioConnection          patchCord10(playSdWav9, 1, mixer8, 0);
AudioConnection          patchCord11(playSdWav8, 0, mixer3, 3);
AudioConnection          patchCord12(playSdWav8, 1, mixer7, 3);
AudioConnection          patchCord13(playSdWav15, 0, mixer1, 2);
AudioConnection          patchCord14(playSdWav15, 1, mixer9, 2);
AudioConnection          patchCord15(playSdWav6, 0, mixer3, 1);
AudioConnection          patchCord16(playSdWav6, 1, mixer7, 1);
AudioConnection          patchCord17(playSdWav5, 0, mixer3, 0);
AudioConnection          patchCord18(playSdWav5, 1, mixer7, 0);
AudioConnection          patchCord19(playSdWav7, 0, mixer3, 2);
AudioConnection          patchCord20(playSdWav7, 1, mixer7, 2);
AudioConnection          patchCord21(playSdWav14, 0, mixer1, 1);
AudioConnection          patchCord22(playSdWav14, 1, mixer9, 1);
AudioConnection          patchCord23(playSdWav16, 0, mixer1, 3);
AudioConnection          patchCord24(playSdWav16, 1, mixer9, 3);
AudioConnection          patchCord25(playSdWav11, 0, mixer2, 2);
AudioConnection          patchCord26(playSdWav11, 1, mixer8, 2);
AudioConnection          patchCord27(playSdWav12, 0, mixer2, 3);
AudioConnection          patchCord28(playSdWav12, 1, mixer8, 3);
AudioConnection          patchCord29(playSdWav13, 0, mixer1, 0);
AudioConnection          patchCord30(playSdWav13, 1, mixer9, 0);
AudioConnection          patchCord31(playSdWav10, 0, mixer2, 1);
AudioConnection          patchCord32(playSdWav10, 1, mixer8, 1);
AudioConnection          patchCord33(mixer7, 0, mixer10, 1);
AudioConnection          patchCord34(mixer3, 0, mixer5, 1);
AudioConnection          patchCord35(mixer4, 0, mixer5, 0);
AudioConnection          patchCord36(mixer6, 0, mixer10, 0);
AudioConnection          patchCord37(mixer8, 0, mixer10, 2);
AudioConnection          patchCord38(mixer2, 0, mixer5, 2);
AudioConnection          patchCord39(mixer9, 0, mixer10, 3);
AudioConnection          patchCord40(mixer1, 0, mixer5, 3);
AudioConnection          patchCord41(mixer5, 0, i2s1, 0);
AudioConnection          patchCord42(mixer10, 0, i2s1, 1);

AudioControlSGTL5000     sgtl5000_1;
// GUItool: end automatically generated code

//PINS
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   13
#define e1r 14
#define e1g 15
#define e1sw 3

//VARS
char filechar[13] = "BANK02/00.WAV";
char *MENU [] = {"Channel","Mode","Bank","Save & Exit"};
int globalsettings[]={10,1,2,0};//channel,mode,bank,smth
float slotvol[]={0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,};
byte slot[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
long oldPosition = 999;
long newPosition = 666;
byte i=0;
bool e1press=false;

//**************************************************************************************
void setup() 
{
  pinMode(e1r,OUTPUT);
  pinMode(e1g,OUTPUT);
  pinMode(e1sw,INPUT_PULLUP);

  
  lcd.init(); // initialize the lcd
  lcd.backlight();

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Press and hold");
  lcd.setCursor(0, 1);
  lcd.print("for settings");
  //delay(500);
  
  
    
  Serial.begin(57600);
  MIDI.begin(MIDI_CHANNEL_OMNI);
 
  AudioMemory(32);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);

  if (!(SD.begin(SDCARD_CS_PIN))) 
  {
    while (1) 
    {
      lcd.clear();
      lcd.print("SD FAIL");
      delay(500);
      lcd.clear();
      delay(500);     
    }
  }
  if (digitalRead(e1sw)==LOW)  
   { 
   settings();
   }
  
}
//**************************************************************************************
void settings()
{
  while(true)
  {
  newPosition = myEnc.read()/2;
  if (newPosition != oldPosition) 
  {
    oldPosition = newPosition;
    i = oldPosition;
    
    if(i>=5)
    {
      i=0;
      myEnc.write(0);
    }
    if(i==0||i==1)
    {
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print(MENU[0]);
      lcd.setCursor(14, 0);
      lcd.print(globalsettings[0]);
    
      lcd.setCursor(1, 1);
      lcd.print(MENU[1]);        
    
      switch(globalsettings[1])
      {
        case 0:
          lcd.setCursor(13, 1);
          lcd.print("USB");
        break;
      
        case 1:
          lcd.setCursor(12, 0);
          lcd.print("MIDI");
        break;
      }
    }
    
    else if(i==2||i==3)
    {
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print(MENU[2]);

      lcd.setCursor(1, 1);
      lcd.print(MENU[3]);
    }
        
    lcd.setCursor(0, i%2 );
    lcd.write(0x7E);//arrow char
    }
    if (digitalRead(e1sw)==LOW)
    {
          
    }
}
}
//**************************************************************************************
void loop()
{
  if(MIDI.read())
 {
  byte type = MIDI.getType();//type
  if(type==0x90)
 {

   switch(globalsettings[1])
   {
   case 0:  
     usbmidi();
   break;

   case 1:

      dinmidi(); 
   break;
   }
 }
 }
 else
 {
  newPosition = myEnc.read()/2;
  
  if (newPosition != oldPosition) 
  {
    oldPosition = newPosition;
    i = oldPosition;
    if(i>=16)
    {
      i=0;
      myEnc.write(0);
    }
    if(i<0)
    {
      i=15;
      myEnc.write(i*2);
    }
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Slot ");
    lcd.write(0x7E);
    lcd.print(i);
    lcd.print(" ");
    lcd.print("Spl ");
    lcd.print(slot[i]);    
   }
   
   if (digitalRead(e1sw)==LOW&&e1press==false)  
   { 
      e1press=true;
      digitalWrite(e1r , HIGH);
      lcd.setCursor(0, 5);
      lcd.print(" ");
      slotMenuSelect(i);
      lcd.setCursor(1, 0);
      lcd.print("                ");
      myEnc.write(i*2);   
      delay(200);   
   }
   else
   {   
       e1press=false;
       //digitalWrite(e1r , LOW);
       //digitalWrite(e1g , LOW);
   }

 }
}
//**************************************************************************************
void slotMenuSelect(byte slot_number)
{
  byte n;
  lcd.setCursor(0, 1);
  lcd.write(0x7E);
  lcd.print("Sample  ");  
  myEnc.write(0);
  
  while(digitalRead(e1sw)==LOW&&e1press==true)
  {
    
    delay(200);
    //wait for depress
  }  
  e1press=false;
  while(true)
  { 

    
    newPosition = myEnc.read()/2;
    if (newPosition != oldPosition) 
    {
      oldPosition = newPosition;
      n = oldPosition;
      if(n>=6)
      {
        n=0;
        myEnc.write(0);
      }
      if(n<0)
      {
        n=5;
        myEnc.write(5*2);
      }
      
      switch(n)
      {
      case 0:
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("Sample  ");
      break;
      
      case 1:
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("Vol     ");
      break; 
      
      case 2:
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("Start  ");
      break; 
      
      case 3:
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("End     ");
      break;
      
      case 4:
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("Rev     ");
      break;

      case 5:
        lcd.setCursor(0, 1);
        lcd.write(0x7E);//arrow
        lcd.print("Back    ");
      break;
      }
    }
    
    if (digitalRead(e1sw)==LOW&&e1press==false)  
    { 
      e1press=true;
           
      switch(n)
      {
      case 0:
        slotSampleSelect(slot_number);
        
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("Sample         ");
        
        delay(200);
      break;
      
      case 1:
        //slotSampleSelect();
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("WIP_Back      ");        
      break; 
      
      case 2:
        //slotSampleSelect();
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("WIP_Back      ");
        break; 
      
      case 3:
        //slotSampleSelect();
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("WIP_Back      ");
      break;
      
      case 4:
        //slotSampleSelect();
        lcd.setCursor(0, 1);
        lcd.write(0x7E);
        lcd.print("WIP_Back      ");
        break;

      case 5:        
        return 0;
      break;
      }
    }
    if (digitalRead(e1sw)==HIGH)
    {
      e1press=false;
    }
  }
}
//**************************************************************************************
void slotSampleSelect(const byte slot_number)
{
  byte n=0;
  myEnc.write(0);
  lcd.setCursor(0, 1);
  lcd.print("Sample ");
  lcd.write(0x7E);
  lcd.setCursor(8, 1);
  lcd.print(slot[slot_number]);

  while(digitalRead(e1sw)==LOW&&e1press==true)
  {
    
    //wait for depress
    delay(200);
  }
    e1press=false;
  while (true)
  {
   if(myEnc.read()<0)
   {
    myEnc.write(99*2);
   }
   
  newPosition = myEnc.read()/2;  
  
  if (newPosition != oldPosition) 
  {
    
    oldPosition = newPosition;
    n = oldPosition;
 Serial.print(n);
    if(n>99)
    {
      n=0;
      myEnc.write(0);
    }
        
    lcd.setCursor(8, 1);
    lcd.print(n);
    lcd.print(" ");

    playPlayer(n,slot_number);
    
    
  }
  if(digitalRead(e1sw)==LOW&&e1press==false)
    {
      slot[slot_number]=n;      
      lcd.setCursor(12,0);
      lcd.print(n);
      lcd.print(" ");
      return 0;
    }
  }  
}
//**************************************************************************************
void usbmidi()
{  
  /*
    Serial.println(usbMIDI.getChannel());    
    Serial.println(usbMIDI.getType(),HEX);       
    Serial.print(usbMIDI.getData1(),HEX);
    Serial.print(" HEX| ");
    Serial.print(usbMIDI.getData1(),DEC);
    Serial.print(" DEC| ");
    Serial.println();    
    Serial.print(usbMIDI.getData2(),HEX);
    Serial.print(" HEX| ");
    Serial.print(usbMIDI.getData2(),DEC);
    Serial.print(" DEC| ");
    Serial.println();
    */
    
    byte type = usbMIDI.getType();
    byte data1 = usbMIDI.getData1();
    byte data2 = usbMIDI.getData2();
    
        

    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x24)
    {
      playPlayer(slot[0],0);      
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x25)
    {
       playPlayer(slot[1],1);    
     }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x26)
    {
      //playFile(slot[2],2);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x27)
    {
      //playFile(slot[3],3);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x28)
    {
      //playFile(slot[4],4);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x29)
    {
      //playFile(slot[5],5);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x2A)
    {
      //playFile(slot[6],6);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x2B)
    {
      //playFile(slot[7],7);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x2C)
    {
      //playFile(slot[8],8);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x2D)
    {
      //playFile(slot[9],9);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x2E)
    {
      //playFile(slot[10],10);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x2F)
    {
      //playFile(slot[11],11);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x30)
    {
      //playFile(slot[12],12);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x31)
    {
      //playFile(slot[13],13);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x32)
    {
      //playFile(slot[14],14);
    }
    if(usbMIDI.getType()==0x90&&usbMIDI.getData1()==0x33)
    {
     // playFile(slot[15],15);
    }
}        

//**************************************************************************************
void playPlayer(const byte sample_num, const byte player_num)
{
  byte x = (sample_num/10)%10;
  byte y = (sample_num)%10;
  filechar[7]='0'+x;
  filechar[8]='0'+y;
  //Serial.println(filechar);
  switch(player_num)
   {
    case 0:
    //playWav1.stop();
    playSdWav1.play(filechar);
    //Serial.println(filechar);
    break;

    case 1:
    //playWav2.stop();
    playSdWav2.play(filechar);
    break;

    case 2:
    //playWav3.stop();
    playSdWav3.play(filechar);
    break;
    
    case 3:
    //playWav4.stop();
    playSdWav4.play(filechar);
    break;
    
    case 4:
    //playWav5.stop();
    playSdWav5.play(filechar);
    break;
    
    case 5:
    //playWav6.stop();
    playSdWav6.play(filechar);
    break;
    
    case 6:
    //playWav7.stop();
    playSdWav7.play(filechar);
    break;
    
    case 7:
    //playWav8.stop();
    playSdWav8.play(filechar);
    break;
    
    case 8:
    //playWav9.stop();
    playSdWav9.play(filechar);
    break;
    
    case 9:
    //playWav10.stop();
    playSdWav10.play(filechar);
    break;
    
    case 10:
    //playWav11.stop();
    playSdWav11.play(filechar);
    break;
    
    case 11:
    //playWav12.stop();
    playSdWav12.play(filechar);
    break;
    
    case 12:
    //playWav13.stop();
    playSdWav13.play(filechar);
    break;
    
    case 13:
    //playWav14.stop();
    playSdWav14.play(filechar);
    break;
    
    case 14:
    //playWav15.stop();
    playSdWav15.play(filechar);
    break;
    
    case 15:
    //playWav16.stop();
    playSdWav16.play(filechar);
    break;
   }
}
//**************************************************************************************
void dinmidi()
{  
  /*
    Serial.println(MIDI.getChannel());    
    Serial.println(MIDI.getType(),HEX);       
    Serial.print(MIDI.getData1(),HEX);
    Serial.print(" HEX| ");
    Serial.print(MIDI.getData1(),DEC);
    Serial.print(" DEC| ");
    Serial.println();    
    Serial.print(MIDI.getData2(),HEX);
    Serial.print(" HEX| ");
    Serial.print(MIDI.getData2(),DEC);
    Serial.print(" DEC| ");
    Serial.println();
    */
    
    
    
    
    
    
   // if(type==0x90)
    //{ 
      byte data1 = MIDI.getData1();//note
      byte data2 = MIDI.getData2();//velo
      
      switch(data1)
      {
      case 0x24:
        playPlayer(slot[0],0);
       break;

      case 0x25:
        playPlayer(slot[1],1);
      break;

      case 0x26:
        playPlayer(slot[2],2);
      break;

       case 0x27:
        playPlayer(slot[3],3);
      break;

      case 0x28:
        playPlayer(slot[4],4);
      break;

      case 0x29:
        playPlayer(slot[5],5);
      break;

      case 0x2A:
        playPlayer(slot[6],6);
      break;

      case 0x2B:
        playPlayer(slot[7],7);
      break;

      case 0x2C:
        playPlayer(slot[8],8);
      break;

      case 0x2D:
        playPlayer(slot[9],9);
      break;

      case 0x2E:
        playPlayer(slot[10],10);
      break;

      case 0x2F:
        playPlayer(slot[11],11);
      break;

      case 0x30:
        playPlayer(slot[12],12);
      break;

      case 0x31:
       playPlayer(slot[13],13);
      break;

      case 0x32:
        playPlayer(slot[14],14);
      break;

      case 0x33:
        playPlayer(slot[15],15);
      break;
      }    
   // }
    
}

The code is also very much WIP so it's quite ugly.

If you like the whole concept here is render of a case for it from Fusion 360
VolcaBox.jpg
For now it's a crude prototype but it follows exact dimentions of Volcas so you can use any ready made stands for it.
 
Status
Not open for further replies.
Back
Top