Skip to content

Commit 037d134

Browse files
committed
Wrap projectM instance access in a mutex
1 parent 58856fb commit 037d134

File tree

4 files changed

+60
-24
lines changed

4 files changed

+60
-24
lines changed

src/app.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::sync::{Arc, Mutex};
2+
13
use projectm_rs::core::{ProjectMHandle, Projectm};
24
use sdl2::video::GLProfile;
35

@@ -7,9 +9,12 @@ pub mod main_loop;
79
pub mod playlist;
810
pub mod video;
911

12+
/// Thread-safe wrapper around the projectM instance.
13+
pub type ProjectMWrapped = Arc<Mutex<ProjectMHandle>>;
14+
1015
/// Application state
1116
pub struct App {
12-
pm: ProjectMHandle,
17+
pm: ProjectMWrapped,
1318
playlist: projectm_rs::playlist::Playlist,
1419
sdl_context: sdl2::Sdl,
1520
window: sdl2::video::Window,
@@ -65,11 +70,14 @@ impl App {
6570
let (width, height) = window.drawable_size(); // highDPI aware
6671
Projectm::set_window_size(pm, width.try_into().unwrap(), height.try_into().unwrap());
6772

73+
// create a mutex to protect the projectM instance
74+
let pm = Arc::new(Mutex::new(pm));
75+
6876
// initialize audio
69-
let audio = audio::Audio::new(&sdl_context, pm);
77+
let audio = audio::Audio::new(&sdl_context, pm.clone());
7078

7179
Self {
72-
pm,
80+
pm: pm.clone(),
7381
playlist,
7482
sdl_context,
7583
window,

src/app/audio.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use projectm_rs::core::ProjectMHandle;
22
use sdl2::audio::{AudioCallback, AudioDevice, AudioSpecDesired};
3+
use std::sync::Arc;
4+
use std::sync::Mutex;
35

46
use super::config::FrameRate;
7+
use super::ProjectMWrapped;
58

69
type AudioDeviceIndex = u32;
710
type SampleFormat = f32; // format of audio samples
@@ -17,11 +20,12 @@ pub struct Audio {
1720
is_capturing: bool,
1821
frame_rate: Option<FrameRate>,
1922
capturing_device: Option<AudioDevice<AudioCaptureCallback>>,
20-
projectm: ProjectMHandle,
23+
projectm: ProjectMWrapped,
2124
}
2225

26+
/// Wrapper around the audio subsystem to capture audio and pass it to projectM.
2327
impl Audio {
24-
pub fn new(sdl_context: &sdl2::Sdl, projectm: ProjectMHandle) -> Self {
28+
pub fn new(sdl_context: &sdl2::Sdl, projectm: ProjectMWrapped) -> Self {
2529
let audio_subsystem = sdl_context.audio().unwrap();
2630

2731
Self {
@@ -52,7 +56,8 @@ impl Audio {
5256
}
5357
}
5458

55-
pub fn set_device(&mut self, device_index: AudioDeviceIndex) {
59+
/// Start capturing audio from device_index.
60+
pub fn capture_device(&mut self, device_index: AudioDeviceIndex) {
5661
self.stop_audio_capture();
5762
self.device_index = device_index;
5863
self.begin_audio_capture();
@@ -64,12 +69,14 @@ impl Audio {
6469
.expect("could not get audio device")
6570
}
6671

72+
/// Select a new audio device and start capturing audio from it.
6773
pub fn open_next_device(&mut self) {
6874
let device_list = self.get_device_list();
6975
let current_device_index = self.device_index;
7076

71-
let next_device_index = current_device_index + 1 % device_list.len() as AudioDeviceIndex;
72-
self.set_device(next_device_index);
77+
let next_device_index = (current_device_index + 1) % device_list.len() as AudioDeviceIndex;
78+
println!("Opening next device: {}", next_device_index);
79+
self.capture_device(next_device_index);
7380
}
7481

7582
fn get_device_list(&self) -> Vec<AudioCaptureDevice> {
@@ -105,7 +112,8 @@ impl Audio {
105112
samples: Some(buffer_size),
106113
};
107114

108-
let audio_device = self
115+
// open audio device for capture
116+
let audio_device = match self
109117
.audio_subsystem // sdl
110118
.open_capture(None, &desired_spec, |_spec| {
111119
println!(
@@ -118,14 +126,19 @@ impl Audio {
118126

119127
// return callback fn
120128
AudioCaptureCallback {
121-
pm: self.projectm,
129+
pm: self.projectm.clone(),
122130
// spec,
123131
// buffer_size,
124132
// buffer: vec![0; buffer_size as usize],
125133
// position: 0,
126134
}
127-
})
128-
.unwrap();
135+
}) {
136+
Ok(device) => device,
137+
Err(e) => {
138+
println!("Error opening audio device: {}", e);
139+
return;
140+
}
141+
};
129142

130143
// take ownership of device
131144
self.capturing_device = Some(audio_device);
@@ -140,18 +153,21 @@ impl Audio {
140153
println!("Stopping audio capture for device {}", current_device_name);
141154

142155
println!(
143-
"current capture device: {:?}",
156+
"Current capture device status: {:?}",
144157
self.capturing_device.as_ref().unwrap().status()
145158
);
146159

147160
self.is_capturing = false;
148-
// drop(self.capturing_device); // stop capturing
149161
self.capturing_device = None;
150162
}
151163
}
152164

153165
struct AudioCaptureCallback {
154-
pm: ProjectMHandle,
166+
// audio_tx: mpsc::Sender<Vec<SampleFormat>>,
167+
168+
// we need to keep a reference to the projectm instance to
169+
// add the audio data to it
170+
pm: Arc<Mutex<ProjectMHandle>>,
155171
// spec: sdl2::audio::AudioSpec,
156172
// buffer_size: SampleFormat,
157173
// buffer: Vec<u8>,
@@ -166,7 +182,9 @@ impl AudioCallback for AudioCaptureCallback {
166182
// we are receiving some chunk of audio data
167183
// we need to pass it to projectm
168184
fn callback(&mut self, out: &mut [SampleFormat]) {
169-
let pm = self.pm;
170-
projectm_rs::core::Projectm::pcm_add_float(pm, out.to_vec(), 2);
185+
{
186+
let pm = *self.pm.lock().unwrap();
187+
projectm_rs::core::Projectm::pcm_add_float(pm, out.to_vec(), 2);
188+
}
171189
}
172190
}

src/app/config.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,35 +39,38 @@ impl Default for Config {
3939

4040
impl App {
4141
pub fn load_config(&self, config: &Config) {
42+
let pm = *self.pm.lock().unwrap();
43+
4244
// load presets if provided
4345
if let Some(preset_path) = &config.preset_path {
4446
self.add_preset_path(preset_path);
4547
}
4648

4749
// set frame rate if provided
4850
if let Some(frame_rate) = config.frame_rate {
49-
Projectm::set_fps(self.pm, frame_rate)
51+
Projectm::set_fps(pm, frame_rate)
5052
}
5153

5254
// load textures if provided
5355
if let Some(texture_path) = &config.texture_path {
5456
let mut paths: Vec<String> = Vec::new();
5557
paths.push(texture_path.into());
56-
Projectm::set_texture_search_paths(self.pm, &paths, 1);
58+
Projectm::set_texture_search_paths(pm, &paths, 1);
5759
}
5860

5961
// set beat sensitivity if provided
6062
if let Some(beat_sensitivity) = config.beat_sensitivity {
61-
Projectm::set_beat_sensitivity(self.pm, beat_sensitivity);
63+
Projectm::set_beat_sensitivity(pm, beat_sensitivity);
6264
}
6365

6466
// set preset duration if provided
6567
if let Some(preset_duration) = config.preset_duration {
66-
Projectm::set_preset_duration(self.pm, preset_duration);
68+
Projectm::set_preset_duration(pm, preset_duration);
6769
}
6870
}
6971

7072
pub fn get_frame_rate(&self) -> FrameRate {
71-
Projectm::get_fps(self.pm)
73+
let pm = *self.pm.lock().unwrap();
74+
Projectm::get_fps(pm)
7275
}
7376
}

src/app/main_loop.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ impl App {
8181
// Next audio capture input device (ctl-I, cmd-I)
8282
Event::KeyUp {
8383
keycode: Some(Keycode::I),
84-
keymod: sdl2::keyboard::Mod::LCTRLMOD,
84+
keymod:
85+
sdl2::keyboard::Mod::LCTRLMOD
86+
| sdl2::keyboard::Mod::RCTRLMOD
87+
| sdl2::keyboard::Mod::LGUIMOD
88+
| sdl2::keyboard::Mod::RGUIMOD,
8589
..
8690
} => {
8791
self.audio.open_next_device();
@@ -97,7 +101,10 @@ impl App {
97101
dummy_audio::generate_random_audio_data(self.pm);
98102

99103
// render a frame
100-
Projectm::render_frame(self.pm);
104+
{
105+
let pm = *self.pm.lock().unwrap();
106+
Projectm::render_frame(pm);
107+
}
101108

102109
// swap buffers
103110
self.window.gl_swap_window();

0 commit comments

Comments
 (0)