Skip to content
Closed
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
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on:
push:
branches: [ main, feat/demo-wav ]
pull_request:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install build deps
run: |
sudo apt-get update
sudo apt-get install -y git autoconf automake libtool gcc make pkg-config
- name: Prepare build
run: |
./autogen.sh
./configure
- name: Build
run: |
make -j2
- name: Run tests
run: |
make check -j2
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ set(Oac_PUBLIC_HEADER
${CMAKE_CURRENT_SOURCE_DIR}/include/oac_defines.h
${CMAKE_CURRENT_SOURCE_DIR}/include/oac_multistream.h
${CMAKE_CURRENT_SOURCE_DIR}/include/oac_projection.h
${CMAKE_CURRENT_SOURCE_DIR}/include/oac_types.h)
${CMAKE_CURRENT_SOURCE_DIR}/include/oac_types.h
${CMAKE_CURRENT_SOURCE_DIR}/include/opus_types.h)

if(OAC_CUSTOM_MODES)
list(APPEND Oac_PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/include/oac_custom.h)
Expand Down
35 changes: 35 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Contributing
============

Thank you for contributing to OAC! This document explains how to run the test
suite locally and what to expect from CI when opening a pull request.

Running tests locally
---------------------

After building the project (see `README`), run the integrated tests:

% make check

On recent systems the test suite should complete quickly and report the
number of passing tests. For example:

Testsuite summary for oac
# TOTAL: 17
# PASS: 17

Continuous Integration
----------------------

The repository includes GitHub Actions workflows that build the project and
run `make -f Makefile.unix check` on push and pull requests. Please make
sure tests pass locally before opening a PR; the CI will run the test
suite and must succeed before merging.

Tips
----

- If a test fails locally, run `make clean` and rebuild before investigating.
- Some tests require a POSIX-like environment (e.g., Linux or macOS).

If you have questions, open an issue or discuss on the PR.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a separate change and does not belong to this PR

19 changes: 17 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ if OAC_ARM_EXTERNAL_ASM
liboac_la_LIBADD += libarmasm.la
endif

pkginclude_HEADERS = include/oac.h include/oac_multistream.h include/oac_types.h include/oac_defines.h include/oac_projection.h
pkginclude_HEADERS = include/oac.h include/oac_multistream.h include/oac_types.h include/opus_types.h include/oac_defines.h include/oac_projection.h

noinst_HEADERS = $(OAC_HEAD) $(SILK_HEAD) $(CELT_HEAD) $(LPCNET_HEAD)

Expand All @@ -174,6 +174,8 @@ noinst_PROGRAMS = celt/tests/test_unit_cwrs32 \
tests/test_oac_extensions \
tests/test_oac_padding \
tests/test_oac_projection \
tests/test_oac_demo_wav \
tests/test_oac_demo_input_wav \
trivial_example

TESTS = celt/tests/test_unit_cwrs32 \
Expand All @@ -191,7 +193,8 @@ TESTS = celt/tests/test_unit_cwrs32 \
tests/test_oac_encode \
tests/test_oac_extensions \
tests/test_oac_padding \
tests/test_oac_projection
tests/test_oac_projection \
tests/test_oac_demo_input_wav

oac_demo_SOURCES = src/oac_demo.c
if ENABLE_LOSSGEN
Expand Down Expand Up @@ -247,6 +250,18 @@ if OAC_ARM_EXTERNAL_ASM
tests_test_oac_projection_LDADD += libarmasm.la
endif

# Test for oac_demo WAV output
TESTS += \
tests/test_oac_demo_wav \
tests/test_oac_demo_encode_only

tests_test_oac_demo_wav_SOURCES = tests/test_oac_demo_wav.c
tests_test_oac_demo_wav_LDADD = liboac.la $(NE10_LIBS) $(LIBM)

# Test for oac_demo encode-only + decode-to-wav
tests_test_oac_demo_encode_only_SOURCES = tests/test_oac_demo_encode_only.c
tests_test_oac_demo_encode_only_LDADD = liboac.la $(NE10_LIBS) $(LIBM)

silk_tests_test_unit_LPC_inv_pred_gain_SOURCES = silk/tests/test_unit_LPC_inv_pred_gain.c
silk_tests_test_unit_LPC_inv_pred_gain_LDADD = $(SILK_OBJ) $(LPCNET_OBJ) $(CELT_OBJ) $(NE10_LIBS) $(LIBM)
if OAC_ARM_EXTERNAL_ASM
Expand Down
20 changes: 19 additions & 1 deletion Makefile.unix
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,25 @@ TESTOACPADDING_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOACPADDING_SRCS_C))
OACCOMPARE_SRCS_C = src/oac_compare.c
OACCOMPARE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OACCOMPARE_SRCS_C))

TESTS := test_oac_api test_oac_decode test_oac_encode test_oac_extensions test_oac_padding
TESTS := test_oac_api test_oac_decode test_oac_encode test_oac_extensions test_oac_padding test_oac_demo_wav test_oac_demo_input_wav test_oac_demo_encode_only

TESTOACDEMOWAV_SRCS_C = tests/test_oac_demo_wav.c
TESTOACDEMOWAV_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOACDEMOWAV_SRCS_C))

TESTOACDEMOINPUTWAV_SRCS_C = tests/test_oac_demo_input_wav.c
TESTOACDEMOINPUTWAV_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOACDEMOINPUTWAV_SRCS_C))

TESTOACDEMOENCODEONLY_SRCS_C = tests/test_oac_demo_encode_only.c
TESTOACDEMOENCODEONLY_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOACDEMOENCODEONLY_SRCS_C))

test_oac_demo_wav$(EXESUFFIX): $(TESTOACDEMOWAV_OBJS) $(TARGET)
$(LINK.o.cmdline)

test_oac_demo_input_wav$(EXESUFFIX): $(TESTOACDEMOINPUTWAV_OBJS) $(TARGET)
$(LINK.o.cmdline)

test_oac_demo_encode_only$(EXESUFFIX): $(TESTOACDEMOENCODEONLY_OBJS) $(TARGET)
$(LINK.o.cmdline)

# Rules
all: lib oac_demo oac_compare $(TESTS)
Expand Down
7 changes: 6 additions & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,15 @@ options:
-inbandfec : enable SILK inband FEC
-forcemono : force mono encoding, even for stereo input
-dtx : enable SILK DTX
-wav : write output as WAV file (with header) when decoding
-wav_in : read input as WAV file and use header (sample rate, channels, format)
-loss <perc> : simulate packet loss, in percent (0-100); default: 0

input and output are little-endian signed 16-bit PCM files or OAC
bitstreams with simple oac_demo custom framing.
bitstreams with simple oac_demo custom framing. Use -wav to produce a
standard WAV file (with header) for decoded PCM output, and use -wav_in
when encoding to read standard WAV files as input (the WAV header will
be used to determine sample rate, channels and sample format).

== Testing ==

Expand Down
9 changes: 9 additions & 0 deletions include/opus_types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* Compatibility shim: some generated DNN headers expect "opus_types.h".
Include the canonical project header and avoid duplicating definitions. */

#ifndef OPUS_TYPES_H
#define OPUS_TYPES_H

#include "oac_types.h"

#endif /* OPUS_TYPES_H */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes (opus_types.h) should no longer be needed once PR1 is merged.

151 changes: 144 additions & 7 deletions src/oac_demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdint.h>
#include "oac.h"
#include "debug.h"
#include "oac_types.h"
Expand Down Expand Up @@ -140,6 +141,7 @@ void print_usage( char* argv[] ) {
fprintf(stderr, "-inbandfec : enable SILK inband FEC\n" );
fprintf(stderr, "-forcemono : force mono encoding, even for stereo input\n" );
fprintf(stderr, "-dtx : enable SILK DTX\n" );
fprintf(stderr, "-wav : write output as WAV file (with header)\n" );
fprintf(stderr,
"-loss <perc> : optimize for loss percentage and simulate packet loss, in percent (0-100); default: 0\n" );
#ifdef ENABLE_LOSSGEN
Expand Down Expand Up @@ -425,6 +427,11 @@ int main(int argc, char *argv[]) {
int k;
oac_int32 skip = 0;
int format = FORMAT_S16_LE;
int output_wav = 0;
oac_uint64 wav_data_bytes = 0;
int input_wav = 0;
uint32_t input_wav_data_size = 0;
long input_wav_data_start = 0;
int stop = 0;
oac_int32 *in = NULL;
oac_int32 *out = NULL;
Expand Down Expand Up @@ -729,6 +736,14 @@ int main(int argc, char *argv[]) {
check_decoder_option(encode_only, "-ignore_extensions");
ignore_extensions = 1;
args++;
} else if (strcmp( argv[ args ], "-wav_in" ) == 0) {
check_encoder_option(decode_only, "-wav_in");
input_wav = 1;
args++;
} else if (strcmp( argv[ args ], "-wav" ) == 0) {
check_decoder_option(encode_only, "-wav");
output_wav = 1;
args++;
#ifdef ENABLE_OSCE_TRAINING_DATA
} else if (strcmp( argv[ args ], "-silk_random_switching" ) == 0) {
silk_random_switching = atoi( argv[ args + 1 ] );
Expand Down Expand Up @@ -762,25 +777,131 @@ int main(int argc, char *argv[]) {
fprintf (stderr, "Could not open input file %s\n", argv[argc - 2]);
goto failure;
}
if (input_wav) {
unsigned char hdr[12];
if (fread(hdr, 1, 12, fin) != 12) {
fprintf(stderr, "Failed reading WAV header from %s\n", inFile);
goto failure;
}
if (memcmp(hdr, "RIFF", 4) != 0 || memcmp(hdr + 8, "WAVE", 4) != 0) {
fprintf(stderr, "Input file %s is not a WAV file\n", inFile);
goto failure;
}
/* Parse chunks to find 'fmt ' then 'data' */
while (1) {
unsigned char chunk[8];
if (fread(chunk, 1, 8, fin) != 8) {
fprintf(stderr, "Invalid WAV file %s\n", inFile);
goto failure;
}
uint32_t chunk_size = chunk[4] | (chunk[5]<<8) | (chunk[6]<<16) | (chunk[7]<<24);
if (memcmp(chunk, "fmt ", 4) == 0) {
unsigned char fmt[16];
size_t toread = chunk_size < 16 ? chunk_size : 16;
if (fread(fmt, 1, toread, fin) != toread) {
fprintf(stderr, "Invalid WAV fmt chunk in %s\n", inFile);
goto failure;
}
uint16_t audio_format = fmt[0] | (fmt[1]<<8);
uint16_t num_channels = fmt[2] | (fmt[3]<<8);
uint32_t sr = fmt[4] | (fmt[5]<<8) | (fmt[6]<<16) | (fmt[7]<<24);
uint16_t bits_per_sample = fmt[14] | (fmt[15]<<8);
if (chunk_size > toread) fseek(fin, chunk_size - toread, SEEK_CUR);
/* Now find data chunk */
while (1) {
if (fread(chunk, 1, 8, fin) != 8) {
fprintf(stderr, "Invalid WAV file %s (no data chunk)\n", inFile);
goto failure;
}
uint32_t cs = chunk[4] | (chunk[5]<<8) | (chunk[6]<<16) | (chunk[7]<<24);
if (memcmp(chunk, "data", 4) == 0) {
input_wav_data_size = cs;
input_wav_data_start = ftell(fin);
break;
} else {
if (fseek(fin, cs, SEEK_CUR) < 0) {
fprintf(stderr, "Failed seeking WAV chunk\n");
goto failure;
}
}
}
sampling_rate = (oac_int32) sr;
channels = (int) num_channels;
if (bits_per_sample == 16) format = FORMAT_S16_LE;
else if (bits_per_sample == 24) format = FORMAT_S24_LE;
else if (audio_format == 3 && bits_per_sample == 32) format = FORMAT_F32_LE;
else {
fprintf(stderr, "Unsupported WAV format: audio_format %d bits %d\n", audio_format, bits_per_sample);
goto failure;
}
fprintf(stderr, "Input WAV: %d Hz, %d channels, %d bits\n", sampling_rate, channels, bits_per_sample);
break;
} else {
if (fseek(fin, chunk_size, SEEK_CUR) < 0) {
fprintf(stderr, "Invalid WAV file %s\n", inFile);
goto failure;
}
}
}
}
if (mode_list) {
int size;
int sample_size = 2;
if (format == FORMAT_S24_LE) sample_size = 3;
else if (format == FORMAT_F32_LE) sample_size = 4;
fseek(fin, 0, SEEK_END);
size = ftell(fin);
if (input_wav) {
size -= input_wav_data_start;
fseek(fin, input_wav_data_start, SEEK_SET);
} else {
fseek(fin, 0, SEEK_SET);
}
fprintf(stderr, "File size is %d bytes\n", size);
fseek(fin, 0, SEEK_SET);
mode_switch_time = size/sample_size/channels/nb_modes_in_list;
fprintf(stderr, "Switching mode every %d samples\n", mode_switch_time);
}
if (input_wav && !mode_list) {
/* Ensure file position is at the start of PCM data */
if (fseek(fin, input_wav_data_start, SEEK_SET) < 0) {
fprintf(stderr, "Failed to seek to WAV data start\n");
goto failure;
}
}

outFile = argv[argc - 1];
fout = fopen(outFile, "wb+");
if (!fout) {
fprintf (stderr, "Could not open output file %s\n", argv[argc - 1]);
goto failure;
}
if (output_wav) {
/* Write a WAV header placeholder (we'll update sizes at the end). */
unsigned char header[44] = {0};
int bits_per_sample = (format == FORMAT_S16_LE) ? 16 : (format == FORMAT_S24_LE) ? 24 : 32;
int audio_format = (format == FORMAT_F32_LE) ? 3 : 1;
uint32_t byte_rate = (uint32_t)(sampling_rate * channels * (bits_per_sample/8));
uint16_t block_align = (uint16_t)(channels * (bits_per_sample/8));
memcpy(header + 0, "RIFF", 4);
/* chunk size = 36 + subchunk2size (unknown yet, set to 36) */
header[4] = 36 & 0xFF; header[5] = (36>>8) & 0xFF; header[6] = (36>>16) & 0xFF; header[7] = (36>>24) & 0xFF;
memcpy(header + 8, "WAVE", 4);
memcpy(header + 12, "fmt ", 4);
header[16] = 16 & 0xFF; header[17] = 0; header[18] = 0; header[19] = 0;
header[20] = audio_format & 0xFF; header[21] = (audio_format>>8) & 0xFF;
header[22] = channels & 0xFF; header[23] = (channels>>8) & 0xFF;
header[24] = sampling_rate & 0xFF; header[25] = (sampling_rate>>8) & 0xFF; header[26] = (sampling_rate>>16) & 0xFF; header[27] = (sampling_rate>>24) & 0xFF;
header[28] = byte_rate & 0xFF; header[29] = (byte_rate>>8) & 0xFF; header[30] = (byte_rate>>16) & 0xFF; header[31] = (byte_rate>>24) & 0xFF;
header[32] = block_align & 0xFF; header[33] = (block_align>>8) & 0xFF;
header[34] = bits_per_sample & 0xFF; header[35] = (bits_per_sample>>8) & 0xFF;
memcpy(header + 36, "data", 4);
/* subchunk2size (data size) left at 0 for now */
if (fwrite(header, 1, 44, fout) != 44) {
fprintf(stderr, "Error writing wav header\n");
goto failure;
}
wav_data_bytes = 0;
}

if (!decode_only) {
enc = oac_encoder_create(sampling_rate, channels, application, &err);
Expand Down Expand Up @@ -1137,12 +1258,15 @@ int main(int argc, char *argv[]) {
fbytes[4*i + 3] = (s.i>>24)&0xFF;
}
}
if (fwrite(fbytes, format_size[format]*channels, output_samples - skip,
fout) != (unsigned)(output_samples - skip)) {
fprintf(stderr, "Error writing.\n");
goto failure;
{
size_t bytes_to_write = (size_t)format_size[format]*channels*(output_samples - skip);
if (fwrite(fbytes, 1, bytes_to_write, fout) != bytes_to_write) {
fprintf(stderr, "Error writing.\n");
goto failure;
}
if (output_wav) wav_data_bytes += bytes_to_write;
tot_out += output_samples - skip;
}
tot_out += output_samples - skip;
}
if (output_samples < skip)skip -= output_samples;
else skip = 0;
Expand Down Expand Up @@ -1223,8 +1347,21 @@ int main(int argc, char *argv[]) {
free(data);
if (fin)
fclose(fin);
if (fout)
if (fout) {
if (output_wav) {
/* Update RIFF chunk size and data chunk size */
uint32_t data_size = (uint32_t)wav_data_bytes;
uint32_t chunk_size = 36 + data_size;
unsigned char buf[4];
buf[0] = chunk_size & 0xFF; buf[1] = (chunk_size>>8) & 0xFF; buf[2] = (chunk_size>>16) & 0xFF; buf[3] = (chunk_size>>24) & 0xFF;
fseek(fout, 4, SEEK_SET);
fwrite(buf, 1, 4, fout);
buf[0] = data_size & 0xFF; buf[1] = (data_size>>8) & 0xFF; buf[2] = (data_size>>16) & 0xFF; buf[3] = (data_size>>24) & 0xFF;
fseek(fout, 40, SEEK_SET);
fwrite(buf, 1, 4, fout);
}
fclose(fout);
}
free(in);
free(out);
free(fbytes);
Expand Down
Loading
Loading