C# 将音符写入wav文件
我对如何记音符(如A、B、C#等)或和弦(同时记多个音符)并将其写入wav文件感兴趣 据我所知,每个音符都有一个与之相关的特定频率(完美音高)-例如A4(中间C上方的a)是440 Hz(完整列表向下2/3) 如果我的理解是正确的,这个音高是在频域中,因此需要对其应用快速傅里叶逆变换来生成时域等效物 我想知道的是:C# 将音符写入wav文件,c#,audio,wav,C#,Audio,Wav,我对如何记音符(如A、B、C#等)或和弦(同时记多个音符)并将其写入wav文件感兴趣 据我所知,每个音符都有一个与之相关的特定频率(完美音高)-例如A4(中间C上方的a)是440 Hz(完整列表向下2/3) 如果我的理解是正确的,这个音高是在频域中,因此需要对其应用快速傅里叶逆变换来生成时域等效物 我想知道的是: 和弦是如何工作的?他们是平均投球数吗 当wav文件的内容是波形时,如何指定播放每个音符的时间长度 多个音符被反向FFT转换成一个字节数组的结果如何,这些字节构成了wav文件中的数据
- 和弦是如何工作的?他们是平均投球数吗
- 当wav文件的内容是波形时,如何指定播放每个音符的时间长度
- 多个音符被反向FFT转换成一个字节数组的结果如何,这些字节构成了wav文件中的数据
- 与此相关的任何其他相关信息
int channels = 1;
int bitsPerSample = 8;
//WaveFile is custom class to create a wav file.
WaveFile file = new WaveFile(channels, bitsPerSample, 11025);
int seconds = 60;
int samples = 11025 * seconds; //Create x seconds of audio
// Sound Data Size = Number Of Channels * Bits Per Sample * Samples
byte[] data = new byte[channels * bitsPerSample/8 * samples];
//Creates a Constant Sound
for(int i = 0; i < data.Length; i++)
{
data[i] = (byte)(256 * Math.Sin(i));
}
file.SetData(data, samples);
int通道=1;
int-bitsPerSample=8;
//WaveFile是创建wav文件的自定义类。
波形文件=新的波形文件(通道,比特采样,11025);
整数秒=60;
int samples=11025*秒//创建x秒的音频
//声音数据大小=通道数*每个采样位*采样
字节[]数据=新字节[通道*bitsPerSample/8*样本];
//发出持续的声音
for(int i=0;i
这会(不知何故)产生一种恒定的声音,但我不完全理解代码与结果之间的关系。您的思路是正确的。:) 音频信号 您不需要进行逆FFT(可以,但需要为它找到一个库或实现它,并生成一个信号作为它的输入)。直接从IFFT生成我们期望的结果要容易得多,IFFT是具有给定频率的正弦信号 正弦的参数取决于要生成的音符和生成的波形文件的频率(通常等于44100Hz,在您的示例中使用的是11025Hz) 对于1Hz音调,您需要一个周期等于1秒的正弦信号。对于44100 Hz,每秒有44100个采样,这意味着我们需要一个周期等于44100个采样的正弦信号。由于正弦周期等于(2*Pi),我们得到: 对于440 Hz,我们得到:
sin(44100*f) = sin(440*tau)
44100*f = 440*tau
f = 440 * tau / 44100 = 440 * 2 * pi / 44100
在C#中,这可能是这样的:
double toneFreq = 440d;
double f = toneFreq * 2d * Math.PI / 44100d;
for (int i = 0; i<data.Length; i++)
data[i] = (byte)(128 + 127*Math.Sin(f*i));
data[i] = (data[i] + volume * Math.Sin(phaseshift + i * multiplier * 440.0)));
/\
/ \__________
/ \
/ \
A D S R
double toneFreq=440d;
双f=toneFreq*2d*Math.PI/44100d;
对于(inti=0;i而言,您的思路是正确的
让我们看一下您的示例:
for(int i = 0; i < data.Length; i++)
data[i] = (byte)(256 * Math.Sin(i));
如果相移为零,显然没有变化。2π的相移(或π的任意偶数倍)也没有变化,因为sin的周期为2π。0到2π之间的每个值都会在音调“开始”的位置沿波移动一点点
精确计算出正确的相移可能有点棘手。如果你读了我关于生成“连续下降”的文章Shepard幻觉音调,你会看到我用了一些简单的演算来确保一切都在没有任何pop的情况下持续变化。你可以使用类似的技术来找出正确的转变是什么,使pop消失
我正在尝试如何生成phaseshift值。“Arcin((新音符的第一个数据样本)-(上一个音符的最后一个数据样本))/noteVolume”是否正确
首先要意识到的是,可能没有一个“正确的值”。如果结束音非常响亮,并且在峰值结束,而开始音非常安静,那么新音调中可能没有与旧音调值匹配的点
假设有一个解,它是什么?你有一个结束样本,称它为y,你想找到相移x,这样
y = v * sin(x + i * freq)
当我为零时,这就是
x = arcsin(y / v)
然而,这可能不太正确!假设你有
你想附加
有两种可能的相移:
及
大胆猜测哪一个听起来更好。:-)
弄清楚你是处于波浪的“上冲”还是“下冲”可能有点棘手。如果你不想算出真正的数学,你可以做一些简单的试探,比如“连续数据点之间的差异符号在转换时改变了吗?”
技术2:ADSR信封
如果你正在模拟一些听起来像真实乐器的东西,那么你可以通过如下改变音量来获得好的效果
你要做的是每个音符有四个不同的部分,叫做攻击、衰减、维持和释放。乐器上演奏的音符的音量可以这样建模:
double toneFreq = 440d;
double f = toneFreq * 2d * Math.PI / 44100d;
for (int i = 0; i<data.Length; i++)
data[i] = (byte)(128 + 127*Math.Sin(f*i));
data[i] = (data[i] + volume * Math.Sin(phaseshift + i * multiplier * 440.0)));
/\
/ \__________
/ \
/ \
A D S R
音量从零开始。然后,攻击发生了:声音迅速上升到峰值音量。然后,它会稍微衰减到维持的水平。然后它保持在这个水平,也许在音符播放时缓慢下降,然后释放回零
如果你这样做,那么就没有流行音乐,因为每个音符的开始和结束都是零音量的。发行版确保了这一点
不同的仪器有不同的“信封”。例如,管风琴的攻击、腐烂和释放时间都非常短;一切都是持续的,而持续是无限的。您现有的代码就像一个管风琴。与钢琴相比。同样,短暂的攻击,短暂的衰减,短暂的释放,但声音在持续的过程中会逐渐变得安静
攻击、衰减和释放部分可能很短,太短而听不见,但足够长以防止弹出。尝试在音符播放时改变音量,看看会发生什么。还没有人提到
data[i] = (data[i] + volume * Math.Sin(phaseshift + i * multiplier * 440.0)));
y = v * sin(x + i * freq)
x = arcsin(y / v)
/\
/ \__________
/ \
/ \
A D S R
DECAY = 0.99
while( n < 99999 )
outbuf[n++] = buf[k]
newVal = DECAY * ( buf[k] + buf_prev ) / 2
buf_prev = buf[k]
buf[k] = newVal
k = (k+1) % 100