C语言实现--计算MIDI音符的频率

2019-10-10  本文已影响0人  莹子说她想吃烤冷面

MIDI音符数据

MIDI音符数据的值是无符号 seven-bit 整数,每个整数的取值范围是[0,127]。MIDI音符数据包含的信息有:key numbers(调式标记,例如B小调), various controllers (控制器输入变化,如踩踏板、弯音), Key Velocity and Pressure, and so on.

中央C(Middle C,C5)的MIDI音符编号被定义为60,中央C低一个八度的C编号为60-12=48。编号的取值范围是[0,127]。MIDI最低音是C0,比中央C低5个八度,编号为0。
(ps: 在88键钢琴上中央C是第四个C,所以在乐理中,中央C标记为C4,和在MIDI中是不一样的。)
(pss: 这个编号不要和字母的ASCII编码搞混了,不是一码事哈。'C'的ASCII码是67)

音高与频率的关系

中央C上的A音符(Concert A,A5)发出的频率为440Hz(表示成"A=440Hz",或是"A440"),通常被当作“标准音高”。其编号是69。

音符编号 p 与其基频 f 的关系满足以下公式:
p=69+12 \times \log _{2}\left(\frac{f}{440}\right) f=440 \times 2^{(p-69) / 12}
以此计算得知中央C的频率约为261.6Hz。

semitone_ratio

音符每升高1个八度,其频率就变为之前的2倍。也就是说,音符每升高一个半音,其频率就乘上\sqrt[12]{2}=1.0594631 乘12次1.0594631就相当于升高1个八度(12个半音),其频率翻倍。1.0594631也叫做semitone_ratio。

代码

midi2freq

输入MIDI编号,输出其频率。(思路:先计算C0的频率,然后乘semitone_ratio的midinote次方。)

#include <iostream>
#include <stdio.h>
#include <math.h>

int main()
{
    double semitone_ratio;
    double c0; /* for frequency of MIDI Note 0 */
    double c5; /* for frequency of Middle C */
    double frequency; /* . . . which we want to find, */
    int midinote; /* . . . given this note. */
    
    /* calculate required numbers */
    
    semitone_ratio = pow(2, 1/12.0); /* approx. 1.0594631 */
    /* find Middle C, three semitones above low A = 220 */
    c5 = 220.0 * pow(semitone_ratio, 3);
    /* MIDI Note 0 is C, 5 octaves below Middle C */
    c0 = c5 * pow(0.5, 5);
    
    /* calculate a frequency for a given MIDI Note Number */
    printf("Input MIDI Note: ");
    scanf("%d",&midinote);
    // midinote = 73; /* C# above A = 440 */
    frequency = c0 * pow(semitone_ratio, midinote);
    
    printf("MIDI Note %d has frequency %f\n",midinote,frequency);
    
    return 0;
}

freq2midi

输入频率,输出频率最接近的MIDI编号以及偏离的pitchbend百分比。(思路:假设频率是最低音的x个2倍,那么编号就是12个半音乘以x。)

#include <iostream>
#include <stdio.h>
#include <math.h>

int main()
{
    double semitone_ratio;
    double c0; /* for frequency of MIDI Note 0 */
    double c5; /* for frequency of Middle C */
    double frequency; /* . . . which we want to find, */
    double midinote; /* . . . given this note. */
    double diff;
    
    /* calculate required numbers */
    
    semitone_ratio = pow(2, 1/12.0); /* approx. 1.0594631 */
    /* find Middle C, three semitones above low A = 220 */
    c5 = 220.0 * pow(semitone_ratio, 3);
    /* MIDI Note 0 is C, 5 octaves below Middle C */
    c0 = c5 * pow(0.5, 5);
    
    printf("Input MIDI frequency: ");
    scanf("%lf",&frequency);
    midinote = log2(frequency/c0)*12;
    
    diff = midinote-(int)midinote;
    if (diff<0.5){  // 如果“四舍”
        printf("The nearest MIDI Note is %d, and the pitchbend is +%lf%%\n",(int)midinote,diff*100);
    }
    else{            // 如果“五入”
        printf("The nearest MIDI Note is %d, and the pitchbend is %lf%%\n",(int)midinote+1,(diff-1)*100);
    }
    
    return 0;
}
上一篇下一篇

猜你喜欢

热点阅读