Skip to content

Commit

Permalink
Provide continuous audio stream processing example as a consequence of
Browse files Browse the repository at this point in the history
…#18 #38 solving #33 #34 #37
  • Loading branch information
jurihock committed Nov 17, 2023
1 parent 16c33e6 commit 9e99645
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 10 deletions.
28 changes: 18 additions & 10 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
cmake_minimum_required(VERSION 3.1...3.18)

project(StftPitchShiftExample)
project(StftPitchShiftExamples)

# basic example of single file processing
add_executable(example "${CMAKE_CURRENT_LIST_DIR}/example.cpp")

# at least C++ 20 is mandatory
target_compile_features(example PRIVATE cxx_std_20)
# advanced example of continuous stream processing
add_executable(realtime "${CMAKE_CURRENT_LIST_DIR}/realtime.cpp")

# just the case of default build e.g. if LibStftPitchShift.cmake is already included
target_link_libraries(example stftpitchshift)
foreach(name example realtime)

# if installed via ppa:jurihock/stftpitchshift on Ubuntu
# target_link_libraries(example stftpitchshift)
# at least C++ 20 is mandatory
target_compile_features(${name} PRIVATE cxx_std_20)

# if installed via vcpkg
# find_package(stftpitchshift CONFIG REQUIRED)
# target_link_libraries(example stftpitchshift::stftpitchshift)
# just the case of default build e.g. if LibStftPitchShift.cmake is already included
target_link_libraries(${name} stftpitchshift)

# if installed via ppa:jurihock/stftpitchshift on Ubuntu
# target_link_libraries(${name} stftpitchshift)

# if installed via vcpkg
# find_package(stftpitchshift CONFIG REQUIRED)
# target_link_libraries(${name} stftpitchshift::stftpitchshift)

endforeach()
125 changes: 125 additions & 0 deletions examples/realtime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <memory>
#include <span>
#include <tuple>
#include <vector>

#include <StftPitchShift/STFT.h>
#include <StftPitchShift/StftPitchShiftCore.h>

using namespace stftpitchshift;

// basic parameters
// - samplerate as required
// - overlap at least 4
const double samplerate = 44100;
const size_t overlap = 4;

// analysis and synthesis window size
// power of two each of them
// - for best quality: analysis = synthesis
// - for lower latency: analysis > synthesis
// - synthesis window size corresponds to the audio frame size and vice versa
const std::tuple<size_t, size_t> framesize = { 1024, 1024 };

// hop size refers to the synthesis window size
const size_t hopsize = std::get<1>(framesize) / overlap;

// delay buffers for input and output frames
struct { std::vector<double> input, output; } buffer;

// modules required for pitch shifting
std::shared_ptr<STFT<double>> stft;
std::shared_ptr<StftPitchShiftCore<double>> core;

// example of an IO callback
void audio_interface_callback(std::span<float> input, std::span<float> output);

int main()
{
// prepare for audio stream processing

const size_t total_buffer_size =
std::get<0>(framesize) +
std::get<1>(framesize);

buffer.input.resize(total_buffer_size);
buffer.output.resize(total_buffer_size);

stft = std::make_shared<STFT<double>>(framesize, hopsize);
core = std::make_shared<StftPitchShiftCore<double>>(framesize, hopsize, samplerate);

// set pitch shifting parameters as required

core->factors({ 1 });
core->quefrency(0 * 1e-3);
core->distortion(1);
core->normalization(false);

// simulate continuous audio stream processing

const size_t n = static_cast<size_t>(samplerate);
const size_t m = std::get<1>(framesize);

std::vector<float> x(n), y(n);

for (size_t i = 0; (i + m) < n; i += m)
{
std::cout << "processing audio frame " << (i / m + 1) << " of " << (n / m) << std::endl;

std::span<float> input = std::span(x.data(), m);
std::span<float> output = std::span(y.data(), m);

audio_interface_callback(input, output);
}

return 0;
}

void audio_interface_callback(std::span<float> input, std::span<float> output)
{
const auto analysis_window_size = std::get<0>(framesize);
const auto synthesis_window_size = std::get<1>(framesize);

// this is the expected input and output frame size
assert(input.size() == synthesis_window_size);
assert(output.size() == synthesis_window_size);

// shift input buffer
std::copy(
buffer.input.begin() + synthesis_window_size,
buffer.input.end(),
buffer.input.begin());

// copy new input samples
std::copy(
input.begin(),
input.end(),
buffer.input.begin() + analysis_window_size);

// apply pitch shifting within the STFT routine
(*stft)(buffer.input, buffer.output, [&](std::span<std::complex<double>> dft)
{
core->shiftpitch(dft);
});

// copy new output samples back
std::copy(
buffer.output.begin() - synthesis_window_size + analysis_window_size,
buffer.output.end() - synthesis_window_size,
output.begin());

// shift output buffer
std::copy(
buffer.output.begin() + synthesis_window_size,
buffer.output.end(),
buffer.output.begin());

// prepare for the next callback
std::fill(
buffer.output.begin() + analysis_window_size,
buffer.output.end(),
0);
}

0 comments on commit 9e99645

Please sign in to comment.