Skip to content

JoeLumbley/Audio-Playback-CS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

74 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Audio Playback C#

Audio Playback C# is a powerful and versatile tool for managing audio playback using the Windows Multimedia API. This application provides a comprehensive set of features for playing audio, making it an essential resource for developers and enthusiasts alike.

002

Key Features:

  • Simultaneous Playback: Harness the full potential of the Windows Multimedia API to play multiple audio files simultaneously, allowing for rich and immersive audio experiences.

  • Volume Control: Customize the volume levels of individual audio tracks with precision, ensuring an optimal audio balance tailored to your specific requirements.

  • Looping and Overlapping: Seamlessly loop audio tracks and play overlapping sounds, enabling the creation of captivating and dynamic audio compositions.

  • MCI Integration: Leverage the power of the Media Control Interface (MCI) to interact with multimedia devices, providing a standardized and platform-independent approach to controlling multimedia hardware.

  • User-Friendly Interface: Enjoy a user-friendly and intuitive interface, designed to streamline the process of managing and controlling audio playback operations.

With its robust functionality and seamless integration with the Windows Multimedia API, this application empowers users to create engaging multimedia applications with ease. Whether you are a seasoned developer or an aspiring enthusiast, the Audio Playback Application is your gateway to unlocking the full potential of audio playback on the Windows platform.

Clone the repository now and embark on a transformative audio playback experience! Let's dive into the world of audio together!


Code Walkthrough

In this walkthrough, we will break down the code that implements an AudioPlayer struct and a Form1 class to manage audio playback.

Index


Namespaces

Using Directives

using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;

In this example, we are importing:

  • System.Runtime.InteropServices
  • System.Text
  • System.Diagnostics This line imports the System.Diagnostics namespace, which provides classes for debugging and tracing. It allows us to print debug messages to the console.

Namespace Declaration

namespace Audio_Playback_CS
  • Here, we define a namespace called Audio_Playback_CS. Namespaces are used to organize code and avoid naming conflicts with other parts of the program.

Index


AudioPlayer Structure

public struct AudioPlayer
  • Struct Definition: The AudioPlayer struct is defined to encapsulate the functionalities related to audio playback.

DLL Import

[DllImport("winmm.dll", EntryPoint = "mciSendStringW")]
private static extern int mciSendStringW([MarshalAs(UnmanagedType.LPTStr)] string lpszCommand,
                                         [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpszReturnString,
                                         uint cchReturn, IntPtr hwndCallback);
  • DllImport: This attribute allows us to call functions from unmanaged libraries. Here, we are importing mciSendStringW from winmm.dll, which is used for multimedia control.
  • Parameters:
    • lpszCommand: The command string to send to the MCI (Media Control Interface).
    • lpszReturnString: A StringBuilder to store the return string from the command.
    • cchReturn: The size of the return string.
    • hwndCallback: A handle to a callback window (not used here).

Index


Adding Sounds

Sounds Array

private string[]? Sounds;

This declares an array named Sounds to store the names of sounds that have been added.

AddSound Method

public bool AddSound(string SoundName, string FilePath)

This method adds a sound to the player. It takes the name of the sound and the path to the sound file as parameters.

if (!string.IsNullOrWhiteSpace(SoundName) && File.Exists(FilePath))

Checks if the sound name is not empty or whitespace and if the file exists.

string CommandOpen = $"open \"{FilePath}\" alias {SoundName}";

Creates a command string to open the sound file and assign it an alias.

The escape character \ is used to include special characters in a string. In this case, the escape sequence \" allows you to include a double quote within a string that's also enclosed in double quotes.

Here's why it's needed: If your file path has spaces, it needs to be enclosed in quotes when you use it in commands. Without escaping the quotes, the string would get cut off at the first double quote it encounters.

For example, let's say your file path is C:\My Files\file.wav.

  • Without escaping: string CommandOpen = $"open "{FilePath}" alias {SoundName}"; would cause an error because the quotes are not properly handled.
  • With escaping: string CommandOpen = $"open \"{FilePath}\" alias {SoundName}"; ensures that the quotes are included as part of the string, making it open "C:\My Files\file.wav" alias SoundAlias.

This way, the entire file path is correctly recognized even if it contains spaces, and the command will execute as expected.

if (Sounds == null)

Checks if the Sounds array is uninitialized.

if (SendMciCommand(CommandOpen, IntPtr.Zero))

Sends the command to open the sound file.

Sounds = new string[1];
Sounds[0] = SoundName;
return true;

Initializes the Sounds array with the new sound and returns True.

else if (!Sounds.Contains(SoundName))

Checks if the sound is not already in the array.

Array.Resize(ref Sounds, Sounds.Length + 1);
Sounds[Sounds.Length - 1] = SoundName;
return true;

Adds the new sound to the Sounds array and returns True.

Debug.Print($"The sound was not added {SoundName}");
return false;

Prints a debug message and returns False if the sound could not be added.

Index


Setting Volume

SetVolume Method

public bool SetVolume(string SoundName, int Level)
{
    if (Sounds != null && Sounds.Contains(SoundName) && Level >= 0 && Level <= 1000)
    {
        string CommandVolume = $"setaudio {SoundName} volume to {Level}";
        return SendMciCommand(CommandVolume, IntPtr.Zero);
    }

    Debug.Print($"The volume was not set {SoundName}");
    return false;
}
  • Method SetVolume:
    • Checks if Sounds is not null, if the sound exists, and if the volume level is within the valid range (0 to 1000).
    • Constructs a command to set the audio volume for the specified sound.
    • Sends the command using SendMciCommand and returns the result.
    • Logs a message and returns false if the conditions are not met.

Index


Looping Sounds

LoopSound Method

public bool LoopSound(string SoundName)
{
    if (Sounds != null && Sounds.Contains(SoundName))
    {
        string CommandSeekToStart = $"seek {SoundName} to start";
        string CommandPlayRepeat = $"play {SoundName} repeat";
        return SendMciCommand(CommandSeekToStart, IntPtr.Zero) &&
               SendMciCommand(CommandPlayRepeat, IntPtr.Zero);
    }

    Debug.Print($"The sound is not looping {SoundName}");
    return false;
}
  • Method LoopSound:
    • Checks if the sound exists in the Sounds array.
    • Constructs commands to seek to the start of the sound and play it in repeat mode.
    • Sends both commands and returns true if successful; otherwise, logs a message and returns false.

Index


Playing Sounds

PlaySound Method

private bool PlaySound(string SoundName)
{
    if (Sounds != null && Sounds.Contains(SoundName))
    {
        string CommandSeekToStart = $"seek {SoundName} to start";
        string CommandPlay = $"play {SoundName} notify";
        return SendMciCommand(CommandSeekToStart, IntPtr.Zero) &&
               SendMciCommand(CommandPlay, IntPtr.Zero);
    }

    Debug.Print($"The sound is not playing {SoundName}");
    return false;
}
  • Method PlaySound:
    • Similar to LoopSound, but constructs commands to play the sound once.
    • Uses notify to allow the program to receive notification when the sound finishes playing.
    • Returns true if the commands were successful; otherwise, it logs a message and returns false.

Index


Pausing Sounds

PauseSound Method

public bool PauseSound(string SoundName)
{
    if (Sounds != null && Sounds.Contains(SoundName))
    {
        string CommandPause = $"pause {SoundName} notify";
        return SendMciCommand(CommandPause, IntPtr.Zero);
    }

    Debug.Print($"The sound is not paused {SoundName}");
    return false;
}
  • Method PauseSound:
    • Checks if the sound exists.
    • Constructs a command to pause the sound and sends it.
    • Returns true if successful; otherwise, logs a message and returns false.

Index


Managing Overlapping Sounds

Adding Overlapping Sounds

public void AddOverlapping(string SoundName, string FilePath)
{
    foreach (string Suffix in new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L" })
    {
        AddSound(SoundName + Suffix, FilePath);
    }
}
  • Method AddOverlapping:
    • Adds multiple sounds with suffixes (A to L) to allow overlapping playback.
    • Calls AddSound for each suffixed name.

Playing Overlapping Sounds

public void PlayOverlapping(string SoundName)
{
    foreach (string Suffix in new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L" })
    {
        if (!IsPlaying(SoundName + Suffix))
        {
            PlaySound(SoundName + Suffix);
            return;
        }
    }
}
  • Method PlayOverlapping:
    • Plays the first sound that is not currently playing among the suffixed sounds.

Setting Volume for Overlapping Sounds

public void SetVolumeOverlapping(string SoundName, int Level)
{
    foreach (string Suffix in new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L" })
    {
        SetVolume(SoundName + Suffix, Level);
    }
}
  • Method SetVolumeOverlapping:
    • Sets the volume for all suffixed sounds using the SetVolume method.

Index


Sending MCI Commands

private bool SendMciCommand(string command, IntPtr hwndCallback)
{
    StringBuilder ReturnString = new StringBuilder(128);

    try
    {
        return mciSendStringW(command, ReturnString, 0, hwndCallback) == 0;
    }
    catch (Exception ex)
    {
        Debug.Print($"Error: {ex.Message}");
        return false;
    }
}
  • Method SendMciCommand:
    • Sends a command to the MCI and checks for errors.
    • Returns true if the command was successful; otherwise, logs the error and returns false.

Index


Getting Sound Status

private string GetStatus(string SoundName, string StatusType)
{
    try
    {
        if (Sounds != null && Sounds.Contains(SoundName))
        {
            string CommandStatus = $"status {SoundName} {StatusType}";
            StringBuilder StatusReturn = new StringBuilder(128);
            mciSendStringW(CommandStatus, StatusReturn, 128, IntPtr.Zero);
            return StatusReturn.ToString().Trim().ToLower();
        }
    }
    catch (Exception ex)
    {
        Debug.Print($"Error getting status: {ex.Message}");
    }

    return string.Empty;
}
  • Method GetStatus:
    • Retrieves the status of a sound (e.g., whether it is playing).
    • Constructs a status command and returns the result as a string.

Index


Closing Sounds

CloseSounds Method

public void CloseSounds()
{
    if (Sounds != null)
    {
        foreach (string Sound in Sounds)
        {
            string CommandClose = $"close {Sound}";

            SendMciCommand(CommandClose, IntPtr.Zero);

        }

        Sounds = null;

    }

}
  • Closes all open sounds by sending a close command for each sound in the Sounds array.
  • Sounds = null;: This line sets the Sounds array to null. By doing this, it effectively clears the reference to the array, ensuring that all resources associated with the sounds are released. It also prevents further usage of the array without reinitializing it, which is a good practice for memory management and avoiding potential errors in your application.

Index


Form Class and Event Handlers

public partial class Form1 : Form
{
    private AudioPlayer Player;

    private void Form1_Load(object sender, EventArgs e)
    {
        Text = "Audio Playback CS - Code with Joe";

        CreateSoundFiles();

        string FilePath = Path.Combine(Application.StartupPath, "level.mp3");
        Player.AddSound("Music", FilePath);
        Player.SetVolume("Music", 600);

        FilePath = Path.Combine(Application.StartupPath, "CashCollected.mp3");
        Player.AddOverlapping("CashCollected", FilePath);
        Player.SetVolumeOverlapping("CashCollected", 900);

        Player.LoopSound("Music");

        Debug.Print($"Running... {DateTime.Now}");
    }
  • Form1 Class: Inherits from Form, which is part of Windows Forms for creating GUI applications.
  • Player Field: An instance of AudioPlayer is created to manage audio playback.
  • Form1_Load Method:
    • Sets the form title.
    • Calls CreateSoundFiles to ensure the sound files exist.
    • Adds sounds and sets their volumes.
    • Loops the background music and logs the current time.

Button Click Events

private void Button1_Click(object sender, EventArgs e)
{
    Player.PlayOverlapping("CashCollected");
}

private void Button2_Click(object sender, EventArgs e)
{
    if (Player.IsPlaying("Music"))
    {
        Player.PauseSound("Music");
        button2.Text = "Play Loop";
    }
    else
    {
        Player.LoopSound("Music");
        button2.Text = "Pause Loop";
    }
}
  • Button1_Click: Plays the overlapping "CashCollected" sound when the button is clicked.
  • Button2_Click: Toggles between pausing the music and looping it, updating the button text accordingly.

Form Closing Event

private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    Player.CloseSounds();
}
  • Form1_Closing: Ensures all sounds are closed when the form is closing.

Index


Creating Sound Files

private void CreateSoundFiles()
{
    string filePath = Path.Combine(Application.StartupPath, "level.mp3");
    CreateFileFromResource(filePath, Audio_Playback_CS.Resource1.level);

    filePath = Path.Combine(Application.StartupPath, "CashCollected.mp3");
    CreateFileFromResource(filePath, Audio_Playback_CS.Resource1.CashCollected);
}
  • Method CreateSoundFiles:
    • Creates sound files from resources if they do not already exist.

Creating Files from Resources

private void CreateFileFromResource(string filePath, byte[] resource)
{
    try
    {
        if (!File.Exists(filePath))
        {
            File.WriteAllBytes(filePath, resource);
        }
    }
    catch (Exception ex)
    {
        Debug.Print($"Error creating file: {ex.Message}");
    }
}
  • Method CreateFileFromResource:
    • Writes byte arrays from resources to files on disk.
    • Catches exceptions and logs errors if file creation fails.

Index


Adding Resources

To add a resource file to your Visual Studio project, follow these steps:

  1. Add a New Resource File:
    • From the Project menu, select Add New Item....
    • In the dialog that appears, choose Resource File from the list of templates.
    • Name your resource file (e.g., Resource1.resx) and click Add.

005

006

  1. Open the Resource Editor:
    • Double-click the newly created .resx file to open the resource editor.

007

  1. Add Existing Files:
    • In the resource editor, click on the Green Plus Sign or right-click in the resource pane and select Add Resource.
    • Choose Add Existing File... from the context menu.
    • Navigate to the location of the MP3 file (or any other resource file) you want to add, select it, and click Open.

008

  1. Verify the Addition:

    • Ensure that your MP3 file appears in the list of resources in the resource editor. It should now be accessible via the Resource class in your code.
  2. Accessing the Resource in Code:

    • You can access the added resource in your code using the following syntax:
      CreateFileFromResource(filePath, YourProjectNamespace.Resource1.YourResourceName);
      
      // Example
      CreateFileFromResource(filePath, Resource1.CashCollected);
  3. Save Changes:

    • Don’t forget to save your changes to the .resx file.

009

By following these steps, you can easily add any existing MP3 file or other resources to your Visual Studio project and utilize them within your Audio Playback application.


Related Projects

This project serves as a direct port of the original Audio Playback project created in VB.NET, which you can also explore for a different perspective on the same concepts. For more information and to access the complete code, visit the Audio Playback Repository and the Audio Playback C# Repository. Happy coding!

013


Index

Adding Sounds

Setting Volume

Looping Sounds

Playing Sounds

Pausing Sounds

Managing Overlapping Sounds

Sending MCI Commands

Getting Sound Status

Closing Sounds

Form Class and Event Handlers

Creating Sound Files

Adding Resources


This code provides a comprehensive example of how to create an audio playback application in C#. We covered everything from adding sounds to managing their playback and volume. By understanding each part of this code, you can build a solid foundation for working with audio in your applications.

Feel free to experiment with the code and modify it to enhance your learning experience!