Pode um desenvolvedor Java sonhar com DSP?
Introdução
Pode um desenvolvedor Java sonhar com DSP?
Introdução
Nos últimos meses, tenho me desafiado a trabalhar com Java para análise de áudio e processamento de sinal digital (DSP). Pode parecer uma escolha improvável utilizar Java para este tipo de tarefa, dada a quantidade de linguagens (ex.: PureData, Max/MSP, SuperCollider, Python e Faust) e de bibliotecas e frameworks (ex.: librosa, JUCE, iPlug2, AudioKit e Jamba) consolidados há anos no mercado. Ainda assim, o Java continua a evoluir. Nas últimas décadas, uma série de projetos conduzidos pela comunidade transformou a linguagem de forma substancial, e pretendo dissertar melhor sobre suas possibilidades no contexto de processamento de áudio em um artigo futuro.
Por ora, este texto documenta parte do conhecimento que adquiri ao utilizar o Project Panama para conectar bibliotecas nativas de processamento de áudio ao Java, com o objetivo simples de organizar e compartilhar o que aprendi. O conteúdo aqui não almeja cobrir todos os conceitos e pormenores do Project Panama; há muitos artigos na internet que já cumprem esse papel. Meu objetivo é simplesmente demonstrar uma aplicação prática que evidencie a eficácia do projeto em conectar bibliotecas DSP nativas, escritas em outras linguagens, ao ecossistema Java.
O restante do artigo organiza-se da seguinte forma: começo apresentando o Project Panama, o que é, o que propõe, em qual estágio se encontra e quais são suas vantagens e desvantagens. Em seguida, demonstro seu uso em um cenário onde calculo Transformadas Discretas de Fourier em Java por meio de chamadas a funções e sub-rotinas escritas em Fortran. Por fim, apresento as conclusões.
O que é Project Panama?
O Project Panama é uma iniciativa da OpenJDK com o objetivo de integrar e aprimorar a interoperabilidade da Máquina Virtual Java (JVM) com APIs nativas externas escritas em C, C++ ou em qualquer linguagem capaz de expor uma Interface Binária de Aplicação (ABI) compatível com C. Para alcançar esse objetivo, o projeto contempla:
- Comunicação entre a JVM e código nativo;
- Invocação de funções nativas a partir da JVM;
- Acesso a dados no heap da JVM e fora dele;
- Novos layouts de dados na memória gerenciada;
- Integração e gerenciamento de bibliotecas nativas;
- APIs dedicadas ao carregamento e gerenciamento de bibliotecas;
- Extração automática de APIs com base em arquivos de cabeçalho C;
- Definição de metadados nativos legíveis pela JVM;
- Suporte experimental à adoção de bibliotecas nativas de difícil integração;
- Mecanismos de resolução dinâmica de classes e métodos;
- Otimizações JIT voltadas ao código nativo;
- Recursos de interposição e encapsulamento para fins de segurança e instrumentação.
Estado atual do projeto
O projeto avança por meio de três componentes principais. A Vector API introduz uma interface para expressão de cálculos vetoriais que o compilador traduz, de forma confiável em tempo de execução, em instruções otimizadas para arquiteturas de CPU compatíveis. A Foreign Function and Memory API (FFM) permite chamar bibliotecas nativas em C, como PortAudio, JACK e CoreAudio, com menos overhead e maior segurança. Por fim, o jextract gera automaticamente bindings Java a partir de cabeçalhos C de bibliotecas nativas.
Com exceção da Vector API, que já demonstra maturidade suficiente para uso em produção em alguns projetos (mas cuja finalização aguarda avanços do Project Valhalla, mais especificamente a estabilização dos value types), o Project Panama já não é tão ativo assim. A FFM API foi finalizada no JDK 22 com boa recepção da comunidade, e o foco atual está em viabilizar acesso à memória nativa de forma mais rápida, reduzindo os tempos de inicialização e aquecimento (warmup) decorrentes do uso de method e variable handles. Já o jextract concentra seus esforços em aproveitar a futura API de StableValues para emitir bindings mais simples e com menos classes auxiliares. Em resumo, o trabalho nesses componentes consiste menos em novas funcionalidades e mais em manutenção regular.
Seus prós e contras
O Project Panama trouxe melhorias concretas à forma como aplicações Java interagem com código nativo. A alternativa padrão para esse tipo de tarefa sempre foi a Java Native Interface (JNI) que, apesar de funcional, é complexa, verbosa e propensa a erros. Com a FFM API e o jextract, a redução de boilerplate torna o código mais legível e seguro, facilitando o acesso a bibliotecas nativas.
Além dessa melhoria no design, um dos grandes atrativos do projeto está no desempenho. As chamadas a funções nativas tendem a ser mais eficientes do que via JNI, beneficiando-se de otimizações da JVM. Isso torna a solução vantajosa em contextos onde atraso é um ponto crítico, como processamento intensivo de dados, computação científica, machine learning ou sistemas de baixa latência. Outro ponto forte é o controle direto sobre a memória: por meio de abstrações como segmentos de memória, é possível lidar com estruturas grandes ou formatos binários de maneira previsível e eficiente, contornando a sobrecarga do garbage collector em cenários críticos.
Por outro lado, mesmo sendo mais simples que o JNI, as funcionalidades do Project Panama ainda exigem a compreensão de conceitos como layouts de memória, arenas de alocação e mecanismos de ligação com código nativo. A curva de aprendizado é considerável, especialmente para desenvolvedores acostumados ao modelo tradicional do Java, mais abstraído e protegido. Além disso, embora a API esteja mais estável no Java 25, ela ainda é relativamente recente no ecossistema, o que se reflete em menor suporte por parte de ferramentas, bibliotecas e frameworks. Outro aspecto importante é que o uso do Project Panama reintroduz problemas que o Java historicamente evitou. Ao lidar com memória manual e código nativo, o desenvolvedor fica exposto a riscos como vazamentos de memória, corrupção de dados e falhas graves de execução, como segmentation faults. Isso também torna o debugging mais complexo, já que identificar se um erro está no código Java ou na camada nativa nem sempre é trivial.
A portabilidade também pode ser afetada. Ao depender de bibliotecas nativas, o comportamento da aplicação pode variar entre sistemas operacionais e arquiteturas, exigindo cuidados adicionais para garantir compatibilidade entre ambientes. Diante disso, o Project Panama faz mais sentido em cenários com necessidade clara de alto desempenho ou integração com bibliotecas nativas já existentes. Em aplicações mais convencionais, como backends web ou APIs REST (onde o Java padrão já oferece desempenho suficiente), sua adoção pode introduzir complexidade desnecessária.
Aplicando o Project Panama na prática
Nosso estudo de caso será a integração com a biblioteca FFTPack, um pacote de sub-rotinas originalmente escrito em Fortran para a transformada rápida de Fourier de sequências periódicas e outras sequências simétricas. Ela inclui transformadas complexas, reais, seno, cosseno e de quarto de onda, e seu algoritmo serve de base para diversas implementações modernas. Um exemplo é a biblioteca Scipy do Python, que deriva diretamente do FFTPack e expõe essas transformadas por uma interface simplificada.
Nosso maior desafio nesta integração será resolver a incompatibilidade de interfaces, visto que o FFTPack é uma biblioteca Fortran pura e o jextract exige headers C para gerar os bindings Java. A boa notícia é que a biblioteca foi modernizada e reescrita pela comunidade Fortran com suporte a iso_c_binding, o que permite expor suas funções com ABI C diretamente.
Nossa estratégia consistirá, portanto, em criar uma camada de tradução. Encapsulamos os algoritmos do FFTPack em um wrapper C que expõe uma interface compatível com esse ecossistema. Em seguida, compilamos tudo como uma biblioteca compartilhada e utilizamos o jextract para analisar o header C e gerar os bindings em Java, permitindo que nosso código invoque diretamente as funções do FFTPack em Fortran.
Antes de iniciarmos, é necessário preparar nosso ambiente com as seguintes ferramentas:
- Java 25+ para suporte ao Project Panama;
- Maven 3.9+;
- JExtract;
- Ferramentas para compilar C e FFTPack: (Cmake, gfortran e fypp);
1. Configurando o projeto para compilar a biblioteca FFTPack nativa
O primeiro passo consiste em compilar e instalar a biblioteca FFTPack junto ao seu wrapper C. Para isso, o projeto depende de três arquivos: um responsável por exportar as funções da biblioteca com ABI C pura; um header C que permite ao jextract gerar os bindings para invocar as funções nativas do FFTPack; e o CMakeLists.txt, que gerencia todo o processo de compilação e construção do projeto.
- Arquivo para exportar as funções Fortran
! Wrapper que exporta as funções do fftpack com ABI C pura.
! iso_c_binding garante que os tipos e convenções de chamada
! sejam compatíveis com C (e portanto com jextract).
module fftpack_c
use iso_c_binding
use fftpack, only: dfftf, dfftb, dffti
implicit none
contains
! Inicializa os fatores para N pontos
subroutine c_dffti(n, wsave) bind(C, name="c_dffti")
integer(c_int), value, intent(in) :: n
real(c_double), intent(out) :: wsave(*)
call dffti(n, wsave)
end subroutine
! Forward FFT (real → complexo empacotado)
subroutine c_dfftf(n, r, wsave) bind(C, name="c_dfftf")
integer(c_int), value, intent(in) :: n
real(c_double), intent(inout) :: r(*)
real(c_double), intent(in) :: wsave(*)
call dfftf(n, r, wsave)
end subroutine
! Backward FFT (complexo empacotado → real)
subroutine c_dfftb(n, r, wsave) bind(C, name="c_dfftb")
integer(c_int), value, intent(in) :: n
real(c_double), intent(inout) :: r(*)
real(c_double), intent(in) :: wsave(*)
call dfftb(n, r, wsave)
end subroutine
end module fftpack_c
- Header C
#ifndef FFTPACK_H
#define FFTPACK_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Inicializa o array wsave para N pontos.
* wsave deve ter tamanho >= 2*N + 15.
*/
void c_dffti(int n, double* wsave);
/**
* Forward FFT in-place de r[0..n-1].
* Resultado empacotado: r[0]=DC, r[1]=Re[1], r[2]=Im[1], ...
*/
void c_dfftf(int n, double* r, const double* wsave);
/**
* Backward (inverse) FFT in-place de r[0..n-1].
* ATENÇÃO: resultado NÃO normalizado — divida por N.
*/
void c_dfftb(int n, double* r, const double* wsave);
#ifdef __cplusplus
}
#endif
#endif /* FFTPACK_H */
- Arquivo CMakeLists
cmake_minimum_required(VERSION 3.22)
project(fftpack_cwrapper LANGUAGES Fortran)
# Puxa o pacote fftpack compilado como uma dependência externa.
find_library(FFTPACK_LIB
NAMES fortran_fftpack
PATHS "${FFTPACK_INSTALL_DIR}/lib"
REQUIRED
)
find_path(FFTPACK_MODULE_DIR
NAMES fftpack.mod
PATHS "${FFTPACK_INSTALL_DIR}"
PATH_SUFFIXES "lib/cmake/fftpack" "include" "mod"
REQUIRED
)
# Biblioteca compartilhada que expõe a ABI C
add_library(fftpack_cwrapper SHARED fftpack_c.f90)
target_link_libraries(fftpack_cwrapper PRIVATE "${FFTPACK_LIB}")
# Aponta para arquivos .mod gerados pela compilação do fftpack.
target_include_directories(fftpack_cwrapper PRIVATE "${FFTPACK_MODULE_DIR}")
set_target_properties(fftpack_cwrapper PROPERTIES
BUILD_RPATH "${FFTPACK_INSTALL_DIR}/lib"
INSTALL_RPATH "${FFTPACK_INSTALL_DIR}/lib" # caminho absoluto gravado na biblioteca
)
# Instala lib e header
install(TARGETS fftpack_cwrapper
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
install(FILES fftpack.h
DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
)
Para facilitar a reprodução do ambiente, o script bash abaixo automatiza a compilação e a instalação do FFTPack junto ao seu wrapper C:
#!/bin/bash
set -e
echo "=== Construindo biblioteca nativa FFTPack ==="
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FFTPACK_DIR="$SCRIPT_DIR/../native/fftpack"
WRAPPER_DIR="$SCRIPT_DIR/../native/fftpack_cwrapper"
FFTPACK_CMAKE_BUILD="$SCRIPT_DIR/../build/fftpack/cmake"
WRAPPER_CMAKE_BUILD="$SCRIPT_DIR/../build/fftpack/cmake_wrapper"
INSTALL_DIR="$SCRIPT_DIR/../build/native/fftpack"
mkdir -p "$FFTPACK_CMAKE_BUILD"
mkdir -p "$WRAPPER_CMAKE_BUILD"
mkdir -p "$INSTALL_DIR"
# ── 1. Compila o fftpack original ─────────────────────────────────────────
echo ""
echo "--- Step 1: Construindo fftpack core ---"
cmake -B "$FFTPACK_CMAKE_BUILD" \
-S "$FFTPACK_DIR" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" \
-DBUILD_SHARED_LIBS=ON \
-DBUILD_TESTING=OFF
cmake --build "$FFTPACK_CMAKE_BUILD" --parallel "$(nproc)"
cmake --install "$FFTPACK_CMAKE_BUILD"
# ── 2. Compila o C wrapper ─────────────────────────────────────────────────
echo ""
echo "--- Step 2: Construindo C wrapper ---"
# Localiza os arquivos .mod gerados pelo fftpack
# (gfortran os coloca em subpastas do build ou install)
FFTPACK_MOD_DIR=$(find "$INSTALL_DIR" -name "fftpack.mod" -exec dirname {} \; | head -1)
if [ -z "$FFTPACK_MOD_DIR" ]; then
# Fallback: procura no diretório de build
FFTPACK_MOD_DIR=$(find "$FFTPACK_CMAKE_BUILD" -name "fftpack.mod" -exec dirname {} \; | head -1)
fi
if [ -z "$FFTPACK_MOD_DIR" ]; then
echo "ERRO: fftpack.mod não encontrado. O build do fftpack falhou?"
exit 1
fi
echo "fftpack.mod encontrado em: $FFTPACK_MOD_DIR"
cmake -B "$WRAPPER_CMAKE_BUILD" \
-S "$WRAPPER_DIR" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" \
-DFFTPACK_INSTALL_DIR="$INSTALL_DIR" \
-DFFTPACK_MODULE_DIR="$FFTPACK_MOD_DIR"
cmake --build "$WRAPPER_CMAKE_BUILD" --parallel "$(nproc)"
cmake --install "$WRAPPER_CMAKE_BUILD"
echo ""
echo "=== FFTPack compilado com sucesso ==="
echo "Library location : $INSTALL_DIR/lib"
echo "Headers location : $INSTALL_DIR/include"
2. Gerando os bindings com jextract e chamando a biblioteca nativa via Java
Após a etapa descrita acima, executamos o comando abaixo para que o jextract gere os bindings Java:
jextract \
--output target/generated-sources/jextract \
--target-package org.com.fftpack.transforms \
-I build/native/fftpack/include \
-l :${ABSOLUTE_PATH}/libfftpack_cwrapper.so build/native/fftpack/include/fftpack.h
Os comandos opcionais que utilizamos foram:
--output <path>: Especifica o diretório onde os arquivos gerados serão salvos. Se esta opção não for especificada, o diretório atual será usado;--target-package <package>: Nome do pacote de destino para as classes geradas. Se esta opção não for especificada, será utilizado um pacote sem nome;-I <dir>: Adiciona o diretório ao final da lista de caminhos de pesquisa de inclusão;-l <libspec>: Especifica uma biblioteca compartilhada que deve ser carregada pela classe de cabeçalho gerada. Se <libspec> começar com :, então o que segue é interpretado como um caminho de biblioteca. Caso contrário, <libspec> denota um nome de biblioteca.
Após o bindings estarem acessíveis, podemos utilizá-los em nossas classes Java envoltos por uma Arena, que controlará o ciclo de vida dos segmentos de memória nativa.
package org.com.fftpack.transforms;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
public class FastFourierTransform {
public double[] forward(double[] input) {
int N = input.length;
int wSaveLen = 2 * N + 15;
try (Arena arena = Arena.ofConfined()) {
MemorySegment wsave = arena.allocate(ValueLayout.JAVA_DOUBLE, wSaveLen);
fftpack_h.c_dffti(N, wsave);
MemorySegment r = arena.allocate(ValueLayout.JAVA_DOUBLE, N);
for (int i = 0; i < N; i++) {
r.setAtIndex(ValueLayout.JAVA_DOUBLE, i, input[i]);
}
fftpack_h.c_dfftf(N, r, wsave);
// Copia resultado de volta para Java
double[] result = new double[N];
for (int i = 0; i < N; i++) {
result[i] = r.getAtIndex(ValueLayout.JAVA_DOUBLE, i);
}
return result;
}
}
public double[] inverse(double[] packed) {
int N = packed.length;
int wSaveLen = 2 * N + 15;
try (Arena arena = Arena.ofConfined()) {
MemorySegment wsave = arena.allocate(ValueLayout.JAVA_DOUBLE, wSaveLen);
fftpack_h.c_dffti(N, wsave);
MemorySegment r = arena.allocate(ValueLayout.JAVA_DOUBLE, N);
for (int i = 0; i < N; i++) {
r.setAtIndex(ValueLayout.JAVA_DOUBLE, i, packed[i]);
}
fftpack_h.c_dfftb(N, r, wsave);
double[] result = new double[N];
for (int i = 0; i < N; i++) {
result[i] = r.getAtIndex(ValueLayout.JAVA_DOUBLE, i) / N;
}
return result;
}
}
}
3. Otimizando as etapas via build do projeto
Para simplificar a execução de todas as etapas descritas até aqui, podemos configurar o pom.xml do projeto para que execute os scripts que compilam a biblioteca, identifique o sistema operacional em que a aplicação rodará e gere os bindings com o jextract via arquivo de configuração. Isso torna o processo mais prático caso queiramos integrar mais de uma biblioteca nativa ao projeto.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- properties, dependencyManagement e dependencies do projeto -->
<profiles>
<profile>
<id>linux</id>
<activation>
<os>
<family>unix</family>
<name>Linux</name>
</os>
</activation>
<properties>
<lib.extension>so</lib.extension>
<lib.prefix>lib</lib.prefix>
<lib.suffix>.so</lib.suffix>
</properties>
</profile>
<profile>
<id>mac</id>
<activation>
<os>
<family>mac</family>
</os>
</activation>
<properties>
<lib.extension>dylib</lib.extension>
<lib.prefix>lib</lib.prefix>
<lib.suffix>.dylib</lib.suffix>
</properties>
</profile>
<profile>
<id>windows</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<lib.extension>dll</lib.extension>
<lib.prefix></lib.prefix>
<lib.suffix>.dll</lib.suffix>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<release>${java.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>${exec-maven-plugin.version}</version>
<executions>
<!-- 1. Compila todas as libs nativas (itera sobre todos os scripts) -->
<execution>
<id>compile-all-native-libraries</id>
<phase>generate-sources</phase>
<goals><goal>exec</goal></goals>
<configuration>
<executable>bash</executable>
<arguments>
<argument>-c</argument>
<argument>
set -e;
for script in ${scripts.dir}/*.sh; do
echo "[janis] Running: $script";
bash "$script";
done
</argument>
</arguments>
</configuration>
</execution>
<!-- 2. Gera bindings jextract para todos os submódulos via ficheiro de configuração -->
<execution>
<id>generate-all-bindings</id>
<phase>generate-sources</phase>
<goals><goal>exec</goal></goals>
<configuration>
<executable>bash</executable>
<arguments>
<argument>${project.basedir}/scripts/jextract-all.sh</argument>
<argument>${jextract.config.file}</argument>
<argument>${project.build.directory}/generated-sources/jextract</argument>
<argument>${native.build.dir}</argument>
<argument>${lib.prefix}</argument>
<argument>${lib.suffix}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals><goal>add-source</goal></goals>
<configuration>
<sources>
<source>${generated-source.dir}</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Conclusões
Neste artigo, aprendemos a conectar a biblioteca FFTPack ao Java por meio da FFM API e do jextract, dois componentes do Project Panama. Para viabilizar a integração, criamos uma camada de tradução em C que nos permitiu gerar bindings Java e invocar diretamente as funções nativas do FFTPack, originalmente escritas em Fortran.
O relato acima ilustra como essas novas funcionalidades abrem caminho para integrar ao Java bibliotecas clássicas de DSP sem reescrevê-las ou abandonar um ecossistema já familiar. Implementações consolidadas e amplamente testadas passam a fazer parte do ecossistema da linguagem, reduzindo a barreira de entrada para quem deseja explorar processamento de sinais. Mais do que isso, o Project Panama permite que a linguagem se beneficie de décadas de trabalho acumulado em códigos que resistem ao tempo, foram refinados em inúmeros casos de uso e não precisam ser reescrito para operar dentro da JVM.
Essa ponte representa menos um atalho técnico e mais uma mudança de perspectiva para um ecossistema que, historicamente, é distante do campo de análise e processamento digital de sinais. O Java não precisa começar do zero para se tornar uma opção viável nesse domínio.
Fontes
- Azeemi, M. D. (2025, September 25). Java’s Project Panama — The revolution software world needs. Medium. https://medium.com/@muhammaddaniyalazeemi/javas-project-panama-the-revolution-software-world-needs-782608cd1d02
- OpenJDK. (n.d.). jextract: A tool for generating Java bindings from native library headers [Repositório de software]. GitHub. Recuperado em 3 de maio de 2026, de https://github.com/openjdk/jextract
- OpenJDK. (2025, October 01). JEP 529: Vector API (Eleventh incubator). https://openjdk.org/jeps/529
- OpenJDK. (n.d.). panama-foreign: Interconnecting JVM and native code — Foreign Function & Memory API [Repositório de software]. GitHub. Recuperado em 3 de maio de 2026, de https://github.com/openjdk/panama-foreign
- OpenJDK. (n.d.). panama-vector: Vector API [Repositório de software]. GitHub. Recuperado em 3 de maio de 2026, de https://github.com/openjdk/panama-vector
- OpenJDK. (n.d.). Project Panama: Interconnecting JVM and native code. Recuperado em 3 de maio de 2026, de https://openjdk.org/projects/panama/
- Oracle. (n.d.). Panama [Tag page]. Inside Java. Recuperado em 3 de maio de 2026, de https://inside.java/tag/panama
- Parlog, N. (2025, January 16). Java’s plans for 2025 — Inside Java Newscast #83. nipafx. https://nipafx.dev/inside-java-newscast-83/
- Parlog, N. [Java]. (2026, January 8). Java’s plans for 2026 — Inside Java Newscast #104 [Video]. YouTube. https://www.youtube.com/watch?v=1lYsDMOc7hM
- Swarztrauber, P. N. (s.d.). FFTPACK: A package of Fortran subprograms for the fast Fourier transform [Software]. Netlib Repository. Recuperado em 4 de maio de 2026, de https://www.netlib.org/fftpack/
- SciPy Contributors. (s.d.). SciPy [Repositório de software]. GitHub. Recuperado em 4 de maio de 2026, de https://github.com/scipy/scipy
메타데이터
- post_id
- a4ea0673c38e
- slug
- pode-um-desenvolvedor-java-sonhar-com-dsp-a4ea0673c38e
- url
- https://medium.com/@thalesroel/pode-um-desenvolvedor-java-sonhar-com-dsp-a4ea0673c38e
- canonical_url
- https://medium.com/@thalesroel/pode-um-desenvolvedor-java-sonhar-com-dsp-a4ea0673c38e
- author_url
- https://medium.com/@thalesroel
- status
- ok
- fetched_at
- 2026-06-09 15:37:30