Audio on Windows Desktop

I’ve ported the PC version of a game I originally wrote in XNA to Windows 8 desktop (not universal app) - all content is recompiled using the new content builder. Pretty much everything works as it did in XNA except for the audio:

Sound effects do not play at all, either via SoundEffect.Play or by creating an Instance and playing that. I’ve made sure that my volume and MasterVolume are both set at 1.0f, and I’ve also tried using the old XNA-build .XNBs of the wavs in place of those from the content generator. My PC is not muted, and my system sounds are switched on… so I’m out of ideas with this one!

MP3 playback is not behaving as expected, and I’m not sure exactly what the cause is - if I call MediaPlayer.Stop then start a new tune almost immediately, the new tune plays, but if there is a delay, between Stop and Play, the new tune does not start… I’m still trying to work out the exact conditions for this, but I know it’s not the MP3 files that are causing this as I’ve swapped and changed the ones I’m testing with around.

Has anyone else hit these problems, and if so, how did you go round them?

Thanks!

Using Windows or WindowsGL? One uses XAudio for sound and the other uses OpenAL. OpenAL requires the OpenAL library to be install, which you will find in the MonoGame installation as oalinst.exe.

It’s a Windows project, not GL.

Swapped over to a WindowsGL project and the sound effects now work… but music does not work at all (as opposed to the sporadic issue I mentioned before) - trying to load a song throws an error, and I think I’m hitting the same wall as this.

I’m reluctant to switch over to WAVs for the music, as this would mean different files for different versions of the project, and also add roughly 100Mb to the overall size of the game.

I also spotted that fullscreen does not appear to work - despite calling the following:

GraphicsDeviceManager graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferWidth = screenWidth;
graphics.PreferredBackBufferHeight = screenHeight;
graphics.IsFullScreen = fullScreen;

… the game runs in a window, regardless of what values I pass for screenWidth and screenHeight.

Once again, has anyone had any experience with these issues and circumventing them? Also, if I were to use a WindowsGL project, would my players need to install OpenAL as well?

Thanks!

Well, I know one method that still works just like it worked in previous versions of monogame, to change to fullscreen.

You need to enumerate through SupportedDisplayModes and choose one of them. I believe in current version, you can switch to fullscreen only if your display supports this resolution. In example like this:

foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
{
    if ((dm.Width == 1920) && (dm.Height == 1080))
    {
         graphics.PreferredBackBufferWidth = dm.Width;
         graphics.PreferredBackBufferHeight = dm.Height;
         graphics.IsFullScreen = true;
         graphics.ApplyChanges();
     }
}

Hope that helps somehow!

@mpeg88 - interesting… using what you’ve given here as a basis, I did a little digging. And it seems that despite my display resolution being set to 1920x1080, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode is returning that the screen is 1536x864 - don’t know if this a bug or something else is up…

But forcing the resolution to a specific value is not an option, as I cannot be certain that players will have screens of this size and aspect ratio. But thanks all the same!

Did this issue ever get resolved? I’m having the exact same problem:
Windows 8.1 64bit, Monogame 3.4 binary DLLs

Platform2D Sample:
Build-Run using WindowsDX SLN = No SoundEffects, Yes Music
Build-Run using WindowsGL SLN = Yes SoundEffects, No Music

Is this something that is fixed in the develop branch perhaps? Last time we tried Monogame was 3.0 and this wasn’t an issue.

I’ve got a basic Windows project here that loads and plays sound effects and music. There have been no fixes in this area, so I’m nto sure what is happening on your system.

In case anyone else runs into this issue, I’ve narrowed it down to drivers.

I was running a fresh build of windows 8.1 and dxdiag showed everything was in working order, but obviously something wasn amiss. The solution for me was to just run the directx redistributable. I suspect this isn’t an uncommon issue as another fellow on the Monogame IRC channel was going through the same thing when we were trying to debug this.

This only dawned on me when I tried the platform2d compiled sample on multiple computers in the house and it worked for the ones that had big name games (blizzard games mostly) installed, but didn’t work on anything that was work/vanilla. Thanks for taking the time to try it out KonajuGames. I’m not sure if this is something Monogame can deal with, but for our game we’ll be packing the directx redistributable into the installer in order to circumvent this happening in customer hands.

Glad to see the solution was found.

If you distribute a game built with the MonoGame DirectX platform, you should always distribute the DirectX runtime installer and run it as part of the game install.

Also to note, the WindowsGL version never got resolved. I still get no music even when I manually install OpenAL from the monogame folder (oalinst.exe). Hopefully someone else has a solution for this as we’re still on the fence about using WindowsGL or WindowsDX.

The old Song system used SDL to play music.

In newest develop branch it has been replaced by NVorbis and OpenAL so whatever was causing problems, it should already be resolved in the develop branch.

Hi crao0zy,

I’m actually using a self-compiled version of the current develop branch. I was trying to see if the previous issue I had was resolved. Using the newest develop branch and the windowsGL project, I’m still getting no music. Is there a vorbis driver/codec I have to have installed before music will play properly in the new develop branch WindowsGL projects?

NVorbis uses OpenAL to play audio. That means that SoundEffects also don’t work on you PC with MonoGame.

Nope, sound effects work. We’re running the Platform2D project, still can’t get it to play music on my computer. When the man jumps, the sound effect is there, just no music.

I use the WinMM library to play audio for MonoGame.WinGL instead of the content pipeline:

https://github.com/dmanning23/AudioBuddy/blob/master/Source/AudioManager.cs

Darn it, I updated to Win8.1 and my winmm hack doesn’t work anymore.
Sound effects work fine, but the content loader wants music to be in .wav format. The content pipeline compiles to m4a file, so that doesn’t work either.
Anyone able to get music playing in WindowGL?

Apologies for the long post, but hopefully you will find the below useful regarding the resolution.

In my windows project I have a resolution class (see below) and then in my “Game1()” method I have the following:

// Change Virtual Resolution
Resolution.Initialize(graphics, Color.DarkGoldenrod);
Resolution.SetVirtualResolution(1920, 1080);    // Size game is designed for
Resolution.SetResolution(1600, 900, false);     // Actual size of window to display

I’m designing my game for a 1920x1080 res but set it windowed (1600x900 - so I can move it and see my debug screen etc.), but the virtual viewport in the screen is still 1920x1080, change the “false” above to true for full screen.

EDIT: Forgot to mention at the top of the DRAW() method you will need :

Resolution.BeginDraw();

At the very top of the file you will also see a “MouseHelper” this is to convert the mouse to the correct viewport virtual position.

Hopefully this will be helpful.

//====================================================

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace ScreenManagerTesting
{
	public static class MouseHelper
	{
		/// <summary>
		/// Translates the actual mouse position obtained from Mouse.GetState() into the virtual mouse position#
		/// after a scaling matrix is applied to the viewport
		/// </summary>
		public static Point CurrentMousePosition
		{
			get
			{
				MouseState mouse = Mouse.GetState();

				Vector2 mousePosition = new Vector2(mouse.X, mouse.Y);
				Vector2 virtualViewport = new Vector2(Resolution.VirtualViewportX, Resolution.VirtualViewportY);
				mousePosition = Vector2.Transform(mousePosition - virtualViewport, Matrix.Invert(Resolution.GetTransformationMatrix()));

				Point virtualMousePosition = new Point((int)mousePosition.X, (int)mousePosition.Y);
				return virtualMousePosition;
			}
		}
	}

	static class Resolution
	{
		static private GraphicsDeviceManager graphicsDevice = null;

		static private int virtualWidth = 1920;     // Size to display at / designed for size
		static private int virtualHeight = 1080;    // Size to display at / designed for size

		static private int screenWidth = 1920;      // Actual Screen size
		static private int screenHeight = 1080;     // Actual Screen size
		static private bool fullScreen = false;

		static private bool recreateScaleMatrix = true;
		static private Matrix scaleMatrix;

		static private int virtualViewportX;
		static private int virtualViewportY;

		static private Color backGroundColor; // This is used for when it's a letter/pillar box display


		/// <summary>
		/// Initializes the specified device.
		/// </summary>
		/// <param name="device">The graphics device.</param>
		/// <param name="backgroundColor">Color of the background.(Default: Color.Black)</param>
		static public void Initialize(GraphicsDeviceManager device, Color? backgroundColor = null)
		{
			Resolution.screenWidth = device.PreferredBackBufferWidth;
			Resolution.screenHeight = device.PreferredBackBufferHeight;
			Resolution.graphicsDevice = device;
			Resolution.recreateScaleMatrix = true;

			Resolution.backGroundColor = backgroundColor ?? Color.Black;

			ApplyResolutionSettings();
		}

		static public Rectangle VirtualViewport
		{
			get
			{
				return new Rectangle(0, 0, Resolution.virtualWidth, Resolution.virtualHeight);
			}
		}

		public static int VirtualViewportX
		{
			get { return virtualViewportX; }
		}

		public static int VirtualViewportY
		{
			get { return virtualViewportY; }
		}

		static public Matrix GetTransformationMatrix()
		{
			if (Resolution.recreateScaleMatrix)
			{
				RecreateScaleMatrix();
			}

			return Resolution.scaleMatrix;
		}

		/// <summary>
		/// Sets the virtual resolution. This is the resolution the "game" is designed in.
		/// If game is ran in resolution not of the same aspect ration, then it will be
		/// displayed as a letterbox/pillar box.
		/// </summary>
		/// <param name="virtualWidth">Width of the virtual screen.</param>
		/// <param name="virtualHeight">Height of the virtual screen.</param>
		static public void SetVirtualResolution(int virtualWidth = 1920, int virtualHeight = 1080)
		{
			Resolution.virtualWidth = virtualWidth;
			Resolution.virtualHeight = virtualHeight;
			Resolution.recreateScaleMatrix = true;
		}

		/// <summary>
		/// Sets the actual display resolution. e.g. the users monitor size. If smaller than current monitor
		/// resolution, then will display in window (unless set to full screen)
		/// </summary>
		/// <param name="width">The width.</param>
		/// <param name="height">The height.</param>
		/// <param name="fullScreen">if set to <c>true</c> [full screen].</param>
		static public void SetResolution(int width = 1600, int height = 900, bool fullScreen = false)
		{
			Resolution.fullScreen = fullScreen;
			Resolution.screenWidth = (fullScreen) ? GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width : width;
			Resolution.screenHeight = (fullScreen) ? GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height : height;

			ApplyResolutionSettings();
		}

		static private void ApplyResolutionSettings()
		{
#if XBOX360
			Resolution.fullScreen = true;
#endif
			// If we aren't using a full screen mode, the height and width of the window can
			// be set to anything equal to or smaller than the actual screen size.
			if (Resolution.fullScreen == false)
			{
				if ((Resolution.screenWidth <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width)
					&& (Resolution.screenHeight <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height))
				{
					graphicsDevice.PreferredBackBufferWidth = Resolution.screenWidth;
					graphicsDevice.PreferredBackBufferHeight = Resolution.screenHeight;
					graphicsDevice.IsFullScreen = Resolution.fullScreen;
					graphicsDevice.ApplyChanges();
				}
			}
			else
			{
				// If we are using full screen mode, we should check to make sure that the display
				// adapter can handle the video mode we are trying to set. To do this, we will
				// iterate through the display modes supported by the adapter and check them against
				// the mode we want to set.
				foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
				{
					// Check the width and height of each mode against the passed values
					if ((dm.Width == Resolution.screenWidth) && (dm.Height == Resolution.screenHeight))
					{
						// The mode is supported, so set the buffer formats, apply changes and return
						graphicsDevice.PreferredBackBufferWidth = Resolution.screenWidth;
						graphicsDevice.PreferredBackBufferHeight = Resolution.screenHeight;
						graphicsDevice.IsFullScreen = Resolution.fullScreen;
						graphicsDevice.ApplyChanges();
					}
				}
			}
			Resolution.recreateScaleMatrix = true;
			Resolution.screenWidth = graphicsDevice.PreferredBackBufferWidth;
			Resolution.screenHeight = graphicsDevice.PreferredBackBufferHeight;
		}

		/// <summary>
		/// Sets the device to use the draw pump
		/// Sets correct aspect ratio
		/// </summary>
		static public void BeginDraw()
		{
			BeginDraw(Resolution.backGroundColor);
		}

		/// <summary>
		/// Sets the device to use the draw pump
		/// Sets correct aspect ratio
		/// </summary>
		static public void BeginDraw(Color backgroundColor)
		{
			FullViewport();
			ResetViewport();
			graphicsDevice.GraphicsDevice.Clear(backgroundColor);
		}

		static private void RecreateScaleMatrix()
		{
			Resolution.recreateScaleMatrix = false;
			Resolution.scaleMatrix = Matrix.CreateScale(
													   (float)graphicsDevice.GraphicsDevice.Viewport.Width / virtualWidth,
													   (float)graphicsDevice.GraphicsDevice.Viewport.Height / virtualHeight,
													   1f);
		}

		/// <summary>
		/// Sets the graphics device viewport to the screen width/height.
		/// </summary>
		static public void FullViewport()
		{
			Viewport vp = new Viewport();
			vp.X = vp.Y = 0;
			vp.Width = Resolution.screenWidth;
			vp.Height = Resolution.screenHeight;
			graphicsDevice.GraphicsDevice.Viewport = vp;
		}

		/// <summary>
		/// Resets the viewport. Calculates the proper viewport according to aspect ration
		/// </summary>
		static public void ResetViewport()
		{
			float targetAspectRatio = GetVirtualAspectRatio();
			// figure out the largest area that fits in this resolution at the desired aspect ratio
			int width = graphicsDevice.PreferredBackBufferWidth;
			int height = (int)(width / targetAspectRatio + .5f);
			bool changed = false;

			if (height > graphicsDevice.PreferredBackBufferHeight)
			{
				height = graphicsDevice.PreferredBackBufferHeight;
				// PillarBox
				width = (int)(height * targetAspectRatio + .5f);
				changed = true;
			}

			// set up the new viewport centered in the backbuffer
			Viewport viewport = new Viewport();

			viewport.X = (graphicsDevice.PreferredBackBufferWidth / 2) - (width / 2);
			viewport.Y = (graphicsDevice.PreferredBackBufferHeight / 2) - (height / 2);
			viewport.Width = width;
			viewport.Height = height;
			viewport.MinDepth = 0;
			viewport.MaxDepth = 1;

			virtualViewportX = viewport.X;
			virtualViewportY = viewport.Y;
			if (changed)
			{
				Resolution.recreateScaleMatrix = true;
			}
			graphicsDevice.GraphicsDevice.Viewport = viewport;
		}

		/// <summary>
		/// Get virtual Mode Aspect Ratio
		/// </summary>
		/// <returns>aspect ratio</returns>
		static public float GetVirtualAspectRatio()
		{
			return (float)Resolution.virtualWidth / (float)Resolution.virtualHeight;
		}
	}
}

Another user having an issue with this.

WindowsGL project ported from iOS/Android. So far port has gone fairly smoothly but can’t get mp3 (Song) files to play. Wav (SoundEffect) files are working fine (once I installed OpenAL).

Is this fixed in the latest develop branch? I don’t want to spend time going down that rabbit-hole unless it’s likely to work!

cheers

Based on dmanning’s idea above I have successfully got mp3 files to play and loop using winmm.dll so no requirement for Windows Media Player or Direct X. Luckily I have stuff like sound pretty abstracted out in my code anyway so it was pretty easy to swap this in in place of the XNA/MonoGame calls. Here’s a simple class to play or loop an mp3 file - this code could probably do with tidying up but it works!

I have my mp3 resources just set to ‘copy’ in the pipeline tool.

#region Using Statements
using System;
using System.Text;
using System.Runtime.InteropServices;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Input;

#endregion
namespace com.bitbull.meat.platform.windows
{
    public class WinmmMp3MediaType
    {
        [DllImport("winmm.dll")]
        private static extern long mciSendString(string lpstrCommand, StringBuilder lpstrReturnString, int uReturnLength, int hwndCallback);

        private string filename;
        private string command;

// Expects a name like 'yourfile' WITHOUT an extension (just for consistency with Content.Load)
// Expects files to be stored at root of Content directory with no pre-processing 
        internal WinmmMp3MediaType(string name)
        {
            filename = "Content/" + name + ".mp3";
        }

        internal override void Play(int loopcount, float volume)
        {
            StringBuilder retvalue = new StringBuilder();

// You need to stop and close and currently playing files otherwise it doesn't work properly
            command = "stop Mp3File";
            mciSendString(command, null, 0, 0);
            command = "close Mp3File";
            mciSendString(command, null, 0, 0);

            command = "open " + "\"" + filename + "\"" + " type MPEGVideo alias Mp3File";
            mciSendString(command, retvalue, 0, 0);

// I've commented out the code for setting the volume as it's specific to the way I do things but basically this command expects an integer between 0 (min volume) and 1000 (max volume) 
            // float master_volume = MEAT.Platform.MediaInterface.MusicVolume / 100.0f;
            // mciSendString(string.Concat("setaudio Mp3File left volume to ", (int)volume*master_volume*1000), null, 0, 0);
            // mciSendString(string.Concat("setaudio Mp3File right volume to ", (int)volume*master_volume * 1000), null, 0, 0);

            command = "play Mp3File";
            if ( loopcount<=0 )
            {
                command += " REPEAT";
            }
            mciSendString(command, retvalue, 0, 0);
        }

        internal override void Stop()
        {
            command = "stop Mp3File";
            mciSendString(command, null, 0, 0);
            command = "close Mp3File";
            mciSendString(command, null, 0, 0);
        }
    }
}