Skip to content

Commit

Permalink
implement start and end timestamps as options
Browse files Browse the repository at this point in the history
  • Loading branch information
skomski committed Jul 14, 2020
1 parent 1b243cf commit 3538856
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 19 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Expand Up @@ -21,11 +21,11 @@ autoloop = ["libpulse-binding", "ctrlc"]
default = ["mp3", "flac", "vorbis", "wav", "http", "gui", "terminal-ui"]
flac = ["claxon"]
gui = ["iced", "iced_native"]
terminal-ui = ["tui", "crossterm"]
http = ["warp", "futures"]
mp3 = ["minimp3", "mp3-duration"]
opus = ["audiopus", "ogg"]
telegram = ["tgbot", "fuzzy-matcher"]
terminal-ui = ["tui", "crossterm"]
vorbis = ["lewton", "ogg_metadata"]
wav = ["hound"]
xm = ["libxm-soundboard"]
Expand Down Expand Up @@ -73,8 +73,8 @@ mp3-duration = {version = "0.1.10", optional = true}
ogg = {version = "0.7", optional = true}
ogg_metadata = {version = "0.4", optional = true}

crossterm = {version = "0.17", optional = true}
tui = {version = "0.9", optional = true, default-features = false, features = ['crossterm']}
crossterm = {version = "0.17", optional = true}

[build-dependencies]
fs_extra = "1"
8 changes: 4 additions & 4 deletions soundboard.toml
Expand Up @@ -2,7 +2,7 @@
# output_device = "Speaker/HP (Realtek High Definition Audio(SST))" # optional else default device
# loopback_device = "CABLE Input (VB-Audio Virtual Cable)" # required: change to your virtual loopback output

http_server = true # api and webui; 3030 is the default port
no_gui = false # no native gui
stop_hotkey = "CTRL-ALT-E" # stop all sound
disable_simultaneous_playback = true # stop currently playing sounds when playing a new sound
disable_simultaneous_playback = false # stop currently playing sounds when playing a new sound
http_server = true # api and webui; 3030 is the default port
no_gui = false # no native gui
stop_hotkey = "CTRL-ALT-E" # stop all sound
11 changes: 6 additions & 5 deletions soundboards/favorites.toml
Expand Up @@ -2,12 +2,10 @@ name = 'favorites'
position = 0

[[sound]]
hotkey = 'CTRL-SHIFT-3'
name = 'Krasser Kai Uwe'
path = 'krasserKaiUwe.mp3'

[[sound]]
hotkey = 'CTRL-2'
name = 'Ich rascht aus'
path = 'ich-rascht-aus.flac'

Expand All @@ -32,7 +30,12 @@ name = 'Hierhoch'
path = 'https://alleswirdaushackgemacht.de/audio/ronny/hierhoch.mp3'

[[sound]]
hotkey = 'CTRL-1'
name = 'wo sind die brillen'
path = 'wo ist der latest shit.mp3'
start = 18

[[sound]]
end = 6.7
name = 'wo ist der latest shit'
path = 'wo ist der latest shit.mp3'

Expand All @@ -41,7 +44,6 @@ name = 'Wer wird Millionär - Showstart'
path = 'https://www.myinstants.com//media/sounds/wer-wird-millionar-soundtracks-soundstart.mp3'

[[sound]]
hotkey = 'CTRL-3'
name = 'Summasummarum'
path = 'Summasummarum.mp3'

Expand All @@ -59,7 +61,6 @@ name = 'the one and only'
path = 'oneandonly.wav'

[[sound]]
hotkey = 'CTRL-ALT-S'
name = 'steam incoming'
path = 'https://www.myinstants.com/media/sounds/message_2.mp3'

Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Expand Up @@ -162,6 +162,8 @@ pub struct SoundConfig {
pub name: String,
pub path: String,
pub hotkey: Option<String>,
pub start: Option<f32>,
pub end: Option<f32>,
#[serde(rename = "header")]
pub headers: Option<Vec<HeaderConfig>>,

Expand All @@ -175,6 +177,8 @@ impl PartialEq for SoundConfig {
&& self.hotkey == other.hotkey
&& self.path == other.path
&& self.headers == other.headers
&& self.start == other.start
&& self.end == other.end
}
}
impl Eq for SoundConfig {}
Expand Down
2 changes: 1 addition & 1 deletion src/gui/panel_view.rs
Expand Up @@ -84,7 +84,7 @@ impl PanelView {
self.panes.iter_mut().for_each(|(_, state)| {
if let Some(sound) = sounds
.iter()
.find(|(_, s, _, _)| s.path == state.sound_button.config.path)
.find(|(_, s, _, _)| *s == state.sound_button.config)
{
state.playing = true;
state.status = sound.0;
Expand Down
7 changes: 6 additions & 1 deletion src/hotkey.rs
Expand Up @@ -34,6 +34,11 @@ impl HotkeyManager {
where
F: 'static + FnMut() + Send,
{
let position = self.registered_hotkeys.iter().position(|h| h == &hotkey);
if position.is_some() {
return Err(anyhow!("hotkey already registered {}", hotkey));
}

let hotkey_clone = hotkey.clone();
match GLOBAL_HOTKEY_MAP.lock().entry(hotkey.clone()) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
Expand Down Expand Up @@ -68,7 +73,7 @@ impl HotkeyManager {
pub fn unregister(&mut self, hotkey: &config::Hotkey) -> Result<()> {
let position = self.registered_hotkeys.iter().position(|h| h == hotkey);
if position.is_none() {
return Err(anyhow!("no hotkey registered with this id"));
return Err(anyhow!("hotkey not registered {}", hotkey));
}
self.registered_hotkeys.remove(position.unwrap());

Expand Down
4 changes: 4 additions & 0 deletions src/http_server.rs
Expand Up @@ -439,6 +439,8 @@ pub async fn run(
path: upload_name,
hotkey: None,
headers: None,
start: None,
end: None,
full_path: sound_path.to_str().unwrap().to_owned(),
};

Expand Down Expand Up @@ -571,6 +573,8 @@ pub async fn run(
hotkey: sound_add_request.hotkey,
headers: None,
full_path: sound_add_request.path,
start: None,
end: None,
},
);

Expand Down
63 changes: 62 additions & 1 deletion src/sound.rs
Expand Up @@ -6,6 +6,7 @@ use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use std::thread::JoinHandle;
use std::time::Duration;

use super::config;
use super::download;
Expand Down Expand Up @@ -197,6 +198,8 @@ struct SoundKey {
pub path: String,
pub hotkey: Option<String>,
pub headers: Option<Vec<config::HeaderConfig>>,
pub start: Option<f32>,
pub end: Option<f32>,
}

impl From<config::SoundConfig> for SoundKey {
Expand All @@ -206,6 +209,8 @@ impl From<config::SoundConfig> for SoundKey {
headers: sound_config.headers,
name: sound_config.name,
hotkey: sound_config.hotkey,
start: sound_config.start,
end: sound_config.end,
}
}
}
Expand Down Expand Up @@ -290,11 +295,65 @@ fn insert_sink_with_config(
sound_config, device_name
);

if let Some(start) = sound_config.start {
if start < 0.0 {
return Err(anyhow!("error: start timestamp is negative {}", start));
}
}

if let Some(end) = sound_config.end {
if end < 0.0 {
return Err(anyhow!("error: end timestamp is negative {}", end));
}
}

let reader = std::io::BufReader::with_capacity(1000 * 50, std::fs::File::open(path)?);
let mut decoder = Decoder::new(reader)?;
let mut reader = std::io::BufReader::with_capacity(1000 * 50, std::fs::File::open(path)?);
let total_duration = decoder.total_duration_mut(&mut reader);
sink.play(sound_config.clone().into(), decoder);
let total_duration = match (total_duration, sound_config.start, sound_config.end) {
(Some(total_duration), Some(start), None) => {
if let Some(duration) = total_duration.checked_sub(Duration::from_secs_f32(start)) {
Some(duration)
} else {
return Err(anyhow!(
"error: total_duration - supplied start timestamp is negative"
));
}
}
(Some(total_duration), None, None) => Some(total_duration),
(None, None, Some(end)) => Some(Duration::from_secs_f32(end)),
(Some(total_duration), None, Some(end)) => {
let end = Duration::from_secs_f32(end);
if total_duration < end {
Some(total_duration)
} else {
Some(end)
}
}
(total_duration, Some(start), Some(mut end)) => {
if let Some(total_duration) = total_duration {
if total_duration.as_secs_f32() < end {
end = total_duration.as_secs_f32();
}
}
if end - start < 0.0 {
return Err(anyhow!(
"error: end - start duration is negative start: {} end: {}",
start,
end
));
}
Some(Duration::from_secs_f32(end - start))
}
(None, _, None) => None,
};
sink.play(
sound_config.clone().into(),
decoder,
sound_config.start,
sound_config.end,
)?;

match sinks.entry(sound_config.into()) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
Expand Down Expand Up @@ -474,6 +533,8 @@ fn run_sound_message_loop(
headers: id.headers.clone(),
hotkey: id.hotkey.clone(),
full_path: String::new(),
start: id.start,
end: id.end,
},
instant.elapsed(),
*total_duration,
Expand Down
59 changes: 54 additions & 5 deletions src/sound/sink.rs
Expand Up @@ -24,7 +24,9 @@ unsafe impl Sync for ConverterWrapper {}
unsafe impl Send for ConverterWrapper {}

type SourcesType<T, S> = std::sync::Arc<
parking_lot::Mutex<HashMap<T, Vec<(S, VecDeque<i16>, Option<ConverterWrapper>)>>>,
parking_lot::Mutex<
HashMap<T, Vec<(S, VecDeque<i16>, Option<ConverterWrapper>, f32, f32, f32)>>,
>,
>;

pub struct Sink<T, S>
Expand Down Expand Up @@ -71,7 +73,26 @@ where
let mut unlocked = hash_map_clone.lock();

for (key, sources) in unlocked.iter_mut() {
for (source, buffer, resampler) in sources {
for (source, buffer, resampler, start, end, current_duration) in sources {
if *start > 0.0 {
source
.skip(
((*start * source.sample_rate() as f32) * source.channels() as f32)
as usize,
)
.next();
*current_duration = *start;
*start = 0.0;
}
if current_duration >= end {
remove_keys.push(key.clone());
continue;
}

*current_duration += ((output.sample_count() / output.channels() as usize)
as f32)
/ device.sample_rate() as f32;

if source.sample_rate() != device.sample_rate()
|| source.channels() != output.channels() as u16
{
Expand Down Expand Up @@ -173,17 +194,45 @@ where
})
}

pub fn play(&mut self, key: T, source: S) {
pub fn play(&mut self, key: T, source: S, start: Option<f32>, end: Option<f32>) -> Result<()> {
let mut unlocked = self.sources.lock();
let start_float = {
let start = start.unwrap_or_default();
if start < 0.0 {
return Err(anyhow!("supplied start timestamp is negative {}", start));
}
start
};
let end_float = {
if let Some(end_duration) = end {
if end_duration < 0.0 {
return Err(anyhow!(
"supplied end timestamp is negative {}",
end_duration
));
}
end_duration
} else {
f32::INFINITY
}
};
match unlocked.entry(key) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
let entry = entry.get_mut();
entry.push((source, VecDeque::new(), None));
entry.push((source, VecDeque::new(), None, start_float, end_float, 0.0));
}
std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert(vec![(source, VecDeque::new(), None)]);
entry.insert(vec![(
source,
VecDeque::new(),
None,
start_float,
end_float,
0.0,
)]);
}
}
Ok(())
}

pub fn remove(&mut self, key: &T) {
Expand Down
2 changes: 2 additions & 0 deletions src/telegram.rs
Expand Up @@ -100,6 +100,8 @@ fn send_new_sound_config(
hotkey: None,
full_path: new_sound_path.to_str().unwrap().to_owned(),
path: full_name,
start: None,
end: None,
};

let telegram_soundboard_index = config.soundboards.iter().position(|s| s.name == "telegram");
Expand Down

0 comments on commit 3538856

Please sign in to comment.