Game Development Community

[RESOLVED] Audio Datablocks in SimSet and/or working TGB Audio Manager

by Chris Hoopes · in Torque Game Builder · 12/16/2012 (11:04 am) · 17 replies

I've been trying to build out behaviors that allow an editor through the TGB interface to assign sound effects to various actions and I've run into a bit of a brick wall. Entering a predefined AudioProfile into a text field works fine but it would be nice to be able to select an audio file from a dropdown instead.

I found this TGB Audio Manager that was written for an older version of TGB but it doesn't seem to work at all under 1.7.6. Options aren't saved, imports don't work and the interface itself feels kind of broken. And for some reason once you close and reopen TGB it removes itself as a resource until you manually add it again.

So if being able to add and manage sound files through the TGB interface is too much of a pipe dream I'd at least like to be able to manage the audio datablocks myself and then loop through some sort of SimSet to create the dropdown myself. My issue though is that I have no idea what the syntax for that would be and I've yet to find any examples of someone doing the same thing. I though that creating something akin to the /gameScripts/manages/datablocks.cs file but with AudioDescriptions and AudioProfiles would work but this just throws all kinds of errors:

$audioDatablockSet = new SimSet() {
   canSaveDynamicFields = "1";
   setType = "Datablocks";

   datablock AudioDescription( AmbientSoundMed )
   {
      volume = 1.0;
      isLooping = true;
      type = $AmbientAudioType;
   };
   
   datablock AudioProfile( PlaceholderAmbient )
   {
       filename    = "./data/audio/music/placeholder.ogg";
       description	= AmbientSoundMed;
       preload     = true;
   };
};

I thought that maybe I needed to use the "new datablock" command in there like how datablocks.cs does "new t2dImageMapDatablock" but that didn't work either.

So to anyone who has attempted something like this, how do I go about providing a nice clean dropdown list of audio profiles that my level designers can pick from and is there any possible way to get the TGB Audio Manager working with the 1.7.6?

Thanks for any help you can give.

#1
12/16/2012 (5:19 pm)
I remember fiddling with this previously, but ended up settling for a much simpler (yet more complicated :) solution which doesn't need an audio manager. I just put sounds of different types into one of three directories, and a script (re)creates the files needed for them when launching the editor.

Example from setup scripts in a project:
// Build a music-only datablock file
function buildMusic()
{
	%blocks = "~/managed/music.cs";
	fileDelete(%blocks);
	fileDelete(%blocks @ ".dso");
	%music = new FileObject();
	if(%music.openForWrite(%blocks))
	{
		// Get content of the music directory, which we
		// presume are all looping tracks.
		%s = "~/data/audio/music/*.ogg";
		%file = findFirstFile(%s);
		while(%file !$= "")
		{
			%file = fileBase(%file);
			%path = "~/data/audio/music/" @ %file @ ".ogg";
			%music.writeLine("datablock AudioProfile(" @ %file @ ")n{");
			%music.writeLine("	filename = "" @ %path @ "";");
			%music.writeLine("	description = "MusicLoop";");
			%music.writeLine("	preload = false;");
			%music.writeLine("};n");
			%file = findNextFile(%s);
		}
		%music.close();
	}
}

// Used by behaviours for configuration.
// Builds a string suitable for dropdown menus, containing
// all files according to the pattern supplied.
function fileDropdown(%pattern)
{
	%file = findFirstFile(%pattern);
	%file = fileBase(%file);
	%list = %file @ "	======================";
	%file = findNextFile(%pattern);
	while(%file !$= "")
	{
		// Ignore other files as you see fit
		if(strpos(%file, ".DS_Store") == -1)
		{
			%file = fileBase(%file);
			%list = %list @ "	" @ %file;
		}
		%file = findNextFile(%pattern);
	}
	return %list;
}

// This line should only be called from the editor
if(($Editor::musicbuild != true) && (isToolBuild()))
{
	buildMusic();
	$Editor::musicbuild = true; // Ensures it's only called once per session
	$Editor::musicdropdown = fileDropdown("~/data/audio/music/*.ogg");
}
// Load music datablocks
exec("~/managed/music.cs");

Example behaviour which uses this:
if(!isObject(MusicCrap))
{
	%template = new BehaviorTemplate(MusicCrap);
	%template.friendlyName = "Music crap";
	%template.behaviorType = "My custom stuff";
	%template.description  = "Set up background music.";

	%list = $Editor::musicdropdown;
	%default = getWord(%list, 1);
	%template.addBehaviorField(Music, "Background music", ENUM, %default, %list);
}

function MusicCrap::onAddToScene(%this)
{
	alxStopAll();
	if(isObject(%this.Music)) transitionMusic(%this.Music);
}

Tested and mostly working. If it breaks anything, you get to keep all the pieces.
(Probably uses some stuff from the platformerkit for transitions, so you'd have to modify that as needed.)
#2
12/16/2012 (8:07 pm)
This looks really promising. A few questions/comments though. First off there were some small syntax errors in the code you posted. Some unescaped quotes in strings and two stray 'n' characters that were breaking things. The corrected code:
while(%file !$= "")
{
	%file = fileBase(%file);
	%path = "~/data/audio/music/" @ %file @ ".ogg";
	%music.writeLine("datablock AudioProfile(" @ %file @ "){");
	%music.writeLine("	filename = "" @ %path @ "";");
	%music.writeLine("	description = "MusicLoop";");
	%music.writeLine("	preload = false;");
	%music.writeLine("};");
	%file = findNextFile(%s);
}

But now that the code compiles correctly I still can't get this to run as it seems for some reason opening the project in TGB never passes isToolbuild(). If I get rid of that check and just run the project through Torsion the script executes correctly but throws the generated file into "C:UsersMeAppDataRoamingIndependentUntitledGamegamemanaged"

So how can I debug this? I thought it might be a permissions problem but I gave everyone full permissions for all files and folders in my project. And I can't do simple echos because launching the TGB and opening a project doesn't write anything to console.log.

Thanks for your help so far and if we could figure this last problem out it'd be great as I'm sure other people could benefit from this.
#3
12/17/2012 (6:42 am)
TGB's console log is in <installFolder>/tgb. TGB does not write to the project console.log file (that would be really annoying and confusing).

There is also a Torsion project associated with TGB in <installFolder>/tgb/Torsion - this way you can add tool scripts like this and debug them through Torsion.
#4
12/17/2012 (6:57 am)
I did not know that! I'll give fixing it a shot and report back with what I find later then. Thanks!
#5
12/17/2012 (9:06 am)
Yeah, little-known secret! Very handy, and you can look at how the current tools are made to help you make your own tools.
#6
12/17/2012 (10:02 am)
Sorry to be a pain about this but there must be something that I'm not understanding about the project loading workflow here. What I've done so far is create a new file with the above code in /MyProject/game/gameScripts/AudioDataBlocks.cs and then attempt to execute it from somewhere that gets called when the TGB loads. Based on the /tgb/console.log file it looked like a good place for this would be in /MyProject/common/gameScripts/projectManagement.cs as this is where project resources are loaded when the editor is launched but is this incorrect? It seems like no matter what file I modify in my project I'm not changing anything in the TGB log file and nothing different is happening when the tool is launched.

Am I just completely misunderstand how this is meant to work?
#7
12/17/2012 (12:49 pm)
Alright - it sounds like you're missing the disconnect between Torque Game Buider and the game itself.

/MyProjects ... is for the game. Almost none of this gets executed until you actually run the game.

<tgbInstallFolder>/tgb ... is the editor, this is actually the "builder" part of Torque Game Builder.

I think that the audio "manager" functionality that you're trying to get here (automatic generation of profiles) belongs in the editor portion - probably somewhere like /tools or maybe /tools/editorClasses. Then exec() it in /tools/main.cs.

If you're feeling really ambitious you can build a gui pop-up for setting profile properties for your sounds.
#8
12/17/2012 (1:10 pm)
There were no stray letters - they were supposed to be a backslash and an n to split lines :)

Seems the forum software is still broken, and mangles code in interesting ways when anything but a-z, A-Z, 0-9 or simple punctuation is used. If you want corrected code (and the complete file with line endings corrected etc.) you'll have to visit IRC ;)

The original audio manager most definitely needs to be plugged into TGB's editor script hierarchy somewhere. It's a scary place.

My sound solution just needs the first chunk of code exec'd at the right spot - which I forgot to mention. I actually put that into the "behavior" folder which the system automatically executes. In my project it's in "game/scripts/behaviors", but I might have made some modifications to structure. Everything inside that folder is recursively executed by some setup script that gets called by both editor and game executables. Code outside functions is directly executed, so it's a good place to do various setup, checking for existence of files and creating them if need be.

The second chunk of code can be the entirety of a behaviour script, and added to one object in a level (via the TGB editor) to hold various settings. The full version of the behaviour does a few more things for level defaults. You can go nuts with settings there. Unfortunately behaviour fields for the editor are not dynamic; they're only set up as they are added to a scene. Perhaps a future editor can improve on this, and also make audio a first-class citizen.

(Ask on IRC if you're really stuck - much faster response there if I'm not in Borderlands 2)
#9
12/17/2012 (1:31 pm)
Oh I didn't know there was an IRC channel y'all hang out in. Is it the one from the discussion thread (Server: irc.maxgaming.net, Channel: #GarageGames)? If so I'll gladly join in while I jam on stuff later. In the mean time I'll try what you've suggested Ronny.
#10
12/17/2012 (1:53 pm)
Yep, that's the correct server & channel.
#11
12/17/2012 (2:24 pm)
Awesome. Well I moved the code into a behavior like you suggested and it works flawlessly. I'm splitting all of the audio up into music, ambient loops and one-off sounds for ease of storage and sorting and having them as a dropdown will be so much easier for everyone once we start building out levels.

And yeah I'll idle in that IRC when I get to workin' from now on. Thanks a bunch!
#12
12/17/2012 (4:55 pm)
See? Now I have another side project. I'll plug that into the editor and add some gui support so you can choose sound channels, channel volume, looping, etc when importing sounds, then I'll drop it back in your laps to play with.

Probably be after winter break before that gets done, though - I'm also trying to finish my Bioware-like conversation system (with editor for T3D). Then port it to T2D - again, the editors should be the fun part....
#13
12/17/2012 (6:30 pm)
It's always best to get other people to do the work for you :)
#14
12/22/2012 (7:28 am)
I started writing a cool editor that would almost automatically drop into the TGB editor (add itself to the project menu and all) and then I realized that it won't work in T2D MIT anyway....

So I settled for dropping this in audio.cs, right after the first two audio description definitions:
new AudioDescription(AudioChannel3)
{
   volume = 1.0;
   isLooping = true;
   isStreaming=true;
   is3D = false;
   type = $musicAudioType;
};

new AudioDescription(AudioChannel4)
{
   volume = 1.0;
   isLooping = true;
   isStreaming=true;
   is3D = false;
   type = $musicAudioType;
};

%file = findFirstFile("game/data/audio/*.wav");
while (%file !$= "")
{
	%fileName = "game/data/audio/" @ fileName(%file);
	echo(" @@@ creating AudioProfile for " @ %fileName);
	%soundName = fileBase(%file);
	%name = strreplace(%soundName, "loop", "");
	if (%name !$= %soundName)
		%desc = "AudioChannel1";
	else
		%desc = "AudioChannel2";
	new AudioProfile(%name)
	{
		fileName=%fileName;
		description=%desc;
		preload=false;
	};
	%source = alxCreateSource(%name);
	echo(" @@@ " @ %name.getName() @ ":" @%name.fileName @ ":" @ %source @ " created");
	%file = findNextFile("game/data/audio/*.wav");
}

%file = findFirstFile("game/data/audio/*.mp3");
while (%file !$= "")
{
	%fileName = "game/data/audio/" @ fileName(%file);
	echo(" @@@ creating AudioProfile for " @ %file);
	%soundName = fileBase(%file);
	%name = strreplace(%soundName, "loop", "");
	if (%name !$= %soundName)
		%desc = "AudioChannel3";
	else
		%desc = "AudioChannel4";
	new AudioProfile(%name)
	{
		fileName=%fileName;
		description=%desc;
		preload=false;
	};
	%source = alxCreateSource(%name);
	echo(" @@@ " @ %name.getName() @ ":" @ %source @ " created");
	%file = findNextFile("game/data/audio/*.mp3");
}

It goes through your audio files and creates profiles that match the file names (without extension, of course). If you want your sound to loop, add "loop" to the sound filename. Or you can change that to _loop or whatever.

Note that mp3's don't play on Windows! Even if you change the extension to .wav....
#15
12/22/2012 (8:24 am)
Hey that's pretty handy. Thanks!

And while we're on the topic of sound, I was looking for the min and max values for volume passed to alxSourcef() when I found this thread. Is that correct in that 0 is silent and 1 is the maximum volume?

And how about for AL_PITCH? 1 is definitely the audio file's default pitch so does that mean the values go from 0 to 2?
#16
12/22/2012 (2:05 pm)
Yes, but it's better to ensure you pass a float there - 0.0 to 1.0. Sometimes TorqueScript does weird things if you pass 1 when it expects 1.0 (though I haven't seen any issues with this particular set of calls).

As far as what the different AL_ bits do, these are directly from the OpenAL spec and can be looked up there. Here's what they say about that one:
Quote:
AL_PITCH Description: Desired pitch shift, where 1.0 equals identity. Each reduction by 50 percent equals a pitch shift of -12 semitones (one octave reduction). Each doubling equals a pitch shift of 12 semitones (one octave increase).

And I guess I should clarify my script post just a hair - that script will allow you to call alxPlay(<yourSoundFileName>) and it will play the sound from that file name (no extension).

Please note that I only spent about an hour on writing and testing that - and most of that was spent discovering that a sound that I had named with .wav was actually an .mp3....
#17
12/22/2012 (2:21 pm)
Oh excellent. Yeah I set up my ambient sound behavior to take in floats for both the volume and pitch so I should be good there. And thanks for the clarification on OpenAL spec, I'll keep that in mind once we start compiling sounds in earnest.

And I've completely had the naming thing happen to me. It makes you feel like you're losing your mind until you figure out what's happened.