
目錄
零、總腳本:
一、核心功能:交互模塊拆解
二、核心價值
一、初始化腳本:參數配置與啟動界面
①廢話不說,直接上代碼
②代碼模塊拆解與詳細解讀
(一)基礎參數定義模塊:搭建古琴音準與演奏邏輯框架
1. 音高基礎參數:定調與空弦音初始化
2. 演奏技法參數:定義左右手動作邏輯
3. 徽位參數:還原古琴 13 徽的物理特性
(二)可視化界面繪制模塊:1:1 還原古琴視覺結構
1. 窗口初始化:適配屏幕尺寸的比例設計
2. 琴弦繪制:還原 7 根弦的位置與粗細差異
3. 徽位繪制:13 徽的位置與尺寸精準還原
4. 輔助設置:關閉坐標軸,專注視覺體驗
(三)預留功能模塊:為后續交互與音頻合成鋪路
二、演奏音符函數
①廢話不說,先上代碼
②核心輸入參數與作用
核心邏輯:從參數到頻率的計算
音頻生成與播放流程
核心功能總結
三、撥弦發聲函數
核心功能與輸入輸出
工作原理:從諧波疊加到真實音色
關鍵設計:貼近真實弦樂器的特性
三、其他函數
零、總腳本:
%%
run('ini.m');%初始化
%guqin_gui();
function guqin_gui()% 初始化狀態:按音狀態為 1,泛音狀態為 0playing_mode = 1; mode_text = uicontrol('Style','text','String','按音','Position',[10,window_height-50,100,30],'FontSize',12,'ForegroundColor','white');arrow_left = uicontrol('Style','pushbutton','String','<','Position',[10,window_height-100,30,30],'Callback',@arrow_left_callback);arrow_right = uicontrol('Style','pushbutton','String','>','Position',[50,window_height-100,30,30],'Callback',@arrow_right_callback);pressed_mic = zeros(7,1); % 存儲每根弦的按音徽位for i = 1:7pressed_mic(i) = 0; % 初始化為散音end% 為小鍵盤按鍵添加回調函數set(f, 'KeyPressFcn', @key_press_callback);function key_press_callback(hObject, event)% 獲取按鍵的字符key = event.Keyswitch keycase '1'play_note(1);case '2'play_note(2);case '3'play_note(3);case '4'play_note(4);case '5'play_note(5);case '6'play_note(6);case '7'play_note(7);case '0'% 散音for i = 1:7pressed_mic(i) = 0;endupdate_display();case 'numpad1'pressed_mic(1) = 1;update_display();case 'numpad2'pressed_mic(2) = 2;update_display();case 'numpad3'pressed_mic(3) = 3;update_display();case 'numpad4'pressed_mic(4) = 4;update_display();case 'numpad5'pressed_mic(5) = 5;update_display();case 'numpad6'pressed_mic(6) = 6;update_display();case 'numpad7'pressed_mic(7) = 7;update_display();case 'numpad8'pressed_mic(1) = 8;update_display();case 'numpad9'pressed_mic(2) = 9;update_display();case 'add'pressed_mic(3) = 10;update_display();case 'divide'pressed_mic(4) = 11;update_display();case 'multiply'pressed_mic(5) = 12;update_display();case 'subtract'pressed_mic(6) = 13;update_display();% 'numlock' 'add'return' 'decimal' 'numpad0'case ' 'if playing_mode == 1playing_mode = 0;set(mode_text,'String','泛音');elseplaying_mode = 1;set(mode_text,'String','按音');endendendfunction arrow_left_callback(hObject, event)% 向左移動按音位置for i = 1:7if pressed_mic(i) > 0pressed_mic(i) = max(pressed_mic(i)-1,0);endendupdate_display();endfunction arrow_right_callback(hObject, event)% 向右移動按音位置for i = 1:7if playing_mode == 1 && pressed_mic(i) < 13pressed_mic(i) = min(pressed_mic(i)+1,13);endendupdate_display();endfunction update_display()for i = 1:7if playing_mode == 1if pressed_mic(i) == 0text(0.7, y_right_pos(i), '散音', 'FontSize', 10, 'Color', 'white');elsetext(0.7, y_right_pos(i), num2str(pressed_mic(i)), 'FontSize', 10, 'Color', 'white');endelsetext(0.7, y_right_pos(i), '泛音', 'FontSize', 10, 'Color', 'white');endendend% 音高與頻率的變換函數
% freq=fr_of(note,octave)
% [note_str, octave,cent_offset] = nt_of(freq)end
這是古琴模擬系統的交互控制中樞,負責搭建可視化操作界面、綁定鍵盤 / 按鈕事件,實現 “演奏模式切換、徽位選擇、音位顯示” 的完整交互邏輯,連接虛擬界面與發聲功能。
核心功能:交互模塊拆解
-
初始化交互狀態
- 默認
playing_mode=1
(按音模式),界面顯示 “按音” 文本; pressed_mic
數組存儲 7 根弦的當前按音徽位(初始 0,即散音)。
- 默認
-
界面控件與事件綁定
- 模式切換:空格鍵切換 “按音 / 泛音”,同步更新界面文本;
- 徽位調整:左右箭頭按鈕整體增減所有弦的徽位(限制 1-13);
- 鍵盤控制:
- 主鍵盤 1-7:觸發對應弦發聲;
- 小鍵盤 1-9/+-*/:設置對應弦的徽位(1-13);
- 0 鍵:重置所有弦為散音。
-
狀態顯示更新
update_display
函數實時在界面標注每根弦的狀態:按音時顯示 “散音” 或徽位數字,泛音時顯示 “泛音”,確保用戶直觀掌握當前演奏參數。
作為 “用戶操作” 與 “play_note
發聲函數” 的中間層,它將鍵盤 / 按鈕輸入轉化為具體的 “弦號、演奏模式、徽位” 參數,實現 “所見即所控、所控即所發” 的虛擬演奏體驗。
一、初始化腳本:參數配置與啟動界面
①廢話不說,直接上代碼
以下算作初始化代碼,創建腳本文件,命名為“ini.m”并存儲。
%演奏方式:泛音、按音對應0和1
%徽位:1-13,0為不按
% 初始化音名和八度 %弦號:從低音到高音分別是1234567
%正宮調定弦
notes = {'C', 'D', 'F', 'G', 'A', 'C', 'D'};
octaves = [3, 3, 3, 3, 3, 4, 4];
% 徽位與琴弦右側長度倍數關系的數據表
mic_positions = [1/8; 1/6; 1/5; 1/4; 1/3; 2/5; 1/2; 3/5; 2/3; 3/4; 4/5; 5/6; 7/8];
%徽位標準的大小
mic_size = [1/8 1/6 1/5 1/4 1/3 1/5 1/2 1/5 1/3 1/4 1/5 1/6 1/8];
% 七條琴弦粗細
line_widths = [3.2 3 2.6 2.3 2 1.6 1.3];
%指:(左手:取音法,右手:撥弦法
boxian=['挑','勾','抹','托','剔','劈','打','帶','艸如一','撮','歷','','','','','',''];
quyin=['大','食','中','名','跪','艸'];%散音,指左手不取音,默認空弦音
%另,上下進退復屬于位置變更,
type=['按','泛'];%按音,泛音。延音?
marker_num=['一','二','三','四','五','六','七','八','九','十','十一','十二','十三'];
marker_tenth=['一','二','三','四','五','六','七','八','九','十'];
%初始七根弦的空弦音頻率數組
base_freq=[0 0 0 0 0 0 0];
for i=1:7base_freq(i)=fr_of(notes{i},octaves(i));%計算填充
end% num_harmonics: 諧波數量% harmonic_amps: 諧波振幅比例,長度為 num_harmonics 的向量% harmonic_phases: 諧波相位,長度為 num_harmonics 的向量% decay_factor: 衰減系數,范圍在 0 到 1 之間% duration: 聲音持續時間% 生成時間向量 % 生成面板琴弦和徽位% 獲取屏幕的大小screen_size = get(0,'ScreenSize');screen_width = screen_size(3); % 屏幕的寬度screen_height = screen_size(4); % 屏幕的高度% 計算窗口的高度,根據 125:22 的長寬比aspect_ratio = 125 / 22; window_height = screen_width / aspect_ratio;% 確保窗口的高度不超過屏幕的高度,如果超過,則根據屏幕高度計算窗口寬度if window_height > screen_heightwindow_height = screen_height;window_width = window_height * aspect_ratio;elsewindow_width = screen_width;end% 創建圖形窗口,使其寬度充滿屏幕或高度達到屏幕高度(取決于哪個先滿足條件)f = figure('Position', [0, 0, window_width, window_height]);%寬:長=125:22% 設置窗口背景顏色為棕黑色set(f, 'Color', [0.2 0.1 0]); % 關閉菜單和工具欄set(f, 'MenuBar', 'none', 'ToolBar', 'none');% 繪制七條琴弦y_pos = linspace(0.9, 0.1, 7); % 七條琴弦的 y 位置x_start = 0.1;x_end = 0.9;width = 0.8; % 琴弦橫向填充整個窗口height = width / aspect_ratio; y_pos = 0.5 + (y_pos - mean(y_pos)) * height / (max(y_pos) - min(y_pos));y_left_pos = 0.5 + (y_pos - 0.5) * 0.6;y_right_pos = 0.5 +(y_pos - 0.5) * 1.0;% 存儲琴弦的線條句柄line_handles = zeros(1, 7);for i = 1:7line_handles(i) = plot([x_start, x_end], [y_left_pos(i), y_right_pos(i)], 'LineWidth', line_widths(i), 'Color', [1 1 0.8]); % 亮黃白色的琴弦text(0.9, y_right_pos(i), num2str(i), 'FontSize', 12, 'Color', 'white'); % 弦號白色hold on;end % 繪制徽位for j = 1:13x_mic = x_start + (x_end - x_start) * mic_positions(j);size=round(20.*mic_size(j));plot(x_mic, (y_right_pos(1)-y_left_pos(1))/(x_end-x_start)*(x_mic-y_left_pos(1))+y_pos(1)-0.5*(y_pos(2)-y_pos(1)), 'wo', 'MarkerSize',size, 'MarkerFaceColor', [240, 180, 50]./256);hold on;end %每根弦上,任意位置點的繪制%l代表有效長度,即該點到琴弦右側的距離占總琴弦長度的比例
% for i = 1:7
% k=(y_left_pos(i)- y_right_pos(i))/(x_start-x_end);%琴弦的斜率
% for j = 1:13
% x_mic = x_start + (x_end - x_start) * mic_positions(j);
% l=mic_positions(14-j);
% y=-k*l*(x_end - x_start)+y_right_pos(i);
% plot(x_mic, y, 'ro', 'MarkerSize',line_widths(i)+2, 'MarkerFaceColor', 'r');
% hold on;
% end
% endaxis off; % 關閉坐標軸顯示
這段 MATLAB 代碼是一套古琴數字化模擬系統的基礎模塊,主要實現兩大核心功能:一是通過計算初始化古琴 7 根琴弦的空弦音頻率,定義泛音、按音等演奏參數;二是構建可視化的古琴虛擬界面,精準還原古琴的琴弦、徽位等物理結構,為后續的交互演奏(如點擊徽位發聲、模擬按弦動作)提供視覺載體。整體代碼邏輯從 “音頻參數定義” 到 “視覺界面繪制” 層層遞進,是古琴數字化仿真項目的核心奠基代碼。
②代碼模塊拆解與詳細解讀
(一)基礎參數定義模塊:搭建古琴音準與演奏邏輯框架
這部分代碼是整個系統的 “數據字典”,從音高、演奏技法、徽位尺寸等維度,為古琴模擬建立標準化參數體系,具體分為 6 類核心參數:
1. 音高基礎參數:定調與空弦音初始化
% 正宮調定弦(古琴經典調式,對應現代音高)
notes = {'C', 'D', 'F', 'G', 'A', 'C', 'D'}; % 7根弦的音名(從低音弦1到高音弦7)
octaves = [3, 3, 3, 3, 3, 4, 4]; % 對應八度(弦1-5為小字三組,弦6-7為小字四組)
% 初始化空弦音頻率數組(調用fr_of函數計算音名對應的頻率,如C3約130.81Hz)
base_freq=[0 0 0 0 0 0 0];
for i=1:7base_freq(i)=fr_of(notes{i},octaves(i));% 循環填充7根弦的空弦頻率
end
- 關鍵作用:確定古琴的 “基準音高”,
base_freq
數組最終存儲的是每根空弦音的具體頻率(如弦 1 為 C3,頻率約 130.81Hz),是后續計算泛音、按音頻率的基礎。 - 依賴函數:
fr_of
是自定義的 “音名轉頻率” 函數,核心是根據十二平均律,將 “音名 + 八度” 轉化為具體的赫茲值(Hz)。
2. 演奏技法參數:定義左右手動作邏輯
% 右手撥弦法(共12種經典技法,空值為預留擴展位)
boxian=['挑','勾','抹','托','剔','劈','打','帶','艸如一','撮','歷','','','','','',''];
% 左手取音法(含散音邏輯:左手不取音時默認空弦音)
quyin=['大','食','中','名','跪','艸'];% “大/食/中/名/跪”對應不同手指按弦,“艸”為散音標識
% 演奏類型(核心兩類:按音、泛音;延音為預留功能)
type=['按','泛'];
- 設計邏輯:為后續 “交互演奏” 預留接口 —— 比如用戶點擊界面時,系統可根據選擇的 “右手技法(如挑)” 和 “左手取音法(如食指按弦)”,調用不同的音頻合成邏輯(如按音有按壓阻尼感,泛音更清亮)。
3. 徽位參數:還原古琴 13 徽的物理特性
% 徽位與琴弦右側長度倍數關系(核心物理參數)
mic_positions = [1/8; 1/6; 1/5; 1/4; 1/3; 2/5; 1/2; 3/5; 2/3; 3/4; 4/5; 5/6; 7/8];
% 徽位視覺大小(13徽尺寸隨位置變化,符合真實古琴徽位比例)
mic_size = [1/8 1/6 1/5 1/4 1/3 1/5 1/2 1/5 1/3 1/4 1/5 1/6 1/8];
% 徽位標識文字(1-10用“一-十”,11-13用“十一-十三”)
marker_num=['一','二','三','四','五','六','七','八','九','十','十一','十二','十三'];
marker_tenth=['一','二','三','四','五','六','七','八','九','十'];
- 核心原理:
mic_positions
是關鍵 —— 古琴 13 徽的位置并非均勻分布,而是根據 “弦長比例” 確定(如 7 徽在弦長 1/2 處,是泛音的核心位置),該數組存儲的是 “徽位到琴弦右側的距離占總弦長的比例”,后續繪制徽位和計算泛音頻率都依賴此參數。
(二)可視化界面繪制模塊:1:1 還原古琴視覺結構
這部分代碼是 “虛擬古琴” 的視覺載體,通過計算屏幕尺寸、琴弦位置、徽位坐標,在 MATLAB 圖形窗口中精準繪制古琴外觀,具體分為 4 個核心步驟:
1. 窗口初始化:適配屏幕尺寸的比例設計
% 獲取屏幕分辨率,計算窗口大小(遵循古琴長寬比125:22)
screen_size = get(0,'ScreenSize'); % 獲取屏幕尺寸:[左,下,寬,高]
screen_width = screen_size(3); % 屏幕寬度
screen_height = screen_size(4); % 屏幕高度
aspect_ratio = 125 / 22; % 古琴標準長寬比(長:寬=125:22)
% 計算窗口高度(優先寬充滿屏幕,若高度超屏幕則按高度適配)
window_height = screen_width / aspect_ratio;
if window_height > screen_heightwindow_height = screen_height;window_width = window_height * aspect_ratio;
elsewindow_width = screen_width;
end
% 創建圖形窗口(無菜單/工具欄,背景為棕黑色,模擬古琴木胎顏色)
f = figure('Position', [0, 0, window_width, window_height]);
set(f, 'Color', [0.2 0.1 0]); % 背景色:棕黑色(RGB值)
set(f, 'MenuBar', 'none', 'ToolBar', 'none'); % 關閉菜單和工具欄,專注視覺
- 設計細節:嚴格遵循古琴的真實長寬比(125:22),同時適配不同屏幕分辨率,避免界面拉伸變形;棕黑色背景模擬古琴的木胎質感,提升視覺沉浸感。
2. 琴弦繪制:還原 7 根弦的位置與粗細差異
% 計算7根弦的Y軸位置(從高音弦到低音弦,縱向均勻分布)
y_pos = linspace(0.9, 0.1, 7); % 初始縱向比例(0.9為高音弦,0.1為低音弦)
x_start = 0.1; % 琴弦左端起點(相對窗口寬度的比例,0.1即10%處)
x_end = 0.9; % 琴弦右端終點(90%處)
width = 0.8; % 琴弦橫向長度(占窗口寬度80%)
height = width / aspect_ratio; % 琴弦縱向適配高度
% 調整琴弦Y軸位置,確保居中且適配窗口高度
y_pos = 0.5 + (y_pos - mean(y_pos)) * height / (max(y_pos) - min(y_pos));
y_left_pos = 0.5 + (y_pos - 0.5) * 0.6; % 琴弦左端Y坐標(略微內收,模擬琴首)
y_right_pos = 0.5 +(y_pos - 0.5) * 1.0; % 琴弦右端Y坐標(模擬琴尾)% 循環繪制7根弦(存儲句柄,便于后續交互)
line_handles = zeros(1, 7);
for i = 1:7% 繪制琴弦:亮黃白色(模擬蠶絲弦顏色),粗細隨弦號變化(低音弦粗,高音弦細)line_handles(i) = plot([x_start, x_end], [y_left_pos(i), y_right_pos(i)], ...'LineWidth', line_widths(i), 'Color', [1 1 0.8]);% 在琴弦右端標注弦號(白色文字,1為低音弦,7為高音弦)text(0.9, y_right_pos(i), num2str(i), 'FontSize', 12, 'Color', 'white');hold on; % 保持繪圖窗口,繼續繪制后續元素
end
- 關鍵細節:
- 琴弦粗細:
line_widths = [3.2 3 2.6 2.3 2 1.6 1.3]
,嚴格遵循真實古琴 “低音弦粗、高音弦細” 的規律(弦 1 最粗 3.2pt,弦 7 最細 1.3pt); - 位置設計:琴弦左端(琴首)略微內收(
y_left_pos
乘以 0.6),模擬真實古琴 “琴首窄、琴尾寬” 的形態,視覺更逼真。
- 琴弦粗細:
3. 徽位繪制:13 徽的位置與尺寸精準還原
% 循環繪制13個徽位(從1徽到13徽)
for j = 1:13% 計算徽位X坐標(根據mic_positions的弦長比例,映射到窗口寬度)x_mic = x_start + (x_end - x_start) * mic_positions(j);% 計算徽位視覺大小(按mic_size比例縮放,默認基礎20pt)size=round(20.*mic_size(j));% 繪制徽位:白色外圈+米黃色填充(模擬螺鈿徽位,真實古琴常用材質)plot(x_mic, (y_right_pos(1)-y_left_pos(1))/(x_end-x_start)*(x_mic-y_left_pos(1))+y_pos(1)-0.5*(y_pos(2)-y_pos(1)), ...'wo', 'MarkerSize',size, 'MarkerFaceColor', [240, 180, 50]./256);hold on;
end
- 核心邏輯:
- 徽位位置:通過
x_mic = x_start + (x_end - x_start) * mic_positions(j)
,將 “弦長比例” 轉化為窗口中的實際 X 坐標,確保 13 徽的位置與真實古琴完全一致(如 7 徽在 X 軸中間,對應弦長 1/2 處); - 徽位外觀:白色外圈(
'wo'
即白色圓圈)+ 米黃色填充(RGB [240,180,50]/256),模擬真實古琴的 “螺鈿徽位” 質感,同時尺寸隨mic_size
變化(如 7 徽最大,1、13 徽最小),符合真實古琴的徽位大小規律。
- 徽位位置:通過
4. 輔助設置:關閉坐標軸,專注視覺體驗
axis off; % 關閉坐標軸顯示,避免網格、刻度干擾古琴視覺效果
- 作用:隱藏 MATLAB 默認的坐標軸、刻度和網格,讓虛擬古琴界面更簡潔,視覺上更接近真實樂器。
(三)預留功能模塊:為后續交互與音頻合成鋪路
代碼中注釋掉的部分(如下)和未完善的音頻參數(如諧波、衰減系數),是為后續功能擴展預留的接口。
- 擴展方向:
- 交互功能:解開注釋后,可實現 “點擊徽位顯示紅色按點”,模擬左手按弦動作;
- 音頻合成:結合
base_freq
和徽位比例(如泛音頻率 = 空弦頻率 ×(1 / 徽位比例)),可計算出任意徽位的音高,再通過諧波參數合成對應的古琴音色。
二、演奏音符函數
①廢話不說,先上代碼
function note=play_note(string_num, play_style, fret_num)% string_num:弦號,從低音到高音分別是 1234567,弦號(string_num)為索引,調用 base_freq(string_num)即獲取對應弦的基音頻率% play_style:演奏方式,泛音、按音對應 1 和 2,如果是 1,則音色空靈,隨機生成 num_harmonics: 諧波數量為 2-4;如果是 2 為按音,生成諧波數量 5-7% fret_num:徽位,1-13,假設按 fret_num 徽,% ①泛音模式下,弦的等分段發音,發音頻率為發音弦基頻的為 mic_size(string_num)^(-1)倍,% ②按音模式下,有效弦長發音,即發音弦基頻的為 mic_positions(string_num)^(-1)倍,% 0為不按,發音為基頻,以此獲得 out_freq base_freq=[87.30705785825, 97.99885899543, 109.99943633644, 130.8127826503, 146.8323839587, 164.8137784564, 195.9977179909];%base_freq=[130.812782650299,146.832383958704,174.614115716502,195.997717990875,220,261.625565300599,293.664767917407]./2;% 徽位與琴弦右側長度倍數關系的數據表mic_positions = [1/8; 1/6; 1/5; 1/4; 1/3; 2/5; 1/2; 3/5; 2/3; 3/4; 4/5; 5/6; 7/8];%徽位標準的大小mic_size = [1/8 1/6 1/5 1/4 1/3 1/5 1/2 1/5 1/3 1/4 1/5 1/6 1/8 ];% 初始化 fundamental_freqfundamental_freq = base_freq(string_num); % 根據演奏方式和徽位計算實際基頻%有效弦長fl=floor(fret_num);if fl==0l=(fret_num-fl)/8;elseif fl==13l=7/8+(fret_num-fl)/8;elsel=mic_positions(fl)+(fret_num-fl)*(mic_positions(fl+1)-mic_positions(fl));endif play_style == 2%按音 fundamental_freq = fundamental_freq / l;num_harmonics = randi([2, 4]);elsefundamental_freq = fundamental_freq / l;num_harmonics = randi([5, 7]);end draw_point(string_num,l);note=nt_of(fundamental_freq);harmonic_amps = 1./(1:num_harmonics); % 諧波振幅比例,例如 1/nharmonic_phases = 2*pi*rand(1, num_harmonics); % 諧波相位,隨機生成decay_factor = -1.5.*sqrt((1:num_harmonics)); % 衰減系數duration = 5; % 持續時間,單位為秒Fs = 44100; % 采樣頻率 % 調用 string_pluck 函數生成弦樂撥弦的聲音[y, ~] = string_pluck(fundamental_freq, num_harmonics, harmonic_amps, harmonic_phases, decay_factor, duration, Fs);% 播放生成的聲音player = audioplayer(y, Fs);% 播放音頻play(player);% 等待 2 秒后停止pause(1);stop(player);
end% % 繪制生成的音頻信號的時域波形
% figure;
% plot(t, y);
% title('弦樂撥弦的時域波形');
% xlabel('時間 (s)');
% ylabel('幅度');
% grid on;
該函數是古琴數字化模擬系統的 “發聲控制核心”,作用是根據用戶輸入的 “弦號、演奏方式、徽位”,計算對應音高并生成、播放古琴音色,本質是 “參數映射→頻率計算→音頻合成→播放輸出” 的完整流程。
②核心輸入參數與作用
函數接收 3 個關鍵參數,直接決定發聲的 “弦、法、位”,對應真實古琴演奏的核心要素:
- string_num(弦號):1-7(1 為低音弦,7 為高音弦),用于調用預設的
base_freq
數組,獲取對應弦的空弦基頻(如弦 1 基頻約 87.31Hz); - play_style(演奏方式):1 = 泛音、2 = 按音,決定音色特點(泛音空靈、按音厚重)與諧波數量(泛音 2-4 個諧波,按音 5-7 個諧波);
- fret_num(徽位):0(空弦)或 1-13(具體徽位),用于計算 “有效弦長比例”,最終確定實際發聲頻率。
核心邏輯:從參數到頻率的計算
函數最關鍵的步驟是 “根據徽位和演奏方式,把空弦基頻轉化為實際發聲頻率”,核心是 “有效弦長比例” 的計算:
- 徽位插值計算(l 值):
當fret_num
不是整數(如 1.5,代表 1 徽與 2 徽之間)時,通過 “線性插值” 計算精準的 “有效弦長比例 l”—— 空弦(fret_num=0)時 l=1/8,13 徽以上時 l=7/8 + 插值,1-13 徽間則基于mic_positions
(徽位弦長比例表)計算,確保任意位置的音高都精準; - 頻率換算:
無論是泛音還是按音,均通過 “空弦基頻 ÷ 有效弦長比例 l” 得到實際發聲頻率(原理:弦長越短,頻率越高,符合物理規律),僅通過 “諧波數量” 區分兩種演奏方式的音色(泛音諧波少更空靈,按音諧波多更厚重)。
音頻生成與播放流程
頻率確定后,函數完成 “音色合成→播放” 的閉環:
- 音色參數配置:
自動生成諧波相關參數 —— 振幅按 “1/n” 規律衰減(n 為諧波序號,符合真實弦振規律)、相位隨機(避免音色單調)、衰減系數隨諧波序號增大而減小(高頻諧波先衰減,模擬古琴余音特點); - 音頻合成與播放:
調用string_pluck
函數(弦振音頻合成函數),基于上述參數生成時域音頻信號;再通過audioplayer
創建播放器,播放音頻(默認持續 1 秒后停止,避免余音過長干擾后續操作); - 視覺聯動:
調用draw_point
函數,在虛擬古琴界面上標記當前演奏的 “弦 - 徽位置”(如弦 3 的 5 徽處畫點),實現 “聽覺 + 視覺” 的同步反饋。
核心功能總結
該函數的價值在于 “把抽象的演奏指令(弦、法、位)轉化為可聽的古琴聲音”,既還原了真實古琴的物理發聲規律(弦長與頻率的關系、泛 / 按音的音色差異),又通過參數化設計實現了 “任意弦、任意徽、任意演奏方式” 的靈活發聲,是連接 “虛擬界面交互” 與 “音頻輸出” 的關鍵橋梁。
三、撥弦發聲函數
function [y, t] = string_pluck(fundamental_freq, num_harmonics, harmonic_amps, harmonic_phases, decay_factor, duration, Fs)% 輸入參數:% fundamental_freq: 基頻% num_harmonics: 諧波數量% harmonic_amps: 諧波振幅比例,長度為 num_harmonics 的向量% harmonic_phases: 諧波相位,長度為 num_harmonics 的向量% decay_factor: 衰減系數,范圍在 0 到 1 之間% duration: 聲音持續時間% Fs: 采樣頻率 % 生成時間向量t = 0:1/Fs:duration-1/Fs;y = zeros(size(t));% 生成弦樂撥弦的音色for k = 1:num_harmonicsfreq = k * fundamental_freq;amp = harmonic_amps(k);phase = harmonic_phases(k);decay=decay_factor(k);% 生成諧波分量y_harmonic = amp * sin(2*pi*freq*t + phase);% 應用衰減y_harmonic = y_harmonic.* exp(decay*t);% 疊加諧波分量y = y + y_harmonic;end
end
string_pluck
函數是古琴(或其他弦樂器)數字化模擬系統中的 “音色合成引擎”,其核心功能是根據輸入的聲學參數,生成具有真實弦振特性的音頻信號。它通過數學建模的方式,模擬弦樂器被撥奏后的振動規律,最終輸出可播放的時域音頻數據。
核心功能與輸入輸出
- 核心作用:將抽象的聲學參數(基頻、諧波、衰減等)轉化為具體的音頻信號,模擬弦樂器撥弦發聲的物理過程。
- 輸入參數:包含 7 個關鍵參數,全面定義了聲音的基本特征:
fundamental_freq
:基頻(決定音高的核心參數)num_harmonics
:諧波數量(決定音色豐富度)harmonic_amps
:諧波振幅比例(決定音色特點)harmonic_phases
:諧波相位(影響音色細節)decay_factor
:衰減系數(決定聲音的持續特性)duration
:聲音持續時間(單位:秒)Fs
:采樣頻率(標準為 44100Hz,即音頻的 “分辨率”)
- 輸出結果:
y
:生成的音頻信號(時域波形數據)t
:對應的時間向量(與音頻信號同步的時間軸)
工作原理:從諧波疊加到真實音色
函數采用 “多諧波疊加” 的經典弦樂合成方法,模擬真實弦振動的物理特性,核心步驟分為 3 步:
-
時間向量生成
首先創建與音頻時長匹配的時間軸?t
,計算公式為?t = 0:1/Fs:duration-1/Fs
,確保每個采樣點都對應精確的時間位置(例如 44100Hz 采樣率下,每個點間隔約 0.0000227 秒)。 -
諧波分量計算
循環生成每個諧波的振動波形(共?num_harmonics
?個):- 每個諧波的頻率為 “基頻的整數倍”(
freq = k * fundamental_freq
),符合弦振動的物理規律(弦振動會同時產生基頻和整數倍諧波); - 每個諧波的振幅由?
harmonic_amps(k)
?決定(通常高頻諧波振幅更低); - 每個諧波的初始相位由?
harmonic_phases(k)
?控制(隨機相位可避免不同諧波間的固定干涉,使音色更自然)。
- 每個諧波的頻率為 “基頻的整數倍”(
-
衰減與疊加
- 對每個諧波分量應用衰減:
y_harmonic = y_harmonic .* exp(decay * t)
,模擬真實弦振動的能量損耗(高頻諧波通常衰減更快); - 將所有諧波分量疊加:
y = y + y_harmonic
,最終形成完整的弦樂音色波形。
- 對每個諧波分量應用衰減:
關鍵設計:貼近真實弦樂器的特性
- 諧波結構:通過整數倍諧波模擬弦的振動模式,符合弦樂器 “基頻 + 泛音” 的頻譜特點;
- 振幅衰減:使用指數衰減函數?
exp(decay * t)
?模擬弦振動的能量耗散,使聲音有 “起音 - 持續 - 衰減” 的自然過程; - 參數靈活性:通過調整諧波數量、振幅比例和衰減系數,可模擬不同弦樂器(古琴、吉他、小提琴等)或同一種樂器的不同演奏技法(如古琴的泛音、按音)。
string_pluck
函數是連接 “聲學參數” 與 “可聽聲音” 的關鍵模塊,它通過數學建模的方式,精準復現了弦樂器發聲的物理原理。在古琴模擬系統中,它接收play_note
函數傳遞的音高和演奏方式參數,最終生成符合古琴音色特點的音頻信號,為虛擬演奏提供了核心的聲音輸出能力。
三、其他函數
function draw_point(i, l)% 輸入參數:% i: 琴弦弦號,從 1 到 7 的整數% l: 有效弦長,表示按音位置% 假設 x_start 和 x_end 是徽位圖的起始和結束 x 坐標,y_left_pos 和 y_right_pos 是左右位置數組,line_widths 是線寬數組x_start = 0.1; x_end = 0.9;y_left_pos = [0.54224,0.52816,0.51408,0.50000,0.48592,0.47184,0.45776];y_right_pos = [[0.57040,0.54693,0.52346,0.50000,0.47653,0.45306,0.42960]];line_widths = [3.2 3 2.6 2.3 2 1.6 1.3];
% % 生成一個唯一的標記用于刪除
% marker_id = ['marker_', num2str(i), '_', num2str(l)]
% % 檢查是否存在之前繪制的標記,如果存在則刪除
% if ishandle(findobj('Tag', marker_id))
% delete(findobj('Tag', marker_id));
% end % 生成一個唯一的標記用于刪除marker_id = 'marker_'; % 檢查是否存在之前繪制的標記,存在則刪delete(findobj('Tag', marker_id)); % 繪制徽位圖上的點h = plot(x_end - l*(x_end - x_start), (y_left_pos(i)- y_right_pos(i))*l+y_right_pos(i), 'ro', 'MarkerSize',line_widths(i)+2, 'MarkerFaceColor', 'r');% 為當前繪制的點設置標記set(h, 'Tag', marker_id);hold on;
end
% 讀取 new.wav 文件
[audio, Fs] = audioread('new.wav');% 計算時頻圖
window = hamming(256); % 使用漢明窗,窗口長度為 256
noverlap = 128; % 窗口重疊長度為 128
nfft = 512; % FFT 點數為 512% 使用 spectrogram 函數計算并繪制時頻圖
spectrogram(audio, window, noverlap, nfft, Fs, 'yaxis');% 添加標題和軸標簽
title('Time-Frequency Spectrogram of new.wav');
xlabel('Time (s)');
ylabel('Frequency (Hz)');% 添加顏色條
colorbar;
function freq = fr_of(note, octave)% 十二平均律中相鄰半音的頻率比semitone_ratio = 2^(1/12);% 以 A4 = 440Hz 為基準base_A4 = 440;% 計算 A 在不同八度的頻率base_A = base_A4 * 2^(octave - 4);switch notecase 'C'freq = base_A / (semitone_ratio)^9;case 'Db' % 將 bD 等同于 #Ccase '#C'freq = base_A / (semitone_ratio)^8;case 'D'freq = base_A / (semitone_ratio)^7;case 'Eb' % 將 bE 等同于 #Dcase '#D'freq = base_A / (semitone_ratio)^6;case 'E'freq = base_A / (semitone_ratio)^5;case 'F'freq = base_A / (semitone_ratio)^4;case 'Gb' % 將 bG 等同于 #Fcase '#F'freq = base_A / (semitone_ratio)^3;case 'G'freq = base_A / (semitone_ratio)^2;case 'Ab' % 將 bA 等同于 #Gcase '#G'freq = base_A / (semitone_ratio)^1;case 'A'freq = base_A;case 'Bb' % 將 bB 等同于 #Acase '#A'freq = base_A * semitone_ratio;case 'B'freq = base_A * (semitone_ratio)^2;otherwiseerror('Invalid note name');endend
function [marker, tenth] = l_to_marker(l)% l: 等效弦長 % 徽位位置(距離琴弦右側的有效長度為)% 徽位與琴弦右側長度倍數關系的數據表mic_positions = [1/8; 1/6; 1/5; 1/4; 1/3; 2/5; 1/2; 3/5; 2/3; 3/4; 4/5; 5/6; 7/8];marker_num=['一','二','三','四','五','六','七','八','九','十','十一','十二','十三'];marker_tenth=['一','二','三','四','五','六','七','八','九']; % 找到徽位index = find(l < mic_positions, 1, 'first')-1;if isempty(index)index=13;%只有13徽讀取不到。marker='十三';tenth_index = round((l - mic_positions(index))* 80);elseif index == 0marker='零';% 對于一徽之內,特殊處理,分為十等份tenth_index= round(l*80);else marker=marker_num(index);tenth_index = round((l - mic_positions(index)) / (mic_positions(index+1) - mic_positions(index)) * 10);endif tenth_index == 0tenth = '整';elseif tenth_index ==10tenth = '整';marker=marker_num(index+1);elsetenth = marker_tenth(tenth_index);end
end
function [noteout, note_str, octave, cent_offset] = nt_of(freq)% 十二平均律中相鄰半音的頻率比base_A4 = 440;octave = 0;% 將頻率調整到 A4 所在的八度范圍while freq < base_A4 / 2freq = freq * 2;octave = octave - 1;endwhile freq >= base_A4 * 2freq = freq / 2;octave = octave + 1;end% 計算相對于 A4 的音分數cents = 1200 * log2(freq / base_A4);% 計算半音數semitones = round(cents / 100); % 計算音分偏移量cent_offset = mod(cents, 100);if cent_offset >= 50cent_offset = cent_offset - 100;endif cent_offset <= -50cent_offset = cent_offset + 100;end% 處理八度變化if semitones >= 12octave = octave + floor(semitones / 12);semitones = mod(semitones, 12);elseif semitones < 0octave = octave + floor(semitones / 12);semitones = mod(semitones, 12);endswitch semitonescase 0note = 'A';case 1note = 'A#';case 2note = 'B';case 3note = 'C';case 4note = 'C#';case 5note = 'D';case 6note = 'D#';case 7note = 'E';case 8note = 'F';case 9note = 'F#';case 10note = 'G';case 11note = 'G#';endnote_str = note;octave = octave + 4; % 將八度調整到實際的音樂八度范圍if cent_offset >= 0noteout = [note_str, num2str(octave), '+', num2str(abs(cent_offset))];elsenoteout = [note_str, num2str(octave), '-', num2str(abs(cent_offset))];endend
function [note_out, badu]=num2note(num,octave,key)
%key調:輸入1234567
% CDEFGAB
% 1.5是#C或bD;2.5是#D或bC;4.5是#F或bG;5.5是#G或bA;6.5是#A或bB
%唱名到音名的轉換
if (key>6.4 || key<2.1)badu=octave;%和C調一樣
elsebadu=octave+1;
end
switch numcase 1num_offset=0;%Ccase 1.5num_offset=1;%C#case 2num_offset=2;%Dcase 2.5num_offset=3;%D#case 3num_offset=4;case 4num_offset=5;case 4.5num_offset=6;case 5num_offset=7;case 5.5num_offset=8;case 6num_offset=9;case 6.5num_offset=10;case 7num_offset=11;
end
num=num_offset;
switch keycase 1offset=0;%Ccase 1.5offset=1;%C#case 2offset=2;%Dcase 2.5offset=3;%D#case 3offset=4;case 4offset=5;case 4.5offset=6;case 5offset=7;case 5.5offset=8;case 6offset=9;case 6.5offset=10;case 7offset=11;
end
key=offset;
pitch=key+num;
badu_add=floor(pitch/12);
seminote=pitch - badu_add;switch seminote+3case 12note = 'A';case 13note = 'A#';case 14note = 'B';case 3note = 'C';case 4note = 'C#';case 5note = 'D';case 6note = 'D#';case 7note = 'E';case 8note = 'F';case 9note = 'F#';case 10note = 'G';case 11note = 'G#';otherwiseerror('Invalid key name'); endnote_out=note;
end
將所有函數和腳本文件按照對應名稱存好放置于同一文件夾即可運行,快來試試你的賽博樂器吧~