### Install Nx.Signal and related libraries Source: https://hexdocs.pm/nx_signal/spectrogram.html Install the necessary libraries for signal processing and visualization. This is typically the first step in using these tools. ```elixir Mix.install([ {:nx_signal, "~> 0.3"}, {:vega_lite, "~> 0.1"}, {:kino_vega_lite, "~> 0.1"} ]) ``` -------------------------------- ### Find Local Minima Excluding Global Minimum Source: https://hexdocs.pm/nx_signal/NxSignal.PeakFinding.html This example demonstrates finding local minima using `argrelextrema` with a comparator that excludes the global minimum. It ensures that only local minima, not the absolute smallest value in the tensor, are identified. ```elixir iex> x = Nx.tensor([0, 1, 0, 2, 1, 3, 0, 1]) iex> global_minimum = Nx.reduce_min(x) iex> comparator = fn x, y -> ...> x_not_global = Nx.not_equal(x, global_minimum) ...> y_not_global = Nx.not_equal(y, global_minimum) ...> both_not_global = Nx.logical_and(x_not_global, y_not_global) ...> Nx.logical_and(Nx.less(x, y), both_not_global) ...> end iex> result = NxSignal.PeakFinding.argrelextrema(x, comparator) iex> result.valid_indices #Nx.Tensor< u32 1 > iex> result.indices[0..0] #Nx.Tensor< s32[1][1] [ [4] ] > ``` -------------------------------- ### Generate a sawtooth waveform Source: https://hexdocs.pm/nx_signal/NxSignal.Waveforms.html Example of defining a 5Hz waveform sampled at 500Hz for 1 second. ```elixir t = Nx.linspace(0, 1, n: 500) n = Nx.multiply(2 * :math.pi() * 5, t) wave = NxSignal.Waveforms.sawtooth(n) ``` -------------------------------- ### Find Relative Extrema with Custom Comparator Source: https://hexdocs.pm/nx_signal/NxSignal.PeakFinding.html Use `argrelextrema` with a custom comparator function to find elements that meet specific criteria relative to their neighbors. This example defines a maximum as a value greater than or equal to double its neighbors. ```elixir iex> comparator = fn x, y -> Nx.greater_equal(x, Nx.multiply(y, 2)) end iex> x = Nx.tensor([0, 1, 3, 2, 0, 1, 0, 0, 0, 2, 1]) iex> result = NxSignal.PeakFinding.argrelextrema(x, comparator) iex> result.valid_indices #Nx.Tensor< u32 3 > iex> result.indices[0..2] #Nx.Tensor< s32[3][1] [ [5], [7], [9] ] > ``` -------------------------------- ### Generate polynomial sweeps with NxSignal Source: https://hexdocs.pm/nx_signal/NxSignal.Waveforms.html Demonstrates generating polynomial sweeps using different coefficients and phase offsets. ```elixir iex> t = Nx.linspace(0, 10, n: 5) iex> NxSignal.Waveforms.polynomial_sweep(t, Nx.tensor([2, 0, 1])) #Nx.Tensor< f32[5] [1.0, 0.8660273, -0.5000064, 1.7942519e-5, -0.4999892] > iex> NxSignal.Waveforms.polynomial_sweep(t, Nx.tensor([2, 0, 1]), phi: :math.pi() / 2) #Nx.Tensor< f32[5] [-4.371139e-8, 0.49999946, -0.8660195, 1.0, 0.86603385] > iex> NxSignal.Waveforms.polynomial_sweep(t, Nx.tensor([1, 0])) #Nx.Tensor< f32[5] [1.0, 0.70710653, -1.0, 0.7071085, 1.0] > iex> NxSignal.Waveforms.polynomial_sweep(t, Nx.tensor([1, 0]), phi: 180, phi_unit: :degrees) #Nx.Tensor< f32[5] [-1.0, -0.70710695, 1.0, -0.70711297, -1.0] > ``` -------------------------------- ### NxSignal Module Overview Source: https://hexdocs.pm/nx_signal/api-reference.html Overview of the available modules within the NxSignal library for digital signal processing. ```APIDOC ## NxSignal Modules ### Description The NxSignal library provides several modules to handle digital signal processing tasks using Nx tensors. ### Modules - **NxSignal.Convolution**: Provides functions for convolution operations. - **NxSignal.Filters**: Contains common filter functions for signal processing. - **NxSignal.PeakFinding**: Implements algorithms for identifying peaks in signals. - **NxSignal.Waveforms**: Includes functions to calculate waveforms based on time tensors. - **NxSignal.Windows**: Provides common window functions used in signal analysis. ``` -------------------------------- ### Windowing Utility Source: https://hexdocs.pm/nx_signal/NxSignal.html Returns a tensor of K windows of length N from an input tensor. ```APIDOC ## AS_WINDOWED as_windowed(tensor, opts) ### Description Returns a tensor of K windows of length N. ### Parameters #### Options - **:window_length** (number) - Required - The number of samples in a window. - **:stride** (number) - Optional - The number of samples to skip between windows. Defaults to 1. - **:padding** (atom/string) - Optional - Padding mode: :reflect, :valid, :same, or :zeros. Defaults to :valid. ``` -------------------------------- ### Prepare signal data Source: https://hexdocs.pm/nx_signal/filtering.html Generate synthetic signal data and prepare it for visualization. ```elixir fs = 16.0e3 window_duration_seconds = 100.0e-3 window_length = 2 ** ceil(:math.log2(fs * window_duration_seconds)) signal_duration_seconds = 3 signal_length = ceil(signal_duration_seconds * fs) sin = fn freq, n -> Nx.sin(Nx.multiply(2 * :math.pi() * freq / fs, n)) end half_n = Nx.iota({div(signal_length, 2)}) sin220 = sin.(220, half_n) sin440 = sin.(440, half_n) sin1000 = sin.(440 * 5 / 2, half_n) sin3000 = sin.(220 * 4 / 3 * 4, half_n) n = Nx.iota({signal_length}) t = Nx.divide(n, fs) data = Nx.concatenate([Nx.add(sin440, sin1000), Nx.add(sin220, sin3000)]) # Data for plotting slice = (signal_length - 3000)..(signal_length - 2750) plot_data = %{y: Nx.to_flat_list(data[[slice]]), x: Nx.to_flat_list(t[[slice]])} ``` ```elixir VegaLite.new(width: 600, height: 400, title: "Signal sample") |> VegaLite.data_from_values(plot_data, only: ["x", "y"]) |> VegaLite.mark(:line) |> VegaLite.encode_field(:x, "x", type: :quantitative) |> VegaLite.encode_field(:y, "y", type: :quantitative) ``` -------------------------------- ### Plot Audio Sample Data Source: https://hexdocs.pm/nx_signal/spectrogram.html Visualizes a segment of the generated audio data as a line plot using Vega-Lite. Ensure the data `d` is available in the scope. ```elixir VegaLite.new(width: 600, height: 600, title: "Audio Sample") |> VegaLite.data_from_values(d, only: ["n", "data"]) |> VegaLite.mark(:line) |> VegaLite.encode_field(:x, "n", type: :ordinal) |> VegaLite.encode_field(:y, "data", type: :quantitative) ``` -------------------------------- ### Design and visualize FIR filter Source: https://hexdocs.pm/nx_signal/filtering.html Create a low-pass FIR filter and visualize its time-domain coefficients and frequency response. ```elixir # Cutoff frequency in Hz fc = 600 # Design the FIR filter coefficients using the window method h = NxSignal.Filters.firwin(window_length, fc, sampling_rate: fs, window: :hann) # Separate periodic Hann window for STFT analysis stft_window = NxSignal.Windows.hann(window_length) hfft = h |> Nx.fft(length: window_length) |> Nx.abs() |> Nx.add(1.0e-10) hfft_power = hfft |> Nx.log() |> Nx.divide(Nx.log(10)) |> Nx.multiply(20) f_idx = Nx.iota({window_length}) |> Nx.subtract(div(window_length, 2)) f = Nx.multiply(f_idx, fs / window_length) plot_data = %{ n: Enum.to_list(0..(window_length - 1)), h: Nx.to_flat_list(h), hfft: Nx.to_flat_list( Nx.take_along_axis( hfft_power, Nx.select(Nx.less(f_idx, 0), Nx.add(f_idx, window_length), f_idx) ) ), f: Nx.to_flat_list(f) } ``` ```elixir VegaLite.new( width: 600, height: 400, title: "600 Hz Low-Pass filter (Hann window); L = 2048" ) |> VegaLite.data_from_values(plot_data, only: ["n", "h"]) |> VegaLite.mark(:line) |> VegaLite.encode_field(:x, "n", type: :quantitative) |> VegaLite.encode_field(:y, "h", type: :quantitative) ``` ```elixir VegaLite.new( width: 600, height: 400, title: "FFT - 600 Hz Low-Pass filter (Hann window); L = 2048" ) |> VegaLite.data_from_values(plot_data, only: ["f", "hfft"]) |> VegaLite.mark(:line) |> VegaLite.encode_field(:x, "f", type: :quantitative) |> VegaLite.encode_field(:y, "hfft", type: :quantitative) ``` -------------------------------- ### Windowing Utilities Source: https://hexdocs.pm/nx_signal Functions for manipulating signal tensors into windowed formats. ```APIDOC ## as_windowed ### Description Returns a tensor of K windows of length N from the input tensor. ### Method FUNCTION ### Endpoint NxSignal.as_windowed(tensor, opts) ### Parameters #### Arguments - **tensor** (Tensor) - Required - The input signal tensor. #### Options - **window_length** (number) - Optional - The number of samples in a window. - **stride** (number) - Optional - The number of samples to skip between windows. Defaults to 1. - **padding** (atom/string) - Optional - Padding mode: :reflect, :valid, :zeros, or custom padding. Defaults to :valid. ``` -------------------------------- ### Design an FIR filter with firwin Source: https://hexdocs.pm/nx_signal/NxSignal.Filters.html Computes FIR filter coefficients using the window method. ```elixir iex> coeffs = NxSignal.Filters.firwin(5, [0.3], window: :hamming, sampling_rate: 2.0) iex> Nx.shape(coeffs) {5} ``` -------------------------------- ### Create windowed views of tensors with NxSignal.as_windowed Source: https://hexdocs.pm/nx_signal/NxSignal.html Generates sliding window views of a tensor. Supports custom window lengths, strides, and padding strategies like reflection. ```elixir iex> NxSignal.as_windowed(Nx.tensor([0, 1, 2, 3, 4, 10, 11, 12]), window_length: 4) #Nx.Tensor< s32[5][4] [ [0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 10], [3, 4, 10, 11], [4, 10, 11, 12] ] > ``` ```elixir iex> NxSignal.as_windowed(Nx.tensor([0, 1, 2, 3, 4, 10, 11, 12]), window_length: 3) #Nx.Tensor< s32[6][3] [ [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 10], [4, 10, 11], [10, 11, 12] ] > ``` ```elixir iex> NxSignal.as_windowed(Nx.tensor([0, 1, 2, 3, 4, 10, 11]), window_length: 2, stride: 2, padding: [{0, 3}]) #Nx.Tensor< s32[5][2] [ [0, 1], [2, 3], [4, 10], [11, 0], [0, 0] ] > ``` ```elixir iex> t = Nx.iota({7}); iex> NxSignal.as_windowed(t, window_length: 6, padding: :reflect, stride: 1) #Nx.Tensor< s32[8][6] [ [3, 2, 1, 0, 1, 2], [2, 1, 0, 1, 2, 3], [1, 0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 5], [3, 4, 5, 6, 5, 4], [4, 5, 6, 5, 4, 3] ] > ``` ```elixir iex> NxSignal.as_windowed(Nx.iota({10}), window_length: 6, padding: :reflect, stride: 2) #Nx.Tensor< s32[6][6] [ [3, 2, 1, 0, 1, 2], [1, 0, 1, 2, 3, 4], [1, 2, 3, 4, 5, 6], [3, 4, 5, 6, 7, 8], [5, 6, 7, 8, 9, 8], [7, 8, 9, 8, 7, 6] ] > ``` -------------------------------- ### Generate periodic square waves Source: https://hexdocs.pm/nx_signal/NxSignal.Waveforms.html Demonstrates square wave generation with constant and time-varying duty cycles. ```elixir iex> t = Nx.iota({10}) |> Nx.multiply(:math.pi() * 2 / 10) iex> NxSignal.Waveforms.square(t, duty: 0.1) #Nx.Tensor< s32[10] [1, -1, -1, -1, -1, -1, -1, -1, -1, -1] > iex> NxSignal.Waveforms.square(t, duty: 0.5) #Nx.Tensor< s32[10] [1, 1, 1, 1, 1, -1, -1, -1, -1, -1] > iex> NxSignal.Waveforms.square(t, duty: 1) #Nx.Tensor< s32[10] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] > iex> t = Nx.iota({10}) |> Nx.multiply(:math.pi() * 2 / 10) iex> duty = Nx.tensor([0.1, 0, 0.3, 0, 0.5, 0, 0.7, 0, 0.9, 0]) iex> NxSignal.Waveforms.square(t, duty: duty) #Nx.Tensor< s32[10] [1, -1, 1, -1, 1, -1, 1, -1, 1, -1] > ``` -------------------------------- ### Generate Triangular Window Source: https://hexdocs.pm/nx_signal/NxSignal.Windows.html Creates a triangular window. Options include window length, output type, and axis name. ```elixir iex> NxSignal.Windows.triangular(3) #Nx.Tensor< f32[3] [0.5, 1.0, 0.5] > ``` -------------------------------- ### NxSignal.Windows Functions Source: https://hexdocs.pm/nx_signal/NxSignal.Windows.html Common window functions for signal processing. ```APIDOC ## bartlett(n, opts) ### Description Bartlett triangular window. ### Parameters #### Path Parameters - **n** (integer) - Required - The window length. #### Options - **:type** (tuple) - Optional - The output type for the window. Defaults to {:f, 32}. - **:name** (atom) - Optional - The axis name. Defaults to nil. ## blackman(n, opts) ### Description Blackman window. ### Parameters #### Path Parameters - **n** (integer) - Required - The window length. #### Options - **:is_periodic** (boolean) - Optional - If true, produces a periodic window, otherwise produces a symmetric window. Defaults to true. - **:type** (tuple) - Optional - The output type for the window. Defaults to {:f, 32}. - **:name** (atom) - Optional - The axis name. Defaults to nil. ## hamming(n, opts) ### Description Hamming window. ### Parameters #### Path Parameters - **n** (integer) - Required - The window length. #### Options - **:is_periodic** (boolean) - Optional - If true, produces a periodic window, otherwise produces a symmetric window. Defaults to true. - **:type** (tuple) - Optional - The output type for the window. Defaults to {:f, 32}. - **:name** (atom) - Optional - The axis name. Defaults to nil. ## hann(n, opts) ### Description Hann window. ### Parameters #### Path Parameters - **n** (integer) - Required - The window length. #### Options - **:is_periodic** (boolean) - Optional - If true, produces a periodic window, otherwise produces a symmetric window. Defaults to true. - **:type** (tuple) - Optional - The output type for the window. Defaults to {:f, 32}. - **:name** (atom) - Optional - The axis name. Defaults to nil. ## kaiser(n, opts) ### Description Creates a Kaiser window of size window_length using a Bessel function. ### Parameters #### Path Parameters - **n** (integer) - Required - The window length. #### Options - **:is_periodic** (boolean) - Optional - If true, produces a periodic window, otherwise produces a symmetric window. Defaults to true. - **:type** (tuple) - Optional - The output type for the window. Defaults to {:f, 32}. - **:beta** (float) - Optional - Shape parameter. Defaults to 12.0. - **:eps** (float) - Optional - Epsilon value to avoid division by zero. Defaults to 1.0e-7. - **:axis_name** (atom) - Optional - The axis name. Defaults to nil. ## rectangular(n, opts) ### Description Rectangular window, useful when no window function should be applied. ### Parameters #### Path Parameters - **n** (integer) - Required - The window length. #### Options - **:type** (atom) - Optional - The output type. Defaults to s64. ## triangular(n, opts) ### Description Triangular window. ### Parameters #### Path Parameters - **n** (integer) - Required - The window length. #### Options - **:type** (tuple) - Optional - The output type for the window. Defaults to {:f, 32}. - **:name** (atom) - Optional - The axis name. Defaults to nil. ``` -------------------------------- ### Find relative minima with order Source: https://hexdocs.pm/nx_signal/NxSignal.PeakFinding.html Use the order option to define the neighborhood size for relative minima detection. ```elixir iex> x = Nx.tensor([2, 1, 2, 3, 2, 0, 1, 0]) iex> %{indices: indices, valid_indices: valid_indices} = NxSignal.PeakFinding.argrelmin(x, order: 3) iex> valid_indices #Nx.Tensor< u32 1 > iex> indices #Nx.Tensor< s32[8][1] [ [1], [-1], [-1], [-1], [-1], [-1], [-1], [-1] ] > iex> Nx.slice_along_axis(indices, 0, Nx.to_number(valid_indices), axis: 0) #Nx.Tensor< s32[1][1] [ [1] ] > ``` -------------------------------- ### Plot Spectrogram with Default Window Source: https://hexdocs.pm/nx_signal/spectrogram.html Calculates and plots the spectrogram of the audio data using a default window duration. This provides a baseline visualization of the signal's frequency content over time. ```elixir Spectrogram.calculate_stft_and_plot_spectrogram(data, fs, 50.0e-3) ``` -------------------------------- ### Visualize reconstructed signal Source: https://hexdocs.pm/nx_signal/filtering.html Plots the reconstructed time-domain signal using VegaLite. ```elixir VegaLite.new(width: 600, height: 400, title: "Reconstructed Signal") |> VegaLite.data_from_values(plot_data, only: ["x", "y"]) |> VegaLite.mark(:line) |> VegaLite.encode_field(:x, "x", type: :quantitative) |> VegaLite.encode_field(:y, "y", type: :quantitative) ``` -------------------------------- ### Prepare spectrogram data for plotting Source: https://hexdocs.pm/nx_signal/filtering.html Iterates over time and frequency indices to format spectrogram data into a map suitable for visualization. ```elixir plot_data = for t_idx <- 0..(Nx.size(t) - 1), f_idx <- 0..max_f, Nx.to_number(f[[f_idx]]) <= 4000, reduce: %{"t" => [], "f" => [], "s" => [], "filtered_s" => []} do %{"t" => t_acc, "f" => f_acc, "s" => s_acc, "filtered_s" => filtered_s_acc} -> %{ "t" => [Nx.to_number(t[[t_idx]]) | t_acc], "f" => [Float.round(Nx.to_number(f[[f_idx]]), 3) | f_acc], "s" => [Nx.to_number(spectrogram[[t_idx, f_idx]]) | s_acc], "filtered_s" => [Nx.to_number(filtered_spectrogram[[t_idx, f_idx]]) | filtered_s_acc] } end ``` -------------------------------- ### Perform overlap-and-add reconstruction with NxSignal.overlap_and_add Source: https://hexdocs.pm/nx_signal/NxSignal.html Reconstructs a signal from windowed segments by overlapping and adding them. Requires specifying the overlap length. ```elixir iex> NxSignal.overlap_and_add(Nx.iota({3, 4}), overlap_length: 0) #Nx.Tensor< s32[12] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] > ``` ```elixir iex> NxSignal.overlap_and_add(Nx.iota({3, 4}), overlap_length: 3) #Nx.Tensor< s32[6] [0, 5, 15, 18, 17, 11] > ``` ```elixir iex> t = Nx.tensor([[[[0, 1, 2, 3], [4, 5, 6, 7]]], [[[10, 11, 12, 13], [14, 15, 16, 17]]]]) |> Nx.vectorize(x: 2, y: 1) iex> NxSignal.overlap_and_add(t, overlap_length: 3) #Nx.Tensor< vectorized[x: 2][y: 1] s32[5] [ [ [0, 5, 7, 9, 7] ], [ [10, 25, 27, 29, 17] ] ] > ``` -------------------------------- ### Generate Bartlett Triangular Window Source: https://hexdocs.pm/nx_signal/NxSignal.Windows.html Creates a Bartlett triangular window of a specified size. Options include output type and axis name. ```elixir iex> NxSignal.Windows.bartlett(3) #Nx.Tensor< f32[3] [0.0, 0.6666667, 0.6666666] > ``` -------------------------------- ### Define Spectrogram plotting module Source: https://hexdocs.pm/nx_signal/filtering.html Creates a reusable module for generating VegaLite spectrogram visualizations. ```elixir defmodule Spectrogram do alias VegaLite, as: Vl def plot(title, dataset) do Vl.new(title: title, width: 500, height: 500) |> Vl.mark(:rect) |> Vl.data_from_values(dataset) |> Vl.encode_field(:x, "t", type: :quantitative, title: "Time (seconds)", axis: [tick_min_step: 0.1], grid: false ) |> Vl.encode_field(:y, "f", type: :quantitative, sort: "-x", title: "Frequency (Hz)", axis: [tick_count: 25], grid: false ) |> Vl.encode_field(:color, "s", aggregate: :max, type: :quantitative, scale: [scheme: "viridis"], legend: [title: "dBFS"] ) |> Vl.config(view: [stroke: nil]) end end ``` -------------------------------- ### Spectrogram Calculation and Plotting Module Source: https://hexdocs.pm/nx_signal/spectrogram.html Defines a module for calculating and plotting spectrograms from audio input. It includes functions for STFT, data transformation, and visualization. ```elixir defmodule Spectrogram do alias VegaLite, as: Vl import Nx.Defn def calculate_stft_and_plot_spectrogram( input, fs, window_duration_ms, plot_cutoff_frequency \ 4000 ) do n_window = ceil(fs * window_duration_ms) {spectrogram, f, t, max_f} = stft(input, fs: fs, n_window: n_window) max_f = Nx.to_number(max_f) spectrogram = Nx.slice(spectrogram, [0, 0], [Nx.size(t), max_f]) f = Nx.slice(f, [0], [max_f]) spectrogram |> to_plot_data(f, t, plot_cutoff_frequency) |> plot() end defn stft(input, opts) do fs = opts[:fs] n_window = opts[:n_window] # ms to samples window = NxSignal.Windows.hann(n: n_window, is_periodic: true) # use the default overlap of 50% {s, t, f} = NxSignal.stft(input, window, sampling_rate: fs, fft_length: 1024) max_f = Nx.select(f >= fs / 2, Nx.iota(f.shape), Nx.size(f) + 1) |> Nx.argmin() spectrogram = Nx.abs(s) # to dBFS spectrogram = 20 * Nx.log(spectrogram / Nx.reduce_max(spectrogram)) / Nx.log(10) {spectrogram, f, t, max_f} end defp to_plot_data(s, f, t, plot_cutoff_frequency) do for t_idx <- 0..(Nx.size(t) - 1), f_idx <- 0..(Nx.size(f) - 1), Nx.to_number(f[[f_idx]]) <= plot_cutoff_frequency, reduce: %{"t" => [], "f" => [], "s" => []} do %{"t" => t_acc, "f" => f_acc, "s" => s_acc} -> %{"t" => [Nx.to_number(t[[t_idx]]) | t_acc], "f" => [Float.round(Nx.to_number(f[[f_idx]]), 3) | f_acc], "s" => [Nx.to_number(s[[t_idx, f_idx]]) | s_acc]} end end defp plot(dataset) do Vl.new(title: "Spectrogram", width: 500, height: 500) |> Vl.mark(:rect) |> Vl.data_from_values(dataset) |> Vl.encode_field(:x, "t", type: :quantitative, title: "Time (seconds)", axis: [tick_min_step: 0.1], grid: false ) |> Vl.encode_field(:y, "f", type: :quantitative, sort: "-x", title: "Frequency (Hz)", axis: [tick_count: 25], grid: false ) |> Vl.encode_field(:color, "s", aggregate: :max, type: :quantitative, scale: [scheme: "viridis"], legend: [title: "dBFS"] ) |> Vl.config(view: [stroke: nil]) end end ``` -------------------------------- ### Windowing Functions Source: https://hexdocs.pm/nx_signal Functions for creating and manipulating windowed signals. ```APIDOC ## as_windowed(tensor, opts \\ []) ### Description Returns a tensor of K windows of length N. ### Method N/A (Function Call) ### Endpoint N/A ### Parameters #### Arguments - **tensor** (Nx.Tensor) - Required - The input tensor. #### Options - **:window_size** (integer) - Required - The size of each window. - **:step** (integer) - Optional - The step size between windows. Defaults to `window_size`. - **:padding` (integer) - Optional - The amount of zero-padding to apply to the ends of the tensor. Defaults to `0`. ### Request Example ```elixir input_tensor = Nx.tensor(1..10) NxSignal.as_windowed(input_tensor, window_size: 3, step: 2) ``` ### Response #### Success Response (200) - **Tensor** (Nx.Tensor) - A tensor where each row represents a windowed segment of the input. #### Response Example ```json { "example": "#Nx.Tensor<\n i32[4, 3]\n [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]\n>" } ``` ``` ```APIDOC ## overlap_and_add(tensor, opts \\ []) ### Description Performs the overlap-and-add algorithm over an {..., M, N}-shaped tensor, where M is the number of windows and N is the window size. ### Method N/A (Function Call) ### Endpoint N/A ### Parameters #### Arguments - **tensor** (Nx.Tensor) - Required - The input tensor containing windowed segments. #### Options - **:step** (integer) - Required - The step size between windows (must be less than `window_size`). - **:window_size** (integer) - Optional - The size of each window. Defaults to the last dimension of the input tensor. ### Request Example ```elixir windowed_tensor = Nx.tensor([[1, 2, 3], [3, 4, 5], [5, 6, 7]]) NxSignal.overlap_and_add(windowed_tensor, step: 2) ``` ### Response #### Success Response (200) - **Tensor** (Nx.Tensor) - The reconstructed signal after overlap-and-add. #### Response Example ```json { "example": "#Nx.Tensor<\n i32[7]\n [1, 5, 12, 15, 18, 13, 7]\n>" } ``` ``` -------------------------------- ### Prepare signal data for line plot Source: https://hexdocs.pm/nx_signal/filtering.html Converts reconstructed signal slices into a flat list format for line chart visualization. ```elixir plot_data = %{ y: Nx.to_flat_list(data_out[[slice]]), x: Nx.to_flat_list(Nx.divide(n, fs)[[slice]]) } ``` -------------------------------- ### Generate Kaiser Window Source: https://hexdocs.pm/nx_signal/NxSignal.Windows.html Creates a Kaiser window using a Bessel function. Customizable with shape parameter 'beta', periodicity, output type, and epsilon. ```elixir iex> NxSignal.Windows.kaiser(4, beta: 12.0, is_periodic: true) #Nx.Tensor< f32[4] [5.277619e-5, 0.21566667, 1.0, 0.21566667] > ``` ```elixir iex> NxSignal.Windows.kaiser(5, beta: 12.0, is_periodic: true) #Nx.Tensor< f32[5] [5.277619e-5, 0.10171464, 0.792937, 0.792937, 0.10171464] > ``` ```elixir iex> NxSignal.Windows.kaiser(4, beta: 12.0, is_periodic: false) #Nx.Tensor< f32[4] [5.277619e-5, 0.5188395, 0.51883906, 5.277619e-5] > ``` -------------------------------- ### Plot Spectrogram with Shorter Window Source: https://hexdocs.pm/nx_signal/spectrogram.html Generates a spectrogram using a shorter window duration (25ms). Shorter windows improve time resolution but decrease frequency resolution. ```elixir Spectrogram.calculate_stft_and_plot_spectrogram(data, fs, 25.0e-3) ``` -------------------------------- ### Generate Hamming Window Source: https://hexdocs.pm/nx_signal/NxSignal.Windows.html Creates a Hamming window. Can be periodic or symmetric, with options for output type and axis name. ```elixir iex> NxSignal.Windows.hamming(5, is_periodic: true) #Nx.Tensor< f32[5] [0.08000001, 0.3978522, 0.9121479, 0.9121478, 0.39785212] > ``` ```elixir iex> NxSignal.Windows.hamming(5, is_periodic: false) #Nx.Tensor< f32[5] [0.08000001, 0.54, 1.0, 0.54, 0.08000001] > ``` -------------------------------- ### Generate Chirp Waveform with Logarithmic Interpolation Source: https://hexdocs.pm/nx_signal/NxSignal.Waveforms.html Generates a chirp waveform using logarithmic frequency interpolation. `f0` and `f1` must be non-zero and have the same sign. ```elixir t = Nx.linspace(0, 10, n: 5) NxSignal.Waveforms.chirp(t, 10, 10, 1, method: :logarithmic) ``` -------------------------------- ### Plot spectrograms Source: https://hexdocs.pm/nx_signal/filtering.html Generates visualizations for both raw and filtered spectrogram data. ```elixir Spectrogram.plot("Spectrogram", Map.take(plot_data, ["t", "f", "s"])) ``` ```elixir Spectrogram.plot( "Filtered Spectrogram", Map.take(plot_data, ["t", "f"]) |> Map.put("s", plot_data["filtered_s"]) ) ``` -------------------------------- ### Generate Blackman Window Source: https://hexdocs.pm/nx_signal/NxSignal.Windows.html Generates a Blackman window. Supports periodic and symmetric window types, with options for output type and axis name. ```elixir iex> NxSignal.Windows.blackman(5, is_periodic: false) #Nx.Tensor< f32[5] [-1.4901161e-8, 0.34000003, 0.99999994, 0.34000003, -1.4901161e-8] > ``` ```elixir iex> NxSignal.Windows.blackman(5, is_periodic: true) #Nx.Tensor< f32[5] [-1.4901161e-8, 0.20077012, 0.84922993, 0.84922993, 0.20077012] > ``` ```elixir iex> NxSignal.Windows.blackman(6, is_periodic: true, type: {:f, 32}) #Nx.Tensor< f32[6] [-1.4901161e-8, 0.13, 0.63, 0.99999994, 0.63, 0.13] > ``` -------------------------------- ### Generate Rectangular Window Source: https://hexdocs.pm/nx_signal/NxSignal.Windows.html Produces a rectangular window, useful when no window function is needed. Supports different output types. ```elixir iex> NxSignal.Windows.rectangular(5) #Nx.Tensor< s64[5] [1, 1, 1, 1, 1] > ``` ```elixir iex> NxSignal.Windows.rectangular(5, type: :f32) #Nx.Tensor< f32[5] [1.0, 1.0, 1.0, 1.0, 1.0] > ``` -------------------------------- ### Find relative maxima with order Source: https://hexdocs.pm/nx_signal/NxSignal.PeakFinding.html Use the order option to define the neighborhood size for relative maxima detection. ```elixir iex> x = Nx.tensor([2, 1, 2, 3, 2, 0, 1, 0]) iex> %{indices: indices, valid_indices: valid_indices} = NxSignal.PeakFinding.argrelmax(x, order: 3) iex> valid_indices #Nx.Tensor< u32 1 > iex> indices #Nx.Tensor< s32[8][1] [ [3], [-1], [-1], [-1], [-1], [-1], [-1], [-1] ] > iex> Nx.slice_along_axis(indices, 0, Nx.to_number(valid_indices), axis: 0) #Nx.Tensor< s32[1][1] [ [3] ] > ``` -------------------------------- ### Generate Audio Data with Sine Waves Source: https://hexdocs.pm/nx_signal/spectrogram.html Generates a digital audio signal composed of multiple sine waves at specified frequencies. This code sets up the sample rate and duration, then creates and concatenates sine waves. ```elixir # You can load an audio file here. For this example, # we're producing 3 seconds of 220Hz, 440Hz, 1kHz and 3kHz sine waves fs = 44.1e3 t_max = 3 full_n = ceil(fs * t_max) half_n = div(full_n, 2) # samples/sec * sec = samples n = Nx.iota({half_n}) sin = fn freq, n -> Nx.sin(Nx.multiply(2 * :math.pi() * freq / fs, n)) end sin220 = sin.(220, n) sin440 = sin.(440, n) sin1000 = sin.(1000, n) sin3000 = sin.(3000, n) data = Nx.concatenate([Nx.add(sin440, sin1000), Nx.add(sin220, sin3000)]) n = Nx.iota({full_n}) d = %{data: Nx.to_flat_list(data[[1000..1250]]), n: Nx.to_flat_list(n[[1000..1250]])} ``` -------------------------------- ### Generate Chirp Waveform with Quadratic Interpolation Source: https://hexdocs.pm/nx_signal/NxSignal.Waveforms.html Generates a chirp waveform using quadratic frequency interpolation. The `vertex_zero` option controls the vertex position. ```elixir t = Nx.linspace(0, 10, n: 5) NxSignal.Waveforms.chirp(t, 10, 10, 1, method: :quadratic) ``` ```elixir t = Nx.linspace(0, 10, n: 5) NxSignal.Waveforms.chirp(t, 10, 10, 1, method: :quadratic, vertex_zero: false) ``` -------------------------------- ### Generate Hann Window Source: https://hexdocs.pm/nx_signal/NxSignal.Windows.html Generates a Hann window, which can be periodic or symmetric. Options include output type and axis name. ```elixir iex> NxSignal.Windows.hann(5, is_periodic: false) #Nx.Tensor< f32[5] [0.0, 0.5, 1.0, 0.5, 0.0] > ``` ```elixir iex> NxSignal.Windows.hann(5, is_periodic: true) #Nx.Tensor< f32[5] [0.0, 0.34549153, 0.90450853, 0.9045085, 0.34549144] > ``` -------------------------------- ### fft_frequencies Source: https://hexdocs.pm/nx_signal/NxSignal.html Computes the frequency bins for a FFT with given options. ```APIDOC ## fft_frequencies(sampling_rate, opts) ### Description Computes the frequency bins for a FFT with given options. ### Parameters #### Arguments - **sampling_rate** (float) - Required - Sampling frequency in Hz. #### Options - **:fft_length** (integer) - Optional - Number of FFT frequency bins. - **:type** (tuple) - Optional - Optional output type. Defaults to {:f, 32}. - **:name** (atom) - Optional - Optional axis name for the tensor. Defaults to :frequencies ``` -------------------------------- ### Generate Chirp Waveform with Hyperbolic Interpolation Source: https://hexdocs.pm/nx_signal/NxSignal.Waveforms.html Generates a chirp waveform using hyperbolic frequency interpolation. Ensure time tensor `t` is defined. ```elixir t = Nx.linspace(0, 10, n: 5) NxSignal.Waveforms.chirp(t, 10, 10, 1, method: :hyperbolic) ``` -------------------------------- ### Plot Spectrogram with Medium Window Source: https://hexdocs.pm/nx_signal/spectrogram.html Generates a spectrogram using a medium window duration (100ms). This offers a balance between time and frequency resolution. ```elixir Spectrogram.calculate_stft_and_plot_spectrogram(data, fs, 100.0e-3) ``` -------------------------------- ### Compute Inverse Short-Time Fourier Transform (ISTFT) Source: https://hexdocs.pm/nx_signal/NxSignal.html Reconstructs a time-domain signal from its Short-Time Fourier Transform (STFT) representation. Ensure the window and options match those used for the STFT. Note that edge distortions may occur due to windowing. ```elixir iex> t = Nx.tensor([10, 10, 1, 0, 10, 10, 2, 20]) iex> w = NxSignal.Windows.hann(4) iex> opts = [sampling_rate: 1, fft_length: 4] iex> {z, _time, _freqs} = NxSignal.stft(t, w, opts) iex> result = NxSignal.istft(z, w, opts) iex> Nx.as_type(result, Nx.type(t)) #Nx.Tensor< s32[8] [0, 10, 1, 0, 10, 10, 2, 20] > ``` ```elixir iex> t = Nx.tensor([10, 10, 1, 0, 10, 10, 2, 20]) iex> w = NxSignal.Windows.hann(4) iex> opts = [scaling: :spectrum, sampling_rate: 1, fft_length: 4] iex> {z, _time, _freqs} = NxSignal.stft(t, w, opts) iex> result = NxSignal.istft(z, w, opts) iex> Nx.as_type(result, Nx.type(t)) #Nx.Tensor< s32[8] [0, 10, 1, 0, 10, 10, 2, 20] > ``` ```elixir iex> t = Nx.tensor([10, 10, 1, 0, 10, 10, 2, 20], type: :f32) iex> w = NxSignal.Windows.hann(4) iex> opts = [scaling: :psd, sampling_rate: 1, fft_length: 4] iex> {z, _time, _freqs} = NxSignal.stft(t, w, opts) iex> result = NxSignal.istft(z, w, opts) iex> Nx.as_type(result, Nx.type(t)) #Nx.Tensor< f32[8] [0.0, 10.0, 0.99999994, -2.1900146e-7, 10.0, 10.0, 2.0000002, 20.0] > ``` -------------------------------- ### Generate Chirp Waveform with Linear Interpolation Source: https://hexdocs.pm/nx_signal/NxSignal.Waveforms.html Generates a chirp waveform using linear frequency interpolation. Ensure time tensor `t` is defined. ```elixir t = Nx.linspace(0, 10, n: 5) NxSignal.Waveforms.chirp(t, 10, 10, 1, method: :linear) ```