Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/light/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,12 @@ if(MONKY_LIGHT_WITH_RTC)
target_compile_definitions(monky_light_ui PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
else()
find_library(MONKY_APPKIT_FRAMEWORK AppKit REQUIRED)
# AXIsProcessTrusted: push-to-talk needs Accessibility to see keys.
find_library(MONKY_APPLICATION_SERVICES_FRAMEWORK ApplicationServices REQUIRED)
add_library(monky_light_ui STATIC src/ui/tray_macos.mm)
set_source_files_properties(src/ui/tray_macos.mm PROPERTIES COMPILE_OPTIONS "-fobjc-arc")
target_link_libraries(monky_light_ui PUBLIC "${MONKY_APPKIT_FRAMEWORK}")
target_link_libraries(monky_light_ui PUBLIC "${MONKY_APPKIT_FRAMEWORK}"
"${MONKY_APPLICATION_SERVICES_FRAMEWORK}")
endif()
target_include_directories(monky_light_ui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(monky_light_ui PUBLIC monky_light_json)
Expand Down
9 changes: 9 additions & 0 deletions apps/light/QA.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,12 @@ available, so the terminal shows what each click did.
| 6.19 | Measure the same phase with and without `--tray` | The tray does not measurably change idle resource use | | |
| 6.20 | A channel without `Speak` | The mute row is marked and disabled, with the note that there is no permission to speak | | |
| 6.21 | Close the console window during a call (Windows only) | The others see Light leave at once (logout), the icon disappears and no process is left | | |
| 6.22 | Start with `--ptt-key F13` (or `Mouse4`) and turn `Push-to-talk` on | The row is checked and shows the key; nothing is watched outside a call | | |
| 6.23 | In a call, hold and release the key with another application in front | Others only hear you while the key is held, and the start of speech is not cut; the key still reaches the other application | | |
| 6.24 | Hold the key for several seconds | A single `ptt` `active:true` in the terminal, no repetition | | |
| 6.25 | Hold the key and turn `Push-to-talk` off from the menu | Transmits all the time again, with no `command-error` | | |
| 6.26 | Without Accessibility permission (macOS only) | The system asks once and the notice appears in the menu; after allowing it and reopening the menu, the key works | | |
| 6.27 | Speak in voice-activity mode | The icon turns light green while you speak and back to green when you stop | | |
| 6.28 | Open `In the call` while someone speaks | They appear with `(speaking)`; you are not listed | | |
| 6.29 | Choose 0%, 200%, and 100% for a participant | Silent, louder, back to normal; nobody else notices a difference | | |
| 6.30 | Restart the Light and reopen `In the call` | The chosen volume is still checked and applied | | |
9 changes: 9 additions & 0 deletions apps/light/QA.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,12 @@ valendo, então o terminal serve para conferir o que cada clique fez.
| 6.19 | Medir a mesma fase com `--tray` e sem | A bandeja não muda o consumo em repouso de forma perceptível | | |
| 6.20 | Canal sem `Falar` | O mute fica marcado e desabilitado, com a nota de que falta permissão para falar | | |
| 6.21 | Fechar a janela do console durante uma chamada (só Windows) | Os outros veem o Light sair na hora (logout), o ícone desaparece e não sobra processo | | |
| 6.22 | Iniciar com `--ptt-key F13` (ou `Mouse4`) e ligar `Apertar para falar` | O item fica marcado e mostra a tecla; fora de chamada nada é observado | | |
| 6.23 | Em chamada, segurar e soltar a tecla com outro aplicativo em primeiro plano | Os outros só ouvem enquanto a tecla está segurada, e o início da fala não é cortado; a tecla continua chegando ao outro aplicativo | | |
| 6.24 | Segurar a tecla por vários segundos | Um único `ptt` `active:true` no terminal, sem repetição | | |
| 6.25 | Segurar a tecla e desligar `Apertar para falar` pelo menu | Volta a transmitir sempre, sem `command-error` | | |
| 6.26 | Sem autorização de Acessibilidade (só macOS) | O sistema pede a autorização uma vez e o aviso aparece no menu; após autorizar e reabrir o menu, a tecla funciona | | |
| 6.27 | Falar com o modo de atividade de voz | O ícone fica verde claro enquanto você fala e volta ao verde ao parar | | |
| 6.28 | Abrir `Na chamada` enquanto alguém fala | A pessoa aparece com `(falando)`; você não aparece na lista | | |
| 6.29 | Escolher 0%, 200% e 100% para um participante | Some, fica mais alto, volta ao normal; os outros não percebem diferença | | |
| 6.30 | Reiniciar o Light e reabrir `Na chamada` | O volume escolhido continua marcado e aplicado | | |
81 changes: 76 additions & 5 deletions apps/light/README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ Enter one JSON object per line in the terminal, using the received channel ID:
{"command":"devices"}
{"command":"set-input","deviceId":"DEVICE-ID"}
{"command":"set-output","deviceId":null}
{"command":"set-input-mode","mode":"push_to_talk"}
{"command":"ptt","active":true}
{"command":"set-volume","sessionId":"SESSION-ID","volume":150}
{"command":"stats","id":"my-query"}
{"command":"leave"}
{"command":"reconnect"}
Expand Down Expand Up @@ -129,6 +132,47 @@ quitting remain available. Denial keeps the microphone muted and emits
`microphone-error`; it does not try to bypass system settings. The macOS
permission flow still requires execution on a Mac.

### Speaking indicator and push-to-talk

During a call, the core emits `speaking` with `sessionId` and
`speaking:true|false` whenever someone, including the local user, starts or
stops speaking. As in the full client, this is **not** published to the server:
each client derives who is speaking from the audio.

- **Remote participants:** the level comes from the RTP header (RFC 6464) the
sender already includes, read every 100 ms. Nothing is decoded or measured
separately. While the user is deafened, no one is shown speaking.
- **Local user:** the level is measured on raw capture, before echo
cancellation and noise suppression. Loud, steady background noise can count
as speech.
- The threshold is -40 dBov (the 0.01 `audioLevel` the full client uses), and
speech ends only after 400 ms of silence so it does not flicker between words.
- Sampling exists only during a call; outside one, no thread wakes up.

`set-input-mode` selects `voice_activity` (default: always transmitting) or
`push_to_talk` and saves the choice in the profile. In push-to-talk, `ptt` with
`active:true`/`false` opens and closes transmission. The core does not capture
keys: the global hotkey belongs to whatever controls the process; the tray has
no global shortcuts yet. Capture stays open while the key is released, so a press takes
effect immediately. The track sends silence, and the local user is not shown
speaking. Mute, deafen, and server restrictions still apply while the key is
held. Switching modes or leaving the call releases the key. `ptt` outside
push-to-talk mode is rejected.

### Per-participant volume

`set-volume` sets, from 0 to 200%, how loud you hear one voice session
(`sessionId` from the participant list), like the full client's slider. It is a
local preference: neither the server nor the other participants know about it.
The choice is saved in the profile, applies at once to the current call and to
later ones, in both P2P and SFU, and emits `participant-volume`. `stats` lists in
`userVolumes` the non-default volumes of the current call's sessions.

WebRTC applies the gain to the decoded stream before mixing: there is no extra
processing, and 100% costs nothing. Setting 100 again removes the entry. The
profile keeps up to 256 sessions with a changed volume; beyond that, `set-volume`
is rejected until one is set back to 100.

## Tray interface

`--tray` adds the icon to the Windows notification area or the macOS status bar.
Expand All @@ -153,6 +197,9 @@ rendering between activations. It carries:
- the server voice channels, with the current one marked, and `Leave the channel`;
- mute and deafen, marked and disabled when the server imposed them or the
channel does not allow speaking;
- `Push-to-talk`, which switches the input mode and shows the key;
- `In the call`, with each participant, who is speaking, and the volume, in
presets from 0 to 200%;
- `Audio devices`, with input, output, and `System default`;
- `Reconnect now` and `Quit Monky Light`.

Expand All @@ -162,8 +209,9 @@ and the switch of the web interface. Actual switches and cards belong to the
panels, which are not part of this milestone.

The icon reports the state with the same colours as the full client: grey
disconnected, accent connected without a call, green in a call, and red with a
diagonal bar when muted or deafened. It is drawn at runtime and does **not**
disconnected, accent connected without a call, green in a call, light green
while you speak, and red with a diagonal bar when muted or deafened. Only your
own speech redraws the icon; other people's appears in the menu when it opens. It is drawn at runtime and does **not**
reuse the full Monky icon, because one edition must not pass for the other; the
Light's own identity belongs to distribution, which is not implemented yet. On
macOS the icon is a template system symbol, which follows the menu bar
Expand All @@ -185,8 +233,31 @@ The language follows the operating system, between Portuguese and English.
Choosing the language inside the Light depends on the settings panel, which does
not exist yet.

There are still no native panels, global shortcuts, connection screen, or
installer: the tray starts with the same command-line options as the core.
### Push-to-talk shortcut

The key is chosen with `--ptt-key` and saved in the profile: `A`–`Z`, `0`–`9`,
`F1`–`F24`, `Mouse4`, `Mouse5` (side buttons), `RightControl`, `RightAlt`,
`RightShift`, `Backquote`, `Insert`, or `Pause`. There is no default key, so it
never takes one the user plays with; without a key, the menu row says how to
choose one. Caps Lock and Num Lock are left out because holding them changes
their system state. macOS reports neither `F21`–`F24` nor `Pause`; the menu says so.

The shortcut is watched **only while it can transmit**: in a call, in
push-to-talk mode. Otherwise nothing is installed and nothing is spent. The key
is never consumed, so it keeps working in the foreground application, and
keyboard auto-repeat becomes a single press.

- **Windows:** a low-level keyboard hook or, for `Mouse4`/`Mouse5`, a mouse
hook, installed on the interface thread. The mouse hook is called on every
pointer movement, so it exists only when the key is a button.
- **macOS:** an AppKit global event monitor. The system only delivers keys to an
application allowed under *System Settings › Privacy & Security ›
Accessibility*; the Light asks once and shows the notice in the menu. Mouse
buttons need no such permission. The macOS shortcut still requires
qualification on a Mac.

There are still no native panels, connection screen, or installer: the tray
starts with the same command-line options as the core.

## Shared contracts

Expand Down Expand Up @@ -226,7 +297,7 @@ require explicit recovery, never silent replacement. Directories containing
another application's files are rejected.

`monky-light-settings.json` stores only user preferences (currently audio
devices) and never participates in identity validation. It is replaced
devices, the input mode, and each participant's volume) and never participates in identity validation. It is replaced
atomically under the profile lock. Invalid or unreadable settings emit a
`warning`, fall back to defaults, and are replaced by the next save; an
interrupted settings write does not require recovery.
Expand Down
81 changes: 76 additions & 5 deletions apps/light/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ Digite um objeto JSON por linha no terminal, usando o ID recebido:
{"command":"devices"}
{"command":"set-input","deviceId":"ID-DO-DISPOSITIVO"}
{"command":"set-output","deviceId":null}
{"command":"set-input-mode","mode":"push_to_talk"}
{"command":"ptt","active":true}
{"command":"set-volume","sessionId":"ID-DA-SESSAO","volume":150}
{"command":"stats","id":"minha-consulta"}
{"command":"leave"}
{"command":"reconnect"}
Expand Down Expand Up @@ -130,6 +133,47 @@ precisa transmitir. Enquanto a permissão está pendente, é possível receber
mutado e gera `microphone-error`; não tenta contornar as configurações do sistema.
O fluxo de permissão do macOS ainda requer execução em um Mac.

### Indicador de fala e push-to-talk

Durante uma chamada, o núcleo emite `speaking` com `sessionId` e
`speaking:true|false` sempre que alguém, inclusive o próprio usuário, começa ou
para de falar. Como no cliente completo, isso **não** é publicado no servidor:
cada cliente deduz quem fala a partir do áudio.

- **Participantes remotos:** o nível vem do cabeçalho RTP (RFC 6464) que o
remetente já envia, consultado a cada 100 ms. Nada é decodificado ou medido
à parte. Enquanto o usuário está em deafen, ninguém aparece falando.
- **Próprio usuário:** o nível é medido na captura bruta, antes do cancelamento
de eco e da supressão de ruído. Ruído de fundo alto e constante pode contar
como fala.
- O limiar é -40 dBov (o `audioLevel` 0,01 usado pelo cliente completo), e a
fala só termina após 400 ms de silêncio, para não piscar entre palavras.
- A amostragem só existe durante a chamada; fora dela nenhuma thread acorda.

`set-input-mode` escolhe `voice_activity` (padrão: transmite sempre) ou
`push_to_talk` e salva a escolha no perfil. Em push-to-talk, `ptt` com
`active:true`/`false` abre e fecha a transmissão. O núcleo não captura teclas:
o atalho global é responsabilidade de quem controla o processo; a bandeja
ainda não tem atalhos globais. A captura continua aberta enquanto a tecla está solta, então
pressionar tem efeito imediato. A faixa envia silêncio, e o próprio usuário não
aparece falando. Mute, deafen e restrições do servidor continuam valendo mesmo
com a tecla pressionada. Trocar de modo ou sair da chamada solta a tecla.
`ptt` fora do modo push-to-talk é recusado.

### Volume por participante

`set-volume` ajusta de 0 a 200% o volume com que você ouve uma sessão de voz
(`sessionId` da lista de participantes), como o controle deslizante do cliente
completo. É uma preferência local: o servidor e os outros participantes não
sabem dela. A escolha é salva no perfil, vale imediatamente na chamada atual e
nas próximas, inclusive em P2P e SFU, e gera `participant-volume`. `stats` traz
em `userVolumes` os volumes diferentes de 100 das sessões da chamada atual.

O ganho é aplicado pelo próprio WebRTC ao fluxo decodificado, antes da mixagem:
não há processamento extra, e 100% não custa nada. Voltar a 100 apaga a entrada.
O perfil guarda até 256 sessões com volume alterado; acima disso, `set-volume`
é recusado até que alguma volte a 100.

## Interface de bandeja

`--tray` adiciona o ícone na área de notificação do Windows ou na barra de status
Expand All @@ -154,6 +198,9 @@ renderizando entre as ativações. Ele traz:
- os canais de voz do servidor, com o canal atual marcado, e `Sair do canal`;
- mute e deafen, marcados e desabilitados quando a restrição é do servidor ou
quando o canal não permite falar;
- `Apertar para falar`, que alterna o modo de entrada e mostra a tecla;
- `Na chamada`, com cada participante, quem está falando e o volume, em
predefinições de 0 a 200%;
- `Dispositivos de áudio`, com entrada, saída e `Padrão do sistema`;
- `Reconectar agora` e `Sair do Monky Light`.

Expand All @@ -163,8 +210,9 @@ selecionável e do switch da interface web. Switch e card propriamente ditos val
para os painéis, que não fazem parte desta etapa.

O ícone reflete o estado com as mesmas cores do cliente completo: cinza
desconectado, accent conectado sem chamada, verde em chamada e vermelho com uma
barra diagonal quando mudo ou silenciado. Ele é desenhado em tempo de execução e
desconectado, accent conectado sem chamada, verde em chamada, verde claro
enquanto você fala e vermelho com uma barra diagonal quando mudo ou silenciado.
Só a sua fala redesenha o ícone; a de outras pessoas aparece no menu ao abri-lo. Ele é desenhado em tempo de execução e
**não** reutiliza o ícone do Monky completo, porque uma edição não deve se passar
pela outra; a identidade própria do Light faz parte da distribuição, ainda não
implementada. No macOS o ícone é um símbolo de sistema em modo *template*, que
Expand All @@ -185,8 +233,31 @@ carregam o estado atual, para o leitor de tela não anunciar apenas "ícone".
O idioma acompanha o do sistema operacional, entre português e inglês. Escolher o
idioma dentro do Light depende do painel de configurações, que ainda não existe.

Ainda não há painéis nativos, atalhos globais, tela de conexão nem instalador: a
bandeja é iniciada com as mesmas opções de linha de comando do núcleo.
### Atalho do apertar para falar

A tecla é escolhida com `--ptt-key` e salva no perfil: `A`–`Z`, `0`–`9`,
`F1`–`F24`, `Mouse4`, `Mouse5` (botões laterais), `RightControl`, `RightAlt`,
`RightShift`, `Backquote`, `Insert` ou `Pause`. Não há tecla padrão, para não
tomar uma tecla que a pessoa usa em jogo; sem tecla, o item do menu diz como
escolher. Caps Lock e Num Lock ficam de fora porque segurá-los altera o estado
deles no sistema. O macOS não informa `F21`–`F24` nem `Pause`; o menu avisa.

O atalho é observado **só enquanto pode transmitir**: em chamada e no modo
apertar para falar. Fora disso nada é instalado e não há custo. A tecla nunca é
consumida, então continua funcionando no aplicativo em primeiro plano, e a
repetição automática do teclado vira um único aperto.

- **Windows:** hook de baixo nível do teclado ou, para `Mouse4`/`Mouse5`, do
mouse, instalado na thread da interface. O hook de mouse é chamado a cada
movimento do ponteiro, por isso só existe quando a tecla é um botão.
- **macOS:** monitor global de eventos do AppKit. O sistema só entrega teclas a
um aplicativo autorizado em *Ajustes do Sistema › Privacidade e Segurança ›
Acessibilidade*; o Light pede a autorização uma vez e mostra o aviso no menu.
Os botões do mouse não exigem essa autorização. O atalho no macOS ainda
precisa de qualificação em um Mac.

Ainda não há painéis nativos, tela de conexão nem instalador: a bandeja é
iniciada com as mesmas opções de linha de comando do núcleo.

## Contratos compartilhados

Expand Down Expand Up @@ -226,7 +297,7 @@ componentes da identidade exigem recuperação explícita, nunca recriação sil
Diretórios com arquivos de outros aplicativos são recusados.

`monky-light-settings.json` guarda apenas preferências do usuário (hoje, os
dispositivos de áudio) e nunca participa da validação da identidade. É
dispositivos de áudio, o modo de entrada e o volume de cada participante) e nunca participa da validação da identidade. É
substituído de forma atômica sob o lock do perfil. Configurações inválidas ou
ilegíveis geram `warning`, usam os padrões e são substituídas no próximo
salvamento; uma gravação interrompida das configurações não exige recuperação.
Expand Down
Loading
Loading