Page 1 of 1

Sound volume based on distance

Posted: Sun May 07, 2023 7:12 am
by jinxtengu
Hi.

I wanted to find a way to code Sound effects so that
the volume of a sound is louder the closer one
is to the object which is emitting the sound.
I am using .ogg as the primary sound format.


Lets say for example a cricket is chirping in the game,
and we wanted the sound to be audible as the player
approaches the source of the sound.


would we call an expression just before the sound is called
which performs a calculation of distance and sets
sample volume based on a ratio of that distance?


Again I may need to be reminded of how to code
relational distance between two models.
Any Ideas? :D

Re: Sound volume based on distance

Posted: Sun May 07, 2023 10:31 am
by Kjell
Hi jinxtengu,
jinxtengu wrote: Sun May 07, 2023 7:12 amI wanted to find a way to code Sound effects so that the volume of a sound is louder the closer one is to the object which is emitting the sound.
Simply calculate the distance between the sound source / emitter and the camera and set the sound volume accordingly. Here's a example ( press left mouse button to walk ).

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" CameraPosition="0 1 10" FileVersion="2">
  <OnLoaded>
    <SpawnModel Model="Cricket" Position="0 0.25 0" SpawnStyle="1"/>
    <ZExpression>
      <Expression>
<![CDATA[//

CricketSample.Length = 12000/44100;]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[//

Mouse[1] = Mouse[0];
Mouse[0] = App.MousePosition.X;]]>
      </Expression>
    </ZExpression>
    <KeyPress Keys="{">
      <OnPressed>
        <ZExpression>
          <Expression>
<![CDATA[// Simple debug camera control

float dt = App.DeltaTime;

//

App.CameraRotation.Y += (Mouse[0]-Mouse[1])*8*dt;

float a = App.CameraRotation.Y*PI*2;

App.CameraPosition.X += sin(a)*4*dt;
App.CameraPosition.Z -= cos(a)*4*dt;]]>
          </Expression>
        </ZExpression>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnRender>
    <UseMaterial Material="GridMaterial"/>
    <RenderMesh Mesh="GridMesh"/>
  </OnRender>
  <Content>
    <Array Name="Mouse" SizeDim1="2"/>
    <Model Name="Cricket" Position="0 0.25 0">
      <Definitions>
        <Variable Name="CricketTimer"/>
      </Definitions>
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[//

float a, s, x, z;

// Get position relative to camera

x = Cricket.Position.X - App.CameraPosition.X;
z = Cricket.Position.Z - App.CameraPosition.Z;

// Calculate distance to camera

s = sqrt(x*x+z*z);

CricketSound.Volume = clamp(1/log2(s), 0, 1);

// Calculate panning from normalized vector

a = App.CameraRotation.Y*PI*2;

x /= s;
z /= s;

CricketSound.Pan = (x*cos(a)+z*sin(a))+0.5;

// When timer runs out play sound random number of times

CricketTimer -= App.DeltaTime;

if(CricketTimer < 0)
{
  CricketTimer += 2;

  CricketSound.Length = CricketSample.Length*floor(1+rnd()*4)-0.02;
  @PlaySound(Sound: CricketSound, ByReference: 1);
}]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <UseMaterial Material="CricketMaterial"/>
        <RenderMesh Mesh="CricketMesh"/>
      </OnRender>
    </Model>
    <Mesh Name="CricketMesh">
      <Producers>
        <MeshSphere ZSamples="4" RadialSamples="4"/>
        <MeshExpression Scale="0.5 0.25 0.5" Expression="//"/>
      </Producers>
    </Mesh>
    <Material Name="CricketMaterial" Shading="1" Color="0.502 1 0.502 1"/>
    <Sound Name="CricketSound" Length="0.8163" Volume="0.301" Sample="CricketSample" UseSampleHz="255"/>
    <Sample Name="CricketSample" Length="0.2721">
      <Producers>
        <SampleImport SampleFileFormat="1">
          <SampleData>
<![CDATA[78DAD5577B38945DD7DFE314A1E6293234344A654A8590F1AA87898C53324C0C4D72981C0735333AEA99278784248D7918D3E4D4C8D054928A128A3424724A52A44C99D299228F77CFE8F9DEE7BBAEF7BBDEEBFBE3FBE3BBAF7DEF7BEDB5D7FDDB6BADBDF6BAD7ED111AEA0514C0CFCB69FDEC7391D5DB2B0803445CCCDEA070BA8C8170289F9D61D5CC3EAF233C646F82BFBF8980CF62BBD1E9F9FF98F9FB65AE388BB31A4EFB86C786ADF5D81B8A890A0FDA2EE7629C31E6A666E6A6E6A6EB31C61EB4E8F0D8BD543A359A81952FAB3CFBEA12FCE6ED08D902A8606FEC965CE22397E3BA74B6D996DCA02C97E3E71217E10967E85A5EA5B85E5E64F98DAB9844A49ED736AC722E318318AE1B9C29A390AECECB190B6C569E0946FA3BD7F7E444A6D957A11335B94437EC5D363115AFAAEB958E6D64135DF0739633E6D98CA0BD325C8CEDAFF3E86557399CE0B45246968BC428C40D4FC02665B9CC39E7B5154FB0E4F410B1FA742231C2FE355C125F8549D43CDA9E4992E9596787D4AB980BB601804C2D40A52A2426972B00730094F28D335E18672CC365053B2F928B40E72301A88BD5723EA575FFB291EB2313D74E074F527CB7158934CEA4F4CA1AC622445B866A4728C93DA78769D00841A96A92950106BA688B710352F12FF78020635CCA8ACA91C3F48C84DDB8B0558D238CD8A5DD23CED1AB464678F436DCCA3C7A93B584474FDDBBDB2C0CCAF1E80B1276636729FEDD11FB48A18C6AC29524ECD9828272DCCA11FB08AB1EC85B5429C045AC6D84D4D25723CEB4B5DD723CC9CF4EA853428FDD2A432E1C1FF18BC6EB406A936EC9213ADE5AF277B959F7407BED5156C97B4A3084B345BFA31C53C29B8BE70C14DD310AD3D83BB7D4A9A8E8147114C3F66E72C69E99B55C1500C3442D22C130E9679762988C0A6AC696F08ECE5D555A5C986A2E36B9D26ADE22EBA497AEB4D9BC35BD8BDCAC71A17281ABFBD56EDFE00B3724BCC8798FFCC22F5CED71C19E83BB8C95FB8F057551D50DC9B4DF320B2AE0ED1563CFE72BC8DDC902F273617B9FEEE0C4F857BB47B775623B38C356E9D6D2E5E6A14DEACEA6F8F75007C669813A710313CCE713CCE1DEA4403287FADFDAACE9D0121612AF3A6BC8395470AB9924FFF436FC5F66CA36B4CE13DDB8C7C869AFD1FD2C13E7D316AE576C1F74DABA7F71F0D472EFF476F461380E543AF6B1DD7B19EE7D0C12F92A855C45EBEBA1057068CF27E2FFD698FA72130900BC26641413528B0919C3D659468802C332007465C1629C5A649D314CC83272E6143BF35F910B774708420F0ACF2BC845EC66CF3300C6DC1758C10B6CE10B5CE1329742AA5FA12042649C5721AD16FDA7B6561EE09900B49F42DF3F858601EE0C0984DCBBE632EFC66ADCCB84410F23FEA78D1E5FA043291DCA7FDF00004C8DEE104D9CE816CE6C6FD7ABDEEE57BD5DB5491EA4FF7459BA775AB843C72E87A70ED4059DA87F85315CBEFCD5336403F234DF6C85E1D59D864AA7B29E99A96827F0CD0B35F63EC30F23130CA0D37E010ED0F226CC3D24BD19E7547A7A014E729EC1D693A0CF651043B18D39410F8CB31F13CB4FD033884E178AD841266BE2D8C413A54D50590534BC4D12A1FA4AC01380443302B6582B281D57C2DB9C8E95E4D093CD46D0F40C9C249F415C25E131B6BADC45C161098F4EB459994DDFEAB2B200C60798036FF582F64C6D841D0FB0ECB36C3D3E5978B22D3CB41CBD18144F0B4AEF27DBEE2F6E5EA48D1DDE242F12ADCF9B44E6D83EB4D8D8E94D7A6841EA8643983B388EA46E5A2793D9CF200D7068FD1314FF45719E24DA8037A57F03A9779CD2371E4FEE61067FB2257D75EB646EEC6450BB99347F268532E1E6C571EFCDA6763269FDD954CA3E6ADF84652783D249A2F631287D71549F6B14F2787C2733E9793635C0874A46C5512692FC7BB35F3128033E549F0DDBE1AC7F6FD2500F3330D7D6830491290F2D283E4C4A800FA5B7C7B2932D43EEF3A690E32032C5A787F9D04286DC6F45F1E1C8907D38CC01064586FC8DE433CE24F7660FF5D08636507A2748E4F124382B43BEC1A4A03691BEC67BC5250D31A8CF7B939E93A329D69C0E667C60366DC8974AC61D19F2E50C4C645321942FAD0FB59DBC2839E0DB9957E3F1C3FB9914DDE801496BF57B8D7BEF8BA7C49DDDB4E0DCF801F2EE807D1943289BDB1F864B8517F344AB354A4CD46FBF93365D99AAAD481749A78470B8AABFE1B2B4F6F38FDA4B26456F271B225A1A2E3DA9327B5A1B6D723B16DD1229159B496B3DAB988703F6CFBDF399C1A41CD84DB937CE640C3B591F11C3AD45C8B676A1D8AEC070B502CB1A014E1A1D2F0C21171A9339C6A982D7D58561D5A2E2704169383FDC8F1BCA15840B2BB05C91493837DC8FBF9BCB2DC915847145915C4179AA00DB2288105784732BDE5657D06A4451FD85E1F261145760525351D62F7A3B5A5D4C16941EE08747F021945C585476801F71901F26E41B0B851142E105A1D0C41FCE725F970A56A6428EE00257B8AA45207C52112EAC8A120A6935C2B79315346935D4842614ACC91346C8902B4CFAABD741AD64C85C49AE40BEAE70B53FBF842B431E392E80E0267902A19823C91542E4D1E3FC483972C41361385786BC264FB0AA45449BAA958A2BA285C228882C952919D552FB5E2C1C931B2857A3624D7FC5FB968AF229B9813235846B5B2A688BAF444D5587717F6AB5AABF62CD5435D4EABD581453262C97CA866393A25869B5E9CD8AA82961F964F5EAFEEA18A979FF78BC030C412F4B5A3F3C273004C7A990D56141F32791FCADDCBBABA83004614A0DB470EF83A7A8C7BDBB9246E6D002ABA84350F81AD57F9CD99BCD0CE88D1FEEA50E8C5457BC6BA9B830252C9BAC1A9B6A88958A3EC00F3A2A16A672D3020D2DBD8A0520056685F4C07433D513C436DC9C024F314E820969B35735CC6EC5A91AB2B3208FCD369760D859445548F9A91BB2D9C4486500B4AC61DE7A9C8FBD6C2D8B1604CB1B8084615FE8CFC2882715C1CE7CE8D55261D53B024772BC301CEE943FE4084A8522A9943BCB918E42FFC839D5D56BB985A590535D21829B2214ADAEA9782B8584502A16994C3508AB45ABA14BA50D97A030DC82FEAA98A7307FD7BE9F6A387501A6F11F0D661A4E0830A8360FD8295B9567C4122D16F98635BB008C226099AA182620F12A8689994417C373EC05305D0FC2CFEA2EBCAEB145882340AE840ED98C2D967D73CFFDFB8EF03F4DFCDBAEB4F87F21FCFFB2CBFEBF01865FE2D4589079F0AFC824C0204BF774C136F112906623A8E07667636C502BAE090D43F1153A11490C5F734D0C63B4B88B48CBA513FDD2A0D07A4971B196FF03DC312231EDF2083772ACB8727BA4C852E2E75766D9CBA33FB851B9285204AB707A8B59AFBCE443FD2CFE94C1A0853560193CBE64D3E0DCE1BBEBAF9056D03D2713798D24A6E32470D1A549B2A1AAA1B76CC840E2CFA3E8B2739298299F8585137D9E8DBCD89EA32F1B16B065472911E9626C289F4D44DAA8FE45C93AA28B9C522B60C34A7CD7EF8AC08EA559B68CA775427991CC05B0BE60A139F9C4B644FB0CFB939AF662CFF59A78375C476ED0D688F5DA5E9938E419C602DD08FBBE9DE6EA307B6300A64E0989C2187FC3ABA698C12236A515A99644BA5497AFA1871E41D4293A243A80CC9C7C42CD75D65AC51DED8D943978553CF2A82FDA0EA1666D3D94BCF2D987C4C5BC7C2FEF518CBCB0D10083F058E583A54BB96597AED735B73F1E7CFD4175F60F0C7C03C07A132B25656495C95A53730B2B6B1BDBFFFA4F63B158F2A79D9C96FF8929859AFDEB4F4C2658FC728717D600BD586FF162949E9E015A4F1FADA7ABA78B5EA2AF8F42A1F4D1BB76A3503A68747048C83E7A3091278EC52BB62BBCE878BEB4B979F2E0BACD835BDE278D5FD1F0C297ECB4D7E2B5451C7D6038B6CE3B00E3317D9DBC68ED7AB65DF19F9BEEDF7D9397D9F534E589727E0106DC559BD1D8DFBC3CAEE3E1DC33E72C765DFB64FCF229454DC14E19A42C3C761BB1DB6071E06F7D31458FC09E7085D61EC1514D3B459052F2DBAF9AFB477D349C4AB2A30C9CD3773829553E62AAA9E4AB3477AEB30C1CB35C3AF2EE98660FE65BC7C9075A804D98983B79F36D344B3530A6333B5EB7A4D9DBE844478E42A23822ACAD65CC8BE540CB7BBCDC2DFE63755F3C76B0154D06E9E2315496CEE760B0CBE1F6C8444F69DAB192A3F5F66D2FA9A0BCEB3550B7534C59285D376137BD85A3CE0BDB59BD958FCA247C7F81074F9F9E4F55CE570177EDDB1109AE5844EC8AE409ED20DAB7AF3853D95C1668CF84A96DD20D21AC099B77EB46248D50178B045D8F45298AF988BBF6D675AF74C1983958C26FBCFBC063C31DD5D825A0B2EBAA8E05D0C4AC904AD2C1F24D413FBCEF9CE4B1FC0A7E8B40ABB09BDD375AAF7BE851B20CF3E9E04482DABD938FD40243638C8B27D3C55455DE49A5AEC52CE4CDA06D6C89771EE39321D247AC94DE2AD23BAD33BD47C5A83E34373A77AB4EA1CE2FA2B36A6B17409B1D7F6F75025BFB30475F1D3AFEA877667A0CE511607148A1B875C4E88CCEF7F12275CCC99BD5558321380FF355F72BBFF63A7A2840A31C14DBB719C3BD922E0900FABC2407C943EB98D8C20916B62D5EA1EBA9205D45EE8FD788B1D2C1BA7DCBB857345CA6BACEE1EB7781BB5D290410F675BE83C691272AB50F2DA3FABE7D1952F5DF93D5907E51255DFC209C7B3387A7B4B4D0206043C39F7E58ED832EE7446A6CB1BBBBE9BA29B1D22F6BD04537EA674213F3DD1CAF972BB0C56383CD86A25F5D91059B68D2357F3ED03F986372CD4AB3FC514A86723EC2E1F573A6C68F697A8EF5C1D51BDB0AF94B96B65A8D82974F1D39EAB2704AB06B985C3D179C5730686E2EE5E46D10E1E7E8F5DDDAB9267E6AE6D57EE5DB3ACA74CD0D04D19ECE452E88D77B798BAD3442379E715B1624F13B333A99539AB055CC1F0483C5012DFAFF200A37ED3B2CB27AA87A5A67BFFA8A3050FEFA0AF3E0D93996ED40CD76EC725DF8E7F10063AD64743AA8BA3E1093BA69F2F6E9CAE4991AE347964F318A8CACFC172BDAD80870B368FED1B4679BA3371D4AE46755BECC429ED1EC07E95969973FDFD959FB0FAB3781202FDEBEDD73A51DC2EEBAEF718E7363D4B34BF7154CF4E39AEABE8897D92BEEBA3C7EF3FE93C983DA80ED52A6FDE97058BB443305A8FD82FCC25E91E4C96EA420D8E2E6F36D69C4A30EB65F0D754AD4B399F589B6B1215B94D8AD4C659B757C578C50782DCDE953502012E5487AB39ADD2A52C8D1B91D00B6F17B6BBF0F180896949E40BF4FB49B4839ABD4FAF8BC8DBA9D268C803FF7598EF31A973FFFB3FAE3A9516BA5CE1A559B37B7286BE28767F69A0C3D4F5B7A3D790E612D7361D00A864ADD1BADA48F3F04394938E292AD030884B457FF7C7F6D15C7420F84DE36B77ABA4374F808CE9CD8DA0A2B0305B5FC19DB6DBDDD179FD76BB28E850644FD76290C14F9396A3E7F671E07367DBFB5E3407CFF4CC7F51F33374C7223AEDBE355A35FB885827CF16FBDAFDEF828270EC4FC9A51B63CD3FD50C15C75E4E8E7562F26FD2033E010795F49D78A5D0A9AED8025DC79F3F1BAE73D31A641FBDD568AF8D441CB158B8041B18FD4E60FD174BF3DC2E0CDC618DEA6CF33736FE27F2D8B7CD496BC907874B15628BE15F123BB2C6DF843F488DAEACB160FADE69F6A32015B5167636C6A0EB067164B8E9DC1D35762C02E05DF8E537F58E905FCB13341353966F08E2669CB21C505F9F1C1079A989223FD338DEFD6017EF3C0B39CDAA01CE7EA1FD3CB7CD5084D1B131B17041EC30DBF376D7C40AEACC7870E797DE794C7794629A61E9934FDF2B9A7A6CAB1DDFDD425C936C5C3E26B17EF6E99BA4711D46420B2BEBBCE0FCEBBDAC538A4325C96E019FD803CDA51BF7FE1F72AE2CB5AFB092BDC48F4AF715C83CFBED2E361262CB8ECC61862C0125BACEBD22D0BAC77139243ED3348A6DDA4E95BFBC93B4A4CBF2D4311F71B2977D37597072A2DB796BC1BA71D20955F1A7AD635FFD681ED0AD11F8A6D5FD9FD921C786F9B125EF4713222E8A48F76DD4EE58B6A7B33D07F68AF7C3894F7AC7E0DD756A7735F509EF6DEB0CFDB409D22BF799A19141FB9FFC4BD800223DB4E95F0BE1397FB54BE4DEB270C9C1DCADB3E77E4505ABD8ADDE6E4F6DF4F17D5504302263617E4ECB2DD9DF0C924DF75DAB5B4A978EEC7C151EDD59F7C2FEB06B0AEA82F06DCC203848FC0A8FD40E944D6DCF73E643568D5F444FEE4646C466688D55BCF666F434672D53C70383FD5DD846AE09D5B37F23D887F59453F41CF8ED5F666F3EBF55AEBF63DB3E94A4720AED5C72BCDB428B214165E0348B0537ADA3EE2E1C5FBC754C03F0148DA41C1]]>
          </SampleData>
        </SampleImport>
      </Producers>
    </Sample>
    <Mesh Name="GridMesh">
      <Producers>
        <MeshBox Scale="8 8 1" Grid2DOnly="255"/>
        <MeshTransform Rotation="-0.25 0 0"/>
      </Producers>
    </Mesh>
    <Bitmap Name="GridBitmap" Width="2" Height="2" Filter="1">
      <Producers>
        <BitmapExpression>
          <Expression>
<![CDATA[//

if(X+Y == 1)Pixel = 1; else Pixel.B = 1;]]>
          </Expression>
        </BitmapExpression>
      </Producers>
    </Bitmap>
    <Material Name="GridMaterial" Shading="1" Light="0">
      <Textures>
        <MaterialTexture Texture="GridBitmap" TextureScale="8 8 1" TextureWrapMode="1" TexCoords="1"/>
      </Textures>
    </Material>
  </Content>
</ZApplication>
I'm also setting the panning of the sound based on the relative position and camera rotation .. just in case you were going to ask that next :wink:

K

Re: Sound volume based on distance

Posted: Tue May 09, 2023 9:15 am
by jinxtengu
Thanks again.
Yes, this is almost exactly what I was looking for, although, I notice that the sound ceases entirely when the player is directly over the top of the source of the sound. Is that because it's clipping a value of 1?

Re: Sound volume based on distance

Posted: Tue May 09, 2023 10:09 am
by Kjell
Hi jinxtengu,
jinxtengu wrote: Tue May 09, 2023 9:15 amI notice that the sound ceases entirely when the player is directly over the top of the source of the sound. Is that because it's clipping a value of 1?
Whoops .. forgot to abs() the result of log2() :oops: Change the volume assignment line in Cricket.OnUpdate to ..

Code: Select all

CricketSound.Volume = clamp(1/abs(log2(s)), 0, 1);
K

Re: Sound volume based on distance

Posted: Wed Jul 05, 2023 5:52 am
by jinxtengu
Many thanks for the update!! :D