@matjash/pixi-native-win32-x64 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
Binary file
|
|
@@ -7,10 +7,13 @@ use std::thread;
|
|
|
7
7
|
use std::time::{Duration, Instant};
|
|
8
8
|
|
|
9
9
|
use maudio::audio::sample_rate::SampleRate;
|
|
10
|
-
use maudio::data_source::
|
|
10
|
+
use maudio::data_source::data_source_builder::DataSourceBuilder;
|
|
11
|
+
use maudio::data_source::pcm_source::PcmSource;
|
|
11
12
|
use maudio::data_source::sources::pcm_ring_buffer::{PcmRbRecv, PcmRbSend, PcmRingBuffer};
|
|
13
|
+
use maudio::data_source::{DataSource, SourceContext};
|
|
12
14
|
use maudio::engine::{engine_builder::EngineBuilder, Engine};
|
|
13
15
|
use maudio::sound::{notifier::EndNotifier, sound_builder::SoundBuilder, Sound};
|
|
16
|
+
use maudio::{ErrorKinds, MaResult, MaudioError};
|
|
14
17
|
use napi::bindgen_prelude::*;
|
|
15
18
|
use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode};
|
|
16
19
|
use napi_derive::napi;
|
|
@@ -112,9 +115,64 @@ impl StreamingControl {
|
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
|
|
118
|
+
struct SharedPcmSource {
|
|
119
|
+
samples: Arc<Vec<f32>>,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
impl SharedPcmSource {
|
|
123
|
+
fn new(samples: Arc<Vec<f32>>) -> Self {
|
|
124
|
+
Self { samples }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
impl PcmSource<f32> for SharedPcmSource {
|
|
129
|
+
fn fill_pcm_frames(
|
|
130
|
+
&mut self,
|
|
131
|
+
output: &mut [f32],
|
|
132
|
+
context: &mut SourceContext,
|
|
133
|
+
) -> MaResult<usize> {
|
|
134
|
+
let channels = context.data_format.channels as usize;
|
|
135
|
+
let frame_count = self.samples.len() / channels;
|
|
136
|
+
let cursor = usize::try_from(context.cursor).unwrap_or(usize::MAX);
|
|
137
|
+
if cursor >= frame_count {
|
|
138
|
+
return Ok(0);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let output_frames = output.len() / channels;
|
|
142
|
+
let frames_to_copy = output_frames.min(frame_count - cursor);
|
|
143
|
+
let sample_start = cursor * channels;
|
|
144
|
+
let samples_to_copy = frames_to_copy * channels;
|
|
145
|
+
output[..samples_to_copy]
|
|
146
|
+
.copy_from_slice(&self.samples[sample_start..sample_start + samples_to_copy]);
|
|
147
|
+
context.cursor += frames_to_copy as u64;
|
|
148
|
+
Ok(frames_to_copy)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn seek_to_pcm_frame(&mut self, frame_index: u64, context: &mut SourceContext) -> MaResult<()> {
|
|
152
|
+
let frame_count = self.samples.len() / context.data_format.channels as usize;
|
|
153
|
+
if frame_index > frame_count as u64 {
|
|
154
|
+
return Err(MaudioError::new_ma_error(ErrorKinds::InvalidOperation(
|
|
155
|
+
"Audio seek is outside the cached PCM source",
|
|
156
|
+
)));
|
|
157
|
+
}
|
|
158
|
+
context.cursor = frame_index;
|
|
159
|
+
Ok(())
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fn cursor_in_pcm_frames(&self, context: &SourceContext) -> MaResult<u64> {
|
|
163
|
+
Ok(context.cursor)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
fn length_in_pcm_frames(&self, context: &SourceContext) -> MaResult<u64> {
|
|
167
|
+
Ok((self.samples.len() / context.data_format.channels as usize) as u64)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
type StaticDataSource = DataSource<f32, SharedPcmSource>;
|
|
172
|
+
|
|
115
173
|
enum VoiceSource {
|
|
116
174
|
Loading,
|
|
117
|
-
Static(Box<
|
|
175
|
+
Static(Box<StaticDataSource>),
|
|
118
176
|
Streaming {
|
|
119
177
|
receiver: Box<PcmRbRecv<f32>>,
|
|
120
178
|
control: Arc<StreamingControl>,
|
|
@@ -643,8 +701,8 @@ impl EngineRuntime {
|
|
|
643
701
|
let Some(mut voice) = self.voices.remove(&id) else {
|
|
644
702
|
continue;
|
|
645
703
|
};
|
|
646
|
-
let result =
|
|
647
|
-
.map_err(|error| format!("Cannot create miniaudio
|
|
704
|
+
let result = build_static_data_source(samples)
|
|
705
|
+
.map_err(|error| format!("Cannot create miniaudio data source: {error}"))
|
|
648
706
|
.and_then(|buffer| {
|
|
649
707
|
voice.source = VoiceSource::Static(Box::new(buffer));
|
|
650
708
|
self.attach_sound(&mut voice)
|
|
@@ -830,11 +888,12 @@ impl EngineRuntime {
|
|
|
830
888
|
.snapshot
|
|
831
889
|
.queued_frames
|
|
832
890
|
.store(queued, Ordering::Release);
|
|
833
|
-
if control.ready.load(Ordering::Acquire)
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
891
|
+
if control.ready.load(Ordering::Acquire)
|
|
892
|
+
&& !voice.play_announced
|
|
893
|
+
&& sound.play_sound().is_ok()
|
|
894
|
+
{
|
|
895
|
+
voice.play_announced = true;
|
|
896
|
+
state.queue_event(voice.owner_id, "play", Some(*id), None, None);
|
|
838
897
|
}
|
|
839
898
|
let starved = voice.play_announced
|
|
840
899
|
&& voice.playing
|
|
@@ -913,30 +972,25 @@ fn start_static_decode(
|
|
|
913
972
|
preload: bool,
|
|
914
973
|
request_id: Option<u32>,
|
|
915
974
|
) {
|
|
975
|
+
let key = cache_key(&options);
|
|
976
|
+
if let Some(samples) = state
|
|
977
|
+
.cache
|
|
978
|
+
.lock()
|
|
979
|
+
.ok()
|
|
980
|
+
.and_then(|cache| cache.get(&key).cloned())
|
|
981
|
+
{
|
|
982
|
+
complete_static_decode(&state, &options, preload, request_id, samples);
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
|
|
916
986
|
thread::spawn(move || {
|
|
917
|
-
let
|
|
918
|
-
let decoded = state
|
|
919
|
-
.cache
|
|
920
|
-
.lock()
|
|
921
|
-
.ok()
|
|
922
|
-
.and_then(|cache| cache.get(&key).cloned())
|
|
923
|
-
.map_or_else(
|
|
924
|
-
|| decode_static(&options, state.sample_rate).map(Arc::new),
|
|
925
|
-
Ok,
|
|
926
|
-
);
|
|
987
|
+
let decoded = decode_static(&options, state.sample_rate).map(Arc::new);
|
|
927
988
|
match decoded {
|
|
928
989
|
Ok(samples) => {
|
|
929
990
|
if let Ok(mut cache) = state.cache.lock() {
|
|
930
991
|
cache.insert(key, Arc::clone(&samples));
|
|
931
992
|
}
|
|
932
|
-
|
|
933
|
-
state.queue_event(options.owner_id, "load", request_id, None, None);
|
|
934
|
-
} else {
|
|
935
|
-
state.push_command(EngineCommand::AttachStatic {
|
|
936
|
-
id: options.id,
|
|
937
|
-
samples,
|
|
938
|
-
});
|
|
939
|
-
}
|
|
993
|
+
complete_static_decode(&state, &options, preload, request_id, samples);
|
|
940
994
|
}
|
|
941
995
|
Err(message) => {
|
|
942
996
|
if !preload {
|
|
@@ -954,6 +1008,28 @@ fn start_static_decode(
|
|
|
954
1008
|
});
|
|
955
1009
|
}
|
|
956
1010
|
|
|
1011
|
+
fn complete_static_decode(
|
|
1012
|
+
state: &SharedState,
|
|
1013
|
+
options: &NativeVoiceOptions,
|
|
1014
|
+
preload: bool,
|
|
1015
|
+
request_id: Option<u32>,
|
|
1016
|
+
samples: Arc<Vec<f32>>,
|
|
1017
|
+
) {
|
|
1018
|
+
if preload {
|
|
1019
|
+
state.queue_event(options.owner_id, "load", request_id, None, None);
|
|
1020
|
+
} else {
|
|
1021
|
+
state.push_command(EngineCommand::AttachStatic {
|
|
1022
|
+
id: options.id,
|
|
1023
|
+
samples,
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
fn build_static_data_source(samples: Arc<Vec<f32>>) -> MaResult<StaticDataSource> {
|
|
1029
|
+
DataSourceBuilder::new(CHANNELS as u32, SampleRate::Sr48000)
|
|
1030
|
+
.build_f32(SharedPcmSource::new(samples))
|
|
1031
|
+
}
|
|
1032
|
+
|
|
957
1033
|
fn start_streaming_decode(
|
|
958
1034
|
state: Arc<SharedState>,
|
|
959
1035
|
options: NativeVoiceOptions,
|
|
@@ -1233,6 +1309,29 @@ fn redact_credentials(message: &str) -> String {
|
|
|
1233
1309
|
mod tests {
|
|
1234
1310
|
use super::*;
|
|
1235
1311
|
|
|
1312
|
+
#[test]
|
|
1313
|
+
fn static_voices_share_pcm_with_independent_cursors() {
|
|
1314
|
+
let samples = Arc::new(vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6]);
|
|
1315
|
+
let mut first = build_static_data_source(Arc::clone(&samples)).unwrap();
|
|
1316
|
+
let second = build_static_data_source(Arc::clone(&samples)).unwrap();
|
|
1317
|
+
|
|
1318
|
+
assert_eq!(Arc::strong_count(&samples), 3);
|
|
1319
|
+
assert_eq!(first.cursor_in_pcm_frames().unwrap(), 0);
|
|
1320
|
+
assert_eq!(second.cursor_in_pcm_frames().unwrap(), 0);
|
|
1321
|
+
|
|
1322
|
+
assert_eq!(first.read_pcm_frames(1).unwrap().frames(), 1);
|
|
1323
|
+
assert_eq!(first.cursor_in_pcm_frames().unwrap(), 1);
|
|
1324
|
+
assert_eq!(second.cursor_in_pcm_frames().unwrap(), 0);
|
|
1325
|
+
|
|
1326
|
+
first.seek_to_pcm_frame(0).unwrap();
|
|
1327
|
+
assert_eq!(first.cursor_in_pcm_frames().unwrap(), 0);
|
|
1328
|
+
assert!(first.seek_to_pcm_frame(4).is_err());
|
|
1329
|
+
|
|
1330
|
+
drop(first);
|
|
1331
|
+
drop(second);
|
|
1332
|
+
assert_eq!(Arc::strong_count(&samples), 1);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1236
1335
|
#[test]
|
|
1237
1336
|
fn atempo_decomposes_extreme_rates() {
|
|
1238
1337
|
assert_eq!(build_atempo_filter(4.0).unwrap(), "atempo=2,atempo=2");
|