Skip to content
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

Adding Krusher module #323

Merged
merged 20 commits into from
Jul 24, 2023
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
9 changes: 9 additions & 0 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ jobs:
path: modules/BYOD-add-ons
token: ${{ secrets.OUR_GITHUB_PAT }}

- name: Checkout Jai
uses: actions/checkout@v2
if: github.actor == 'jatinchowdhury18'
with:
ref: main
repository: Chowdhury-DSP/jai-minimal
token: ${{ secrets.OUR_GITHUB_PAT }}
path: modules/jai

- name: Configure
shell: bash
env:
Expand Down
18 changes: 18 additions & 0 deletions modules/cmake/FindJaiCompiler.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
if(NOT (IOS OR LINUX))
if(WIN32)
set(JAI_COMPILER_EXE "jai.exe")
elseif(APPLE)
set(JAI_COMPILER_EXE "jai-macos")
else()
set(JAI_COMPILER_EXE "jai-linux")
endif()

find_program(JAI_COMPILER
NAMES ${JAI_COMPILER_EXE}
HINTS ${CMAKE_SOURCE_DIR}/modules/jai/bin ${CMAKE_SOURCE_DIR}/../../Research/jai/bin
)
message(STATUS "Jai compiler: ${JAI_COMPILER}")
else()
message(STATUS "Skipping Jai checks on this platform")
set(JAI_COMPILER "JAI_COMPILER-NOTFOUND")
endif()
14 changes: 13 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ if(NOT (IOS OR BUILD_RELEASE))
add_subdirectory(headless)
endif()

# main source files
target_sources(BYOD PRIVATE
BYOD.cpp

Expand Down Expand Up @@ -140,6 +141,7 @@ target_sources(BYOD PRIVATE
processors/other/cry_baby/CryBabyNDK.cpp
processors/other/spring_reverb/SpringReverb.cpp
processors/other/spring_reverb/SpringReverbProcessor.cpp
processors/other/krusher/Krusher.cpp

processors/utility/CleanGain.cpp
processors/utility/FreqBandSplitter.cpp
Expand All @@ -155,8 +157,15 @@ target_sources(BYOD PRIVATE
processors/netlist_helpers/NetlistViewer.cpp
)

target_precompile_headers(BYOD PRIVATE pch.h)
# Jai files
include(${CMAKE_SOURCE_DIR}/modules/cmake/FindJaiCompiler.cmake)
if (NOT(${JAI_COMPILER} STREQUAL "JAI_COMPILER-NOTFOUND"))
message(STATUS "Configuring Jai compilation!")
add_subdirectory(jai)
target_compile_definitions(BYOD PRIVATE BYOD_BUILDING_JAI_MODULES=1)
endif()

# AVX/SSE files for accelerated neural nets
make_lib_simd_runtime(rnn_accelerated processors/drive/neural_utils/RNNAccelerated.cpp)
foreach(target IN ITEMS rnn_accelerated_sse_or_arm rnn_accelerated_avx)
target_link_libraries(${target} PRIVATE config_flags juce::juce_recommended_lto_flags warning_flags)
Expand All @@ -177,7 +186,10 @@ target_compile_definitions(rnn_accelerated_sse_or_arm PRIVATE RTNEURAL_DEFAULT_A
target_compile_definitions(rnn_accelerated_avx PRIVATE RTNEURAL_DEFAULT_ALIGNMENT=32)
target_link_libraries(BYOD PRIVATE rnn_accelerated)

# special flags for MSVC
if (MSVC)
target_compile_options(BYOD PRIVATE /bigobj)
endif ()

# pre-compiled header
target_precompile_headers(BYOD PRIVATE pch.h)
4 changes: 4 additions & 0 deletions src/jai/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.build/
*.lib
*.a
*_jai_lib.h
42 changes: 42 additions & 0 deletions src/jai/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
if(WIN32)
set(JAI_LIBRARY_FILE "byod_jai_lib.lib")
else()
set(JAI_LIBRARY_FILE "byod_jai_lib.a")
endif()

add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${JAI_LIBRARY_FILE}
COMMAND ${JAI_COMPILER} build.jai
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS build.jai krusher/bit_reduction.jai krusher/lofi_downsampler.jai
)
add_custom_target(jai_library_build DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${JAI_LIBRARY_FILE})

add_library(byod_jai_lib STATIC IMPORTED GLOBAL)
add_dependencies(byod_jai_lib jai_library_build)

set_target_properties(byod_jai_lib
PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${JAI_LIBRARY_FILE}
)

target_link_libraries(BYOD PRIVATE byod_jai_lib)
target_sources(BYOD PRIVATE SharedJaiContext.cpp stb_sprintf.cpp)
target_compile_definitions(BYOD PRIVATE STB_SPRINTF_IMPLEMENTATION=1)

if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))
string(JOIN " " STB_CXX_FLAGS
"-Wno-language-extension-token"
"-Wno-zero-as-null-pointer-constant"
"-Wno-cast-align"
"-Wno-implicit-fallthrough"
"-Wno-conditional-uninitialized"
"-Wno-duplicate-decl-specifier"
"-Wno-unreachable-code"
)
set_source_files_properties(stb_sprintf.cpp
TARGET_DIRECTORY BYOD
PROPERTIES COMPILE_FLAGS "${STB_CXX_FLAGS}"
)
endif()

#TODO: remove stb_sprintf once Jai.Basic no longer depends on it!
12 changes: 12 additions & 0 deletions src/jai/SharedJaiContext.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "SharedJaiContext.h"
#include "byod_jai_lib.h"

JaiContextWrapper::JaiContextWrapper()
{
internal = __jai_runtime_init (0, nullptr);
}

JaiContextWrapper::~JaiContextWrapper()
{
__jai_runtime_fini (internal);
}
17 changes: 17 additions & 0 deletions src/jai/SharedJaiContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <juce_core/juce_core.h>

struct jai_Context;
struct JaiContextWrapper
{
JaiContextWrapper();
~JaiContextWrapper();

operator jai_Context*() { return internal; }; // NOLINT

private:
jai_Context* internal = nullptr;
};

using SharedJaiContext = juce::SharedResourcePointer<JaiContextWrapper>;
44 changes: 44 additions & 0 deletions src/jai/build.jai
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#import "Basic";
#import "Compiler";
#import "generate_c_header";

#run build();

SRC_FILES :: string.[
"krusher/lofi_downsampler.jai",
"krusher/bit_reduction.jai"
];

build :: () {
header_info : Header_Info;
header_info.jai_type_prefix = "jai_";

w := compiler_create_workspace();

target_options := get_build_options(w);
target_options.output_executable_name = "byod_jai_lib";
target_options.output_type = .STATIC_LIBRARY; // specifies output to be a static library
target_options.backend = .LLVM;
target_options.text_output_flags = 1;
set_optimization(*target_options, .OPTIMIZED);

set_build_options(target_options, w);

compiler_begin_intercept(w);
for file, _ : SRC_FILES {
add_build_file(tprint("%/%", #filepath, file), w);
}
while true {
message := compiler_wait_for_message();
handle_message(*header_info, message);
if message.kind == {
case .COMPLETE;
break;
}
}
compiler_end_intercept(w);

generate_header(*header_info, "byod_jai_lib.h");

set_build_options_dc(.{do_output=false}); // No executable for this workspace.
}
159 changes: 159 additions & 0 deletions src/jai/krusher/bit_reduction.jai
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
Krusher_Bit_Reducer_Filter_State :: struct {
p1: s32;
p2: s32;
}

#program_export
krusher_bit_reduce_process_block :: (buffer: **float,
num_channels: s32,
num_samples: s32,
filter_index: s32,
bit_depth: s32,
filter_states: *Krusher_Bit_Reducer_Filter_State) #c_call {
small_block_size : s32 : 16;
samples_int : [small_block_size]s16 = ---;

for channel : 0..num_channels-1 {
samples_remaining : s32 = num_samples;
while samples_remaining > 0 {
samples_to_process := ifx samples_remaining > small_block_size then small_block_size else samples_remaining;
defer { samples_remaining -= samples_to_process; }

samples_float_span : []float32;
samples_float_span.data = buffer[channel] + num_samples - samples_remaining;
samples_float_span.count = samples_to_process;

memset(*samples_int, 0, size_of(s16) * small_block_size);
samples_int_span : []s16;
samples_int_span.data = samples_int.data;
samples_int_span.count = samples_to_process;

convert_float_to_int(samples_float_span, samples_int_span);

if (bit_depth < 12) {
br_data := bit_reduce_encode(samples_int_span, bit_depth);
bit_reduce_decode(br_data, samples_int_span, cast(BR_Filter) filter_index, *filter_states[channel]);
}

convert_int_to_float(samples_int_span, samples_float_span);
}
}
}

#scope_file
BIT_MASKS :: u16.[
0, // 0
0x0001, // 1
0x0003, // 2
0x0007, // 3
0x000F, // 4
0x001F, // 5
0x003F, // 6
0x007F, // 7
0x00FF, // 8
0x01FF, // 9
0x03FF, // 10
0x07FF, // 11
0x0FFF, // 12
0x1FFF, // 13
0x3FFF, // 14
0x7FFF, // 15
];

Bit_Reduction_Block :: struct {
shift_amount: u8;
data: [16] u16;
}

BR_Filter :: enum {
TYPE_0;
TYPE_1;
TYPE_2;
TYPE_3;
}

encode_sample :: inline (shift: u8, bit_depth: s32, x: s16) -> u16 #no_context {
value_unsigned := cast(u16) (x + (1 << 8));
return cast(u16) (value_unsigned >> shift) & BIT_MASKS[bit_depth];
}

decode_sample :: inline (shift: u8, x: u16) -> s16 #no_context {
return cast(s16) (cast(u16) (x << shift) - (1 << 8));
}

bit_reduce_decode :: (using br_block: Bit_Reduction_Block,
out: []s16,
filter: BR_Filter,
state: *Krusher_Bit_Reducer_Filter_State) #no_context {

type1_filter :: inline (nibble_2r: s16, using state: *Krusher_Bit_Reducer_Filter_State) -> s16 #no_context {
y := cast(s32) nibble_2r + ((p1 * 15) >> 4);
p2 = 0;
p1 = y;
return cast(s16) (y >> 4);
}

type2_filter :: inline (nibble_2r: s16, using state: *Krusher_Bit_Reducer_Filter_State) -> s16 #no_context {
y := cast(s32) nibble_2r + ((p1 * 61) >> 5) - ((p2 * 15) >> 4);
p2 = p1;
p1 = y;
return cast(s16) (y >> 5);
}

type3_filter :: inline (nibble_2r: s16, using state: *Krusher_Bit_Reducer_Filter_State) -> s16 #no_context {
y := cast(s32) nibble_2r + ((p1 * 115) >> 6) - ((p2 * 13) >> 4);
p2 = p1;
p1 = y;
return cast(s16) (y >> 6);
}

for br_sample, i : data {
if #complete filter == {
case .TYPE_0; out[i] = decode_sample(shift_amount, br_sample);
case .TYPE_1; out[i] = type1_filter (decode_sample(shift_amount, br_sample), state);
case .TYPE_2; out[i] = type2_filter (decode_sample(shift_amount, br_sample), state);
case .TYPE_3; out[i] = type3_filter (decode_sample(shift_amount, br_sample), state);
}
}
}

bit_reduce_encode :: (pcm_data: []s16, bit_depth: s32) -> Bit_Reduction_Block #no_context {
shift_best : u8 = 0;
err_min : float64 = Math.FLOAT64_MAX;

for s : cast(u8) 0.. cast(u8) (16 - bit_depth) {
err_square_accum : float64 = 0.0;
for pcm_sample, _ : pcm_data {
pred := decode_sample(s, encode_sample(s, bit_depth, pcm_sample));
err := cast(float64) (pcm_sample - pred);
err_square_accum += err * err;
}

if err_square_accum < err_min {
err_min = err_square_accum;
shift_best = s;
}
}

using br_block : Bit_Reduction_Block = ---;
shift_amount = shift_best;
for pcm_sample, i : pcm_data {
data[i] = encode_sample(shift_best, bit_depth, pcm_sample);
}

return br_block;
}

convert_float_to_int :: (data_float: [] float, data_int: [] s16) #no_context {
for float_sample, i : data_float {
data_int[i] = cast(s16) (float_sample * cast(float32) (1 << 8));
}
}

convert_int_to_float :: (data_int: [] s16, data_float: [] float) #no_context {
for _, i : data_float {
data_float[i] = cast(float32) data_int[i] / cast(float32) (1 << 8);
}
}

Math :: #import "Math";
Loading
Loading