att base de dados

This commit is contained in:
JotaChina committed 2026-03-28 12:50:52 -03:00
1 parent ceb7908da4
commit 73693e5da9
14 files changed
+3010 -1

No files matched your search

+631
View File
@@ -0,0 +1,631 @@
import os
import sys
import multiprocessing
import concurrent.futures # NOVO: Necessário para usar o ProcessPoolExecutor
import csv
import time
import glob
from datetime import datetime, timedelta
# ================= 1. LIMITADOR DE CPU DINÂMICO =================
def configurar_limites_cpu():
try:
total_cores = multiprocessing.cpu_count()
print("--- Configuração de Hardware ---")
print(f"Núcleos Totais: {total_cores}")
print("Núcleos Alocados: 1 thread por processo (Multiprocessing Real)")
print("Núcleos Reservados para o Sistema: 1")
# IMPORTANTE: Em processamento paralelo real (ProcessPoolExecutor),
# as libs em C++ devem usar apenas "1" thread para evitar colisão na CPU (oversubscription)
os.environ["OMP_NUM_THREADS"] = "1"
os.environ["MKL_NUM_THREADS"] = "1"
os.environ["TF_NUM_INTRAOP_THREADS"] = "1"
os.environ["TF_NUM_INTEROP_THREADS"] = "1"
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
except Exception as e:
print(f"Aviso: Não foi possível limitar CPU automaticamente: {e}")
configurar_limites_cpu()
# ================= IMPORTS =================
import json
import yaml
import logging
import gc
import numpy as np
import psutil
import essentia
import essentia.standard as es
from tqdm import tqdm
import unicodedata
# Desativa logs internos do Essentia
essentia.log.info.active = False
essentia.log.warning.active = False
# ================= CONFIGURAÇÕES =================
BASE_DIR = "/var/www/html/trens/src_mmpSearch"
PASTA_WAVS = os.path.join(BASE_DIR, "wav")
PASTA_LOTES_YAML = os.path.join(BASE_DIR, "lotes_yaml")
# --- SISTEMA DE DOIS MODELOS ---
MODELO_EMBEDDING = "discogs-effnet-bs64-1.pb"
MODELO_CLASSIFIER = "genre_discogs400.pb"
MODELO_CLASSES = "genre_discogs400.json"
# Saídas
DIR_SAIDA = os.path.join(BASE_DIR, "saida_analises")
os.makedirs(DIR_SAIDA, exist_ok=True)
ARQUIVO_CHECKPOINT = os.path.join(DIR_SAIDA, "checkpoint_analises.jsonl")
ARQUIVO_FINAL_JSON = os.path.join(DIR_SAIDA, "db_final_completo.json")
ARQUIVO_AUDITORIA = os.path.join(DIR_SAIDA, "audit_performance.csv")
# Limites de Segurança
LIMITE_RAM_MINIMA_MB = 500
MAX_DURACAO_ANALISE = 20 * 60
# Logs
LOG_DIR = os.path.join(BASE_DIR, "logs/classificacao")
os.makedirs(LOG_DIR, exist_ok=True)
TIMESTAMP = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
logging.basicConfig(
filename=os.path.join(LOG_DIR, f"log_{TIMESTAMP}.log"),
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
)
DATABASE_YAML = {}
# Variáveis globais para os Workers conseguirem enxergar os modelos em memória
GLOBAL_EMBEDDING = None
GLOBAL_CLASSIFIER = None
GLOBAL_CLASSES = None
# ================= CLASSE DE AUDITORIA OTIMIZADA =================
class Auditoria:
def __init__(self, arquivo_csv):
self.arquivo_csv = arquivo_csv
self.inicio_global = time.time()
self.process = psutil.Process(os.getpid())
# Mantém o arquivo aberto em modo 'append' na memória (Resolve Gargalo HD)
cabecalho = not os.path.exists(self.arquivo_csv)
self.file = open(self.arquivo_csv, "a", newline="", encoding="utf-8")
self.writer = csv.writer(self.file)
if cabecalho:
self.writer.writerow(
[
"timestamp",
"arquivo",
"tamanho_mb",
"duracao_audio_s",
"tempo_proc_s",
"ram_uso_mb",
"cpu_percent",
]
)
self.file.flush()
def registrar_processamento(self, nome_arquivo, tamanho_bytes, duracao_audio, tempo_gasto):
try:
ram_mb = self.process.memory_info().rss / (1024 * 1024)
cpu_pct = self.process.cpu_percent(interval=None)
tamanho_mb = tamanho_bytes / (1024 * 1024)
agora = datetime.now().strftime("%H:%M:%S")
# Escreve na mesma conexão de arquivo aberta
self.writer.writerow(
[
agora,
nome_arquivo,
round(tamanho_mb, 2),
round(duracao_audio, 2),
round(tempo_gasto, 4),
round(ram_mb, 2),
cpu_pct,
]
)
self.file.flush()
except Exception as e:
logging.error(f"Erro ao auditar arquivo: {e}")
def verificar_marco(self, contagem):
pass # Você pode customizar logs de progresso aqui depois se quiser
def fechar(self):
try:
self.file.close()
except:
pass
# ================= FUNÇÕES AUXILIARES =================
def verificar_memoria_segura(tamanho_arquivo_bytes):
mem = psutil.virtual_memory()
livre_mb = mem.available / (1024 * 1024)
estimativa_uso_ram = tamanho_arquivo_bytes * 15
uso_mb = estimativa_uso_ram / (1024 * 1024)
if livre_mb < LIMITE_RAM_MINIMA_MB:
print(f" [!] RAM Crítica ({livre_mb:.1f}MB livres). Forçando limpeza...")
gc.collect()
time.sleep(2)
mem = psutil.virtual_memory()
if mem.available / (1024 * 1024) < LIMITE_RAM_MINIMA_MB:
return False, "RAM insuficiente no sistema"
if uso_mb > (livre_mb * 0.8):
return (
False,
f"Arquivo muito grande para RAM atual (Est: {uso_mb:.1f}MB, Livre: {livre_mb:.1f}MB)",
)
return True, "OK"
def normalizar_chave(texto):
if not texto:
return ""
s = str(texto)
if "/" in s:
s = os.path.basename(s)
s = os.path.splitext(s)[0]
s = unicodedata.normalize("NFKD", s).encode("ASCII", "ignore").decode("ASCII")
return s.lower().replace(" ", "").replace("-", "").replace("_", "").strip()
def carregar_database_yaml():
print(f"--- Carregando Metadados de Lotes YAML em: {PASTA_LOTES_YAML} ---")
padrao = os.path.join(PASTA_LOTES_YAML, "*.yml")
arquivos_lote = sorted(glob.glob(padrao))
db = {}
if not arquivos_lote:
logging.warning(f"Nenhum lote encontrado em {PASTA_LOTES_YAML}")
return {}
total_carregados = 0
for arquivo in arquivos_lote:
try:
with open(arquivo, "r", encoding="utf-8") as f:
dados_lote = yaml.safe_load(f)
if not dados_lote:
continue
lista = dados_lote if isinstance(dados_lote, list) else [dados_lote]
for proj in lista:
if not isinstance(proj, dict):
continue
candidatos = [
proj.get("file"),
proj.get("original_title"),
proj.get("src"),
]
for k in candidatos:
if k:
chave = normalizar_chave(k)
if chave:
db[chave] = proj
total_carregados += len(lista)
print(f"-> Carregado: {os.path.basename(arquivo)} ({len(lista)} projetos)")
except Exception as e:
logging.error(f"Erro ao ler lote {arquivo}: {e}")
print(f"X Erro ao ler {os.path.basename(arquivo)}")
print(f"--- Total de Projetos no DB (Memória): {len(db)} (de {total_carregados} lidos) ---")
return db
def carregar_checkpoint():
processados = set()
dados_existentes = []
if os.path.exists(ARQUIVO_CHECKPOINT):
with open(ARQUIVO_CHECKPOINT, "r", encoding="utf-8") as f:
for linha in f:
try:
obj = json.loads(linha)
reprocessar = False
if "analise_ia" in obj and "erro" in obj["analise_ia"]:
reprocessar = True
if "analise_tecnica" in obj:
est = obj["analise_tecnica"].get("estrutura_segundos", [])
if len(est) == 2 and est[1] == 12.0:
reprocessar = True
if reprocessar:
continue
processados.add(obj["arquivo"])
dados_existentes.append(obj)
except:
pass
return processados, dados_existentes
def salvar_progresso(resultado):
with open(ARQUIVO_CHECKPOINT, "a", encoding="utf-8") as f:
json.dump(resultado, f, ensure_ascii=False)
f.write("\n")
def calcular_complexidade(projeto_yaml):
score = 0
W_INST_SYNTH, W_INST_SAMPLE = 3.0, 1.0
W_AUTOMATION, W_PATTERN, W_FX = 2.0, 0.5, 1.5
stats = {"num_automations": 0, "num_patterns": 0, "num_effects": 0, "num_synths": 0}
tracks = projeto_yaml.get("tracks", [])
for track in tracks:
instruments = track.get("instruments", [])
for inst in instruments:
nome_plugin = inst.get("plugin_name", "").lower()
nome_inst = inst.get("instrument_name", "").lower()
if "automation" in nome_inst or "automation" in nome_plugin:
score += W_AUTOMATION
stats["num_automations"] += 1
elif nome_plugin in [
"zynaddsubfx",
"monstro",
"lb302",
"watsyn",
"opyd",
"sfxr",
]:
score += W_INST_SYNTH
stats["num_synths"] += 1
else:
score += W_INST_SAMPLE
patterns = inst.get("patterns", [])
qtd_patterns = len(patterns)
score += qtd_patterns * W_PATTERN
stats["num_patterns"] += qtd_patterns
fxchain = inst.get("fxchain", {})
if str(fxchain.get("enabled")) == "1":
num_fx = int(fxchain.get("numofeffects", 0))
score += num_fx * W_FX
stats["num_effects"] += num_fx
if score <= 15:
estrelas = 1
elif score <= 40:
estrelas = 2
elif score <= 80:
estrelas = 3
elif score <= 120:
estrelas = 4
else:
estrelas = 5
return {"score_total": round(score, 2), "estrelas": estrelas, "detalhes": stats}
# ================= NÚCLEO DE ANÁLISE =================
def detectar_estrutura(audio_vec, sample_rate, duration):
try:
if duration < 30:
return [0.0, round(duration, 2)]
frame_size = 2048
hop_size = 512
rms_algo = es.RMS()
flux_algo = es.Flux()
w = es.Windowing(type="hann")
spec = es.Spectrum()
rms_curve = []
flux_curve = []
for frame in es.FrameGenerator(
audio_vec, frameSize=frame_size, hopSize=hop_size
):
s = spec(w(frame))
rms_curve.append(rms_algo(frame))
flux_curve.append(flux_algo(s))
min_len = min(len(rms_curve), len(flux_curve))
rms_curve = np.array(rms_curve[:min_len])
flux_curve = np.array(flux_curve[:min_len])
if np.max(rms_curve) > 0:
rms_curve = rms_curve / np.max(rms_curve)
if np.max(flux_curve) > 0:
flux_curve = flux_curve / np.max(flux_curve)
novelty = rms_curve * 0.5 + flux_curve * 0.5
segundos_por_frame = hop_size / sample_rate
pontos_mudanca = [0.0]
window_frames = int(10 / segundos_por_frame)
step_frames = int(5 / segundos_por_frame)
total_frames = len(novelty)
for i in range(0, total_frames - window_frames, step_frames):
janela = novelty[i : i + window_frames]
if np.std(janela) > 0.1:
pico_idx = i + np.argmax(janela)
tempo = round(pico_idx * segundos_por_frame, 2)
if tempo - pontos_mudanca[-1] > 15:
pontos_mudanca.append(tempo)
pontos_mudanca.append(round(duration, 2))
return pontos_mudanca
except Exception as e:
logging.error(f"Erro estrutura nova: {e}")
return [0.0, round(duration, 2)]
def analisar_faixa(caminho_arquivo, embedding_model, classifier_model, classes_raw):
# --- AUDITORIA: INÍCIO DO TIMER ---
inicio_timer = time.perf_counter()
tamanho_arquivo = os.path.getsize(caminho_arquivo)
pode_processar, motivo = verificar_memoria_segura(tamanho_arquivo)
if not pode_processar:
logging.warning(f"SKIPPED {os.path.basename(caminho_arquivo)}: {motivo}")
return None, 0, 0
nome_arquivo = os.path.basename(caminho_arquivo)
chave_busca = normalizar_chave(nome_arquivo)
dados_humano = DATABASE_YAML.get(chave_busca)
metadata = {
"arquivo": nome_arquivo,
"caminho": caminho_arquivo,
"dados_projeto": {},
"analise_tecnica": {},
"analise_ia": {},
}
if dados_humano:
metadata["dados_projeto"] = {
"titulo": dados_humano.get("original_title") or dados_humano.get("file"),
"bpm_original": dados_humano.get("bpm"),
"tags_yaml": dados_humano.get("tags"),
"autor": dados_humano.get("author", "Desconhecido"),
}
else:
metadata["analise_tecnica"]["complexidade"] = {
"score_total": 0,
"estrelas": 0,
"detalhes": "YAML não encontrado",
}
duracao_real = 0.0
# 1. ANÁLISE TÉCNICA E EXTRAÇÃO ÚNICA (HQ 44.1kHz)
try:
# Lê do HD APENAS UMA VEZ
loader_hq = es.MonoLoader(filename=caminho_arquivo, sampleRate=44100)
audio_hq = loader_hq()
if len(audio_hq) > (MAX_DURACAO_ANALISE * 44100):
audio_hq = audio_hq[: int(MAX_DURACAO_ANALISE * 44100)]
duracao_real = len(audio_hq) / 44100.0
audio_hq_vec = essentia.array(audio_hq)
rhythm = es.RhythmExtractor2013(method="multifeature")
bpm, _, conf, _, _ = rhythm(audio_hq_vec)
key_ex = es.KeyExtractor()
key, scale, _ = key_ex(audio_hq_vec)
loudness = es.Loudness()(audio_hq_vec)
estrutura = detectar_estrutura(audio_hq_vec, 44100, duracao_real)
bpm_final = round(bpm, 1)
origem = "algoritmo"
if dados_humano and dados_humano.get("bpm"):
try:
bpm_str = str(dados_humano.get("bpm")).replace("'", "").replace('"', "")
b = float(bpm_str)
if b > 0:
bpm_final = b
origem = "yaml (humano)"
except:
pass
metadata["analise_tecnica"].update(
{
"bpm": bpm_final,
"origem_bpm": origem,
"tom": key,
"escala": scale,
"intensidade_db": round(loudness, 2),
"estrutura_segundos": estrutura,
"duracao_detectada": round(duracao_real, 2),
}
)
metadata["analise_tecnica"]["complexidade"] = (
calcular_complexidade(dados_humano) if dados_humano else None
)
except Exception as e:
logging.error(f"Erro math {nome_arquivo}: {e}")
metadata["analise_tecnica"]["erro"] = str(e)
return metadata, 0, 0
# 2. ANÁLISE IA (LQ 16kHz via Resample da RAM - Resolve Leitura Dupla de HD)
if embedding_model and classifier_model and classes_raw:
try:
resample_algo = es.Resample(inputSampleRate=44100, outputSampleRate=16000)
audio_lq_vec = resample_algo(audio_hq_vec)
embeddings = embedding_model(audio_lq_vec)
predictions = classifier_model(embeddings)
avg_preds = np.mean(predictions, axis=0)
top_indices = np.argsort(avg_preds)[::-1][:10]
tags_detectadas = []
genero_pai = "Unknown"
for i, idx in enumerate(top_indices):
score = float(avg_preds[idx])
if score < 0.05:
continue
nome_full = classes_raw[idx]
partes = nome_full.split("---")
tag_limpa = partes[-1]
if i == 0:
genero_pai = partes[0] if len(partes) > 0 else tag_limpa
tags_detectadas.append({"tag": tag_limpa, "score": round(score, 3)})
metadata["analise_ia"] = {
"genero_macro": genero_pai,
"estilo_principal": tags_detectadas[0]["tag"] if tags_detectadas else "Unknown",
"nuvem_tags": tags_detectadas,
}
del audio_lq_vec
del embeddings
except Exception as e:
msg = f"Erro IA Pipeline: {str(e)}"
logging.error(msg)
metadata["analise_ia"] = {"erro": msg}
else:
metadata["analise_ia"] = {"status": "Modelos nao carregados"}
# Limpeza Limpa
del audio_hq
del audio_hq_vec
gc.collect()
tempo_gasto = time.perf_counter() - inicio_timer
return metadata, duracao_real, tempo_gasto
# ================= MAIN (Multiprocessing) =================
def worker_analise(caminho):
# Processa usando as instâncias dos modelos carregadas na thread mestre
global GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES
return analisar_faixa(caminho, GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES)
def main():
print("\n--- INICIANDO PROCESSADOR + AUDITORIA ---")
# Referencia variávies globais do script para alimentar os childs process do pool
global DATABASE_YAML, GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES
DATABASE_YAML = carregar_database_yaml()
ja_processados, lista_resultados = carregar_checkpoint()
auditor = Auditoria(ARQUIVO_AUDITORIA)
if not os.path.exists(PASTA_WAVS):
print("CRÍTICO: Pasta WAV não encontrada!")
return
todos = [f for f in os.listdir(PASTA_WAVS) if f.lower().endswith((".wav", ".ogg"))]
a_fazer = [os.path.join(PASTA_WAVS, f) for f in todos if f not in ja_processados]
total = len(a_fazer)
print(f"Total Arquivos: {len(todos)} | Novos a Fazer: {total}")
print(f"Auditoria será salva em: {ARQUIVO_AUDITORIA}")
# === CARREGAMENTO MODELOS GLOBAIS ===
path_embed = MODELO_EMBEDDING if os.path.exists(MODELO_EMBEDDING) else os.path.join(BASE_DIR, MODELO_EMBEDDING)
path_class = MODELO_CLASSIFIER if os.path.exists(MODELO_CLASSIFIER) else os.path.join(BASE_DIR, MODELO_CLASSIFIER)
path_json = MODELO_CLASSES if os.path.exists(MODELO_CLASSES) else os.path.join(BASE_DIR, MODELO_CLASSES)
if os.path.exists(path_embed) and os.path.exists(path_class) and os.path.exists(path_json):
print("--- Carregando Modelos de IA ---")
try:
with open(path_json, "r") as f:
GLOBAL_CLASSES = json.load(f)["classes"]
print("[1/2] Carregando Extrator de Embeddings...")
if hasattr(es, "TensorflowPredictEffnetDiscogs"):
GLOBAL_EMBEDDING = es.TensorflowPredictEffnetDiscogs(graphFilename=path_embed, output="PartitionedCall:1")
else:
print(" -> Usando TensorflowPredict genérico.")
GLOBAL_EMBEDDING = es.TensorflowPredict(graphFilename=path_embed, input="serving_default_model_Placeholder", output="PartitionedCall:1")
print(" -> Embeddings carregados com sucesso.")
GLOBAL_CLASSIFIER = es.TensorflowPredict2D(graphFilename=path_class, input="serving_default_model_Placeholder", output="PartitionedCall:0")
print("[2/2] Classificador de Gênero carregado.")
except Exception as e:
print(f"ERRO CRÍTICO AO CARREGAR IA: {e}")
return
else:
print("Arquivos de modelo incompletos.")
return
if total == 0:
print("Tudo atualizado.")
return
# ProcessPoolExecutor lidará com N filas ao mesmo tempo
max_workers = max(1, multiprocessing.cpu_count() - 1)
print(f"-> Iniciando processamento paralelo com {max_workers} workers.")
contagem_sessao = 0
try:
with concurrent.futures.ProcessPoolExecutor(max_workers=max_workers) as executor:
resultados_futuros = {executor.submit(worker_analise, caminho): caminho for caminho in a_fazer}
# O as_completed processa os que terminam mais rápido em vez de travar na ordem
for future in tqdm(concurrent.futures.as_completed(resultados_futuros), total=len(a_fazer), unit="track"):
try:
res, duracao, tempo_gasto = future.result()
caminho = resultados_futuros[future]
if res:
salvar_progresso(res)
lista_resultados.append(res)
tamanho = os.path.getsize(caminho)
auditor.registrar_processamento(res['arquivo'], tamanho, duracao, tempo_gasto)
contagem_sessao += 1
auditor.verificar_marco(contagem_sessao)
except Exception as e:
logging.error(f"Erro ao processar um dos arquivos no worker: {e}")
except KeyboardInterrupt:
print("\nParando paralelismo graciosamente...")
finally:
auditor.fechar() # Fecha o CSV com segurança
tempo_total_sessao = time.time() - auditor.inicio_global
print("\n--- FIM DO PROCESSAMENTO ---")
print(f"Tempo Total: {str(timedelta(seconds=int(tempo_total_sessao)))}")
print("Gerando JSON final consolidado...")
with open(ARQUIVO_FINAL_JSON, "w", encoding="utf-8") as f:
json.dump(lista_resultados, f, indent=4, ensure_ascii=False)
print(f"Dados JSON: {ARQUIVO_FINAL_JSON}")
print(f"Auditoria CSV: {ARQUIVO_AUDITORIA}")
if __name__ == "__main__":
main()
@@ -0,0 +1,457 @@
import yaml
import json
import os
import logging
import csv
import unicodedata
import time
import psutil
import glob # Import necessário para ler múltiplos arquivos
from datetime import datetime, timedelta
# --- CONFIGURAÇÕES GERAIS ---
BASE_DIR = "/var/www/html/trens/src_mmpSearch"
LOG_DIR = os.path.join(BASE_DIR, "logs/classificacao_pedagogica")
os.makedirs(LOG_DIR, exist_ok=True)
TIMESTAMP = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# Configuração de Log
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - CLASSIFICADOR - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(os.path.join(LOG_DIR, f"log_pedagogico_{TIMESTAMP}.log")),
logging.StreamHandler(),
],
)
# --- ARQUIVOS DE ENTRADA/SAÍDA ---
def encontrar_caminho(nome_arquivo):
"""Procura o arquivo em locais comuns do projeto."""
candidatos = [
os.path.join(BASE_DIR, "saida_analises", nome_arquivo),
os.path.join(BASE_DIR, "data", nome_arquivo),
os.path.join(BASE_DIR, nome_arquivo),
nome_arquivo,
]
for c in candidatos:
if os.path.exists(c):
return c
return nome_arquivo
# --- MUDANÇA AQUI: Aponta para a pasta dos lotes ---
PASTA_LOTES_YAML = os.path.join(BASE_DIR, "lotes_yaml")
# ARQUIVO_YAML = "all.yml" <-- Removido/Comentado
ARQUIVO_AUDIO_INPUT = encontrar_caminho("db_final_completo.json")
ARQUIVO_FINAL_OUTPUT = os.path.join(
BASE_DIR, "saida_analises", "db_projetos_classificados.json"
)
ARQUIVO_AUDITORIA = os.path.join(
BASE_DIR, "saida_analises", "audit_classificacao_pedagogica.csv"
)
# --- LISTAS DE REFERÊNCIA ---
PLUGINS_NATIVOS = [
"tripleoscillator",
"zynaddsubfx",
"sfxr",
"organic",
"audiofileprocessor",
"lb302",
"kicker",
"watsyn",
"bitinvader",
"freeboy",
"mallets",
"vibed",
]
# --- CLASSE DE AUDITORIA DE PERFORMANCE ---
class AuditoriaPerformance:
def __init__(self):
self.inicio_global = time.time()
self.process = psutil.Process(os.getpid())
self.marcos = [1, 10, 100, 500, 1000, 2000, 5000]
def get_metricas(self):
"""Retorna uso atual de RAM (MB) e CPU (%)"""
try:
ram_mb = self.process.memory_info().rss / (1024 * 1024)
cpu_pct = self.process.cpu_percent(interval=None)
return round(ram_mb, 2), cpu_pct
except:
return 0.0, 0.0
def verificar_marco(self, contagem):
if contagem in self.marcos:
tempo_decorrido = time.time() - self.inicio_global
logging.info(
f"--- MARCO: {contagem} projetos processados em {str(timedelta(seconds=int(tempo_decorrido)))} ---"
)
# --- FUNÇÕES AUXILIARES ---
def normalizar_chave(texto):
if not texto:
return ""
s = str(texto)
if "." in s:
s = os.path.splitext(s)[0]
s = unicodedata.normalize("NFKD", s).encode("ASCII", "ignore").decode("ASCII")
return s.lower().replace(" ", "").replace("-", "").replace("_", "").strip()
def calcular_nivel_ponderado(
num_tracks, tem_automacao, usa_zyn, qtd_secoes_audio, fx_criativos
):
pontos = 0
# 1. Volume
if num_tracks >= 8:
pontos += 1
if num_tracks >= 16:
pontos += 2
if num_tracks >= 32:
pontos += 3
if num_tracks >= 64:
pontos += 4
elif num_tracks >= 128:
pontos += 5
# 2. Técnica
if tem_automacao:
pontos += 2
if usa_zyn:
pontos += 1.5
if fx_criativos:
pontos += 1
# 3. Estrutura (Áudio)
if qtd_secoes_audio >= 3:
pontos += 1.5
elif qtd_secoes_audio >= 5:
pontos += 3
elif qtd_secoes_audio >= 10:
pontos += 5
if pontos < 3:
return "Iniciante"
if pontos < 6:
return "Intermediário"
return "Avançado"
def analisar_tags_pedagogicas(tracks, bpm, qtd_secoes):
tags = []
tem_kicker = False
tem_zyn = False
tem_automacao = False
tem_fx_chain = False
rolagem_trap = False
for t in tracks:
inst = t.get("instrumenttrack")
if not inst:
continue
nome = inst.get("name", "").lower()
plugin = inst.get("plugin_name", "").lower()
if plugin == "kicker":
tem_kicker = True
if plugin == "zynaddsubfx":
tem_zyn = True
if "automation" in str(inst).lower():
tem_automacao = True
fx = inst.get("fxchain", {})
if fx and int(fx.get("numofeffects", 0)) > 0:
tem_fx_chain = True
patterns = inst.get("patterns", [])
if patterns:
for pat in patterns:
steps = pat.get("steps", [])
consecutivos = 0
for step in steps:
val = 1 if step else 0
consecutivos = (consecutivos + 1) if val else 0
if consecutivos >= 3 and ("hat" in nome):
rolagem_trap = True
if tem_kicker:
tags.append("Síntese de Bateria")
if tem_zyn:
tags.append("Design de Som Avançado")
if tem_automacao:
tags.append("Automação")
if tem_fx_chain:
tags.append("Mixagem com FX")
if rolagem_trap and bpm > 115:
tags.append("Trap Rhythm")
if bpm < 100 and not rolagem_trap:
tags.append("Boombap Groove")
if qtd_secoes >= 4:
tags.append("Arranjo Completo")
elif qtd_secoes <= 1:
tags.append("Loop Estático")
else:
tags.append("Estrutura Simples")
return tags, tem_automacao, tem_zyn, tem_fx_chain
# --- FUNÇÃO PARA CARREGAR LOTES YAML ---
def carregar_todos_yamls(pasta_lotes):
"""Lê todos os arquivos YAML da pasta especificada e retorna uma lista única."""
logging.info(f"Buscando arquivos YAML em: {pasta_lotes}")
padrao = os.path.join(pasta_lotes, "*.yml")
arquivos = sorted(glob.glob(padrao))
todos_projetos = []
if not arquivos:
logging.warning("Nenhum arquivo YAML encontrado na pasta de lotes.")
# Tenta fallback para o arquivo único antigo se existir
caminho_unico = encontrar_caminho("all.yml")
if os.path.exists(caminho_unico):
logging.info(f"Fallback: Carregando arquivo único {caminho_unico}")
with open(caminho_unico, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
return []
for arq in arquivos:
try:
logging.info(f"Carregando lote: {os.path.basename(arq)}")
with open(arq, "r", encoding="utf-8") as f:
lote = yaml.safe_load(f)
if lote:
# Garante que seja uma lista
if isinstance(lote, list):
todos_projetos.extend(lote)
elif isinstance(lote, dict):
todos_projetos.append(lote)
except Exception as e:
logging.error(f"Erro ao ler arquivo {arq}: {e}")
logging.info(f"Total de projetos carregados dos lotes: {len(todos_projetos)}")
return todos_projetos
# --- MAIN ---
def main():
logging.info("--- INICIANDO CLASSIFICADOR PEDAGÓGICO (COM AUDITORIA E LOTES) ---")
auditor = AuditoriaPerformance()
# 1. Carregar YAML (Adaptado para Lotes)
if not os.path.exists(PASTA_LOTES_YAML):
logging.warning(f"Pasta de lotes não encontrada: {PASTA_LOTES_YAML}")
# Tenta criar se não existir (embora deva existir com arquivos)
# os.makedirs(PASTA_LOTES_YAML, exist_ok=True)
dados_projetos = carregar_todos_yamls(PASTA_LOTES_YAML)
if not dados_projetos:
logging.critical("Nenhum dado de projeto carregado. Encerrando.")
return
# 2. Carregar JSON de Áudio
lookup_audio = {}
total_audio_carregado = 0
if os.path.exists(ARQUIVO_AUDIO_INPUT):
logging.info(f"Lendo Análise de Áudio: {ARQUIVO_AUDIO_INPUT}")
with open(ARQUIVO_AUDIO_INPUT, "r", encoding="utf-8") as f:
lista_audio = json.load(f)
for item in lista_audio:
chave = normalizar_chave(item.get("arquivo", ""))
if chave:
lookup_audio[chave] = item
total_audio_carregado = len(lista_audio)
else:
logging.warning(f"Arquivo de áudio {ARQUIVO_AUDIO_INPUT} não encontrado.")
db_final = []
estatisticas = {"Iniciante": 0, "Intermediário": 0, "Avançado": 0, "Total": 0}
# --- PREPARAR CSV DE AUDITORIA ---
with open(ARQUIVO_AUDITORIA, "w", newline="", encoding="utf-8") as csvfile:
writer = csv.writer(csvfile)
# Cabeçalho expandido com métricas de performance
writer.writerow(
[
"Timestamp",
"ID_Projeto",
"Audio_Encontrado",
"Qtd_Secoes",
"Nivel",
"Genero_Final",
"Tempo_Proc_ms",
"RAM_Uso_MB",
"Tags",
]
)
logging.info(f"Processando {len(dados_projetos)} projetos...")
contagem_processados = 0
for proj in dados_projetos:
if not proj or not isinstance(proj, dict):
continue
# --- INÍCIO TIMER INDIVIDUAL ---
inicio_item = time.perf_counter()
nome_arquivo = proj.get("file", "")
if not nome_arquivo:
continue
try:
# Lógica de Classificação
chave_proj = normalizar_chave(nome_arquivo)
dados_audio = lookup_audio.get(chave_proj, {})
analise_tec = dados_audio.get("analise_tecnica", {})
analise_ia = dados_audio.get("analise_ia", {})
raw_bpm = proj.get("bpm", 120)
try:
bpm_projeto = float(raw_bpm)
except:
bpm_projeto = 120.0
est_seg = analise_tec.get("estrutura_segundos", [])
qtd_secoes = len(est_seg) if isinstance(est_seg, list) else 1
tracks = proj.get("tracks", [])
tags, tem_auto, tem_zyn, tem_fx = analisar_tags_pedagogicas(
tracks, bpm_projeto, qtd_secoes
)
plugins_usados = set()
usa_vst_externo = False
for t in tracks:
inst_track = t.get("instrumenttrack", {})
if not inst_track:
continue
p = inst_track.get("plugin_name", "")
if p:
p_norm = p.lower()
plugins_usados.add(p_norm)
if p_norm not in PLUGINS_NATIVOS:
usa_vst_externo = True
nivel = calcular_nivel_ponderado(
len(tracks), tem_auto, tem_zyn, qtd_secoes, tem_fx
)
estatisticas[nivel] += 1
estatisticas["Total"] += 1
genero = "Desconhecido"
if analise_ia and "estilo_principal" in analise_ia:
genero = analise_ia["estilo_principal"]
elif (
"genero_macro" in analise_ia
and analise_ia["genero_macro"] != "Unknown"
):
genero = analise_ia["genero_macro"]
if genero == "Desconhecido" or genero == "Unknown":
if "Trap Rhythm" in tags:
genero = "Trap (Rule-based)"
elif bpm_projeto > 120:
genero = "Electronic (Rule-based)"
else:
genero = "HipHop/Downtempo (Rule-based)"
# Objeto Final
item = {
"id": nome_arquivo,
"titulo": proj.get("original_title", nome_arquivo),
"match_audio": bool(dados_audio),
"pedagogia": {
"nivel": nivel,
"tags_aprendizado": tags,
"plugins_principais": list(plugins_usados),
"requer_vst_externo": usa_vst_externo,
},
"tecnica": {
"bpm": bpm_projeto,
"tom": analise_tec.get("tom", "N/A"),
"escala": analise_tec.get("escala", "N/A"),
"estrutura_qtd_secoes": qtd_secoes,
"intensidade_media": analise_tec.get("intensidade_db", "N/A"),
},
"classificacao": {
"genero": genero,
"mood": "Energético"
if analise_tec.get("intensidade_db", -20) > -10
else "Suave",
},
}
db_final.append(item)
# --- FIM TIMER INDIVIDUAL ---
tempo_ms = (time.perf_counter() - inicio_item) * 1000
ram_atual, _ = auditor.get_metricas()
ts_agora = datetime.now().strftime("%H:%M:%S")
# Registra Auditoria com Performance
writer.writerow(
[
ts_agora,
nome_arquivo,
"SIM" if dados_audio else "NAO",
qtd_secoes,
nivel,
genero,
f"{tempo_ms:.3f}", # Tempo em milissegundos com 3 casas
f"{ram_atual:.2f}", # RAM em MB
"|".join(tags),
]
)
contagem_processados += 1
auditor.verificar_marco(contagem_processados)
except Exception as e:
logging.error(
f"Erro ao processar projeto '{proj.get('file', 'unknown')}': {e}"
)
continue
# Salvar Resultado JSON
logging.info(f"Salvando JSON Final: {ARQUIVO_FINAL_OUTPUT}")
with open(ARQUIVO_FINAL_OUTPUT, "w", encoding="utf-8") as f:
json.dump(db_final, f, indent=4, ensure_ascii=False)
tempo_total = time.time() - auditor.inicio_global
logging.info("--- RELATÓRIO FINAL ---")
logging.info(f"Tempo Total de Execução: {str(timedelta(seconds=int(tempo_total)))}")
logging.info(f"Total Processado: {estatisticas['Total']}")
logging.info(f"Iniciantes: {estatisticas['Iniciante']}")
logging.info(f"Intermediários: {estatisticas['Intermediário']}")
logging.info(f"Avançados: {estatisticas['Avançado']}")
logging.info(
f"Taxa de Casamento com Áudio: {len(lookup_audio)}/{total_audio_carregado}"
)
logging.info(f"Auditoria salva em: {ARQUIVO_AUDITORIA}")
if __name__ == "__main__":
main()
@@ -0,0 +1,274 @@
import yaml
import json
import os
import logging
# --- CONFIGURAÇÕES DE LOG ---
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - CLASSIFICADOR - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler("classificacao_pedagogica.log"),
logging.StreamHandler(),
],
)
# --- CONFIGURAÇÕES ---
ARQUIVO_YAML = "all.yml"
ARQUIVO_AUDIO = "analise_audio.json"
ARQUIVO_FINAL = "db_projetos_classificados.json"
PLUGINS_NATIVOS = [
"tripleoscillator",
"zynaddsubfx",
"sfxr",
"organic",
"audiofileprocessor",
"lb302",
"kicker",
"watsyn",
"bitinvader",
"freeboy",
"mallets",
"vibed",
]
# --- LÓGICA PEDAGÓGICA AVANÇADA ---
def calcular_nivel_ponderado(
num_tracks, tem_automacao, plugins_complexos, qtd_secoes_audio, fx_criativos
):
"""
Calcula nível pedagógico (Iniciante, Intermediário, Avançado)
baseado em esforço de engenharia e complexidade musical.
"""
pontos = 0
# 1. Volume de Trabalho
if num_tracks >= 8:
pontos += 1
if num_tracks >= 16:
pontos += 1
# 2. Profundidade Técnica
if tem_automacao:
pontos += 2 # Automação é sinal forte de polimento
if plugins_complexos:
pontos += 1.5 # Usar ZynAddSubFx requer estudo
if fx_criativos:
pontos += 1 # Usar efeitos além do básico
# 3. Estrutura Musical (Vinda do Essentia)
# Se o áudio tem > 3 seções detectadas, há arranjo (não é só loop)
if qtd_secoes_audio >= 3:
pontos += 2
elif qtd_secoes_audio >= 5:
pontos += 3
# Classificação
if pontos < 3:
return "Iniciante"
if pontos < 6:
return "Intermediário"
return "Avançado"
def analisar_tags_pedagogicas(tracks, bpm, qtd_secoes, tom, escala):
tags = []
# Flags de estado técnico
tem_zyn = False
tem_automacao = False
tem_fx_chain = False
qtd_nativos = 0
qtd_vst_externos = 0
tem_audio_processor = False
for t in tracks:
inst = t.get("instrumenttrack")
if not inst:
continue
plugin = inst.get("plugin_name", "").lower()
# Mapeamento de Plugins e Dependências
if plugin in PLUGINS_NATIVOS:
qtd_nativos += 1
elif plugin == "vestige":
qtd_vst_externos += 1
if plugin == "zynaddsubfx":
tem_zyn = True
if plugin == "audiofileprocessor":
tem_audio_processor = True
if "automation" in str(inst).lower():
tem_automacao = True
fx = inst.get("fxchain", {})
if fx and int(fx.get("numofeffects", 0)) > 0:
tem_fx_chain = True
# --- 1. Estética da Escassez e Modulação Marginal ---
if qtd_nativos > 0 and qtd_vst_externos == 0:
tags.append("Estética da Escassez: Síntese Nativa")
if tem_automacao and qtd_vst_externos == 0:
tags.append("Design de Som Marginal (Automação como Timbre)")
# --- 2. Taxonomia de Bloom (Carga Cognitiva) ---
if tem_zyn or tem_fx_chain:
tags.append("Bloom: Criação (Sound Design Complexo)")
elif tem_audio_processor and not tem_automacao:
tags.append("Bloom: Aplicação (Sample Inalterado)")
# --- 3. Fluência Estrutural (Macroforma Musical) ---
if qtd_secoes >= 3:
tags.append("Fluência Estrutural: Arranjo Completo (Macroforma)")
else:
tags.append("Fluência Estrutural: Loop Estático")
# --- 4. Acessibilidade Ergonômica (Música de Computador) ---
if tom == "A" and escala == "minor":
tags.append("Ergonomia Autodidata (Teclado QWERTY / Lá Menor)")
# --- 5. Herança Cultural / Andamento ---
if 80 <= bpm <= 95:
tags.append("Cadência Clássica (Boom Bap)")
elif bpm >= 130:
tags.append("Estética Trap/Funk/Eletrônica / Alta Energia")
return tags
def main():
logging.info("Iniciando Classificador Mestre...")
# 1. Carregar YAML
if not os.path.exists(ARQUIVO_YAML):
logging.critical(f"{ARQUIVO_YAML} não encontrado.")
return
with open(ARQUIVO_YAML, "r", encoding="utf-8") as f:
dados_projetos = yaml.safe_load(f)
# 2. Carregar JSON de Áudio
lookup_audio = {}
if os.path.exists(ARQUIVO_AUDIO):
with open(ARQUIVO_AUDIO, "r", encoding="utf-8") as f:
lista_audio = json.load(f)
for item in lista_audio:
# Normaliza chave de busca (nome do arquivo sem extensão)
nome_base = os.path.splitext(item["arquivo"])[0]
lookup_audio[nome_base] = item
else:
logging.warning(
"JSON de áudio não encontrado. Classificação será apenas estática."
)
db_final = []
logging.info(f"Processando {len(dados_projetos)} entradas do YAML...")
for proj in dados_projetos:
if not proj:
continue
try:
nome_arquivo = proj.get("file", "")
if not nome_arquivo:
continue
# Tenta casar com dados de áudio
# Nota: Seu YAML usa nomes como 'trap-fyrebreak', o arquivo de audio deve ser 'trap-fyrebreak.wav'
# O lookup_audio já está sem extensão.
dados_audio = lookup_audio.get(nome_arquivo, {})
analise_tec = dados_audio.get("analise_tecnica", {})
analise_ia = dados_audio.get("analise_ia", {})
# Dados Técnicos
raw_bpm = proj.get("bpm", 120)
bpm_projeto = (
float(raw_bpm) if str(raw_bpm).replace(".", "", 1).isdigit() else 120.0
)
# Feature nova: Quantidade de seções (Intro, Verso...)
qtd_secoes = analise_tec.get("qtd_secoes_detectadas", 1)
# Análise Estática (Tracks e Plugins)
tracks = proj.get("tracks", [])
tags, tem_auto, tem_zyn, tem_fx = analisar_tags_pedagogicas(
tracks=proj.get("tracks", []),
bpm=bpm_projeto,
qtd_secoes=qtd_secoes,
tom=analise_tec.get("tom", "N/A"),
escala=analise_tec.get("escala", "N/A"),
)
# Verifica plugins externos
plugins_usados = set()
usa_vst_externo = False
for t in tracks:
p = t.get("instrumenttrack", {}).get("plugin_name", "")
if p:
plugins_usados.add(p)
if p.lower() not in PLUGINS_NATIVOS:
usa_vst_externo = True
# Cálculo de Nível (Nova Lógica)
nivel = calcular_nivel_ponderado(
len(tracks), tem_auto, tem_zyn, qtd_secoes, tem_fx
)
# Define Gênero (IA tem prioridade, depois regras manuais)
genero = analise_ia.get("genero_predito", "Desconhecido")
if genero == "Desconhecido" or analise_ia.get("confianca_genero", 0) < 0.4:
# Fallback manual simples
if "Trap Rhythm" in tags:
genero = "Trap"
elif bpm_projeto > 120:
genero = "Electronic"
else:
genero = "HipHop/Downtempo"
# Montagem do Objeto
item = {
"id": nome_arquivo,
"titulo": proj.get("original_title", nome_arquivo),
"pedagogia": {
"nivel": nivel,
"tags_aprendizado": tags,
"plugins_principais": list(plugins_usados),
"requer_vst_externo": usa_vst_externo,
},
"tecnica": {
"bpm": bpm_projeto,
"tom": analise_tec.get("tom", "N/A"),
"escala": analise_tec.get("escala", "N/A"),
"estrutura_detectada": f"{qtd_secoes} seções distintas",
},
"classificacao": {
"genero": genero,
"mood": "Energético"
if analise_tec.get("intensidade", -20) > -10
else "Suave",
},
}
db_final.append(item)
except Exception as e:
logging.error(f"Erro ao processar projeto '{proj.get('file')}': {e}")
continue
# Salvar Resultado
with open(ARQUIVO_FINAL, "w", encoding="utf-8") as f:
json.dump(db_final, f, indent=4, ensure_ascii=False)
logging.info(f"Sucesso! {len(db_final)} projetos classificados e salvos.")
if __name__ == "__main__":
main()
@@ -0,0 +1,468 @@
{
"name": "EffnetDiscogs",
"type": "Music style classification and embeddings",
"link": "https://essentia.upf.edu/models/music-style-classification/discogs-effnet/discogs-effnet-bs64-1.pb",
"version": "1",
"description": "Prediction of the top-400 music styles in the Discogs-4M dataset (unreleased)",
"author": "Pablo Alonso",
"email": "pablo.alonso@upf.edu",
"release_date": "2022-02-17",
"framework": "tensorflow",
"framework_version": "2.8.0",
"classes": [
"Blues---Boogie Woogie",
"Blues---Chicago Blues",
"Blues---Country Blues",
"Blues---Delta Blues",
"Blues---Electric Blues",
"Blues---Harmonica Blues",
"Blues---Jump Blues",
"Blues---Louisiana Blues",
"Blues---Modern Electric Blues",
"Blues---Piano Blues",
"Blues---Rhythm & Blues",
"Blues---Texas Blues",
"Brass & Military---Brass Band",
"Brass & Military---Marches",
"Brass & Military---Military",
"Children's---Educational",
"Children's---Nursery Rhymes",
"Children's---Story",
"Classical---Baroque",
"Classical---Choral",
"Classical---Classical",
"Classical---Contemporary",
"Classical---Impressionist",
"Classical---Medieval",
"Classical---Modern",
"Classical---Neo-Classical",
"Classical---Neo-Romantic",
"Classical---Opera",
"Classical---Post-Modern",
"Classical---Renaissance",
"Classical---Romantic",
"Electronic---Abstract",
"Electronic---Acid",
"Electronic---Acid House",
"Electronic---Acid Jazz",
"Electronic---Ambient",
"Electronic---Bassline",
"Electronic---Beatdown",
"Electronic---Berlin-School",
"Electronic---Big Beat",
"Electronic---Bleep",
"Electronic---Breakbeat",
"Electronic---Breakcore",
"Electronic---Breaks",
"Electronic---Broken Beat",
"Electronic---Chillwave",
"Electronic---Chiptune",
"Electronic---Dance-pop",
"Electronic---Dark Ambient",
"Electronic---Darkwave",
"Electronic---Deep House",
"Electronic---Deep Techno",
"Electronic---Disco",
"Electronic---Disco Polo",
"Electronic---Donk",
"Electronic---Downtempo",
"Electronic---Drone",
"Electronic---Drum n Bass",
"Electronic---Dub",
"Electronic---Dub Techno",
"Electronic---Dubstep",
"Electronic---Dungeon Synth",
"Electronic---EBM",
"Electronic---Electro",
"Electronic---Electro House",
"Electronic---Electroclash",
"Electronic---Euro House",
"Electronic---Euro-Disco",
"Electronic---Eurobeat",
"Electronic---Eurodance",
"Electronic---Experimental",
"Electronic---Freestyle",
"Electronic---Future Jazz",
"Electronic---Gabber",
"Electronic---Garage House",
"Electronic---Ghetto",
"Electronic---Ghetto House",
"Electronic---Glitch",
"Electronic---Goa Trance",
"Electronic---Grime",
"Electronic---Halftime",
"Electronic---Hands Up",
"Electronic---Happy Hardcore",
"Electronic---Hard House",
"Electronic---Hard Techno",
"Electronic---Hard Trance",
"Electronic---Hardcore",
"Electronic---Hardstyle",
"Electronic---Hi NRG",
"Electronic---Hip Hop",
"Electronic---Hip-House",
"Electronic---House",
"Electronic---IDM",
"Electronic---Illbient",
"Electronic---Industrial",
"Electronic---Italo House",
"Electronic---Italo-Disco",
"Electronic---Italodance",
"Electronic---Jazzdance",
"Electronic---Juke",
"Electronic---Jumpstyle",
"Electronic---Jungle",
"Electronic---Latin",
"Electronic---Leftfield",
"Electronic---Makina",
"Electronic---Minimal",
"Electronic---Minimal Techno",
"Electronic---Modern Classical",
"Electronic---Musique Concr\u00e8te",
"Electronic---Neofolk",
"Electronic---New Age",
"Electronic---New Beat",
"Electronic---New Wave",
"Electronic---Noise",
"Electronic---Nu-Disco",
"Electronic---Power Electronics",
"Electronic---Progressive Breaks",
"Electronic---Progressive House",
"Electronic---Progressive Trance",
"Electronic---Psy-Trance",
"Electronic---Rhythmic Noise",
"Electronic---Schranz",
"Electronic---Sound Collage",
"Electronic---Speed Garage",
"Electronic---Speedcore",
"Electronic---Synth-pop",
"Electronic---Synthwave",
"Electronic---Tech House",
"Electronic---Tech Trance",
"Electronic---Techno",
"Electronic---Trance",
"Electronic---Tribal",
"Electronic---Tribal House",
"Electronic---Trip Hop",
"Electronic---Tropical House",
"Electronic---UK Garage",
"Electronic---Vaporwave",
"Folk, World, & Country---African",
"Folk, World, & Country---Bluegrass",
"Folk, World, & Country---Cajun",
"Folk, World, & Country---Canzone Napoletana",
"Folk, World, & Country---Catalan Music",
"Folk, World, & Country---Celtic",
"Folk, World, & Country---Country",
"Folk, World, & Country---Fado",
"Folk, World, & Country---Flamenco",
"Folk, World, & Country---Folk",
"Folk, World, & Country---Gospel",
"Folk, World, & Country---Highlife",
"Folk, World, & Country---Hillbilly",
"Folk, World, & Country---Hindustani",
"Folk, World, & Country---Honky Tonk",
"Folk, World, & Country---Indian Classical",
"Folk, World, & Country---La\u00efk\u00f3",
"Folk, World, & Country---Nordic",
"Folk, World, & Country---Pacific",
"Folk, World, & Country---Polka",
"Folk, World, & Country---Ra\u00ef",
"Folk, World, & Country---Romani",
"Folk, World, & Country---Soukous",
"Folk, World, & Country---S\u00e9ga",
"Folk, World, & Country---Volksmusik",
"Folk, World, & Country---Zouk",
"Folk, World, & Country---\u00c9ntekhno",
"Funk / Soul---Afrobeat",
"Funk / Soul---Boogie",
"Funk / Soul---Contemporary R&B",
"Funk / Soul---Disco",
"Funk / Soul---Free Funk",
"Funk / Soul---Funk",
"Funk / Soul---Gospel",
"Funk / Soul---Neo Soul",
"Funk / Soul---New Jack Swing",
"Funk / Soul---P.Funk",
"Funk / Soul---Psychedelic",
"Funk / Soul---Rhythm & Blues",
"Funk / Soul---Soul",
"Funk / Soul---Swingbeat",
"Funk / Soul---UK Street Soul",
"Hip Hop---Bass Music",
"Hip Hop---Boom Bap",
"Hip Hop---Bounce",
"Hip Hop---Britcore",
"Hip Hop---Cloud Rap",
"Hip Hop---Conscious",
"Hip Hop---Crunk",
"Hip Hop---Cut-up/DJ",
"Hip Hop---DJ Battle Tool",
"Hip Hop---Electro",
"Hip Hop---G-Funk",
"Hip Hop---Gangsta",
"Hip Hop---Grime",
"Hip Hop---Hardcore Hip-Hop",
"Hip Hop---Horrorcore",
"Hip Hop---Instrumental",
"Hip Hop---Jazzy Hip-Hop",
"Hip Hop---Miami Bass",
"Hip Hop---Pop Rap",
"Hip Hop---Ragga HipHop",
"Hip Hop---RnB/Swing",
"Hip Hop---Screw",
"Hip Hop---Thug Rap",
"Hip Hop---Trap",
"Hip Hop---Trip Hop",
"Hip Hop---Turntablism",
"Jazz---Afro-Cuban Jazz",
"Jazz---Afrobeat",
"Jazz---Avant-garde Jazz",
"Jazz---Big Band",
"Jazz---Bop",
"Jazz---Bossa Nova",
"Jazz---Contemporary Jazz",
"Jazz---Cool Jazz",
"Jazz---Dixieland",
"Jazz---Easy Listening",
"Jazz---Free Improvisation",
"Jazz---Free Jazz",
"Jazz---Fusion",
"Jazz---Gypsy Jazz",
"Jazz---Hard Bop",
"Jazz---Jazz-Funk",
"Jazz---Jazz-Rock",
"Jazz---Latin Jazz",
"Jazz---Modal",
"Jazz---Post Bop",
"Jazz---Ragtime",
"Jazz---Smooth Jazz",
"Jazz---Soul-Jazz",
"Jazz---Space-Age",
"Jazz---Swing",
"Latin---Afro-Cuban",
"Latin---Bai\u00e3o",
"Latin---Batucada",
"Latin---Beguine",
"Latin---Bolero",
"Latin---Boogaloo",
"Latin---Bossanova",
"Latin---Cha-Cha",
"Latin---Charanga",
"Latin---Compas",
"Latin---Cubano",
"Latin---Cumbia",
"Latin---Descarga",
"Latin---Forr\u00f3",
"Latin---Guaguanc\u00f3",
"Latin---Guajira",
"Latin---Guaracha",
"Latin---MPB",
"Latin---Mambo",
"Latin---Mariachi",
"Latin---Merengue",
"Latin---Norte\u00f1o",
"Latin---Nueva Cancion",
"Latin---Pachanga",
"Latin---Porro",
"Latin---Ranchera",
"Latin---Reggaeton",
"Latin---Rumba",
"Latin---Salsa",
"Latin---Samba",
"Latin---Son",
"Latin---Son Montuno",
"Latin---Tango",
"Latin---Tejano",
"Latin---Vallenato",
"Non-Music---Audiobook",
"Non-Music---Comedy",
"Non-Music---Dialogue",
"Non-Music---Education",
"Non-Music---Field Recording",
"Non-Music---Interview",
"Non-Music---Monolog",
"Non-Music---Poetry",
"Non-Music---Political",
"Non-Music---Promotional",
"Non-Music---Radioplay",
"Non-Music---Religious",
"Non-Music---Spoken Word",
"Pop---Ballad",
"Pop---Bollywood",
"Pop---Bubblegum",
"Pop---Chanson",
"Pop---City Pop",
"Pop---Europop",
"Pop---Indie Pop",
"Pop---J-pop",
"Pop---K-pop",
"Pop---Kay\u014dkyoku",
"Pop---Light Music",
"Pop---Music Hall",
"Pop---Novelty",
"Pop---Parody",
"Pop---Schlager",
"Pop---Vocal",
"Reggae---Calypso",
"Reggae---Dancehall",
"Reggae---Dub",
"Reggae---Lovers Rock",
"Reggae---Ragga",
"Reggae---Reggae",
"Reggae---Reggae-Pop",
"Reggae---Rocksteady",
"Reggae---Roots Reggae",
"Reggae---Ska",
"Reggae---Soca",
"Rock---AOR",
"Rock---Acid Rock",
"Rock---Acoustic",
"Rock---Alternative Rock",
"Rock---Arena Rock",
"Rock---Art Rock",
"Rock---Atmospheric Black Metal",
"Rock---Avantgarde",
"Rock---Beat",
"Rock---Black Metal",
"Rock---Blues Rock",
"Rock---Brit Pop",
"Rock---Classic Rock",
"Rock---Coldwave",
"Rock---Country Rock",
"Rock---Crust",
"Rock---Death Metal",
"Rock---Deathcore",
"Rock---Deathrock",
"Rock---Depressive Black Metal",
"Rock---Doo Wop",
"Rock---Doom Metal",
"Rock---Dream Pop",
"Rock---Emo",
"Rock---Ethereal",
"Rock---Experimental",
"Rock---Folk Metal",
"Rock---Folk Rock",
"Rock---Funeral Doom Metal",
"Rock---Funk Metal",
"Rock---Garage Rock",
"Rock---Glam",
"Rock---Goregrind",
"Rock---Goth Rock",
"Rock---Gothic Metal",
"Rock---Grindcore",
"Rock---Grunge",
"Rock---Hard Rock",
"Rock---Hardcore",
"Rock---Heavy Metal",
"Rock---Indie Rock",
"Rock---Industrial",
"Rock---Krautrock",
"Rock---Lo-Fi",
"Rock---Lounge",
"Rock---Math Rock",
"Rock---Melodic Death Metal",
"Rock---Melodic Hardcore",
"Rock---Metalcore",
"Rock---Mod",
"Rock---Neofolk",
"Rock---New Wave",
"Rock---No Wave",
"Rock---Noise",
"Rock---Noisecore",
"Rock---Nu Metal",
"Rock---Oi",
"Rock---Parody",
"Rock---Pop Punk",
"Rock---Pop Rock",
"Rock---Pornogrind",
"Rock---Post Rock",
"Rock---Post-Hardcore",
"Rock---Post-Metal",
"Rock---Post-Punk",
"Rock---Power Metal",
"Rock---Power Pop",
"Rock---Power Violence",
"Rock---Prog Rock",
"Rock---Progressive Metal",
"Rock---Psychedelic Rock",
"Rock---Psychobilly",
"Rock---Pub Rock",
"Rock---Punk",
"Rock---Rock & Roll",
"Rock---Rockabilly",
"Rock---Shoegaze",
"Rock---Ska",
"Rock---Sludge Metal",
"Rock---Soft Rock",
"Rock---Southern Rock",
"Rock---Space Rock",
"Rock---Speed Metal",
"Rock---Stoner Rock",
"Rock---Surf",
"Rock---Symphonic Rock",
"Rock---Technical Death Metal",
"Rock---Thrash",
"Rock---Twist",
"Rock---Viking Metal",
"Rock---Y\u00e9-Y\u00e9",
"Stage & Screen---Musical",
"Stage & Screen---Score",
"Stage & Screen---Soundtrack",
"Stage & Screen---Theme"
],
"model_types": [
"frozen_model",
"SavedModel",
"onnx"
],
"dataset": {
"name": "Discogs-4M (unreleased)",
"citation": "In-house dataset",
"size": "4M full tracks (3.3M used)",
"metrics": {
"ROC-AUC": 0.95417,
"PR-AUC": 0.20629
}
},
"schema": {
"inputs": [
{
"name": "serving_default_melspectrogram",
"type": "float",
"shape": [
64,
128,
96
]
}
],
"outputs": [
{
"name": "PartitionedCall:0",
"type": "float",
"shape": [
64,
400
],
"op": "Sigmoid",
"output_purpose": "predictions"
},
{
"name": "PartitionedCall:1",
"type": "float",
"shape": [
64,
1280
],
"op": "Flatten",
"output_purpose": "embeddings"
}
]
},
"citation": "@inproceedings{alonso2022music,\n title={Music Representation Learning Based on Editorial Metadata from Discogs},\n author={Alonso-Jim{\\'e}nez, Pablo and Serra, Xavier and Bogdanov, Dmitry},\n booktitle={Conference of the International Society for Music Information Retrieval (ISMIR)},\n year={2022}\n}",
"inference": {
"sample_rate": 16000,
"algorithm": "TensorflowPredictEffnetDiscogs"
}
}
Binary file not shown.
+452
View File
@@ -0,0 +1,452 @@
{
"name": "Genre Discogs400",
"type": "Music genre classification",
"link": "https://essentia.upf.edu/models/classification-heads/genre_discogs400/genre_discogs400-discogs-effnet-1.pb",
"version": "1",
"description": "Prediction of 400 music styles in the from the Discogs taxonomy",
"author": "Pablo Alonso",
"email": "pablo.alonso@upf.edu",
"release_date": "2023-05-04",
"framework": "tensorflow",
"framework_version": "2.8.0",
"classes": [
"Blues---Boogie Woogie",
"Blues---Chicago Blues",
"Blues---Country Blues",
"Blues---Delta Blues",
"Blues---Electric Blues",
"Blues---Harmonica Blues",
"Blues---Jump Blues",
"Blues---Louisiana Blues",
"Blues---Modern Electric Blues",
"Blues---Piano Blues",
"Blues---Rhythm & Blues",
"Blues---Texas Blues",
"Brass & Military---Brass Band",
"Brass & Military---Marches",
"Brass & Military---Military",
"Children's---Educational",
"Children's---Nursery Rhymes",
"Children's---Story",
"Classical---Baroque",
"Classical---Choral",
"Classical---Classical",
"Classical---Contemporary",
"Classical---Impressionist",
"Classical---Medieval",
"Classical---Modern",
"Classical---Neo-Classical",
"Classical---Neo-Romantic",
"Classical---Opera",
"Classical---Post-Modern",
"Classical---Renaissance",
"Classical---Romantic",
"Electronic---Abstract",
"Electronic---Acid",
"Electronic---Acid House",
"Electronic---Acid Jazz",
"Electronic---Ambient",
"Electronic---Bassline",
"Electronic---Beatdown",
"Electronic---Berlin-School",
"Electronic---Big Beat",
"Electronic---Bleep",
"Electronic---Breakbeat",
"Electronic---Breakcore",
"Electronic---Breaks",
"Electronic---Broken Beat",
"Electronic---Chillwave",
"Electronic---Chiptune",
"Electronic---Dance-pop",
"Electronic---Dark Ambient",
"Electronic---Darkwave",
"Electronic---Deep House",
"Electronic---Deep Techno",
"Electronic---Disco",
"Electronic---Disco Polo",
"Electronic---Donk",
"Electronic---Downtempo",
"Electronic---Drone",
"Electronic---Drum n Bass",
"Electronic---Dub",
"Electronic---Dub Techno",
"Electronic---Dubstep",
"Electronic---Dungeon Synth",
"Electronic---EBM",
"Electronic---Electro",
"Electronic---Electro House",
"Electronic---Electroclash",
"Electronic---Euro House",
"Electronic---Euro-Disco",
"Electronic---Eurobeat",
"Electronic---Eurodance",
"Electronic---Experimental",
"Electronic---Freestyle",
"Electronic---Future Jazz",
"Electronic---Gabber",
"Electronic---Garage House",
"Electronic---Ghetto",
"Electronic---Ghetto House",
"Electronic---Glitch",
"Electronic---Goa Trance",
"Electronic---Grime",
"Electronic---Halftime",
"Electronic---Hands Up",
"Electronic---Happy Hardcore",
"Electronic---Hard House",
"Electronic---Hard Techno",
"Electronic---Hard Trance",
"Electronic---Hardcore",
"Electronic---Hardstyle",
"Electronic---Hi NRG",
"Electronic---Hip Hop",
"Electronic---Hip-House",
"Electronic---House",
"Electronic---IDM",
"Electronic---Illbient",
"Electronic---Industrial",
"Electronic---Italo House",
"Electronic---Italo-Disco",
"Electronic---Italodance",
"Electronic---Jazzdance",
"Electronic---Juke",
"Electronic---Jumpstyle",
"Electronic---Jungle",
"Electronic---Latin",
"Electronic---Leftfield",
"Electronic---Makina",
"Electronic---Minimal",
"Electronic---Minimal Techno",
"Electronic---Modern Classical",
"Electronic---Musique Concr\u00e8te",
"Electronic---Neofolk",
"Electronic---New Age",
"Electronic---New Beat",
"Electronic---New Wave",
"Electronic---Noise",
"Electronic---Nu-Disco",
"Electronic---Power Electronics",
"Electronic---Progressive Breaks",
"Electronic---Progressive House",
"Electronic---Progressive Trance",
"Electronic---Psy-Trance",
"Electronic---Rhythmic Noise",
"Electronic---Schranz",
"Electronic---Sound Collage",
"Electronic---Speed Garage",
"Electronic---Speedcore",
"Electronic---Synth-pop",
"Electronic---Synthwave",
"Electronic---Tech House",
"Electronic---Tech Trance",
"Electronic---Techno",
"Electronic---Trance",
"Electronic---Tribal",
"Electronic---Tribal House",
"Electronic---Trip Hop",
"Electronic---Tropical House",
"Electronic---UK Garage",
"Electronic---Vaporwave",
"Folk, World, & Country---African",
"Folk, World, & Country---Bluegrass",
"Folk, World, & Country---Cajun",
"Folk, World, & Country---Canzone Napoletana",
"Folk, World, & Country---Catalan Music",
"Folk, World, & Country---Celtic",
"Folk, World, & Country---Country",
"Folk, World, & Country---Fado",
"Folk, World, & Country---Flamenco",
"Folk, World, & Country---Folk",
"Folk, World, & Country---Gospel",
"Folk, World, & Country---Highlife",
"Folk, World, & Country---Hillbilly",
"Folk, World, & Country---Hindustani",
"Folk, World, & Country---Honky Tonk",
"Folk, World, & Country---Indian Classical",
"Folk, World, & Country---La\u00efk\u00f3",
"Folk, World, & Country---Nordic",
"Folk, World, & Country---Pacific",
"Folk, World, & Country---Polka",
"Folk, World, & Country---Ra\u00ef",
"Folk, World, & Country---Romani",
"Folk, World, & Country---Soukous",
"Folk, World, & Country---S\u00e9ga",
"Folk, World, & Country---Volksmusik",
"Folk, World, & Country---Zouk",
"Folk, World, & Country---\u00c9ntekhno",
"Funk / Soul---Afrobeat",
"Funk / Soul---Boogie",
"Funk / Soul---Contemporary R&B",
"Funk / Soul---Disco",
"Funk / Soul---Free Funk",
"Funk / Soul---Funk",
"Funk / Soul---Gospel",
"Funk / Soul---Neo Soul",
"Funk / Soul---New Jack Swing",
"Funk / Soul---P.Funk",
"Funk / Soul---Psychedelic",
"Funk / Soul---Rhythm & Blues",
"Funk / Soul---Soul",
"Funk / Soul---Swingbeat",
"Funk / Soul---UK Street Soul",
"Hip Hop---Bass Music",
"Hip Hop---Boom Bap",
"Hip Hop---Bounce",
"Hip Hop---Britcore",
"Hip Hop---Cloud Rap",
"Hip Hop---Conscious",
"Hip Hop---Crunk",
"Hip Hop---Cut-up/DJ",
"Hip Hop---DJ Battle Tool",
"Hip Hop---Electro",
"Hip Hop---G-Funk",
"Hip Hop---Gangsta",
"Hip Hop---Grime",
"Hip Hop---Hardcore Hip-Hop",
"Hip Hop---Horrorcore",
"Hip Hop---Instrumental",
"Hip Hop---Jazzy Hip-Hop",
"Hip Hop---Miami Bass",
"Hip Hop---Pop Rap",
"Hip Hop---Ragga HipHop",
"Hip Hop---RnB/Swing",
"Hip Hop---Screw",
"Hip Hop---Thug Rap",
"Hip Hop---Trap",
"Hip Hop---Trip Hop",
"Hip Hop---Turntablism",
"Jazz---Afro-Cuban Jazz",
"Jazz---Afrobeat",
"Jazz---Avant-garde Jazz",
"Jazz---Big Band",
"Jazz---Bop",
"Jazz---Bossa Nova",
"Jazz---Contemporary Jazz",
"Jazz---Cool Jazz",
"Jazz---Dixieland",
"Jazz---Easy Listening",
"Jazz---Free Improvisation",
"Jazz---Free Jazz",
"Jazz---Fusion",
"Jazz---Gypsy Jazz",
"Jazz---Hard Bop",
"Jazz---Jazz-Funk",
"Jazz---Jazz-Rock",
"Jazz---Latin Jazz",
"Jazz---Modal",
"Jazz---Post Bop",
"Jazz---Ragtime",
"Jazz---Smooth Jazz",
"Jazz---Soul-Jazz",
"Jazz---Space-Age",
"Jazz---Swing",
"Latin---Afro-Cuban",
"Latin---Bai\u00e3o",
"Latin---Batucada",
"Latin---Beguine",
"Latin---Bolero",
"Latin---Boogaloo",
"Latin---Bossanova",
"Latin---Cha-Cha",
"Latin---Charanga",
"Latin---Compas",
"Latin---Cubano",
"Latin---Cumbia",
"Latin---Descarga",
"Latin---Forr\u00f3",
"Latin---Guaguanc\u00f3",
"Latin---Guajira",
"Latin---Guaracha",
"Latin---MPB",
"Latin---Mambo",
"Latin---Mariachi",
"Latin---Merengue",
"Latin---Norte\u00f1o",
"Latin---Nueva Cancion",
"Latin---Pachanga",
"Latin---Porro",
"Latin---Ranchera",
"Latin---Reggaeton",
"Latin---Rumba",
"Latin---Salsa",
"Latin---Samba",
"Latin---Son",
"Latin---Son Montuno",
"Latin---Tango",
"Latin---Tejano",
"Latin---Vallenato",
"Non-Music---Audiobook",
"Non-Music---Comedy",
"Non-Music---Dialogue",
"Non-Music---Education",
"Non-Music---Field Recording",
"Non-Music---Interview",
"Non-Music---Monolog",
"Non-Music---Poetry",
"Non-Music---Political",
"Non-Music---Promotional",
"Non-Music---Radioplay",
"Non-Music---Religious",
"Non-Music---Spoken Word",
"Pop---Ballad",
"Pop---Bollywood",
"Pop---Bubblegum",
"Pop---Chanson",
"Pop---City Pop",
"Pop---Europop",
"Pop---Indie Pop",
"Pop---J-pop",
"Pop---K-pop",
"Pop---Kay\u014dkyoku",
"Pop---Light Music",
"Pop---Music Hall",
"Pop---Novelty",
"Pop---Parody",
"Pop---Schlager",
"Pop---Vocal",
"Reggae---Calypso",
"Reggae---Dancehall",
"Reggae---Dub",
"Reggae---Lovers Rock",
"Reggae---Ragga",
"Reggae---Reggae",
"Reggae---Reggae-Pop",
"Reggae---Rocksteady",
"Reggae---Roots Reggae",
"Reggae---Ska",
"Reggae---Soca",
"Rock---AOR",
"Rock---Acid Rock",
"Rock---Acoustic",
"Rock---Alternative Rock",
"Rock---Arena Rock",
"Rock---Art Rock",
"Rock---Atmospheric Black Metal",
"Rock---Avantgarde",
"Rock---Beat",
"Rock---Black Metal",
"Rock---Blues Rock",
"Rock---Brit Pop",
"Rock---Classic Rock",
"Rock---Coldwave",
"Rock---Country Rock",
"Rock---Crust",
"Rock---Death Metal",
"Rock---Deathcore",
"Rock---Deathrock",
"Rock---Depressive Black Metal",
"Rock---Doo Wop",
"Rock---Doom Metal",
"Rock---Dream Pop",
"Rock---Emo",
"Rock---Ethereal",
"Rock---Experimental",
"Rock---Folk Metal",
"Rock---Folk Rock",
"Rock---Funeral Doom Metal",
"Rock---Funk Metal",
"Rock---Garage Rock",
"Rock---Glam",
"Rock---Goregrind",
"Rock---Goth Rock",
"Rock---Gothic Metal",
"Rock---Grindcore",
"Rock---Grunge",
"Rock---Hard Rock",
"Rock---Hardcore",
"Rock---Heavy Metal",
"Rock---Indie Rock",
"Rock---Industrial",
"Rock---Krautrock",
"Rock---Lo-Fi",
"Rock---Lounge",
"Rock---Math Rock",
"Rock---Melodic Death Metal",
"Rock---Melodic Hardcore",
"Rock---Metalcore",
"Rock---Mod",
"Rock---Neofolk",
"Rock---New Wave",
"Rock---No Wave",
"Rock---Noise",
"Rock---Noisecore",
"Rock---Nu Metal",
"Rock---Oi",
"Rock---Parody",
"Rock---Pop Punk",
"Rock---Pop Rock",
"Rock---Pornogrind",
"Rock---Post Rock",
"Rock---Post-Hardcore",
"Rock---Post-Metal",
"Rock---Post-Punk",
"Rock---Power Metal",
"Rock---Power Pop",
"Rock---Power Violence",
"Rock---Prog Rock",
"Rock---Progressive Metal",
"Rock---Psychedelic Rock",
"Rock---Psychobilly",
"Rock---Pub Rock",
"Rock---Punk",
"Rock---Rock & Roll",
"Rock---Rockabilly",
"Rock---Shoegaze",
"Rock---Ska",
"Rock---Sludge Metal",
"Rock---Soft Rock",
"Rock---Southern Rock",
"Rock---Space Rock",
"Rock---Speed Metal",
"Rock---Stoner Rock",
"Rock---Surf",
"Rock---Symphonic Rock",
"Rock---Technical Death Metal",
"Rock---Thrash",
"Rock---Twist",
"Rock---Viking Metal",
"Rock---Y\u00e9-Y\u00e9",
"Stage & Screen---Musical",
"Stage & Screen---Score",
"Stage & Screen---Soundtrack",
"Stage & Screen---Theme"
],
"model_types": ["frozen_model", "SavedModel", "onnx"],
"dataset": {
"name": "Discogs-4M (unreleased)",
"citation": "In-house dataset",
"size": "4M full tracks (3.3M used)",
"metrics": {
"ROC-AUC": 0.95417,
"PR-AUC": 0.20629
}
},
"schema": {
"inputs": [
{
"name": "serving_default_model_Placeholder",
"type": "float",
"shape": ["batch_size", 1280]
}
],
"outputs": [
{
"name": "PartitionedCall:0",
"type": "float",
"shape": ["batch_size", 400],
"op": "Sigmoid",
"output_purpose": "predictions"
}
]
},
"citation": "@inproceedings{alonso2022music,\n title={Music Representation Learning Based on Editorial Metadata from Discogs},\n author={Alonso-Jim{\\'e}nez, Pablo and Serra, Xavier and Bogdanov, Dmitry},\n booktitle={Conference of the International Society for Music Information Retrieval (ISMIR)},\n year={2022}\n}",
"inference": {
"sample_rate": 16000,
"algorithm": "TensorflowPredict2D",
"embedding_model": {
"algorithm": "TensorflowPredictEffnetDiscogs",
"model_name": "discogs-effnet-bs64-1",
"link": "https://essentia.upf.edu/models/music-style-classification/discogs-effnet/discogs-effnet-bs64-1.pb"
}
}
}
Binary file not shown.