Привет! Уже продолжительное время я успешно использую этот рекордер. И мне захотелось сделать его версию на Си. Давай сделаем.

Отличная идея! Давай создадим версию этого аудиорекордера на C. Я сохраню всю функциональность и структуру, но перепишу её в стиле C.

Вот полная версия wavrec.c:
c

/**
 * Audio recorder.
 * For GNU/Linux 64 bit version.
 * Version: 1.
 * Written on C (https://en.wikipedia.org/wiki/C_(programming_language))
 * Copyright (C) 2025  Artyomov Alexander
 * Used https://chat.deepseek.com/
 * http://self-made-free.ru/
 * aralni@mail.ru
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <alsa/asoundlib.h>

#define SAMPLE_RATE 44100       // Sampling rate
#define NUM_CHANNELS 2          // Number of channels (stereo)
#define BITS_PER_SAMPLE 16      // Bit depth
#define SILENCE_THRESHOLD 0.01  // Silence threshold
#define SILENCE_DURATION 1.0    // Silence duration to stop recording
#define INDICATOR_WIDTH 20      // Level indicator width
#define BUFFER_FRAMES 4096      // Buffer size in frames

volatile int should_stop = 0;

// Signal handler
void handle_signal(int signal) {
    switch (signal) {
        case SIGINT:
        case SIGTERM:
            printf("\nReceived termination signal. Stopping recording...\n");
            should_stop = 1;
            break;
    }
}

// Calculate RMS (Root Mean Square) of the signal
double calculate_rms(const int16_t* buffer, int num_samples) {
    double sum = 0.0;
    for (int i = 0; i < num_samples; i++) {
        double normalized = buffer[i] / 32768.0;
        sum += normalized * normalized;
    }
    return sqrt(sum / num_samples);
}

// Thread for level indicator display
typedef struct {
    int16_t* buffer;
    int buffer_size;
    double level_left;
    double level_right;
    pthread_mutex_t lock;
    int running;
    pthread_t thread;
} LevelIndicatorThread;

// Thread function for level indicator
void* level_indicator_thread_func(void* arg) {
    LevelIndicatorThread* indicator = (LevelIndicatorThread*)arg;
    
    while (indicator->running) {
        pthread_mutex_lock(&indicator->lock);
        
        double left_sum = 0.0, right_sum = 0.0;
        int left_count = 0, right_count = 0;
        
        for (int i = 0; i < indicator->buffer_size; i++) {
            double normalized = indicator->buffer[i] / 32768.0;
            if (i % 2 == 0) {
                left_sum += fabs(normalized);
                left_count++;
            } else {
                right_sum += fabs(normalized);
                right_count++;
            }
        }
        
        indicator->level_left = left_count > 0 ? left_sum / left_count : 0;
        indicator->level_right = right_count > 0 ? right_sum / right_count : 0;
        
        pthread_mutex_unlock(&indicator->lock);
        
        printf("\rSignal level: L=%.2f%% R=%.2f%%",
               indicator->level_left * 100, indicator->level_right * 100);
        fflush(stdout);
        
        usleep(100000); // Update every 100 ms
    }
    
    return NULL;
}

// Initialize level indicator thread
LevelIndicatorThread* init_level_indicator(int buffer_size) {
    LevelIndicatorThread* indicator = (LevelIndicatorThread*)malloc(sizeof(LevelIndicatorThread));
    if (!indicator) return NULL;
    
    indicator->buffer = (int16_t*)malloc(buffer_size * sizeof(int16_t));
    if (!indicator->buffer) {
        free(indicator);
        return NULL;
    }
    
    indicator->buffer_size = buffer_size;
    indicator->level_left = 0.0;
    indicator->level_right = 0.0;
    indicator->running = 1;
    pthread_mutex_init(&indicator->lock, NULL);
    
    if (pthread_create(&indicator->thread, NULL, level_indicator_thread_func, indicator) != 0) {
        free(indicator->buffer);
        free(indicator);
        return NULL;
    }
    
    return indicator;
}

// Update level indicator buffer
void update_level_indicator(LevelIndicatorThread* indicator, const int16_t* buffer, int buffer_size) {
    pthread_mutex_lock(&indicator->lock);
    memcpy(indicator->buffer, buffer, buffer_size * sizeof(int16_t));
    pthread_mutex_unlock(&indicator->lock);
}

// Stop and destroy level indicator thread
void destroy_level_indicator(LevelIndicatorThread* indicator) {
    if (indicator) {
        indicator->running = 0;
        pthread_join(indicator->thread, NULL);
        pthread_mutex_destroy(&indicator->lock);
        free(indicator->buffer);
        free(indicator);
    }
}

// Write WAV header
void write_wav_header(FILE* file, unsigned int data_size, int sample_rate, 
                      int num_channels, int bits_per_sample) {
    unsigned int chunk_size = data_size + 36;
    unsigned short audio_format = 1; // PCM
    unsigned short block_align = num_channels * bits_per_sample / 8;
    unsigned int byte_rate = sample_rate * block_align;
    
    // RIFF header
    fwrite("RIFF", 4, 1, file);
    fwrite(&chunk_size, 4, 1, file);
    fwrite("WAVE", 4, 1, file);
    
    // fmt chunk
    fwrite("fmt ", 4, 1, file);
    unsigned int fmt_size = 16;
    fwrite(&fmt_size, 4, 1, file);
    fwrite(&audio_format, 2, 1, file);
    fwrite(&num_channels, 2, 1, file);
    fwrite(&sample_rate, 4, 1, file);
    fwrite(&byte_rate, 4, 1, file);
    fwrite(&block_align, 2, 1, file);
    fwrite(&bits_per_sample, 2, 1, file);
    
    // data chunk header
    fwrite("data", 4, 1, file);
    fwrite(&data_size, 4, 1, file);
}

// Update WAV header with final data size
void update_wav_header(FILE* file, unsigned int data_size) {
    unsigned int chunk_size = data_size + 36;
    
    fseek(file, 4, SEEK_SET);
    fwrite(&chunk_size, 4, 1, file);
    
    fseek(file, 40, SEEK_SET);
    fwrite(&data_size, 4, 1, file);
}

// Main recording function
void record_wav(const char* output_file, double silence_threshold, 
                int discard_silence, int num_channels) {
    snd_pcm_t* alsa_handle = NULL;
    snd_pcm_hw_params_t* alsa_params = NULL;
    int err;
    unsigned int rate = SAMPLE_RATE;
    snd_pcm_uframes_t frames = BUFFER_FRAMES;
    int buffer_size;
    int16_t* buffer;
    FILE* file = NULL;
    LevelIndicatorThread* indicator = NULL;
    time_t silence_start_time = 0;
    int is_silent = 0;
    int paused = 0;
    unsigned int data_size = 0;
    int bytes_written;
    
    // Open ALSA device for capture
    err = snd_pcm_open(&alsa_handle, "default", SND_PCM_STREAM_CAPTURE, 0);
    if (err < 0) {
        fprintf(stderr, "Error opening ALSA device: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    // Allocate hardware parameters
    snd_pcm_hw_params_malloc(&alsa_params);
    if (!alsa_params) {
        fprintf(stderr, "Error allocating memory for ALSA parameters\n");
        goto cleanup;
    }
    
    // Initialize hardware parameters
    err = snd_pcm_hw_params_any(alsa_handle, alsa_params);
    if (err < 0) {
        fprintf(stderr, "Error initializing ALSA parameters: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    // Set parameters
    err = snd_pcm_hw_params_set_access(alsa_handle, alsa_params, 
                                        SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err < 0) {
        fprintf(stderr, "Error setting access type: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, 
                                        SND_PCM_FORMAT_S16_LE);
    if (err < 0) {
        fprintf(stderr, "Error setting format: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_channels(alsa_handle, alsa_params, num_channels);
    if (err < 0) {
        fprintf(stderr, "Error setting channels: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &rate, NULL);
    if (err < 0) {
        fprintf(stderr, "Error setting sample rate: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_buffer_size_near(alsa_handle, alsa_params, &frames);
    if (err < 0) {
        fprintf(stderr, "Error setting buffer size: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    // Apply parameters
    err = snd_pcm_hw_params(alsa_handle, alsa_params);
    if (err < 0) {
        fprintf(stderr, "Error applying parameters: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    printf("Used sample rate: %u Hz\n", rate);
    printf("Number of channels: %d\n", num_channels);
    printf("Buffer frames: %lu\n", frames);
    
    // Calculate buffer size
    buffer_size = frames * num_channels;
    buffer = (int16_t*)malloc(buffer_size * sizeof(int16_t));
    if (!buffer) {
        fprintf(stderr, "Error allocating buffer\n");
        goto cleanup;
    }
    
    // Create output file
    file = fopen(output_file, "wb");
    if (!file) {
        fprintf(stderr, "Error creating output file\n");
        goto cleanup;
    }
    
    // Write initial WAV header
    write_wav_header(file, 0, rate, num_channels, BITS_PER_SAMPLE);
    data_size = 0;
    
    // Initialize level indicator
    indicator = init_level_indicator(buffer_size);
    if (!indicator) {
        fprintf(stderr, "Warning: Could not start level indicator\n");
    }
    
    printf("Recording started. Press Ctrl+C to stop.\n");
    
    // Main recording loop
    while (!should_stop) {
        // Read data from ALSA
        err = snd_pcm_readi(alsa_handle, buffer, frames);
        if (err < 0) {
            fprintf(stderr, "Error reading from ALSA: %s\n", snd_strerror(err));
            break;
        }
        
        // Update level indicator
        if (indicator) {
            update_level_indicator(indicator, buffer, buffer_size);
        }
        
        // Check signal level
        if (discard_silence) {
            double rms = calculate_rms(buffer, buffer_size);
            is_silent = (rms < silence_threshold);
            
            if (is_silent) {
                if (silence_start_time == 0) {
                    silence_start_time = time(NULL);
                } else if (difftime(time(NULL), silence_start_time) >= SILENCE_DURATION) {
                    if (!paused) {
                        printf("\nSilence detected, pausing...\n");
                        paused = 1;
                    }
                }
            } else {
                if (paused) {
                    printf("\nSound detected, resuming recording...\n");
                    paused = 0;
                }
                silence_start_time = 0;
            }
        }
        
        // Write data to file if not paused
        if (!paused) {
            bytes_written = fwrite(buffer, sizeof(int16_t), buffer_size, file);
            data_size += bytes_written * sizeof(int16_t);
        }
    }
    
    printf("\nStopping recording...\n");
    
    // Update WAV header
    update_wav_header(file, data_size);
    
    // Truncate file if necessary
    long expected_size = data_size + 44;
    fseek(file, 0, SEEK_END);
    long actual_size = ftell(file);
    
    if (actual_size != expected_size) {
        fprintf(stderr, "Warning: File size mismatch. Truncating...\n");
        ftruncate(fileno(file), expected_size);
    }
    
cleanup:
    // Clean up resources
    if (indicator) {
        destroy_level_indicator(indicator);
    }
    if (file) {
        fclose(file);
    }
    if (buffer) {
        free(buffer);
    }
    if (alsa_params) {
        snd_pcm_hw_params_free(alsa_params);
    }
    if (alsa_handle) {
        snd_pcm_close(alsa_handle);
    }
}

// Print usage information
void print_usage(const char* program_name) {
    printf("Usage: %s <outputfile.wav> [silence_threshold] [discard_silence] [channels]\n",
           program_name);
    printf("  silence_threshold: silence threshold (default: 0.01)\n");
    printf("  discard_silence: discard silence (1 or 0, default: 1)\n");
    printf("  channels: number of channels (1 for mono, 2 for stereo, default: 2)\n");
}

int main(int argc, char* argv[]) {
    const char* output_file;
    double silence_threshold = SILENCE_THRESHOLD;
    int discard_silence = 1;
    int channels = NUM_CHANNELS;
    
    // Parse command line arguments
    if (argc < 2) {
        print_usage(argv[0]);
        return 1;
    }
    
    output_file = argv[1];
    
    if (argc >= 3) {
        silence_threshold = atof(argv[2]);
    }
    if (argc >= 4) {
        discard_silence = atoi(argv[3]);
    }
    if (argc >= 5) {
        channels = atoi(argv[4]);
        if (channels != 1 && channels != 2) {
            fprintf(stderr, "Error: channels must be 1 or 2\n");
            return 1;
        }
    }
    
    // Set up signal handlers
    signal(SIGINT, handle_signal);
    signal(SIGTERM, handle_signal);
    
    // Start recording
    record_wav(output_file, silence_threshold, discard_silence, channels);
    
    printf("Recording finished successfully.\n");
    return 0;
}

И вот Makefile для компиляции:
makefile

# Makefile for wavrec

CC = gcc
CFLAGS = -Wall -O2 -pthread
LDFLAGS = -lasound -lm -pthread
TARGET = wavrec
SOURCES = wavrec.c

all: $(TARGET)

$(TARGET): $(SOURCES)
	$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES) $(LDFLAGS)

clean:
	rm -f $(TARGET)

install:
	cp $(TARGET) /usr/local/bin/

uninstall:
	rm -f /usr/local/bin/$(TARGET)

.PHONY: all clean install uninstall

Компиляция и использование:
bash

# Компиляция
make

# Запуск (стерео, стандартные параметры)
./wavrec recording.wav

# Моно, порог тишины 0.02
./wavrec recording.wav 0.02 1 1

# Отключить пропуск тишины
./wavrec recording.wav 0.01 0

# Установка в систему
sudo make install

Основные изменения при переводе на C:

    Управление памятью - ручное выделение/освобождение

    Обработка сигналов - через стандартный signal()

    Потоки - через pthread вместо TThread

    Файловый ввод/вывод - стандартные FILE* функции

    Обработка ошибок - через коды возврата и goto cleanup

    Структуры - вместо классов и объектов

Программа сохраняет всю функциональность оригинала: запись WAV, определение тишины, индикатор уровня сигнала и корректную обработку сигналов завершения.
gcc -Wall -O2 -pthread -o wavrec wavrec.c -lasound -lm -pthread
wavrec.c: В функции «level_indicator_thread_func»:
wavrec.c:81:9: ошибка: неявная декларация функции «pthread_mutex_lock» [-Wimplicit-function-declaration]
   81 |         pthread_mutex_lock(&indicator->lock);
      |         ^~~~~~~~~~~~~~~~~~
wavrec.c:100:9: ошибка: неявная декларация функции «pthread_mutex_unlock» [-Wimplicit-function-declaration]
  100 |         pthread_mutex_unlock(&indicator->lock);
      |         ^~~~~~~~~~~~~~~~~~~~
wavrec.c: В функции «init_level_indicator»:
wavrec.c:127:5: ошибка: неявная декларация функции «pthread_mutex_init» [-Wimplicit-function-declaration]
  127 |     pthread_mutex_init(&indicator->lock, NULL);
      |     ^~~~~~~~~~~~~~~~~~
wavrec.c:129:9: ошибка: неявная декларация функции «pthread_create»; имелось в виду «timer_create»? [-Wimplicit-function-declaration]
  129 |     if (pthread_create(&indicator->thread, NULL, level_indicator_thread_func, indicator) != 0) {
      |         ^~~~~~~~~~~~~~
      |         timer_create
wavrec.c: В функции «destroy_level_indicator»:
wavrec.c:149:9: ошибка: неявная декларация функции «pthread_join»; имелось в виду «pthread_kill»? [-Wimplicit-function-declaration]
  149 |         pthread_join(indicator->thread, NULL);
      |         ^~~~~~~~~~~~
      |         pthread_kill
wavrec.c:150:9: ошибка: неявная декларация функции «pthread_mutex_destroy» [-Wimplicit-function-declaration]
  150 |         pthread_mutex_destroy(&indicator->lock);
      |         ^~~~~~~~~~~~~~~~~~~~~
make: *** [/home/first/mysoft/wavrecc/Makefile:12: wavrec] Ошибка 1

Нужно добавить заголовочный файл для pthread. Вот исправленная версия wavrec.c:
c

/**
 * Audio recorder.
 * For GNU/Linux 64 bit version.
 * Version: 1.
 * Written on C (https://en.wikipedia.org/wiki/C_(programming_language))
 * Copyright (C) 2025  Artyomov Alexander
 * Used https://chat.deepseek.com/
 * http://self-made-free.ru/
 * aralni@mail.ru
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>  // Добавлен заголовочный файл для pthread
#include <alsa/asoundlib.h>

#define SAMPLE_RATE 44100       // Sampling rate
#define NUM_CHANNELS 2          // Number of channels (stereo)
#define BITS_PER_SAMPLE 16      // Bit depth
#define SILENCE_THRESHOLD 0.01  // Silence threshold
#define SILENCE_DURATION 1.0    // Silence duration to stop recording
#define INDICATOR_WIDTH 20      // Level indicator width
#define BUFFER_FRAMES 4096      // Buffer size in frames

volatile int should_stop = 0;

// Signal handler
void handle_signal(int signal) {
    switch (signal) {
        case SIGINT:
        case SIGTERM:
            printf("\nReceived termination signal. Stopping recording...\n");
            should_stop = 1;
            break;
    }
}

// Calculate RMS (Root Mean Square) of the signal
double calculate_rms(const int16_t* buffer, int num_samples) {
    double sum = 0.0;
    for (int i = 0; i < num_samples; i++) {
        double normalized = buffer[i] / 32768.0;
        sum += normalized * normalized;
    }
    return sqrt(sum / num_samples);
}

// Thread for level indicator display
typedef struct {
    int16_t* buffer;
    int buffer_size;
    double level_left;
    double level_right;
    pthread_mutex_t lock;
    int running;
    pthread_t thread;
} LevelIndicatorThread;

// Thread function for level indicator
void* level_indicator_thread_func(void* arg) {
    LevelIndicatorThread* indicator = (LevelIndicatorThread*)arg;
    
    while (indicator->running) {
        pthread_mutex_lock(&indicator->lock);
        
        double left_sum = 0.0, right_sum = 0.0;
        int left_count = 0, right_count = 0;
        
        for (int i = 0; i < indicator->buffer_size; i++) {
            double normalized = indicator->buffer[i] / 32768.0;
            if (i % 2 == 0) {
                left_sum += fabs(normalized);
                left_count++;
            } else {
                right_sum += fabs(normalized);
                right_count++;
            }
        }
        
        indicator->level_left = left_count > 0 ? left_sum / left_count : 0;
        indicator->level_right = right_count > 0 ? right_sum / right_count : 0;
        
        pthread_mutex_unlock(&indicator->lock);
        
        printf("\rSignal level: L=%.2f%% R=%.2f%%",
               indicator->level_left * 100, indicator->level_right * 100);
        fflush(stdout);
        
        usleep(100000); // Update every 100 ms
    }
    
    return NULL;
}

// Initialize level indicator thread
LevelIndicatorThread* init_level_indicator(int buffer_size) {
    LevelIndicatorThread* indicator = (LevelIndicatorThread*)malloc(sizeof(LevelIndicatorThread));
    if (!indicator) return NULL;
    
    indicator->buffer = (int16_t*)malloc(buffer_size * sizeof(int16_t));
    if (!indicator->buffer) {
        free(indicator);
        return NULL;
    }
    
    indicator->buffer_size = buffer_size;
    indicator->level_left = 0.0;
    indicator->level_right = 0.0;
    indicator->running = 1;
    pthread_mutex_init(&indicator->lock, NULL);
    
    if (pthread_create(&indicator->thread, NULL, level_indicator_thread_func, indicator) != 0) {
        free(indicator->buffer);
        free(indicator);
        return NULL;
    }
    
    return indicator;
}

// Update level indicator buffer
void update_level_indicator(LevelIndicatorThread* indicator, const int16_t* buffer, int buffer_size) {
    pthread_mutex_lock(&indicator->lock);
    memcpy(indicator->buffer, buffer, buffer_size * sizeof(int16_t));
    pthread_mutex_unlock(&indicator->lock);
}

// Stop and destroy level indicator thread
void destroy_level_indicator(LevelIndicatorThread* indicator) {
    if (indicator) {
        indicator->running = 0;
        pthread_join(indicator->thread, NULL);
        pthread_mutex_destroy(&indicator->lock);
        free(indicator->buffer);
        free(indicator);
    }
}

// Write WAV header
void write_wav_header(FILE* file, unsigned int data_size, int sample_rate, 
                      int num_channels, int bits_per_sample) {
    unsigned int chunk_size = data_size + 36;
    unsigned short audio_format = 1; // PCM
    unsigned short block_align = num_channels * bits_per_sample / 8;
    unsigned int byte_rate = sample_rate * block_align;
    
    // RIFF header
    fwrite("RIFF", 4, 1, file);
    fwrite(&chunk_size, 4, 1, file);
    fwrite("WAVE", 4, 1, file);
    
    // fmt chunk
    fwrite("fmt ", 4, 1, file);
    unsigned int fmt_size = 16;
    fwrite(&fmt_size, 4, 1, file);
    fwrite(&audio_format, 2, 1, file);
    fwrite(&num_channels, 2, 1, file);
    fwrite(&sample_rate, 4, 1, file);
    fwrite(&byte_rate, 4, 1, file);
    fwrite(&block_align, 2, 1, file);
    fwrite(&bits_per_sample, 2, 1, file);
    
    // data chunk header
    fwrite("data", 4, 1, file);
    fwrite(&data_size, 4, 1, file);
}

// Update WAV header with final data size
void update_wav_header(FILE* file, unsigned int data_size) {
    unsigned int chunk_size = data_size + 36;
    
    fseek(file, 4, SEEK_SET);
    fwrite(&chunk_size, 4, 1, file);
    
    fseek(file, 40, SEEK_SET);
    fwrite(&data_size, 4, 1, file);
}

// Main recording function
void record_wav(const char* output_file, double silence_threshold, 
                int discard_silence, int num_channels) {
    snd_pcm_t* alsa_handle = NULL;
    snd_pcm_hw_params_t* alsa_params = NULL;
    int err;
    unsigned int rate = SAMPLE_RATE;
    snd_pcm_uframes_t frames = BUFFER_FRAMES;
    int buffer_size;
    int16_t* buffer = NULL;
    FILE* file = NULL;
    LevelIndicatorThread* indicator = NULL;
    time_t silence_start_time = 0;
    int is_silent = 0;
    int paused = 0;
    unsigned int data_size = 0;
    int bytes_written;
    
    // Open ALSA device for capture
    err = snd_pcm_open(&alsa_handle, "default", SND_PCM_STREAM_CAPTURE, 0);
    if (err < 0) {
        fprintf(stderr, "Error opening ALSA device: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    // Allocate hardware parameters
    snd_pcm_hw_params_malloc(&alsa_params);
    if (!alsa_params) {
        fprintf(stderr, "Error allocating memory for ALSA parameters\n");
        goto cleanup;
    }
    
    // Initialize hardware parameters
    err = snd_pcm_hw_params_any(alsa_handle, alsa_params);
    if (err < 0) {
        fprintf(stderr, "Error initializing ALSA parameters: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    // Set parameters
    err = snd_pcm_hw_params_set_access(alsa_handle, alsa_params, 
                                        SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err < 0) {
        fprintf(stderr, "Error setting access type: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, 
                                        SND_PCM_FORMAT_S16_LE);
    if (err < 0) {
        fprintf(stderr, "Error setting format: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_channels(alsa_handle, alsa_params, num_channels);
    if (err < 0) {
        fprintf(stderr, "Error setting channels: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &rate, NULL);
    if (err < 0) {
        fprintf(stderr, "Error setting sample rate: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_buffer_size_near(alsa_handle, alsa_params, &frames);
    if (err < 0) {
        fprintf(stderr, "Error setting buffer size: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    // Apply parameters
    err = snd_pcm_hw_params(alsa_handle, alsa_params);
    if (err < 0) {
        fprintf(stderr, "Error applying parameters: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    printf("Used sample rate: %u Hz\n", rate);
    printf("Number of channels: %d\n", num_channels);
    printf("Buffer frames: %lu\n", frames);
    
    // Calculate buffer size
    buffer_size = frames * num_channels;
    buffer = (int16_t*)malloc(buffer_size * sizeof(int16_t));
    if (!buffer) {
        fprintf(stderr, "Error allocating buffer\n");
        goto cleanup;
    }
    
    // Create output file
    file = fopen(output_file, "wb");
    if (!file) {
        fprintf(stderr, "Error creating output file\n");
        goto cleanup;
    }
    
    // Write initial WAV header
    write_wav_header(file, 0, rate, num_channels, BITS_PER_SAMPLE);
    data_size = 0;
    
    // Initialize level indicator
    indicator = init_level_indicator(buffer_size);
    if (!indicator) {
        fprintf(stderr, "Warning: Could not start level indicator\n");
    }
    
    printf("Recording started. Press Ctrl+C to stop.\n");
    
    // Main recording loop
    while (!should_stop) {
        // Read data from ALSA
        err = snd_pcm_readi(alsa_handle, buffer, frames);
        if (err < 0) {
            fprintf(stderr, "Error reading from ALSA: %s\n", snd_strerror(err));
            break;
        }
        
        // Update level indicator
        if (indicator) {
            update_level_indicator(indicator, buffer, buffer_size);
        }
        
        // Check signal level
        if (discard_silence) {
            double rms = calculate_rms(buffer, buffer_size);
            is_silent = (rms < silence_threshold);
            
            if (is_silent) {
                if (silence_start_time == 0) {
                    silence_start_time = time(NULL);
                } else if (difftime(time(NULL), silence_start_time) >= SILENCE_DURATION) {
                    if (!paused) {
                        printf("\nSilence detected, pausing...\n");
                        paused = 1;
                    }
                }
            } else {
                if (paused) {
                    printf("\nSound detected, resuming recording...\n");
                    paused = 0;
                }
                silence_start_time = 0;
            }
        }
        
        // Write data to file if not paused
        if (!paused) {
            bytes_written = fwrite(buffer, sizeof(int16_t), buffer_size, file);
            data_size += bytes_written * sizeof(int16_t);
        }
    }
    
    printf("\nStopping recording...\n");
    
    // Update WAV header
    update_wav_header(file, data_size);
    
    // Truncate file if necessary
    long expected_size = data_size + 44;
    fseek(file, 0, SEEK_END);
    long actual_size = ftell(file);
    
    if (actual_size != expected_size) {
        fprintf(stderr, "Warning: File size mismatch. Truncating...\n");
        ftruncate(fileno(file), expected_size);
    }
    
cleanup:
    // Clean up resources
    if (indicator) {
        destroy_level_indicator(indicator);
    }
    if (file) {
        fclose(file);
    }
    if (buffer) {
        free(buffer);
    }
    if (alsa_params) {
        snd_pcm_hw_params_free(alsa_params);
    }
    if (alsa_handle) {
        snd_pcm_close(alsa_handle);
    }
}

// Print usage information
void print_usage(const char* program_name) {
    printf("Usage: %s <outputfile.wav> [silence_threshold] [discard_silence] [channels]\n",
           program_name);
    printf("  silence_threshold: silence threshold (default: 0.01)\n");
    printf("  discard_silence: discard silence (1 or 0, default: 1)\n");
    printf("  channels: number of channels (1 for mono, 2 for stereo, default: 2)\n");
}

int main(int argc, char* argv[]) {
    const char* output_file;
    double silence_threshold = SILENCE_THRESHOLD;
    int discard_silence = 1;
    int channels = NUM_CHANNELS;
    
    // Parse command line arguments
    if (argc < 2) {
        print_usage(argv[0]);
        return 1;
    }
    
    output_file = argv[1];
    
    if (argc >= 3) {
        silence_threshold = atof(argv[2]);
    }
    if (argc >= 4) {
        discard_silence = atoi(argv[3]);
    }
    if (argc >= 5) {
        channels = atoi(argv[4]);
        if (channels != 1 && channels != 2) {
            fprintf(stderr, "Error: channels must be 1 or 2\n");
            return 1;
        }
    }
    
    // Set up signal handlers
    signal(SIGINT, handle_signal);
    signal(SIGTERM, handle_signal);
    
    // Start recording
    record_wav(output_file, silence_threshold, discard_silence, channels);
    
    printf("Recording finished successfully.\n");
    return 0;
}

Основное изменение: добавлена строка #include <pthread.h> в начале файла. Это решит все ошибки, связанные с pthread функциями.

Теперь компиляция должна пройти успешно:
bash

gcc -Wall -O2 -pthread -o wavrec wavrec.c -lasound -lm

Или используя Makefile:
bash

make clean
make

wavnorm.pas
PAS 4.7KB
Спасибо! Теперь всё работает замечательно. Давай так же переведём другую утилиту wavnorm на Си.

Отлично! Давай переведём нормализатор громкости wavnorm на C. Вот полная версия:
c

/**
 * Volume normalizer.
 * For GNU/Linux 64 bit version.
 * Version: 1.
 * Written on C (https://en.wikipedia.org/wiki/C_(programming_language))
 * Copyright (C) 2025  Artyomov Alexander
 * Used https://chat.deepseek.com/
 * http://self-made-free.ru/
 * aralni@mail.ru
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>

// WAV file header structure (PCM format)
#pragma pack(push, 1)
typedef struct {
    char     riff_id[4];        // "RIFF"
    uint32_t riff_size;         // File size - 8
    char     wave_id[4];        // "WAVE"
    char     fmt_id[4];         // "fmt "
    uint32_t fmt_size;          // Size of fmt chunk (16 for PCM)
    uint16_t audio_format;      // Audio format (1 for PCM)
    uint16_t num_channels;      // Number of channels
    uint32_t sample_rate;       // Sample rate
    uint32_t byte_rate;         // Sample rate * block_align
    uint16_t block_align;       // Num channels * bits_per_sample / 8
    uint16_t bits_per_sample;   // Bits per sample
    char     data_id[4];        // "data"
    uint32_t data_size;         // Size of data chunk
} WAVHeader;
#pragma pack(pop)

// Function to read WAV header
int read_wav_header(FILE* file, WAVHeader* header) {
    fseek(file, 0, SEEK_SET);
    size_t bytes_read = fread(header, sizeof(WAVHeader), 1, file);
    
    if (bytes_read != 1) {
        fprintf(stderr, "Error: Cannot read WAV header\n");
        return 0;
    }
    
    // Verify WAV file signature
    if (memcmp(header->riff_id, "RIFF", 4) != 0 ||
        memcmp(header->wave_id, "WAVE", 4) != 0 ||
        memcmp(header->fmt_id, "fmt ", 4) != 0 ||
        memcmp(header->data_id, "data", 4) != 0) {
        fprintf(stderr, "Error: Invalid WAV file format\n");
        return 0;
    }
    
    // Check if PCM format
    if (header->audio_format != 1) {
        fprintf(stderr, "Error: Only PCM format is supported (format: %d)\n", 
                header->audio_format);
        return 0;
    }
    
    // Check if 16-bit
    if (header->bits_per_sample != 16) {
        fprintf(stderr, "Error: Only 16-bit audio is supported (bits: %d)\n",
                header->bits_per_sample);
        return 0;
    }
    
    return 1;
}

// Function to write WAV header
void write_wav_header(FILE* file, const WAVHeader* header) {
    fseek(file, 0, SEEK_SET);
    fwrite(header, sizeof(WAVHeader), 1, file);
}

// Function to read audio data from WAV file
int16_t* read_audio_data(FILE* file, const WAVHeader* header, int* num_samples) {
    int16_t* data;
    int total_samples;
    
    // Calculate number of samples
    total_samples = header->data_size / (header->num_channels * (header->bits_per_sample / 8));
    *num_samples = total_samples;
    
    // Allocate memory for audio data
    data = (int16_t*)malloc(header->data_size);
    if (!data) {
        fprintf(stderr, "Error: Cannot allocate memory for audio data\n");
        return NULL;
    }
    
    // Read audio data
    fseek(file, sizeof(WAVHeader), SEEK_SET);
    size_t bytes_read = fread(data, 1, header->data_size, file);
    
    if (bytes_read != header->data_size) {
        fprintf(stderr, "Error: Cannot read audio data (read %zu of %u bytes)\n",
                bytes_read, header->data_size);
        free(data);
        return NULL;
    }
    
    return data;
}

// Function to write audio data to WAV file
int write_audio_data(FILE* file, const int16_t* data, const WAVHeader* header) {
    fseek(file, sizeof(WAVHeader), SEEK_SET);
    size_t bytes_written = fwrite(data, 1, header->data_size, file);
    
    if (bytes_written != header->data_size) {
        fprintf(stderr, "Error: Cannot write audio data (wrote %zu of %u bytes)\n",
                bytes_written, header->data_size);
        return 0;
    }
    
    return 1;
}

// Function to find maximum amplitude
double find_max_amplitude(const int16_t* data, int num_samples, int num_channels) {
    double max_amplitude = 0.0;
    
    for (int i = 0; i < num_samples; i++) {
        for (int ch = 0; ch < num_channels; ch++) {
            int idx = i * num_channels + ch;
            double amplitude = fabs(data[idx] / 32768.0);
            if (amplitude > max_amplitude) {
                max_amplitude = amplitude;
            }
        }
    }
    
    return max_amplitude;
}

// Function to normalize audio data
void normalize_audio_data(int16_t* data, int num_samples, int num_channels, double scale_factor) {
    for (int i = 0; i < num_samples; i++) {
        for (int ch = 0; ch < num_channels; ch++) {
            int idx = i * num_channels + ch;
            double normalized = data[idx] * scale_factor;
            
            // Clip to prevent overflow
            if (normalized > 32767.0) {
                data[idx] = 32767;
            } else if (normalized < -32768.0) {
                data[idx] = -32768;
            } else {
                data[idx] = (int16_t)normalized;
            }
        }
    }
}

// Main normalization function
int normalize_wav(const char* input_file, const char* output_file) {
    FILE* infile = NULL;
    FILE* outfile = NULL;
    WAVHeader header;
    int16_t* audio_data = NULL;
    int num_samples = 0;
    double max_amplitude;
    double scale_factor;
    int result = 0;
    
    // Open input file
    infile = fopen(input_file, "rb");
    if (!infile) {
        fprintf(stderr, "Error: Cannot open input file '%s'\n", input_file);
        return 0;
    }
    
    // Check if file is empty
    fseek(infile, 0, SEEK_END);
    long file_size = ftell(infile);
    if (file_size == 0) {
        fprintf(stderr, "Error: File '%s' is empty\n", input_file);
        fclose(infile);
        return 0;
    }
    fseek(infile, 0, SEEK_SET);
    
    // Read WAV header
    if (!read_wav_header(infile, &header)) {
        fclose(infile);
        return 0;
    }
    
    // Display file information
    printf("Loading WAV file: %s\n", input_file);
    printf("Sample rate: %u Hz\n", header.sample_rate);
    printf("Number of channels: %u\n", header.num_channels);
    printf("Data chunk size: %u bytes\n", header.data_size);
    printf("Bits per sample: %u\n", header.bits_per_sample);
    
    // Check if there is data
    if (header.data_size == 0) {
        fprintf(stderr, "Error: File contains no data or has invalid structure\n");
        fclose(infile);
        return 0;
    }
    
    // Read audio data
    audio_data = read_audio_data(infile, &header, &num_samples);
    if (!audio_data) {
        fclose(infile);
        return 0;
    }
    fclose(infile);
    infile = NULL;
    
    // Find maximum amplitude
    max_amplitude = find_max_amplitude(audio_data, num_samples, header.num_channels);
    
    // Calculate scale factor
    if (max_amplitude > 0) {
        scale_factor = 1.0 / max_amplitude;
    } else {
        scale_factor = 1.0;
    }
    
    printf("Maximum amplitude: %.4f\n", max_amplitude);
    printf("Scale factor: %.4f\n", scale_factor);
    
    // Normalize audio data
    normalize_audio_data(audio_data, num_samples, header.num_channels, scale_factor);
    
    // Open output file
    outfile = fopen(output_file, "wb");
    if (!outfile) {
        fprintf(stderr, "Error: Cannot create output file '%s'\n", output_file);
        free(audio_data);
        return 0;
    }
    
    // Write WAV header
    write_wav_header(outfile, &header);
    
    // Write normalized audio data
    if (!write_audio_data(outfile, audio_data, &header)) {
        fclose(outfile);
        free(audio_data);
        return 0;
    }
    
    printf("Saved normalized WAV file: %s\n", output_file);
    printf("Normalization completed successfully.\n");
    
    result = 1;
    
    // Cleanup
    if (outfile) fclose(outfile);
    if (audio_data) free(audio_data);
    
    return result;
}

// Print usage information
void print_usage(const char* program_name) {
    printf("Usage: %s <input.wav> <output.wav>\n", program_name);
    printf("  Normalizes the volume of a WAV file to maximum amplitude.\n");
    printf("  Supports 16-bit PCM WAV files only.\n");
}

int main(int argc, char* argv[]) {
    const char* input_file;
    const char* output_file;
    
    // Parse command line arguments
    if (argc < 3) {
        print_usage(argv[0]);
        return 1;
    }
    
    input_file = argv[1];
    output_file = argv[2];
    
    // Normalize WAV file
    if (!normalize_wav(input_file, output_file)) {
        fprintf(stderr, "Normalization failed.\n");
        return 1;
    }
    
    return 0;
}

И Makefile для компиляции:
makefile

# Makefile for wavnorm

CC = gcc
CFLAGS = -Wall -O2 -lm
TARGET = wavnorm
SOURCES = wavnorm.c

all: $(TARGET)

$(TARGET): $(SOURCES)
	$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES) -lm

clean:
	rm -f $(TARGET)

install:
	cp $(TARGET) /usr/local/bin/

uninstall:
	rm -f /usr/local/bin/$(TARGET)

.PHONY: all clean install uninstall

Компиляция и использование:
bash

# Компиляция
make

# Нормализация WAV файла
./wavnorm input.wav output.wav

# Или с полными путями
./wavnorm /path/to/input.wav /path/to/output.wav

# Установка в систему
sudo make install

Особенности реализации на C:

    Структура WAVHeader - упакованная структура для прямого чтения/записи заголовка

    Обработка данных - 16-битные сэмплы хранятся в формате interleaved (L,R,L,R...)

    Нормализация - вычисляется максимальная амплитуда и применяется коэффициент масштабирования

    Клиппинг - предотвращает переполнение при масштабировании

    Обработка ошибок - проверка всех операций ввода/вывода и выделения памяти

Программа поддерживает только 16-битные PCM WAV файлы, как и оригинальная Pascal версия. Нормализация выполняется с сохранением всех метаданных исходного файла.