StepTNT
StepTNT

Reputation: 3967

Toggling audio in an UWP app

I'm building an app with optional audio support, and it will feature some kind of background sounds (like a looping soundtrack that keeps on playing in every page).

What I did is to create an audio manager that allows me to manage my sounds, and this should mute/unmute the audio based on user's settings.

Unfortunately this doesn't happen, and the audio keeps on playing even if the user disables it.

This is a sample of my code:

public static async Task StartSoundManager()
{            
    // Get audio stream from app folder
    // ...
    BackgroundSound.SetSource(currentStream, currentFile.ContentType);
    BackgroundSound.IsLooping = true;  
    ToggleSounds();
}

public static void ToggleSounds()
{                        
    BackgroundSound.IsMuted = !Settings.IsAudioOn;
}

public bool IsAudioOn
{
    // standard getter
    set
    {
        // save value
        SoundManager.ToggleSounds();
    }
}

After some tests, IsMuted is set correctly (I've also tried setting volume to 0) but nothing happens when changing settings.

Do you guys have any idea on why such a simple task is not working as expected? It seems to me that you can't change volume after setting the source, and this feels really wrong.

EDIT: more complete class

public static class AudioManager
{

    public const string BACKGROUND = "BACKGROUND.mp3";

    private static readonly MediaElement BackgroundSound = new MediaElement();

    public static async Task StartSoundManager()
    {
        // Get folder
        var folder =
                await (await Package.Current.InstalledLocation.GetFolderAsync("Assets")).GetFolderAsync("Audio");

        var currentFile = await folder.GetFileAsync(BACKGROUND);
        var currentStream = await currentFile.OpenAsync(FileAccessMode.Read);
        BackgroundSound.SetSource(currentStream, currentFile.ContentType);

        // Set mode and volume
        BackgroundSound.IsLooping = true;
        ToggleSounds();
    }

    public static void ToggleSounds()
    {   
        BackgroundSound.IsMuted = !Settings.IsAudioOn; // IsAudioOn is false, still the sound plays
    }

}

Upvotes: 0

Views: 1222

Answers (2)

Gary93730
Gary93730

Reputation: 441

I have a similar situation. I have a ToggleMenuFlyoutItem for turning sounds on and off app-wide that writes to the local settings:

toggle menu flyout

Here is the XAML:

        <Button IsTabStop="False" Style="{StaticResource qButtonStyleFinal}" TabIndex="2" x:Name="btnMore" BorderBrush="{x:Null}" Foreground="{x:Null}"
            Content="1" Grid.Column="37" Margin="5" HorizontalAlignment="Center" Grid.Row="1" Grid.RowSpan="4" Grid.ColumnSpan="2" MaxWidth="60" MaxHeight="60">
        <Button.Background>
            <ImageBrush ImageSource="ms-appx:///Assets/more_project.png" Stretch="Uniform" />
        </Button.Background>
        <Button.Flyout>
            <MenuFlyout>
                <ToggleMenuFlyoutItem x:Name="mfiToggleSounds" Text="sound effects" IsChecked="{x:Bind Mode=TwoWay, Path=Model.Sounds}"></ToggleMenuFlyoutItem>
                <MenuFlyoutItem x:Name="mfiExportExcel" Text="export to Excel" Tapped="mfiExportExcel_Tapped" />
                <MenuFlyoutItem x:Name="mfiExportCSV" Text="export to CSV" Tapped="mfiExportCSV_Tapped" />
                <MenuFlyoutItem x:Name="mfiEmailDeveloper" Text="email developer" Tapped="mfiEmailDeveloper_Tapped" />
            </MenuFlyout>
        </Button.Flyout>
    </Button>

Changes are handled in the property set event:

    private bool _sounds = true;

        public bool Sounds
    {
        get => _sounds;

        set
        {
            _sounds = value;
            NotifyPropertyChanged();
            var localSettings = ApplicationData.Current.LocalSettings;
            localSettings.Values["sounds"] = _sounds;
        }
    }

Setting is loaded on the main page Page_Loaded event as follows:

            // retrieve settings
        var localSettings = ApplicationData.Current.LocalSettings;
        if (localSettings.Values.ContainsKey("sounds"))
        {
            clsGlobal.statModel.Sounds = (bool)localSettings.Values["sounds"];
        }

And when I play a sound, a simple if condition is included:

if (clsGlobal.statModel.Sounds && clsGlobal.fileInputs.ContainsKey("problem.wav"))
                {
                    var snd1 = clsGlobal.fileInputs["problem.wav"];
                    if (snd1 != null)
                    {
                        snd1.Reset();
                        snd1.Start();
                    }
                }

I am using the AudioGraph API for my sounds. Works better than the MediaPlayer route since that was giving me a weird situation where the sounds would not play sometimes on the first request. AudioGraph works better for me and is really fast to play sounds.

Upvotes: 0

Jay Zuo
Jay Zuo

Reputation: 15758

MediaElement is a XAML control, to make MediaElement.IsMuted property work, we need to add MediaElement into Visual Tree. For example, in your code, we can change BackgroundSound to a public field like:

public static readonly MediaElement BackgroundSound = new MediaElement();

And then in a page (e.g. MainPage) add it to the page:

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    await AudioManager.StartSoundManager();

    rootGrid.Children.Add(AudioManager.BackgroundSound);
}

After this, your ToggleSounds method should be able to work.

But since you want to keep on playing in every page, add MediaElement into the page may be not a good practice, here I'd suggest you use MediaPlayer class instead of MediaElement like:

public static class AudioManager
{
    public const string BACKGROUND = "BACKGROUND.mp3";

    private static readonly MediaPlayer BackgroundSound = new MediaPlayer();

    public static void StartSoundManager()
    {
        BackgroundSound.Source = MediaSource.CreateFromUri(new Uri($"ms-appx:///Assets/Audio/{BACKGROUND}"));

        BackgroundSound.IsLoopingEnabled = true;

        ToggleSounds();

        BackgroundSound.Play();
    }

    public static void ToggleSounds()
    {
        BackgroundSound.IsMuted = !Settings.IsAudioOn; // IsAudioOn is false, still the sound plays
    }
}

For more info, please see Play audio and video with MediaPlayer.

Upvotes: 1

Related Questions