Introdução
Um som curto pode avisar que uma tarefa terminou. Neste laboratório, você criará o próprio som: um laço em C calcula uma sequência de números que se transforma em um arquivo WAV reproduzível. Primeiro ouça um exemplo funcional de 440 Hz, altere apenas sua altura e depois complete sua própria função reutilizável de amostragem.
Você precisa conhecer variáveis, aritmética, laços, arrays e funções de C, além da navegação básica no terminal. Amostragem de áudio, radianos e saída binária são apresentados aqui; ponteiros e processamento de sinais não são pré-requisitos. O compilador, um pequeno gravador WAV transparente e um reprodutor no navegador que só lê arquivos estão preparados. Todo o áudio é uma síntese original; nenhum dispositivo de gravação é necessário. Os 50 minutos são uma estimativa do autor, incluindo leitura e erros comuns.
Ouvir um tom funcional
Nesta etapa, você reproduzirá um tom funcional de meio segundo e alterará um valor para deixá-lo mais agudo. Comece em Terminal. cd muda de diretório; este caminho absoluto também recupera o diretório de trabalho após um engano. Mantenha seu trabalho aqui.
cd /home/labex/project/sound-studio
Uma amostra é um número com sinal que descreve a forma de onda em um instante. PCM significa modulação por código de pulsos: o arquivo guarda diretamente esses valores. Este laboratório usa um canal, chamado mono, e 16.000 amostras por segundo. Suas amostras de 16 bits com sinal representam inteiros de -32.768 a 32.767. Valores positivos e negativos são os dois lados da onda; zero é o centro. A amplitude controla o afastamento de zero. Se estiver ouvindo por alto-falantes ou fones, comece com um volume confortável.
O arquivo preparado sample.c cria uma senoide de 440 Hz: 440 ciclos por segundo. sin, declarada em <math.h>, recebe um ângulo em radianos e retorna um valor entre -1 e 1. Um ciclo completo corresponde a 2 * pi radianos. Multiplicar por 10.000 ajusta essa forma a uma amplitude moderada. Examinaremos o cálculo do tempo depois de ouvir o resultado.
Compile o exemplo fornecido. gcc compila C; -std=c11 seleciona C11, -Wall -Wextra ativa avisos úteis e -o sample dá nome ao executável. pcm.c fornece o gravador WAV. -lm, colocado após os arquivos-fonte, vincula a biblioteca matemática usada por sin. Esses arquivos não devem gerar avisos do compilador.
gcc -std=c11 -Wall -Wextra sample.c pcm.c -lm -o sample
./sample executa o programa neste diretório. Ele escreve bytes WAV binários na saída padrão. > redireciona esses bytes para um arquivo; não execute sem redirecionamento, pois bytes de áudio não são texto legível no terminal. outputs é um diretório já preparado. Use este novo nome:
./sample > outputs/sample-440.wav
O sucesso não imprime texto. Mude para a aba Sound Studio. Clique em Refresh files, escolha sample-440.wav em WAV file e clique em Load sound. A página deve mostrar Ready to play e 8000 samples · 16000 Hz · 0.500 s. Clique em Play e experimente Pause e Playback position. Você pode focar o controle de posição com Tab e usar Home, End e as setas. Os controles nativos podem arredondar uma duração inferior a um segundo para 0:00; o texto acima e a posição em segundos mostram o tempo exato.
Clique em Download WAV para salvar o arquivo real. O reprodutor lê o arquivo criado; não gera amostras ausentes. Cannot load sound indica arquivo ausente ou não suportado. Isso é diferente de um erro de conexão que impede a abertura da própria página. Se errar um caminho, volte a Terminal, use o cd acima, confira o nome, repita o comando correspondente e atualize a lista.

Observe sample-440.wav selecionado, os metadados de meio segundo e os controles Play, Playback position e Download WAV.
Volte a Terminal. cat exibe texto. Leia o exemplo curto:
cat sample.c
O laço visita os índices de amostra de 0 a 7999. seconds = (double)i / rate converte um índice em tempo: o índice 0 está em 0 segundo e o índice 160 em 0,010 segundo. (double) solicita divisão decimal em vez de inteira. A fase, 2.0 * pi * frequency * seconds, indica o ângulo percorrido nos ciclos. (int) converte o seno escalado em inteiro descartando a parte fracionária. A função fornecida wav_sample escreve os dois bytes PCM desse número; não escolhe o número por você.
Agora altere apenas a frequência. O próximo comando usa sed, um editor de texto para uma substituição específica. -i edita o arquivo indicado no próprio local; a expressão entre aspas s/old/new/ substitui o texto literal entre o primeiro par de barras pelo texto entre o segundo. Ela edita apenas sample.c.
sed -i 's/frequency = 440/frequency = 660/' sample.c
Recompile para incluir a alteração no executável e salve outro arquivo. Preserve também o primeiro WAV.
gcc -std=c11 -Wall -Wextra sample.c pcm.c -lm -o sample
./sample > outputs/sample-660.wav
Mude para Sound Studio, atualize, carregue sample-660.wav e reproduza. Ele tem as mesmas 8.000 amostras e meio segundo de duração, com altura maior. Só a frequência mudou. Preserve sample.c editado, o executável sample e os dois arquivos. Esta comparação é uma preparação; o programa reutilizável e seus resultados separados vêm a seguir.
Calcular cada amostra por conta própria
Nesta etapa, você completará write_tone para que uma função C funcione com diferentes alturas, durações e amplitudes. Comece em Terminal, em /home/labex/project/sound-studio.
A 16.000 amostras por segundo, 300 milissegundos exigem 16000 * 300 / 1000 = 4800 amostras. Um milissegundo é um milésimo de segundo. Durações em milissegundos inteiros têm contagens inteiras exatas nessa taxa. Um laço que começa em i = 0 e continua enquanto i < count emite exatamente count valores; <= acrescentaria uma amostra indesejada.
Considere um exemplo muito menor: a oito amostras por segundo, uma senoide de um ciclo por segundo tem aproximadamente os valores 0, 0.71, 1, 0.71, 0, -0.71, -1, -0.71 nos primeiros oito índices. O índice avança o tempo e sin fornece a posição vertical. Nossa taxa real de 16.000 Hz produz muito mais valores por ciclo.
Leia a interface e a função inicial fornecidas:
cat tone.h tone.c
write_tone(frequency, milliseconds, amplitude) deve gerar todas as amostras. A versão inicial escreve zeros; portanto, pode compilar e produzir um WAV estruturalmente válido, mas silencioso. Nome, tamanho ou término com sucesso não provam a existência de um tom.
Mantenha main.c, tone.h, pcm.h, pcm.c, inspect_wav.py e audio.py inalterados. O main.c preparado lê três argumentos, valida, escreve o cabeçalho e chama sua função. Você não precisa alterar o analisador de argumentos baseado em ponteiros. tone.c é sua implementação; o navegador já está pronto.
O gravador separa o empacotamento da síntese. wav_begin(count) escreve o cabeçalho uma vez; wav_sample(value) escreve um inteiro fornecido. Nenhuma gera uma onda. O pequeno pcm.c usa putchar para escrever um byte na saída padrão e fputs para nomes fixos de blocos. Ele emite o byte menos significativo antes do mais significativo, em vez de despejar uma estrutura C. Amostras negativas são codificadas em sua representação de 16 bits; o gravador rejeita valores fora do intervalo com sinal, sem limitar ou substituir valores.
Estes são os campos escritos. O nome fmt inclui o espaço final.
| Campo | Significado neste laboratório |
|---|---|
RIFF, size, WAVE |
Identificam um contêiner WAV; size é o total de bytes menos os primeiros 8 |
fmt, size 16 |
Descrevem os próximos 16 bytes de formato |
Format 1, channels 1 |
PCM sem compressão, mono |
Sample rate 16000 |
Amostras por segundo |
Byte rate 32000 |
16.000 quadros por segundo × 2 bytes por quadro |
Block alignment 2, sample bits 16 |
Um quadro contém uma amostra de dois bytes |
data, size 2 * count |
Identificam e contam os bytes PCM reais seguintes |
Você pode ler esse módulo fornecido com cat pcm.c. Seus campos descrevem a saída; alterar apenas um cabeçalho não transforma amostras silenciosas em som.
Implemente a função curta abaixo. <math.h> declara sin. pi é uma constante decimal local, então o programa não depende da macro não padronizada M_PI. amplitude é um fator aplicado antes da conversão em inteiro; o máximo de 12.000 do analisador mantém as amostras senoidais dentro dos 16 bits. Cada chamada começa seu índice de tempo em zero.
cat > tone.c substitui apenas esse arquivo-fonte. O here-document <<'C' envia as linhas seguintes a cat até a última linha contendo apenas C. As aspas no delimitador preservam literalmente o texto C. Cole o bloco inteiro, incluindo a última linha.
cat > tone.c <<'C'
#include <math.h>
#include "tone.h"
#include "pcm.h"
void write_tone(int frequency, int milliseconds, int amplitude) {
const int rate = 16000;
const double pi = 3.14159265358979323846;
int count = rate * milliseconds / 1000;
for (int i = 0; i < count; i++) {
double seconds = (double)i / rate;
double phase = 2.0 * pi * frequency * seconds;
int sample = (int)(amplitude * sin(phase));
wav_sample(sample);
}
}
C
Compile a função com o chamador e o gravador preparados. Se houver um erro de linha, corrija tone.c ou repita todo o bloco de substituição e recompile. Um executável antigo após falha de compilação testaria código antigo.
gcc -std=c11 -Wall -Wextra main.c tone.c pcm.c -lm -o tone
O executável recebe frequência em Hz, duração em milissegundos inteiros e amplitude, nessa ordem. Os intervalos inclusivos são frequência 200–1200, duração 100–1000 e amplitude 1000–12000. Crie duas saídas novas:
./tone 440 300 8000 > outputs/tone-440.wav
./tone 660 500 10000 > outputs/tone-660.wav
Use o leitor preparado para inspecionar os bytes realmente salvos. python3 executa a ferramenta Python indicada, somente para leitura; você não precisa escrever Python. Ela informa arquivos ausentes, incompletos ou não suportados e nunca os regenera.
python3 inspect_wav.py outputs/tone-440.wav
Saída esperada:
Format: signed 16-bit mono PCM
Rate: 16000 Hz
Frames: 4800
Duration: 0.300 s
Peak: 8000
Mude para Sound Studio. Atualize, carregue e reproduza tone-440.wav, depois tone-660.wav. As durações mostradas são 0,300 e 0,500 segundo. Baixe também uma das novas saídas. São resultados da sua função, separados de sample-440.wav e sample-660.wav.

A seleção e Ready to play identificam tone-660.wav; os metadados descrevem as amostras realmente salvas.
Preserve tone.c, o executável funcional tone e ambas as saídas novas. A verificação lê os WAVs, testa o executável entregue e recompila sua fonte inalterada em um diretório descartável com outros parâmetros válidos. Os três devem funcionar; não bastam a fonte, um executável antigo ou um exemplo copiado. Matemática e arredondamento inteiro equivalentes são aceitos: a amplitude ajustada pode diferir até 2% da solicitada mais 2 unidades de amostra; os picos podem exceder o pedido em até 2 unidades; e o resíduo RMS em relação a uma senoide na frequência solicitada, isto é, a raiz da média dos quadrados dos resíduos, pode ser no máximo 0,5% da amplitude mais 2 unidades. O RMS representa um erro de amostra de tamanho médio. Essa tolerância acomoda arredondamentos, não uma permissão adicional para mudar a altura pedida. A contagem exata de quadros e o formato PCM continuam obrigatórios. A fórmula fornecida atende confortavelmente aos limites.
Verificar a duração e se recuperar de uma rejeição
Nesta etapa, você reutilizará a função para uma duração curta não redonda e distinguirá uma entrada inválida de um arquivo de som defeituoso. Comece em Terminal.
Gere 101 milissegundos a 733 Hz com amplitude 7.373. Um ciclo não precisa dividir exatamente a duração do segmento; count ainda define o número de amostras.
./tone 733 101 7373 > outputs/short-tone.wav
python3 inspect_wav.py outputs/short-tone.wav
Procure Frames: 1616 e Duration: 0.101 s. Isso vem diretamente de 16000 * 101 / 1000. A última amostra ocorre pouco antes do fim do segmento; não adicione outra no ponto final. Um começo ou fim brusco pode clicar, pois este primeiro laboratório ainda não aplica fade à amplitude. O próximo laboratório guiado ensina mudanças graduais de ganho e pausas entre tons.
O próximo comando solicita intencionalmente 99 milissegundos, abaixo do mínimo. Use um nome novo e descartável. O shell abre um arquivo redirecionado antes de o programa validar argumentos, então a rejeição pode deixar um arquivo vazio; ela não garante preservar um arquivo antigo com esse nome.
./tone 440 99 10000 > outputs/rejected.wav
echo $? mostra imediatamente o código de saída nativo do comando anterior: zero indica sucesso, um valor diferente de zero indica rejeição ou falha. Não execute outro comando entre a tentativa e a leitura.
echo $?
Espere a explicação do intervalo publicado e o código 2. É uma rejeição normal de entrada, não um motivo para reiniciar o ambiente. 99 é o argumento de duração inválido. Os WAVs bons existentes são arquivos separados e continuam disponíveis.
Mude para Sound Studio, atualize, selecione e carregue rejected.wav. A página deve mostrar Cannot load sound e explicar que não é um WAV PCM completo e suportado. Reprodução e download continuam indisponíveis. Um arquivo vazio não vira um tom por terminar em .wav.
Volte a Terminal. rm -- remove o arquivo exato após --, que encerra as opções do comando. Remova apenas esta saída falha e descartável:
rm -- outputs/rejected.wav
Recupere-se usando o mínimo permitido e outro nome novo:
./tone 440 100 10000 > outputs/minimum-tone.wav
python3 inspect_wav.py outputs/minimum-tone.wav
Agora espere 1.600 quadros e 0,100 segundo. Volte a Sound Studio, atualize e reproduza minimum-tone.wav, depois carregue short-tone.wav e use Download WAV. Para testar o teclado, percorra os controles visíveis com Tab, use Enter em Play e Home, End e as setas no controle de posição. Pause antes de mudar de arquivo.
Agora há uma fonte reutilizável, um executável correspondente e saídas realmente reproduzíveis. Preserve a comparação do exemplo e seus próprios resultados. As verificações rodam em diretórios descartáveis e leem os arquivos existentes; não reparam o programa nem criam o que falta. Decodificação e reprodução no navegador não provam que um alto-falante físico esteja conectado, e este subconjunto WAV limitado não é um codificador de áudio geral.
Resumo
Você conectou o índice de tempo de um laço C a um som real. Alterou a altura de um exemplo, implementou uma amostragem senoidal reutilizável, manteve amplitudes no intervalo de 16 bits com sinal e salvou WAVs com contagens exatas de quadros. Inspecionou, reproduziu e baixou arquivos reais e se recuperou de uma rejeição intencional de parâmetro. Em seguida, poderá combinar tons com silêncio e fades graduais.



