patterns no projeto do MMPSearch
This commit is contained in:
1 parent
52d1d90fc1
commit
34a44ec763
23 files changed
+94100
-6629
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,135 +1,247 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# FUNÇÃO AUXILIAR: CALCULAR STEPS A PARTIR DE POSIÇÕES DE NOTAS
|
||||
# (Esta função é necessária para a injeção de steps)
|
||||
# -----------------------------------------------------------
|
||||
|
||||
def calculate_steps_from_notes(pattern_element, ticks_per_step=12):
|
||||
"""
|
||||
Calcula o array de steps [True, False, ...] a partir das notas XML.
|
||||
|
||||
:param pattern_element: O objeto ElementTree para a tag <pattern>.
|
||||
:param ticks_per_step: O número de ticks por step (12 para 1/16 no LMMS).
|
||||
:return: Um array de booleanos representando os steps marcados.
|
||||
"""
|
||||
try:
|
||||
# Pega o número total de steps (ex: 16, 64)
|
||||
total_steps = int(pattern_element.attrib.get('steps', 16))
|
||||
except ValueError:
|
||||
total_steps = 16
|
||||
|
||||
steps = [False] * total_steps
|
||||
|
||||
notes = pattern_element.findall('note')
|
||||
|
||||
for note in notes:
|
||||
try:
|
||||
# Posição da nota em ticks
|
||||
note_pos_ticks = int(note.attrib.get('pos', 0))
|
||||
|
||||
# Converte a posição em ticks para o índice do step (arredondando)
|
||||
step_index = round(note_pos_ticks / ticks_per_step)
|
||||
|
||||
if 0 <= step_index < total_steps:
|
||||
steps[step_index] = True
|
||||
except ValueError:
|
||||
# Ignora notas com posição inválida
|
||||
continue
|
||||
|
||||
return steps
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# FUNÇÃO PRINCIPAL: PARSE_BASSLINES (MESCLADA)
|
||||
# -----------------------------------------------------------
|
||||
|
||||
def parse_basslines(track):
|
||||
"""
|
||||
Processa uma track <track type="1"> (Beat/Bassline), extrai todos os seus
|
||||
instrumentos (sub-tracks), coleta detalhes e calcula os steps
|
||||
para cada pattern.
|
||||
"""
|
||||
|
||||
# Nome da track principal (ex: "Beat/Bassline 0")
|
||||
track_name = track.attrib.get('name', 'N/A')
|
||||
bbtrack = track.find('./bbtrack')
|
||||
instruments = []
|
||||
tags = ''
|
||||
|
||||
# 'tags' agora é usado para coletar os tipos de plugins para o retorno
|
||||
plugin_tags = set()
|
||||
|
||||
if bbtrack is not None:
|
||||
for container in bbtrack.findall('./trackcontainer'):
|
||||
for instrument_track in container.findall('./track'):
|
||||
instrument_info = {}
|
||||
instrument_info['instrument_name'] = instrument_track.attrib.get('name', 'N/A')
|
||||
instrument_info['instrument_type'] = instrument_track.attrib.get('type', 'N/A')
|
||||
#print(instrument_info['tags'])
|
||||
# <instrumenttrack>
|
||||
instrumenttrack = instrument_track.find('./instrumenttrack')
|
||||
if instrumenttrack is not None:
|
||||
instrument_info.update({
|
||||
'pitch': instrumenttrack.attrib.get('pitch', ''),
|
||||
'pan': instrumenttrack.attrib.get('pan', ''),
|
||||
'vol': instrumenttrack.attrib.get('vol', ''),
|
||||
'pitchrange': instrumenttrack.attrib.get('pitchrange', ''),
|
||||
'basenote': instrumenttrack.attrib.get('basenote', ''),
|
||||
'fxch': instrumenttrack.attrib.get('fxch', ''),
|
||||
'usemasterpitch': instrumenttrack.attrib.get('usemasterpitch', '')
|
||||
})
|
||||
if bbtrack is None:
|
||||
# Se não houver bbtrack, pode ser uma bassline vazia (como a 1 e 2)
|
||||
return {
|
||||
'bassline_name': track_name,
|
||||
'type': 'bassline',
|
||||
'tags': '', # Retorna tag vazia
|
||||
'instruments': instruments # Retorna lista vazia
|
||||
}
|
||||
|
||||
# <audiofileprocessor>
|
||||
audiofileprocessor = instrument_track.find('.//audiofileprocessor')
|
||||
if audiofileprocessor is not None:
|
||||
if 'audiofileprocessor' not in tags:
|
||||
tags = 'audiofileprocessor'
|
||||
instrument_info['audiofileprocessor'] = {
|
||||
'amp': audiofileprocessor.attrib.get('amp', ''),
|
||||
'src': audiofileprocessor.attrib.get('src', ''),
|
||||
'lframe': audiofileprocessor.attrib.get('lframe', ''),
|
||||
'stutter': audiofileprocessor.attrib.get('stutter', ''),
|
||||
'interp': audiofileprocessor.attrib.get('interp', ''),
|
||||
'sframe': audiofileprocessor.attrib.get('sframe', ''),
|
||||
'looped': audiofileprocessor.attrib.get('looped', ''),
|
||||
'eframe': audiofileprocessor.attrib.get('eframe', ''),
|
||||
'reversed': audiofileprocessor.attrib.get('reversed', ''),
|
||||
for container in bbtrack.findall('./trackcontainer'):
|
||||
# Itera sobre cada instrumento (sub-track tipo 0) dentro da bassline
|
||||
for instrument_track in container.findall('./track'):
|
||||
instrument_info = {}
|
||||
|
||||
# Pega o nome do instrumento (ex: "kicker", "snare_hiphop02.ogg")
|
||||
instrument_info['instrument_name'] = instrument_track.attrib.get('name', 'N/A')
|
||||
instrument_info['instrument_type'] = instrument_track.attrib.get('type', 'N/A')
|
||||
|
||||
# --- Início da Coleta de Detalhes (da sua função base) ---
|
||||
|
||||
# <instrumenttrack>
|
||||
instrumenttrack = instrument_track.find('./instrumenttrack')
|
||||
if instrumenttrack is not None:
|
||||
instrument_info.update({
|
||||
'pitch': instrumenttrack.attrib.get('pitch', ''),
|
||||
'pan': instrumenttrack.attrib.get('pan', ''),
|
||||
'vol': instrumenttrack.attrib.get('vol', ''),
|
||||
'pitchrange': instrumenttrack.attrib.get('pitchrange', ''),
|
||||
'basenote': instrumenttrack.attrib.get('basenote', ''),
|
||||
'fxch': instrumenttrack.attrib.get('fxch', ''),
|
||||
'usemasterpitch': instrumenttrack.attrib.get('usemasterpitch', '')
|
||||
})
|
||||
|
||||
# Adiciona o nome real do plugin (kicker, audiofileprocessor, etc.)
|
||||
instrument_tag = instrument_track.find('.//instrument')
|
||||
if instrument_tag is not None:
|
||||
plugin_name = instrument_tag.attrib.get('name')
|
||||
if plugin_name:
|
||||
plugin_tags.add(plugin_name) # Adiciona à tag da bassline
|
||||
instrument_info['plugin_name'] = plugin_name # Salva o nome do plugin
|
||||
|
||||
# <audiofileprocessor>
|
||||
audiofileprocessor = instrument_track.find('.//audiofileprocessor')
|
||||
if audiofileprocessor is not None:
|
||||
instrument_info['audiofileprocessor'] = {
|
||||
'amp': audiofileprocessor.attrib.get('amp', ''),
|
||||
'src': audiofileprocessor.attrib.get('src', ''),
|
||||
'lframe': audiofileprocessor.attrib.get('lframe', ''),
|
||||
'stutter': audiofileprocessor.attrib.get('stutter', ''),
|
||||
'interp': audiofileprocessor.attrib.get('interp', ''),
|
||||
'sframe': audiofileprocessor.attrib.get('sframe', ''),
|
||||
'looped': audiofileprocessor.attrib.get('looped', ''),
|
||||
'eframe': audiofileprocessor.attrib.get('eframe', ''),
|
||||
'reversed': audiofileprocessor.attrib.get('reversed', ''),
|
||||
}
|
||||
|
||||
# <eldata>
|
||||
eldata = instrument_track.find('./eldata')
|
||||
if eldata is not None:
|
||||
eldata_info = {
|
||||
'fwet': eldata.attrib.get('fwet', ''),
|
||||
'ftype': eldata.attrib.get('ftype', ''),
|
||||
'fcut': eldata.attrib.get('fcut', ''),
|
||||
'fres': eldata.attrib.get('fres', '')
|
||||
}
|
||||
|
||||
elvol = eldata.find('./elvol')
|
||||
if elvol is not None:
|
||||
eldata_info['elvol'] = {
|
||||
'sustain': elvol.attrib.get('sustain', ''),
|
||||
'lamt': elvol.attrib.get('lamt', ''),
|
||||
'lshp': elvol.attrib.get('lshp', ''),
|
||||
'amt': elvol.attrib.get('amt', ''),
|
||||
'pdel': elvol.attrib.get('pdel', ''),
|
||||
'lpdel': elvol.attrib.get('lpdel', ''),
|
||||
'lspd_numerator': elvol.attrib.get('lspd_numerator', ''),
|
||||
'lspd_syncmode': elvol.attrib.get('lspd_syncmode', ''),
|
||||
'latt': elvol.attrib.get('latt', ''),
|
||||
'ctlenvamt': elvol.attrib.get('ctlenvamt', ''),
|
||||
'x100': elvol.attrib.get('x100', ''),
|
||||
'dec': elvol.attrib.get('dec', ''),
|
||||
'hold': elvol.attrib.get('hold', ''),
|
||||
'rel': elvol.attrib.get('rel', ''),
|
||||
'lspd_denominator': elvol.attrib.get('lspd_denominator', ''),
|
||||
'userwavefile': elvol.attrib.get('userwavefile', ''),
|
||||
'att': elvol.attrib.get('att', ''),
|
||||
'lspd': elvol.attrib.get('lspd', '')
|
||||
}
|
||||
|
||||
# <eldata>
|
||||
eldata = instrument_track.find('./eldata')
|
||||
if eldata is not None:
|
||||
eldata_info = {
|
||||
'fwet': eldata.attrib.get('fwet', ''),
|
||||
'ftype': eldata.attrib.get('ftype', ''),
|
||||
'fcut': eldata.attrib.get('fcut', ''),
|
||||
'fres': eldata.attrib.get('fres', '')
|
||||
elcut = eldata.find('./elcut')
|
||||
if elcut is not None:
|
||||
eldata_info['elcut'] = {
|
||||
'cutoff': elcut.attrib.get('cutoff', ''),
|
||||
'q': elcut.attrib.get('q', '')
|
||||
}
|
||||
|
||||
elvol = eldata.find('./elvol')
|
||||
if elvol is not None:
|
||||
eldata_info['elvol'] = {
|
||||
'sustain': elvol.attrib.get('sustain', ''),
|
||||
'lamt': elvol.attrib.get('lamt', ''),
|
||||
'lshp': elvol.attrib.get('lshp', ''),
|
||||
'amt': elvol.attrib.get('amt', ''),
|
||||
'pdel': elvol.attrib.get('pdel', ''),
|
||||
'lpdel': elvol.attrib.get('lpdel', ''),
|
||||
'lspd_numerator': elvol.attrib.get('lspd_numerator', ''),
|
||||
'lspd_syncmode': elvol.attrib.get('lspd_syncmode', ''),
|
||||
'latt': elvol.attrib.get('latt', ''),
|
||||
'ctlenvamt': elvol.attrib.get('ctlenvamt', ''),
|
||||
'x100': elvol.attrib.get('x100', ''),
|
||||
'dec': elvol.attrib.get('dec', ''),
|
||||
'hold': elvol.attrib.get('hold', ''),
|
||||
'rel': elvol.attrib.get('rel', ''),
|
||||
'lspd_denominator': elvol.attrib.get('lspd_denominator', ''),
|
||||
'userwavefile': elvol.attrib.get('userwavefile', ''),
|
||||
'att': elvol.attrib.get('att', ''),
|
||||
'lspd': elvol.attrib.get('lspd', '')
|
||||
}
|
||||
|
||||
elcut = eldata.find('./elcut')
|
||||
if elcut is not None:
|
||||
eldata_info['elcut'] = {
|
||||
'cutoff': elcut.attrib.get('cutoff', ''),
|
||||
'q': elcut.attrib.get('q', '')
|
||||
}
|
||||
|
||||
elres = eldata.find('./elres')
|
||||
if elres is not None:
|
||||
eldata_info['elres'] = {
|
||||
'res': elres.attrib.get('res', '')
|
||||
}
|
||||
|
||||
instrument_info['eldata'] = eldata_info
|
||||
|
||||
# <chordcreator>
|
||||
chordcreator = instrument_track.find('./chordcreator')
|
||||
if chordcreator is not None:
|
||||
instrument_info['chordcreator'] = {
|
||||
'chord_enabled': chordcreator.attrib.get('chord-enabled', ''),
|
||||
'chordrange': chordcreator.attrib.get('chordrange', ''),
|
||||
'chord': chordcreator.attrib.get('chord', '')
|
||||
elres = eldata.find('./elres')
|
||||
if elres is not None:
|
||||
eldata_info['elres'] = {
|
||||
'res': elres.attrib.get('res', '')
|
||||
}
|
||||
|
||||
# <arpeggiator>
|
||||
arpeggiator = instrument_track.find('./arpeggiator')
|
||||
if arpeggiator is not None:
|
||||
instrument_info['arpeggiator'] = {
|
||||
'arptime': arpeggiator.attrib.get('arptime', ''),
|
||||
'arpmode': arpeggiator.attrib.get('arpmode', ''),
|
||||
'arp': arpeggiator.attrib.get('arp', ''),
|
||||
'arprange': arpeggiator.attrib.get('arprange', '')
|
||||
}
|
||||
instrument_info['eldata'] = eldata_info
|
||||
|
||||
# <midiport>
|
||||
midiport = instrument_track.find('./midiport')
|
||||
if midiport is not None:
|
||||
instrument_info['midiport'] = {
|
||||
'outputprogram': midiport.attrib.get('outputprogram', ''),
|
||||
'inputchannel': midiport.attrib.get('inputchannel', ''),
|
||||
'outputcontroller': midiport.attrib.get('outputcontroller', ''),
|
||||
'inputcontroller': midiport.attrib.get('inputcontroller', ''),
|
||||
'outputchannel': midiport.attrib.get('outputchannel', ''),
|
||||
'writable': midiport.attrib.get('writable', ''),
|
||||
'fixedinputvelocity': midiport.attrib.get('fixedinputvelocity', ''),
|
||||
'basevelocity': midiport.attrib.get('basevelocity', ''),
|
||||
'readable': midiport.attrib.get('readable', ''),
|
||||
'fixedoutputvelocity': midiport.attrib.get('fixedoutputvelocity', ''),
|
||||
'fixedoutputnote': midiport.attrib.get('fixedoutputnote', '')
|
||||
}
|
||||
# <chordcreator>
|
||||
chordcreator = instrument_track.find('./chordcreator')
|
||||
if chordcreator is not None:
|
||||
instrument_info['chordcreator'] = {
|
||||
'chord_enabled': chordcreator.attrib.get('chord-enabled', ''),
|
||||
'chordrange': chordcreator.attrib.get('chordrange', ''),
|
||||
'chord': chordcreator.attrib.get('chord', '')
|
||||
}
|
||||
|
||||
instruments.append(instrument_info)
|
||||
# <arpeggiator>
|
||||
arpeggiator = instrument_track.find('./arpeggiator')
|
||||
if arpeggiator is not None:
|
||||
instrument_info['arpeggiator'] = {
|
||||
'arptime': arpeggiator.attrib.get('arptime', ''),
|
||||
'arpmode': arpeggiator.attrib.get('arpmode', ''),
|
||||
'arp': arpeggiator.attrib.get('arp', ''),
|
||||
'arprange': arpeggiator.attrib.get('arprange', '')
|
||||
}
|
||||
|
||||
# <midiport>
|
||||
midiport = instrument_track.find('./midiport')
|
||||
if midiport is not None:
|
||||
instrument_info['midiport'] = {
|
||||
'outputprogram': midiport.attrib.get('outputprogram', ''),
|
||||
'inputchannel': midiport.attrib.get('inputchannel', ''),
|
||||
'outputcontroller': midiport.attrib.get('outputcontroller', ''),
|
||||
'inputcontroller': midiport.attrib.get('inputcontroller', ''),
|
||||
'outputchannel': midiport.attrib.get('outputchannel', ''),
|
||||
'writable': midiport.attrib.get('writable', ''),
|
||||
'fixedinputvelocity': midiport.attrib.get('fixedinputvelocity', ''),
|
||||
'basevelocity': midiport.attrib.get('basevelocity', ''),
|
||||
'readable': midiport.attrib.get('readable', ''),
|
||||
'fixedoutputvelocity': midiport.attrib.get('fixedoutputvelocity', ''),
|
||||
'fixedoutputnote': midiport.attrib.get('fixedoutputnote', '')
|
||||
}
|
||||
|
||||
# --- Fim da Coleta de Detalhes ---
|
||||
|
||||
|
||||
# --- INJEÇÃO DA LÓGICA DE STEPS ---
|
||||
# Encontra todos os patterns associados a este instrumento
|
||||
pattern_elements = instrument_track.findall('pattern')
|
||||
patterns_with_steps = []
|
||||
|
||||
for pattern_xml in pattern_elements:
|
||||
# Chama a função auxiliar para calcular o array de steps
|
||||
steps_array = calculate_steps_from_notes(pattern_xml)
|
||||
|
||||
pattern_data = {
|
||||
'name': pattern_xml.attrib.get('name'),
|
||||
'pos': pattern_xml.attrib.get('pos'),
|
||||
'steps': steps_array, # <-- AQUI ESTÃO OS STEPS
|
||||
}
|
||||
patterns_with_steps.append(pattern_data)
|
||||
|
||||
# Adiciona a lista de patterns (com steps) ao instrumento
|
||||
instrument_info['patterns'] = patterns_with_steps
|
||||
# --- FIM DA INJEÇÃO DE STEPS ---
|
||||
|
||||
instruments.append(instrument_info)
|
||||
|
||||
# Lógica de Tags (Melhorada)
|
||||
# Define a tag principal da bassline com base nos plugins encontrados
|
||||
final_tag = ''
|
||||
if 'audiofileprocessor' in plugin_tags:
|
||||
final_tag = 'audiofileprocessor'
|
||||
elif 'kicker' in plugin_tags:
|
||||
final_tag = 'kicker'
|
||||
elif 'tripleoscillator' in plugin_tags:
|
||||
final_tag = 'tripleoscillator'
|
||||
elif plugin_tags:
|
||||
final_tag = list(plugin_tags)[0] # Pega o primeiro que encontrar
|
||||
|
||||
return {
|
||||
'bassline_name': track_name,
|
||||
'type': 'bassline',
|
||||
'tags': tags, #verificar aqui
|
||||
'tags': final_tag,
|
||||
'instruments': instruments
|
||||
}
|
||||
|
||||
# Se este arquivo for executado diretamente, ele não fará nada
|
||||
if __name__ == "__main__":
|
||||
print("Este arquivo contém a função parse_basslines() e deve ser importado por outro script.")
|
||||
Reference in new issue
Block a user