浏览代码

[Added] Documentation
[Added] Sound Type saving in editor
[Changed] Refactored inline with discussion

Alex Parlett 11 年之前
父节点
当前提交
c2110573c6

+ 7 - 0
Bin/Data/Scripts/Editor.as

@@ -164,6 +164,7 @@ void LoadConfig()
     XMLElement resourcesElem = configElem.GetChild("resources");
     XMLElement consoleElem = configElem.GetChild("console");
     XMLElement varNamesElem = configElem.GetChild("varnames");
+    XMLElement soundTypesElem = configElem.GetChild("soundtypes");
 
     if (!cameraElem.isNull)
     {
@@ -272,6 +273,9 @@ void LoadConfig()
     
     if (!varNamesElem.isNull)
         globalVarNames = varNamesElem.GetVariantMap();
+        
+    if (!soundTypesElem.isNull)
+        LoadSoundTypes(soundTypesElem);
 }
 
 void SaveConfig()
@@ -288,6 +292,7 @@ void SaveConfig()
     XMLElement resourcesElem = configElem.CreateChild("resources");
     XMLElement consoleElem = configElem.CreateChild("console");
     XMLElement varNamesElem = configElem.CreateChild("varnames");
+    XMLElement soundTypesElem = configElem.CreateChild("soundtypes");
 
     cameraElem.SetFloat("nearclip", viewNearClip);
     cameraElem.SetFloat("farclip", viewFarClip);
@@ -358,6 +363,8 @@ void SaveConfig()
     consoleElem.SetAttribute("commandinterpreter", console.commandInterpreter);
 
     varNamesElem.SetVariantMap(globalVarNames);
+    
+    SaveSoundTypes(soundTypesElem);
 
     config.Save(File(configFileName, FILE_WRITE));
 }

+ 81 - 63
Bin/Data/Scripts/Editor/EditorSoundType.as

@@ -3,11 +3,11 @@
 Window@ soundTypeEditorWindow;
 Dictionary mappings;
 
-const uint DEFAULT_SOUND_TYPES_COUNT = 5;
+const uint DEFAULT_SOUND_TYPES_COUNT = 1;
 
 class SoundTypeMapping
 {
-    StringHash key;
+    String key;
     float value;
 
     SoundTypeMapping()
@@ -15,21 +15,15 @@ class SoundTypeMapping
     }
     
     SoundTypeMapping(const String&in key, const float&in value)
-    {
-        this.key = StringHash(key);
-        this.value = value;
-    }
-    
-    SoundTypeMapping(const StringHash&in key, const float&in value)
     {
         this.key = key;
-        this.value = value;
+        this.value = Clamp(value, 0.0f, 1.0f);
     }
     
     void Update(float value)
     {
         this.value = Clamp(value, 0.0f, 1.0f);
-        audio.SetMasterGain(this.key, this.value);
+        audio.masterGain[this.key] = this.value;
     }
 }
 
@@ -42,7 +36,7 @@ void CreateSoundTypeEditor()
     ui.root.AddChild(soundTypeEditorWindow);
     soundTypeEditorWindow.opacity = uiMaxOpacity;
 
-    InitsoundTypeEditorWindow();
+    InitSoundTypeEditorWindow();
     RefreshSoundTypeEditorWindow();
 
     int height = Min(ui.root.height - 60, 750);    
@@ -54,20 +48,22 @@ void CreateSoundTypeEditor()
     SubscribeToEvent(soundTypeEditorWindow.GetChild("CloseButton", true), "Released", "HideSoundTypeEditor");
     SubscribeToEvent(soundTypeEditorWindow.GetChild("AddButton", true), "Released", "AddSoundTypeMapping");
     
-    SubscribeToEvent(soundTypeEditorWindow.GetChild("EffectValue", true), "TextFinished", "EditGain");
-    SubscribeToEvent(soundTypeEditorWindow.GetChild("AmbientValue", true), "TextFinished", "EditGain");
-    SubscribeToEvent(soundTypeEditorWindow.GetChild("VoiceValue", true), "TextFinished", "EditGain");
-    SubscribeToEvent(soundTypeEditorWindow.GetChild("MusicValue", true), "TextFinished", "EditGain");
     SubscribeToEvent(soundTypeEditorWindow.GetChild("MasterValue", true), "TextFinished", "EditGain");
 }
 
-void InitsoundTypeEditorWindow()
+void InitSoundTypeEditorWindow()
 {
-    mappings["Effect"] = SoundTypeMapping(GetHashFromSoundType(SOUND_EFFECT), audio.GetMasterGain(SOUND_EFFECT));
-    mappings["Ambient"] = SoundTypeMapping(GetHashFromSoundType(SOUND_AMBIENT), audio.GetMasterGain(SOUND_AMBIENT));
-    mappings["Voice"] = SoundTypeMapping(GetHashFromSoundType(SOUND_VOICE), audio.GetMasterGain(SOUND_VOICE));
-    mappings["Music"] = SoundTypeMapping(GetHashFromSoundType(SOUND_MUSIC), audio.GetMasterGain(SOUND_MUSIC));
-    mappings["Master"] = SoundTypeMapping(GetHashFromSoundType(SOUND_MASTER), audio.GetMasterGain(SOUND_MASTER));
+    if (!mappings.Exists(SOUND_MASTER))
+        mappings[SOUND_MASTER] = SoundTypeMapping(SOUND_MASTER, audio.masterGain[SOUND_MASTER]);
+        
+    for (uint i = DEFAULT_SOUND_TYPES_COUNT; i < mappings.length; i++)
+    {
+        String key = mappings.keys[i]; 
+
+        SoundTypeMapping@ mapping;
+        if (mappings.Get(key, @mapping))
+            AddUserUIElements(key, mapping.value);
+    }    
 }
 
 void RefreshSoundTypeEditorWindow()
@@ -78,11 +74,7 @@ void RefreshSoundTypeEditorWindow()
 
 void RefreshDefaults(UIElement@ root)
 {
-    UpdateMappingValue("Effect", root.GetChild("Effect", true));
-    UpdateMappingValue("Ambient", root.GetChild("Ambient", true));
-    UpdateMappingValue("Voice", root.GetChild("Voice", true));
-    UpdateMappingValue("Music", root.GetChild("Music", true));
-    UpdateMappingValue("Master", root.GetChild("Master", true));
+    UpdateMappingValue(SOUND_MASTER, root.GetChild(SOUND_MASTER, true));
 }
 
 void RefreshUser(UIElement@ root)
@@ -104,46 +96,46 @@ void UpdateMappingValue(const String&in key, UIElement@ root)
         if (mappings.Get(key, @mapping) && value !is null)
         {
             value.text = mapping.value;
-            root.vars["DragDropContent"] = String(mapping.key.value);
+            root.vars["DragDropContent"] = mapping.key;
         }
     }
 }
 
 void AddUserUIElements(const String&in key, const String&in gain)
 {
-        ListView@ container = soundTypeEditorWindow.GetChild("UserContainer", true);
-        
-        UIElement@ itemParent = UIElement();
-        container.AddItem(itemParent);
-        
-        itemParent.style = "ListRow";
-        itemParent.name = key;
-        itemParent.layoutSpacing = 10;
-        
-        Text@ keyText = Text();
-        LineEdit@ gainEdit = LineEdit();
-        Button@ removeButton = Button();
-        
-        itemParent.AddChild(keyText);
-        itemParent.AddChild(gainEdit);
-        itemParent.AddChild(removeButton);
-        itemParent.dragDropMode = DD_SOURCE;
-        
-        keyText.text = key;
-        keyText.textAlignment = HA_LEFT;
-        keyText.SetStyleAuto();
-        
-        gainEdit.maxLength = 4;
-        gainEdit.maxWidth = 2147483647;
-        gainEdit.minWidth = 100;
-        gainEdit.name = key + "Value";
-        gainEdit.text = gain;
-        gainEdit.SetStyleAuto();
-
-        removeButton.style = "CloseButton";
-        
-        SubscribeToEvent(removeButton, "Released", "DeleteSoundTypeMapping");
-        SubscribeToEvent(gainEdit, "TextFinished", "EditGain");
+    ListView@ container = soundTypeEditorWindow.GetChild("UserContainer", true);
+
+    UIElement@ itemParent = UIElement();
+    container.AddItem(itemParent);
+
+    itemParent.style = "ListRow";
+    itemParent.name = key;
+    itemParent.layoutSpacing = 10;
+
+    Text@ keyText = Text();
+    LineEdit@ gainEdit = LineEdit();
+    Button@ removeButton = Button();
+
+    itemParent.AddChild(keyText);
+    itemParent.AddChild(gainEdit);
+    itemParent.AddChild(removeButton);
+    itemParent.dragDropMode = DD_SOURCE;
+
+    keyText.text = key;
+    keyText.textAlignment = HA_LEFT;
+    keyText.SetStyleAuto();
+
+    gainEdit.maxLength = 4;
+    gainEdit.maxWidth = 2147483647;
+    gainEdit.minWidth = 100;
+    gainEdit.name = key + "Value";
+    gainEdit.text = gain;
+    gainEdit.SetStyleAuto();
+
+    removeButton.style = "CloseButton";
+
+    SubscribeToEvent(removeButton, "Released", "DeleteSoundTypeMapping");
+    SubscribeToEvent(gainEdit, "TextFinished", "EditGain");
 }
 
 void AddSoundTypeMapping(StringHash eventType, VariantMap& eventData)
@@ -154,8 +146,10 @@ void AddSoundTypeMapping(StringHash eventType, VariantMap& eventData)
     
     if (!key.text.empty && !gain.text.empty && !mappings.Exists(key.text))
     {
-        AddUserUIElements(key.text,gain.text);
-        mappings[key.text] = SoundTypeMapping(key.text, gain.text.ToFloat());
+        SoundTypeMapping@ mapping = SoundTypeMapping(key.text, gain.text.ToFloat());
+    
+        mappings[key.text] = mapping;
+        AddUserUIElements(key.text, mapping.value);
     }
     
     key.text = "";
@@ -170,7 +164,7 @@ void DeleteSoundTypeMapping(StringHash eventType, VariantMap& eventData)
     UIElement@ parent = button.parent;
     
     mappings.Erase(parent.name);
-    button.parent.Remove();
+    parent.Remove();
 }
 
 void EditGain(StringHash eventType, VariantMap& eventData)
@@ -197,4 +191,28 @@ bool ShowSoundTypeEditor()
 void HideSoundTypeEditor()
 {
     soundTypeEditorWindow.visible = false;
+}
+
+void SaveSoundTypes(XMLElement&in root)
+{
+    for (uint i = 0; i < mappings.length; i++)
+    {
+        String key = mappings.keys[i]; 
+
+        SoundTypeMapping@ mapping;
+        if (mappings.Get(key, @mapping))
+            root.SetFloat(key, mapping.value);
+    }
+}
+
+void LoadSoundTypes(const XMLElement&in root)
+{
+    for (uint i = 0; i < root.numAttributes ; i++)
+    {
+        String key = root.GetAttributeNames()[i];
+        float gain = root.GetFloat(key);
+    
+        if (!key.empty && !mappings.Exists(key))
+            mappings[key] = SoundTypeMapping(key, gain);
+    }
 }

+ 0 - 52
Bin/Data/UI/EditorSoundTypeWindow.xml

@@ -27,58 +27,6 @@
 		<attribute name="Min Size" value="0 60" />
 		<element type="BorderImage" internal="true" style="none">
 			<element internal="true" style="none">
-				<element style="ListRow">
-					<attribute name="Name" value="Effect" />
-					<attribute name="Layout Spacing" value="10" />
-					<element type="Text">
-						<attribute name="Text" value="Effect" />
-					</element>
-					<element type="LineEdit">
-						<attribute name="Name" value="EffectValue" />
-						<attribute name="Min Size" value="100 0" />
-						<attribute name="Max Size" value="2147483647 20" />
-						<attribute name="Max Length" value="4" />
-					</element>
-				</element>
-				<element style="ListRow">
-					<attribute name="Name" value="Ambient" />
-					<attribute name="Layout Spacing" value="10" />
-					<element type="Text">
-						<attribute name="Text" value="Ambient" />
-					</element>
-					<element type="LineEdit">
-						<attribute name="Name" value="AmbientValue" />
-						<attribute name="Min Size" value="100 0" />
-						<attribute name="Max Size" value="2147483647 20" />
-						<attribute name="Max Length" value="4" />
-					</element>
-				</element>
-				<element style="ListRow">
-					<attribute name="Name" value="Voice" />
-					<attribute name="Layout Spacing" value="10" />
-					<element type="Text">
-						<attribute name="Text" value="Voice" />
-					</element>
-					<element type="LineEdit">
-						<attribute name="Name" value="VoiceValue" />
-						<attribute name="Min Size" value="100 0" />
-						<attribute name="Max Size" value="2147483647 20" />
-						<attribute name="Max Length" value="4" />
-					</element>
-				</element>
-				<element style="ListRow">
-					<attribute name="Name" value="Music" />
-					<attribute name="Layout Spacing" value="10" />
-					<element type="Text">
-						<attribute name="Text" value="Music" />
-					</element>
-					<element type="LineEdit">
-						<attribute name="Name" value="MusicValue" />
-						<attribute name="Min Size" value="100 0" />
-						<attribute name="Max Size" value="2147483647 20" />
-						<attribute name="Max Length" value="4" />
-					</element>
-				</element>
 				<element style="ListRow">
 					<attribute name="Name" value="Master" />
 					<attribute name="Layout Spacing" value="10" />

+ 75 - 7
Docs/Reference.dox

@@ -1509,14 +1509,9 @@ To hear pseudo-3D positional sounds, a SoundListener component must exist in a s
 
 The output is software mixed for an unlimited amount of simultaneous sounds. Ogg Vorbis sounds are decoded on the fly, and decoding them can be memory- and CPU-intensive, so WAV files are recommended when a large number of short sound effects need to be played.
 
-For purposes of volume control, each SoundSource is classified into one of four categories:
+For purposes of volume control, each SoundSource can be classified into a user defined group which is multiplied with a master category and the individual SoundSource gain set using \ref SoundSource::SetGain "SetGain()" for the final volume level.
 
-- %Sound effects
-- Ambient
-- Music
-- Voice
-
-A master gain category also exists that affects the final output level. To control the category volumes, use \ref Audio::SetMasterGain "SetMasterGain()".
+To control the category volumes, use \ref Audio::SetMasterGain "SetMasterGain()", which defines the category if it didn't already exist.
 
 The SoundSource components support automatic removal from the node they belong to, once playback is finished. To use, call \ref SoundSource::SetAutoRemove "SetAutoRemove()" on them. This may be useful when a game object plays several "fire and forget" sound effects.
 
@@ -1753,6 +1748,79 @@ Sprites are a special kind of %UI element that allow subpixel (float) positionin
 
 Due to the free transformability, sprites can not be reliably queried with \ref UI::GetElementAt "GetElementAt()". Also, only other sprites should be parented to sprites, as the other elements do not support scaling and rotation.
 
+\section UI_Cursor_Shapes Cursor Shapes
+
+Urho3D supports custom Cursor Shapes defined from an \ref Image.
+
+The Shape can be an OS default from the \ref CursorShape "CursorShape" enum, which are automatically switched to and from by the \ref UI "UI" subsystem, but can be manually switched to using \ref Cursor::SetShape "Cursor::SetShape(CursorShape)".
+
+Alternatively they can be defined using a name in String format to identify it, which can only be manually switched to and from using \ref Cursor::SetShape "Cursor::SetShape(const String&)".
+
+There are a number of reserved names that are used for the OS defaults:
+    - Normal
+    - IBeam
+    - Cross
+    - ResizeVertical
+    - ResizeDiagonalTopRight
+    - ResizeHorizontal
+    - ResizeDiagonalTopLeft
+    - ResizeAll
+    - AcceptDrop
+    - RejectDrop
+    - Busy
+    - BusyArrow
+	
+Cursor Shapes can be define in a number of different ways:
+
+XML:
+\code
+	<element type="Cursor">
+		<attribute name="Shapes">
+			<variant type="VariantVector" >
+				<variant type="String" value="Normal" />
+				<variant type="ResourceRef" value="Image;Textures/UI.png" />
+				<variant type="IntRect" value="0 0 12 24" />
+				<variant type="IntVector2" value="0 0" />
+			</variant>  
+			<variant type="VariantVector" >
+				<variant type="String" value="Custom" />
+				<variant type="ResourceRef" value="Image;Textures/UI.png" />
+				<variant type="IntRect" value="12 0 12 36" />
+				<variant type="IntVector2" value="0 0" />
+			</variant>  
+		</atrribute>
+	</element>
+\endcode
+
+C++:
+\code
+    UI* ui = GetSubsystem<UI>();
+    ResourceCache* rc = GetSubsystem<ResourceCache>();
+
+    Cursor* cursor = new Cursor(context_);
+    Image* image = rc->GetResource<Image>("Textures/UI.png");
+    if (image)
+	{
+        cursor->DefineShape(CS_NORMAL, image, IntRect(0, 0, 12, 24), IntVector2(0, 0));
+		cursor->DefineShape("Custom", image, IntRect(12, 0, 12, 36), IntVector2(0, 0));
+	}
+
+    ui->SetCursor(cursor);
+\endcode
+
+Angelcode:
+\code
+	Cursor@ cursor = new Cursor();
+	Image@ image = cache.GetResource("Image", "Textures/UI.png");
+	if (image !is null)
+	{
+		cursor.DefineShape(CS_NORMAL, image, IntRect(0, 0, 12, 24), IntVector2(0, 0));
+		cursor.DefineShape("Custom", image, IntRect(12, 0, 12, 36), IntVector2(0, 0));
+	}
+		
+	ui.SetCursor(cursor);
+\endcode
+
 \page Urho2D Urho2D
 In order to make 2D games in Urho3D, the Urho2D sublibrary is provided. Urho2D includes 2D graphics and 2D physics.
 

+ 11 - 33
Source/Engine/Audio/Audio.cpp

@@ -53,8 +53,8 @@ Audio::Audio(Context* context) :
     sampleSize_(0),
     playing_(false)
 {
-    for (unsigned i = 0; i < MAX_SOUND_TYPES; ++i)
-        masterGain_[soundTypeHashes[i]] = 1.0f;
+    // Set the master to the default value
+    masterGain_[SOUND_MASTER] = 1.0f;
     
     // Register Audio library object factories
     RegisterAudioLibrary(context_);
@@ -149,15 +149,7 @@ void Audio::Stop()
     playing_ = false;
 }
 
-void Audio::SetMasterGain(SoundType type, float gain)
-{
-    if (type >= MAX_SOUND_TYPES)
-        return;
-
-    SetMasterGain(soundTypeHashes[type], gain);
-}
-
-void Audio::SetMasterGain(const StringHash& type, float gain)
+void Audio::SetMasterGain(const String& type, float gain)
 {
     masterGain_[type] = Clamp(gain, 0.0f, 1.0f);
 }
@@ -176,19 +168,9 @@ void Audio::StopSound(Sound* soundClip)
     }
 }
 
-
-float Audio::GetMasterGain(SoundType type) const
-{
-    if (type >= MAX_SOUND_TYPES)
-        return 0.0f;
-
-    return GetMasterGain(soundTypeHashes[type]);
-}
-
-
-float Audio::GetMasterGain(const StringHash& type) const
+float Audio::GetMasterGain(const String& type) const
 {
-    VariantMap::ConstIterator findIt = masterGain_.Find(type);
+    HashMap<String, Variant>::ConstIterator findIt = masterGain_.Find(type);
     if (findIt == masterGain_.End())
         return 0.0f;
 
@@ -216,18 +198,14 @@ void Audio::RemoveSoundSource(SoundSource* channel)
     }
 }
 
-float Audio::GetSoundSourceMasterGain(SoundType type) const
+float Audio::GetSoundSourceMasterGain(const String& type) const
 {
-    if (type >= MAX_SOUND_TYPES)
-        return 0.0f;
-    
-    return GetSoundSourceMasterGain(soundTypeHashes[type]);
-}
+    HashMap<String, Variant>::ConstIterator masterIt = masterGain_.Find(SOUND_MASTER);
 
-float Audio::GetSoundSourceMasterGain(const StringHash& type) const
-{
-    VariantMap::ConstIterator masterIt = masterGain_.Find(soundTypeHashes[SOUND_MASTER]);
-    VariantMap::ConstIterator typeIt = masterGain_.Find(type);
+    if (type == String::EMPTY)
+        return masterIt->second_.GetFloat();
+
+    HashMap<String, Variant>::ConstIterator typeIt = masterGain_.Find(type);
 
     if (typeIt == masterGain_.End() || typeIt == masterIt)
         return masterIt->second_.GetFloat();

+ 5 - 11
Source/Engine/Audio/Audio.h

@@ -54,10 +54,8 @@ public:
     bool Play();
     /// Suspend sound output.
     void Stop();
-    /// Set master gain on a specific sound type such as sound effecs, music or voice using the enum for backwards compatibility.
-    void SetMasterGain(SoundType type, float gain);
     /// Set master gain on a specific sound type such as sound effects, music or voice.
-    void SetMasterGain(const StringHash& type, float gain);
+    void SetMasterGain(const String& type, float gain);
     /// Set active sound listener for 3D sounds.
     void SetListener(SoundListener* listener);
     /// Stop any sound source playing a certain sound clip.
@@ -75,17 +73,15 @@ public:
     bool IsPlaying() const { return playing_; }
     /// Return whether an audio stream has been reserved.
     bool IsInitialized() const { return deviceID_ != 0; }
-    /// Return master gain for a specific sound source type by enum.
-    float GetMasterGain(SoundType type) const;
     /// Return master gain for a specific sound source type.
-    float GetMasterGain(const StringHash& type) const;
+    float GetMasterGain(const String& type) const;
     /// Return active sound listener.
     SoundListener* GetListener() const;
     /// Return all sound sources.
     const PODVector<SoundSource*>& GetSoundSources() const { return soundSources_; }
 
     /// Return whether the specified master gain has been defined.
-    bool IsMasterGain(const StringHash& type) const { return masterGain_.Contains(type); }
+    bool IsMasterGain(const String& type) const { return masterGain_.Contains(type); }
 
     /// Add a sound source to keep track of. Called by SoundSource.
     void AddSoundSource(SoundSource* soundSource);
@@ -94,9 +90,7 @@ public:
     /// Return audio thread mutex.
     Mutex& GetMutex() { return audioMutex_; }
     /// Return sound type specific gain multiplied by master gain.
-    float GetSoundSourceMasterGain(SoundType type) const;
-    /// Return sound type specific gain multiplied by master gain.
-    float GetSoundSourceMasterGain(const StringHash& type) const;
+    float GetSoundSourceMasterGain(const String& type) const;
 
     /// Mix sound sources into the buffer.
     void MixOutput(void *dest, unsigned samples);
@@ -125,7 +119,7 @@ private:
     /// Playing flag.
     bool playing_;
     /// Master gain by sound source type.
-    VariantMap masterGain_;
+    HashMap<String, Variant> masterGain_;
     /// Sound sources.
     PODVector<SoundSource*> soundSources_;
     /// Sound listener.

+ 2 - 22
Source/Engine/Audio/AudioDefs.h

@@ -22,30 +22,10 @@
 
 #pragma once
 
-#include "StringHash.h"
-
 namespace Urho3D
 {
 
-/// SoundSource type enumeration for master gain control.
-enum SoundType
-{
-    SOUND_EFFECT = 0,
-    SOUND_AMBIENT,
-    SOUND_VOICE,
-    SOUND_MUSIC,
-    SOUND_MASTER,
-    MAX_SOUND_TYPES
-};
-
-static const StringHash soundTypeHashes[MAX_SOUND_TYPES] =
-{
-    "Effect",
-    "Ambient",
-    "Voice",
-    "Music",
-    "Master"
-};
-
+/// SoundSource type defaults
+static const String SOUND_MASTER = "Master";
 
 }

+ 1229 - 1272
Source/Engine/Audio/SoundSource.cpp

@@ -1,1273 +1,1230 @@
-//
-// Copyright (c) 2008-2014 the Urho3D project.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#include "Precompiled.h"
-#include "Audio.h"
-#include "Context.h"
-#include "ResourceCache.h"
-#include "Sound.h"
-#include "SoundSource.h"
-#include "SoundStream.h"
-
-#include <cstring>
-
-#include "DebugNew.h"
-
-namespace Urho3D
-{
-
-#define INC_POS_LOOPED() \
-    pos += intAdd; \
-    fractPos += fractAdd; \
-    if (fractPos > 65535) \
-    { \
-        fractPos &= 65535; \
-        ++pos; \
-    } \
-    while (pos >= end) \
-        pos -= (end - repeat); \
-
-#define INC_POS_ONESHOT() \
-    pos += intAdd; \
-    fractPos += fractAdd; \
-    if (fractPos > 65535) \
-    { \
-        fractPos &= 65535; \
-        ++pos; \
-    } \
-    if (pos >= end) \
-    { \
-        pos = 0; \
-        break; \
-    } \
-
-#define INC_POS_STEREO_LOOPED() \
-    pos += (intAdd << 1); \
-    fractPos += fractAdd; \
-    if (fractPos > 65535) \
-    { \
-        fractPos &= 65535; \
-        pos += 2; \
-    } \
-    while (pos >= end) \
-        pos -= (end - repeat); \
-
-#define INC_POS_STEREO_ONESHOT() \
-    pos += (intAdd << 1); \
-    fractPos += fractAdd; \
-    if (fractPos > 65535) \
-    { \
-        fractPos &= 65535; \
-        pos += 2; \
-    } \
-    if (pos >= end) \
-    { \
-        pos = 0; \
-        break; \
-    } \
-
-#define GET_IP_SAMPLE() (((((int)pos[1] - (int)pos[0]) * fractPos) / 65536) + (int)pos[0])
-
-#define GET_IP_SAMPLE_LEFT() (((((int)pos[2] - (int)pos[0]) * fractPos) / 65536) + (int)pos[0])
-
-#define GET_IP_SAMPLE_RIGHT() (((((int)pos[3] - (int)pos[1]) * fractPos) / 65536) + (int)pos[1])
-
-static const float AUTOREMOVE_DELAY = 0.25f;
-
-static const int STREAM_SAFETY_SAMPLES = 4;
-
-extern const char* AUDIO_CATEGORY;
-
-static const char* typeNames[MAX_SOUND_TYPES] =
-{
-    "Effect",
-    "Ambient",
-    "Voice",
-    "Music",
-    0
-};
-
-template<> SoundType Variant::Get<SoundType>() const
-{
-    return static_cast<SoundType>(GetInt());
-}
-
-SoundSource::SoundSource(Context* context) :
-    Component(context),
-    soundType_(soundTypeHashes[SOUND_EFFECT]),
-    frequency_(0.0f),
-    gain_(1.0f),
-    attenuation_(1.0f),
-    panning_(0.0f),
-    autoRemoveTimer_(0.0f),
-    autoRemove_(false),
-    position_(0),
-    fractPosition_(0),
-    timePosition_(0.0f),
-    unusedStreamSize_(0)
-{
-    audio_ = GetSubsystem<Audio>();
-
-    if (audio_)
-        audio_->AddSoundSource(this);
-}
-
-SoundSource::~SoundSource()
-{
-    if (audio_)
-        audio_->RemoveSoundSource(this);
-}
-
-void SoundSource::RegisterObject(Context* context)
-{
-    context->RegisterFactory<SoundSource>(AUDIO_CATEGORY);
-
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Sound", GetSoundAttr, SetSoundAttr, ResourceRef, ResourceRef(Sound::GetTypeStatic()), AM_DEFAULT);
-    ENUM_ACCESSOR_ATTRIBUTE("Sound Type", GetSoundTypeAttr, SetSoundTypeAttr, SoundType, typeNames, SOUND_MASTER, AM_EDIT | AM_READ);
-    ATTRIBUTE("Type", StringHash, soundType_, soundTypeHashes[SOUND_EFFECT], AM_DEFAULT);
-    ATTRIBUTE("Frequency", float, frequency_, 0.0f, AM_DEFAULT);
-    ATTRIBUTE("Gain", float, gain_, 1.0f, AM_DEFAULT);
-    ATTRIBUTE("Attenuation", float, attenuation_, 1.0f, AM_DEFAULT);
-    ATTRIBUTE("Panning", float, panning_, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Is Playing", IsPlaying, SetPlayingAttr, bool, false, AM_DEFAULT);
-    ATTRIBUTE("Autoremove on Stop", bool, autoRemove_, false, AM_FILE);
+//
+// Copyright (c) 2008-2014 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Precompiled.h"
+#include "Audio.h"
+#include "Context.h"
+#include "ResourceCache.h"
+#include "Sound.h"
+#include "SoundSource.h"
+#include "SoundStream.h"
+
+#include <cstring>
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+#define INC_POS_LOOPED() \
+    pos += intAdd; \
+    fractPos += fractAdd; \
+    if (fractPos > 65535) \
+    { \
+        fractPos &= 65535; \
+        ++pos; \
+    } \
+    while (pos >= end) \
+        pos -= (end - repeat); \
+
+#define INC_POS_ONESHOT() \
+    pos += intAdd; \
+    fractPos += fractAdd; \
+    if (fractPos > 65535) \
+    { \
+        fractPos &= 65535; \
+        ++pos; \
+    } \
+    if (pos >= end) \
+    { \
+        pos = 0; \
+        break; \
+    } \
+
+#define INC_POS_STEREO_LOOPED() \
+    pos += (intAdd << 1); \
+    fractPos += fractAdd; \
+    if (fractPos > 65535) \
+    { \
+        fractPos &= 65535; \
+        pos += 2; \
+    } \
+    while (pos >= end) \
+        pos -= (end - repeat); \
+
+#define INC_POS_STEREO_ONESHOT() \
+    pos += (intAdd << 1); \
+    fractPos += fractAdd; \
+    if (fractPos > 65535) \
+    { \
+        fractPos &= 65535; \
+        pos += 2; \
+    } \
+    if (pos >= end) \
+    { \
+        pos = 0; \
+        break; \
+    } \
+
+#define GET_IP_SAMPLE() (((((int)pos[1] - (int)pos[0]) * fractPos) / 65536) + (int)pos[0])
+
+#define GET_IP_SAMPLE_LEFT() (((((int)pos[2] - (int)pos[0]) * fractPos) / 65536) + (int)pos[0])
+
+#define GET_IP_SAMPLE_RIGHT() (((((int)pos[3] - (int)pos[1]) * fractPos) / 65536) + (int)pos[1])
+
+static const float AUTOREMOVE_DELAY = 0.25f;
+
+static const int STREAM_SAFETY_SAMPLES = 4;
+
+extern const char* AUDIO_CATEGORY;
+
+
+SoundSource::SoundSource(Context* context) :
+    Component(context),
+    soundType_(String::EMPTY),
+    frequency_(0.0f),
+    gain_(1.0f),
+    attenuation_(1.0f),
+    panning_(0.0f),
+    autoRemoveTimer_(0.0f),
+    autoRemove_(false),
+    position_(0),
+    fractPosition_(0),
+    timePosition_(0.0f),
+    unusedStreamSize_(0)
+{
+    audio_ = GetSubsystem<Audio>();
+
+    if (audio_)
+        audio_->AddSoundSource(this);
+}
+
+SoundSource::~SoundSource()
+{
+    if (audio_)
+        audio_->RemoveSoundSource(this);
+}
+
+void SoundSource::RegisterObject(Context* context)
+{
+    context->RegisterFactory<SoundSource>(AUDIO_CATEGORY);
+
+    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    MIXED_ACCESSOR_ATTRIBUTE("Sound", GetSoundAttr, SetSoundAttr, ResourceRef, ResourceRef(Sound::GetTypeStatic()), AM_DEFAULT);
+    MIXED_ACCESSOR_ATTRIBUTE("Type", GetSoundType, SetSoundType, String, "Effect", AM_DEFAULT);
+    ATTRIBUTE("Frequency", float, frequency_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE("Gain", float, gain_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE("Attenuation", float, attenuation_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE("Panning", float, panning_, 0.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Is Playing", IsPlaying, SetPlayingAttr, bool, false, AM_DEFAULT);
+    ATTRIBUTE("Autoremove on Stop", bool, autoRemove_, false, AM_FILE);
     ACCESSOR_ATTRIBUTE("Play Position", GetPositionAttr, SetPositionAttr, int, 0, AM_FILE);
-}
-
-void SoundSource::Play(Sound* sound)
-{
-    if (!audio_)
-        return;
-
-    // If no frequency set yet, set from the sound's default
-    if (frequency_ == 0.0f && sound)
-        SetFrequency(sound->GetFrequency());
-
-    // If sound source is currently playing, have to lock the audio mutex
-    if (position_)
-    {
-        MutexLock lock(audio_->GetMutex());
-        PlayLockless(sound);
-    }
-    else
-        PlayLockless(sound);
-
-    MarkNetworkUpdate();
-}
-
-void SoundSource::Play(Sound* sound, float frequency)
-{
-    SetFrequency(frequency);
-    Play(sound);
-}
-
-void SoundSource::Play(Sound* sound, float frequency, float gain)
-{
-    SetFrequency(frequency);
-    SetGain(gain);
-    Play(sound);
-}
-
-void SoundSource::Play(Sound* sound, float frequency, float gain, float panning)
-{
-    SetFrequency(frequency);
-    SetGain(gain);
-    SetPanning(panning);
-    Play(sound);
-}
-
-void SoundSource::Play(SoundStream* stream)
-{
-    if (!audio_)
-        return;
-
-    // If no frequency set yet, set from the stream's default
-    if (frequency_ == 0.0f && stream)
-        SetFrequency(stream->GetFrequency());
-
-    SharedPtr<SoundStream> streamPtr(stream);
-
-    // If sound source is currently playing, have to lock the audio mutex. When stream playback is explicitly
-    // requested, clear the existing sound if any
-    if (position_)
-    {
-        MutexLock lock(audio_->GetMutex());
-        sound_.Reset();
-        PlayLockless(streamPtr);
-    }
-    else
-    {
-        sound_.Reset();
-        PlayLockless(streamPtr);
-    }
-
-    // Stream playback is not supported for network replication, no need to mark network dirty
-}
-
-void SoundSource::Stop()
-{
-    if (!audio_)
-        return;
-
-    // If sound source is currently playing, have to lock the audio mutex
-    if (position_)
-    {
-        MutexLock lock(audio_->GetMutex());
-        StopLockless();
-    }
-    else
-        StopLockless();
-
-    MarkNetworkUpdate();
-}
-
-void SoundSource::SetSoundType(SoundType type)
-{
-    if (type == SOUND_MASTER || type >= MAX_SOUND_TYPES)
-        return;
-
-    soundType_ = soundTypeHashes[type];
-    MarkNetworkUpdate();
-}
-
-void SoundSource::SetSoundType(const StringHash& type)
-{
-    if (type == soundTypeHashes[SOUND_MASTER])
-        return;
-
-    if (!audio_->IsMasterGain(type))
-        audio_->SetMasterGain(type, 1.0f);
-
-    soundType_ = type;
-    MarkNetworkUpdate();
-}
-
-void SoundSource::SetFrequency(float frequency)
-{
-    frequency_ = Clamp(frequency, 0.0f, 535232.0f);
-    MarkNetworkUpdate();
-}
-
-void SoundSource::SetGain(float gain)
-{
-    gain_ = Max(gain, 0.0f);
-    MarkNetworkUpdate();
-}
-
-void SoundSource::SetAttenuation(float attenuation)
-{
-    attenuation_ = Clamp(attenuation, 0.0f, 1.0f);
-    MarkNetworkUpdate();
-}
-
-void SoundSource::SetPanning(float panning)
-{
-    panning_ = Clamp(panning, -1.0f, 1.0f);
-    MarkNetworkUpdate();
-}
-
-void SoundSource::SetAutoRemove(bool enable)
-{
-    autoRemove_ = enable;
-}
-
-bool SoundSource::IsPlaying() const
-{
-    return (sound_ || soundStream_) && position_ != 0;
-}
-
-void SoundSource::SetPlayPosition(signed char* pos)
-{
-    // Setting play position on a stream is not supported
-    if (!audio_ || !sound_ || soundStream_)
-        return;
-
-    MutexLock lock(audio_->GetMutex());
-    SetPlayPositionLockless(pos);
-}
-
-void SoundSource::Update(float timeStep)
-{
-    if (!audio_ || !IsEnabledEffective())
-        return;
-
-    // If there is no actual audio output, perform fake mixing into a nonexistent buffer to check stopping/looping
-    if (!audio_->IsInitialized())
-        MixNull(timeStep);
-
-    // Free the stream if playback has stopped
-    if (soundStream_ && !position_)
-        StopLockless();
-
-    // Check for autoremove
-    if (autoRemove_)
-    {
-        if (!IsPlaying())
-        {
-            autoRemoveTimer_ += timeStep;
-            if (autoRemoveTimer_ > AUTOREMOVE_DELAY)
-            {
-                Remove();
-                // Note: this object is now deleted, so only returning immediately is safe
-                return;
-            }
-        }
-        else
-            autoRemoveTimer_ = 0.0f;
-    }
-}
-
-void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, bool interpolation)
-{
-    if (!position_ || (!sound_ && !soundStream_) || !IsEnabledEffective())
-        return;
-
-    int streamFilledSize, outBytes;
-
-    if (soundStream_ && streamBuffer_)
-    {
-        int streamBufferSize = streamBuffer_->GetDataSize();
-        // Calculate how many bytes of stream sound data is needed
-        int neededSize = (int)((float)samples * frequency_ / (float)mixRate);
-        // Add a little safety buffer. Subtract previous unused data
-        neededSize += STREAM_SAFETY_SAMPLES;
-        neededSize *= soundStream_->GetSampleSize();
-        neededSize -= unusedStreamSize_;
-        neededSize = Clamp(neededSize, 0, streamBufferSize - unusedStreamSize_);
-
-        // Always start play position at the beginning of the stream buffer
-        position_ = streamBuffer_->GetStart();
-
-        // Request new data from the stream
-        signed char* dest = streamBuffer_->GetStart() + unusedStreamSize_;
-        outBytes = neededSize ? soundStream_->GetData(dest, neededSize) : 0;
-        dest += outBytes;
-        // Zero-fill rest if stream did not produce enough data
-        if (outBytes < neededSize)
-            memset(dest, 0, neededSize - outBytes);
-
-        // Calculate amount of total bytes of data in stream buffer now, to know how much went unused after mixing
-        streamFilledSize = neededSize + unusedStreamSize_;
-    }
-
-    // If streaming, play the stream buffer. Otherwise play the original sound
-    Sound* sound = soundStream_ ? streamBuffer_ : sound_;
-    if (!sound)
-        return;
-
-    // Choose the correct mixing routine
-    if (!sound->IsStereo())
-    {
-        if (interpolation)
-        {
-            if (stereo)
-                MixMonoToStereoIP(sound, dest, samples, mixRate);
-            else
-                MixMonoToMonoIP(sound, dest, samples, mixRate);
-        }
-        else
-        {
-            if (stereo)
-                MixMonoToStereo(sound, dest, samples, mixRate);
-            else
-                MixMonoToMono(sound, dest, samples, mixRate);
-        }
-    }
-    else
-    {
-        if (interpolation)
-        {
-            if (stereo)
-                MixStereoToStereoIP(sound, dest, samples, mixRate);
-            else
-                MixStereoToMonoIP(sound, dest, samples, mixRate);
-        }
-        else
-        {
-            if (stereo)
-                MixStereoToStereo(sound, dest, samples, mixRate);
-            else
-                MixStereoToMono(sound, dest, samples, mixRate);
-        }
-    }
-
-    // Update the time position. In stream mode, copy unused data back to the beginning of the stream buffer
-    if (soundStream_)
-    {
-        timePosition_ += ((float)samples / (float)mixRate) * frequency_ / soundStream_->GetFrequency();
-
-        unusedStreamSize_ = Max(streamFilledSize - (int)(size_t)(position_ - streamBuffer_->GetStart()), 0);
-        if (unusedStreamSize_)
-            memcpy(streamBuffer_->GetStart(), (const void*)position_, unusedStreamSize_);
-
-        // If stream did not produce any data, stop if applicable
-        if (!outBytes && soundStream_->GetStopAtEnd())
-        {
-            position_ = 0;
-            return;
-        }
-    }
-    else if (sound_)
-        timePosition_ = ((float)(int)(size_t)(position_ - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
-}
-
-void SoundSource::SetSoundAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    Sound* newSound = cache->GetResource<Sound>(value.name_);
-    if (IsPlaying())
-        Play(newSound);
-    else
-    {
-        // When changing the sound and not playing, free previous sound stream and stream buffer (if any)
-        soundStream_.Reset();
-        streamBuffer_.Reset();
-        sound_ = newSound;
-    }
-}
-
-void SoundSource::SetPlayingAttr(bool value)
-{
-    if (value)
-    {
-        if (!IsPlaying())
-            Play(sound_);
-    }
-    else
-        Stop();
-}
-
-void SoundSource::SetPositionAttr(int value)
-{
-    if (sound_)
-        SetPlayPosition(sound_->GetStart() + value);
-}
-
-ResourceRef SoundSource::GetSoundAttr() const
-{
-    return GetResourceRef(sound_, Sound::GetTypeStatic());
-}
-
-int SoundSource::GetPositionAttr() const
-{
-    if (sound_ && position_)
-        return (int)(GetPlayPosition() - sound_->GetStart());
-    else
-        return 0;
-}
-
-SoundType SoundSource::GetSoundTypeAttr() const
-{
-    for (int i = 0; i < MAX_SOUND_TYPES; i++)
-    {
-        if (soundType_ == soundTypeHashes[i])
-            return static_cast<SoundType>(i);
-    }
-
-    // Valid as this defaults to 0 in type names.
-    return SOUND_MASTER;
-}
-
-void SoundSource::SetSoundTypeAttr(SoundType type)
-{
-    // Ensure that we don't set twice via normal attribute and backwards compatibility attribute.
-    if (soundType_ == StringHash::ZERO)
-        SetSoundType(type);
-}
-
-
-void SoundSource::PlayLockless(Sound* sound)
-{
-    // Reset the time position in any case
-    timePosition_ = 0.0f;
-
-    if (sound)
-    {
-        if (!sound->IsCompressed())
-        {
-            // Uncompressed sound start
-            signed char* start = sound->GetStart();
-            if (start)
-            {
-                // Free existing stream & stream buffer if any
-                soundStream_.Reset();
-                streamBuffer_.Reset();
-                sound_ = sound;
-                position_ = start;
-                fractPosition_ = 0;
-                return;
-            }
-        }
-        else
-        {
-            // Compressed sound start
-            PlayLockless(sound->GetDecoderStream());
-            sound_ = sound;
-            return;
-        }
-    }
-
-    // If sound pointer is null or if sound has no data, stop playback
-    StopLockless();
-    sound_.Reset();
-}
-
-void SoundSource::PlayLockless(SharedPtr<SoundStream> stream)
-{
-    // Reset the time position in any case
-    timePosition_ = 0.0f;
-
-    if (stream)
-    {
-        // Setup the stream buffer
-        unsigned sampleSize = stream->GetSampleSize();
-        unsigned streamBufferSize = sampleSize * stream->GetIntFrequency() * STREAM_BUFFER_LENGTH / 1000;
-
-        streamBuffer_ = new Sound(context_);
-        streamBuffer_->SetSize(streamBufferSize);
-        streamBuffer_->SetFormat(stream->GetIntFrequency(), stream->IsSixteenBit(), stream->IsStereo());
-        streamBuffer_->SetLooped(true);
-
-        soundStream_ = stream;
-        unusedStreamSize_ = 0;
-        position_ = streamBuffer_->GetStart();
-        fractPosition_ = 0;
-        return;
-    }
-
-    // If stream pointer is null, stop playback
-    StopLockless();
-}
-
-void SoundSource::StopLockless()
-{
-    position_ = 0;
-    timePosition_ = 0.0f;
-
-    // Free the sound stream and decode buffer if a stream was playing
-    soundStream_.Reset();
-    streamBuffer_.Reset();
-}
-
-void SoundSource::SetPlayPositionLockless(signed char* pos)
-{
-    // Setting position on a stream is not supported
-    if (!sound_ || soundStream_)
-        return;
-
-    signed char* start = sound_->GetStart();
-    signed char* end = sound_->GetEnd();
-    if (pos < start)
-        pos = start;
-    if (sound_->IsSixteenBit() && (pos - start) & 1)
-        ++pos;
-    if (pos > end)
-        pos = end;
-
-    position_ = pos;
-    timePosition_ = ((float)(int)(size_t)(pos - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
-}
-
-void SoundSource::MixMonoToMono(Sound* sound, int* dest, unsigned samples, int mixRate)
-{
-    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
-    int vol = (int)(256.0f * totalGain + 0.5f);
-    if (!vol)
-    {
-        MixZeroVolume(sound, samples, mixRate);
-        return;
-    }
-
-    float add = frequency_ / (float)mixRate;
-    int intAdd = (int)add;
-    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
-    int fractPos = fractPosition_;
-
-    if (sound->IsSixteenBit())
-    {
-        short* pos = (short*)position_;
-        short* end = (short*)sound->GetEnd();
-        short* repeat = (short*)sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + (*pos * vol) / 256;
-                ++dest;
-                INC_POS_LOOPED();
-            }
-            position_ = (signed char*)pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + (*pos * vol) / 256;
-                ++dest;
-                INC_POS_ONESHOT();
-            }
-            position_ = (signed char*)pos;
-        }
-    }
-    else
-    {
-        signed char* pos = (signed char*)position_;
-        signed char* end = sound->GetEnd();
-        signed char* repeat = sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + *pos * vol;
-                ++dest;
-                INC_POS_LOOPED();
-            }
-            position_ = pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + *pos * vol;
-                ++dest;
-                INC_POS_ONESHOT();
-            }
-            position_ = pos;
-        }
-    }
-    fractPosition_ = fractPos;
-}
-
-void SoundSource::MixMonoToStereo(Sound* sound, int* dest, unsigned samples, int mixRate)
-{
-    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
-    int leftVol = (int)((-panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
-    int rightVol = (int)((panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
-    if (!leftVol && !rightVol)
-    {
-        MixZeroVolume(sound, samples, mixRate);
-        return;
-    }
-
-    float add = frequency_ / (float)mixRate;
-    int intAdd = (int)add;
-    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
-    int fractPos = fractPosition_;
-
-    if (sound->IsSixteenBit())
-    {
-        short* pos = (short*)position_;
-        short* end = (short*)sound->GetEnd();
-        short* repeat = (short*)sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + (*pos * leftVol) / 256;
-                ++dest;
-                *dest = *dest + (*pos * rightVol) / 256;
-                ++dest;
-                INC_POS_LOOPED();
-            }
-            position_ = (signed char*)pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + (*pos * leftVol) / 256;
-                ++dest;
-                *dest = *dest + (*pos * rightVol) / 256;
-                ++dest;
-                INC_POS_ONESHOT();
-            }
-            position_ = (signed char*)pos;
-        }
-    }
-    else
-    {
-        signed char* pos = (signed char*)position_;
-        signed char* end = sound->GetEnd();
-        signed char* repeat = sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + *pos * leftVol;
-                ++dest;
-                *dest = *dest + *pos * rightVol;
-                ++dest;
-                INC_POS_LOOPED();
-            }
-            position_ = pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + *pos * leftVol;
-                ++dest;
-                *dest = *dest + *pos * rightVol;
-                ++dest;
-                INC_POS_ONESHOT();
-            }
-            position_ = pos;
-        }
-    }
-
-    fractPosition_ = fractPos;
-}
-
-void SoundSource::MixMonoToMonoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
-{
-    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
-    int vol = (int)(256.0f * totalGain + 0.5f);
-    if (!vol)
-    {
-        MixZeroVolume(sound, samples, mixRate);
-        return;
-    }
-
-    float add = frequency_ / (float)mixRate;
-    int intAdd = (int)add;
-    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
-    int fractPos = fractPosition_;
-
-    if (sound->IsSixteenBit())
-    {
-        short* pos = (short*)position_;
-        short* end = (short*)sound->GetEnd();
-        short* repeat = (short*)sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + (GET_IP_SAMPLE() * vol) / 256;
-                ++dest;
-                INC_POS_LOOPED();
-            }
-            position_ = (signed char*)pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + (GET_IP_SAMPLE() * vol) / 256;
-                ++dest;
-                INC_POS_ONESHOT();
-            }
-            position_ = (signed char*)pos;
-        }
-    }
-    else
-    {
-        signed char* pos = (signed char*)position_;
-        signed char* end = sound->GetEnd();
-        signed char* repeat = sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + GET_IP_SAMPLE() * vol;
-                ++dest;
-                INC_POS_LOOPED();
-            }
-            position_ = pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + GET_IP_SAMPLE() * vol;
-                ++dest;
-                INC_POS_ONESHOT();
-            }
-            position_ = pos;
-        }
-    }
-
-    fractPosition_ = fractPos;
-}
-
-void SoundSource::MixMonoToStereoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
-{
-    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
-    int leftVol = (int)((-panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
-    int rightVol = (int)((panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
-    if (!leftVol && !rightVol)
-    {
-        MixZeroVolume(sound, samples, mixRate);
-        return;
-    }
-
-    float add = frequency_ / (float)mixRate;
-    int intAdd = (int)add;
-    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
-    int fractPos = fractPosition_;
-
-    if (sound->IsSixteenBit())
-    {
-        short* pos = (short*)position_;
-        short* end = (short*)sound->GetEnd();
-        short* repeat = (short*)sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                int s = GET_IP_SAMPLE();
-                *dest = *dest + (s * leftVol) / 256;
-                ++dest;
-                *dest = *dest + (s * rightVol) / 256;
-                ++dest;
-                INC_POS_LOOPED();
-            }
-            position_ = (signed char*)pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                int s = GET_IP_SAMPLE();
-                *dest = *dest + (s * leftVol) / 256;
-                ++dest;
-                *dest = *dest + (s * rightVol) / 256;
-                ++dest;
-                INC_POS_ONESHOT();
-            }
-            position_ = (signed char*)pos;
-        }
-    }
-    else
-    {
-        signed char* pos = (signed char*)position_;
-        signed char* end = sound->GetEnd();
-        signed char* repeat = sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                int s = GET_IP_SAMPLE();
-                *dest = *dest + s * leftVol;
-                ++dest;
-                *dest = *dest + s * rightVol;
-                ++dest;
-                INC_POS_LOOPED();
-            }
-            position_ = pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                int s = GET_IP_SAMPLE();
-                *dest = *dest + s * leftVol;
-                ++dest;
-                *dest = *dest + s * rightVol;
-                ++dest;
-                INC_POS_ONESHOT();
-            }
-            position_ = pos;
-        }
-    }
-
-    fractPosition_ = fractPos;
-}
-
-void SoundSource::MixStereoToMono(Sound* sound, int* dest, unsigned samples, int mixRate)
-{
-    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
-    int vol = (int)(256.0f * totalGain + 0.5f);
-    if (!vol)
-    {
-        MixZeroVolume(sound, samples, mixRate);
-        return;
-    }
-
-    float add = frequency_ / (float)mixRate;
-    int intAdd = (int)add;
-    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
-    int fractPos = fractPosition_;
-
-    if (sound->IsSixteenBit())
-    {
-        short* pos = (short*)position_;
-        short* end = (short*)sound->GetEnd();
-        short* repeat = (short*)sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                int s = ((int)pos[0] + (int)pos[1]) / 2;
-                *dest = *dest + (s * vol) / 256;
-                ++dest;
-                INC_POS_STEREO_LOOPED();
-            }
-            position_ = (signed char*)pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                int s = ((int)pos[0] + (int)pos[1]) / 2;
-                *dest = *dest + (s * vol) / 256;
-                ++dest;
-                INC_POS_STEREO_ONESHOT();
-            }
-            position_ = (signed char*)pos;
-        }
-    }
-    else
-    {
-        signed char* pos = (signed char*)position_;
-        signed char* end = sound->GetEnd();
-        signed char* repeat = sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                int s = ((int)pos[0] + (int)pos[1]) / 2;
-                *dest = *dest + s * vol;
-                ++dest;
-                INC_POS_STEREO_LOOPED();
-            }
-            position_ = pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                int s = ((int)pos[0] + (int)pos[1]) / 2;
-                *dest = *dest + s * vol;
-                ++dest;
-                INC_POS_STEREO_ONESHOT();
-            }
-            position_ = pos;
-        }
-    }
-
-    fractPosition_ = fractPos;
-}
-
-void SoundSource::MixStereoToStereo(Sound* sound, int* dest, unsigned samples, int mixRate)
-{
-    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
-    int vol = (int)(256.0f * totalGain + 0.5f);
-    if (!vol)
-    {
-        MixZeroVolume(sound, samples, mixRate);
-        return;
-    }
-
-    float add = frequency_ / (float)mixRate;
-    int intAdd = (int)add;
-    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
-    int fractPos = fractPosition_;
-
-    if (sound->IsSixteenBit())
-    {
-        short* pos = (short*)position_;
-        short* end = (short*)sound->GetEnd();
-        short* repeat = (short*)sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + (pos[0] * vol) / 256;
-                ++dest;
-                *dest = *dest + (pos[1] * vol) / 256;
-                ++dest;
-                INC_POS_STEREO_LOOPED();
-            }
-            position_ = (signed char*)pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + (pos[0] * vol) / 256;
-                ++dest;
-                *dest = *dest + (pos[1] * vol) / 256;
-                ++dest;
-                INC_POS_STEREO_ONESHOT();
-            }
-            position_ = (signed char*)pos;
-        }
-    }
-    else
-    {
-        signed char* pos = (signed char*)position_;
-        signed char* end = sound->GetEnd();
-        signed char* repeat = sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + pos[0] * vol;
-                ++dest;
-                *dest = *dest + pos[1] * vol;
-                ++dest;
-                INC_POS_STEREO_LOOPED();
-            }
-            position_ = pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + pos[0] * vol;
-                ++dest;
-                *dest = *dest + pos[1] * vol;
-                ++dest;
-                INC_POS_STEREO_ONESHOT();
-            }
-            position_ = pos;
-        }
-    }
-
-    fractPosition_ = fractPos;
-}
-
-void SoundSource::MixStereoToMonoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
-{
-    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
-    int vol = (int)(256.0f * totalGain + 0.5f);
-    if (!vol)
-    {
-        MixZeroVolume(sound, samples, mixRate);
-        return;
-    }
-
-    float add = frequency_ / (float)mixRate;
-    int intAdd = (int)add;
-    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
-    int fractPos = fractPosition_;
-
-    if (sound->IsSixteenBit())
-    {
-        short* pos = (short*)position_;
-        short* end = (short*)sound->GetEnd();
-        short* repeat = (short*)sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
-                *dest = *dest + (s * vol) / 256;
-                ++dest;
-                INC_POS_STEREO_LOOPED();
-            }
-            position_ = (signed char*)pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
-                *dest = *dest + (s * vol) / 256;
-                ++dest;
-                INC_POS_STEREO_ONESHOT();
-            }
-            position_ = (signed char*)pos;
-        }
-    }
-    else
-    {
-        signed char* pos = (signed char*)position_;
-        signed char* end = sound->GetEnd();
-        signed char* repeat = sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
-                *dest = *dest + s * vol;
-                ++dest;
-                INC_POS_STEREO_LOOPED();
-            }
-            position_ = pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
-                *dest = *dest + s * vol;
-                ++dest;
-                INC_POS_STEREO_ONESHOT();
-            }
-            position_ = pos;
-        }
-    }
-
-    fractPosition_ = fractPos;
-}
-
-void SoundSource::MixStereoToStereoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
-{
-    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
-    int vol = (int)(256.0f * totalGain + 0.5f);
-    if (!vol)
-    {
-        MixZeroVolume(sound, samples, mixRate);
-        return;
-    }
-
-    float add = frequency_ / (float)mixRate;
-    int intAdd = (int)add;
-    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
-    int fractPos = fractPosition_;
-
-    if (sound->IsSixteenBit())
-    {
-        short* pos = (short*)position_;
-        short* end = (short*)sound->GetEnd();
-        short* repeat = (short*)sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + (GET_IP_SAMPLE_LEFT() * vol) / 256;
-                ++dest;
-                *dest = *dest + (GET_IP_SAMPLE_RIGHT() * vol) / 256;
-                ++dest;
-                INC_POS_STEREO_LOOPED();
-            }
-            position_ = (signed char*)pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + (GET_IP_SAMPLE_LEFT() * vol) / 256;
-                ++dest;
-                *dest = *dest + (GET_IP_SAMPLE_RIGHT() * vol) / 256;
-                ++dest;
-                INC_POS_STEREO_ONESHOT();
-            }
-            position_ = (signed char*)pos;
-        }
-    }
-    else
-    {
-        signed char* pos = (signed char*)position_;
-        signed char* end = sound->GetEnd();
-        signed char* repeat = sound->GetRepeat();
-
-        if (sound->IsLooped())
-        {
-            while (samples--)
-            {
-                *dest = *dest + GET_IP_SAMPLE_LEFT() * vol;
-                ++dest;
-                *dest = *dest + GET_IP_SAMPLE_RIGHT() * vol;
-                ++dest;
-                INC_POS_STEREO_LOOPED();
-            }
-            position_ = pos;
-        }
-        else
-        {
-            while (samples--)
-            {
-                *dest = *dest + GET_IP_SAMPLE_LEFT() * vol;
-                ++dest;
-                *dest = *dest + GET_IP_SAMPLE_RIGHT() * vol;
-                ++dest;
-                INC_POS_STEREO_ONESHOT();
-            }
-            position_ = pos;
-        }
-    }
-
-    fractPosition_ = fractPos;
-}
-
-void SoundSource::MixZeroVolume(Sound* sound, unsigned samples, int mixRate)
-{
-    float add = frequency_ * (float)samples / (float)mixRate;
-    int intAdd = (int)add;
-    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
-    unsigned sampleSize = sound->GetSampleSize();
-
-    fractPosition_ += fractAdd;
-    if (fractPosition_ > 65535)
-    {
-        fractPosition_ &= 65535;
-        position_ += sampleSize;
-    }
-    position_ += intAdd * sampleSize;
-
-    if (position_ > sound->GetEnd())
-    {
-        if (sound->IsLooped())
-        {
-            while (position_ >= sound->GetEnd())
-            {
-                position_ -= (sound->GetEnd() - sound->GetRepeat());
-            }
-        }
-        else
-            position_ = 0;
-    }
-}
-
-void SoundSource::MixNull(float timeStep)
-{
-    if (!position_ || !sound_ || !IsEnabledEffective())
-        return;
-
-    // Advance only the time position
-    timePosition_ += timeStep * frequency_ / sound_->GetFrequency();
-
-    if (sound_->IsLooped())
-    {
-        // For simulated playback, simply reset the time position to zero when the sound loops
-        if (timePosition_ >= sound_->GetLength())
-            timePosition_ -= sound_->GetLength();
-    }
-    else
-    {
-        if (timePosition_ >= sound_->GetLength())
-        {
-            position_ = 0;
-            timePosition_ = 0.0f;
-        }
-    }
-}
-
-}
+}
+
+void SoundSource::Play(Sound* sound)
+{
+    if (!audio_)
+        return;
+
+    // If no frequency set yet, set from the sound's default
+    if (frequency_ == 0.0f && sound)
+        SetFrequency(sound->GetFrequency());
+
+    // If sound source is currently playing, have to lock the audio mutex
+    if (position_)
+    {
+        MutexLock lock(audio_->GetMutex());
+        PlayLockless(sound);
+    }
+    else
+        PlayLockless(sound);
+
+    MarkNetworkUpdate();
+}
+
+void SoundSource::Play(Sound* sound, float frequency)
+{
+    SetFrequency(frequency);
+    Play(sound);
+}
+
+void SoundSource::Play(Sound* sound, float frequency, float gain)
+{
+    SetFrequency(frequency);
+    SetGain(gain);
+    Play(sound);
+}
+
+void SoundSource::Play(Sound* sound, float frequency, float gain, float panning)
+{
+    SetFrequency(frequency);
+    SetGain(gain);
+    SetPanning(panning);
+    Play(sound);
+}
+
+void SoundSource::Play(SoundStream* stream)
+{
+    if (!audio_)
+        return;
+
+    // If no frequency set yet, set from the stream's default
+    if (frequency_ == 0.0f && stream)
+        SetFrequency(stream->GetFrequency());
+
+    SharedPtr<SoundStream> streamPtr(stream);
+
+    // If sound source is currently playing, have to lock the audio mutex. When stream playback is explicitly
+    // requested, clear the existing sound if any
+    if (position_)
+    {
+        MutexLock lock(audio_->GetMutex());
+        sound_.Reset();
+        PlayLockless(streamPtr);
+    }
+    else
+    {
+        sound_.Reset();
+        PlayLockless(streamPtr);
+    }
+
+    // Stream playback is not supported for network replication, no need to mark network dirty
+}
+
+void SoundSource::Stop()
+{
+    if (!audio_)
+        return;
+
+    // If sound source is currently playing, have to lock the audio mutex
+    if (position_)
+    {
+        MutexLock lock(audio_->GetMutex());
+        StopLockless();
+    }
+    else
+        StopLockless();
+
+    MarkNetworkUpdate();
+}
+
+void SoundSource::SetSoundType(const String& type)
+{
+    if (type == SOUND_MASTER)
+        return;
+
+    if (!audio_->IsMasterGain(type))
+        audio_->SetMasterGain(type, 1.0f);
+
+    soundType_ = type;
+    MarkNetworkUpdate();
+}
+
+void SoundSource::SetFrequency(float frequency)
+{
+    frequency_ = Clamp(frequency, 0.0f, 535232.0f);
+    MarkNetworkUpdate();
+}
+
+void SoundSource::SetGain(float gain)
+{
+    gain_ = Max(gain, 0.0f);
+    MarkNetworkUpdate();
+}
+
+void SoundSource::SetAttenuation(float attenuation)
+{
+    attenuation_ = Clamp(attenuation, 0.0f, 1.0f);
+    MarkNetworkUpdate();
+}
+
+void SoundSource::SetPanning(float panning)
+{
+    panning_ = Clamp(panning, -1.0f, 1.0f);
+    MarkNetworkUpdate();
+}
+
+void SoundSource::SetAutoRemove(bool enable)
+{
+    autoRemove_ = enable;
+}
+
+bool SoundSource::IsPlaying() const
+{
+    return (sound_ || soundStream_) && position_ != 0;
+}
+
+void SoundSource::SetPlayPosition(signed char* pos)
+{
+    // Setting play position on a stream is not supported
+    if (!audio_ || !sound_ || soundStream_)
+        return;
+
+    MutexLock lock(audio_->GetMutex());
+    SetPlayPositionLockless(pos);
+}
+
+void SoundSource::Update(float timeStep)
+{
+    if (!audio_ || !IsEnabledEffective())
+        return;
+
+    // If there is no actual audio output, perform fake mixing into a nonexistent buffer to check stopping/looping
+    if (!audio_->IsInitialized())
+        MixNull(timeStep);
+
+    // Free the stream if playback has stopped
+    if (soundStream_ && !position_)
+        StopLockless();
+
+    // Check for autoremove
+    if (autoRemove_)
+    {
+        if (!IsPlaying())
+        {
+            autoRemoveTimer_ += timeStep;
+            if (autoRemoveTimer_ > AUTOREMOVE_DELAY)
+            {
+                Remove();
+                // Note: this object is now deleted, so only returning immediately is safe
+                return;
+            }
+        }
+        else
+            autoRemoveTimer_ = 0.0f;
+    }
+}
+
+void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, bool interpolation)
+{
+    if (!position_ || (!sound_ && !soundStream_) || !IsEnabledEffective())
+        return;
+
+    int streamFilledSize, outBytes;
+
+    if (soundStream_ && streamBuffer_)
+    {
+        int streamBufferSize = streamBuffer_->GetDataSize();
+        // Calculate how many bytes of stream sound data is needed
+        int neededSize = (int)((float)samples * frequency_ / (float)mixRate);
+        // Add a little safety buffer. Subtract previous unused data
+        neededSize += STREAM_SAFETY_SAMPLES;
+        neededSize *= soundStream_->GetSampleSize();
+        neededSize -= unusedStreamSize_;
+        neededSize = Clamp(neededSize, 0, streamBufferSize - unusedStreamSize_);
+
+        // Always start play position at the beginning of the stream buffer
+        position_ = streamBuffer_->GetStart();
+
+        // Request new data from the stream
+        signed char* dest = streamBuffer_->GetStart() + unusedStreamSize_;
+        outBytes = neededSize ? soundStream_->GetData(dest, neededSize) : 0;
+        dest += outBytes;
+        // Zero-fill rest if stream did not produce enough data
+        if (outBytes < neededSize)
+            memset(dest, 0, neededSize - outBytes);
+
+        // Calculate amount of total bytes of data in stream buffer now, to know how much went unused after mixing
+        streamFilledSize = neededSize + unusedStreamSize_;
+    }
+
+    // If streaming, play the stream buffer. Otherwise play the original sound
+    Sound* sound = soundStream_ ? streamBuffer_ : sound_;
+    if (!sound)
+        return;
+
+    // Choose the correct mixing routine
+    if (!sound->IsStereo())
+    {
+        if (interpolation)
+        {
+            if (stereo)
+                MixMonoToStereoIP(sound, dest, samples, mixRate);
+            else
+                MixMonoToMonoIP(sound, dest, samples, mixRate);
+        }
+        else
+        {
+            if (stereo)
+                MixMonoToStereo(sound, dest, samples, mixRate);
+            else
+                MixMonoToMono(sound, dest, samples, mixRate);
+        }
+    }
+    else
+    {
+        if (interpolation)
+        {
+            if (stereo)
+                MixStereoToStereoIP(sound, dest, samples, mixRate);
+            else
+                MixStereoToMonoIP(sound, dest, samples, mixRate);
+        }
+        else
+        {
+            if (stereo)
+                MixStereoToStereo(sound, dest, samples, mixRate);
+            else
+                MixStereoToMono(sound, dest, samples, mixRate);
+        }
+    }
+
+    // Update the time position. In stream mode, copy unused data back to the beginning of the stream buffer
+    if (soundStream_)
+    {
+        timePosition_ += ((float)samples / (float)mixRate) * frequency_ / soundStream_->GetFrequency();
+
+        unusedStreamSize_ = Max(streamFilledSize - (int)(size_t)(position_ - streamBuffer_->GetStart()), 0);
+        if (unusedStreamSize_)
+            memcpy(streamBuffer_->GetStart(), (const void*)position_, unusedStreamSize_);
+
+        // If stream did not produce any data, stop if applicable
+        if (!outBytes && soundStream_->GetStopAtEnd())
+        {
+            position_ = 0;
+            return;
+        }
+    }
+    else if (sound_)
+        timePosition_ = ((float)(int)(size_t)(position_ - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
+}
+
+void SoundSource::SetSoundAttr(const ResourceRef& value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    Sound* newSound = cache->GetResource<Sound>(value.name_);
+    if (IsPlaying())
+        Play(newSound);
+    else
+    {
+        // When changing the sound and not playing, free previous sound stream and stream buffer (if any)
+        soundStream_.Reset();
+        streamBuffer_.Reset();
+        sound_ = newSound;
+    }
+}
+
+void SoundSource::SetPlayingAttr(bool value)
+{
+    if (value)
+    {
+        if (!IsPlaying())
+            Play(sound_);
+    }
+    else
+        Stop();
+}
+
+void SoundSource::SetPositionAttr(int value)
+{
+    if (sound_)
+        SetPlayPosition(sound_->GetStart() + value);
+}
+
+ResourceRef SoundSource::GetSoundAttr() const
+{
+    return GetResourceRef(sound_, Sound::GetTypeStatic());
+}
+
+int SoundSource::GetPositionAttr() const
+{
+    if (sound_ && position_)
+        return (int)(GetPlayPosition() - sound_->GetStart());
+    else
+        return 0;
+}
+
+void SoundSource::PlayLockless(Sound* sound)
+{
+    // Reset the time position in any case
+    timePosition_ = 0.0f;
+
+    if (sound)
+    {
+        if (!sound->IsCompressed())
+        {
+            // Uncompressed sound start
+            signed char* start = sound->GetStart();
+            if (start)
+            {
+                // Free existing stream & stream buffer if any
+                soundStream_.Reset();
+                streamBuffer_.Reset();
+                sound_ = sound;
+                position_ = start;
+                fractPosition_ = 0;
+                return;
+            }
+        }
+        else
+        {
+            // Compressed sound start
+            PlayLockless(sound->GetDecoderStream());
+            sound_ = sound;
+            return;
+        }
+    }
+
+    // If sound pointer is null or if sound has no data, stop playback
+    StopLockless();
+    sound_.Reset();
+}
+
+void SoundSource::PlayLockless(SharedPtr<SoundStream> stream)
+{
+    // Reset the time position in any case
+    timePosition_ = 0.0f;
+
+    if (stream)
+    {
+        // Setup the stream buffer
+        unsigned sampleSize = stream->GetSampleSize();
+        unsigned streamBufferSize = sampleSize * stream->GetIntFrequency() * STREAM_BUFFER_LENGTH / 1000;
+
+        streamBuffer_ = new Sound(context_);
+        streamBuffer_->SetSize(streamBufferSize);
+        streamBuffer_->SetFormat(stream->GetIntFrequency(), stream->IsSixteenBit(), stream->IsStereo());
+        streamBuffer_->SetLooped(true);
+
+        soundStream_ = stream;
+        unusedStreamSize_ = 0;
+        position_ = streamBuffer_->GetStart();
+        fractPosition_ = 0;
+        return;
+    }
+
+    // If stream pointer is null, stop playback
+    StopLockless();
+}
+
+void SoundSource::StopLockless()
+{
+    position_ = 0;
+    timePosition_ = 0.0f;
+
+    // Free the sound stream and decode buffer if a stream was playing
+    soundStream_.Reset();
+    streamBuffer_.Reset();
+}
+
+void SoundSource::SetPlayPositionLockless(signed char* pos)
+{
+    // Setting position on a stream is not supported
+    if (!sound_ || soundStream_)
+        return;
+
+    signed char* start = sound_->GetStart();
+    signed char* end = sound_->GetEnd();
+    if (pos < start)
+        pos = start;
+    if (sound_->IsSixteenBit() && (pos - start) & 1)
+        ++pos;
+    if (pos > end)
+        pos = end;
+
+    position_ = pos;
+    timePosition_ = ((float)(int)(size_t)(pos - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
+}
+
+void SoundSource::MixMonoToMono(Sound* sound, int* dest, unsigned samples, int mixRate)
+{
+    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
+    int vol = (int)(256.0f * totalGain + 0.5f);
+    if (!vol)
+    {
+        MixZeroVolume(sound, samples, mixRate);
+        return;
+    }
+
+    float add = frequency_ / (float)mixRate;
+    int intAdd = (int)add;
+    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
+    int fractPos = fractPosition_;
+
+    if (sound->IsSixteenBit())
+    {
+        short* pos = (short*)position_;
+        short* end = (short*)sound->GetEnd();
+        short* repeat = (short*)sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + (*pos * vol) / 256;
+                ++dest;
+                INC_POS_LOOPED();
+            }
+            position_ = (signed char*)pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + (*pos * vol) / 256;
+                ++dest;
+                INC_POS_ONESHOT();
+            }
+            position_ = (signed char*)pos;
+        }
+    }
+    else
+    {
+        signed char* pos = (signed char*)position_;
+        signed char* end = sound->GetEnd();
+        signed char* repeat = sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + *pos * vol;
+                ++dest;
+                INC_POS_LOOPED();
+            }
+            position_ = pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + *pos * vol;
+                ++dest;
+                INC_POS_ONESHOT();
+            }
+            position_ = pos;
+        }
+    }
+    fractPosition_ = fractPos;
+}
+
+void SoundSource::MixMonoToStereo(Sound* sound, int* dest, unsigned samples, int mixRate)
+{
+    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
+    int leftVol = (int)((-panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
+    int rightVol = (int)((panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
+    if (!leftVol && !rightVol)
+    {
+        MixZeroVolume(sound, samples, mixRate);
+        return;
+    }
+
+    float add = frequency_ / (float)mixRate;
+    int intAdd = (int)add;
+    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
+    int fractPos = fractPosition_;
+
+    if (sound->IsSixteenBit())
+    {
+        short* pos = (short*)position_;
+        short* end = (short*)sound->GetEnd();
+        short* repeat = (short*)sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + (*pos * leftVol) / 256;
+                ++dest;
+                *dest = *dest + (*pos * rightVol) / 256;
+                ++dest;
+                INC_POS_LOOPED();
+            }
+            position_ = (signed char*)pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + (*pos * leftVol) / 256;
+                ++dest;
+                *dest = *dest + (*pos * rightVol) / 256;
+                ++dest;
+                INC_POS_ONESHOT();
+            }
+            position_ = (signed char*)pos;
+        }
+    }
+    else
+    {
+        signed char* pos = (signed char*)position_;
+        signed char* end = sound->GetEnd();
+        signed char* repeat = sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + *pos * leftVol;
+                ++dest;
+                *dest = *dest + *pos * rightVol;
+                ++dest;
+                INC_POS_LOOPED();
+            }
+            position_ = pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + *pos * leftVol;
+                ++dest;
+                *dest = *dest + *pos * rightVol;
+                ++dest;
+                INC_POS_ONESHOT();
+            }
+            position_ = pos;
+        }
+    }
+
+    fractPosition_ = fractPos;
+}
+
+void SoundSource::MixMonoToMonoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
+{
+    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
+    int vol = (int)(256.0f * totalGain + 0.5f);
+    if (!vol)
+    {
+        MixZeroVolume(sound, samples, mixRate);
+        return;
+    }
+
+    float add = frequency_ / (float)mixRate;
+    int intAdd = (int)add;
+    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
+    int fractPos = fractPosition_;
+
+    if (sound->IsSixteenBit())
+    {
+        short* pos = (short*)position_;
+        short* end = (short*)sound->GetEnd();
+        short* repeat = (short*)sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + (GET_IP_SAMPLE() * vol) / 256;
+                ++dest;
+                INC_POS_LOOPED();
+            }
+            position_ = (signed char*)pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + (GET_IP_SAMPLE() * vol) / 256;
+                ++dest;
+                INC_POS_ONESHOT();
+            }
+            position_ = (signed char*)pos;
+        }
+    }
+    else
+    {
+        signed char* pos = (signed char*)position_;
+        signed char* end = sound->GetEnd();
+        signed char* repeat = sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + GET_IP_SAMPLE() * vol;
+                ++dest;
+                INC_POS_LOOPED();
+            }
+            position_ = pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + GET_IP_SAMPLE() * vol;
+                ++dest;
+                INC_POS_ONESHOT();
+            }
+            position_ = pos;
+        }
+    }
+
+    fractPosition_ = fractPos;
+}
+
+void SoundSource::MixMonoToStereoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
+{
+    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
+    int leftVol = (int)((-panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
+    int rightVol = (int)((panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
+    if (!leftVol && !rightVol)
+    {
+        MixZeroVolume(sound, samples, mixRate);
+        return;
+    }
+
+    float add = frequency_ / (float)mixRate;
+    int intAdd = (int)add;
+    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
+    int fractPos = fractPosition_;
+
+    if (sound->IsSixteenBit())
+    {
+        short* pos = (short*)position_;
+        short* end = (short*)sound->GetEnd();
+        short* repeat = (short*)sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                int s = GET_IP_SAMPLE();
+                *dest = *dest + (s * leftVol) / 256;
+                ++dest;
+                *dest = *dest + (s * rightVol) / 256;
+                ++dest;
+                INC_POS_LOOPED();
+            }
+            position_ = (signed char*)pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                int s = GET_IP_SAMPLE();
+                *dest = *dest + (s * leftVol) / 256;
+                ++dest;
+                *dest = *dest + (s * rightVol) / 256;
+                ++dest;
+                INC_POS_ONESHOT();
+            }
+            position_ = (signed char*)pos;
+        }
+    }
+    else
+    {
+        signed char* pos = (signed char*)position_;
+        signed char* end = sound->GetEnd();
+        signed char* repeat = sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                int s = GET_IP_SAMPLE();
+                *dest = *dest + s * leftVol;
+                ++dest;
+                *dest = *dest + s * rightVol;
+                ++dest;
+                INC_POS_LOOPED();
+            }
+            position_ = pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                int s = GET_IP_SAMPLE();
+                *dest = *dest + s * leftVol;
+                ++dest;
+                *dest = *dest + s * rightVol;
+                ++dest;
+                INC_POS_ONESHOT();
+            }
+            position_ = pos;
+        }
+    }
+
+    fractPosition_ = fractPos;
+}
+
+void SoundSource::MixStereoToMono(Sound* sound, int* dest, unsigned samples, int mixRate)
+{
+    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
+    int vol = (int)(256.0f * totalGain + 0.5f);
+    if (!vol)
+    {
+        MixZeroVolume(sound, samples, mixRate);
+        return;
+    }
+
+    float add = frequency_ / (float)mixRate;
+    int intAdd = (int)add;
+    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
+    int fractPos = fractPosition_;
+
+    if (sound->IsSixteenBit())
+    {
+        short* pos = (short*)position_;
+        short* end = (short*)sound->GetEnd();
+        short* repeat = (short*)sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                int s = ((int)pos[0] + (int)pos[1]) / 2;
+                *dest = *dest + (s * vol) / 256;
+                ++dest;
+                INC_POS_STEREO_LOOPED();
+            }
+            position_ = (signed char*)pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                int s = ((int)pos[0] + (int)pos[1]) / 2;
+                *dest = *dest + (s * vol) / 256;
+                ++dest;
+                INC_POS_STEREO_ONESHOT();
+            }
+            position_ = (signed char*)pos;
+        }
+    }
+    else
+    {
+        signed char* pos = (signed char*)position_;
+        signed char* end = sound->GetEnd();
+        signed char* repeat = sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                int s = ((int)pos[0] + (int)pos[1]) / 2;
+                *dest = *dest + s * vol;
+                ++dest;
+                INC_POS_STEREO_LOOPED();
+            }
+            position_ = pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                int s = ((int)pos[0] + (int)pos[1]) / 2;
+                *dest = *dest + s * vol;
+                ++dest;
+                INC_POS_STEREO_ONESHOT();
+            }
+            position_ = pos;
+        }
+    }
+
+    fractPosition_ = fractPos;
+}
+
+void SoundSource::MixStereoToStereo(Sound* sound, int* dest, unsigned samples, int mixRate)
+{
+    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
+    int vol = (int)(256.0f * totalGain + 0.5f);
+    if (!vol)
+    {
+        MixZeroVolume(sound, samples, mixRate);
+        return;
+    }
+
+    float add = frequency_ / (float)mixRate;
+    int intAdd = (int)add;
+    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
+    int fractPos = fractPosition_;
+
+    if (sound->IsSixteenBit())
+    {
+        short* pos = (short*)position_;
+        short* end = (short*)sound->GetEnd();
+        short* repeat = (short*)sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + (pos[0] * vol) / 256;
+                ++dest;
+                *dest = *dest + (pos[1] * vol) / 256;
+                ++dest;
+                INC_POS_STEREO_LOOPED();
+            }
+            position_ = (signed char*)pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + (pos[0] * vol) / 256;
+                ++dest;
+                *dest = *dest + (pos[1] * vol) / 256;
+                ++dest;
+                INC_POS_STEREO_ONESHOT();
+            }
+            position_ = (signed char*)pos;
+        }
+    }
+    else
+    {
+        signed char* pos = (signed char*)position_;
+        signed char* end = sound->GetEnd();
+        signed char* repeat = sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + pos[0] * vol;
+                ++dest;
+                *dest = *dest + pos[1] * vol;
+                ++dest;
+                INC_POS_STEREO_LOOPED();
+            }
+            position_ = pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + pos[0] * vol;
+                ++dest;
+                *dest = *dest + pos[1] * vol;
+                ++dest;
+                INC_POS_STEREO_ONESHOT();
+            }
+            position_ = pos;
+        }
+    }
+
+    fractPosition_ = fractPos;
+}
+
+void SoundSource::MixStereoToMonoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
+{
+    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
+    int vol = (int)(256.0f * totalGain + 0.5f);
+    if (!vol)
+    {
+        MixZeroVolume(sound, samples, mixRate);
+        return;
+    }
+
+    float add = frequency_ / (float)mixRate;
+    int intAdd = (int)add;
+    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
+    int fractPos = fractPosition_;
+
+    if (sound->IsSixteenBit())
+    {
+        short* pos = (short*)position_;
+        short* end = (short*)sound->GetEnd();
+        short* repeat = (short*)sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
+                *dest = *dest + (s * vol) / 256;
+                ++dest;
+                INC_POS_STEREO_LOOPED();
+            }
+            position_ = (signed char*)pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
+                *dest = *dest + (s * vol) / 256;
+                ++dest;
+                INC_POS_STEREO_ONESHOT();
+            }
+            position_ = (signed char*)pos;
+        }
+    }
+    else
+    {
+        signed char* pos = (signed char*)position_;
+        signed char* end = sound->GetEnd();
+        signed char* repeat = sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
+                *dest = *dest + s * vol;
+                ++dest;
+                INC_POS_STEREO_LOOPED();
+            }
+            position_ = pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
+                *dest = *dest + s * vol;
+                ++dest;
+                INC_POS_STEREO_ONESHOT();
+            }
+            position_ = pos;
+        }
+    }
+
+    fractPosition_ = fractPos;
+}
+
+void SoundSource::MixStereoToStereoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
+{
+    float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
+    int vol = (int)(256.0f * totalGain + 0.5f);
+    if (!vol)
+    {
+        MixZeroVolume(sound, samples, mixRate);
+        return;
+    }
+
+    float add = frequency_ / (float)mixRate;
+    int intAdd = (int)add;
+    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
+    int fractPos = fractPosition_;
+
+    if (sound->IsSixteenBit())
+    {
+        short* pos = (short*)position_;
+        short* end = (short*)sound->GetEnd();
+        short* repeat = (short*)sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + (GET_IP_SAMPLE_LEFT() * vol) / 256;
+                ++dest;
+                *dest = *dest + (GET_IP_SAMPLE_RIGHT() * vol) / 256;
+                ++dest;
+                INC_POS_STEREO_LOOPED();
+            }
+            position_ = (signed char*)pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + (GET_IP_SAMPLE_LEFT() * vol) / 256;
+                ++dest;
+                *dest = *dest + (GET_IP_SAMPLE_RIGHT() * vol) / 256;
+                ++dest;
+                INC_POS_STEREO_ONESHOT();
+            }
+            position_ = (signed char*)pos;
+        }
+    }
+    else
+    {
+        signed char* pos = (signed char*)position_;
+        signed char* end = sound->GetEnd();
+        signed char* repeat = sound->GetRepeat();
+
+        if (sound->IsLooped())
+        {
+            while (samples--)
+            {
+                *dest = *dest + GET_IP_SAMPLE_LEFT() * vol;
+                ++dest;
+                *dest = *dest + GET_IP_SAMPLE_RIGHT() * vol;
+                ++dest;
+                INC_POS_STEREO_LOOPED();
+            }
+            position_ = pos;
+        }
+        else
+        {
+            while (samples--)
+            {
+                *dest = *dest + GET_IP_SAMPLE_LEFT() * vol;
+                ++dest;
+                *dest = *dest + GET_IP_SAMPLE_RIGHT() * vol;
+                ++dest;
+                INC_POS_STEREO_ONESHOT();
+            }
+            position_ = pos;
+        }
+    }
+
+    fractPosition_ = fractPos;
+}
+
+void SoundSource::MixZeroVolume(Sound* sound, unsigned samples, int mixRate)
+{
+    float add = frequency_ * (float)samples / (float)mixRate;
+    int intAdd = (int)add;
+    int fractAdd = (int)((add - floorf(add)) * 65536.0f);
+    unsigned sampleSize = sound->GetSampleSize();
+
+    fractPosition_ += fractAdd;
+    if (fractPosition_ > 65535)
+    {
+        fractPosition_ &= 65535;
+        position_ += sampleSize;
+    }
+    position_ += intAdd * sampleSize;
+
+    if (position_ > sound->GetEnd())
+    {
+        if (sound->IsLooped())
+        {
+            while (position_ >= sound->GetEnd())
+            {
+                position_ -= (sound->GetEnd() - sound->GetRepeat());
+            }
+        }
+        else
+            position_ = 0;
+    }
+}
+
+void SoundSource::MixNull(float timeStep)
+{
+    if (!position_ || !sound_ || !IsEnabledEffective())
+        return;
+
+    // Advance only the time position
+    timePosition_ += timeStep * frequency_ / sound_->GetFrequency();
+
+    if (sound_->IsLooped())
+    {
+        // For simulated playback, simply reset the time position to zero when the sound loops
+        if (timePosition_ >= sound_->GetLength())
+            timePosition_ -= sound_->GetLength();
+    }
+    else
+    {
+        if (timePosition_ >= sound_->GetLength())
+        {
+            position_ = 0;
+            timePosition_ = 0.0f;
+        }
+    }
+}
+
+}

+ 3 - 9
Source/Engine/Audio/SoundSource.h

@@ -60,10 +60,8 @@ public:
     void Play(SoundStream* stream);
     /// Stop playback.
     void Stop();
-    /// Set sound type, determines the master gain group by enum.
-    void SetSoundType(SoundType type);   
     /// Set sound type, determines the master gain group.
-    void SetSoundType(const StringHash& type);
+    void SetSoundType(const String& type);
     /// Set frequency.
     void SetFrequency(float frequency);
     /// Set gain. 0.0 is silence, 1.0 is full volume.
@@ -82,7 +80,7 @@ public:
     /// Return playback position.
     volatile signed char* GetPlayPosition() const { return position_; }
     /// Return sound type, determines the master gain group.
-    StringHash GetSoundType() const { return soundType_; }
+    String GetSoundType() const { return soundType_; }
     /// Return playback time position.
     float GetTimePosition() const { return timePosition_; }
     /// Return frequency.
@@ -113,16 +111,12 @@ public:
     void SetPlayingAttr(bool value);
     /// Return sound position attribute.
     int GetPositionAttr() const;
-    /// Set sound type attribute for backwards compatibility.
-    void SetSoundTypeAttr(SoundType type);
-    /// Return sound type attribute for backwards compatibility.
-    SoundType GetSoundTypeAttr() const;
     
 protected:
     /// Audio subsystem.
     WeakPtr<Audio> audio_;
     /// SoundSource type, determines the master gain group.
-    StringHash soundType_;
+    String soundType_;
     /// Frequency.
     float frequency_;
     /// Gain.

+ 0 - 2
Source/Engine/Core/Attribute.h

@@ -46,8 +46,6 @@ static const unsigned AM_NODEID = 0x10;
 static const unsigned AM_COMPONENTID = 0x20;
 /// Attribute is a node ID vector where first element is the amount of nodes.
 static const unsigned AM_NODEIDVECTOR = 0x40;
-/// Attribute is only read not written, mainly used for backwards compatibility with serialization.
-static const unsigned AM_READ = 0x80;
 
 class Serializable;
 

+ 6 - 16
Source/Engine/LuaScript/pkgs/Audio/Audio.pkg

@@ -1,22 +1,13 @@
 $#include "Audio.h"
 
-enum SoundType
-{
-    SOUND_EFFECT,
-    SOUND_AMBIENT,
-    SOUND_VOICE,
-    SOUND_MUSIC,
-    SOUND_MASTER,
-    MAX_SOUND_TYPES
-};
+static const String SOUND_MASTER;
 
 class Audio : public Object
 {
     bool SetMode(int bufferLengthMSec, int mixRate, bool stereo, bool interpolation = true);
     bool Play();
     void Stop();
-    void SetMasterGain(SoundType type, float gain);
-    void SetMasterGain(const StringHash& type, float gain);
+    void SetMasterGain(const String& type, float gain);
     void SetListener(SoundListener* listener);
     void StopSound(Sound* sound);
 
@@ -26,16 +17,15 @@ class Audio : public Object
     bool IsStereo() const;
     bool IsPlaying() const;
     bool IsInitialized() const;
-    bool IsMasterGain(const StringHash& type) const;
-    float GetMasterGain(SoundType type) const;
-    float GetMasterGain(const StringHash& type) const;
+    bool IsMasterGain(const String& type) const;
+    float GetMasterGain(const String& type) const;
     SoundListener* GetListener() const;
     const PODVector<SoundSource*>& GetSoundSources() const;
 
     void AddSoundSource(SoundSource* soundSource);
     void RemoveSoundSource(SoundSource* soundSource);
-    float GetSoundSourceMasterGain(SoundType type) const;
-    float GetSoundSourceMasterGain(const StringHash& type) const;
+    float GetSoundSourceMasterGain(const String& type) const;
+	
     void MixOutput(void *dest, unsigned samples);
 
     tolua_readonly tolua_property__get_set unsigned sampleSize;

+ 3 - 4
Source/Engine/LuaScript/pkgs/Audio/SoundSource.pkg

@@ -9,8 +9,7 @@ class SoundSource : public Component
     void Play(Sound* sound, float frequency, float gain);
     void Play(Sound* sound, float frequency, float gain, float panning);
     void Stop();
-    void SetSoundType(SoundType type);
-    void SetSoundType(const StringHash& type);
+    void SetSoundType(const String& type);
     void SetFrequency(float frequency);
     void SetGain(float gain);
     void SetAttenuation(float attenuation);
@@ -18,7 +17,7 @@ class SoundSource : public Component
     void SetAutoRemove(bool enable);
 
     Sound* GetSound() const;
-    StringHash GetSoundType() const;
+    String GetSoundType() const;
     float GetTimePosition() const;
     float GetFrequency() const;
     float GetGain() const;
@@ -28,7 +27,7 @@ class SoundSource : public Component
     bool IsPlaying() const;
     
     tolua_readonly tolua_property__get_set Sound* sound;
-    tolua_property__get_set StringHash soundType;
+    tolua_property__get_set String soundType;
     tolua_readonly tolua_property__get_set float timePosition;
     tolua_property__get_set float frequency;
     tolua_property__get_set float gain;

+ 3 - 3
Source/Engine/LuaScript/pkgs/UI/Cursor.pkg

@@ -26,12 +26,12 @@ class Cursor : public BorderImage
     void DefineShape(CursorShape shape, Image* image, const IntRect& imageRect, const IntVector2& hotSpot);
     
     void SetShape(CursorShape shape);
-    void SetShape(const StringHash& shape);
+    void SetShape(const String& shape);
     void SetUseSystemShapes(bool enable);
-    StringHash GetShape() const;
+    String GetShape() const;
     bool GetUseSystemShapes() const;
 
-    tolua_property__get_set StringHash shape;
+    tolua_property__get_set String shape;
     tolua_property__get_set bool useSystemShapes;
 };
 

+ 1 - 1
Source/Engine/Scene/Serializable.cpp

@@ -319,7 +319,7 @@ bool Serializable::LoadXML(const XMLElement& source, bool setInstanceDefault)
         while (attempts)
         {
             const AttributeInfo& attr = attributes->At(i);
-            if (((attr.mode_ & AM_FILE) || (attr.mode_ & AM_READ)) && !attr.name_.Compare(name, true))
+            if ((attr.mode_ & AM_FILE) && !attr.name_.Compare(name, true))
             {
                 Variant varValue;
 

+ 2 - 4
Source/Engine/Script/APITemplates.h

@@ -788,10 +788,8 @@ template <class T> void RegisterSoundSource(asIScriptEngine* engine, const char*
     engine->RegisterObjectMethod(className, "void Play(Sound@+, float, float)", asMETHODPR(T, Play, (Sound*, float, float), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void Play(Sound@+, float, float, float)", asMETHODPR(T, Play, (Sound*, float, float, float), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void Stop()", asMETHOD(T, Stop), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void SetSoundType(const StringHash&in)", asMETHODPR(T, SetSoundType, (const StringHash&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void SetSoundType(SoundType)", asMETHODPR(T, SetSoundType, (SoundType), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_soundType(const StringHash&in)", asMETHODPR(T, SetSoundType, (const StringHash&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "StringHash get_soundType() const", asMETHOD(T, GetSoundType), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void set_soundType(const String&in)", asMETHOD(T, SetSoundType), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "String get_soundType() const", asMETHOD(T, GetSoundType), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_frequency(float)", asMETHOD(T, SetFrequency), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "float get_frequency() const", asMETHOD(T, GetFrequency), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_gain(float)", asMETHOD(T, SetGain), asCALL_THISCALL);

+ 4 - 29
Source/Engine/Script/AudioAPI.cpp

@@ -45,12 +45,7 @@ void RegisterSound(asIScriptEngine* engine)
 
 void RegisterSoundSources(asIScriptEngine* engine)
 {
-    engine->RegisterEnum("SoundType");
-    engine->RegisterEnumValue("SoundType", "SOUND_EFFECT", SOUND_EFFECT);
-    engine->RegisterEnumValue("SoundType", "SOUND_AMBIENT", SOUND_AMBIENT);
-    engine->RegisterEnumValue("SoundType", "SOUND_VOICE", SOUND_VOICE);
-    engine->RegisterEnumValue("SoundType", "SOUND_MUSIC", SOUND_MUSIC);
-    engine->RegisterEnumValue("SoundType", "SOUND_MASTER", SOUND_MASTER);
+    engine->RegisterGlobalProperty("const String SOUND_MASTER", (void*) &SOUND_MASTER);
     
     RegisterSoundSource<SoundSource>(engine, "SoundSource");
     RegisterSoundSource<SoundSource3D>(engine, "SoundSource3D");
@@ -80,34 +75,15 @@ static Audio* GetAudio()
     return GetScriptContext()->GetSubsystem<Audio>();
 }
 
-static float GetMasterGainFromEnum(SoundType type, Audio* audio)
-{
-    return audio->GetMasterGain(type);
-}
-
-static float GetMasterGainFromHash(const StringHash& type, Audio* audio)
-{
-    return audio->GetMasterGain(type);
-}
-
-static StringHash GetHashFromType(SoundType type)
-{
-    return soundTypeHashes[type];
-}
-
 void RegisterAudio(asIScriptEngine* engine)
 {
     RegisterObject<Audio>(engine, "Audio");
     engine->RegisterObjectMethod("Audio", "void SetMode(int, int, bool, bool interpolate = true)", asMETHOD(Audio, SetMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "bool Play()", asMETHOD(Audio, Play), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "void Stop()", asMETHOD(Audio, Stop), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Audio", "void SetMasterGain(SoundType, float)", asMETHODPR(Audio, SetMasterGain, (SoundType, float), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Audio", "float GetMasterGain(SoundType) const", asFUNCTION(GetMasterGainFromEnum), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("Audio", "void SetMasterGain(const StringHash&in, float)", asMETHODPR(Audio, SetMasterGain, (const StringHash&, float), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Audio", "float GetMasterGain(const StringHash&in) const", asFUNCTION(GetMasterGainFromHash), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("Audio", "void set_masterGain(const StringHash&in, float)", asMETHODPR(Audio, SetMasterGain, (const StringHash&, float), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Audio", "float get_masterGain(const StringHash&in) const", asFUNCTION(GetMasterGainFromHash), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("Audio", "bool IsMasterGain(const StringHash&in) const", asMETHOD(Audio, IsMasterGain), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Audio", "void set_masterGain(const String&in, float)", asMETHOD(Audio, SetMasterGain), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Audio", "float get_masterGain(const String&in) const", asMETHOD(Audio, GetMasterGain), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Audio", "bool IsMasterGain(const String&in) const", asMETHOD(Audio, IsMasterGain), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "void set_listener(SoundListener@+)", asMETHOD(Audio, SetListener), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "SoundListener@+ get_listener() const", asMETHOD(Audio, GetListener), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "uint get_sampleSize() const", asMETHOD(Audio, GetSampleSize), asCALL_THISCALL);
@@ -117,7 +93,6 @@ void RegisterAudio(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Audio", "bool get_playing() const", asMETHOD(Audio, IsPlaying), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "bool get_initialized() const", asMETHOD(Audio, IsInitialized), asCALL_THISCALL);
     engine->RegisterGlobalFunction("Audio@+ get_audio()", asFUNCTION(GetAudio), asCALL_CDECL);
-    engine->RegisterGlobalFunction("StringHash GetHashFromSoundType(SoundType type)", asFUNCTION(GetHashFromType), asCALL_CDECL);
 }
 
 void RegisterAudioAPI(asIScriptEngine* engine)

+ 0 - 1
Source/Engine/Script/SceneAPI.cpp

@@ -45,7 +45,6 @@ static void RegisterSerializable(asIScriptEngine* engine)
     engine->RegisterGlobalProperty("const uint AM_NODEID", (void*)&AM_NODEID);
     engine->RegisterGlobalProperty("const uint AM_COMPONENTID", (void*)&AM_COMPONENTID);
     engine->RegisterGlobalProperty("const uint AM_NODEIDVECTOR", (void*)&AM_NODEIDVECTOR);
-    engine->RegisterGlobalProperty("const uint AM_READ", (void*) &AM_READ);
 
     RegisterSerializable<Serializable>(engine, "Serializable");
 }

+ 3 - 3
Source/Engine/Script/UIAPI.cpp

@@ -169,10 +169,10 @@ static void RegisterCursor(asIScriptEngine* engine)
     RegisterBorderImage<Cursor>(engine, "Cursor");
     engine->RegisterObjectMethod("Cursor", "void DefineShape(const String&in, Texture@+, const IntRect&in, const IntVector2&in)", asMETHODPR(Cursor, DefineShape, (CursorShape, Image*, const IntRect&, const IntVector2&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Cursor", "void DefineShape(CursorShape, Texture@+, const IntRect&in, const IntVector2&in)", asMETHODPR(Cursor, DefineShape, (const String&, Image*, const IntRect&, const IntVector2&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Cursor", "void SetShape(const StringHash&in)", asMETHODPR(Cursor, SetShape, (const StringHash&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Cursor", "void SetShape(const String&in)", asMETHODPR(Cursor, SetShape, (const String&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Cursor", "void SetShape(CursorShape)", asMETHODPR(Cursor, SetShape, (CursorShape), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Cursor", "void set_shape(const StringHash&in)", asMETHODPR(Cursor, SetShape, (const StringHash&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Cursor", "StringHash get_shape() const", asMETHOD(Cursor, GetShape), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Cursor", "void set_shape(const String&in)", asMETHODPR(Cursor, SetShape, (const String&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Cursor", "String get_shape() const", asMETHOD(Cursor, GetShape), asCALL_THISCALL);
     engine->RegisterObjectMethod("Cursor", "void set_useSystemShapes(bool)", asMETHOD(Cursor, SetUseSystemShapes), asCALL_THISCALL);
     engine->RegisterObjectMethod("Cursor", "bool get_useSystemShapes() const", asMETHOD(Cursor, GetUseSystemShapes), asCALL_THISCALL);
 }

+ 12 - 28
Source/Engine/UI/Cursor.cpp

@@ -52,22 +52,6 @@ static const char* shapeNames[] =
     "BusyArrow"
 };
 
-static const StringHash shapeHashes[] =
-{
-    "Normal",
-    "IBeam",
-    "Cross",
-    "ResizeVertical",
-    "ResizeDiagonalTopRight",
-    "ResizeHorizontal",
-    "ResizeDiagonalTopLeft",
-    "ResizeAll",
-    "AcceptDrop",
-    "RejectDrop",
-    "Busy",
-    "BusyArrow"
-};
-
 /// OS cursor shape lookup table matching cursor shape enumeration
 static const int osCursorLookup[CS_MAX_SHAPES] =
 {
@@ -89,13 +73,13 @@ extern const char* UI_CATEGORY;
 
 Cursor::Cursor(Context* context) :
     BorderImage(context),
-    shape_(shapeHashes[CS_NORMAL]),
+    shape_(shapeNames[CS_NORMAL]),
     useSystemShapes_(false),
     osShapeDirty_(false)
 {
     // Define the defaults for system cursor usage.
     for (unsigned i = 0; i < CS_MAX_SHAPES; i++)
-        shapeInfos_[shapeHashes[i]] = CursorShapeInfo(shapeNames[i], i);
+        shapeInfos_[shapeNames[i]] = CursorShapeInfo(i);
 
     // Subscribe to OS mouse cursor visibility changes to be able to reapply the cursor shape
     SubscribeToEvent(E_MOUSEVISIBLECHANGED, HANDLER(Cursor, HandleMouseVisibleChanged));
@@ -103,7 +87,7 @@ Cursor::Cursor(Context* context) :
 
 Cursor::~Cursor()
 {
-    HashMap<StringHash, CursorShapeInfo>::Iterator iter = shapeInfos_.Begin();
+    HashMap<String, CursorShapeInfo>::Iterator iter = shapeInfos_.Begin();
     for (iter; iter != shapeInfos_.End(); iter++)
     {
         if (iter->second_.osCursor_)
@@ -157,7 +141,7 @@ void Cursor::DefineShape(const String& shape, Image* image, const IntRect& image
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     
     if (!shapeInfos_.Contains(shape))
-        shapeInfos_[shape] = CursorShapeInfo(shape);
+        shapeInfos_[shape] = CursorShapeInfo();
 
     CursorShapeInfo& info = shapeInfos_[shape];
 
@@ -184,15 +168,15 @@ void Cursor::DefineShape(const String& shape, Image* image, const IntRect& image
     // Reset current shape if it was edited
     if (shape_ == shape)
     {
-        shape_ = StringHash::ZERO;
+        shape_ = String::EMPTY;
         SetShape(shape);
     }
 }
 
 
-void Cursor::SetShape(const StringHash& shape)
+void Cursor::SetShape(const String& shape)
 {
-    if (shape_ == shape || !shapeInfos_.Contains(shape))
+    if (shape == String::EMPTY || shape.Empty() || shape_ == shape || !shapeInfos_.Contains(shape))
         return;
 
     shape_ = shape;
@@ -205,16 +189,16 @@ void Cursor::SetShape(const StringHash& shape)
     // To avoid flicker, the UI subsystem will apply the OS shape once per frame. Exception: if we are using the
     // busy shape, set it immediately as we may block before that
     osShapeDirty_ = true;
-    if (shape_ == shapeHashes[CS_BUSY])
+    if (shape_ == shapeNames[CS_BUSY])
         ApplyOSCursorShape();
 }
 
 void Cursor::SetShape(CursorShape shape)
 {
-    if (shape_ == shapeHashes[shape] || shape < CS_NORMAL || shape >= CS_MAX_SHAPES)
+    if (shape < CS_NORMAL || shape >= CS_MAX_SHAPES || shape_ == shapeNames[shape])
         return;
 
-    SetShape(shapeHashes[shape]);
+    SetShape(shapeNames[shape]);
 }
 
 void Cursor::SetUseSystemShapes(bool enable)
@@ -251,14 +235,14 @@ VariantVector Cursor::GetShapesAttr() const
 {
     VariantVector ret;
 
-    HashMap<StringHash, CursorShapeInfo>::ConstIterator iter = shapeInfos_.Begin();
+    HashMap<String, CursorShapeInfo>::ConstIterator iter = shapeInfos_.Begin();
     for (iter; iter != shapeInfos_.End(); iter++)
     {
         if (iter->second_.imageRect_ != IntRect::ZERO)
         {
             // Could use a map but this simplifies the UI xml.
             VariantVector shape;
-            shape.Push(iter->second_.name_);
+            shape.Push(iter->first_);
             shape.Push(GetResourceRef(iter->second_.texture_, Texture2D::GetTypeStatic()));
             shape.Push(iter->second_.imageRect_);
             shape.Push(iter->second_.hotSpot_);

+ 5 - 20
Source/Engine/UI/Cursor.h

@@ -54,7 +54,6 @@ struct URHO3D_API CursorShapeInfo
 {
     /// Construct with defaults.
     CursorShapeInfo() :
-    name_(String::EMPTY),
         imageRect_(IntRect::ZERO),
         hotSpot_(IntVector2::ZERO),
         osCursor_(0),
@@ -63,20 +62,8 @@ struct URHO3D_API CursorShapeInfo
     {
     }
 
-    /// Construct with name.
-    CursorShapeInfo(const String& name) :
-        name_(name),
-        imageRect_(IntRect::ZERO),
-        hotSpot_(IntVector2::ZERO),
-        osCursor_(0),
-        systemDefined_(false),
-        systemCursor_(-1)
-    {
-    }
-
-    /// Construct with name.
-    CursorShapeInfo(const String& name, int systemCursor) :
-        name_(name),
+    /// Construct with system cursor.
+    CursorShapeInfo(int systemCursor) :
         imageRect_(IntRect::ZERO),
         hotSpot_(IntVector2::ZERO),
         osCursor_(0),
@@ -85,8 +72,6 @@ struct URHO3D_API CursorShapeInfo
     {
     }
 
-    /// Name.
-    String name_;
     /// Image.
     SharedPtr<Image> image_;
     /// Texture.
@@ -124,7 +109,7 @@ public:
     /// Define a shape.
     void DefineShape(CursorShape shape, Image* image, const IntRect& imageRect, const IntVector2& hotSpot);
     /// Set current shape.
-    void SetShape(const StringHash& shape);
+    void SetShape(const String& shape);
     /// Set current shape.
     void SetShape(CursorShape shape);
     /// Set whether to use system default shapes. Is only possible when the OS mouse cursor has been set visible from the Input subsystem.
@@ -146,9 +131,9 @@ protected:
     void HandleMouseVisibleChanged(StringHash eventType, VariantMap& eventData);
 
     /// Current shape index.
-    StringHash shape_;
+    String shape_;
     /// Shape definitions.
-    HashMap<StringHash, CursorShapeInfo> shapeInfos_;
+    HashMap<String, CursorShapeInfo> shapeInfos_;
     /// Use system default shapes flag.
     bool useSystemShapes_;
     /// OS cursor shape needs update flag.