在 Supercollider 中循环通过缓冲区数组
Looping though a Buffer Array in Supercollider
我试图遍历一个缓冲区数组,每个缓冲区都包含从磁盘读取的声音样本,但我在让 SynthDef 重置其指向缓冲区的指针时遇到问题。
我做了以下事情:
假设我有一个声音文件文件夹,我已将它们全部读入名为“~buffers”的缓冲区数组中
我只想按顺序遍历数组,连续播放样本并在最后一个之后停止。
我定义了一个简单的SynthDef,然后把调用它的Synth放到一个Routine中:
(
SynthDef(\playBuffer,{arg out = 0, buf;
var sig;
sig = PlayBuf.ar(2, buf, doneAction: Done.freeSelf);
Out.ar(out, sig);
}).add
~routine = Routine({
~buffers.do({
arg item;
var synth;
synth = Synth(\playBuffer, [\buf, item]);
item.duration.wait;
synth.free;
});
});
~routine.play;
)
它没有按预期工作---合成器总是播放相同的声音,第一个,尽管持续时间对应于不同的样本。
我认为问题可能是我的 \playbuffer SynthDef 中的函数(至少根据帮助文件)没有用循环中的不同 bufnum 参数重新计算。
事实上,如果我使用 Buffer.play 来动态创建 synthDef 和 Synth,我可以循环遍历缓冲区。用此代码替换我的例程有效:
(
~routine2 = Routine({
~buffers.do({
arg item;
item.play;
item.duration.wait;
});
});
~routine2.play;
)
但是:它非常粗糙,因为现在除了通过 Buffer.play 的 mul 参数改变幅度外,我无法操纵缓冲区输出。
我想做的是在我自己的代码中复制 Buffer.play 的行为——即时创建 SynthDef 和 Synth。但我没有运气。事实上我不知道从哪里开始,可能是因为我没有完全掌握 SuperCollider 的服务器对功能的处理。我应该制作一个合成函数并在例程的循环中使用它吗?或者我应该在循环内移动 SynthDef 的定义(这看起来是等价的)?我尝试了后者,但仍然播放相同的声音。
也许我走错了路---我是 SuperCollider 的新手。
您第一个示例中的代码是正确的。如果我像这样填充缓冲区:
(
s.makeBundle(nil, {
~buffers = [1, 2, 3, 4, 5].collect {
|i|
var b;
b = Buffer.alloc(s, 44100, 1);
b.sine3([100, 150, 175] * i, 0.25);
};
})
)
然后用您的代码示例播放它们:
(
SynthDef(\playBuffer,{arg out = 0, buf;
var sig;
sig = PlayBuf.ar(1, buf, doneAction: Done.freeSelf);
Out.ar(out, sig);
}).add;
~routine = Routine({
~buffers.do({
arg item;
Synth(\playBuffer, [\buf, item]);
item.duration.wait;
});
});
~routine.play;
)
这很好用,我听到了升调。 (我将您的示例更改为单通道缓冲区,并像您已经在做的那样删除了 .free
Done.freeSelf
)。如果您每次都听到相同的声音播放,则问题可能出在您加载缓冲区的代码中,而不是播放中。
一个陷阱:缓冲区的 duration
属性 在您加载它们后无法立即使用 - 读取音频文件是异步的,并且 SC 在加载之前不知道持续时间。如果您在播放前立即执行 Buffer.read
,则您的持续时间可能是例如0
或 nil
,这会导致意外结果。
我在一个任务中试过了,但我认为它完全像你在日常工作中所做的那样。
你必须把缓冲区放在数组中。
像缓冲区=[1,2,3,4,5]
但最好以这种方式编码。
\缓冲区=[a.bufnum,b.bufnum,c.bufnum,d.bufnum]
并在 SynthDef 的 PlayBuf 的第二个参数中设置缓冲区变量。
因为你可能会在你的服务器加载其他缓冲区,如果你把缓冲区的编号放在数组中,它通常会播放你不想播放的错误缓冲区,。
我试图遍历一个缓冲区数组,每个缓冲区都包含从磁盘读取的声音样本,但我在让 SynthDef 重置其指向缓冲区的指针时遇到问题。
我做了以下事情:
假设我有一个声音文件文件夹,我已将它们全部读入名为“~buffers”的缓冲区数组中
我只想按顺序遍历数组,连续播放样本并在最后一个之后停止。
我定义了一个简单的SynthDef,然后把调用它的Synth放到一个Routine中:
( SynthDef(\playBuffer,{arg out = 0, buf; var sig; sig = PlayBuf.ar(2, buf, doneAction: Done.freeSelf); Out.ar(out, sig); }).add ~routine = Routine({ ~buffers.do({ arg item; var synth; synth = Synth(\playBuffer, [\buf, item]); item.duration.wait; synth.free; }); }); ~routine.play; )
它没有按预期工作---合成器总是播放相同的声音,第一个,尽管持续时间对应于不同的样本。
我认为问题可能是我的 \playbuffer SynthDef 中的函数(至少根据帮助文件)没有用循环中的不同 bufnum 参数重新计算。
事实上,如果我使用 Buffer.play 来动态创建 synthDef 和 Synth,我可以循环遍历缓冲区。用此代码替换我的例程有效:
(
~routine2 = Routine({
~buffers.do({
arg item;
item.play;
item.duration.wait;
});
});
~routine2.play;
)
但是:它非常粗糙,因为现在除了通过 Buffer.play 的 mul 参数改变幅度外,我无法操纵缓冲区输出。 我想做的是在我自己的代码中复制 Buffer.play 的行为——即时创建 SynthDef 和 Synth。但我没有运气。事实上我不知道从哪里开始,可能是因为我没有完全掌握 SuperCollider 的服务器对功能的处理。我应该制作一个合成函数并在例程的循环中使用它吗?或者我应该在循环内移动 SynthDef 的定义(这看起来是等价的)?我尝试了后者,但仍然播放相同的声音。
也许我走错了路---我是 SuperCollider 的新手。
您第一个示例中的代码是正确的。如果我像这样填充缓冲区:
(
s.makeBundle(nil, {
~buffers = [1, 2, 3, 4, 5].collect {
|i|
var b;
b = Buffer.alloc(s, 44100, 1);
b.sine3([100, 150, 175] * i, 0.25);
};
})
)
然后用您的代码示例播放它们:
(
SynthDef(\playBuffer,{arg out = 0, buf;
var sig;
sig = PlayBuf.ar(1, buf, doneAction: Done.freeSelf);
Out.ar(out, sig);
}).add;
~routine = Routine({
~buffers.do({
arg item;
Synth(\playBuffer, [\buf, item]);
item.duration.wait;
});
});
~routine.play;
)
这很好用,我听到了升调。 (我将您的示例更改为单通道缓冲区,并像您已经在做的那样删除了 .free
Done.freeSelf
)。如果您每次都听到相同的声音播放,则问题可能出在您加载缓冲区的代码中,而不是播放中。
一个陷阱:缓冲区的 duration
属性 在您加载它们后无法立即使用 - 读取音频文件是异步的,并且 SC 在加载之前不知道持续时间。如果您在播放前立即执行 Buffer.read
,则您的持续时间可能是例如0
或 nil
,这会导致意外结果。
我在一个任务中试过了,但我认为它完全像你在日常工作中所做的那样。 你必须把缓冲区放在数组中。 像缓冲区=[1,2,3,4,5] 但最好以这种方式编码。 \缓冲区=[a.bufnum,b.bufnum,c.bufnum,d.bufnum] 并在 SynthDef 的 PlayBuf 的第二个参数中设置缓冲区变量。 因为你可能会在你的服务器加载其他缓冲区,如果你把缓冲区的编号放在数组中,它通常会播放你不想播放的错误缓冲区,。