问题描述
我正在一个项目中,我想通过组合不同的正弦波来生成(简单)声音.我使用的是arduino mkrZero,因为它内置了I2S接口,并且似乎具有足够的处理能力来满足我的需求.
I'm working on a project where I want to generate (simple) sound by combining different sine waves. I'm using an arduino mkrZero, as it has I2S interface built in and it seems to have enough processing power for what I want.
我已经按照与arduino I2S simpleTone教程相同的方式对系统进行了布线:
I have wired my system exactly like in the tutorial for arduino I2S simpleTone:
教程代码可以正常工作,并且我得到一个简单的方波扬声器发出的声音.
And the tutorial code works just fine, and I get a simple square wave tone from the speaker.
现在,我已经修改了代码以生成正弦波,为sin函数提供了一个查找表,以使其足够快:
Now I have modified the code to generate sine wave, there is a lookup table for the sin function to make it fast enough:
#include <I2S.h>
uint16_t isin16[] = {
0, 1144, 2287, 3430, 4571, 5712, 6850, 7987, 9121, 10252, 11380,
12505, 13625, 14742, 15854, 16962, 18064, 19161, 20251, 21336, 22414,
23486, 24550, 25607, 26655, 27696, 28729, 29752, 30767, 31772, 32768,
33753, 34728, 35693, 36647, 37589, 38521, 39440, 40347, 41243, 42125,
42995, 43851, 44695, 45524, 46340, 47142, 47929, 48702, 49460, 50203,
50930, 51642, 52339, 53019, 53683, 54331, 54962, 55577, 56174, 56755,
57318, 57864, 58392, 58902, 59395, 59869, 60325, 60763, 61182, 61583,
61965, 62327, 62671, 62996, 63302, 63588, 63855, 64103, 64331, 64539,
64728, 64897, 65047, 65176, 65286, 65375, 65445, 65495, 65525, 65535,
}; //0-90
const int sincount = 2;
int freqs[] = {50*360,51*360};
float amps[] ={0.1,0.1};
const int sampleRate = 8000; // sample rate in Hz
short sample = 0;
double t = 0;
double dt = 1.0/(1.0*sampleRate);
short LR[] = {0,0};
void setup() {
Serial.begin(115200);
// start I2S at the sample rate with 16-bits per sample
if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) {
while (1); // do nothing
}
}
void loop() {
sample = 0;
for(int n= 0;n<sincount;n++)
{
sample += fSin(freqs[n]*t)*amps[n];
}
t += dt;
LR[0] = sample;
LR[1] = sample;
I2S.write(LR[0]);//left channel
I2S.write(LR[1]);//right channel
}
float fSin(long x)
{
boolean pos = true; // positive - keeps an eye on the sign.
if (x < 0)
{
x = -x;
pos = false;
}
if (x >= 360) x %= 360;
if (x > 180)
{
x -= 180;
pos = !pos;
}
if (x > 90) x = 180 - x;
if (pos) return isin16[x]; // = /65535.0
return isin16[x];
}
这也很好.
但是!
如果我稍稍更改了代码然后写了
If I change the code a bit and I write
I2S.write(LR,2);
代替
I2S.write(LR[0]);//left channel
I2S.write(LR[1]);//right channel
一切都中断了,扬声器的声音听起来像是可怕的尖叫声
Everything just breaks, the sound from the speaker sounds like a horrible scream
从I2S库参考中:
将二进制数据写入I2S接口.该数据作为样本发送 或一系列样品.
Writes binary data to the I2S interface. This data is sent as a sample or series of samples.
语法
I2S.write(val)
//阻止
I2S.write(buf, len)
//不阻止
参数
val:作为单个样本发送的值
val: a value to send as a single sample
buf:要作为一系列样本发送的数组
buf: an array to send as a series of samples
len:缓冲区的长度
len: the length of the buffer
返回
byte write()将返回写入的字节数,尽管读取 该数字是可选的.
byte write() will return the number of bytes written, though reading that number is optional.
我想使用后者版本的write函数,因为它不会阻塞,并且我可以在播放前一个样本时生成新的样本.
I'd like to use the latter version of the write function, because it is not blocking and I can generate new samples while the previous ones are playing.
关于如何使缓冲版本正常工作的任何想法吗?
Any ideas on how to make the buffered version work correctly as well?
推荐答案
在您的代码中,您声明LR
为array of 2 short
, Arduino 中的short
大小为2 bytes
.
In your code, you declare LR
as an array of 2 short
, a short
in Arduino has size 2 bytes
.
当您写这篇文章时:
I2S.write(LR[0]); //left channel
I2S.write(LR[1]); //right channel
给予
size_t I2SClass::write(uint8_t data)
{
return write((int32_t)data);
}
size_t I2SClass::write(int sample)
{
return write((int32_t)sample);
}
您的short
值应自动提升以适合int
类型.现在,我不确定您正在处理的int
的大小,因为我发现的唯一 I2S.h 库用于 SAMD 板在其上int
具有4 bytes
大小的,但通常在 Arduino 上,int
的大小为2 bytes
.无论哪种情况,都应按原样使用该值,而不会出现问题.
your short
values should be automatically promoted to fit into the int
type. Now, I am a bit unsure on the size of int
you are dealing with, because the only I2S.h library I found is for the SAMD board on which an int
has 4 bytes
size, but normally on Arduino the an int
has size of 2 bytes
. In either case, the value should be taken as is, without problems.
当您写这篇文章时:
I2S.write(LR,2);
给予
size_t I2SClass::write(const uint8_t *buffer, size_t size)
{
return write((const void*)buffer, size);
}
size_t I2SClass::write(const void *buffer, size_t size)
{
...
written = _doubleBuffer.write(buffer, size);
...
}
您的array of 2 short
应该转换为const void *
.
从 I2SDoubleBuffer :
size_t I2SDoubleBuffer::write(const void *buffer, size_t size) {
size_t space = availableForWrite();
if (size > space) {
size = space;
}
if (size == 0) {
return 0;
}
memcpy(&_buffer[_index][_length[_index]], buffer, size);
_length[_index] += size;
return size;
}
_doubleBuffer
似乎不知道样本的声明的大小(您声明为16 bits
),它仅复制了从输入到内部缓冲区的字节.
it looks like _doubleBuffer
is not aware of the declared size of a sample (you declared it to be of 16 bits
), it merely copies size
bytes from the input to the internal buffer.
因此,我猜测,实际上发生的是,当您要求缓冲2 shorts
时,实际上仅复制了2 bytes
.
Therefore, I guess that what really happens is that when you ask for 2 shorts
to be buffered only 2 bytes
are actually copied.
示例:
假设您的LR
包含以下值
short LR[2] = { 0x89AB, 0xCDEF };
然后这就是我我想像发生的情况
then this is what I imagine it happens
I2S.write(LR[0]); // actual left sample is 0x89AB
I2S.write(LR[1]); // actual right sample is 0xCDEF
I2S.write(LR, 2); // actual left sample is 0x89AB
// 0xCDEF is not copied
/* next loop iteration */
I2S.write(LR, 2); // actual right sample is 0x89AB
// 0xCDEF is not copied
建议的解决方案:
尝试要求复制4 bytes
:
I2S.write(LR, 4); // actual left sample is 0x89AB
// actual right sample is 0xCDEF
这篇关于Arduino I2S正弦波的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!