首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在一定条件下从列表中创建矩阵的最有效/最干净的方法?

在一定条件下从列表中创建矩阵的最有效/最干净的方法?
EN

Stack Overflow用户
提问于 2019-12-13 23:25:13
回答 1查看 45关注 0票数 2

这也是我第一次在这里发帖,如果我搞砸了,很抱歉。我不知道这是什么标题,但我的课程项目,但我蛮力地摆脱了今天的问题,并想知道是否有一个更有效的方法来做我需要的。我花了两个小时在这上面工作,我很想看看你们会怎么处理它。

背景:使用midi库,您可以提取音频音轨的音符以及播放时的音符。在做了一些工作之后,你得到了这样的东西:

代码语言:javascript
复制
notes = ['B3', 'C4', 'G2', 'G3', 'B3', 'D4', 'F4', 'G2', 'D4', 'F4']
ticks = [0, 1, 12, 12, 12, 15, 15, 22, 22,]

滴答基本上是midi的时间域。它表示该音符第一次播放,列表ticks对应于notes (notes[0]播放时间ticks[0])。就我的目的而言,我只能同时通过4个设备中的一个播放一个音符。所以当我有重复的滴答时,我需要同时演奏一个和弦,或多个音符。默认情况下,设备(Channel0)播放一个音符,channel1播放两个音符,等等……

上面的曲目会像这样演奏;

  • channel0扮演B3 at t=0
  • channel0在
  • &2 play G2扮演C4,G3 & B3在t=12
  • channels0 &1扮演D4 & F4在t=15
  • channels0,1 &2 play G2,B3&en19在en21

Problem:给定notesticks,用正确的时间和注释为0-3通道创建指令.如果和弦有4个以上的音符,我就忽略它们,因为我对它们无能为力。基本上,我需要如下所示的数据结构:

代码语言:javascript
复制
channel0 = ['B3', 'C4', 'G2', 'D4']
channel1 = [  0,    0,  'G3', 'F4']
channel2 = [  0,    0,  'B3',   0 ]
channel3 = [  0,    0,    0,    0 ]

我第一次试图用一堆if语句来强迫它,但最终还是想出了这个方法。

解决方案:首先,我创建了一个元组列表来比较每个滴答在轨道上发生的次数,但这是没有意义的。我刚做了个数组。

代码语言:javascript
复制
res = []
for i in ticks:
    if i not in res:
        res.append(i)
counts = [(ticks.count(x)) for x in res]

然后,我列出了频道的列表。我向后订购是因为从下到上工作“矩阵”更容易。

代码语言:javascript
复制
ch0, ch1, ch2, ch3 = ([] for i in range(4))
final = [ch3, ch2, ch1, ch0]

最后,这件可耻的事终于奏效了。

代码语言:javascript
复制
    countsidx = -1
    while breakMe:
        countsidx += 1
        for finalidx, vchan in enumerate(final):
            if int(finalidx) >= counts[countsidx]:
                vchan.append(0)
            else:
                vchan.append(notes[notesidx])
                notesidx += 1
            if notesidx == len(notes):
                return final
                breakMe = False
    return final

所以我的问题是:我怎么能用更少的代码来完成这个任务呢?有没有人有更简单的方法来做和我一样的事?我喜欢学习最佳实践。我觉得我花了太多时间在这件简单的事情上。

全码

代码语言:javascript
复制
import matplotlib.pyplot as plt
import argparse
import sys
import collections
from mido import MidiFile
from midiutil import MIDIFile
import sys

NOTES = ['C', 'CS', 'D', 'DS', 'E', 'F', 'FS', 'G', 'GS', 'A', 'AS', 'B']
OCTAVES = list(range(11))
NOTES_IN_OCTAVE = len(NOTES)

def countShit(ticks,notes):
    breakMe = True
    res = []
    for i in ticks:
        if i not in res:
            res.append(i)
    counts = [(ticks.count(x)) for x in res]
    print('Counts (ticks,occurences) == ', counts)
    ch0,ch1,ch2,ch3, = ([] for i in range(4))
    final =[ch3,ch2,ch1,ch0]
    notesidx = 0
    countsidx = -1
    while breakMe:
        countsidx += 1
        for finalidx, vchan in enumerate(final):
            if int(finalidx) >= counts[countsidx]:
                vchan.append(0)
            else:
                vchan.append(notes[notesidx])
                notesidx += 1
            if notesidx == len(notes):
                return final
                breakMe = False
    return final

def convertTuple(tup):
    str =  ''.join(tup)
    return str

def number_to_note(number: int) -> tuple:
    octave = number // NOTES_IN_OCTAVE
    assert octave in OCTAVES, errors['notes']
    assert 0 <= number <= 127, errors['notes']
    note = NOTES[number % NOTES_IN_OCTAVE]
    return str(note),str(octave-2)

song = midi.read_midifile('mario_06.mid')
song.make_ticks_abs()
tracks =[]
trackNotes =[]
trackTime = []
trackTicks=[]
for track in song:
    notes = [note for note in track if note.name == 'Note On']
    notes2 = [note for note in track if note.name == 'Note Off']
    pitch = [note.pitch for note in notes]
    tick = [note.tick for note in notes]
    trackTime =[b.tick - a.tick for a,b in zip(notes,notes2)]
    tracks += [tick, pitch]
    trackNotes += pitch
    trackTicks += tick


trackNotesFinal =[]
for i in trackNotes:
    k = str(convertTuple(number_to_note(i)))
    trackNotesFinal.append(k)

trackNotesFinal
channels = countShit(trackTicks,trackNotesFinal)
EN

回答 1

Stack Overflow用户

发布于 2019-12-13 23:59:46

下面是使用zipitertools.groupby的列表理解的解决方案。我假设ticks数组总是有序的。

代码语言:javascript
复制
import itertools
from operator import itemgetter

NUM_CHANNELS = 4

chords = [
    [ note for _, note in chord ]
    for _, chord in itertools.groupby(zip(ticks, notes), key=itemgetter(0))
]
# [['B3'], ['C4'], ['G2', 'G3', 'B3'], ['D4', 'F4'], ['G2', 'D4']]

channels = [
    [ chord[i] if i < len(chord) else None for chord in chords ]
    for i in range(NUM_CHANNELS)
]
# [['B3', 'C4', 'G2', 'D4', 'G2'],
#  [None, None, 'G3', 'F4', 'D4'],
#  [None, None, 'B3', None, None],
#  [None, None, None, None, None]]

由于没有注释,我使用了None而不是0,因为这更像Pythonic (而且作为一种奖励,打印时可以使矩阵列很好地排列)。

由于您可能还想知道矩阵中每一列的时间,这可以通过另一个列表理解来完成:

代码语言:javascript
复制
timings = [ tick for tick, _ in itertools.groupby(ticks) ]
# [0, 1, 12, 15, 22]
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59330993

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档