You would set the volume level of the currently playing buffer to zero or infinity. Since the primary buffer is the only playing buffer I would imagine you would set that to zero. The primary buffer is essentially like the 'Master' on a sound board. The secondary buffers can be thought of as 'Channels' on a sound board. Each secondary buffer can have volume but those volumes will never exceed the final primary buffer volume. The primary buffer, if memory serves me, is handled by DirectSound. The user handles the secondary buffers.
I stopped using DirectSound and moved to OpenAL. I'm not sure there is a C# wrapper for OpenAL but you could easily write one using C++/CLI and interface that with OpenAL. You can make use of the msclr namespace and use auto_gcroot or gcroot to enable un-managed classes to use managed objects (pointers into the managed heap as Microsoft puts it). This allows C++ objects to call into .NET managed objects.
C++ OpenALWrapper.dll - links with OpenAL
Code:
#ifdef _MANAGED
public
#endif
class OpenALWrapper
{
public:
OpenALWrapper(unsigned int numSources)
{
// Init OpenAL here
}
bool AddSound(const char *pSoundName,const char *pFilename)
{
// Add sound here and return result
}
...
...
};
C++/CLI OpenALBridge.dll - links with OpenALWrapper.dll
Code:
#include "OpenALWrapper.h"
using namespace System::Runtime::InteropServices;
public ref class OpenALBridge
{
public:
OpenALBridge(unsigned int numSources)
{
m_pUMOpenAL = new OpenALWrapper(numSources);
}
virtual ~OpenALBridge()
{
if (m_pUMOpenAL)
{
delete m_pUMOpenAL;
m_pUMOpenAL = 0;
}
}
bool AddSound(System::String ^soundName,System::String ^filename)
{
bool result = false;
// Convert System::String to const char *
const char *pSoundName = ConvertToCString(soundName);
const char *pFileName = ConvertToCString(fileName);
// Call into C++ OpenALWrapper
result = m_pUMOpenAL->AddSound(pSoundName,pFileName);
// Cleanup memory allocated in string conversion
CleanupCString(pSoundName);
CleanupCString(pFileName);
return result;
}
bool RemoveSound(System::String ^soundName);
ManagedSoundSample ^GetSound(System::String ^soundName);
bool PlaySound(System::String ^soundName);
...
private:
OpenALWrapper *m_pUMOpenAL;
};
const char * ConvertToCString(System::String ^managedString)
{
return (const char*)(Marshal::StringToHGlobalAnsi(managedString)).ToPointer();
}
void CleanupCString(IntPtr string)
{
Marshall.FreeHGlobal(string);
}
C# - has a reference to OpenALBridge.dll
Code:
public class Foo
{
public ManagedOpenAL { get; private set; }
public Foo(unsigned int numSources)
{
ManagedOpenAL = new OpenALBridge(numSources);
...
}
public bool AddSound(string soundName,string fileName)
{
bool result = false;
if (ManagedOpenAL != null)
{
result = ManagedOpenAL.AddSound(soundName,fileName);
}
return result;
}
...
}
Here is an MSDN link to explain the string conversion routines. StringToHGlobalAnsi allocates a block of memory that must be cleaned up. Note that if you wanted to convert to std::string you could but since you would be calling into a C++ DLL I would recommend the DLL use POD types at the boundary.
Marshal.StringToHGlobalAnsi Method (System.Runtime.InteropServices)
Keep in mind this is a SAMPLE. I did not compile this and I'm sure the IntPtr and const char * are going to give some warnings in the C functions. Now once all this functionality is correctly wrapped and bridged you can use OpenAL from C# just as any other C# object. Ref classes in C++/CLI are managed classes and are used exactly like any other managed object. This allows you to use OpenAL just as any other C++ app would use it. Because you are using C++/CLI ref classes you don't need any special P/Invoke declarations in C# or mess with data type issues (C++ bool is not C# bool). It is also generally faster to use C++/CLI than the corresponding P/Invoke. I could probably wrap OpenAL in less than a week and if I did not need the extended EAX functionality I could wrap it in about a day. Once you get used to the C++/CLI, C#, and C++ communication and relationships calling into C++ from C# and vice versa becomes very simple to do.
The order of creation here is:
C# -> creates the C++/CLI object
C++/CLI -> creates the C++ object
C++ -> creates OpenAL
The call order is:
C# -> C++/CLI -> C++ -> OpenAL
You can do: (for say callbacks or listeners)
OpenAL -> C++ -> C++/CLI -> C#
But you must use gcroot or auto_gcroot so your unmanaged classes can talk to managed classes.