-
Notifications
You must be signed in to change notification settings - Fork 53
Fix warnings, add CMakeLists.txt, break out bswap64 #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
c2daa4b
e88104e
e1bc739
3761798
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| build/ | ||
| tests/ | ||
| dr_flac.h | ||
| sokol_audio.h | ||
| dr_mp3.h | ||
| dr_libs/ | ||
| sokol/ | ||
| qoaconv | ||
| qoaplay | ||
| /*.qoa |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| cmake_minimum_required(VERSION 3.15) | ||
|
|
||
| project(QOA) | ||
|
|
||
| option(INCLUDE_FLAC "Add flac functionality to qoaconv" OFF) | ||
| option(INCLUDE_MP3 "Add mp3 functionality to qoaconv" OFF) | ||
|
|
||
| if (NOT EXISTS ./sokol) | ||
| execute_process(COMMAND git clone https://github.com/floooh/sokol) | ||
| endif() | ||
|
|
||
| set(QOAConvFlags "") | ||
| set(QOAPlayFlags "") | ||
|
|
||
| # LICENSING: DrLibs is declared public domain. If it is beneficial, it could be brought in. | ||
|
|
||
| set(DRLIBS_URL "https://github.com/mackron/dr_libs") | ||
| if (INCLUDE_FLAC) | ||
| if (NOT EXISTS ./dr_libs) | ||
| execute_process(COMMAND git clone ${DRLIBS_URL}) | ||
| endif() | ||
| list(APPEND QOAConvFlags -DQOACONV_HAS_DRFLAC) | ||
| endif() | ||
| if (INCLUDE_MP3) | ||
| if (NOT EXISTS ./dr_libs) | ||
| execute_process(COMMAND git clone ${DRLIBS_URL}) | ||
| endif() | ||
| list(APPEND QOAConvFlags -DQOACONV_HAS_DRMP3) | ||
| endif() | ||
|
|
||
| add_executable(QOAPlay | ||
| qoaplay.c) | ||
| add_executable(QOAConv | ||
| qoaconv.c) | ||
|
|
||
| list(APPEND QOAConvFlags -O3 -Wall -Wextra -std=gnu99) | ||
| list(APPEND QOAPlayFlags -O3 -Wall -Wextra -std=gnu99) | ||
| target_compile_options(QOAPlay PUBLIC ${QOAPlayFlags}) | ||
| target_compile_options(QOAConv PUBLIC ${QOAConvFlags}) | ||
| set(QOAPlayInclude "") | ||
| set(QOAConvInclude "") | ||
| list(APPEND QOAPlayInclude ./sokol) | ||
| if (INCLUDE_MP3 OR INCLUDE_FLAC) | ||
| list(APPEND QOAConvInclude dr_libs/) | ||
| endif() | ||
| target_include_directories(QOAConv PUBLIC ${QOAConvInclude}) | ||
| target_include_directories(QOAPlay PUBLIC ${QOAPlayInclude}) | ||
|
|
||
| if (WIN32) | ||
| target_link_libraries(QOAConv -lm) | ||
| target_link_libraries(QOAPlay -lole32) | ||
| elseif (UNIX) | ||
| target_link_libraries(QOAConv -lm) | ||
| target_link_libraries(QOAPlay -lpthread -lasound) | ||
| endif() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,13 @@ Audio samples in WAV & QOA format can be found at: https://qoaformat.org/samples | |
| wearing headphones. You may unexpectedly produce garbage output that can damage | ||
| your ears. I had more than a few close calls. | ||
|
|
||
| ## Building | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This commit adding cmake seems overkill for this project when for a while we didn't even need a Makefile. This doesn't need a second parallel build system when the first is already overkill.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Take a look at what the CMakeLists.txt does, It brings in everything automatically if it doesn't exist and marks the include directories appropriately. My main goal was to have it be accessible for most people instead of grabbing files off of a browser and pasting them off of GitHub. On top of that, more people are familiar with CMake. If you want to continue maintaining the Makefile you can do so. |
||
|
|
||
| Use `cmake -S . -B build` in the source directory containing qoaconv.c, qoaplay.c | ||
| This creates an isolated build folder called `build` to prevent clutter in the source tree. | ||
| To enable FLAC/MP3 support, add `-DINCLUDE_FLAC` / `-DINCLUDE_MP3` respectively to the | ||
| beginning of the aforementioned command. Then, run `cmake --build build` for the binaries. | ||
|
|
||
| ## Alternative Implementations of QOA | ||
|
|
||
| - [pfusik/qoa-ci](https://github.com/pfusik/qoa-ci) - Ć, transpiling to | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -316,27 +316,33 @@ static inline int qoa_clamp_s16(int v) { | |
| return v; | ||
| } | ||
|
|
||
| static inline qoa_uint64_t qoa_bswap64(qoa_uint64_t v) { | ||
| #if defined(__GNUC__) || defined(__clang__) | ||
| return __builtin_bswap64(v); | ||
| #elif defined(_MSC_VER) | ||
| return _byteswap_uint64(v); | ||
| #else | ||
| return (v >> 56) & 0xff | | ||
| (v >> 48) & 0xff | | ||
| (v >> 40) & 0xff | | ||
| (v >> 32) & 0xff | | ||
| (v >> 24) & 0xff | | ||
| (v >> 16) & 0xff | | ||
| (v >> 8) & 0xff | | ||
| (v >> 0) & 0xff ; | ||
| #endif | ||
| } | ||
|
|
||
| static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) { | ||
| bytes += *p; | ||
| *p += 8; | ||
| return | ||
| ((qoa_uint64_t)(bytes[0]) << 56) | ((qoa_uint64_t)(bytes[1]) << 48) | | ||
| ((qoa_uint64_t)(bytes[2]) << 40) | ((qoa_uint64_t)(bytes[3]) << 32) | | ||
| ((qoa_uint64_t)(bytes[4]) << 24) | ((qoa_uint64_t)(bytes[5]) << 16) | | ||
| ((qoa_uint64_t)(bytes[6]) << 8) | ((qoa_uint64_t)(bytes[7]) << 0); | ||
| return qoa_bswap64(*(qoa_uint64_t*)bytes); | ||
| } | ||
|
|
||
| static inline void qoa_write_u64(qoa_uint64_t v, unsigned char *bytes, unsigned int *p) { | ||
| bytes += *p; | ||
| *p += 8; | ||
| bytes[0] = (v >> 56) & 0xff; | ||
| bytes[1] = (v >> 48) & 0xff; | ||
| bytes[2] = (v >> 40) & 0xff; | ||
| bytes[3] = (v >> 32) & 0xff; | ||
| bytes[4] = (v >> 24) & 0xff; | ||
| bytes[5] = (v >> 16) & 0xff; | ||
| bytes[6] = (v >> 8) & 0xff; | ||
| bytes[7] = (v >> 0) & 0xff; | ||
| *(qoa_uint64_t*)bytes = qoa_bswap64(v); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're assuming that the current machine is little endian and you have to byteswap. It will not break on big endian machines. Before the code didn't make any assumptions and worked on everything. |
||
| } | ||
|
|
||
|
|
||
|
|
@@ -366,7 +372,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned | |
| ), bytes, &p); | ||
|
|
||
|
|
||
| for (int c = 0; c < channels; c++) { | ||
| for (unsigned int c = 0; c < channels; c++) { | ||
| /* If the weights have grown too large, reset them to 0. This may happen | ||
| with certain high-frequency sounds. This is a last resort and will | ||
| introduce quite a bit of noise, but should at least prevent pops/clicks */ | ||
|
|
@@ -385,7 +391,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned | |
| /* Write the current LMS state */ | ||
| qoa_uint64_t weights = 0; | ||
| qoa_uint64_t history = 0; | ||
| for (int i = 0; i < QOA_LMS_LEN; i++) { | ||
| for (unsigned int i = 0; i < QOA_LMS_LEN; i++) { | ||
| history = (history << 16) | (qoa->lms[c].history[i] & 0xffff); | ||
| weights = (weights << 16) | (qoa->lms[c].weights[i] & 0xffff); | ||
| } | ||
|
|
@@ -395,9 +401,13 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned | |
|
|
||
| /* We encode all samples with the channels interleaved on a slice level. | ||
| E.g. for stereo: (ch-0, slice 0), (ch 1, slice 0), (ch 0, slice 1), ...*/ | ||
| for (int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) { | ||
|
|
||
| for (int c = 0; c < channels; c++) { | ||
| qoa_lms_t best_lms; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why don't you just do I also wouldn't move where this is declared. |
||
| memset(&best_lms, 0, sizeof(best_lms)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. memset is a big hammer to suddently spring for a warning about something that will never happen. What happens when someone wants to run this library on embedded and there is no memset? |
||
|
|
||
| for (unsigned int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) { | ||
|
|
||
| for (unsigned int c = 0; c < channels; c++) { | ||
| int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index); | ||
| int slice_start = sample_index * channels + c; | ||
| int slice_end = (sample_index + slice_len) * channels + c; | ||
|
|
@@ -406,9 +416,8 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned | |
| 16 scalefactors, encode all samples for the current slice and | ||
| meassure the total squared error. */ | ||
| qoa_uint64_t best_error = -1; | ||
| qoa_uint64_t best_slice; | ||
| qoa_lms_t best_lms; | ||
| int best_scalefactor; | ||
| qoa_uint64_t best_slice = 0; | ||
| int best_scalefactor = 0; | ||
|
|
||
| for (int sfi = 0; sfi < 16; sfi++) { | ||
| /* There is a strong correlation between the scalefactors of | ||
|
|
@@ -488,9 +497,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) | |
| num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */ | ||
| num_slices * 8 * qoa->channels; /* 8 byte slices */ | ||
|
|
||
| unsigned char *bytes = QOA_MALLOC(encoded_size); | ||
|
|
||
| for (int c = 0; c < qoa->channels; c++) { | ||
| for (unsigned int c = 0; c < qoa->channels; c++) { | ||
| /* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the | ||
| prediction of the first few ms of a file. */ | ||
| qoa->lms[c].weights[0] = 0; | ||
|
|
@@ -500,11 +507,12 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) | |
|
|
||
| /* Explicitly set the history samples to 0, as we might have some | ||
| garbage in there. */ | ||
| for (int i = 0; i < QOA_LMS_LEN; i++) { | ||
| for (unsigned int i = 0; i < QOA_LMS_LEN; i++) { | ||
| qoa->lms[c].history[i] = 0; | ||
| } | ||
| } | ||
|
|
||
| unsigned char *bytes = QOA_MALLOC(encoded_size); | ||
|
|
||
| /* Encode the header and go through all frames */ | ||
| unsigned int p = qoa_encode_header(qoa, bytes); | ||
|
|
@@ -513,7 +521,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) | |
| #endif | ||
|
|
||
| int frame_len = QOA_FRAME_LEN; | ||
| for (int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) { | ||
| for (unsigned int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) { | ||
| frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index); | ||
| const short *frame_samples = sample_data + sample_index * qoa->channels; | ||
| unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p); | ||
|
|
@@ -576,14 +584,14 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa | |
|
|
||
| /* Read and verify the frame header */ | ||
| qoa_uint64_t frame_header = qoa_read_u64(bytes, &p); | ||
| int channels = (frame_header >> 56) & 0x0000ff; | ||
| int samplerate = (frame_header >> 32) & 0xffffff; | ||
| int samples = (frame_header >> 16) & 0x00ffff; | ||
| int frame_size = (frame_header ) & 0x00ffff; | ||
| unsigned int channels = (frame_header >> 56) & 0x0000ff; | ||
| unsigned int samplerate = (frame_header >> 32) & 0xffffff; | ||
| unsigned int samples = (frame_header >> 16) & 0x00ffff; | ||
| unsigned int frame_size = (frame_header ) & 0x00ffff; | ||
|
|
||
| int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels; | ||
| int num_slices = data_size / 8; | ||
| int max_total_samples = num_slices * QOA_SLICE_LEN; | ||
| unsigned int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels; | ||
| unsigned int num_slices = data_size / 8; | ||
| unsigned int max_total_samples = num_slices * QOA_SLICE_LEN; | ||
|
|
||
| if ( | ||
| channels != qoa->channels || | ||
|
|
@@ -596,7 +604,7 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa | |
|
|
||
|
|
||
| /* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */ | ||
| for (int c = 0; c < channels; c++) { | ||
| for (unsigned int c = 0; c < channels; c++) { | ||
| qoa_uint64_t history = qoa_read_u64(bytes, &p); | ||
| qoa_uint64_t weights = qoa_read_u64(bytes, &p); | ||
| for (int i = 0; i < QOA_LMS_LEN; i++) { | ||
|
|
@@ -609,8 +617,8 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa | |
|
|
||
|
|
||
| /* Decode all slices for all channels in this frame */ | ||
| for (int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) { | ||
| for (int c = 0; c < channels; c++) { | ||
| for (unsigned int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) { | ||
| for (unsigned int c = 0; c < channels; c++) { | ||
| qoa_uint64_t slice = qoa_read_u64(bytes, &p); | ||
|
|
||
| int scalefactor = (slice >> 60) & 0xf; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You added this file (and are the reason you need a fix now), why don't you just squash it into the original commit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Managed to squash that but my commit for fixing the
unsigned->unsigned intfor some reason I can't squash.