How to use the vs1053b pitch/tempo shifter plugin?

Writing software for systems that use VLSI Solution's devices as slave codecs to a host microcontroller.
Post Reply
jtlechem
User
Posts: 15
Joined: Tue 2022-05-10 23:54

How to use the vs1053b pitch/tempo shifter plugin?

Post by jtlechem »

I've figured out how to load the .plg file (I think), but then how do you change the tempo? I'm at a total blank for how to code the tempo shift.
Does anyone have any example code for this?


- I'm using Adafruit's vs1053b breakout board with SD card (https://www.adafruit.com/product/1381)
- I'm controlling it with an Arduino Uno, using the Adafruit VS1053 Library (https://github.com/adafruit/Adafruit_VS1053_Library)
- I have connected everything according to the Adafruit tutorial (https://learn.adafruit.com/adafruit-vs1 ... l?view=all)
- And I'm trying to use the vs1053 patch for tempo shifting (https://www.vlsi.fi/en/support/software ... tches.html). Using the plugin file called "vs1053-patches-pitch.plg".


First, to check that I loaded the .plg file properly, here's how I did it. I simply used the loadPlugin() function from the Adafruit VS1053 Library. The .plg file in on the SD card on the vs1053 breakout board, since that's where the loadPlugin() function looks for the plugin.

Code: Select all

// load plugin/patch file
  if (! musicPlayer.loadPlugin("pitch.plg")) {
    Serial.println("Couldn't load plugin!");
    while (1);
  }
It doesn't throw any errors, so I assume it worked?

This is Adafruit's loadPlugin() function:

Code: Select all

uint16_t Adafruit_VS1053::loadPlugin(char *plugname) {

  File plugin = SD.open(plugname);
  if (!plugin) {
    Serial.println("Couldn't open the plugin file");
    Serial.println(plugin);
    return 0xFFFF;
  }

  if ((plugin.read() != 'P') || (plugin.read() != '&') ||
      (plugin.read() != 'H'))
    return 0xFFFF;

  uint16_t type;

  // Serial.print("Patch size: "); Serial.println(patchsize);
  while ((type = plugin.read()) >= 0) {
    uint16_t offsets[] = {0x8000UL, 0x0, 0x4000UL};
    uint16_t addr, len;

    // Serial.print("type: "); Serial.println(type, HEX);

    if (type >= 4) {
      plugin.close();
      return 0xFFFF;
    }

    len = plugin.read();
    len <<= 8;
    len |= plugin.read() & ~1;
    addr = plugin.read();
    addr <<= 8;
    addr |= plugin.read();
    // Serial.print("len: "); Serial.print(len);
    // Serial.print(" addr: $"); Serial.println(addr, HEX);

    if (type == 3) {
      // execute rec!
      plugin.close();
      return addr;
    }

    // set address
    sciWrite(VS1053_REG_WRAMADDR, addr + offsets[type]);
    // write data
    do {
      uint16_t data;
      data = plugin.read();
      data <<= 8;
      data |= plugin.read();
      sciWrite(VS1053_REG_WRAM, data);
    } while ((len -= 2));
  }

  plugin.close();
  return 0xFFFF;
}

Then to code the tempo shift, I tried following the document that comes with the vs1053b patches zip file (https://www.vlsi.fi/fileadmin/software/ ... atches.pdf, page 19)
It says that to enable to the pitch tempo shifter, write the pitch/tempo value to AICTRL0 and then write 0x51 to AIADDR. (except this other document https://www.vlsi.fi/fileadmin/software/ ... hifter.pdf says to write 0x50 ? neither one worked)

So I try writing this in my Arduino script, after loading the plugin, using Adafruit's sciWrite() function:

Code: Select all

musicPlayer.sciWrite(VS1053_SCI_AICTRL0, 16384);
musicPlayer.sciWrite(VS1053_SCI_AIADDR, 0x50);
But the script won't read passed the line writing to AIADDR. It freezes there and the program won't continue. I've tried adding delays in case it either line needs more time, but that didn't help. I also tried different pitch/tempo values and 0x51 instead of 0x50.

Can't figure out if there's more code that needs to accompany this, or any connections I'm missing..
Does anyone have any example code for using the pitch/tempo shifter?




Here is more of my arduino script as a whole:

Code: Select all

#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>

#define CLK 13       // SPI Clock, shared with SD card
#define MISO 12      // Input data, from VS1053/SD card
#define MOSI 11      // Output data, to VS1053/SD card

#define BREAKOUT_RESET  9      // VS1053 reset pin (output)
#define BREAKOUT_CS     10     // VS1053 chip select pin (output)
#define BREAKOUT_DCS    8      // VS1053 Data/command select pin (output)
#define SHIELD_RESET  -1      // VS1053 reset pin (unused)
#define SHIELD_CS     7      // VS1053 chip select pin (output)
#define SHIELD_DCS    6      // VS1053 Data/command select pin (output)
#define CARDCS A0     // Card chip select pin
#define DREQ 3  

Adafruit_VS1053_FilePlayer musicPlayer =
  // create breakout-example object
  Adafruit_VS1053_FilePlayer(BREAKOUT_RESET, BREAKOUT_CS, BREAKOUT_DCS, DREQ, CARDCS);
  
 // define variables used...
 // ...
 
 
 void setup() {
  Serial.begin(9600);
  Serial.println("Adafruit VS1053 Simple Test");

  if (! musicPlayer.begin()) { // initialise the music player
    Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
    while (1);
  }
  Serial.println(F("VS1053 found"));

  if (!SD.begin(CARDCS)) {
    Serial.println(F("SD failed, or not present"));
    while (1);  // don't do anything more
  }
  Serial.println(F("SD found"));
  
  //set volume
  musicPlayer.setVolume(20, 20);
  
  // load plugin/patch file							//<------------- LOAD PLUGIN
  if (! musicPlayer.loadPlugin("pitch.plg")) {		
    Serial.println("Couldn't load plugin!");
    while (1);
  }
  
   // enable pitch/tempo shifter					//<------------- ENABLE PITCH/TEMPO SHIFTER
   // write pitch/tempo value to AICTRL0, write 0x50 to AIADDR (https://www.vlsi.fi/fileadmin/software/VS10XX/vs1053b-patches.pdf, pg 19)
   musicplayer.sciWrite(VS1053_SCI_AICTRL0, -16384);
   musicPlayer.sciWrite(VS1053_SCI_AIADDR, 0x50);
  
  // enable interrupt
  musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);  // DREQ int
  // start playing file
  musicPlayer.startPlayingFile("/track001.wav");
  
} // end void setup()
 
 
void loop () {
    // change tempo with a potentiometer
    potTempo = analogRead(tempoPin);
    ratio = mapfloat(potTempo, 0, 1023, 0.7, 1.5);  // Mapping the pot values to ratio values
    tempo = round(-16384 / ratio);
    musicPlayer.sciWrite(VS1053_SCI_AICTRL0, tempo);		//<------------- WRITE TEMPO TO SCI AICTRL0
     
} // end void loop


// map function that does floats instead of just integers
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
User avatar
pasi
VLSI Staff
Posts: 2006
Joined: Thu 2010-07-15 16:04

Re: How to use the vs1053b pitch/tempo shifter plugin?

Post by pasi »

jtlechem wrote: Mon 2022-06-13 19:07 This is Adafruit's loadPlugin() function:

Code: Select all

uint16_t Adafruit_VS1053::loadPlugin(char *plugname) {
....
  if ((plugin.read() != 'P') || (plugin.read() != '&') ||
      (plugin.read() != 'H'))
    return 0xFFFF;
It seems that loadPlugin is actually loading SPI boot images (which start with "P&H"), not .plg files. loadPlugin returns 0xffff, if you call it with a .plg file. Your error check seems to be reversed. Your check for zero as error ("!loadPlugin()" is the same as "loadPlugin()==0"), but loadPlugin returns non-zero for error.

The .plg file has pseudo-code for how to load the data in the plugin. Have you checked that?
jtlechem wrote: Mon 2022-06-13 19:07It says that to enable to the pitch tempo shifter, write the pitch/tempo value to AICTRL0 and then write 0x51 to AIADDR. (except this other document https://www.vlsi.fi/fileadmin/software/ ... hifter.pdf says to write 0x50 ? neither one worked)
Both will probably crash the decoder, because the plugin isn't loaded... But otherwise, when 0x50 is written to SCI_AIDDR, it will start the patches package. This write is already in the plugin file.

When started, the patches package clears the internal AIADDR variable, and a new write to AIADDR can enable other functions. In this case you can enable the pitch shifter by writing 0x51 to AIADDR, preferably after setting AICTRL0 to something sensible, e.g. to 0x4000.
Visit https://www.facebook.com/VLSISolution VLSI Solution on Facebook
jtlechem
User
Posts: 15
Joined: Tue 2022-05-10 23:54

Re: How to use the vs1053b pitch/tempo shifter plugin?

Post by jtlechem »

That is good to know about the loadPlugin() function, with my inexperience I didn't know what the function was doing or looking for. I let the library authors know about the error check being reversed.

The Adafruit VS1053 Library has another function called applyPatch() which I now see looks just like the pseudo code in the .plg file. I tried using applyPatch() like this:
  • Define the plugin array called "patch" in the beginning of the script (copied the whole array from vs1053-patches-pitch.plg).
    In void setup(), use applyPatch() function.
    Enable the tempo shifter by writing -0x4000 to AICTRL0 then writing 0x51 to AIADDR, according to pg.19 of https://www.vlsi.fi/fileadmin/software/ ... hifter.pdf.
    Start playing audio file.
    Continue to void loop()

Code: Select all

#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>
// ... #define pins...

// plugin array, copied from vs1053b-patches-pitch.plg
#ifndef SKIP_PLUGIN_VARNAME
const unsigned short patch[] = { /* Compressed plugin */
#endif
  0x0007, 0x0001, /*copy 1*/
  0x8050,
  0x0006, 0x0020, /*copy 32*/
  0x2a00, 0xc000, 0x2a02, 0x75c0, 0x3e12, 0xb817, 0x3e14, 0xf812,
  
  // ...... huge array, skip to the end
  
  0x000a, 0x0001, /*copy 1*/
  0x0050,
#define PLUGIN_SIZE 5964
#ifndef SKIP_PLUGIN_VARNAME
};
#endif

void setup() {
   musicPlayer.begin();
   musicPlayer.setVolume(20, 20);
   // ...
   musicPlayer.applyPatch(patch, PLUGIN_SIZE);		// <--- takes 5-6 seconds on this line
   // enable the tempo shifter
   musicPlayer.sciWrite(VS1053_SCI_AICTRL0, -0x4000);
   musicPlayer.sciWrite(VS1053_SCI_AIADDR, 0x51);		// <---- still crashes after this line
   
   musicPlayer.useinterrupt(VS1053_FILEPLAYER_PIN_INT);  // DREQ int
   musicPlayer.startPlayingFile("/track001.wav");
}

void loop()  {
   // code to change tempo
}
Am I missing any steps to get the tempo shifter to work? At this point, I'm just getting the error that there's not enough dynamic memory on the Arduino. So I'm currently trying the modify applyPatch() to get the plugin array from the SD card instead of in the script.
But it still runs part of the script somehow, and I can see that it takes 5 or 6 seconds on the applyPatch() function. Then it still crashes on the line that writes 0x51 to AIADDR.


Here is the library's applyPatch() function:

Code: Select all

void Adafruit_VS1053::applyPatch(const uint16_t *patch, uint16_t patchsize) {
  uint16_t i = 0;

  // Serial.print("Patch size: "); Serial.println(patchsize);
  while (i < patchsize) {
    uint16_t addr, n, val;

    addr = pgm_read_word(patch++);
    n = pgm_read_word(patch++);
    i += 2;

    // Serial.println(addr, HEX);
    if (n & 0x8000U) { // RLE run, replicate n samples
      n &= 0x7FFF;
      val = pgm_read_word(patch++);
      i++;
      while (n--) {
        sciWrite(addr, val);
      }
    } else { // Copy run, copy n samples
      while (n--) {
        val = pgm_read_word(patch++);
        i++;
        sciWrite(addr, val);
      }
    }
  }
}
User avatar
pasi
VLSI Staff
Posts: 2006
Joined: Thu 2010-07-15 16:04

Re: How to use the vs1053b pitch/tempo shifter plugin?

Post by pasi »

Code upload problems often have to do with the wrong SPI speed or clock polarity. It is possible to verify what you've written by 1) not writing to AIADDR, 2) going through the patch/plugin data again, but instead of writing to WRAM, read from it and compare to the data value. Write to WRAMADDR normally.

If you're playing wav (at least for testing), you could test with just the vs1053b Pitch Shifter plugin, which is much smaller code to upload and should fit into the controller memory.

See: https://www.vlsi.fi/en/support/software ... ugins.html
Visit https://www.facebook.com/VLSISolution VLSI Solution on Facebook
Hannu
Senior User
Posts: 396
Joined: Mon 2016-05-30 11:54
Location: Finland
Contact:

Re: How to use the vs1053b pitch/tempo shifter plugin?

Post by Hannu »

I don't know much about Arduino (never used) but if this is same kind of situation as it is with real AVR programs this might be your issue.

The plugin loader uses some progmem read function. My understanding is that, your patch[] array is currently in RAM, not in program flash.

https://www.arduino.cc/reference/en/lan ... s/progmem/
Post Reply