概述
乐器仿真是跨软件工程和音乐理论双领域的交叉学科研究方向,长期以来,受到专业知识的限制,音乐专业人士不能采用软件工程及数学方法对乐器声学原理进行深入的剖析,而软件工程人员对音乐知识了解相对较少,因此涉及音乐的软件工程研究一直相对薄弱。实际上,在现代信号处理、模式识别的技术支撑下,可以揭示音乐背后的数学秘密,提高人类对音乐的理解。软件工程技术在音乐领域的应用,将给人类带来全新的听觉感受,符合国家近期提出的文化强国理念,在人文、科技领域都具有研究意义。
先上个动图给大家看看效果:GUI显示电子琴琴键,黑白键代表不同的音符,点击对应琴键显示音符的时域波形图,并显示音符对应的频率信息,左下角可以调整节拍,即每个音符的持续时间,动图不能播放声音,真实运行其实是有声音的,真正实现了电子琴的效果。
这里贴出部分源代码:
将鼠标点击位置转为音符表:
% 作用: 把鼠标点击的位置转换为在音符表里的对应下标
% x, y: 鼠标点击坐标
function [row, col] = getPositionInTable(x, y)
col = floor((x - 1) ./ 17 + 1);
row = 2; % row = 2 为白键音符所在行。白键排布有规律, 17像素一个白键。
if y < 67 % 67是白键和黑键的交界处,如果鼠标点击的范围可能是黑键
for left = -5 : 17 : 352 % 黑键的排布规律
right = left + 10;
if left > x % 如果x在两个黑键之间,直接跳出。使用上面算好的白键位置即可。
break
end
if left <= x && x <= right % 如果落在某个黑键范围内
preAmount = floor((left + 5) ./ 17 + 1); % 算出是第几个黑键
r = mod(preAmount, 7); % 第1 5 8 12 15个黑键其实是不存在的
if r ~= 1 && r ~= 5 % 如果黑键位置除以7的余数不是1也不是5,表示确实在黑键上
row = 1; % raw = 1 为黑键所在行
col = preAmount; % 列为算出的黑键位置
end
break % 如果确实是不存在的黑键,直接跳出,还是使用上面算好的白键位置。
end
end
end
end
将音符表转为音频信号
function music = noteText2Music(allNotes, noteTexts)
% 用于把音符文本转换为音频信号。
% -------------------------------
% noteText样例:
% { 'E41', 'C41', '01', 'AB41'}
% -------------------------------
global fs;
global t;
scaleTable = {'B', 'AB', 'A', 'GA', 'G', 'FG', ...
'F', 'E', 'DE', 'D', 'CD', 'C'}; % 音阶表
scaleNum = 0;
music = [];
[~, amount] = size(noteTexts); % 获取音符数量
for i = 1 : amount
noteText = noteTexts{i};
if strcmp(noteText(1),'0') == 1 % 音符文本的第一个字符是0表示为休止符
noteLength = str2double(noteText(2));
switch noteLength
case 1
note = zeros(1, 22051); % 01 = 休止 1/2 秒
case 2
note = zeros(1, 11026); % 02 = 休止 1/4 秒
case 3
note = zeros(1, 5513); % 03 = 休止 1/4 秒
end
else
scale = noteText(1:end-2); % 如果不是休止符则第一个字符到倒数第三个字符为音阶
degree = str2double(noteText(end-1:end-1)); % 倒数第二个字符为音度
noteLength = str2double(noteText(end:end)); % 最后一个字符为音符长度,含义与休止符最后一个字符相同
equMartix = strcmpi(scaleTable, scale); % 下面一个For循环用于查找音阶在音阶表中的位置
for scaleNum = 1 : 12
if equMartix(scaleNum) == 1
break
end
end
note = [];
note(1,:) = allNotes{noteLength}(scaleNum, degree,:); % note 为查表获得的音频信号
end
if isempty(music) % 将所有的音频信号连接起来。
music = note;
else
music = [music(:,:) note];
end
end
music = music/max(music); % 归一化
end