1/**************************************************************************/
2/* audio_driver_dummy.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "audio_driver_dummy.h"
32
33#include "core/config/project_settings.h"
34#include "core/os/os.h"
35
36AudioDriverDummy *AudioDriverDummy::singleton = nullptr;
37
38Error AudioDriverDummy::init() {
39 active.clear();
40 exit_thread.clear();
41 samples_in = nullptr;
42
43 if (mix_rate == -1) {
44 mix_rate = _get_configured_mix_rate();
45 }
46
47 channels = get_channels();
48 samples_in = memnew_arr(int32_t, (size_t)buffer_frames * channels);
49
50 if (use_threads) {
51 thread.start(AudioDriverDummy::thread_func, this);
52 }
53
54 return OK;
55}
56
57void AudioDriverDummy::thread_func(void *p_udata) {
58 AudioDriverDummy *ad = static_cast<AudioDriverDummy *>(p_udata);
59
60 uint64_t usdelay = (ad->buffer_frames / float(ad->mix_rate)) * 1000000;
61
62 while (!ad->exit_thread.is_set()) {
63 if (ad->active.is_set()) {
64 ad->lock();
65 ad->start_counting_ticks();
66
67 ad->audio_server_process(ad->buffer_frames, ad->samples_in);
68
69 ad->stop_counting_ticks();
70 ad->unlock();
71 }
72
73 OS::get_singleton()->delay_usec(usdelay);
74 }
75}
76
77void AudioDriverDummy::start() {
78 active.set();
79}
80
81int AudioDriverDummy::get_mix_rate() const {
82 return mix_rate;
83}
84
85AudioDriver::SpeakerMode AudioDriverDummy::get_speaker_mode() const {
86 return speaker_mode;
87}
88
89void AudioDriverDummy::lock() {
90 mutex.lock();
91}
92
93void AudioDriverDummy::unlock() {
94 mutex.unlock();
95}
96
97void AudioDriverDummy::set_use_threads(bool p_use_threads) {
98 use_threads = p_use_threads;
99}
100
101void AudioDriverDummy::set_speaker_mode(SpeakerMode p_mode) {
102 speaker_mode = p_mode;
103}
104
105void AudioDriverDummy::set_mix_rate(int p_rate) {
106 mix_rate = p_rate;
107}
108
109uint32_t AudioDriverDummy::get_channels() const {
110 static const int channels_for_mode[4] = { 2, 4, 8, 16 };
111 return channels_for_mode[speaker_mode];
112}
113
114void AudioDriverDummy::mix_audio(int p_frames, int32_t *p_buffer) {
115 ERR_FAIL_COND(!active.is_set()); // If not active, should not mix.
116 ERR_FAIL_COND(use_threads == true); // If using threads, this will not work well.
117
118 uint32_t todo = p_frames;
119 while (todo) {
120 uint32_t to_mix = MIN(buffer_frames, todo);
121 lock();
122 audio_server_process(to_mix, samples_in);
123 unlock();
124
125 uint32_t total_samples = to_mix * channels;
126
127 for (uint32_t i = 0; i < total_samples; i++) {
128 p_buffer[i] = samples_in[i];
129 }
130
131 todo -= to_mix;
132 p_buffer += total_samples;
133 }
134}
135
136void AudioDriverDummy::finish() {
137 if (use_threads) {
138 exit_thread.set();
139 if (thread.is_started()) {
140 thread.wait_to_finish();
141 }
142 }
143
144 if (samples_in) {
145 memdelete_arr(samples_in);
146 }
147}
148
149AudioDriverDummy::AudioDriverDummy() {
150 singleton = this;
151}
152