如何通过自定义函数将数据文件索引转换为xticlabels?
问题
如何对数据文件的索引应用转换函数,在每个间隔生成xticlabels?
我希望使用自定义的secs_to_beats_bars函数(该函数通过sprintf格式化返回字符串),详情如下。
注意:我希望避免以下操作:
- 手动创建任意刻度标签列表并应用到特定位置
- 编辑或重新创建原始数据文件
示例
假设我有一个以秒(小数)为索引的数据文件:
# Seconds, Pitch, Duration, Velocity 0.16129032258064513, 31, 0.7157258064516129, 71 0.8870967741935483, 31, 0.3578629032258066, 75 1.2499999999999998, 31, 0.3427419354838712, 78 1.6129032258064513, 42, 0.4637096774193551, 75 2.0967741935483866, 37, 0.7056451612903228, 76 2.82258064516129, 33, 0.3427419354838712, 77 3.1854838709677415, 31, 0.3427419354838712, 78 3.548387096774193, 31, 0.46370967741935554, 74 4.032258064516128, 31, 1.0735887096774201, 71 5.120967741935483, 33, 0.34778225806451696, 62 5.483870967741934, 31, 0.32258064516129087, 50 5.806451612903225, 35, 0.48387096774193544, 87 5.96774193548387, 43, 0.3528225806451618, 63 6.330645161290321, 31, 0.7106854838709676, 58
我有一个自定义函数,可将秒数转换为音乐相关的时间系统(如节拍和小节):
# 速度单位为每分钟节拍数,节拍和小节从1开始计数。 # 示例:第1小节第1拍 = "1:1" secs_to_beats(secs, tempo) = int(secs * tempo / 60); secs_to_beats_bars(secs, tempo, beatsperbar) = sprintf('%.0f:%.0f', int(secs_to_beats(secs, tempo) / beatsperbar) + 1, secs_to_beats(secs, tempo) % beatsperbar + 1);
我用它来设置每个节拍间隔的xtics,并通过mxtics将网格进一步细分为十六分音符:
beats_to_secs(beat, tempo) = 1.0/(tempo * 1.0/60.0) * beat; bpm = 80; beatinterval = beats_to_secs(1, bpm); set xtics beatinterval; set mxtics 4; set grid x mx ls 100, ls 101;
最后使用boxxyerror绘图,利用秒数、音高和时长列为每一行创建矩形:
set yrange[0:127]; plot 'midi.dat' u (($1+$3)/2):(($2+0.95)/2):1:($3+$1):($2):($2+0.95) w boxxy fs solid 0.50 border lc 'black';
生成的图表
如您所见,xticlabels以秒为单位,我希望通过上述secs_to_beats_bars函数将其转换为节拍/小节格式。

解决方案
要实现用自定义函数动态生成xticlabels,无需手动创建标签列表或修改原始数据,可以借助gnuplot的循环和动态刻度添加功能,具体步骤如下:
- 完善参数与函数定义:补充每小节节拍数(以常见的4/4拍为例),保留原有的转换逻辑:
# 速度(每分钟节拍数) bpm = 80 # 每小节节拍数(根据实际音乐调整) beatsperbar = 4 # 秒转节拍数 secs_to_beats(secs, tempo) = int(secs * tempo / 60) # 秒转小节:节拍格式字符串 secs_to_beats_bars(secs, tempo, beatsperbar) = sprintf('%.0f:%.0f', int(secs_to_beats(secs, tempo)/beatsperbar)+1, secs_to_beats(secs, tempo)%beatsperbar+1) # 节拍转秒数 beats_to_secs(beat, tempo) = 1.0/(tempo*1.0/60.0)*beat # 单个节拍的时间间隔 beatinterval = beats_to_secs(1, bpm)
- 自动获取数据的时间范围:用
stats命令读取数据文件的时间区间,避免手动指定范围:
stats 'midi.dat' using 1 nooutput x_min = STATS_min x_max = STATS_max
- 动态生成节拍/小节格式的刻度:通过循环自动计算每个节拍位置的标签,添加到x轴:
set xtics () # 清空默认刻度 do for [i=0:int(x_max/beatinterval)+1] { current_sec = i * beatinterval if (current_sec <= x_max) { set xtics add (secs_to_beats_bars(current_sec, bpm, beatsperbar) current_sec) } }
- 保留原有网格与绘图设置:继续使用你原来的网格和绘图命令即可:
set mxtics 4 set grid x mx ls 100, ls 101 set yrange[0:127] plot 'midi.dat' u (($1+$3)/2):(($2+0.95)/2):1:($3+$1):($2):($2+0.95) w boxxy fs solid 0.50 border lc 'black'
这样生成的图表x轴刻度会自动显示为小节:节拍的格式,完全符合需求,既不需要手动编写标签列表,也不需要修改原始数据文件。
内容的提问来源于stack exchange,提问作者lys
相关产品推荐
相关产品推荐

