Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add pitch / playback speed #50

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion js/audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function stop(playback) {
}
}

function audio_play_buffer(sound_key, volume, repeat) {
function audio_play_buffer(sound_key, volume, pitch, repeat) {
let playback_key = playback_key_next++;

let pb = recycle_playback();
Expand All @@ -127,6 +127,7 @@ function audio_play_buffer(sound_key, volume, repeat) {

pb.gain_node.gain.value = volume;
pb.source.loop = repeat;
pb.source.playbackRate.value = pitch;

pb.ended = function() {
stop(pb);
Expand Down Expand Up @@ -177,6 +178,14 @@ function audio_playback_set_volume(playback_key, volume) {
}
}

function audio_playback_set_pitch(playback_key, pitch) {
let playback = playbacks.find(playback => playback.playback_key === playback_key);

if (playback != null) {
playback.source.playbackRate.value = pitch;
}
}

function register_plugin(importObject) {
importObject.env.audio_init = audio_init;
importObject.env.audio_add_buffer = audio_add_buffer;
Expand All @@ -187,6 +196,7 @@ function register_plugin(importObject) {
importObject.env.audio_source_delete = audio_source_delete;
importObject.env.audio_playback_stop = audio_playback_stop;
importObject.env.audio_playback_set_volume = audio_playback_set_volume;
importObject.env.audio_playback_set_pitch = audio_playback_set_pitch;
}

miniquad_add_plugin({ register_plugin, version: "0.1.0", name: "macroquad_audio" });
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ pub use snd::{AudioContext, Playback, Sound};
pub struct PlaySoundParams {
pub looped: bool,
pub volume: f32,
pub pitch: f32,
}

impl Default for PlaySoundParams {
fn default() -> PlaySoundParams {
PlaySoundParams {
looped: false,
volume: 1.,
pitch: 1.,
}
}
}
28 changes: 22 additions & 6 deletions src/mixer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ use std::sync::mpsc;

enum AudioMessage {
AddSound(u32, Vec<f32>),
Play(u32, u32, bool, f32),
Play(u32, u32, bool, f32, f32),
Stop(u32),
StopAll(u32),
SetVolume(u32, f32),
SetPitch(u32, f32),
SetVolumeAll(u32, f32),
Delete(u32),
}
Expand All @@ -23,6 +24,7 @@ pub struct SoundState {
data: Arc<[f32]>,
looped: bool,
volume: f32,
pitch: f32,
}

impl SoundState {
Expand Down Expand Up @@ -95,6 +97,7 @@ impl MixerControl {
play_id,
params.looped,
params.volume,
params.pitch,
));

self.play_id.set(play_id + 1);
Expand Down Expand Up @@ -155,7 +158,7 @@ impl Mixer {
AudioMessage::AddSound(id, data) => {
self.sounds.insert(id, data.into());
}
AudioMessage::Play(sound_id, play_id, looped, volume) => {
AudioMessage::Play(sound_id, play_id, looped, volume, pitch) => {
if let Some(data) = self.sounds.get(&sound_id) {
self.mixer_state.push(SoundState {
sound_id,
Expand All @@ -164,6 +167,7 @@ impl Mixer {
data: data.clone(),
looped,
volume,
pitch,
});
}
}
Expand Down Expand Up @@ -194,6 +198,12 @@ impl Mixer {
sound.volume = volume;
}
}
AudioMessage::SetPitch(play_id, pitch) => {
if let Some(sound) = self.mixer_state.iter_mut().find(|s| s.play_id == play_id)
{
sound.pitch = pitch;
}
}
AudioMessage::Delete(sound_id) => {
for i in (0..self.mixer_state.len()).rev() {
if self.mixer_state[i].sound_id == sound_id {
Expand All @@ -212,17 +222,23 @@ impl Mixer {
let mut i = 0;

while let Some(sound) = self.mixer_state.get_mut(i) {
let pitch = sound.pitch;
let volume = sound.volume;
let mut remainder = buffer.len();

loop {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not quite sure if this is enough - from what I understand, without interpolation+some filtering, pitch by simple re-sampling with nearest neighbor scaling does not sound very good.

Also I am not sure if effects belong to the fill_audio_buffer function

Copy link
Author

@RehkitzDev RehkitzDev May 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added linear interpolation between sample entries. I don't hear much of a difference.

let samples = sound.get_samples(remainder);

for (b, s) in buffer.iter_mut().zip(samples) {
*b += s * volume;
let samples = sound.get_samples((remainder as f32 * pitch).ceil() as usize);

let len = usize::min((samples.len() as f32 / pitch) as usize, remainder);

let mut sample_index = 0.;
for i in 0..len {
buffer[i] += samples[sample_index as usize] * volume;
sample_index += pitch;
}

remainder -= samples.len();
remainder -= len;

if remainder > 0 && sound.looped {
sound.rewind();
Expand Down
9 changes: 7 additions & 2 deletions src/web_snd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ use crate::PlaySoundParams;
extern "C" {
fn audio_init();
fn audio_add_buffer(content: *const u8, content_len: u32) -> u32;
fn audio_play_buffer(buffer: u32, volume: f32, repeat: bool) -> u32;
fn audio_play_buffer(buffer: u32, volume: f32, pitch: f32, repeat: bool) -> u32;
fn audio_source_is_loaded(buffer: u32) -> bool;
fn audio_source_set_volume(buffer: u32, volume: f32);
fn audio_source_stop(buffer: u32);
fn audio_source_delete(buffer: u32);
fn audio_playback_stop(playback: u32);
fn audio_playback_set_volume(playback: u32, volume: f32);
fn audio_playback_set_pitch(playback: u32, pitch: f32);
}

#[no_mangle]
Expand Down Expand Up @@ -45,6 +46,10 @@ impl Playback {
pub fn set_volume(&self, _ctx: &AudioContext, volume: f32) {
unsafe { audio_playback_set_volume(self.0, volume) }
}

pub fn set_pitch(&self, _ctx: &AudioContext, pitch: f32) {
unsafe { audio_playback_set_pitch(self.0, pitch) }
}
}

impl Sound {
Expand All @@ -67,7 +72,7 @@ impl Sound {
}

pub fn play(&self, _ctx: &AudioContext, params: PlaySoundParams) -> Playback {
let id = unsafe { audio_play_buffer(self.0, params.volume, params.looped) };
let id = unsafe { audio_play_buffer(self.0, params.volume, params.pitch, params.looped) };

Playback(id)
}
Expand Down