A way to cut audio before the end of the room using tweenroomlocation

Started by Shaneaphone, Thu 05/09/2024 17:30:36

Previous topic - Next topic

Shaneaphone

First of all - thank you to the genius who invented the tween script module. It's incredible. It's opened up a lot of possibilities.

One I've been struggling with for a while is this:

I have a very long room - 1200 pixels across. My game is in 320x200 pixels. There are four distinct areas that require noise. I'm using global audio channels like AMBIENCE_1 and AMBIENCE_2 to try to play them. To make things more complicated (I designed the room BEFORE i adopted tween) There are three possible entrances/exist to this room. To play the correct sound as the player enters the room I'm using this:

Code: ags

if (player.x < 200)  // L E F T   E N T R Y
{
  AMBIENCE_1 = aForestandsqueak75bpm.Play(eAudioPriorityHigh, eRepeat);
  AMBIENCE_1.TweenRoomLocation(1.0, 2, 133, 300, 100);
}

if (player.x > 1100) // R I G H T  E N T R Y
{
  AMBIENCE_2 = aBeach_Gulls.Play(eAudioPriorityHigh);
  AMBIENCE_2.TweenRoomLocation(1.0, 766, 152, 150, 100, eEaseOutCircTween, eNoBlockTween);
}

I initially thought the fromx and fromy parts of the tweenRoomLocation served this purpose. I now realise that they don't. So my audio from the far left of the room is still audible when the player is very far away from it.

I've tried using hotspots to tween volume and stop/start and fade the audio but it doesn't seem to be working any way I try. I get audio coming back in somehow or abruptly stopping or in a lot of cases breaking the game due to too many tweens being triggered. I can set if statements to see if the channel is playing but that seems to be very hit and miss.

Anyone cracked this or know a simple workaround?

Help me obi wan AGS forums, you're my only hope

Rich
Rich Brown
Beardy Ramblin Games

eri0o

AGS has it's own audio location thing which you can use by setting the location of each audio in room coordinates and I think the volume changes regarding the player character position - you can set them using AudioClip.SetLocation.

Spoiler
This is done in very simple ways - considering we have access to an openAL API in the engine, we could have a more realistical implementation, but someone would need to come up with some design for the API. But openAL is this monster: https://www.openal.org/documentation/OpenAL_Programmers_Guide.pdf
[close]

Anyway, I recommend you don't use any of it and instead script your own thing.

Supposedly you have two (or more) audio you want to play in different places in the room, depending on where the player is. You can simply play all audioclips in repeat at the room load, and set their volumes to zero. It's a good idea to think about your game audio logic, say you have music, and ambient sound, or a radio, anyway, I would set these audios as having different types - it makes some other things easier, remember you need an audio channel to play a sound and you can allocate different audio channels for each type. Or you have three audio channels for music, idnk, it depends.

Anyway, once everything is playing at zero volume, you can just adjust the volume of each audio channel that is playing each audio clip in the room RepExec or in repeatedly_execute_always in the room script according to whatever logic you want.


My "I am lazy to think equations today" approach would be to load the background image in gimp and add a layer for each audio source and color them black and add white as much as I want for where the sound should be and throw some gaussian blur. Save each layer as one PNG image, import in the game project and use (GetPixel*100)/65535 to get the volume for the position.

Other drawing approach can be you use an extra float to store the volume for each audio and then you use regions for the sound volume but instead of setting the sound it approximates to it using a lerp or something else that smooths the volume setting (like a moving window or whatever).

AndreasBlack

Have you tried with regions walk on/off? I think i did something similar with that. I have this guy that is using his drilltool and it's just going to sound when the player is close to him..

Shaneaphone

Quote from: eri0o on Thu 05/09/2024 21:41:48AGS has it's own audio location thing which you can use by setting the location of each audio in room coordinates and I think the volume changes regarding the player character position - you can set them using AudioClip.SetLocation.

Thanks so much. I've been stuck on this issue for 2 days. I actually went away and did exactly this (which is reassuring that you came to the same conclusion). Basically using volume like integers. I have something like this now:

Code: ags
  if (player.x > 599 && player.x < 900)
 {
   
   if (AMBIENCE_4.Volume == 10)
   {
     AMBIENCE_4.TweenFadeIn(1.0);
     AMBIENCE_4.Volume = 100;
     
   }
   
     AMBIENCE_1.Volume = 0;
     AMBIENCE_2.Volume = 0;
  AMBIENCE_3.Volume = 10;
  
   
   
 }

So a series of these with the different regions and I can manually alter the volume of the adjacent sounds.

Though I think the fading might be an issue. And it's not 'solved' until I get the actual layers from my sound man. I've been using 5 different notes on a keyboard for each region at the same volume to try and guage it (would not recommend for sanity lol). It 'works' but I don't know how abrupt the changes are going to be so I wouldn't consider the 'case closed' until then.

Could you explain this one for my simple brain: "Other drawing approach can be you use an extra float to store the volume for each audio and then you use regions for the sound volume but instead of setting the sound it approximates to it using a lerp or something else that smooths the volume setting (like a moving window or whatever)." I don't know what a lerp is.

Thanks again, I was REALLY stressing over this one.
Rich Brown
Beardy Ramblin Games

Shaneaphone

Quote from: AndreasBlack on Fri 06/09/2024 20:00:39Have you tried with regions walk on/off? I think i did something similar with that. I have this guy that is using his drilltool and it's just going to sound when the player is close to him..

Hadn't looked into this idea. Rarely use regions. If my volume idea doesn't work I might investigate this. Thanks. I feel better knowing another angle to try if this doesn't pan out.
Rich Brown
Beardy Ramblin Games

eri0o

@Shaneaphone here's an example of what my idea of regions: ericoporto.github.io/public_html/sound_test/

In this example, the regions looks something like this

Spoiler
[close]

The room script code looks like this

Spoiler
Code: ags
// room script file

// General idea, we are going to create a circle around the player 
// with the camera height as diameter and then we will figure how much from
// the circle is made of each region
// we will then attribute some volume for each thing in each region 
// and then we will use this and some lerping to set the volume for each audio track
// remember to set the audio channel per type accordingly

// how many regions we care about, including zero
#define MAX_REGIONS 6

struct RegionAudioVolume {
  float Loopyloading, Fantasy, Temple, Waves, Clock;
};

RegionAudioVolume region_volume[MAX_REGIONS];

// >>>>>>>>>> IMPORTANT <<<<<<<<<<<<<<<<<<<<
void InitVolumesPerRegion()
{
  region_volume[1].Loopyloading = 100.0;
  region_volume[2].Fantasy = 100.0;
  region_volume[3].Temple = 100.0;
  region_volume[4].Fantasy = 50.0;
  region_volume[4].Waves = 100.0;
  region_volume[5].Clock = 100.0;
  region_volume[5].Fantasy = 60.0;  
}
// >>>>>>>>>> IMPORTANT <<<<<<<<<<<<<<<<<<<<

AudioChannel* acLoopyloading;
AudioChannel* acFantasy;
AudioChannel* acTemple;
AudioChannel* acWaves;
AudioChannel* acClock;

float _Lerp(float from, float to, float t)
{
  return (from + (to - from) * t);
}

float _FRandom()
{
  return IntToFloat(Random(8192))/8192.0;
}

Point* _RandomPointInCircle(int diameter)
{
  Point* p = new Point;
  float r = (IntToFloat(diameter)/2.0)*Maths.Sqrt(_FRandom());
  float theta = _FRandom() * 2.0 * Maths.Pi;
  p.x = FloatToInt(r * Maths.Cos(theta));
  p.y = FloatToInt(r * Maths.Sin(theta));
  return p;
}

#define CIRCLE_POINTS_CACHE_SIZE 8192
Point* _circle_points_cache[CIRCLE_POINTS_CACHE_SIZE];

void room_Load()
{
  oClock.SetView(VIEW_CLOCK);
  oClock.Animate(0, 4, eRepeat, eNoBlock, eForwards);
  player.PlaceOnWalkableArea();
  int circle_diameter = Screen.Viewport.Camera.Height;
  for(int i=0; i<CIRCLE_POINTS_CACHE_SIZE; i++)
  {
    _circle_points_cache[i] = _RandomPointInCircle(circle_diameter);
  }
  
  acLoopyloading = aLoopyloading.Play(eAudioPriorityHigh, eRepeat);
  acLoopyloading.Volume = 0;
  acFantasy = aFantasy.Play(eAudioPriorityHigh, eRepeat);
  acFantasy.Volume = 0;
  acTemple = aTemple.Play(eAudioPriorityHigh, eRepeat);
  acTemple.Volume = 0;
  acWaves = aWaves.Play(eAudioPriorityHigh, eRepeat);
  acWaves.Volume = 0;
  acClock = aClock.Play(eAudioPriorityHigh, eRepeat);
  acClock.Volume = 0;
  InitVolumesPerRegion();
}

// how many points to sample from the circle, less is faster but less precise
#define SAMPLE_POINTS 200

float[] GetWeightedRegions(int x, int y)
{
  float regions[] = new float[MAX_REGIONS];
  int x_limit = Room.Width;
  int y_limit = Room.Height;
  float increment = 1.0/IntToFloat(SAMPLE_POINTS);
  for(int i=0; i<SAMPLE_POINTS; i++)
  {
    int r_x, r_y;
    
    Point* p = _circle_points_cache[Random(CIRCLE_POINTS_CACHE_SIZE - 1)];
    r_x = p.x + x;
    r_y = p.y + y;
    if(!(r_x >= 0 && r_x < x_limit && r_y >= 0 && r_y < y_limit))
    {
      r_x = p.x;
      r_y = p.y;
    }
    
    Region* sampled_region = Region.GetAtRoomXY(r_x, r_y);
    regions[sampled_region.ID] += increment;
  }
  return regions;
}

float smooth_regions[MAX_REGIONS];

void CalculateWeightedRegionOfPosition(int x, int y)
{
  float regions[] = GetWeightedRegions(x, y );
  for(int i=0; i<MAX_REGIONS; i++)
  {
    smooth_regions[i] = _Lerp(smooth_regions[i], regions[i], 0.05);
  }
  //System.Log(eLogInfo, "0: %f, 1: %f, 2: %f, 3: %f, 4: %f, 5: %f", regions[0], regions[1], regions[2], regions[3], regions[4], regions[5]);
  System.Log(eLogInfo, "0: %f, 1: %f, 2: %f, 3: %f, 4: %f, 5: %f", smooth_regions[0], smooth_regions[1], smooth_regions[2], smooth_regions[3], smooth_regions[4], smooth_regions[5]);
}

void UpdateVolumesFromRegions()
{
  CalculateWeightedRegionOfPosition(player.x, player.y - 10);
  RegionAudioVolume total;
  for(int i=0; i<MAX_REGIONS; i++)
  {
    total.Loopyloading += smooth_regions[i] * region_volume[i].Loopyloading;
    total.Fantasy += smooth_regions[i] * region_volume[i].Fantasy;
    total.Temple += smooth_regions[i] * region_volume[i].Temple;
    total.Waves += smooth_regions[i] * region_volume[i].Waves;
    total.Clock += smooth_regions[i] * region_volume[i].Clock;    
  }
  
  System.Log(eLogInfo, "L: %f, F: %f, T: %f, W: %f, C: %f", total.Loopyloading, total.Fantasy, total.Temple, total.Waves, total.Clock);
  
  acLoopyloading.Volume = FloatToInt(total.Loopyloading);
  acFantasy.Volume = FloatToInt(total.Fantasy);
  acTemple.Volume = FloatToInt(total.Temple);
  acWaves.Volume = FloatToInt( total.Waves);
  acClock.Volume = FloatToInt(total.Clock);
}

void repeatedly_execute_always()
{
  UpdateVolumesFromRegions();
}
[close]

There is probably some saner way to do this and someone else can figure some pretty way to package as module. In my case I usually play with sound differently in any chance I can because it's fun, you can do hackery and it usually sounds alright.

Here's is the example game project: TestSound.zip

I recomment running it and playing with the volume values at the top of the room script to get a feel of it.

The way it works is it samples some somewhat 200 random points in a circle that has a diameter equal to camera height and then averages the region of each point and then uses this average to adjust the volume of all the running audio clips. Then you just draw regions that make somewhat sense to how you want the room to sound (since you know you are sampling a circle you can just set a brush in your favorite image editor to have matching size to get a by the eye idea of how things will sound in different places).

Shaneaphone

Thanks a lot. That's a really detailed explanation and example.

I'm still awaiting the actual layers from my partner in crime. I've reached the point where it kind of works but depends on the ambience. So this is another nice little avenue to explore. Also it'll help me learn so double thanks matey!
Rich Brown
Beardy Ramblin Games

SMF spam blocked by CleanTalk