我的第一次唠叨
我为音乐 GUI 制作了一个存根,如下所示。
到目前为止的结果:
我采取的方法如下:
- 上网查找(免版税)高音谱号、低音谱号和支撑的 SVG 图像。
- 不幸的是,MATLAB 不支持矢量图形。因此,将这些 SVG 转换为相当高分辨率的 PNG。
- 在 MATLAB 图形中重新缩放和定位这些图像。
在下面包括我的存根,它显示了我将采取的总体策略。基本上,我会对所有固定的音符、休止符、升号等符号使用相同的方法。为符号查找/创建图像,将其加载到 MATLAB 中,并在需要的位置和时间进行缩放/显示。
当然,你必须有点创意。例如,您不需要 128次休息的图像;只需将第 8个其余部分复制 3 次,并将每个副本偏移一点。另一个例子:将单个 8 分音符的图像在其头部和尾部分开。它的头等于四分音符,只有在同一和弦上的最后一个音符被画出后,它的尾才被画出。
对于绘制非固定符号(想想连线、连接 ≤ 8 分音符组的线等),您必须做出决定:
- 手动绘制它们。您必须对所有内置的绘图功能
line
有创意。patch
可能相当耗时。
- 使用图像,并
imtransform
与朋友一起发挥创意。好处是结果可能看起来更好,并且可能会花费更少的时间。缺点是这些是图像处理工具箱的一部分,并非所有用户都可以访问。
- 真正最好的:检测用户是否有图像处理工具箱(使用
ver('images')
)。如果是,请使用选项 2。如果不是,请使用选项 1。
另外的选择
另一种选择是使用专门的音乐字体。音乐字体将字符映射到音乐符号。好的字体是可矢量化的,这意味着它们可以任意放大或缩小而不会损失质量。这消除了对许多图像的需求。您只需要担心将正确的字符映射到所需的符号以及字符的位置。
但是,对于和弦和非固定符号之类的东西,您仍然必须使用图像。
存根
但现在,这是我使用的存根:
classdef SheetMusicGui < handle
properties (Access = private)
f % Figure handle
h % Axes handle
% Coordinates of the box
topLineY = 0.8;
bottomLineY = 0.2;
leftLineX = 0.1;
maxX = 3;
end
methods
function obj = SheetMusicGui(varargin)
% Initialize figure and axis
obj.f = figure; clf, hold on
set(obj.f,...
'position', [400 400 900 300]);
obj.h = gca;
set(obj.h, ...
'box', 'on',...
'xtick',[], 'xticklabel',[],...
'ytick',[], 'yticklabel',[]);
% Draw the grande staff
obj.drawGrandeStaff(obj.h);
% Make sure all measurements work out
axis([0 3 0 1]);
end
end
methods (Access = private)
%% The Basics
function [h] = drawGrandeStaff(obj, h)
% First bar line
line([obj.leftLineX obj.leftLineX], [obj.bottomLineY obj.topLineY], 'color', 'k');
% Brace
img = imread('brace.png');
X = size(img,2); xExtent = 0.068;
Y = size(img,1); yExtent = xExtent/X*Y;
C = imagesc([0.01 0.01+xExtent],[0.8 0.8-yExtent],img, 'parent',obj.h);
uistack(C, 'bottom'); % At the bottom prevents transparency issues
% Treble staff
line(...
repmat([obj.leftLineX; obj.maxX],1,5), ...
repmat(linspace(obj.topLineY, obj.topLineY-0.15, 5), 2,1), 'color','k')
% Treble clef
obj.drawTrebleClef(obj.leftLineX+0.04, obj.topLineY+0.05);
% Bass staff
line(...
repmat([obj.leftLineX; obj.maxX],1,5),...
repmat(linspace(obj.bottomLineY, obj.bottomLineY+0.15, 5), 2,1), 'color','k')
% Bass clef
obj.drawBassClef(obj.leftLineX+0.04, obj.bottomLineY+0.155);
end
% Draw a G clef at location X,Y
function C = drawTrebleClef(obj, x, y)
persistent img
if isempty(img)
img = imread('GClef.png'); end
% Scale image
X = size(img,2); xExtent = 0.1;
Y = size(img,1); yExtent = xExtent/X*Y;
% Plage image
C = imagesc([x x+xExtent],[y y-yExtent],img, 'parent',obj.h);
uistack(C, 'bottom'); % At the bottom prevents transparency issues
end
% Draw an F clef at location X,Y
function C = drawBassClef(obj, x, y)
persistent img
if isempty(img)
img = imread('FClef.png'); end
% Scale image
X = size(img,2); xExtent = 0.12;
Y = size(img,1); yExtent = xExtent/X*Y;
% Plage image
C = imagesc([x x+xExtent],[y y-yExtent],img, 'parent',obj.h);
uistack(C, 'bottom'); % At the bottom prevents transparency issues
end
function T = drawTime(obj, varargin)
% TODO
% NOTE: you had best use some pre-defined times, like common time,
% 2/2, 3/4, 6/8, etc. It looks much nicer. Only use text() when a
% non-common time is encountered.
end
function K = drawKey(obj, varargin)
% TODO
% NOTE: use sharp/flat below
end
%% Sharps, flats
function T = drawSharp(obj, varargin)
% TODO
end
function T = drawDoubleSharp(obj, varargin)
% TODO
end
function T = drawFlat(obj, varargin)
% TODO
end
function T = drawDoubleFlat(obj, varargin)
% TODO
end
function T = drawNatural(obj, varargin)
% TODO
end
%% Bars
function B = drawBar(obj, varargin)
% TODO
end
function R = drawBeginRepeat(obj, varargin)
% TODO
end
function R = drawEndRepeat(obj, varargin)
% TODO
end
%% Pedalling
function P = drawBeginPedal(obj, varargin)
% TODO
end
function P = drawEndPedal(obj, varargin)
% TODO
end
%% Slurs & lines
function S = drawSlur(obj, varargin)
% TODO
% NOTE: You'll have to do this one by hand
end
function P = drawGlissando(obj, varargin)
% TODO
end
%% Multi-measure rest
function R = drawMultiMeasureRest(obj, varargin)
% TODO
end
%% Whole note/rest
function N = drawWholeNote(obj, pitch, varargin)
% TODO
% Possibly transform note
N = obj.transformNote(N, varargin{:});
end
function R = drawWholeRest(obj, varargin)
% TODO
% Possibly transform rest
N = obj.transformNote(N, varargin{:});
end
%% Half note/rest
function N = drawHalfNote(obj, varargin)
% TODO
% Possibly transform note
N = obj.transformNote(N, varargin{:});
end
function R = drawHalfRest(obj, varargin)
% TODO
% Possibly transform rest
N = obj.transformNote(N, varargin{:});
end
%% Quarter note/rest
function N = drawQuarterNote(obj, varargin)
% TODO
% Possibly transform note
N = obj.transformNote(N, varargin{:});
end
function R = drawQuarterRest(obj, varargin)
% TODO
% Possibly transform rest
N = obj.transformNote(N, varargin{:});
end
%% 8th, 16th, 32nd, 64th, ...
function N = drawSingleShortNote(obj, type, varargin)
% TODO
% NOTE: all short notes have a different number of tails. Just store
% the tail as a separate figure, and copy however many times needed.
% Use the quarter note for the head.
% Possibly transform rest
N = obj.transformNote(N, varargin{:});
end
function R = drawShortRest(obj, type, varargin)
% TODO
% NOTE: all short rests are just copies of the eighth rest, so you can
% juse load one image and copy the desired number of times.
% Possibly transform rest
N = obj.transformNote(N, varargin{:});
end
function N = drawShortNoteGroup(obj, types, varargin)
% TODO
% NOTE: Use the quarter note for all the heads. Draw however many
% lines (with "line" command) where needed. Top line should be
% "fatter"; you can do this by adjusting the "linewidth" property
% Possibly transform one or more members of the group
N = obj.transformNote(N, varargin{:});
end
%% Note/Rest transformations
% (dots, accents, inversion, decoration, etc.)
function N = transformNote(obj, N, varargin)
parameters = varargin(1:2:end);
values = varargin(2:2:end);
if mod(nargin,2)~=0 || ...
~all(cellfun('isclass', parameters), 'char') || ...
~all(cellfun('isclass', values), 'char')
error('transformNotes:no_pv_pairs',...
'Transforming notes is done by all-text parameter/value pairs.');
end
if numel(parameters)==0
return; end
for ii = 1:numel(parameters)
parameter = lower(parameters{ii});
value = lower(values{ii});
switch parameter
% Note may be flipped
case 'orientation'
switch value
case {'upright' 'normal'}
% No action
case {'flip', 'flipped', 'upside down'}
N = flipdim(N,1);
otherwise
% error
end
% Duration of note may be extended
case {'extend' 'extension'}
switch value
case {'single' 'dot'}
case {'double' 'dotdot' 'dot dot' 'ddot'}
case {'triple' 'dotdotdot' 'dot dot dot' 'dddot'}
otherwise
% error
end
% Note may be accented
case {'accent' 'accented'}
switch value
case 'portato'
case 'staccato'
case 'staccatissimo'
case 'legato'
case 'marcato'
case 'marcatissimo'
case 'tenuto'
otherwise
% error
end
% Note may be decorated
case {'decoration' 'decorated'}
switch value
case {'thril' 'thriller'}
case {'pralthril' 'pralthriller' 'praller'}
case 'mordent'
case 'arpeggio'
case 'gruppetto'
case 'glissando'
case 'portamento'
case 'schleifer'
case {'grace note' 'appoggiatura'}
case {'striped grace note' 'acciaccatura'}
otherwise
% error
end
otherwise
warning('transformNotes:unsupported_parameter',...
'Unknown parameter: ''%s''. Ignoring...', parameter);
end
end
end % transformNote
end % Private methods
end % Class definition
而且,您可以按原样运行它,将这些图像另存为GClef.png
,FClef.png
并brace.png
与类定义在同一目录中: