Sound object problem

I have a weird problem, and I’m starting to think this is a Toolset bug because it absolutely makes no sense at all. I created a sound in Cubase, exported it as a mono sound (44100kHz 16 bit wave-file) and put it in a hak pak. Then I tried creating a sound object. When choosing the sound under Properties/Behaviour the toolset claims that this is not a mono sound. But it is!! I can hear it loud and clear in my headphones that it is indeed mono and not stereo. Then when choosing other sounds that are stock sounds for the game I noticed that some of them are 128kbit mono mp3 files. Ok, so I save this sound in Cubase as an mp3 file instead (128kbit mono mp3) and now the sound doesn’t even show up under Properties? What is this crap? Do I have to change the mp3 file to a BMU file or what?

I can play the sound (listen to it) from the Properties, but as soon as I try to save it, the Toolset claims that it’s not mono and therefore I can’t use it as a positional sound.

The really weird thing is, if I select the sound (my original wave file that is) in a conversation, it plays just fine ingame. And in conversation it’s even possible to play stereo sounds…

Does anyone has any clue as to what’s going on?

Ok, I managed to solve it. I found a thread about sound objects on the old forums and used @Lance_Botelle 's somewhat odd method of playing a sound:

https://neverwintervault.org/forums/neverwinter-nights-2/nwn2-scripting/trigger-sound

I still don’t understand what the Toolset is doing, and why it can’t see that the sound I’m using is in fact a mono sound. But hey, a solution is still a solution and this was fairly easy to implement.

This is my script (if I need to do this kind of thing again and forgot how I did it):

#include "ginc_object"
#include "ginc_ipspeaker"

void main()
{

object oEnter = GetEnteringObject();

if(!GetIsPC(oEnter))return;

if(GetLocalInt(OBJECT_SELF,"Done")) return;

object oPlaceable = GetNearestObjectToLocation(OBJECT_TYPE_PLACEABLE, GetLocation(oEnter));

AssignCommand(oPlaceable, PlaySound("sfdoor1", TRUE));

object oPC = GetFirstPC();
SpawnCreatureAtWP("creature","nylwp");

SetLocalInt(OBJECT_SELF,"Done",1);

CreateIPSpeaker("creature","c_creature",GetLocation(oPC),0.5);

}

Edit: My first script just had the function PlaySound, but maybe that function is not possible without AssignCommand, and my second try was trying to create a sound object and then all the weird stuff happened. Anyway… So this all this is solved, but I still don’t understand the sound object weirdness.

1 Like

@andgalf,

That goes back a long way. :slight_smile:

I now have a function I made based on that method, which has undergone a number of alterations since then. Here are some points that I have learned since that time, which you may want to be aware of.

  1. It’s possible that you may not have a useable placeable nearby when you need it.

  2. If the placeable object is more than 20 metres away from the PC, you probably still won’t hear it. Therefore, I introduced a check to create one if required. (Invisible and scaled right down and made walkable.)

  3. If placeable object creation is required, you need a small (0.2 sec) delay before you can fire a sound from it.

  4. Rapid reuse of created object requires some careful handling, although this probably will not apply to your own usage. (I had special requirements.)

If you need me to explain more, I could post my own function as an example, but it uses some dedicated references, which you would need to alter for your own usage. However, if you need this more moving forward, then the function may be useful to you.

Bottom line, if you (or anybody else reading) wishes to see what I did for the LBPlaySound function I now use, just ask, and I will post it. :slight_smile:

1 Like

Thanks for the points, @Lance_Botelle.

Yes, I would very much like to see how you’ve scripted your function.

And if anyone has an explanation for the Toolset’s weird behaviour regarding mono sounds, I would very much like to hear that as well.

@andgalf,

OK, here is a variation of the function I am using, with generic functions …

It should still be functional as I now have it. At the very least, it gives you the idea of where I had made changes. NB: You need a placeable ref template in the toolset … see script.

EDIT: “ESTABLISHED” is just a variable I use to check if combat has started.

////////////////////////////////////////////////////////////////////////////////////////
// PLAY A SOUND WITHOUT INTERRUPTING THE PC (ADAPTED FOR ANDGALF IN FORUMS)
// PLAYS SOUND IMMEDIATELY IF A USEABLE PLACEABLE NEARBY TO PLAY SOUND
// NB: fDelay DOES MAY NOT ALWAYS WORK AS YOU MAY THINK IT DOES HERE!!!!!
////////////////////////////////////////////////////////////////////////////////////////
void LBPlaySound(string sSound, float fDelay = 0.0);
void LBPlaySound(string sSound, float fDelay = 0.0)
{
	object oPC = GetFirstPC();
	
	if(oPC == OBJECT_INVALID){return;}
					
	if(fDelay < 0.2){fDelay = 0.2;}
	
	//SendMessageToPC(oPC, "HEARER PC IS >>> " + GetFirstName(oPC));
	
	int iPlayed = 0; location lLOC = GetLocation(oPC); 
	object oNEW = OBJECT_INVALID; string sTEXT = ""; float fDIS = 0.0;
	
	// I WAS FIRST SEARCHING FOR PLACEABLES THAT I KNEW WERE OK
	//object oTEST = GetNearestObjectByTag("alb_temp_weather", oPC, 1);
	//if(oTEST == OBJECT_INVALID){oTEST = GetNearestObjectByTag("alb_weather_unit", oPC, 1);}
	
	object oTEST = GetNearestObjectToLocation(OBJECT_TYPE_PLACEABLE, lLOC);
	
	if(oTEST != OBJECT_INVALID)
	{		
		fDIS = GetDistanceBetweenLocations(GetLocation(oTEST), lLOC);
		
		if(fDIS < 20.0)
		{	
			// OBJECT MAY HAVE JUST BEEN MADE (IN DOUBLE SOUND PLAY)
			float fWait = 0.0; if(GetLocalInt(oTEST, "OBJREADY") == -1){fWait = 0.2;}
			
			DelayCommand(fWait, AssignCommand(oTEST, PlaySound(sSound, TRUE)));
			iPlayed = 1; 
			sTEXT = "<<< SOUND PLAYED >>>";
		}
	}
	
	if(iPlayed == 0 && !GetPause() && GetGlobalInt("ESTABLISHED") == 0)
	{
		// CREATE NEARBY OBJECT ANYWAY (YOU NEED TO HAVE THIS TEMPLATE IN TOOLSET!!!!)
		oNEW = CreateObject(OBJECT_TYPE_PLACEABLE, "alb_temp_weather", lLOC, FALSE);
		
		// PREVENT OTHER ATTEMPTED SOUNDS PLAYING UNTIL ESTABLISHED
		SetLocalInt(oNEW, "OBJREADY", -1);
		DelayCommand(fDelay, SetLocalInt(oNEW, "OBJREADY", 0));
		
		// PREVENT INTERRUPTING PC FOR CERTAIN ACTIONS
		int iACTION = GetCurrentAction(oPC);
			
		if(!IsInConversation(oPC) && iACTION == ACTION_INVALID)
		{
			AssignCommand(oPC, ClearAllActions());
			AssignCommand(oPC, ActionDoCommand(PlaySound(sSound, TRUE)));		
			
			sTEXT = "<<< CREATING NEW AFTER PC SOUND >>>";
		}
		
		else
		{			
			DelayCommand(fDelay, AssignCommand(oNEW, PlaySound(sSound, TRUE)));
			
			sTEXT = "<<< CREATING NEW WITH DIRECT PLAY >>>";
		}
	}
	
	if(GetGlobalInt("TESTMODE") > 0)
	{
		SetNoticeText(oPC, "PLAY >>> " + sSound + " WITH TAG TEST >>> " + GetTag(oTEST) + " DIS >>>> " + FloatToString(fDIS), 1, 1);
		
		if(sTEXT != ""){SendMessageToPC(oPC, sTEXT);}
	}
}
1 Like

Thanks @Lance_Botelle! I’ll take a look at it closely. May come in handy later on.

Ok, so I have now edited your script a tiny bit, so I can read it a bit better. I think I understand most of it.

If I understand correctly, the important thing is to have a useable placeble near the PC. If there’s no such thing, I need to have a placeable that is useable as a blueprint with whatever tag I choose (I don’t want to use the “alb_temp_weather” so I’ll go with, say, “temp_object”) and that should be it?

Here’s my edited take on the script (just so that I myself can read it better. Peolpe like different styles when reading/writing scripts I’ve noticed, and I always like when curly brackets is placed on a new row, for example):

////////////////////////////////////////////////////////////////////////////////////////
// Play a sound without interrupting the PC (adapted for andgalf).
// Plays sound immediately if there's a useable placeable nearby to play sound.
//
// Note: fdelay does not always work as you may think it does here!
//
// By Lance Botelle
////////////////////////////////////////////////////////////////////////////////////////

void LancePlaySound(string sSound, float fDelay = 0.0)
{
	object oPC = GetFirstPC();
	
	if(oPC == OBJECT_INVALID) return;
	
					
	if(fDelay < 0.2)
	{
	fDelay = 0.2;
	}
	
	//SendMessageToPC(oPC, "Hearer PC is >>> " + GetFirstName(oPC));
	
	int iPlayed = 0; 
	location lLOC = GetLocation(oPC); 
	object oNEW = OBJECT_INVALID; 
	string sTEXT = ""; 
     float fDIS = 0.0;
	
	// I was first searching for placeables that i knew were ok.
	// object oTEST = GetNearestObjectByTag("temp_weather", oPC, 1);
	// if(oTEST == OBJECT_INVALID)
	// {
	// oTEST = GetNearestObjectByTag("weather_unit", oPC, 1);
	// }
	
	object oTEST = GetNearestObjectToLocation(OBJECT_TYPE_PLACEABLE, lLOC);
	
	if(oTEST == OBJECT_VALID)
	{		
		fDIS = GetDistanceBetweenLocations(GetLocation(oTEST), lLOC);
		
		if(fDIS < 20.0)
		{	
			// Object may have just been made (in double sound play)
			float fWait = 0.0; 
			
			if(GetLocalInt(oTEST, "OBJREADY") == -1)
			{
			fWait = 0.2;
			}
			
			DelayCommand(fWait, AssignCommand(oTEST, PlaySound(sSound, TRUE)));
			iPlayed = 1; 
			sTEXT = "<<< Sound played >>>";
		}
	}
	
	if(iPlayed == 0 && !GetPause() && GetGlobalInt("ESTABLISHED") == 0)
	{
		// Create nearby object anyway (You need to have this template in toolset!)
		oNEW = CreateObject(OBJECT_TYPE_PLACEABLE, "temp_object", lLOC, FALSE);
		
		// Prevent other attempted sounds playing until established
		SetLocalInt(oNEW, "OBJREADY", -1);
		DelayCommand(fDelay, SetLocalInt(oNEW, "OBJREADY", 0));
		
		// Prevent interrupting PC for certain actions
		int iACTION = GetCurrentAction(oPC);
			
		if(!IsInConversation(oPC) && iACTION == ACTION_INVALID)
		{
			AssignCommand(oPC, ClearAllActions());
			AssignCommand(oPC, ActionDoCommand(PlaySound(sSound, TRUE)));		
			
			sTEXT = "<<< Creating new after PC sound >>>";
		}
		
		else
		{			
			DelayCommand(fDelay, AssignCommand(oNEW, PlaySound(sSound, TRUE)));
			
			sTEXT = "<<< Creating new with direct play >>>";
		}
	}
	
	if(GetGlobalInt("TESTMODE") > 0)
	{
		SetNoticeText(oPC, "Play >>> " + sSound + " With tag test >>> " + GetTag(oTEST) + " DIS >>>> " + FloatToString(fDIS), 1, 1);
		
		if(sTEXT != "")
                {
               SendMessageToPC(oPC, sTEXT);}
	        }
}

Edit: My guess is that if you have created a temporay placeable to be able to play the sound you need to destroy that object afterwards too. My temporary placeable I made to be extremely small so maybe one can ignore that…but still…

Here’s an alternate, more simplified, version:

// Creates an iPoint at the location of the currently controlled character.
// A sound file is then played off of that iPoint before destroying it. 

void main()
{
	object oPC = GetFirstPC(FALSE);
	object oIP = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_ipoint ", GetLocation(oPC));
								//	do not remove this space	^ 	
	string sWave = "0_archery_warning_0000";	// <--- sound file name

	DelayCommand(0.2f, AssignCommand(oIP, PlaySound(sWave, TRUE)));	
	SetPlotFlag(oIP, FALSE);
	DestroyObject(oIP, 0.3f);	
}
3 Likes

Mmm, I like that. Compact and effecient as always, @travus!

I’ll try it out.

Tried your script (implemented into my own, for my own purposes) @travus and it worked great. I think I will be using that one, since it’s much shorter and simpler and probably won’t fail. The other script feels more like I could make a mistake trying to use useable placeables.

1 Like

@andgalf,

As I say, the version I posted is a function, which can be used in many situations rather than a one-off event. :slight_smile: As a function it also serves in a much broader sense, and accommodates some issues in a broader usage of it.

The key fixing point is that a placeable can be used to play the sound, BUT may not be what you want. This is the “key” to the issue you were trying to resolve, but only assuming the PC was already carrying out an action. (See next.) However, in time, you may find you want to have this facility in many other situations - or maybe not?

The History of the Function

Now, here is a brief summary of why the function ended up as it did. Basically, the ideal solution is to have the PC play the sound directly, as it plays instantly. However, as others have also discovered, if the PC is already doing something else, we do not want to interrupt their actions to play the sound and so having a nearby placeable do it instead is a good substitute.

A downside of using a placeable is that if it is not close enough (less than 20 metres), then you still do not hear the sound it plays. Therefore, the solution was to create a temporary placeable to play the sound. This, however, requires the 0.2 second delay to play the sound.

Like @travus, I originally also deleted this temporary object in one of my earliest renditions of the function. However, I found that I also needed to have two sounds (or more) play close together. Or, I often needed to play new sounds a short while later. Therefore, deleting the placeable shortly after creation appeared pointless, especially as it took next to no resources and could always be reliably called upon again without the need of the 0.2 delay that it requires on first creation. (My own object was scaled 0.001, not useable, made walkable and plot to eliminate any “intrusion”.)

The ideal solution for my own requirement was to have the ability to:-

a) Have the sound file play as close to immediate as possible, whenever possible.
b) Have a sound file play that did not interrupt the PC actions.

So, this (what seems like a rather convoluted script), actually recognises and overcomes these issues as best as possible, by (a) first trying to rely on placeables I know to exist, (b) the PC themselves if not already doing something and ( c) only relying on a new placeable creation (with 0.2 sec delay) as and when really necessary. If the latter, then no longer destroy this “temp” object, as I can use it reliably again moving forward. Check out the debug feedback to see it in operation.

There is one other last “check” I have in the function, to do with a placeable potentially already playing a sound. This check insures the sound currently being played is not interrupted by another call to it to play another sound before the latter is finished playing. This is unlikely to be required by you, but I left it in to show that it can be required in certain circumstances.

The bottom line, again, like previous points we have discussed, I provide you with the “fullest” fix I have to date, which not only covers your immediate issue, but may well cover points you have not yet considered moving forward. This is how I have always been helped in the forums, and I think helps us all to learn more. :slight_smile:

That all said, the simplest script for a one-off situation where the slight delay does not matter, then the basic script is all you need. However, I would also add that if the PC is not doing anything when the sound is needed to be played, then the even easier option would be to use:-

AssignCommand(oPC, ClearAllActions());
AssignCommand(oPC, ActionDoCommand(PlaySound(sSound, TRUE)));	

Where sSound is your sound file reference.

NOTE, however, that while this is probably the cleanest section of the code to do the task most simply, and immediately, it does require the PC not to be doing anything when the code is called, which is why my checks are in there. (It does not even need any placeable creation!)

It’s a case of take what you need to do the job you require, but note the limitations of what you leave out or how you thereafter go ahead. :slight_smile:

I hoped that helped explain some of the potential pitfalls or more efficient usage for you.

Yeah, this is probably never the case, if not having gone into SetCutsceneMode perhaps, but the PC can/will almost always do something, or have the potential to do something, so I don’t think I would ever use that kind of code, even if it’s the absolute simplest one.

I have saved both your and travus’ script in one of my modules, so I can take a look at them if I would need something more like this in the future. For now though, travus’ scipt is the easiest to understand and follow, and suits my needs just fine at the moment, that’s why I chose to use his in this instance.

2 Likes

It’s based on the main principle of using a placeable to play the sound, which is the point here. So, I can understand why you are going this way. However, if you are ever not sure which would be the best to use, that is where the function will discern and apply the best solution for you, so you don’t have to think about it. :slight_smile:

i.e. If your PC can play the sound because they are doing nothing, then that function will make them do it without needing to create the placeable in the first place. As an example, in the format I had delivered the function to you, you would simply run LBPlaySound(“YourSoundHere”); That’s it. The function would work out how to play the sound according to the situation requirements.

I, again, in a new scenario, tried to make the game play a sound by creating a custom Sound Object in the Toolset. Again, when saving the sound as a mono wav file in Cubase, the Toolset falsely claimed it was not a mono sound.

The way I solved this was to import the sound into a free sound editor program called Ocenaudio which has a lot of odd alternatives to a normal wave file that you can save a file as. I tried a few of them which didn’t work until I tried a format that looks like this:

Wav DVI/IMA Adaptive DPCM(*.wav)

I don’t know what kind of odd variation of wave file this is, but now the sound plays ingame for me at last.

Another weird thing with all of this: For testing purposes I tried to load a sound from Sounds_X1.zip. VLC and Windows Media Player have no problems playing any sound in this folder. However, when importing one of the wave files from this folder to Cubase, Cubase couldn’t read it. Cubase said that it wasn’t a valid format or something along that line. Really curious. Maybe the Toolset and NWN2 uses a very specific format of wave files. I have seen before that wave files can obviously have different formats but…well, I find it odd, but I don’t know all the technical aspects of this. Here’s a list of the different formats you could save the file as in Ocenaudio:

@andgalf,

If I recall correctly, NWN2 does handle .wav files a bit differently. I use an app called MP3toBMU, which also allows you to rename the file (which I believe is actually some form of mp3) into a type of .wav version!?

i.e. This may be something that is also affecting things for you.

I believe this is the latest version …

… And don’t forget the issues I had with conflicting CODECs that messed with some of the sound files. i.e. Sound files can be temperamental subject to your computer setup.

1 Like

BMU files are MP3 files. To get around the patents that were flying around Bioware inserted 8 bytes to the beginning of an MP3. Those bytes are “BMU V1.0” if I remember correctly. You can easily confirm this by opening a BMU file in a plain text editor (definitely works with Notepad++) and look at the very start of the file. FWIW that is also a good way to check that an MP3 is not pirated as MP3 files have plain text information embedded in them such as copywrite notices.

TR

1 Like

https://mediaarea.net/en/MediaInfo

just sayin, might help

@Tarot_Redhand @Lance_Botelle NWN2s soundfiles are wave files and not bmu files, like the music for the game. I know all about the bmu files being mp3 files. That’s nothing new to me. I use MP3toBMU all the time when converting music I put in my modules from wave or mp3 to bmu. What I had, for some odd reason, missed, is the button “rename .wav”, and there it clearly states (don’t know why I haven’t seen that) that it can convert mono mp3 files to wave for use in sound effects. So it’s apparent that, as Lance said, that NWN2 handle .wav files differently.

When working with music in my day to day job, and sounds in general, the .wav files thing has never been an issue asside from checking if it’s 16, 24 or 32 bit or if it’s 44.100 kHz or 48.000 kHz, since forever, until I started working with the Toolset and NWN2.

Bottom line: The lesson for me here is that now I know that I can to use MP3toBMU to convert normal .wav files for sounds to the other .wav format that NWN2 uses (otherwise it will think a mono wave file is a stereo wave file, LOL) (or use Ocenaudio and save as Wav DVI/IMA Adaptive DPCM(*.wav)), as well as when I convert music files (be them .wav or .mp3) to bmu. I also spoke to my brother about this, who is more of a tech guy than me, and he confirmed that there are indeed apparently a lot of variation of the .wav format.

Yes, that’s the thing I mean. :slight_smile: