linux c语言播放midi,C语言实现--计算MIDI音符的频率

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 的关系满足以下公式:

math?formula=p%3D69%2B12%20%5Ctimes%20%5Clog%20_%7B2%7D%5Cleft(%5Cfrac%7Bf%7D%7B440%7D%5Cright)

math?formula=f%3D440%20%5Ctimes%202%5E%7B(p-69)%20%2F%2012%7D

以此计算得知中央C的频率约为261.6Hz。

semitone_ratio

音符每升高1个八度,其频率就变为之前的2倍。也就是说,音符每升高一个半音,其频率就乘上

math?formula=%5Csqrt%5B12%5D%7B2%7D%3D1.0594631 乘12次1.0594631就相当于升高1个八度(12个半音),其频率翻倍。1.0594631也叫做semitone_ratio。

代码

midi2freq

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

#include

#include

#include

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

#include

#include

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;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在沉寂了一段时间之后。我开始开发游戏的音频程序了。今天我要为大家展示的是我写的播放MIDI的程序,其实不要以为MIDI的载入和播放很复杂,其实它是相当的简单、只需要选用合适的解码器就行了。现在又几种解码器可以选择。MCI和DirectMusic。其中MCI是windows特别对多媒体应用程序提供的API集合,它可以播放很多流行的格式。但是对MIDI的控制性比较低。而DirectMusic则对MIDI有着较丰富的支持,最典型的就是可以控制节奏(tempo)。 我的代码没有对DirectMusic进行封装,使用C的风格制作的。所以大家看起来很好理解。下面我就来展示我写的代码吧。 Main.cpp /*--------------------------------------------------------------------------- 蒋轶民制作E-mail:jiangcaiyang123@163.com 最后编辑:年月日:29:06 文件名:main.cpp 作用:头文件的实现 ----------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ // 头文件 #include #include #include "main.h" using namespace std; int main( int, char** ) { try { cout << "初始化设备\n"; Initialize();// 初始化DirectMusic cout << "载入声音\n"; LoadSound( TEXT( "test.mid" ) ); // 载入声音文件 cout <SetRepeats( NULL ); // 仅仅播放一次 SetVolume( 0, 0 ); Play(); Sleep( 1000 ); SetTempo( 160 ); cout << "当前播放的速率为" << GetCurrentTempo() << '\n'; while ( IsPlaying() ); UnInitialize(); cout << "释放设备\n"; } catch( CError& e ) { e.SaveToFile(); cout << "错误报告已保存。\n"; } catch( ... ) { cout << "遇到了未知的错误。\n"; } return 0; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值