O SDK MIDI do WAVESampler é o contrato técnico usado para integrar superfícies de controle ao aplicativo por meio de mapas JSON declarativos. O arquivo descreve a topologia do hardware, as mensagens recebidas e enviadas, a compatibilidade das portas, as ações semânticas do WAVESampler e a tradução do estado visual para LEDs, paletas ou mensagens SysEx.

Escopo e princípios do SDK
Um controlador é integrado adicionando um único arquivo .json à pasta de mapas. Não há código executável no mapa, chamadas a controles WPF, scripts, assemblies, URLs, expressões arbitrárias ou lógica específica de fabricante. O hardware é traduzido para ações estáveis; o estado do WAVESampler é traduzido de volta para as capacidades reais da superfície.
- Mapas são dados: um novo equipamento não exige recompilar o WAVESampler.
- Entrada e saída são independentes: MIDI IN pode executar ações sem MIDI OUT; MIDI OUT pode renderizar estado sem entrada.
- Ativação é atômica: o documento inteiro é desserializado, validado e compilado antes de se tornar ativo.
- Áudio continua no BASS: o MIDI apenas encaminha intenção e valores para os serviços existentes.
- A interface é soberana: cores e estados finais vêm do mesmo provedor de aparência usado pelo WAVESampler; o JSON descreve como o hardware consegue representá-los.
Arquitetura do runtime
Catálogo e watcher de dispositivos Windows
↓
Gerenciador independente de MIDI IN e MIDI OUT
↓
Backend de transporte NAudio 2.3.0
↓
Normalização: press | release | change
↓
Mapa validado e compilado em tabelas imutáveis
↓
Dispatcher de ações semânticas
↙ ↓ ↘
Sampler Mixer Playlist
↓ ↓ ↓
Serviços do WAVESampler e execução de áudio BASS
↓
Provedor soberano de aparência
↓
Tradução on/off | brilho | paleta | RGB | SysEx
↓
MIDI OUT
Os callbacks MIDI não leem arquivos, não interpretam JSON e não acessam diretamente a árvore visual. O mapa ativo já contém uma tabela de entrada indexada por (tipo de mensagem, canal, número). Eventos normalizados são enviados ao dispatcher; alterações de estado retornam ao renderer de feedback, que elimina mensagens duplicadas antes de escrever na saída.
Localização, descoberta e ciclo de ativação
Os mapas são procurados somente no primeiro nível da pasta:
<Pasta de documentos configurada no WAVESampler>MidiMaps*.json
- A pasta é criada automaticamente quando o catálogo é consultado.
- Os arquivos são ordenados pelo nome do arquivo; documentos inválidos permanecem diagnosticáveis pelo nome e pela mensagem de erro.
- A instalação não fornece mapas automaticamente. Copiar, renomear, editar e remover arquivos é responsabilidade do usuário ou integrador.
- A lista é examinada quando a página MIDI é inicializada. Nesta versão não existem Importar, Exportar, Learn, Testar ou Mapping Studio.
- Selecionar uma porta não a abre. As conexões são manuais pelos botões Conectar e Tentar novamente.
Ao trocar o mapa, o runtime valida e compila o novo documento, libera gestos pressionados, envia a desativação anterior quando possível, ativa o novo mapa, executa sua inicialização se MIDI OUT estiver conectado e renderiza a superfície completa. Um mapa inválido deixa as portas utilizáveis, mas não executa bindings, inicialização ou feedback.
Regras gerais do JSON
O formato atual aceita exclusivamente schemaVersion: 1. Propriedades desconhecidas nas classes tipadas são rejeitadas. Os objetos capabilities e target são deliberadamente abertos, embora somente os campos reconhecidos pelo runtime produzam comportamento.
| Campo raiz | Obrigatório | Tipo e regra |
|---|---|---|
schemaVersion | Sim | Inteiro. Deve ser exatamente 1. |
id | Sim | Texto não vazio e estável. Recomenda-se namespace reverso: fabricante.modelo.wavesampler.variante. |
name | Sim | Nome exibido na lista de mapeamentos. |
description | Não | Descrição humana do mapa. |
author | Não | Autor ou organização responsável. |
controller | Sim | Perfil físico, endpoints, controles e feedback. |
mapping | Sim | Lista de bindings entre controles e ações. |
Objeto controller
| Campo | Tipo | Função |
|---|---|---|
manufacturer | string | Fabricante, para documentação e diagnóstico. |
model | string | Modelo ou família do equipamento. |
deviceMatch | object | Padrões de compatibilidade separados por direção. |
capabilities | object aberto | Metadados técnicos do hardware. Atualmente não habilitam funções nem substituem a validação dos controles. |
inputDefaults | object | Defaults de entrada; atualmente contém note.release. |
feedbackDefaults | object | Modelo de feedback herdado por controles que não declaram feedback próprio. |
initialization | array | Mensagens enviadas após mapa válido e MIDI OUT conectado. |
deactivation | array | Mensagens best-effort em troca de mapa, desconexão intencional ou desligamento. |
controls | array | Obrigatório e não vazio. Declara todos os controles físicos conhecidos pelo mapa. |
Compatibilidade de endpoints: deviceMatch
"deviceMatch": {
"inputNamePatterns": [
"*Launchpad Mini MK3*",
"*Launchpad MIDI IN*"
],
"outputNamePatterns": [
"*Launchpad Mini MK3*",
"*Launchpad MIDI OUT*"
]
}
Os padrões são comparados ao nome visível do endpoint sem diferenciar maiúsculas de minúsculas. O caractere * representa qualquer sequência. Uma coleção vazia aceita qualquer dispositivo naquela direção. Pelo menos um endpoint deve estar selecionado e todos os endpoints selecionados precisam corresponder ao mapa.
Não trate portas MIDI, DAW e DIN como equivalentes. Alguns controladores expõem diversos pares. O padrão deve selecionar a porta que realmente envia o protocolo descrito no mapa. A identidade persistida do dispositivo usa endpoint, direção, nome, fabricante e produto; deviceMatch é uma regra de compatibilidade, não a identidade física persistida.
Capabilities
capabilities aceita um objeto JSON livre. O mapa de referência usa chaves como noteInput, controlChangeInput, pitchBendInput, velocity, polyAftertouch, channelPressure, sysExInput, sysExOutput, absoluteControls e relativeEncoders. Elas documentam o perfil; o runtime atual não consulta esse objeto. O comportamento efetivo é determinado por controls, bindings e pela validação.
Defaults de liberação
"inputDefaults": {
"note": {
"release": "either"
}
}
A resolução ocorre na ordem control.input.release → controller.inputDefaults.note.release → sem suporte. Valores:
noteOff: o equipamento envia Note Off explícito.noteOnZero: a liberação é Note On com velocity zero.either: aceita ambas as formas MIDI padrão.value: um controle não-note usa valores declarados emreleasedValues.none: não existe evento físico de soltura.
Objeto control
Cada item de controller.controls representa um controle físico ou uma saída endereçável. IDs duplicados são rejeitados. Também é proibido declarar dois controles com a mesma chave de entrada normalizada — mesmo tipo, canal e número.
| Campo | Obrigatório | Semântica |
|---|---|---|
id | Sim | Identidade técnica local, referenciada por mapping.bindings[].control. |
kind | Não | Texto descritivo; padrão button. Exemplos do mapa: button, velocityPad, fader, relativeEncoder. Atualmente não altera a execução. |
position | Não | {row, column}; metadado topológico, não usado para localizar bindings. |
input | Não | Mensagem recebida pelo controle. |
output | Não | Endereço MIDI usado para feedback de valor curto ou como presença de saída para templates. |
feedback | Não | Sobrescreve feedbackDefaults para este controle. |
Configuração de input
| Campo | Tipo / padrão | Detalhe |
|---|---|---|
message | string, obrigatório | note, noteOn, noteOff, controlChange, pitchBend, polyAftertouch, channelPressure, systemExclusive ou sysEx. Note On/Off são normalizados como note; SysEx como systemExclusive. |
channel | 1–16; padrão 1 | Canal MIDI. Para mensagens sem número, ainda participa da definição, embora a chave de runtime use número -1. |
number | 0–127 | Obrigatório para note, Control Change e poly aftertouch. É a nota ou número do controlador. |
release | string | noteOff, noteOnZero, either, value ou none. |
mode | string | absolute ou relativeBinaryOffset. |
minimumValue | 0–127; padrão 0 | Limite inferior declarado da entrada. |
maximumValue | 0–127; padrão 127 | Limite superior; não pode ser menor que o mínimo. |
pressedValues | array 0–127 | Valores de CC convertidos em evento press. |
releasedValues | array 0–127 | Valores de CC convertidos em release. Obrigatório quando release: value. |
neutralValue | 0–127 | Centro obrigatório de relativeBinaryOffset. O delta atual é valor - neutralValue. |
decrementRange | {minimum, maximum} | Faixa válida que documenta valores de decremento do encoder relativo. |
incrementRange | {minimum, maximum} | Faixa válida que documenta valores de incremento. Ambas as faixas são obrigatórias e validadas no modo relativo. |
{
"id": "encoder-volume",
"kind": "relativeEncoder",
"input": {
"message": "controlChange",
"channel": 1,
"number": 16,
"mode": "relativeBinaryOffset",
"neutralValue": 64,
"decrementRange": { "minimum": 1, "maximum": 63 },
"incrementRange": { "minimum": 65, "maximum": 127 }
}
}
Note On com velocity maior que zero gera press; velocity zero gera release quando permitido. Note Off gera release. CC usa primeiro pressedValues/releasedValues; os demais valores geram change. Pitch bend produz valor de 14 bits e delta em torno de 8192. Aftertouch e pressão de canal geram change. SysEx recebido é normalizado como mudança, mas o mapa schema 1 não possui comparação de payload: existe uma única chave global de entrada SysEx.
Configuração de output
"output": {
"message": "noteOn",
"channel": 1,
"number": 36
}
output.message aceita noteOn, noteOff, controlChange ou systemExclusive; channel aceita 1–16; number é obrigatório e limitado a 0–127 para mensagens curtas. Modos onOff, brightness e indexedPalette devem usar uma mensagem curta com número. Para rgb e deviceSpecific, a mensagem efetiva vem do template SysEx de feedback.
Feedback: campos e modos
O controle usa seu próprio objeto feedback; se ele não existir, herda controller.feedbackDefaults. Sem ambos, ou com mode: none, nada é enviado.
| Campo | Regra |
|---|---|
mode | none, onOff, brightness, indexedPalette, rgb ou deviceSpecific. |
minimumValue / maximumValue | Faixa 0–127 usada para escalar brilho. |
offValue / onValue | Valores 0–127 usados por onOff. |
fixedColor | Cor física #RRGGBB, útil como metadado de LED monocromático. Atualmente não substitui a aparência soberana. |
supportsBlink | Declara suporte nativo a piscar. Quando falso e a UI solicita blink, o runtime registra degradação e envia estado sólido. |
supportsPulse | Mesmo comportamento para pulse. |
palette | Dicionário índice 0–127 → cor #RRGGBB. Obrigatório em indexedPalette. |
template | Mensagem SysEx obrigatória para rgb e deviceSpecific. |
- none: ignora feedback.
- onOff: envia
offValuequando apagado eonValuequando aceso/ativo. - brightness: converte brilho normalizado para
minimumValue..maximumValue. - indexedPalette: converte a cor sRGB para CIE Lab e escolhe o menor Delta E 76; empates escolhem o menor índice.
- rgb: expande um template SysEx com componentes de cor.
- deviceSpecific: usa o mesmo mecanismo seguro de template para um protocolo proprietário delimitado.
"feedback": {
"mode": "rgb",
"minimumValue": 0,
"maximumValue": 127,
"supportsBlink": false,
"supportsPulse": false,
"template": {
"send": "sysEx",
"data": "F0 7D 57 53 {r} {g} {b} {brightness} F7"
}
}
Os placeholders aceitos no template atual são exclusivamente {r}, {g}, {b} e {brightness}. Depois da substituição, a sequência precisa conter bytes hexadecimais separados por espaços, começar em F0 e terminar em F7. Qualquer placeholder desconhecido rejeita o mapa.
Inicialização e desativação
initialization e deactivation aceitam operações declarativas:
send | Campos |
|---|---|
noteOn | channel 1–16, number 0–127, value 0–127. |
noteOff | Mesmos campos de mensagem curta. |
controlChange | Canal, número do CC e valor. |
sysEx | data como bytes hexadecimais completos de F0 a F7. |
"initialization": [
{ "send": "sysEx", "data": "F0 7D 57 53 01 01 F7" },
{ "send": "controlChange", "channel": 1, "number": 0, "value": 1 }
],
"deactivation": [
{ "send": "controlChange", "channel": 1, "number": 0, "value": 0 },
{ "send": "sysEx", "data": "F0 7D 57 53 01 00 F7" }
]
Objeto mapping e bindings
mapping.bindings é obrigatório. A lista pode estar vazia, mas então nenhum comando será executado. Vários bindings podem referenciar o mesmo controle e todos os eventos compatíveis serão despachados. Para feedback, o renderer usa o primeiro binding do controle como fonte visual; portanto coloque primeiro a ação cuja aparência o LED deve espelhar.
| Campo | Obrigatório | Função |
|---|---|---|
control | Sim | ID exato de um item em controller.controls. |
on | Não | press, release, change ou gesture. O padrão é gesture para sampler.key e press para as demais ações. gesture é reservado ao gesto completo de sampler key. |
action | Sim | Identificador semântico listado abaixo. |
target | Depende da ação | Objeto aberto validado conforme a família da ação. |
velocity | Não | Curva de velocity; permitida somente em sampler.key e sampler.key.trigger. |
transform | Não | Transforma valor absoluto ou delta relativo antes do dispatcher. |
Velocity
"velocity": {
"target": "triggerVolumeMultiplier",
"curve": "soft",
"minimum": 0.20,
"maximum": 1.0
}
target deve ser triggerVolumeMultiplier. curve aceita linear, soft (raiz quadrada) ou hard (quadrado). Mínimo e máximo precisam ser finitos, ordenados e contidos em 0–1. O resultado é um multiplicador transitório aplicado antes do início do stream; ele não altera volumes persistidos.
Transform de valores
"transform": {
"inputMinimum": 0,
"inputMaximum": 127,
"outputMinimum": 0.0,
"outputMaximum": 1.0
}
Sem transform, um evento change entrega o valor bruto. Com transformação absoluta, o runtime interpola a entrada e limita o resultado à faixa de saída. inputMinimum e inputMaximum não podem ser iguais; valores devem ser finitos; a saída deve estar ordenada.
"transform": {
"inputMinimum": 0,
"inputMaximum": 127,
"outputMinimum": -1.0,
"outputMaximum": 1.0,
"step": 0.01
}
Quando step existe, a saída é relativeDelta × step. O passo deve ser finito e maior que zero. Essa forma é apropriada para ações .adjust com encoder relativo.
Ações do sampler
| Ação | Target | Uso |
|---|---|---|
sampler.key | {"pad":"current"|0..6,"key":"D1"} | Gesto completo de key down/key up. Exige liberação real. |
sampler.key.trigger | Mesmo target | Disparo one-shot para controles sem release; não participa do latch de Hold Key. |
sampler.pad.select | {"pad":0..6} | Seleciona o pad visível. |
sampler.stop | {"scope":"allSamplerChannels"} | Para todos os canais de sampler. |
sampler.stop | {"channel":1..3} | Para um canal de sampler. |
sampler.stop | {"pad":"current"|0..6,"key":"D1"} | Para uma tecla concreta. |
sampler.reloop.down | Vazio | Inicia o gesto de replay/reloop. |
sampler.reloop.up | Vazio | Finaliza o gesto. |
key deve ser um nome válido de System.Windows.Input.Key, como D1 ou A, e precisa corresponder a uma posição reconhecida pelo sampler. pad: current é resolvido no momento do press e capturado como (SamplerPad, Key). Se o usuário trocar de pad antes da liberação, o release continua destinado ao botão original. Um número fixa o controle naquele pad, mesmo quando outro está visível.
Ao desativar MIDI, desconectar a entrada ou trocar o mapa, todos os sampler.key ainda pressionados são liberados pelo runtime. Isso impede Hold Key preso após perda de conexão. Um controle com release: none não pode usar sampler.key nem um binding on: release; use sampler.key.trigger.
Ações do mixer
Toda ação mixer.* requer target.channel igual a 1, 2, 3, 4 ou "master". A validação aceita todos esses alvos; o serviço do mixer aplica somente recursos existentes naquele strip.
| Grupo | Ações | Valor |
|---|---|---|
| Volume | mixer.volume.setmixer.volume.adjust | Absoluto ou delta transformado. |
| Pitch | mixer.pitch.setmixer.pitch.adjust | Absoluto ou delta. |
| Pan | mixer.pan.setmixer.pan.adjust | Absoluto ou delta. |
| Ativação | mixer.enabled.setmixer.enabled.toggle | Booleano. |
| Lock de volume | mixer.volumeLock.setmixer.volumeLock.toggle | Booleano. |
| Lock de pitch | mixer.pitchLock.setmixer.pitchLock.toggle | Booleano. |
| Lock de pan | mixer.panLock.setmixer.panLock.toggle | Booleano. |
| Hold Key | mixer.holdKey.setmixer.holdKey.toggle | Booleano. |
| Reloop | mixer.reloop.setmixer.reloop.toggle | Booleano. |
| Sampler Delay | mixer.samplerDelay.setmixer.samplerDelay.toggle | Booleano. |
| Equalizador | mixer.equalizer.setmixer.equalizer.toggle | Booleano. |
| Efeitos | mixer.effects.setmixer.effects.toggle | Booleano. |
| Multisampling | mixer.multiSampling.setmixer.multiSampling.toggle | Booleano. |
Em ações .set, o booleano vem do valor transformado (>= 0.5) ou de target.value; quando ambos faltam, o padrão é true. Ações .toggle ignoram valor. Ações .adjust são relativas; use step para controlar a sensibilidade.
{
"control": "channel-1-volume",
"on": "change",
"action": "mixer.volume.set",
"target": { "channel": 1 },
"transform": {
"inputMinimum": 0,
"inputMaximum": 127,
"outputMinimum": 0.0,
"outputMaximum": 1.0
}
}
Ações da playlist e Canal 4
| Ação | Target / valor |
|---|---|
playlist.playSelected | Reproduz o item selecionado. |
playlist.pauseResume | Alterna pausa e retomada. |
playlist.stop | Para a reprodução. |
playlist.selectPrevious | Seleciona o item anterior. |
playlist.selectNext | Seleciona o próximo. |
playlist.selectIndex | target.index >= 0 ou valor fornecido por transform; o resultado é arredondado. |
playlist.playPrevious | Reproduz o item anterior. |
playlist.playNext | Reproduz o próximo item. |
playlist.position.set | Valor contínuo, normalmente transformado para 0–1. |
playlist.add.openDialog | Abre o fluxo existente de adicionar arquivos. |
playlist.removeSelected | Remove itens selecionados. |
playlist.clear | Limpa a playlist pelo serviço compartilhado. |
playlist.moveSelectedUp | Move a seleção para cima. |
playlist.moveSelectedDown | Move a seleção para baixo. |
playlist.playerMode.set | Booleano transformado ou target.value; padrão true. |
playlist.playerMode.toggle | Alterna o modo Player. |
Essas ações passam pelo mesmo serviço usado pela interface do player. Elas não simulam cliques nem chamam handlers privados; por isso preservam validações, seleção, estado sujo e comportamento do Canal 4.
Exemplo integrado
{
"schemaVersion": 1,
"id": "acme.grid.wavesampler.standard",
"name": "ACME Grid - WAVESampler",
"description": "Pad com velocity, encoder relativo e LED por paleta.",
"author": "ACME",
"controller": {
"manufacturer": "ACME",
"model": "Grid",
"deviceMatch": {
"inputNamePatterns": ["*ACME Grid MIDI IN*"],
"outputNamePatterns": ["*ACME Grid MIDI OUT*"]
},
"capabilities": {
"noteInput": true,
"controlChangeInput": true,
"velocity": true,
"relativeEncoders": true
},
"inputDefaults": {
"note": { "release": "either" }
},
"feedbackDefaults": {
"mode": "indexedPalette",
"minimumValue": 0,
"maximumValue": 127,
"offValue": 0,
"onValue": 127,
"supportsBlink": true,
"supportsPulse": false,
"palette": {
"0": "#000000",
"5": "#FF0000",
"21": "#00FF00",
"45": "#0000FF",
"69": "#FFFFFF"
}
},
"initialization": [
{ "send": "sysEx", "data": "F0 7D 01 01 F7" }
],
"deactivation": [
{ "send": "sysEx", "data": "F0 7D 01 00 F7" }
],
"controls": [
{
"id": "sample-d1",
"kind": "velocityPad",
"position": { "row": 1, "column": 0 },
"input": {
"message": "note",
"channel": 1,
"number": 36,
"release": "either"
},
"output": {
"message": "noteOn",
"channel": 1,
"number": 36
}
},
{
"id": "volume-1",
"kind": "relativeEncoder",
"input": {
"message": "controlChange",
"channel": 1,
"number": 16,
"mode": "relativeBinaryOffset",
"neutralValue": 64,
"decrementRange": { "minimum": 1, "maximum": 63 },
"incrementRange": { "minimum": 65, "maximum": 127 }
}
},
{
"id": "play",
"kind": "button",
"input": {
"message": "controlChange",
"channel": 1,
"number": 40,
"release": "value",
"pressedValues": [127],
"releasedValues": [0]
},
"output": {
"message": "controlChange",
"channel": 1,
"number": 40
},
"feedback": {
"mode": "onOff",
"offValue": 0,
"onValue": 127
}
}
]
},
"mapping": {
"bindings": [
{
"control": "sample-d1",
"action": "sampler.key",
"target": { "pad": "current", "key": "D1" },
"velocity": {
"target": "triggerVolumeMultiplier",
"curve": "linear",
"minimum": 0.20,
"maximum": 1.0
}
},
{
"control": "volume-1",
"on": "change",
"action": "mixer.volume.adjust",
"target": { "channel": 1 },
"transform": {
"inputMinimum": 0,
"inputMaximum": 127,
"outputMinimum": -1.0,
"outputMaximum": 1.0,
"step": 0.01
}
},
{
"control": "play",
"on": "press",
"action": "playlist.playSelected",
"target": {}
}
]
}
}
Validações que rejeitam o mapa
- JSON inválido, documento vazio, propriedade desconhecida em um modelo tipado ou versão diferente de 1.
id/namevazios, zero controles, IDs duplicados ou binding apontando para controle inexistente.- Dois controles usando a mesma combinação normalizada de mensagem, canal e número.
- Canal fora de 1–16, byte de dados fora de 0–127, faixa invertida ou SysEx sem
F0 ... F7. - Mensagem, release, modo, evento, ação ou feedback desconhecido.
release: valuesemreleasedValues, ou ação release-aware em controle sem liberação.- Target de sampler, mixer ou playlist fora dos limites descritos acima.
- Velocity em ação que não seja sampler key, curva desconhecida ou limites fora de 0–1.
- Transform não finito, faixa de entrada sem amplitude, saída invertida ou
step <= 0. - Paleta vazia, cor que não seja
#RRGGBB, índice fora de 0–127 ou template SysEx inválido.
Feedback soberano e atualização da superfície
Bindings não declaram cores de standby, pressed, playing, active ou locked. O provedor de aparência entrega ao MIDI um snapshot imutável, como isLit, cor resolvida, brilho, efeito e valor normalizado. Assim, mudanças de tema, pad, reprodução, mixer e playlist chegam ao hardware sem duplicar regras visuais no JSON.
Uma renderização completa ocorre quando um mapa válido é ativado, quando MIDI OUT conecta ou quando há refresh visual global. Mudanças específicas renderizam somente controles afetados. Controles ligados a pad: current são todos recalculados quando o pad muda. Mensagens idênticas à última mensagem do mesmo controle são coalescidas.
Diagnóstico, desempenho e limites
- Logs operacionais registram descoberta, validação, troca de endpoint, conexão, inicialização, degradação de feedback e erros de transporte.
- Tráfego Note/CC de alta frequência não deve gerar log normal por evento.
- Erros de ação são isolados ao evento; não fecham conexões saudáveis nem interrompem o BASS.
- Erro de saída suspende feedback até reconexão, sem parar MIDI IN.
- O SDK schema 1 suporta uma entrada e uma saída simultâneas, não múltiplas portas por direção.
- Não inclui MIDI clock, sequenciamento, MPE, playback de arquivo MIDI, síntese BASSMIDI, DAW control genérico, editor de mapas ou learn mode.
- Não importa keymaps, não edita samples e não abre editores por binding.
Checklist de entrega de um mapa
- Use um
idestável e mantenhaschemaVersion: 1. - Meça os nomes reais das portas e diferencie MIDI, DAW e DIN.
- Documente cada mensagem física, inclusive a forma verdadeira de release.
- Use
sampler.key.triggerquando não existir soltura. - Teste valores mínimos, máximos, neutros, curvas de velocity e sensibilidade de encoders.
- Teste MIDI IN sem OUT, OUT sem IN e falhas independentes.
- Teste mudança de pad durante uma tecla pressionada e bindings para pads fixos.
- Teste troca de tema, playback, mixer e playlist para validar o feedback soberano.
- Desconecte o equipamento durante Hold Key e confirme que o estado pressionado é limpo.
- Coloque o JSON em
MidiMaps, reabra a página, selecione as portas, conecte-as manualmente e escolha o mapa compatível.
Para a operação diária da página de configurações, consulte Controladores MIDI — Mapeamento do controlador.

Deixe um comentário