Flashing WT588D-16P without dedicated board using an Arduino

To preface, I'm trying to make a lightsaber with an ESP32.

From the other people who have done this, the sound chip I see used most often is a WT588D sound module, which is capable of gapless playback.

The WT588D comes in a few different configurations. There is a module that comes with a built in USB port which allows you to upload an image to the board using software designed for the chip. This module is bulky and big (and unavailable with local shipping).

There is also a version with just the IC and breakout, and no built in USB port. In order to upload your sounds to this board, you are required to use a dedicated programmer that is sold seperately:

I do not have access to one of these, and the only ones I can find ship from china with 30+ day lead times.

A few people in an old thread have managed to flash this board using an arduino's SPI capability.

Here is how I have mine wired up:

I am using a level shifter so that I can use my 5v mega to talk to the 3v board. Unforunately, when I run a sketch that should pull up the specs of the WT588D, I get information that isn't correct.

Here's the sketch:

/*
    windbond_serial_debug.cpp
    A simple program for the Arduino IDE to help familiarize you with
    using WinBond flash memory; can also be used to download the entire
    contents of a flash chip to a file via a serial port interface.
   
    Important bits of the code: the low-level flash functions (which
    implement the timing diagrams in the datasheet), and a simple
    serial-port command interface that allows you to talk to your
    UNO with any generic terminal application (even the command line).
   
    Copyright 2014, Peter J. Torelli

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>   

Revisions:
    rev 2 - 21-SEP-2014.
    User 'fiaskow' pointed out that driving the WEL instruction after
    program and erase w/o waiting for the op to finish may be corrupting
    execution. Removed this code (also not needed b/c the WEL is already
    cleared after page write or chip erase).
*/
#include <Arduino.h>
#include <SPI.h>

// SS:   pin 10
// MOSI: pin 11
// MISO: pin 12
// SCK:  pin 13

// WinBond flash commands
#define WB_WRITE_ENABLE       0x06
#define WB_WRITE_DISABLE      0x04
#define WB_CHIP_ERASE         0xc7
#define WB_READ_STATUS_REG_1  0x05
#define WB_READ_DATA          0x03
#define WB_PAGE_PROGRAM       0x02
#define WB_JEDEC_ID           0x9f
#define WB_SECTOR_ERASE       0x20

/*
================================================================================
User Interface Routines
The functions below map to user commands. They wrap the low-level calls with
print/debug statements for readability.
================================================================================
*/

/*
 * The JEDEC ID is fairly generic, I use this function to verify the setup
 * is working properly.
 */
void get_jedec_id(void) {
  Serial.println("command: get_jedec_id");
  byte b1, b2, b3;
  _get_jedec_id(&b1, &b2, &b3);
  char buf[128];
  sprintf(buf, "Manufacturer ID: %02xh\nMemory Type: %02xh\nCapacity: %02xh",
    b1, b2, b3);
  Serial.println(buf);
  Serial.println("Ready");
}

/*================================================================================
Low-Level Device Routines
The functions below perform the lowest-level interactions with the flash device.
They match the timing diagrams of the datahsheet. They are called by wrapper
functions which provide a little more feedback to the user. I made them stand-
alone functions so they can be re-used. Each function corresponds to a flash
instruction opcode.
================================================================================
*/

/*
 * See the timing diagram in section 9.2.35 of the
 * data sheet, "Read JEDEC ID (9Fh)".
 */
void _get_jedec_id(byte *b1, byte *b2, byte *b3) {
  digitalWrite(SS, HIGH);
  digitalWrite(SS, LOW);
  SPI.transfer(WB_JEDEC_ID);
  *b1 = SPI.transfer(0); // manufacturer id
  *b2 = SPI.transfer(0); // memory type
  *b3 = SPI.transfer(0); // capacity
  digitalWrite(SS, HIGH);
  not_busy();
} 

/*
 * not_busy() polls the status bit of the device until it
 * completes the current operation. Most operations finish
 * in a few hundred microseconds or less, but chip erase
 * may take 500+ms. Finish all operations with this poll.
 *
 * See section 9.2.8 of the datasheet
 */
 
void not_busy(void) {
  digitalWrite(SS, HIGH); 
  digitalWrite(SS, LOW);
  SPI.transfer(WB_READ_STATUS_REG_1);       
  while (SPI.transfer(0) & 1) {};
  digitalWrite(SS, HIGH); 
}

void setup(void) {
  SPI.begin();
  SPI.setDataMode(0);
  SPI.setBitOrder(MSBFIRST);
  Serial.begin(9600);
  Serial.println("");
  Serial.println("Ready");
  get_jedec_id();
   
}

/*
 * loop() dispatches the commands compiled by the serialEvent
 * parser callback. Some commands take multiple arguments, so
 * I have to split up the strings with some messy manipulation.
 */
void loop(void) {

}

And here is my output:

Ready
command: get_jedec_id
Manufacturer ID: 00h
Memory Type: 00h
Capacity: 00h
Ready

What I should expect to see is something more like this:

Ready
command: get_jedec_id
Manufacturer ID: c2h
Memory Type: 20h
Capacity: 16h
Ready

If I unplug the WT588D module from my breadboard, all I get is "ready" with none of the info. So I know it's communicating with the board in some way, but it's clearly not correct.

Any help would be greatly appreciated. I wonder if this has something to do with using SPI on a 2560 board instead of an Uno board?