改为使用rtaudio
This commit is contained in:
parent
c6a8e16393
commit
7f87fc6ba4
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,9 +1,6 @@
|
|||||||
[submodule "third_party/imgui/imgui"]
|
[submodule "third_party/imgui/imgui"]
|
||||||
path = third_party/imgui/imgui
|
path = third_party/imgui/imgui
|
||||||
url = https://github.com/ocornut/imgui.git
|
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"]
|
[submodule "third_party/spdlog"]
|
||||||
path = third_party/spdlog
|
path = third_party/spdlog
|
||||||
url = https://github.com/gabime/spdlog.git
|
url = https://github.com/gabime/spdlog.git
|
||||||
@ -13,3 +10,6 @@
|
|||||||
[submodule "third_party/taskflow"]
|
[submodule "third_party/taskflow"]
|
||||||
path = third_party/taskflow
|
path = third_party/taskflow
|
||||||
url = https://github.com/taskflow/taskflow.git
|
url = https://github.com/taskflow/taskflow.git
|
||||||
|
[submodule "third_party/rtaudio"]
|
||||||
|
path = third_party/rtaudio
|
||||||
|
url = https://github.com/thestk/rtaudio.git
|
||||||
|
@ -56,19 +56,14 @@ function(retrieve_files out_files)
|
|||||||
set(${out_files} ${RESULT} PARENT_SCOPE)
|
set(${out_files} ${RESULT} PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
set(SHADER_CPP_GENERATED_CMAKE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateShaderCPPFile.cmake" CACHE STRING "" FORCE)
|
|
||||||
|
|
||||||
# setup portaudio
|
# setup portaudio
|
||||||
set(PA_USE_ASIO ON CACHE BOOL "" FORCE)
|
set(RTAUDIO_API_ASIO ON CACHE BOOL "" FORCE)
|
||||||
set(PA_BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(PA_USE_WMME OFF CACHE BOOL "" FORCE)
|
set(RTAUDIO_API_WASAPI ON CACHE BOOL "" FORCE)
|
||||||
set(PA_USE_WDMKS OFF CACHE BOOL "" FORCE)
|
set(RTAUDIO_API_DS OFF CACHE BOOL "" FORCE)
|
||||||
set(PA_USE_WDMKS_DEVICE_INFO OFF CACHE BOOL "" FORCE)
|
|
||||||
set(PA_USE_DS OFF CACHE BOOL "" FORCE)
|
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
set(PA_USE_COREAUDIO ON CACHE BOOL "" FORCE)
|
set(RTAUDIO_API_CORE ON CACHE BOOL "" FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# setup spdlog
|
# setup spdlog
|
||||||
@ -83,7 +78,7 @@ set(SPDLOG_ENABLE_PCH ON CACHE BOOL "" FORCE)
|
|||||||
set(SPDLOG_USE_STD_FORMAT OFF CACHE BOOL "" FORCE)
|
set(SPDLOG_USE_STD_FORMAT OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
link_directories(${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
|
link_directories(${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# setup taskflow
|
# setup taskflow
|
||||||
@ -91,12 +86,7 @@ set(TF_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
|||||||
set(TF_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
set(TF_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
add_subdirectory(third_party/portaudio)
|
add_subdirectory(third_party/rtaudio)
|
||||||
add_subdirectory(third_party/spdlog)
|
add_subdirectory(third_party/spdlog)
|
||||||
add_subdirectory(third_party/mempool)
|
add_subdirectory(third_party/mempool)
|
||||||
add_subdirectory(third_party/taskflow)
|
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)
|
|
||||||
|
@ -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()
|
|
@ -8,10 +8,10 @@ retrieve_files(ALL_FILES)
|
|||||||
add_library(${PROJECT_NAME} SHARED ${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_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} rtaudio spdlog mempool Taskflow)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC PortAudio spdlog mempool Taskflow)
|
target_link_libraries(${PROJECT_NAME} PUBLIC rtaudio spdlog mempool Taskflow)
|
||||||
|
|
||||||
target_precompile_headers(${PROJECT_NAME} PUBLIC extern.h)
|
target_precompile_headers(${PROJECT_NAME} PUBLIC extern.h)
|
||||||
add_definitions(-Dcore_EXPORTS)
|
add_definitions(-Dcore_EXPORTS)
|
||||||
|
@ -1 +0,0 @@
|
|||||||
#include "audio_device.h"
|
|
@ -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;
|
|
||||||
};
|
|
@ -1,46 +1,53 @@
|
|||||||
#include "audio_device_manager.h"
|
#include "audio_device_manager.h"
|
||||||
|
|
||||||
#include "dummy_audio_device.h"
|
|
||||||
#include "port_audio_device.h"
|
|
||||||
#include "audio/mixer/mixer.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) {
|
void audio_device_manager::init(singleton_initliazer& initliazer) {
|
||||||
singleton_t<audio_device_manager>::init(initliazer);
|
singleton_t<audio_device_manager>::init(initliazer);
|
||||||
|
options_.flags = RTAUDIO_NONINTERLEAVED;
|
||||||
dummy_audio_device_ = new dummy_audio_device();
|
|
||||||
main_audio_device_ = new port_audio_device();
|
|
||||||
|
|
||||||
main_audio_device_->init();
|
|
||||||
dummy_audio_device_->init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_device_manager::release(singleton_release_guard& release_guard) {
|
void audio_device_manager::release(singleton_release_guard& release_guard) {
|
||||||
singleton_t<audio_device_manager>::release(release_guard);
|
singleton_t<audio_device_manager>::release(release_guard);
|
||||||
release_guard.require_release<mixer>();
|
release_guard.require_release<mixer>();
|
||||||
delete dummy_audio_device_;
|
|
||||||
delete main_audio_device_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_device_manager::start() {
|
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 {
|
uint32_t audio_device_manager::get_sample_rate() const {
|
||||||
return main_audio_device_->sample_rate();
|
return sample_rate_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_device_manager::set_sample_rate(double sample_rate) {
|
void audio_device_manager::set_sample_rate(uint32_t sample_rate) {
|
||||||
main_audio_device_->set_sample_rate(sample_rate);
|
sample_rate_ = sample_rate;
|
||||||
dummy_audio_device_->set_sample_rate(sample_rate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t audio_device_manager::get_buffer_size() const {
|
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) {
|
void audio_device_manager::set_buffer_size(int buffer_size) {
|
||||||
main_audio_device_->set_buffer_size(buffer_size);
|
options_.numberOfBuffers = buffer_size;
|
||||||
dummy_audio_device_->set_buffer_size(buffer_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t audio_device_manager::get_input_channel_count() const {
|
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 {
|
uint32_t audio_device_manager::get_output_channel_count() const {
|
||||||
return 2; // 现在是固定值, 以后可能会改
|
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");
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "misc/singleton/singleton.h"
|
#include "misc/singleton/singleton.h"
|
||||||
|
|
||||||
class dummy_audio_device;
|
#include "audio/misc/circular_audio_buffer.h"
|
||||||
class port_audio_device;
|
#include "RtAudio.h"
|
||||||
|
|
||||||
class audio_device_manager : public singleton_t<audio_device_manager> {
|
class audio_device_manager : public singleton_t<audio_device_manager> {
|
||||||
public:
|
public:
|
||||||
@ -13,15 +13,30 @@ public:
|
|||||||
|
|
||||||
CORE_API [[nodiscard]] const char* get_name() override { return "audio_device_manager"; }
|
CORE_API [[nodiscard]] const char* get_name() override { return "audio_device_manager"; }
|
||||||
|
|
||||||
CORE_API [[nodiscard]] double get_sample_rate() const;
|
CORE_API [[nodiscard]] uint32_t get_sample_rate() const;
|
||||||
CORE_API void set_sample_rate(double sample_rate);
|
CORE_API void set_sample_rate(uint32_t sample_rate);
|
||||||
CORE_API [[nodiscard]] uint32_t get_buffer_size() const;
|
CORE_API [[nodiscard]] uint32_t get_buffer_size() const;
|
||||||
CORE_API void set_buffer_size(int buffer_size);
|
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_input_channel_count() const;
|
||||||
CORE_API [[nodiscard]] uint32_t get_output_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:
|
private:
|
||||||
port_audio_device* main_audio_device_ = nullptr;
|
RtAudio::StreamParameters input_params_ = {};
|
||||||
dummy_audio_device* dummy_audio_device_ = nullptr;
|
RtAudio::StreamParameters output_params_ = {};
|
||||||
|
RtAudio::StreamOptions options_ = {};
|
||||||
|
uint32_t sample_rate_ = 44100;
|
||||||
|
RtAudio audio_;
|
||||||
};
|
};
|
||||||
|
|
||||||
DEFINE_SINGLETON_INSTANCE(audio_device_manager)
|
DEFINE_SINGLETON_INSTANCE(audio_device_manager)
|
||||||
|
@ -1 +0,0 @@
|
|||||||
#include "dummy_audio_device.h"
|
|
@ -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; }
|
|
||||||
};
|
|
@ -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");
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
@ -9,10 +9,10 @@
|
|||||||
#include "thread_message/thread_message_hubs.h"
|
#include "thread_message/thread_message_hubs.h"
|
||||||
#include "taskflow/taskflow.hpp"
|
#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];
|
int32_t& track_current_layer = processed_tracks[track];
|
||||||
auto& input_channel_nodes = interface->input_channel_nodes;
|
auto& input_channel_nodes = in_interface->input_channel_nodes;
|
||||||
auto& output_channel_nodes = interface->output_channel_nodes;
|
auto& output_channel_nodes = in_interface->output_channel_nodes;
|
||||||
// 如果这个效果器需要从其他轨道输入,那么目标轨道的深度就是这个轨道的深度+1
|
// 如果这个效果器需要从其他轨道输入,那么目标轨道的深度就是这个轨道的深度+1
|
||||||
for (int i = 1; i < input_channel_nodes.size(); ++i)
|
for (int i = 1; i < input_channel_nodes.size(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
#ifdef core_EXPORTS
|
#define CORE_API
|
||||||
#define CORE_API __declspec(dllexport)
|
// #ifdef core_EXPORTS
|
||||||
#else
|
// #define CORE_API __declspec(dllexport)
|
||||||
#define CORE_API __declspec(dllimport)
|
// #else
|
||||||
#endif
|
// #define CORE_API __declspec(dllimport)
|
||||||
|
// #endif
|
||||||
|
|
||||||
#if defined(_DEBUG) || defined(DEBUG)
|
#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__)); }
|
#define checkf(expr, msg, ...) if (!(expr)) { spdlog::error(msg, __VA_ARGS__); throw std::runtime_error(fmt::format(msg, __VA_ARGS__)); }
|
||||||
|
1
third_party/portaudio
vendored
1
third_party/portaudio
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit daaf637f6f9fce670031221abfd7dfde92e5cce3
|
|
1
third_party/rtaudio
vendored
Submodule
1
third_party/rtaudio
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit cb03db3c7e474c843444631e842468d5b258d095
|
2
third_party/taskflow
vendored
2
third_party/taskflow
vendored
@ -1 +1 @@
|
|||||||
Subproject commit a8434d095ff60c53583646e80b6c57c688ee2a4f
|
Subproject commit 10dfa7920d1a786099a92614c4183f6339975728
|
Loading…
x
Reference in New Issue
Block a user