Jump to content

Doppler Effect in MTA


Topo1st

Recommended Posts

  • MTA Team

Interesting.

Well, you should begin with having a mapping, which vehicle (or model) is using a sound.

Then go ahead and make a onClientRender handler. In this handler you get the localPlayer's velocity and position (check for localPlayer's vehicle and use it if available), go through the mapping with a loop and get each vehicle's velocity and position aswell. In this loop you do the calculation as written on the sources you provided (shouldn't be too hard) and set the sound properties (create the sound if not done before). Play with the fPitch and fTempo values to get a nice effect.

PlaySound

SetSoundVolume

SetSoundProperties

OnClientRender

Link to comment
Interesting.

Well, you should begin with having a mapping, which vehicle (or model) is using a sound.

Then go ahead and make a onClientRender handler. In this handler you get the localPlayer's velocity and position (check for localPlayer's vehicle and use it if available), go through the mapping with a loop and get each vehicle's velocity and position aswell. In this loop you do the calculation as written on the sources you provided (shouldn't be too hard) and set the sound properties (create the sound if not done before). Play with the fPitch and fTempo values to get a nice effect.

PlaySound

SetSoundVolume

SetSoundProperties

OnClientRender

The point is that I can't figure out how to take advantage of those formulas. The sound element with the vehicle it's attached to, along with the listener (in fact, the camera element) is known. I need help at the point where I have to do those calculations.

Link to comment
  • MTA Team

Try this code:

  
addEventHandler("onClientRender", root, 
    function () 
        -- Get every sound 
        local sounds = getElementsByType("sound") 
  
        -- Abort if there is no sound active 
        if (#sounds == 0) then 
            return 
        end 
  
        -- Get if possible my vehicle 
        local me = getPedOccupiedVehicle(localPlayer) 
  
        -- Else use my ped 
        if (not isElement(me)) then 
            me = localPlayer 
        end 
  
        -- Get our position 
        local mpX, mpY, mpZ = getElementPosition(me) 
  
        -- Get our velocity 
        local mvX, mvY, mvZ = getElementVelocity(me) 
  
        -- Loop through the sounds 
        for index = 1, #sounds do 
            -- Get the current sound by index 
            local sound = sounds[index] 
  
            -- Get the element, which the sound is attached to 
            local attachedto = getElementAttachedTo(sound) 
  
            -- Continue if the sound is attached to a vehicle 
            if (isElement(attachedto) and getElementType(attachedto) == "vehicle") then 
                -- Get the vehicle's position 
                local tpX, tpY, tpZ = getElementPosition(attachedto) 
  
                -- Calculate the distance between him and us 
                local dX, dY, dZ = distance(mpX, mpY, mpZ, tpX, tpY, tpZ) 
  
                -- Get the actual length from the distance 
                local distance = getDistanceBetweenPoints3D(mpX, mpY, mpZ, tpX, tpY, tpZ) 
  
                -- Get the vehicle's velocity 
                local tvX, tvY, tvZ = getElementVelocity(attachedto) 
  
                -- Calculate the dotproduct from velocity and distance for the sender 
                local dpS = dotproduct(tvX, tvY, tvZ, dX, dY, dZ) 
  
                -- Calculate the dotproduct from velocity and distance for the receiver 
                local dpR = dotproduct(mvX, mvY, mvZ, dX, dY, dZ) 
  
                -- Calculate teh vs value for the sender 
                local vr = dpR / distance 
  
                -- Calculate the vr value for the receiver 
                local vs = dpS / distance 
  
                -- Calculate the frequency (modified formula to improve the effect in GTA) 
                local f = ((1 + vr) / (1 + vs)) * (4.5 * (vs - vr)) 
  
                -- Adjust the sound volume 
                setSoundVolume(sound, 1 - math.min(1, (distance + distance * 0.25) / 300)) 
  
                -- Adjust the sound speed and effect 
                local samplerate, _, _, reverse = getSoundProperties(sound) 
                setSoundProperties(sound, samplerate, f, f, reverse) 
            end 
        end 
    end 
) 
  
function distance(x, y, z, a, b, c) 
    return math.abs(a - x), math.abs(b - y), math.abs(c - z) 
end 
  
function dotproduct(x, y, z, a, b, c) 
    return math.abs(x * a) + math.abs(y * b) + math.abs(z * c) 
end 
  

Link to comment

I modified the code to fit my purpose. Though it should look fine, the result is '-1.#IND'.

Note that applyDopplerEffectToSound is called by another function called by onClientRender.

function setSoundProperty(sound, property, value) 
    if (sound) and (getElementType(sound) == "sound") then 
        local samplerate, tempo, pitch, reversed = getSoundProperties(sound) 
        if (property == "SampleRate") then 
            local success = setSoundProperties(sound, tonumber(value), tempo, pitch, reversed) 
            if (success) then 
                return true 
            end 
        elseif (property == "Tempo") then 
            local success = setSoundProperties(sound, samplerate, tonumber(value), pitch, reversed) 
            if (success) then 
                return true 
            end 
        elseif (property == "Pitch") then 
            local success = setSoundProperties(sound, samplerate, tempo, tonumber(value), reversed) 
            if (success) then 
                return true 
            end 
        elseif (property == "Reversed") then 
            if (value == true) or (value == false) then 
                success = setSoundProperties(sound, samplerate, tempo, pitch, value) 
                if (success) then 
                    return true 
                end 
            end 
        end 
    end 
end 
  
function applyDopplerEffectToSound(sound) 
    local veh = getPedOccupiedVehicle(localPlayer) 
    local soundveh = getElementAttachedTo(sound) 
    local tpX, tpY, tpZ = getElementPosition(soundveh) 
    local tvX, tvY, tvZ = getElementVelocity(soundveh) 
    if (veh) then 
        local mpX, mpY, mpZ = getElementPosition(veh) 
        local mvX, mvY, mvZ = getElementVelocity(veh) 
        local dX, dY, dZ = distance(mpX, mpY, mpZ, tpX, tpY, tpZ) 
        local distance = getDistanceBetweenPoints3D(mpX, mpY, mpZ, tpX, tpY, tpZ) 
        local dpS = dotproduct(tvX, tvY, tvZ, dX, dY, dZ) 
        local dpR = dotproduct(mvX, mvY, mvZ, dX, dY, dZ) 
        local vr = dpR / distance 
        local vs = dpS / distance 
        local f = ((1 + vr) / (1 + vs)) * (4.5 * (vs - vr)) 
        setSoundProperty(sound, "Pitch", f) 
    else 
        local mpX, mpY, mpZ = getElementPosition(localPlayer) 
        local mvX, mvY, mvZ = getElementVelocity(localPlayer) 
        local dX, dY, dZ = distance(mpX, mpY, mpZ, tpX, tpY, tpZ) 
        local distance = getDistanceBetweenPoints3D(mpX, mpY, mpZ, tpX, tpY, tpZ) 
        local dpS = dotproduct(tvX, tvY, tvZ, dX, dY, dZ) 
        local dpR = dotproduct(mvX, mvY, mvZ, dX, dY, dZ) 
        local vr = dpR / distance 
        local vs = dpS / distance 
        local f = ((1 + vr) / (1 + vs)) * (4.5 * (vs - vr)) 
        setSoundProperty(sound, "Pitch", f) 
    end 
end 
  
function distance(x, y, z, a, b, c) 
    return math.abs(a - x), math.abs(b - y), math.abs(c - z) 
end 
      
function dotproduct(x, y, z, a, b, c) 
    return math.abs(x * a) + math.abs(y * b) + math.abs(z * c) 
end 

Link to comment
  • MTA Team

I changed your code, here:

local map_setSoundProperty = {["SampleRate"] = 1, ["Tempo"] = 2, ["Pitch"] = 3, ["Reversed"] = 4} 
function setSoundProperty(sound, property, value) 
    -- Only handle sound elements 
    if (not isElement(sound) or getElementType(sound) ~= "sound") then return end 
  
    -- Get the current properties 
    local properties = { getSoundProperties(sound) } 
  
    -- Get the index by using the property parameter 
    local index = map_setSoundProperty[property] 
  
    -- Apply the value if there is an index for it 
    if (index) then properties[index] = tonumber(value) end 
  
    -- Return the property-success boolean 
    return setSoundProperties(sound, unpack(properties)) 
end 
  
function applyDopplerEffectToSound(sound) 
    -- Get the receiver (use localPlayer if we have no vehicle) 
    local receiver = { e = getPedOccupiedVehicle(localPlayer) } 
    if (not receiver.e) then receiver.e = localPlayer end 
     
    -- Get the sender (abort if it's invalid or if it equals to the receiver) 
    local sender = { e = getElementAttachedTo(sound) } 
    if (not sender.e or sender.e == receiver.e) then return end 
  
    -- Receiver information 
    receiver.p = { getElementPosition(receiver.e) } 
    receiver.v = { getElementVelocity(receiver.e) } 
  
    -- Sender information 
    sender.p = { getElementPosition(sender.e) } 
    sender.v = { getElementVelocity(sender.e) } 
  
    -- Distance calculation (abort if there is no distance) 
    local dx, dy, dz, d = getDistanceBetweenPoints(receiver.p, sender.p) 
    if (d < 1) then return end 
  
    -- Calculate velocity dotproduct for receiver and sender 
    sender.vs = getVelocityDotproduct(sender.v, dx, dy, dz, d) 
    receiver.vr = getVelocityDotproduct(receiver.v, dx, dy, dz, d) 
  
    -- Calculate pitch 
    local p = ((1 + receiver.vr) / (1 + sender.vs)) * (4.5 * math.max(0, sender.vs - receiver.vr)) 
    setSoundProperty(sound, "Pitch", p) 
end 
  
function getDistanceBetweenPoints(pointA, pointB) 
    local x, y, z = math.abs(pointB[1] - pointA[1]), math.abs(pointB[2] - pointA[2]), math.abs(pointB[3] - pointA[3]) 
    local distance = getDistanceBetweenPoints3D(pointA[1], pointA[2], pointA[3], unpack(pointB)) 
    return x, y, z, distance 
end 
  
function getVelocityDotproduct(point, dx, dy, dz, d) 
    if (d == 0) then return 1 end 
    return (math.abs(point[1] * dx) + math.abs(point[2] * dy) + math.abs(point[3] * dz)) / d 
end 
  

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...