Git Product home page Git Product logo

Comments (15)

TMRh20 avatar TMRh20 commented on July 3, 2024

It would help if you would post your code for the modified sine example, including your radio settings.

The radios need to be working optimally to handle the speed of audio transfer.

The first debugging shows that the RX() function on the receiver is called only one time with pipeNo=0 but then it never goes inside the while (radio.available(&pipeNo)). So it seems to me that no radio data is available to the receiver.

Seems really odd, do you have the interrupt pin of the RF24 module correctly connected to pin 4?
BTW you can bypass the use of interrupts by commenting out the line attachInterrupt(digitalPinToInterrupt(4),RX,FALLING); in myRadio.h and calling the RX() function from the main loop.

There are some issues that would need to be addressed to make these sketches compatible.

  1. In setup of wireless speaker, configure the following:
 aaAudio.autoAdjust = 0;
 aaAudio.dacBitsPerSample = 8;
 aaAudio.setSampleRate(16000);

The sine example is 16khz, 8-bit so we need to match those settings.

  1. In the lower part of the sketch, modify to the following:
      radio.read(&aaAudio.dacBuffer, 32);                   // Read the available radio data
      //Received 32 bytes: 32 samples of 8-bits each
      //Send them to the DAC using the channel selected via Serial command
      aaAudio.feedDAC(channelSelection, 32);

Again, to match the 8-bit configuration, we use the 8-bit dacBuffer and feed the dac 32 single bytes. No conversion should be necessary, as with 16-bit.

Any other issues would likely relate to your radio configuration. Make sure you are using opposing tx/rx addresses for example.

If I deploy the SimpleSine and WirelessSpeaker, am I suppose to listen the sine from the DAC0 of the receiver without any change to the code? The sine plays well from the DAC0 of the sender..

Yes, configurable via channelSelection option using Serial input in the example to DAC0, DAC1, or both.

from autoanalogaudio.

vinterlord avatar vinterlord commented on July 3, 2024

Hello,
thanks for your reply, I applied what you said and finally I managed it to work!

By commenting the interrupt code and moving the RX() function into the main loop I discovered that the interrupt mechanism was not working: even if I connected the IRQ pin of the module with the digital pin 4 of the arduino, the receiver (WirelessSpeaker) never went insime the while (radio.available(&pipeNo)) of the RX() function. Could this be an hardware issue? Maybe the irq pin is not working.. the arduino due boards are new and original brand.

If you don't mind I would like to ask you a couple of things just for my clarification:
1- is it correct to setup the sender-device writing to pipe 1 and reading from 0, and the receiver-device writing to pipe 0 and reading from 1? When you say "pipe 2 is for commands" to which commands are you referring to?

These are my configs for the radios:
(SENDER) SimpleSine - myRadio.h:
`void setupRadio(){

radio.begin();
radio.setChannel(1);
radio.setPALevel(RF24_PA_MAX);
radio.setDataRate(RF24_1MBPS);
radio.setAutoAck(0);
radio.setCRCLength(RF24_CRC_8);
radio.setAddressWidth(5);
radio.openReadingPipe(1,pipes[0]);
radio.openReadingPipe(2,pipes[2]);
radio.setAutoAck(2,1);
radio.openWritingPipe(pipes[1]);
radio.txDelay = 0;
radio.csDelay = 0;
radio.maskIRQ(0,1,0);
radio.printDetails();

radio.stopListening();

}
`
(RECEIVER) WirelessSpeaker - myRadio.h
I just set the setPALevel to MAX, the rests of the config is the following:

`void setupRadio(){

radio.begin();
radio.setChannel(1);
radio.setPALevel(RF24_PA_MAX);
radio.setDataRate(RF24_1MBPS);
radio.setAutoAck(0);
radio.setCRCLength(RF24_CRC_8);
radio.setAddressWidth(5);
radio.openReadingPipe(1,pipes[1]);
radio.openReadingPipe(2,pipes[2]);
radio.setAutoAck(2,1);
radio.openWritingPipe(pipes[0]);
radio.txDelay = 0;
radio.csDelay = 0;
radio.maskIRQ(0,1,0);
radio.printDetails();

radio.startListening();

attachInterrupt(digitalPinToInterrupt(4),RX,FALLING);

}`

2- When is the use-case where I have to uncomment the following lines and where I have to use the dacBuffer16 ?
(RECEIVER) WirelessSpeaker - WirelessSpeaker.ino
//for (int i = 0; i < 16; i++) { //Convert signed 16-bit variables into unsigned 12-bit //aaAudio.dacBuffer16[i] += 0x8000; //aaAudio.dacBuffer16[i] = aaAudio.dacBuffer16[i] >> 4; //}

3- What's the logic behind the variable pipeNo in myRadio.h (WirelessSpeaker)? It is initialized with 0 but I configured pipe number 1 to be the one for reading audio packets.. and in fact in RX() the while (radio.available(&pipeNo)) should gather data from pipe 1, is its value changed by some procedures?

4- In a scenario where a sender device is sending audio to a "middle" device that applies some dsp and then sends the modified audio to a receiver device, would you suggest to start with AudioRadioRelay example?

Thank you very much for your time and help

from autoanalogaudio.

TMRh20 avatar TMRh20 commented on July 3, 2024

Cool beans!

If the interrupt is not working, that is a bit odd. Traditionally, interrupts are handled a bit differently, but I don't understand why it would not be working for you.
You could try changing the following in the RX function:
while (radio.available(&pipeNo)) {
to

bool tx,fail,rx;
radio.whatHappened(tx,fail,rx);
if(rx){

This makes it rely solely on the interrupt flags to check for data, rather than checking the FIFO buffer directly using available();. The catch is that the pipeNo variable is no longer used.

  1. Yes, the whole pipe/address thing is confusing, but basically the radios are using opposing addresses on pipes 0,1.
    If you look at the radio settings, you will see radio.setAutoAck(2,1); which enables autoAck on pipe 2, leaving pipes 0 & 1 using multicast (noAck). This allows the sender to use pipe2 to send data or commands to the recipient. They are arbitrary (can be for whatever purpose) but in the example it is used to send in the sampleRate of the coming audio stream. In practice the sender can use samples of any sampleRate, and before streaming a sample, it would send the sample rate to the recipient using pipe2.
    This can be used for any purpose really, such as controlling playback, changing volume, just sending additional data, etc.

  2. If you have 10 to 16-bit audio, then you would need to use the 16-bit dac buffer. There is a simpleSine12Bit example. If you were using that, or directly capturing audio from a microphone to 12-bit audio, you would need to use dacBuffer16. It can also be used for stereo samples, which require 2-bytes per sample.

  3. Yes, when radio.available(&pipeNo) is called, pipeNo is passed to it by reference, and the value (which pipe data has come in on) is loaded into the pipeNo variable. After calling available(&pipeNo), the user then checks the pipeNo variable to see which pipe data came in on. In this case, if it == 2, the sample rate is loaded from the radio, otherwise audio data is loaded. See https://www.arduino.cc/reference/en/language/structure/pointer-access-operators/reference/

  4. That could be a good starting point. You can remove the getADC() stuff and just have it do the relaying and playback, or just the relaying.

from autoanalogaudio.

TMRh20 avatar TMRh20 commented on July 3, 2024

I wrote this out quickly and should probably clarify that radio sending is always done using pipe0. The only change will be the TX address to the correct address for the intended pipe of the recipient. Pipes 1-5 are typically used for receiving since pipe0 is being used intermittently for sending.

from autoanalogaudio.

vinterlord avatar vinterlord commented on July 3, 2024

Hello,
for the moment, I'm just using the RX() function inside the main loop without using interrupts.

However the issue that I'm having now, is that the audio coming from the DAC of the receiver (WirelessSpeaker) is very much distorted and degraded. The use-case that I'm experimenting with, is the simpleSine transmission with 8 bit depth and 16000 of sample rate.

Thus, I've just set the following in both receiver and sender:
aaAudio.dacBitsPerSample = 8; aaAudio.setSampleRate(16000);

and also using the aaAudio.dacBuffer (not the dacBuffer16 since I'm working with 8bits).

What I noticed is that the more I increase the sample rate, the more is the distortion of the audio I get on the receiver

Another use case that I tried with the same problem, is the use of WirelessMicrophone example as the sender device. This time I plugged the output jack of my mp3 player to the A0 and GROUND pins, and I tried 12bits, different sample rate (from 16000 to 44000) and also used dacBuffer and dacBuffer16 accordingly to the bith depth. But the result was always a very distorted sound on the receiver.

When I used the simpleSine as the sender-device, then its dac played well and clean, but for now I couldn't try the same with the wirelessMicrophone.

Do you have any idea about what could be the problem causing this huge distortion?

Thanks again, your advices have been very precious for me

from autoanalogaudio.

vinterlord avatar vinterlord commented on July 3, 2024

Just for clarification, the distortion is so high that I could barely recognize the song from the mp3 player (despite of the input volume). And when I use the sine, the volume that I get on the receiver is dramatically squared and higher than the one from the sender's dac. These things suggest me that it must be something wrong :/

from autoanalogaudio.

TMRh20 avatar TMRh20 commented on July 3, 2024

from autoanalogaudio.

vinterlord avatar vinterlord commented on July 3, 2024

I did some tries during these days. The use-case is the same, I just want to transmit the mp3 from the transmitter to the receiver. I started from WirelessMicrophone and WirelessSpeaker examples and matched bit depth, sample rate etc.

I resolved the software issues related to the distorion:
Following your tip I discovered that the digital samples of the sine were actually transmitting correctly from the transmitter to the receiver. Much of the distortion was due to some serial.prints inserted into the RX() function. Somehow the prints were interfering, I don't know why, but removing all the debug prints from the RX() cleaned much of the audio.

Now I have "analog" issues (I suppose) on my audio, the setup is the following:
My DAC1 output (I'm working in mono) is connected through a 2.1 kOhm resistor to the tip of a jack, the ground of the arduino is connected to the sleeve of the jack. The jack is connected to the preamp input of my audio interface (Focusrite).

My input to the transmitter is an mp3 player with its jack, where the tip is connected to A0 and the sleeve is connected to the ground of the arduino.

The issues are the following:
1- with the setup above, the audio is clean: there is no "electrical" noise on the output. However there is a distortion, like everything is a bit clipped. You can hear what I mean in the attached "issue1.mp3" file inside the zip archive.

2- If I use only the A0 for the input, that is, disconnecting the ground of the arduino from the input jack then the distorition disappears but the electrical noise floor becomes so huge. You can hear this in the attached "issue2.mp3" file.

In my opinion, it seems that the ground signal input is phase-inverted somewhere in the process, do you have any idea or tips to fix the setup?

Thanks a lot
Audio-issues.zip

from autoanalogaudio.

TMRh20 avatar TMRh20 commented on July 3, 2024

Much of the distortion was due to some serial.prints inserted into the RX() function. Somehow the prints were interfering, I don't know why, but removing all the debug prints from the RX() cleaned much of the audio.

I think the Serial.prints were taking too long. At 16khz sample rate, with 32 samples, the RX() function needs to be called 500 times/second. It is very unfortunate that interrupts are not working for you, as I believe you would be getting at least a bit better performance. Without using interrupts, you need to ensure your code is performing as fast as possible, so the RX() function is ready for data.

I think issue #2 is basically normal, that is, you need a GND + Signal connection to your pre-amp, or you will be getting a lot of line noise. I don't think much can be done about taht.

With issue 1, it sounds like the samples themselves are being overdriven.

In the default WirelessSpeaker example, there is the following code at the bottom:

      radio.read(&aaAudio.dacBuffer16, 32);                   // Read the available radio data
      
      for (int i = 0; i < 16; i++) {                          //Convert signed 16-bit variables into unsigned 12-bit
        aaAudio.dacBuffer16[i] += 0x8000;         //Convert from int16_t to uint16_t
        aaAudio.dacBuffer16[i] = aaAudio.dacBuffer16[i] >> 4; //Downshift to 12 bits
      }

The for loop is meant to convert signed 16-bit samples down to unsigned 12, mainly by shifting the sample down to 12-bits.

If you downshift it more, it has the effect of lowering the volume and in some cases, may lower the level of distortion. ie with 8-bit samples:

      for (int i = 0; i < 32; i++) {                          //Convert 8-bit variables into 6-bit
        aaAudio.dacBuffer[i] = aaAudio.dacBuffer[i] >> 2;
      }

If this works, then I would think the pre-amp is being overdriven.

Beyond that, I would try the following:

  1. In transmitter (wirelessMicrophone), copy the adcBuffer to the dacBuffer, and feed it to the DAC. Connect your audio pre-amp. Do you get clean output?

a: If output is clean on sender, the receiver should behave the exact same way. This could indicate a problem with the radio communication. Maybe missed packets? Mishandling of data?
You could add a simple counter to the receiver that increments every time a packet is received. If you print it out every second, it should equal 500 at 16khz. (Don't use delays, use a millis() timer)
b. If output not clean on sender, it is an external input/output problem. You could lower volume on mp3 player, or try downshifting the samples as above.

from autoanalogaudio.

vinterlord avatar vinterlord commented on July 3, 2024

Hello,
by following your tip about feeding the DAC of the transmitter I discovered that it was distorted too and so I realized it must be an analog input issue. I fixed the analog input problem by applying this simple circuit to the input:

input_arduino_due_jack

it basically adds a DC offset to the input signal in order to move the voltage values entirely in the positive part. (because this is what the A0 wants as far as I have googled about)
Now if I transmit 8 bit audio at different sample rates, it works like a charm and the audio of the receiver is good!

However, if I use 10 to 12 bits and the dacBuffer16 the audio is good from the sender's dac but really distorted (worst than the example I gave you) from the receiver's dac. If I print the packets on the receiver I see that the half of the samples inside the 32 byte chunk are zeros. The situation is better if I use lower sample rates, so I think that the RX() function without using interrupts is not suitable for handling dacBuffer16 and 10-12 bits, what do you think about that?

As I said 8bit and dacBuffer are working really good even with high sample rate!

Thanks as always

from autoanalogaudio.

TMRh20 avatar TMRh20 commented on July 3, 2024

Well the formula for calculating data rate is: bit rate = sample rate × bit depth × channels
So 32khz * 8-bits * 1Channel / 8bitsPerByte = 32KB/s
Then 16khz * 16-bits * 1Channel / 8bitsPerByte = 32KB/s

So if you can do 32khz at 8-bits, you shouldn't usually have a problem transferring 16khz at 16-bit.

The zeros may be normal, if the input isn't maxing out at 3.3v and therefore not using the whole 12-bits. Maybe post your code as it is for the 12-bit sampling & receiver?

from autoanalogaudio.

vinterlord avatar vinterlord commented on July 3, 2024

Hello,
I succeded to use the interrupts with RX() by replacing the arduino due.. Probably it was a faulty device. Now I'm using interrupts with 12bit, 16000 sample rate and dacBuffer16 but still the issue of the distorion persists. As you can hear in the attached audioFile, there is a huge distortion but the song could be listenable under it.
issue12bit.zip
The calculations seem correct also to me, in fact I think it could be a software issue.

The following is the code that I use on the receiver when I setup 12bit and dacBuffer16 (interrupts enabled):

void RX() {

  while (radio.available(&pipeNo)) {                          // Check for data and get the pipe number

    if (pipeNo == 2) {
      radio.read(&dynSampleRate, 4);                          // Receive commands using pipe #2
      aaAudio.setSampleRate(dynSampleRate);                   // Pipe 2 is being used for command data, pipe 1 & others for audio data
    } else {

      radio.read(&aaAudio.dacBuffer16, 32);                   // Read the available radio data

      //for (int i = 0; i < 16; i++) {                          //Convert signed 16-bit variables into unsigned 12-bit
      //aaAudio.dacBuffer16[i] += 0x8000;
      //aaAudio.dacBuffer16[i] = aaAudio.dacBuffer16[i] >> 4;
      //}

      //Received 32 bytes: 32 samples of 12s-bits each
      //Send them to the DAC using the channel selected via Serial command
      aaAudio.feedDAC(channelSelection, 32);
    }
  }
}
void setupRadio(){
  
  radio.begin();
  radio.setChannel(1);
  radio.setPALevel(RF24_PA_MIN);
  radio.setDataRate(RF24_2MBPS);
  radio.setAutoAck(0);
  radio.setCRCLength(RF24_CRC_8);
  radio.setAddressWidth(5);
  radio.openReadingPipe(1,pipes[1]);
  radio.openReadingPipe(2,pipes[2]);
  radio.setAutoAck(2,1);
  //radio.openWritingPipe(pipes[0]);
  radio.txDelay = 0;
  radio.csDelay = 0;
  radio.maskIRQ(0,1,0);
  radio.printDetails();

  radio.startListening();

  attachInterrupt(digitalPinToInterrupt(4),RX,FALLING);
  
}

The following is the code that I'm using for the transmitter:

void loop() {

  //Display the timer period variable for each channel every 3 seconds
  if (millis() - dispTimer > 3000) {
    dispTimer = millis();

    TcChannel * t = &(TC0->TC_CHANNEL)[0];
    TcChannel * tt = &(TC0->TC_CHANNEL)[1];

    Serial.print("Ch0:");
    Serial.println(t->TC_RC);
    Serial.print("Ch1:");
    Serial.println(tt->TC_RC);
  }

  // With autoAdjust disabled, getADC() will block until the ADC data is ready
  aaAudio.getADC(32);
  radio.writeFast(&aaAudio.adcBuffer16,32);
  copySamplesToDac();
  aaAudio.feedDAC(1, 32);
}


void copySamplesToDac() {
    for(int i=0; i<32; i++) {
     aaAudio.dacBuffer16[i] = aaAudio.adcBuffer16[i];
    }
}
void setupRadio(){
  
  radio.begin();
  radio.setChannel(1);
  radio.setPALevel(RF24_PA_MIN);
  radio.setDataRate(RF24_2MBPS);
  radio.setAutoAck(0);
  radio.setCRCLength(RF24_CRC_8);
  radio.setAddressWidth(5);
  radio.openReadingPipe(1,pipes[0]);
  radio.openReadingPipe(2,pipes[2]);
  radio.setAutoAck(2,1);
  radio.openWritingPipe(pipes[1]);
  radio.txDelay = 0;
  radio.csDelay = 0;
  radio.maskIRQ(0,1,0);
  radio.printDetails();

  radio.startListening();
  radio.stopListening();
  //attachInterrupt(digitalPinToInterrupt(4),RX,FALLING);
  
}

thanks again for your time

from autoanalogaudio.

TMRh20 avatar TMRh20 commented on July 3, 2024

Ok, interesting.

Problem:
In your transmitter, because you are operating in 12-bit mode, the call to aaAudio.getADC(32); is getting 32 12-bit samples from the ADC and loading them into adcBuffer16 . This ends up using up a total of 32 * 16bits / 8bitsPerByte == 64 bytes.

Then the call of radio.writeFast(&aaAudio.adcBuffer16,32); is only sending 32 bytes total, so you are losing half of your audio.

Solution
So on TX the call should be aaAudio.getADC(16);
Because the ADC is in 12-bit mode, you will be only dealing with 16 samples at a time instead of 32, but they still take up 32 bytes.

So on TX and RX, when dealing with the dacBuffer16, only do 16 iterations when copying, and only send 16 samples at a time. On both use aaAudio.feedDAC(channelSelection,16);

That should do the trick. I'm going to look at updating the examples and documentation to make the behaviour and sizes clearer, because it is confusing.

from autoanalogaudio.

vinterlord avatar vinterlord commented on July 3, 2024

Hello,
Now I "kind of" manage it by following your suggestions! In fact until 24000 / 32000 (12bit) of sample rate the audio is good on the receiver. However if I go beyond that limit, the audio becomes full of crackles and pops. Of course the huge distortion that I had before was a matter of bits matching, but now it seems more a matter of speed:

In fact, if I use interrupts the crackles increase as well as increasing the sample rate. For example if I use 16000 and interrupts then it is ok, but if I go around 24000/32000 I have to disable interrupts and put RX() inside the loop to avoid crackles. Finally if I put 44000 sample rate, then the audio is full of crackles and pops and is getting even worse..

This happens also if my receiver is sending a 12bit sine wave at 44000 sample rate. So it might suggest that it is not a problem of the audio input stuff which we have already been fixed so far (I just added an anti-aliasing filter on the circuit for completeness).

According to what I read in you documentation of the library, the software should be able to stream 16bit at 44000 so maybe there is some more tweaking to do on the software. I tried to reason about buffer size of the write and read but with no success.

Do you have any idea? Just only a tip about where to investingate on the software would be of help.

This is my code (just applied your suggestions)

Transmitter:

void setup() {

  Serial.begin(115200);
  Serial.println("Analog Audio Begin");

  aaAudio.begin(1,1);  //Setup aaAudio using ADC only
  aaAudio.autoAdjust = 0;
  aaAudio.adcBitsPerSample = 12;
  aaAudio.dacBitsPerSample = 12;
  aaAudio.setSampleRate(44000);
  setupRadio();
}

/*********************************************************/

uint32_t dispTimer = 0;
uint8_t channelSelection = 1;
bool stampa = true;

void loop() {

  // With autoAdjust disabled, getADC() will block until the ADC data is ready
  aaAudio.getADC(16);
  radio.writeFast(&aaAudio.adcBuffer16,32);
  copySamplesToDac();
  aaAudio.feedDAC(1, 16);
}


void copySamplesToDac() {
    for(int i=0; i<16; i++) {
     aaAudio.dacBuffer16[i] = aaAudio.adcBuffer16[i];
    }
}

Receiver:

void loop() {
  RX(); 
}

/*********************************************************/

uint32_t dynSampleRate = 0;

// See myRadio.h: Function is attached to an interrupt triggered by radio RX/TX
void RX() {

  while (radio.available(&pipeNo)) {                          // Check for data and get the pipe number

    if (pipeNo == 2) {
      radio.read(&dynSampleRate, 4);                          // Receive commands using pipe #2
      aaAudio.setSampleRate(dynSampleRate);                   // Pipe 2 is being used for command data, pipe 1 & others for audio data
    } else {

      radio.read(&aaAudio.dacBuffer16, 32);                   // Read the available radio data
      //for (int i = 0; i < 16; i++) {                          //Convert signed 16-bit variables into unsigned 12-bit
      //aaAudio.dacBuffer16[i] += 0x8000;
      //aaAudio.dacBuffer16[i] = aaAudio.dacBuffer16[i] >> 4;
      //}
      
      aaAudio.feedDAC(channelSelection, 16);
    }
  }
}

Thank you very much

from autoanalogaudio.

TMRh20 avatar TMRh20 commented on July 3, 2024

from autoanalogaudio.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.