赞
踩
因为最基础的MIDI文件只需要起止(Onset、Offset)、绝对音高(MIDI pitch)就可以播放了(音色之类的都可以设为默认如钢琴),因此才会造成数据如此杂乱。MusicXML格式就好得多,因为其是一个面向乐谱显示或排版的格式,包含基本音乐学信息是其基础。
MusicXML文件分为两种类型:score-partwise与score-timewise,其中较为常见的是score-partwise。
- 谱子信息,XML文件信息
- 各声部信息
- 声部1全曲:
- 小节1:
- 属性
- 音符1
- 音符2
- ……
基于节拍事件(Beat-based)
REMI用时值取代MIDI编码里的音符关(Note off),用小节(Bar)和位置(Position)取代MIDI编码里的时移,并且增加了音乐速度与和弦信息。REMI编码将音乐编码成按照小节、位置、和弦、音乐速度、音高、时值、力度依次排列的离散token序列
相比MIDI-like,REMI的异同:
Note-On
和 Note Velocity
Note-On
事件表示某一特定音高的音的开始事件,Node Velocity
则表示响度。这两种事件在Midi-like和REMI表示中都存在。
Note-Off
和 Note-Duration
在REMI中,使用Note-Duration
取代了Note-off
,将一个音符用三个连续的token表示:Note Velocity
,Note-On
,Note-Duration。
这样做主要有两个原因:
Time-Shift
和 Position & Bar
以固定时值出现的位置&小节(Position & Bar)事件取代原先伴随音符的时间偏移(Time-Shift)事件,能够在音乐数据表示中加入度量结构,避免基于Time-Shift
的模型中时间错误累积的问题。Position&Bar更多优点:
Tempo
用于考虑速度变化(比如常见的beats per minute; BPM)。这类事件在MIDI乐谱中不一定存在,但是在MIDI演奏数据中可以通过对音频进行乐曲速度估计得到。
Chord
按根音和类型定义的共60种和弦被作为额外的支持符号(supportive token)输入给模型。
为了获取Bar
事件位置,本文使用RNN从音频中估计得到强拍(downbeat)位置,并用线性插值的方法得到节拍位置。这样的表示方式可以得到更规则的时间(节奏)序列。
使用基于规则的启发式方法进行符号域和弦识别。遍历片段中的每个音作为可能的和弦根音,并通过与其他音的音程关系计算似然得分,最终得到和弦估计。
为了进一步缩减REMI编码的序列长度,CP提出将REMI编码的离散token序列转化为以复合词为时间单位的token序列
- # ---- define event ---- #
- ''' 8 kinds:
- tempo: 0: IGN (ignore)
- 1: no change
- int: tempo
- chord: 0: IGN
- 1: no change
- str: chord types
- bar-beat: 0: IGN
- int: beat position (1...16)
- int: bar (bar)
- type: 0: eos
- 1: metrical
- 2: note
- duration: 0: IGN
- int: length
- pitch: 0: IGN
- int: pitch
- velocity: 0: IGN
- int: velocity
- '''
-
- # event template
- compound_event = {
- 'tempo': 0,
- 'chord': 0,
- 'bar-beat': 0,
- 'type': 0,
- 'pitch': 0,
- 'duration': 0,
- 'velocity': 0,
- }
-
-
- # event生成的过程 process : 每一个bar,中的每一个tick
- final_sequence = []
- for bar_step in range(0, global_end, BAR_RESOL):
- final_sequence.append(create_bar_event())
-
- # --- piano track --- #
- for timing in range(bar_step, bar_step + BAR_RESOL, TICK_RESOL):
- pos_on = False
- pos_events = []
- pos_text = 'Beat_' + str((timing-bar_step)//TICK_RESOL)
-
- ...metrical (tempo+chord)
- ...for note in notes
- ...
-
- if len(pos_events):
- final_sequence.extend(pos_events)
-
- # BAR ending
- final_sequence.append(create_bar_event())
-
- # EOS
- final_sequence.append(create_eos_event())

- # --- build dictionary --- #
- # all files
- class_keys = pickle.load(
- open(os.path.join(path_indir, eventfiles[0]), 'rb'))[0].keys()
- print('class keys:', class_keys)
-
- # define dictionary
- event2word = {}
- word2event = {}
-
- corpus_kv = collections.defaultdict(list)
- for file in eventfiles:
- for event in pickle.load(open(
- os.path.join(path_indir, file), 'rb')):
- for key in class_keys:
- corpus_kv[key].append(event[key])
-
- for ckey in class_keys:
- class_unique_vals = sorted(
- set(corpus_kv[ckey]), key=lambda x: (not isinstance(x, int), x))
- event2word[ckey] = {key: i for i, key in enumerate(class_unique_vals)}
- word2event[ckey] = {i: key for i, key in enumerate(class_unique_vals)}

Multi-track MIDI 编码方式被提出用于对多个音轨同时编码,具有对和声建模的优势。为了进一步缩短编码长度,并提供足够丰富的音乐信息,OctupleMIDI编码将每个音符编码成一个包含拍号、音乐速度、小节、位置、乐器、音高、时值、力度这8个元素的元组。更短的序列长度可以使模型一次性处理更长的音乐片段,从而提升模型的建模能力。用在MusicBERT中。
music21
可以处理包括MusicXML,MIDI,abc等多种格式的音乐文件,并可以从零开始构建音乐文件或对音乐进行分析。它读取midi文件时,输出是一个个音符object,甚至如果多个音符同时按响,输出就是一个和弦object。而不是一个动作。
- import music21
- mid = music21.converter.parse("twinkle-twinkle-little-star.mid") # 读取midi文件
- for note in mid[0].flat.notes: # 所有的音符
- print(note, note.duration.type, note.offset) # 音符, 时长, 位置
-
- #输出
- >>> <music21.note.Note C> quarter 0.0
- >>> <music21.chord.Chord E3 C3> half 0.0
- >>> <music21.note.Note C> quarter 1.0
- >>> <music21.note.Note G> quarter 2.0
- >>> <music21.chord.Chord C4 C3> half 2.0
- >>> <music21.note.Note G> quarter 3.0
- f = note.Note("F5#") #创建一个音高为F5#的音符
- f.name #音符的音名
- f.step #音符的音级(不包含变化音及八度信息的音名,这里成为音级严格来说并不准确)
- f.pitch.pitchClassString #音级(以C为0级的音程数)
- f.octave #八度
- f.pitch #音符的音高,返回一个音高对象
- f.duration #音符的时值,返回一个时值对象
- f.duration.type #音符的时值的类型 'quarter','half'
-
- f.pitch.midi
-
- #改变属性
- f.duration.dots #改变符点数量
- f.duration.quarterLength #直接改变时值,以四分音符计
- f.pitch.accidental #变化音的类型
- cMajor = chord.Chord(["E3","C4","G4"]) #初始化和弦
- cMajor.add(note.Note("E3")) #添加和弦
- cMajor.duration.quarterLength=2 #和弦也可以修改时值
类似list的基本单位,它可以储存任意music21对象及其组合。
- stream1 = stream.Stream() #创建
- stream1.append(f)
- stream1.insert(0,f)
- stream1.repeatAppend(f, 4) #重复添加
-
- stream1.show('text') #显示
MusPy documentation — MusPy documentation (salu133445.github.io)https://salu133445.github.io/muspy/
MusicAIz — Musicaiz 0.0.2 documentationhttps://carlosholivan.github.io/musicaiz/index.html
支持提供了音乐生成客观评价指标
- import miditoolkit
- midi_obj = miditoolkit.midi.parser.MidiFile(path_midi)
- print(midi_obj)
-
- """
- Output:
- ticks per beat: 480
- max tick: 72002
- tempo changes: 68
- time sig: 2
- key sig: 0
- markers: 71
- lyrics: False
- instruments: 2
- """

Timing in MIDI files is centered around ticks and beats. A beat is the same as a quarter note. Beats are divided into ticks, the smallest unit of time in MIDI.
- from madmom.audio.signal import SignalProcessor, FramedSignalProcessor
- from madmom.audio.stft import ShortTimeFourierTransformProcessor
- from madmom.audio.spectrogram import (
- FilteredSpectrogramProcessor, LogarithmicSpectrogramProcessor,
- SpectrogramDifferenceProcessor)
- from madmom.processors import ParallelProcessor, Processor, SequentialProcessor
多线程提取频谱特征以及频谱的一阶差分
- def madmom_feature(wav):
- sig = SignalProcessor(num_channels=1, sample_rate=44100)
- multi = ParallelProcessor([])
- frame_sizes = [1024, 2048, 4096]
- num_bands = [3, 6, 12]
-
- for frame_size, num_bands in zip(frame_sizes, num_bands):
- frames = FramedSignalProcessor(frame_size=frame_size, fps=100)
- stft = ShortTimeFourierTransformProcessor() # caching FFT window
- filt = FilteredSpectrogramProcessor(
- num_bands=num_bands, fmin=30, fmax=17000, norm_filters=True)
- spec = LogarithmicSpectrogramProcessor(mul=1, add=1)
- diff = SpectrogramDifferenceProcessor(
- diff_ratio=0.5, positive_diffs=True, stack_diffs=np.hstack)
-
- # process each frame size with spec and diff sequentially
- multi.append(SequentialProcessor((frames, stft, filt, spec, diff)))
-
- # stack the features and processes everything sequentially
- pre_processor = SequentialProcessor((sig, multi, np.hstack))
- feature = pre_processor.process( wav)
- return feature

用madmom自带的HMM模块处理beat和downbeat联合检测算法生成的激活值
- from madmom.features.downbeats import DBNDownBeatTrackingProcessor as DownBproc
- hmm_proc = DownBproc(beats_per_bar = [3,4], num_tempi = 80,
- transition_lambda = 180,
- observation_lambda = 21,
- threshold = 0.5, fps = 100)
- #act是用神经网络等音频节拍检测算法处理得到的激活值
- beat_fuser_est = hmm_proc(act)
- beat_pred = beat_fuser_est[:,0]
- downbeat_pred = beat_pred[beat_fuser_est[:,1]==1]
对节拍检测结果和节拍标注值计算多种评估指标:
- from madmom.evaluation.beats import BeatEvaluation
- scr = BeatEvaluation(beat_pred,beat_true)
- print(scr.fmeasure,scr.pscore,scr.cemgil,scr.cmlc,scr.cmlt,
- scr.amlc,scr.amlt)
- librosa.beat:用于检测速度和节拍
- librosa.core:用于从磁盘加载音频和计算各种频谱图
- librosa.decompose:实现矩阵分解进行谐波和冲击源分离通用频谱图分解
- librosa.display:音频特征的显示
- librosa.effects:时域音频处理,音高变换和时间拉伸,时域包装器。
- librosa.feature:特征提取和操作:色度图,伪常数Q(对数频率)变换,Mel频谱图,MFCC和调谐估计
- librosa.filters:滤波器生成色度。伪CQT、CQT等
- librosa.onset:其实检测和起始强度计算。
- librosa.segment:用于结构分段的函数
- librosa.swquence:顺序建模功能
- librosa.util:辅助工具(规范化。填充、居中)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。