Go Down

Topic: A bunch of questions about playback sampled sounds (Read 9025 times) previous topic - next topic

Lucario448

Hi. I tried to upload a sketch that tests the timing of all the functions I would need. I don't know why I can't even compile it. It throws an error telling me that the array variable (const actually) was not declared. Here's the code:

Code: [Select]
const unsigned byte array[9] PROGMEM = {200, 128, 255, 64, 7, 35, 50, 60, 215,};
unsigned long prevMicros = 0;
unsigned long currentMicros = 0;
unsigned long result = 0;


void setup() {
  Serial.begin(9600);
  Serial.println("Testing timing... (microseconds)");
  prevMicros = micros();
  pinMode(9, OUTPUT);
  currentMicros = micros();
  result = currentMicros - prevMicros;
  Serial.print("pinMode takes ");
  Serial.println(result);
 
  pinMode(13, OUTPUT);
  pinMode(2, INPUT);
 
  prevMicros = micros();
  digitalWrite(13, HIGH);
  currentMicros = micros();
  result = currentMicros - prevMicros;
  Serial.print("digitalWrite takes ");
  Serial.println(result);

  prevMicros = micros();
  digitalRead(2);
  currentMicros = micros();
  result = currentMicros - prevMicros;
  Serial.print("digitalRead takes ");
  Serial.println(result);

  prevMicros = micros();
  analogRead(A0);
  currentMicros = micros();
  result = currentMicros - prevMicros;
  Serial.print("analogRead takes ");
  Serial.println(result);

  prevMicros = micros();
  analogWrite(9, 128);
  currentMicros = micros();
  result = currentMicros - prevMicros;
  Serial.print("analogWrite (coded value) takes ");
  Serial.println(result);

  prevMicros = micros();
  analogWrite(9, pgm_read_byte(&array[2]));
  currentMicros = micros();
  result = currentMicros - prevMicros;
  Serial.print("analogWrite (fetched value) takes ");
  Serial.println(result);

  prevMicros = micros();
  Serial.print("Print this line takes ");
  currentMicros = micros();
  result = currentMicros - prevMicros;
  Serial.println(result);

  prevMicros = micros();
  map(512, 0, 1023, 0, 255);
  currentMicros = micros();
  result = currentMicros - prevMicros;
  Serial.print("map takes ");
  Serial.println(result);

  Serial.print("That's it...");
}

void loop() {
  // Nothing happens after setup function

}

Grumpy_Mike

#61
Feb 22, 2016, 02:43 pm Last Edit: Feb 22, 2016, 02:51 pm by Grumpy_Mike
That first line is wrong. You have a comma at the end and the declaration in the wrong order, it should be this:-
Code: [Select]
const PROGMEM byte array[9]  = {200, 128, 255, 64, 7, 35, 50, 60, 215};

This is what I got on Leonardo:-
Code: [Select]

Testing timing... (microseconds)
pinMode takes 8
digitalWrite takes 8
digitalRead takes 8
analogRead takes 208
analogWrite (coded value) takes 12
analogWrite (fetched value) takes 16
Print this line takes 40
map takes 56
That's it...

 
The case you are missing is when you do no operation, this base line overhead must be subtracted from all results for a meaningful measure.

Also due to the low resolution of the micros timer you should do more than one operation and divide the result by N where N is the number of times you did the operation.

Lucario448

All right thank you for sharing your results. Now I know that the analogRead function is the slowest of all. Now I should test it but in the "fast mode"

The case you are missing is when you do no operation, this base line overhead must be subtracted from all results for a meaningful measure.

Also due to the low resolution of the micros timer you should do more than one operation and divide the result by N where N is the number of times you did the operation.
Ok then. So...
  • Do you suggest me to test even the micros function itself?
  • In order to test an operation mutiple times, which one of these statements will be faster: for or while?
  • How can I test a substraction operation? I guess I can create a variable apart from the already existing ones. What kind of substraction shall I test? From coded values or from variables' values?

Grumpy_Mike

Quote
Do you suggest me to test even the micros function itself?
You do that when there is no function between the start and the end of the timing.

Quote
In order to test an operation mutiple times, which one of these statements will be faster: for or while?
The for will be the fastest but simply use copy and paste and repeat the single command then there is not any overhead for the loop. You can not use a blank loop to find the overhead because the compiler spots that and optimists it out of existence.

Quote
How can I test a substraction operation?
Taken care of in the blank, nothing to test - test.

Grumpy_Mike


Lucario448

What makes you think I am upset?
Wait... WHAT? Are you worried because I'm taking longer to post a reply?

Well, whatever. Now I tested my sketch in my Nano board and these are the results:

Code: [Select]
Testing timing... (microseconds)
pinMode takes 8
digitalWrite takes 8
digitalRead takes 8
analogRead takes 208
analogWrite (coded value) takes 12
analogWrite (fetched value) takes 16
Print this line takes 22880
map takes 56
Now testing the analogRead function in its fast mode
Take note of the following 50 numbers!
Ready?
Set
GO!
16
20
20
20
20
20
20
20
20
16
16
16
16
16
16
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
20
That's it...

Notice that I added the "fast mode" of the analogRead function, and tested it 50 times. The average of all that values is 19.44. In conclusion, the analogRead function in "fast mode" takes around 19 and 20 microseconds. Can it be considered correct?

Grumpy_Mike

#66
Feb 25, 2016, 08:24 am Last Edit: Feb 25, 2016, 08:25 am by Grumpy_Mike
Sorry some one posted that I sounded upset. Now they have removed the post so it sounds like I was talking to you.

Yes 20uS sounds about right.

Lucario448

Sorry some one posted that I sounded upset. Now they have removed the post so it sounds like I was talking to you.
Haha, roger that!


Now, on the other side; is there a disadvantage of setting up the analogRead in the so called "fast mode"? Accuracy loss or what else?

Grumpy_Mike

It is going to be a bit nosier the faster it goes but with just speeding it up that much it should be OK. The thing is you are not switching between analogue channels and that is what takes the time. Letting the signal stabilise after the switch.

Lucario448

It is going to be a bit nosier the faster it goes but with just speeding it up that much it should be OK. The thing is you are not switching between analogue channels and that is what takes the time. Letting the signal stabilise after the switch.
So what actually makes it faster? By ignoring the other inputs or skipping the "stabilization time"?


Hey... and by the way, I want to take this even further. I'm going to try to produce stereo audio; but first, clear my doubts:

  • Due to small memory, I must cut down the sampling rate to 8 KHz, but shall I keep treating the sound as if it has a 16 KHz sampling rate?
  • Correct me if I'm wrong. Digital stereo audio stores its samples by alternating all its channels. For instance: if the data is stored in an array, then the even-numbered indexes correspond to the samples of the left channel, and the odd-numbered indexes correspond to the samples of the right channel.
  • By using analogWrite twice per loop (in order to update the duty cycle of both channels), will this create a sort of "phase shift" to the right channel? (if the left one is updated first)


Ok, that's all I need to know. Now I'm going to create the sound for testing, and also spoil you what it will be.
It will be a 1 kHz sine wave that goes throughout the channels! (around and around if looped)

Grumpy_Mike

Quote
Due to small memory, I must cut down the sampling rate to 8 KHz, but shall I keep treating the sound as if it has a 16 KHz sampling rate?
No if it is sampled at 8KHz then treat it as 8KHz.

Quote
So what actually makes it faster?
Speeding up the clock that drives the successive approximation A/D. It means it spends less time on each bit's decision.

Quote
Correct me if I'm wrong. Digital stereo audio stores its samples by alternating all its channels. For instance: if the data is stored in an array, then the even-numbered indexes correspond to the samples of the left channel, and the odd-numbered indexes correspond to the samples of the right channel.
While that could be used it is not the way it is normally done. There is a separate buffer for each channel and it is possible that one channel compresses more than the other. If they are interleaved like this then you can't apply any compression later on.

Quote
By using analogWrite twice per loop (in order to update the duty cycle of both channels), will this create a sort of "phase shift" to the right channel? (if the left one is updated first)
No because the analogue write update does not happen immediately but only when the clock gets to a transition. As this time change is way above the filter's cut off frequency then in effect there is no phase shift.

Lucario448

There is a separate buffer for each channel and it is possible that one channel compresses more than the other. If they are interleaved like this then you can't apply any compression later on.
The samples are in the uncompressed form (thus the sampling rate decrease), so am I ok?. I think that's how a WAV file stores the samples of a stereo sound...

Grumpy_Mike

Quote
The samples are in the uncompressed form
Yes I know, what I said is that you can't apply compression to the whole file.

Sure if you deal with everything in an interleaved format you can do that, but it is not what is normally done.

Go Up