<!-- canonical: https://jmrplens.github.io/phonometry/es/guides/multichannel/ -->
Source: https://jmrplens.github.io/phonometry/es/guides/multichannel/

La mayoría de las sesiones de medición reales producen más de un canal: los
dos oídos de una cabeza binaural, la pareja de micrófonos de una sonda de
intensidad, las varias posiciones de un muestreo de sala, las cápsulas de un
array de conformación de haz. La convención para todos ellos es un único
array con forma `(canales, muestras)` procesado en una sola llamada: cada
función actúa sobre el último eje (el tiempo) y conserva el eje inicial de
canales, de modo que cada fila se analiza exactamente como si fuera la única.

Esa garantía por canal es normativa, no solo cómoda. Los filtros de banda
aplicados a cada fila son los diseños de IEC 61260-1 y las ponderaciones y
balísticas del detector son las de IEC 61672-1, sin cambios respecto a la
ruta monocanal; la vectorización agrupa la aritmética entre filas (un diseño
de filtro, una llamada a SciPy) y nunca las mezcla. Es el procedimiento de
libro de texto para múltiples registros de datos (Bendat y Piersol 2010,
§10.4.2): analizar primero cada registro por separado y calcular lo conjunto
como un paso aparte y explícito.

Usa esta página cuando tus canales sean grabaciones paralelas con el mismo
reloj y quieras espectros o niveles por canal. Cuando la pregunta sea *entre*
canales (cuál es el retardo de A a B, cuánta parte de B se explica por A),
eso es análisis cruzado: consulta
[Correlación y retardo](/phonometry/es/guides/correlation-delay/) y
[Coherencia múltiple y parcial](/phonometry/es/guides/miso-coherence/), las
implementaciones de la correlación cruzada y de los modelos de múltiples
entradas de Bendat y Piersol.

## Un análisis estéreo en una llamada

*Análisis simultáneo de una señal estéreo: canal izquierdo (ruido rosa) vs canal
derecho (barrido senoidal logarítmico).*

<details>
<summary>Mostrar el código de esta figura</summary>

```python

from scipy.signal import chirp
from phonometry import metrology

# Señal estéreo de prueba: ruido rosa a la izquierda, barrido logarítmico a la derecha
fs, duration = 48000, 5
t = np.linspace(0, duration, fs * duration, endpoint=False)
rng = np.random.default_rng(42)
spec = np.fft.rfft(rng.standard_normal(t.size))
spec[1:] /= np.sqrt(np.arange(1, spec.size))   # conformado 1/f: ruido rosa
left = np.fft.irfft(spec, t.size)
right = chirp(t, f0=50, t1=duration, f1=10000, method="logarithmic")

x = np.stack([left, right])                    # (2, n_samples)
spl, freq = metrology.octave_filter(x, fs, fraction=3, limits=[20, 20000])

fig, axes = plt.subplots(2, 1, figsize=(9, 7), sharex=True)
for ax, levels, name in zip(axes, spl, ["Izquierdo: ruido rosa", "Derecho: barrido logarítmico"]):
    ax.semilogx(freq, levels, marker="o", label=name)
    ax.set_ylabel("Nivel [dB]")
    ax.grid(True, which="both", alpha=0.3)
    ax.legend()
axes[-1].set_xlabel("Frecuencia [Hz]")
plt.show()
```

</details>

La convención es consistente en toda la librería: el tiempo siempre es el
**último eje**. Aplica a `octave_filter`, `OctaveFilterBank`, `weighting_filter`,
`time_weighting`, `leq`, `laeq`, `ln_levels` y `spectrogram`.

```python

from phonometry import metrology

# Dos canales calibrados en Pa para que la guía funcione por sí sola
fs = 48000
t = np.arange(fs) / fs
left = 0.2 * np.sin(2 * np.pi * 1000 * t)
right = 0.1 * np.sin(2 * np.pi * 500 * t)

stereo = np.stack([left, right])          # (2, n_samples)
spl, freq = metrology.octave_filter(stereo, fs, fraction=3)
# spl tiene forma (2, n_bands): una fila por canal
```

## Formas aceptadas, de un vistazo

| Entrada | Se interpreta como | Salida típica |
| :--- | :--- | :--- |
| Array 1D `(n,)` | un canal | nivel escalar / `(bands,)` |
| Array 2D `(ch, n)` | `ch` canales de `n` muestras cada uno | niveles `(ch,)` / `(ch, bands)` |
| lista de floats | un canal (se convierte) | como 1D |
| `(ch, n)` en `spectrogram` | STFT multicanal | `(ch, bands, frames)` |

Todo se vectoriza a lo largo del eje inicial de canales: un único diseño de
filtro se aplica a todos los canales en una sola llamada a SciPy, así que 8
canales cuestan mucho menos que 8 ejecuciones separadas. Convención: **canales
primero**, como la mayoría del código DSP (`soundfile` devuelve `(n, ch)`:
transpón con `x.T`).

## Semántica por canal

El procesado multicanal es estrictamente por canal: nunca se mezcla, suma ni
promedia nada a través del eje de canales. Tres consecuencias que conviene
explicitar:

- **Combinar canales es decisión tuya.** Los niveles vuelven uno por canal.
  Si necesitas un nivel promedio del array (por ejemplo los niveles
  promediados por posición de las normas de acústica de salas), combina
  energías tú mismo: `10 * np.log10(np.mean(10 ** (spl / 10), axis=0))`,
  nunca la media aritmética de los valores en dB (ver
  [Niveles](/phonometry/es/guides/levels/) para el porqué).
- **Un `calibration_factor` significa una sensibilidad.** El factor escalar
  multiplica todos los canales, lo que solo es correcto si todos comparten
  la misma sensibilidad. Para un array de micrófonos con calibraciones
  individuales, escala primero las filas, `x * factors[:, None]`, y deja
  `calibration_factor` en 1.
- **Las clases con estado guardan un estado por canal.** En el procesado por
  bloques el array de estado se ajusta al número de canales y un cambio del
  número de canales lo reinicia; ver
  [Procesado por bloques](/phonometry/es/guides/block-processing/).

## Rendimiento: vectorización y caché

`OctaveFilterBank` es la herramienta para el análisis repetido o en
streaming: un banco diseña sus filtros una vez y los aplica a cada trama, y
el broadcasting de NumPy cubre todos los canales de una trama en una sola
llamada de filtrado por banda, sin bucle Python sobre canales.

```python

from phonometry import metrology

# Dos canales calibrados en Pa para que la guía funcione por sí sola
fs = 48000
t = np.arange(fs) / fs
left = 0.2 * np.sin(2 * np.pi * 1000 * t)
right = 0.1 * np.sin(2 * np.pi * 500 * t)
stereo = np.stack([left, right])          # (2, n_samples)

bank = metrology.OctaveFilterBank(fs=48000, fraction=3, filter_type='butter')

# Propiedades calculadas
# bank.freq (centros), bank.freq_d (bordes inferiores), bank.freq_u (superiores), bank.sos

# Procesar múltiples señales de forma eficiente
stream = [stereo]                 # tu secuencia de tramas multicanal
for frame in stream:
    # detrend=True (por defecto) elimina el offset DC y mejora la precisión en graves
    spl, freq = bank.filter(frame, detrend=True)
```

Notas adicionales de rendimiento:

- **Caché de diseño**: `octave_filter()` reutiliza los diseños del banco entre
  llamadas con parámetros idénticos (caché LRU de 32 entradas), así que llamarla
  en bucle no rediseña el banco cada vez. `OctaveFilterBank` te da control
  explícito sobre el ciclo de vida del diseño.
- **Diezmado multitasa**: las bandas graves se filtran a una frecuencia diezmada,
  lo que es a la vez más rápido y numéricamente más estable (consulta
  [Teoría](/phonometry/es/reference/theory/signal-analysis/)).
- **numba opcional**: el kernel del modo `impulse` de la ponderación temporal se
  compila JIT cuando numba está instalado (`pip install phonometry[perf]`).

## Qué cubre esta guía

**Cubierto.** La IEC 61260-1:2014 y la IEC 61672-1:2013 en lo que
condicionan cada fila de un array `(canales, muestras)`: los filtros de
banda, las ponderaciones y la balística del detector aplicados por canal
son los mismos diseños monocanal sin cambios, y el soporte multicanal no
añade contenido normativo propio. La convención misma sigue el
procedimiento de Bendat y Piersol para registros de datos múltiples
(apartado 10.4.2): analizar cada registro individualmente primero, y
tratar lo conjunto como un paso aparte y explícito.

**No cubierto.** Las preguntas entre canales, como el retardo entre dos
canales o cuánto de uno explica el otro, quedan deliberadamente fuera:
`octave_filter`, `weighting_filter` y `time_weighting` nunca mezclan ni
combinan filas. Consulta
[Correlación y retardo](/phonometry/es/guides/correlation-delay/) y
[Coherencia múltiple y parcial](/phonometry/es/guides/miso-coherence/)
para las herramientas entre canales construidas sobre la correlación
cruzada y los modelos de entrada múltiple de Bendat y Piersol.

## Véase también

- [Correlación y retardo](/phonometry/es/guides/correlation-delay/): las preguntas cruzadas en el dominio del tiempo (retardo, alineación) que la ruta por canal deja deliberadamente en tus manos.
- [Coherencia múltiple y parcial](/phonometry/es/guides/miso-coherence/): cuál de varios canales correlados excita realmente una respuesta (Bendat y Piersol, cap. 7).
- [Procesado por bloques](/phonometry/es/guides/block-processing/): la contrapartida en streaming, con un estado de filtro por canal.
- [Niveles](/phonometry/es/guides/levels/): las métricas de nivel por canal, y por qué los dB se combinan energéticamente.
- Referencia de la API: [`phonometry`](/phonometry/es/reference/api/filters/phonometry/) y [`metrology.core`](/phonometry/es/reference/api/filters/core/).
