改为使用rtaudio

This commit is contained in:
Nanako 2024-03-06 21:08:06 +08:00
parent c6a8e16393
commit 7f87fc6ba4
17 changed files with 119 additions and 306 deletions

6
.gitmodules vendored
View File

@ -1,9 +1,6 @@
[submodule "third_party/imgui/imgui"]
path = third_party/imgui/imgui
url = https://github.com/ocornut/imgui.git
[submodule "third_party/portaudio"]
path = third_party/portaudio
url = https://github.com/PortAudio/portaudio.git
[submodule "third_party/spdlog"]
path = third_party/spdlog
url = https://github.com/gabime/spdlog.git
@ -13,3 +10,6 @@
[submodule "third_party/taskflow"]
path = third_party/taskflow
url = https://github.com/taskflow/taskflow.git
[submodule "third_party/rtaudio"]
path = third_party/rtaudio
url = https://github.com/thestk/rtaudio.git

View File

@ -56,19 +56,14 @@ function(retrieve_files out_files)
set(${out_files} ${RESULT} PARENT_SCOPE)
endfunction()
set(SHADER_CPP_GENERATED_CMAKE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateShaderCPPFile.cmake" CACHE STRING "" FORCE)
# setup portaudio
set(PA_USE_ASIO ON CACHE BOOL "" FORCE)
set(PA_BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
set(RTAUDIO_API_ASIO ON CACHE BOOL "" FORCE)
if (WIN32)
set(PA_USE_WMME OFF CACHE BOOL "" FORCE)
set(PA_USE_WDMKS OFF CACHE BOOL "" FORCE)
set(PA_USE_WDMKS_DEVICE_INFO OFF CACHE BOOL "" FORCE)
set(PA_USE_DS OFF CACHE BOOL "" FORCE)
set(RTAUDIO_API_WASAPI ON CACHE BOOL "" FORCE)
set(RTAUDIO_API_DS OFF CACHE BOOL "" FORCE)
elseif(APPLE)
set(PA_USE_COREAUDIO ON CACHE BOOL "" FORCE)
set(RTAUDIO_API_CORE ON CACHE BOOL "" FORCE)
endif()
# setup spdlog
@ -83,7 +78,7 @@ set(SPDLOG_ENABLE_PCH ON CACHE BOOL "" FORCE)
set(SPDLOG_USE_STD_FORMAT OFF CACHE BOOL "" FORCE)
if (MSVC)
link_directories(${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
link_directories(${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
endif ()
# setup taskflow
@ -91,12 +86,7 @@ set(TF_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(TF_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(core)
add_subdirectory(third_party/portaudio)
add_subdirectory(third_party/rtaudio)
add_subdirectory(third_party/spdlog)
add_subdirectory(third_party/mempool)
add_subdirectory(third_party/taskflow)
add_definitions(-DGLFW_INCLUDE_NONE)
# install
install(DIRECTORY ${CMAKE_SOURCE_DIR}/third_party/imgui/imgui/misc/fonts DESTINATION ${CMAKE_BINARY_DIR}/bin)

View File

@ -1,25 +0,0 @@
function(embed_resource resource_file_name source_file_name variable_name)
if(EXISTS "${source_file_name}")
if("${source_file_name}" IS_NEWER_THAN "${resource_file_name}")
return()
endif()
endif()
if(EXISTS "${resource_file_name}")
file(REMOVE "${source_file_name}")
file(READ "${resource_file_name}" hex_content HEX)
string(REPEAT "[0-9a-f]" 32 pattern)
string(REGEX REPLACE "(${pattern})" "\\1\n" content "${hex_content}")
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " content "${content}")
string(REGEX REPLACE ", $" "" content "${content}")
set(array_definition "static const uint8_t ${variable_name} []=\n{\n${content}\n};")
get_filename_component(file_name ${source_file_name} NAME)
set(source "#pragma once\n/**\n * @file ${file_name}\n * @brief Auto generated file.\n */\n${array_definition}\n")
file(WRITE "${source_file_name}" "${source}")
else()
message("ERROR: ${resource_file_name} doesn't exist")
return()
endif()
endfunction()
if(EXISTS "${PATH}")
embed_resource("${PATH}" "${HEADER}" "${GLOBAL}")
endif()

View File

@ -8,10 +8,10 @@ retrieve_files(ALL_FILES)
add_library(${PROJECT_NAME} SHARED ${ALL_FILES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PortAudio spdlog mempool Taskflow)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} rtaudio spdlog mempool Taskflow)
target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PortAudio spdlog mempool Taskflow)
target_link_libraries(${PROJECT_NAME} PUBLIC PortAudio spdlog mempool Taskflow)
target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} rtaudio spdlog mempool Taskflow)
target_link_libraries(${PROJECT_NAME} PUBLIC rtaudio spdlog mempool Taskflow)
target_precompile_headers(${PROJECT_NAME} PUBLIC extern.h)
add_definitions(-Dcore_EXPORTS)

View File

@ -1 +0,0 @@
#include "audio_device.h"

View File

@ -1,29 +0,0 @@
#pragma once
class audio_device {
public:
virtual ~audio_device() = default;
virtual bool init() = 0;
virtual void destroy() = 0;
void set_sample_rate(double sample_rate) {
if (on_set_sample_rate(sample_rate)) {
sample_rate_ = sample_rate;
}
}
void set_buffer_size(uint32_t buffer_size) {
if (on_set_buffer_size(buffer_size)) {
buffer_size_ = buffer_size;
}
}
[[nodiscard]] double sample_rate() const { return sample_rate_; }
[[nodiscard]] uint32_t buffer_size() const { return buffer_size_; }
protected:
virtual bool on_set_sample_rate(double in_sample_rate) = 0;
virtual bool on_set_buffer_size(uint32_t in_buffer_size) = 0;
private:
double sample_rate_ = 44100;
uint32_t buffer_size_ = 2048;
};

View File

@ -1,46 +1,53 @@
#include "audio_device_manager.h"
#include "dummy_audio_device.h"
#include "port_audio_device.h"
#include "audio/mixer/mixer.h"
#include "audio/mixer/mixer_track.h"
#include "audio/plugin_host/midi_sequencer.h"
#include "audio/plugin_host/plugin_host_manager.h"
#include "thread_message/thread_message_hubs.h"
int rt_audio_callback(void *output_buffer, void *input_buffer,
unsigned int frames_nums,
double stream_time,
RtAudioStreamStatus status,
void* user_data)
{
auto* API = static_cast<audio_device_manager*>(user_data);
return API->stream_callback((float**)output_buffer, static_cast<float**>(input_buffer), frames_nums, stream_time, status);
}
void audio_device_manager::init(singleton_initliazer& initliazer) {
singleton_t<audio_device_manager>::init(initliazer);
dummy_audio_device_ = new dummy_audio_device();
main_audio_device_ = new port_audio_device();
main_audio_device_->init();
dummy_audio_device_->init();
options_.flags = RTAUDIO_NONINTERLEAVED;
}
void audio_device_manager::release(singleton_release_guard& release_guard) {
singleton_t<audio_device_manager>::release(release_guard);
release_guard.require_release<mixer>();
delete dummy_audio_device_;
delete main_audio_device_;
}
void audio_device_manager::start() {
main_audio_device_->start();
RtAudio::StreamParameters* input_params = nullptr;
if (input_params_.deviceId != 0) {
input_params = &input_params_;
}
audio_.openStream(&output_params_, input_params, RTAUDIO_FLOAT32, get_sample_rate(), &options_.numberOfBuffers, &rt_audio_callback, this, &options_);
}
double audio_device_manager::get_sample_rate() const {
return main_audio_device_->sample_rate();
uint32_t audio_device_manager::get_sample_rate() const {
return sample_rate_;
}
void audio_device_manager::set_sample_rate(double sample_rate) {
main_audio_device_->set_sample_rate(sample_rate);
dummy_audio_device_->set_sample_rate(sample_rate);
void audio_device_manager::set_sample_rate(uint32_t sample_rate) {
sample_rate_ = sample_rate;
}
uint32_t audio_device_manager::get_buffer_size() const {
return main_audio_device_->buffer_size();
return options_.numberOfBuffers;
}
void audio_device_manager::set_buffer_size(int buffer_size) {
main_audio_device_->set_buffer_size(buffer_size);
dummy_audio_device_->set_buffer_size(buffer_size);
options_.numberOfBuffers = buffer_size;
}
uint32_t audio_device_manager::get_input_channel_count() const {
@ -50,3 +57,52 @@ uint32_t audio_device_manager::get_input_channel_count() const {
uint32_t audio_device_manager::get_output_channel_count() const {
return 2; // 现在是固定值, 以后可能会改
}
int audio_device_manager::stream_callback(float** output, float** input, unsigned long frame_count, double stream_time,
RtAudioStreamStatus status) {
if (render_buffer_.Num() < frame_count * output_params_.nChannels)
return 0;
for (int i = 0; i < output_params_.nChannels; ++i) {
render_buffer_.Pop(input[i], frame_count);
}
return 0;
}
void audio_device_manager::start_render_thread() {
render_thread_ = std::thread(&audio_device_manager::render_thread, this);
render_thread_.detach();
}
void audio_device_manager::stop_render_thread() {
if (render_thread_.joinable())
render_thread_.join();
}
void audio_device_manager::render_thread() {
dummy_track* master = g_mixer.get_master();
spdlog::info("port_audio render thread started");
render_buffer_.SetCapacity(get_buffer_size() * 4);
const uint32_t frames = get_buffer_size();
const uint32_t rate = get_sample_rate();
while (audio_.isStreamRunning()) {
const uint32_t milliseconds = 1.f / (rate / (frames / 4)) * 1000;
g_audio_thread_hub.process_messages();
if (render_buffer_.Num() >= render_buffer_size_) {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
continue;
}
g_midi_sequencer.process(rate, frames);
g_mixer.reset();
g_plugin_host_manager.process(frames);
g_mixer.process(frames);
const auto& master_headers = master->buffer.get_headers_vector();
for (const auto element: master_headers) {
render_buffer_.Push(element, frames);
}
}
spdlog::info("port_audio render thread stopped");
}

View File

@ -1,8 +1,8 @@
#pragma once
#include "misc/singleton/singleton.h"
class dummy_audio_device;
class port_audio_device;
#include "audio/misc/circular_audio_buffer.h"
#include "RtAudio.h"
class audio_device_manager : public singleton_t<audio_device_manager> {
public:
@ -13,15 +13,30 @@ public:
CORE_API [[nodiscard]] const char* get_name() override { return "audio_device_manager"; }
CORE_API [[nodiscard]] double get_sample_rate() const;
CORE_API void set_sample_rate(double sample_rate);
CORE_API [[nodiscard]] uint32_t get_sample_rate() const;
CORE_API void set_sample_rate(uint32_t sample_rate);
CORE_API [[nodiscard]] uint32_t get_buffer_size() const;
CORE_API void set_buffer_size(int buffer_size);
CORE_API [[nodiscard]] uint32_t get_input_channel_count() const;
CORE_API [[nodiscard]] uint32_t get_output_channel_count() const;
int stream_callback(float** output, float** input, unsigned long frame_count, double stream_time, RtAudioStreamStatus status);
protected:
#pragma region render_thread
void start_render_thread();
void stop_render_thread();
void render_thread();
std::thread render_thread_;
circular_audio_buffer<float> render_buffer_;
uint32_t render_buffer_size_ = 512;
std::atomic_bool render_thread_running_ = false;
#pragma endregion
private:
port_audio_device* main_audio_device_ = nullptr;
dummy_audio_device* dummy_audio_device_ = nullptr;
RtAudio::StreamParameters input_params_ = {};
RtAudio::StreamParameters output_params_ = {};
RtAudio::StreamOptions options_ = {};
uint32_t sample_rate_ = 44100;
RtAudio audio_;
};
DEFINE_SINGLETON_INSTANCE(audio_device_manager)

View File

@ -1 +0,0 @@
#include "dummy_audio_device.h"

View File

@ -1,12 +0,0 @@
#pragma once
#include "audio_device.h"
class dummy_audio_device : public audio_device {
public:
bool init() override { return true; }
void destroy() override {}
protected:
bool on_set_buffer_size(uint32_t in_buffer_size) override { return true; }
bool on_set_sample_rate(double in_sample_rate) override { return true; }
};

View File

@ -1,144 +0,0 @@
#include "port_audio_device.h"
#include "audio/mixer/mixer.h"
#include "audio/mixer/mixer_track.h"
#include "audio/plugin_host/midi_sequencer.h"
#include "audio/plugin_host/plugin_host_manager.h"
#include "thread_message/thread_message_hubs.h"
int port_audio_callback(const void* Input, void* Output, unsigned long FrameCount, const PaStreamCallbackTimeInfo* TimeInfo, PaStreamCallbackFlags StatusFlags, void* UserData)
{
auto* API = static_cast<port_audio_device*>(UserData);
return API->stream_callback((float**)Input, static_cast<float**>(Output), FrameCount, TimeInfo, StatusFlags);
}
port_audio_device::~port_audio_device() {
port_audio_device::destroy();
stop();
}
bool port_audio_device::init() {
Pa_Initialize();
return true;
}
void port_audio_device::destroy() {
stop();
Pa_Terminate();
}
void port_audio_device::stop() {
Pa_StopStream(stream_);
Pa_CloseStream(stream_);
stream_ = nullptr;
stop_render_thread();
spdlog::info("port_audio stopped");
}
void port_audio_device::start() {
try {
auto host_api_info = Pa_GetHostApiInfo(Pa_HostApiTypeIdToHostApiIndex(paASIO));
auto device_info = Pa_GetDeviceInfo(host_api_info->defaultOutputDevice);
open_stream(-1, host_api_info->defaultOutputDevice, sample_rate(), buffer_size());
} catch (const std::exception& e) {
spdlog::error("Init failed: {}", e.what());
}
}
void port_audio_device::open_stream(PaDeviceIndex input_device, PaDeviceIndex output_device, double in_sample_rate, uint32_t in_buffer_size) {
const PaStreamParameters* input_params = nullptr;
if (input_device >= 0) {
input_params_.device = input_device;
input_params_.channelCount = 2;
input_params_.sampleFormat = paFloat32 | paNonInterleaved;
input_params_.suggestedLatency = Pa_GetDeviceInfo(input_device)->defaultLowInputLatency;
input_params_.hostApiSpecificStreamInfo = nullptr;
input_params = &input_params_;
}
output_params_.device = output_device;
output_params_.channelCount = 2;
output_params_.sampleFormat = paFloat32 | paNonInterleaved;
output_params_.suggestedLatency = Pa_GetDeviceInfo(output_device)->defaultLowOutputLatency;
output_params_.hostApiSpecificStreamInfo = nullptr;
PaError err = Pa_OpenStream(&stream_, input_params, &output_params_, in_sample_rate, in_buffer_size, paClipOff, port_audio_callback, this);
if (err != paNoError) {
throw std::runtime_error(Pa_GetErrorText(err));
}
err = Pa_StartStream(stream_);
if (err != paNoError) {
throw std::runtime_error(Pa_GetErrorText(err));
}
start_render_thread();
}
int port_audio_device::stream_callback(float** input, float** output, unsigned long frame_count,
const PaStreamCallbackTimeInfo* time_info, PaStreamCallbackFlags status_flags) {
if (render_buffer_.Num() < frame_count * output_params_.channelCount)
return paContinue;
for (int i = 0; i < output_params_.channelCount; ++i) {
render_buffer_.Pop(output[i], frame_count);
}
return paContinue;
}
bool port_audio_device::on_set_buffer_size(uint32_t in_buffer_size) {
stop();
render_buffer_size_ = in_buffer_size * 4;
try {
open_stream(input_params_.device, output_params_.device, sample_rate(), in_buffer_size);
} catch (const std::exception& e) {
spdlog::error("Set buffer size failed: {}", e.what());
return false;
}
return true;
}
bool port_audio_device::on_set_sample_rate(double in_sample_rate) {
stop();
try {
open_stream(input_params_.device, output_params_.device, in_sample_rate, buffer_size());
} catch (const std::exception& e) {
spdlog::error("Set sample rate failed: {}", e.what());
return false;
}
return true;
}
void port_audio_device::start_render_thread() {
render_thread_ = std::thread(&port_audio_device::render_thread, this);
render_thread_.detach();
}
void port_audio_device::stop_render_thread() {
if (render_thread_.joinable())
render_thread_.join();
}
void port_audio_device::render_thread() {
dummy_track* master = g_mixer.get_master();
spdlog::info("port_audio render thread started");
render_buffer_.SetCapacity(buffer_size() * 4);
while (stream_) {
const uint32_t frames = buffer_size();
const double rate = sample_rate();
const uint32_t milliseconds = 1.f / (rate / (frames / 4)) * 1000;
g_audio_thread_hub.process_messages();
if (render_buffer_.Num() >= render_buffer_size_) {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
continue;
}
g_midi_sequencer.process(rate, frames);
g_mixer.reset();
g_plugin_host_manager.process(frames);
g_mixer.process(frames);
const auto& master_headers = master->buffer.get_headers_vector();
for (const auto element: master_headers) {
render_buffer_.Push(element, frames);
}
}
spdlog::info("port_audio render thread stopped");
}

View File

@ -1,37 +0,0 @@
#pragma once
#include "audio_device.h"
#include "portaudio.h"
#include "audio/misc/circular_audio_buffer.h"
class port_audio_device : public audio_device {
public:
~port_audio_device() override;
bool init() override;
void destroy() override;
void stop();
void start();
void open_stream(PaDeviceIndex input_device, PaDeviceIndex output_device, double in_sample_rate, uint32_t in_buffer_size);
int stream_callback(float** input, float** output, unsigned long frame_count, const PaStreamCallbackTimeInfo* time_info, PaStreamCallbackFlags status_flags);
protected:
bool on_set_buffer_size(uint32_t in_buffer_size) override;
bool on_set_sample_rate(double in_sample_rate) override;
private:
#pragma region render_thread
void start_render_thread();
void stop_render_thread();
void render_thread();
std::thread render_thread_;
circular_audio_buffer<float> render_buffer_;
uint32_t render_buffer_size_ = 512;
std::atomic_bool render_thread_running_ = false;
#pragma endregion
PaStreamParameters input_params_ = {};
PaStreamParameters output_params_ = {};
PaStream* stream_ = nullptr;
};

View File

@ -9,10 +9,10 @@
#include "thread_message/thread_message_hubs.h"
#include "taskflow/taskflow.hpp"
void build_effect_channel_interface(mixer_track* track, const channel_interface* interface, std::map<mixer_track*, int32_t>& processed_tracks) {
void build_effect_channel_interface(mixer_track* track, const channel_interface* in_interface, std::map<mixer_track*, int32_t>& processed_tracks) {
int32_t& track_current_layer = processed_tracks[track];
auto& input_channel_nodes = interface->input_channel_nodes;
auto& output_channel_nodes = interface->output_channel_nodes;
auto& input_channel_nodes = in_interface->input_channel_nodes;
auto& output_channel_nodes = in_interface->output_channel_nodes;
// 如果这个效果器需要从其他轨道输入,那么目标轨道的深度就是这个轨道的深度+1
for (int i = 1; i < input_channel_nodes.size(); ++i)
{

View File

@ -1,11 +1,12 @@
#pragma once
#include "spdlog/spdlog.h"
#ifdef core_EXPORTS
#define CORE_API __declspec(dllexport)
#else
#define CORE_API __declspec(dllimport)
#endif
#define CORE_API
// #ifdef core_EXPORTS
// #define CORE_API __declspec(dllexport)
// #else
// #define CORE_API __declspec(dllimport)
// #endif
#if defined(_DEBUG) || defined(DEBUG)
#define checkf(expr, msg, ...) if (!(expr)) { spdlog::error(msg, __VA_ARGS__); throw std::runtime_error(fmt::format(msg, __VA_ARGS__)); }

@ -1 +0,0 @@
Subproject commit daaf637f6f9fce670031221abfd7dfde92e5cce3

1
third_party/rtaudio vendored Submodule

@ -0,0 +1 @@
Subproject commit cb03db3c7e474c843444631e842468d5b258d095

@ -1 +1 @@
Subproject commit a8434d095ff60c53583646e80b6c57c688ee2a4f
Subproject commit 10dfa7920d1a786099a92614c4183f6339975728