Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
libogg-dev \
libpipewire-0.3-dev \
libsndfile1-dev \
libopusenc-dev \
libvorbis-dev \
portaudio19-dev
- uses: actions/checkout@v5
Expand All @@ -49,6 +50,7 @@ jobs:
${{ matrix.audio-backend }}
-DENABLE_MP3LAME=ON
-DENABLE_SNDFILE=ON
-DENABLE_OPUS=ON
-DENABLE_VORBIS=ON
- name: Build
working-directory: ${{ github.workspace }}/build
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/code-scanning.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ jobs:
libogg-dev \
libpipewire-0.3-dev \
libsndfile1-dev \
libopusenc-dev \
libvorbis-dev \
portaudio19-dev
- uses: actions/checkout@v5
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: cpp
queries: security-and-quality
Expand All @@ -56,9 +57,10 @@ jobs:
-DENABLE_PORTAUDIO=ON
-DENABLE_MP3LAME=ON
-DENABLE_SNDFILE=ON
-DENABLE_OPUS=ON
-DENABLE_VORBIS=ON
- name: Build
working-directory: ${{ github.workspace }}/build
run: cmake --build . --config ${{ matrix.build-type }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
17 changes: 12 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
cmake_minimum_required(VERSION 3.11)

project(svar
VERSION 2.0.0
VERSION 2.1.0
DESCRIPTION "Simple Voice Activated Recorder"
LANGUAGES C)

Expand All @@ -27,7 +27,8 @@ endif()

option(ENABLE_MP3LAME "Enable MP3 support.")
option(ENABLE_SNDFILE "Enable WAV support.")
option(ENABLE_VORBIS "Enable OGG support.")
option(ENABLE_OPUS "Enable OPUS support.")
option(ENABLE_VORBIS "Enable VORBIS support.")

configure_file(
${PROJECT_SOURCE_DIR}/config.h.in
Expand Down Expand Up @@ -77,10 +78,16 @@ if(ENABLE_MP3LAME)
target_link_libraries(svarcore Mp3Lame::Mp3Lame)
endif()

if(ENABLE_OPUS)
pkg_check_modules(OggOpus REQUIRED IMPORTED_TARGET libopusenc)
target_sources(svarcore PRIVATE src/writer-opus.c)
target_link_libraries(svarcore PkgConfig::OggOpus)
endif()

if(ENABLE_VORBIS)
pkg_check_modules(VorbisOgg REQUIRED IMPORTED_TARGET vorbis vorbisenc ogg)
target_sources(svarcore PRIVATE src/writer-ogg.c)
target_link_libraries(svarcore PkgConfig::VorbisOgg)
pkg_check_modules(OggVorbis REQUIRED IMPORTED_TARGET vorbis vorbisenc ogg)
target_sources(svarcore PRIVATE src/writer-vorbis.c)
target_link_libraries(svarcore PkgConfig::OggVorbis)
endif()

if(BUILD_TESTING)
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Supported output formats:
- RAW (PCM 16bit interleaved)
- WAV, RF64 ([libsndfile](http://www.mega-nerd.com/libsndfile/))
- MP3 ([mp3lame](http://lame.sourceforge.net/))
- OGG ([libvorbis](http://www.xiph.org/vorbis/))
- OGG/OPUS ([libopusenc](https://opus-codec.org/))
- OGG/VORBIS ([libvorbis](http://www.xiph.org/vorbis/))

For low CPU consumption WAV is recommended - it is the default selection.

Expand All @@ -39,6 +40,6 @@ from the last 100 ms of captured audio.
mkdir build && cd build
cmake .. \
-DENABLE_ALSA=ON -DENABLE_PIPEWIRE=ON -DENABLE_PORTAUDIO=ON \
-DENABLE_SNDFILE=ON -DENABLE_MP3LAME=ON -DENABLE_VORBIS=ON
-DENABLE_SNDFILE=ON -DENABLE_MP3LAME=ON -DENABLE_OPUS=ON -DENABLE_VORBIS=ON
make && make install
```
3 changes: 3 additions & 0 deletions config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
/* Define to 1 if SNDFile is enabled. */
#cmakedefine ENABLE_SNDFILE 1

/* Define to 1 if Ogg Opus is enabled. */
#cmakedefine ENABLE_OPUS 1

/* Define to 1 if Ogg Vorbis is enabled. */
#cmakedefine ENABLE_VORBIS 1

Expand Down
31 changes: 25 additions & 6 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@
#if ENABLE_SNDFILE
# include "writer-wav.h"
#endif
#if ENABLE_OPUS
# include "writer-opus.h"
#endif
#if ENABLE_VORBIS
# include "writer-ogg.h"
# include "writer-vorbis.h"
#endif

/* Application banner used for output file comment string. */
Expand Down Expand Up @@ -97,8 +100,13 @@ static void print_audio_info(void) {
printf("Output bit rate [kbit/s]: min=%d max=%d\n",
bitrate_min / 1000, bitrate_max / 1000);
#endif
#if ENABLE_OPUS
if (writer->type == WRITER_TYPE_OPUS)
printf("Output bit rate [kbit/s]: %d\n",
bitrate_nom / 1000);
#endif
#if ENABLE_VORBIS
if (writer->type == WRITER_TYPE_OGG)
if (writer->type == WRITER_TYPE_VORBIS)
printf("Output bit rate [kbit/s]: min=%d nominal=%d max=%d\n",
bitrate_min / 1000, bitrate_nom / 1000, bitrate_max / 1000);
#endif
Expand Down Expand Up @@ -142,8 +150,10 @@ int main(int argc, char *argv[]) {
/* Select default output format based on available libraries. */
#if ENABLE_SNDFILE
enum writer_type writer_type = WRITER_TYPE_WAV;
#elif ENABLE_OPUS
enum writer_type writer_type = WRITER_TYPE_OPUS;
#elif ENABLE_VORBIS
enum writer_type writer_type = WRITER_TYPE_OGG;
enum writer_type writer_type = WRITER_TYPE_VORBIS;
#elif ENABLE_MP3LAME
enum writer_type writer_type = WRITER_TYPE_MP3;
#else
Expand Down Expand Up @@ -250,8 +260,11 @@ int main(int argc, char *argv[]) {
WRITER_TYPE_WAV,
WRITER_TYPE_RF64,
#endif
#if ENABLE_OPUS
WRITER_TYPE_OPUS,
#endif
#if ENABLE_VORBIS
WRITER_TYPE_OGG,
WRITER_TYPE_VORBIS,
#endif
};

Expand Down Expand Up @@ -379,9 +392,15 @@ int main(int argc, char *argv[]) {
writer_mp3_print_internals(writer);
break;
#endif
#if ENABLE_OPUS
case WRITER_TYPE_OPUS:
writer = writer_opus_new(pcm_format, pcm_channels, pcm_rate,
bitrate_nom, banner);
break;
#endif
#if ENABLE_VORBIS
case WRITER_TYPE_OGG:
writer = writer_ogg_new(pcm_format, pcm_channels, pcm_rate,
case WRITER_TYPE_VORBIS:
writer = writer_vorbis_new(pcm_format, pcm_channels, pcm_rate,
bitrate_min, bitrate_nom, bitrate_max, banner);
break;
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/writer-mp3.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ struct writer * writer_mp3_new(
int bitrate_min, int bitrate_max, const char * comment) {

if (format != PCM_FORMAT_S16LE) {
error("MP3 unsupported PCM format: %s", pcm_format_name(format));
error("LAME: Unsupported PCM format: %s", pcm_format_name(format));
return NULL;
}

Expand Down
127 changes: 127 additions & 0 deletions src/writer-opus.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* SVAR - writer-opus.c
* SPDX-FileCopyrightText: 2025 Arkadiusz Bokowy and contributors
* SPDX-License-Identifier: MIT
*/

#include "writer-opus.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <opusenc.h>

#include "log.h"
#include "pcm.h"
#include "writer.h"

struct writer_opus {
OggOpusEnc * enc;
OggOpusComments * comments;
unsigned int channels;
unsigned int sampling;
int bitrate;
};

static int writer_opus_open(struct writer * writer, const char * pathname) {
struct writer_opus * w = writer->w;

writer->close(writer);

int err;
const int family = w->channels <= 2 ? 0 : 1;
if ((w->enc = ope_encoder_create_file(pathname, w->comments, w->sampling,
w->channels, family, &err)) == NULL) {
error("OPUS: Couldn't create encoder: %s", ope_strerror(err));
errno = EINVAL;
return -1;
}

if ((err = ope_encoder_ctl(w->enc, OPUS_SET_BITRATE(w->bitrate))) != OPE_OK)
warn("OPUS: Couldn't set bitrate: %s", ope_strerror(err));

writer->opened = true;
return 0;
}

static void writer_opus_close(struct writer * writer) {
struct writer_opus * w = writer->w;
writer->opened = false;

if (w->enc == NULL)
return;

ope_encoder_drain(w->enc);
ope_encoder_destroy(w->enc);
w->enc = NULL;
}

static ssize_t writer_opus_write(struct writer * writer, const void * buffer, size_t frames) {
struct writer_opus * w = writer->w;

int err;
/* Write 16-bit PCM samples to the encoder. */
if ((err = ope_encoder_write(w->enc, (const opus_int16 *)buffer, frames)) != OPE_OK) {
error("OPUS: Encoding error: %s", ope_strerror(err));
return -1;
}

return frames;
}

static void writer_opus_free(struct writer * writer) {
if (writer == NULL)
return;
struct writer_opus * w = writer->w;
writer->close(writer);
if (w->comments != NULL)
ope_comments_destroy(w->comments);
free(writer->w);
free(writer);
}

struct writer * writer_opus_new(
enum pcm_format format, unsigned int channels, unsigned int sampling,
int bitrate, const char * comment) {

if (format != PCM_FORMAT_S16LE) {
error("OPUS: Unsupported PCM format: %s", pcm_format_name(format));
return NULL;
}

struct writer * writer;
if ((writer = malloc(sizeof(*writer))) == NULL)
return NULL;

writer->type = WRITER_TYPE_OPUS;
writer->opened = false;
writer->open = writer_opus_open;
writer->write = writer_opus_write;
writer->close = writer_opus_close;
writer->free = writer_opus_free;

struct writer_opus * w;
if ((writer->w = w = calloc(1, sizeof(*w))) == NULL) {
free(writer);
return NULL;
}

if ((w->comments = ope_comments_create()) == NULL) {
writer_opus_free(writer);
return NULL;
}

w->channels = channels;
w->sampling = sampling;
w->bitrate = bitrate;

if (comment != NULL) {
char tag[8 + strlen(comment) + 1];
snprintf(tag, sizeof(tag), "ENCODER=%s", comment);
ope_comments_add_string(w->comments, tag);
}

return writer;
}
18 changes: 18 additions & 0 deletions src/writer-opus.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* SVAR - writer-opus.h
* SPDX-FileCopyrightText: 2025 Arkadiusz Bokowy and contributors
* SPDX-License-Identifier: MIT
*/

#pragma once
#ifndef SVAR_WRITER_OPUS_H_
#define SVAR_WRITER_OPUS_H_

#include "pcm.h"
#include "writer.h"

struct writer * writer_opus_new(
enum pcm_format format, unsigned int channels, unsigned int sampling,
int bitrate, const char * comment);

#endif
Loading