You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Matlab音乐播放代码故障排查:逐频生成播放信号问题求助

问题排查与代码修正

我帮你梳理下这段Octave/Matlab代码里的几个关键问题,这些应该就是导致你没法正常播放音乐的核心原因:

核心问题分析

  • FrTm数组遍历逻辑错误:FrTm是每行存「键位编号, 时长」的二维数组,但你用for i = 1:length(FrTm)循环时,length(FrTm)返回的是数组最大维度的长度,而FrTm(i)会按列优先取元素,导致你把时长值当成键位编号来用,完全打乱了频率和时长的对应关系。正确做法是按行遍历,每次取出一行的键位和时长。

  • M数组的逻辑混乱:你先基于十二平均律计算了M(I)对应钢琴键的频率(这部分是对的),但循环里又用M(i) = FrTm(i)覆盖了之前的频率值,等于直接废掉了原本的频率计算逻辑。应该用FrTm里的键位编号去索引M数组,拿到对应的实际频率。

  • 时间序列t的固定化错误:你定义了固定长度的t = 0:1/18:5,但每个音符的时长应该由FrTm里的第二个值决定,每个音符需要生成对应时长的时间序列,而不是用统一的t。

  • 信号拼接方向错误:你用Signal = [data; Signal];把新音符堆叠到现有信号上方,这会生成多通道信号(每行是一个通道),但你需要的是单声道连续音频,应该按横向拼接(把新音符接在现有信号后面)。另外这种拼接顺序还会让音乐倒放,因为你每次把新音符放在了前面。

  • stem绘图的错误:如果Signal是多行数组,stem会绘制多个序列,而实际你应该绘制拼接后的一维信号。

修正后的代码

function Music3 ()
    Fs = 44100;  % 采样率
    T = 1 / Fs;
    note_unit = 1/18;  % 每个时长单位对应的秒数,对应你原本的时间步长逻辑
    
    % 生成钢琴键对应的频率(7到88号键)
    M = zeros(1, 88);
    for I = 7:88
        M(I) = round(36.8 * (2^(1/12))^(I - 6));
    endfor
    
    Signal = [];
    % FrTm是[N,2]数组:每行[键位编号, 时长单位数]
    FrTm = [50, 3; 50, 3; 52, 3; 54, 3; 50, 3; 54, 3; 52, 3; 45, 3; 50, 3; 50, 3; 52, 3; 54, 3; 50, 6; 49, 3; 1, 3; 50, 3; 50, 3; 52, 3; 54, 3; 55, 3; 54, 3; 52, 3; 50, 3; 49, 3; 45, 3; 47, 3; 49, 3; 50, 6; 50, 3; 1, 3; 47, 5; 49, 1; 47, 3; 45, 3; 47, 3; 49, 3; 50, 3; 1, 3; 45, 5; 47, 1; 45, 3; 43, 3; 42, 6; 45, 3; 1, 3; 47, 5; 49, 1; 47, 3; 45, 3; 47, 3; 49, 3; 50, 3; 47, 3; 45, 3; 50, 3; 49, 3; 52, 3; 50, 6; 50, 6];
    
    % 按行遍历FrTm的每一个音符
    for i = 1:size(FrTm, 1)
        key_num = FrTm(i, 1);
        duration_units = FrTm(i, 2);
        
        % 获取对应频率,键位1设为静音(0频率)
        if key_num == 1
            freq = 0;
        else
            freq = M(key_num);
        end
        
        % 生成当前音符的时间序列:时长为 duration_units * note_unit
        t_note = 0:T:(duration_units * note_unit - T);
        % 生成正弦信号,静音时输出0
        if freq == 0
            data = zeros(size(t_note));
        else
            data = sin(2 * pi * freq * t_note);
        end
        
        % 把当前音符拼接到信号末尾(保持播放顺序)
        Signal = [Signal, data];
    endfor
    
    % 绘制单声道信号的时域图
    plot(Signal);
    % 播放音频
    sound(Signal, Fs);
end

修正点说明

  1. 改用size(FrTm, 1)获取FrTm的行数,确保按行遍历每个音符,正确取出键位和时长。
  2. 保留了M数组的频率计算逻辑,用FrTm的键位编号去索引M拿到对应频率;同时处理了键位1的静音逻辑(输出0信号)。
  3. 为每个音符生成对应时长的时间序列t_note,时长由duration_units * note_unit决定,符合你原本的1/18秒单位设计。
  4. 横向拼接信号Signal = [Signal, data],保证音符按顺序播放,且生成单声道信号。
  5. stem改成plot,更适合展示连续的音频信号;同时处理了静音情况,避免播放杂音。

内容的提问来源于stack exchange,提问作者Inam

火山引擎 最新活动