bitluni / ULPSoundESP32

179 stars 26 forks source link

How can we use also DAC Channel 2? #2

Open w3llschmidt opened 4 years ago

w3llschmidt commented 4 years ago

@bitluni Matthias, it is possible to use DAC Channel 2 also?

I see this in your code but i didnt get the glue how you adress this in

const ulp_insn_t mono[]

  //initialize DACs
  dac_output_enable(DAC_CHANNEL_1);
  dac_output_enable(DAC_CHANNEL_2);
  dac_output_voltage(DAC_CHANNEL_1, 200);
  dac_output_voltage(DAC_CHANNEL_2, 200);
bitluni commented 4 years ago

I a stereo version locally but there seem to be something off since I get hick-ups. Probably miscounted the cycles. If you can fix a pull request would be awesome :-)

`

include <esp32/ulp.h>

include <driver/rtc_io.h>

include <driver/dac.h>

include <soc/rtc.h>

include

unsigned long samplingRate = 44100; const int opcodeCount = 17; const int dacTableStart1 = 2048 - 512; const int dacTableStart2 = dacTableStart1 - 512; const int totalSampleWords = 2048 - 512 - 512 - (opcodeCount + 1); const int totalSamples = totalSampleWords * 2; const int indexAddress = opcodeCount; const int bufferStart = indexAddress + 1;

void startULPSound() { //calculate the actual ULP clock unsigned long rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 1000); unsigned long rtc_fast_freq_hz = 1000000ULL (1 << RTC_CLK_CAL_FRACT) 256 / rtc_8md256_period;

//initialize DACs dac_output_enable(DAC_CHANNEL_1); dac_output_enable(DAC_CHANNEL_2); dac_output_voltage(DAC_CHANNEL_1, 128); dac_output_voltage(DAC_CHANNEL_2, 128);

int retAddress1 = 9; int retAddress2 = 13;

int loopCycles = 114; Serial.print("Real RTC clock: "); Serial.println(rtc_fast_freq_hz); int dt = (rtc_fast_freq_hz / samplingRate) - loopCycles; if(dt < 0) Serial.println("Sampling rate too high"); Serial.print("dt: "); Serial.println(dt); const ulp_insn_t stereo[] = { //reset offset register I_MOVI(R3, 0), //delay to get the right sampling rate I_DELAY(dt), // 6 + dt //reset sample index I_MOVI(R0, 0), // 6 //write the index back to memory for the main cpu I_ST(R0, R3, indexAddress), // 8 //load the samples I_LD(R1, R0, bufferStart), // 8 //mask the lower 8 bits I_ANDI(R2, R1, 255), // 6 //multiply by 2 I_LSHI(R2, R2, 1), // 6 //add start position I_ADDI(R2, R2, dacTableStart1),// 6 //jump to the dac opcode I_BXR(R2), // 4 //back from first dac //mask the upper 8 bits I_ANDI(R1, R1, 0xff00), // 6 //shift the upper bits to right and multiply by 2 I_RSHI(R1, R1, 8 - 1), // 6 //add start position of second dac table I_ADDI(R1, R1, dacTableStart2),// 6 //jump to the dac opcode I_BXR(R1), // 4 //here we get back from writing the second sample //increment the sample index I_ADDI(R0, R0, 1), // 6 //if reached end of the buffer, jump relative to index reset I_BGE(-13, totalSampleWords), // 4 //wait to get the right sample rate (2 cycles more to compensate the index reset) I_DELAY((unsigned int)dt + 2), // 8 + dt //if not, jump absolute to where index is written to memory I_BXI(3)}; // 4 // write io and jump back another 12 + 4 + 12 + 4

size_t load_addr = 0; size_t size = sizeof(stereo)/sizeof(ulp_insn_t); ulp_process_macros_and_load(load_addr, stereo, &size); // this is how to get the opcodes // for(int i = 0; i < size; i++) // Serial.println(RTC_SLOW_MEM[i], HEX);

//create DAC opcode tables for(int i = 0; i < 256; i++) { RTC_SLOW_MEM[dacTableStart1 + i 2] = 0x1D4C0121 | (i << 10); //dac0 RTC_SLOW_MEM[dacTableStart1 + 1 + i 2] = 0x80000000 + retAddress1 4; RTC_SLOW_MEM[dacTableStart2 + i 2] = 0x1D4C0122 | (i << 10); //dac1 RTC_SLOW_MEM[dacTableStart2 + 1 + i 2] = 0x80000000 + retAddress2 4; }

//set all samples to 128 (silence) for(int i = 0; i < totalSampleWords; i++) RTC_SLOW_MEM[bufferStart + i] = 0x8080;

//start RTC_SLOW_MEM[indexAddress] = 0; ulp_run(0); //wait until sure the index of current sample was written while(RTC_SLOW_MEM[indexAddress] == 0) delay(1); }

float toneFreq = 1.f; float samplesPerWave = samplingRate / toneFreq; unsigned char nextSampleLeft() { static float f = 0; f += 1; if(f > samplesPerWave) f -= samplesPerWave; return (unsigned char)(sin(toneFreq M_PI 2 f / samplingRate) 127 + 128); }

unsigned char nextSampleRight() { static float f = 0; f += 1; if(f > samplesPerWave) f -= samplesPerWave; return (unsigned char)(cos(toneFreq M_PI 2 f / samplingRate) 127 + 128); }

int lastFilledWord = 0;

void fillSamples() { static int i = 0; int currentWord = RTC_SLOW_MEM[indexAddress] & 0xffff; while(lastFilledWord != currentWord) { unsigned int w = nextSampleLeft(); w |= nextSampleRight() << 8; i+=3; w = i; RTC_SLOW_MEM[bufferStart + lastFilledWord] = w; lastFilledWord++; if(lastFilledWord == totalSampleWords) lastFilledWord = 0; } }

void setup() { Serial.begin(115200); Serial.print("Total stereo samples: "); Serial.println(totalSampleWords);
Serial.print("Buffer length: "); Serial.println((float)totalSampleWords / samplingRate, 3);
startULPSound(); delay(20); fillSamples(); }

void loop() { delay(10); //Serial.println(RTC_SLOW_MEM[indexAddress] & 0xffff); } `

w3llschmidt commented 4 years ago

Uhh, works like a charm!

Loco steam on Channel 1 and extra noise on Channel 2, parallel. No interruption.

Sounds.h is made of: bitluni.net/wp-content/uploads/2018/03/WavetableEditor.html

/*----------------------------------------------------------------------------------------------------------------------------------

include <freertos/FreeRTOS.h>

include <freertos/task.h>

include <esp32/ulp.h>

include <driver/rtc_io.h>

include <driver/dac.h>

include <soc/rtc.h>

include

include "sounds.h"

/ https://bitluni.net/wp-content/uploads/2018/03/WavetableEditor.html /

const unsigned long samplingRate = 22050;

const int opcodeCount = 17; const int dacTableStart1 = 2048 - 512; const int dacTableStart2 = dacTableStart1 - 512; const int totalSampleWords = 2048 - 512 - 512 - (opcodeCount + 1); const int totalSamples = totalSampleWords * 2; const int indexAddress = opcodeCount; const int bufferStart = indexAddress + 1;

void startULPSound() {

//calculate the actual ULP clock
unsigned long rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 1000);
unsigned long rtc_fast_freq_hz = 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period;

//initialize DACs
dac_output_enable(DAC_CHANNEL_1);
dac_output_enable(DAC_CHANNEL_2);
dac_output_voltage(DAC_CHANNEL_1, 128);
dac_output_voltage(DAC_CHANNEL_2, 128);

int retAddress1 = 9;
int retAddress2 = 13;

int loopCycles = 114;
int dt = (rtc_fast_freq_hz / samplingRate) - loopCycles;

//if(dt < 0) 
const ulp_insn_t stereo[] = {
//reset offset register
I_MOVI(R3, 0),
//delay to get the right sampling rate
I_DELAY(dt), // 6 + dt
//reset sample index
I_MOVI(R0, 0), // 6
//write the index back to memory for the main cpu
I_ST(R0, R3, indexAddress), // 8
//load the samples
I_LD(R1, R0, bufferStart), // 8
//mask the lower 8 bits
I_ANDI(R2, R1, 255), // 6
//multiply by 2
I_LSHI(R2, R2, 1), // 6
//add start position
I_ADDI(R2, R2, dacTableStart1),// 6
//jump to the dac opcode
I_BXR(R2), // 4
//back from first dac
//mask the upper 8 bits
I_ANDI(R1, R1, 0xff00), // 6
//shift the upper bits to right and multiply by 2
I_RSHI(R1, R1, 8 - 1), // 6
//add start position of second dac table
I_ADDI(R1, R1, dacTableStart2),// 6
//jump to the dac opcode
I_BXR(R1), // 4
//here we get back from writing the second sample
//increment the sample index
I_ADDI(R0, R0, 1), // 6
//if reached end of the buffer, jump relative to index reset
I_BGE(-13, totalSampleWords), // 4
//wait to get the right sample rate (2 cycles more to compensate the index reset)
I_DELAY((unsigned int)dt + 2), // 8 + dt
//if not, jump absolute to where index is written to memory
I_BXI(3)}; // 4
// write io and jump back another 12 + 4 + 12 + 4

size_t load_addr = 0;
size_t size = sizeof(stereo)/sizeof(ulp_insn_t);
ulp_process_macros_and_load(load_addr, stereo, &size);
// this is how to get the opcodes
// for(int i = 0; i < size; i++)
// Serial.println(RTC_SLOW_MEM[i], HEX);

//create DAC opcode tables
for(int i = 0; i < 256; i++)
{
    RTC_SLOW_MEM[dacTableStart1 + i * 2] = 0x1D4C0121 | (i << 10); //dac1
    RTC_SLOW_MEM[dacTableStart1 + 1 + i * 2] = 0x80000000 + retAddress1 * 4;
    RTC_SLOW_MEM[dacTableStart2 + i * 2] = 0x1D4C0122 | (i << 10); //dac2
    RTC_SLOW_MEM[dacTableStart2 + 1 + i * 2] = 0x80000000 + retAddress2 * 4;
}

//set all samples to 128 (silence)
for(int i = 0; i < totalSampleWords; i++)
RTC_SLOW_MEM[bufferStart + i] = 0x8080;

//start
RTC_SLOW_MEM[indexAddress] = 0;
ulp_run(0);

//wait until sure the index of current sample was written
while(RTC_SLOW_MEM[indexAddress] == 0) vTaskDelay(100 / portTICK_PERIOD_MS);

}

unsigned char nextSampleLeft() {
static long pos = 0; if(pos >= soundsOffsets[2]) pos = 0; return (unsigned char)((int)soundsSamples[pos++] + 128); }

unsigned char nextSampleRight() { static long pos = 0; if(pos >= soundsOffsets[1]) pos = 0; return (unsigned char)((int)soundsSamples[pos++] + 128); }

int lastFilledWord = 0;

void sound_task() {

startULPSound();

while(1) {

    int currentSample = RTC_SLOW_MEM[indexAddress] & 0xffff;
    int currentWord = currentSample >> 1;

    while(lastFilledWord != currentWord){
        unsigned int w = nextSampleLeft();
        w |= nextSampleRight() << 8;
        RTC_SLOW_MEM[bufferStart + lastFilledWord] = w;
        lastFilledWord++;
        if(lastFilledWord == totalSampleWords)
        lastFilledWord = 0;
    }

vTaskDelay(10 / portTICK_PERIOD_MS);
}   

vTaskDelete(NULL);
}

/*----------------------------------------------------------------------------------------------------------------------------------