Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

speaker streaming and playRaw() #29

Open
belm0 opened this issue Jun 4, 2022 · 12 comments
Open

speaker streaming and playRaw() #29

belm0 opened this issue Jun 4, 2022 · 12 comments

Comments

@belm0
Copy link

belm0 commented Jun 4, 2022

Naively, I thought that repeated calls to Speaker.playRaw() on the same channel with stop_current_sound=false would connect exactly, without a gap. But I tried it and there seems to be clicking artifacts due to a gap.

We could use it to simply stream from an audio file or real-time generated waveform.

Since Speaker has multiple channels and various functionality, it would be nice to combine streaming on one channel with one-shot sounds on other channels under a single API.

Do I understand it correctly? 🙏 @lovyan03

@lovyan03
Copy link
Collaborator

lovyan03 commented Jun 4, 2022

Hello. @belm0
Your understanding is correct. It should be possible to playback without gaps.
I can actually achieve this with speaker and webRadio and A2DP in the library examples.

If you can present code that can reproduce the problem, please show it to me.

@lovyan03
Copy link
Collaborator

lovyan03 commented Jun 4, 2022

@belm0
I have an idea of one cause and will let you know.
The playRaw function places a pointer to the received data in the performance queue and finishes processing.
In other words, it finishes processing without making a copy of the received data.
Therefore, if the data is rewritten in the user code immediately after the playRaw function is called, the output sound will be affected.

Please prepare multiple data buffers on the user code side and use them in order.

Specifically, there are two ways to do this.

Method 1

  • Prepare three data buffers and use them in order.

Method 2

  • Prepare two data buffers and use them in order.
  • and, When you call the playRaw function, divide the buffer in half and call it twice, once in the first half and once in the second half.

@belm0
Copy link
Author

belm0 commented Jun 4, 2022

I was using double buffer-- I'm surprised that triple buffer is needed.

However, I've updated my test to use 4 buffers, and the output is still not smooth. So I must be missing something obvious.

M5.begin();
M5.Speaker.begin();
M5.Speaker.setVolume(64);

M5.Speaker.tone(2000, 100);
delay(1000);

int buf_size = 4096;
auto buf = new uint8_t[buf_size];
int blk_size = buf_size / 4;
int count = 0;
for (int index = 0; ; index = (index + 1) % 4) {
    auto blk = buf + blk_size * index;
    for (int i = 0; i < blk_size; ++i) {
        blk[i] = count % 99;
        ++count;
    }
    M5.Speaker.playRAW(blk, blk_size, 22050, false, 1, 0);
}

@lovyan03
Copy link
Collaborator

lovyan03 commented Jun 4, 2022

@belm0
I ran it through M5StackGRAY and looked at the waveform in the logic analyzer.
It appears that the sawtooth wave is being output as programmed.
I seem to hear no problems with the sound as well, what are the issues you are experiencing?

image

@lovyan03
Copy link
Collaborator

lovyan03 commented Jun 4, 2022

@belm0
If you want to output a triangular wave, do the following

        blk[i] = abs((count % 50) - 25) * 10;

image

@belm0
Copy link
Author

belm0 commented Jun 4, 2022

Thank you. I did intend sawtooth wave in the test.

I haven't looked at the output waveform, but I can hear that it has obvious artifacts. And the artifact varies with the configured buffer size. I'm using Core2.

Here is zip with crude recording of buf_size 4096 and 2048 cases.

Archive.zip

@belm0
Copy link
Author

belm0 commented Jun 4, 2022

I have a Core Basic too, and it seems to be the same problem. So is it about library or platform version?

PLATFORM: Espressif 32 (4.2.0+sha.1059124) > M5Stack Core2
framework-arduinoespressif32 @ 2.0.4+sha.ed33e15 

(also on esp32 2.0.3)

Audio demos seem OK, and when I use i2c API directly it sounds as expected.

@lovyan03
Copy link
Collaborator

lovyan03 commented Jun 4, 2022

test.mp4

I am unable to reproduce the phenomenon. I have no idea what the cause is.
As far as I have tested it with my GRAY and Core2, it seems to sound fine.
I am hearing a steady sawtooth wave, not noise like the audio data you uploaded.

M5Unified : v0.0.7
ArduinoESP32 : v2.0.3

@belm0
Copy link
Author

belm0 commented Jun 4, 2022

It seems to be working now-- sorry for the trouble.

The original problem was using double instead of triple buffering.

However, to make the example code shared above, I switched to Arduino IDE that had an old M5Unified version (0.0.5) which seems to have some other problem. On v0.0.7 or latest Speaker code it seems OK.

In Speaker.hpp, if you would mention that playRaw() needs triple buffer for streaming it might be helpful to others.

Thank you!

@lovyan03
Copy link
Collaborator

lovyan03 commented Jun 4, 2022

I am glad your problem was resolved.
I apologize for the inconvenience caused by the lack of documentation.
I will add a description in the comments.

@belm0
Copy link
Author

belm0 commented Jun 11, 2022

memo: The thread calling playRaw() should have priority >= 1, even for applications with low CPU use. Otherwise there may be several milliseconds of delay either in waking the thread or receiving notifications, causing missed buffer deadlines.

@mdxs
Copy link

mdxs commented Apr 17, 2024

This ticket can be closed as resolved; the OP has indicated to be happy with the answer (and a description was added to the comments).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants