Browse Source

Re-enabled sound latency adjustment.
Removed support for 8-bit sound output.

Lasse Öörni 14 years ago
parent
commit
16aeec5c32
5 changed files with 44 additions and 63 deletions
  1. 1 1
      Docs/GettingStarted.dox
  2. 33 48
      Engine/Audio/Audio.cpp
  3. 1 7
      Engine/Audio/Audio.h
  4. 1 2
      Engine/Engine/AudioAPI.cpp
  5. 8 5
      Engine/Engine/Engine.cpp

+ 1 - 1
Docs/GettingStarted.dox

@@ -84,13 +84,13 @@ Urho3D.exe understands the following command line options:
 -headless   Headless mode. No application window will be created
 -forward    Use forward rendering (default)
 -deferred   Use deferred rendering
+-b<length>  Sound buffer length in milliseconds
 -r<freq>    Mixing frequency in Hz
 -nolimit    Disable frame limiter
 -noshadows  Disable rendering of shadows
 -nosound    Disable sound output
 -noip       Disable sound mixing interpolation
 -8bit       Force 8-bit audio output
--mono       Force mono audio output
 -sm2        Force SM2.0 rendering
 \endverbatim
 

+ 33 - 48
Engine/Audio/Audio.cpp

@@ -33,21 +33,24 @@
 #include "Sound.h"
 #include "SoundSource3D.h"
 
-#include "portaudio.h"
+#include <portaudio.h>
 
 #include "DebugNew.h"
 
+static const int MIN_BUFFERLENGTH = 20;
+static const int MIN_MIXRATE = 11025;
+static const int MAX_MIXRATE = 48000;
+
 static unsigned numInstances = 0;
 
-static int PortAudioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo*
-    timeInfo, PaStreamCallbackFlags statusFlags, void *userData);
+static int AudioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
+    const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData);
 
 OBJECTTYPESTATIC(Audio);
 
 Audio::Audio(Context* context) :
     Object(context),
     stream_(0),
-    clipBufferSize_(0),
     sampleSize_(0),
     playing_(false),
     listenerPosition_(Vector3::ZERO),
@@ -84,30 +87,42 @@ Audio::~Audio()
     }
 }
 
-bool Audio::SetMode(int mixRate, bool sixteenBit, bool stereo, bool interpolate)
+bool Audio::SetMode(int bufferLengthMSec, int mixRate, bool stereo, bool interpolate)
 {
     Release();
     
-    if (Pa_OpenDefaultStream(&stream_, 0, stereo ? 2 : 1, sixteenBit ? paInt16 : paUInt8, mixRate, paFramesPerBufferUnspecified,
-        PortAudioCallback, this) != paNoError)
+    bufferLengthMSec = Max(bufferLengthMSec, MIN_BUFFERLENGTH);
+    mixRate = Clamp(mixRate, MIN_MIXRATE, MAX_MIXRATE);
+    
+    // Guarantee a fragment size that is low enough so that Vorbis decoding buffers do not wrap
+    unsigned fragmentSize = NextPowerOfTwo(mixRate >> 6);
+    
+    PaStreamParameters outputParams;
+    outputParams.device = Pa_GetDefaultOutputDevice();
+    outputParams.channelCount = stereo ? 2 : 1;
+    outputParams.sampleFormat = paInt16;
+    outputParams.suggestedLatency = bufferLengthMSec / 1000.0;
+    outputParams.hostApiSpecificStreamInfo = 0;
+    
+    if (Pa_OpenStream(&stream_, 0, &outputParams, mixRate, fragmentSize, 0, AudioCallback, this) != paNoError)
     {
         LOGERROR("Failed to open audio stream");
         return false;
     }
     
-    sampleSize_ = 1;
+    sampleSize_ = sizeof(short);
     if (stereo)
         sampleSize_ <<= 1;
-    if (sixteenBit)
-        sampleSize_ <<= 1;
+    
+    // Allocate the clipping buffer
+    clipBuffer_ = new int[stereo ? fragmentSize << 1 : fragmentSize];
     
     mixRate_ = mixRate;
     stereo_ = stereo;
-    sixteenBit_ = sixteenBit;
     interpolate_ = interpolate;
     
     LOGINFO("Set audio mode " + String(mixRate_) + " Hz " + (stereo_ ? "stereo" : "mono") + " " +
-        (sixteenBit_ ? "16-bit" : "8-bit") + " " + (interpolate_ ? "interpolated" : ""));
+        (interpolate_ ? "interpolated" : ""));
     
     return Play();
 }
@@ -215,7 +230,7 @@ void Audio::RemoveSoundSource(SoundSource* channel)
     }
 }
 
-int PortAudioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo*
+int AudioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo*
     timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
 {
     Audio* audio = static_cast<Audio*>(userData);
@@ -234,48 +249,19 @@ void Audio::MixOutput(void *dest, unsigned mixSamples)
     if (stereo_)
         clipSamples <<= 1;
     
-    // Make sure the clipbuffer is large enough, then clear it
-    if (clipBufferSize_ < clipSamples)
-    {
-        clipBuffer_ = new int[clipSamples];
-        clipBufferSize_ = clipSamples;
-    }
-    
     // Clear clip buffer
     memset(clipBuffer_.GetPtr(), 0, clipSamples * sizeof(int));
     int* clipPtr = clipBuffer_.GetPtr();
     
     // Mix samples to clip buffer
-    // If the total work request is too large, Ogg Vorbis decode buffers may end up wrapping. Divide to smaller chunks if necessary
-    unsigned maxSamples = mixRate_ * DECODE_BUFFER_LENGTH / 1000 / 4;
-    while (mixSamples)
-    {
-        unsigned currentSamples = Min((int)maxSamples, (int)mixSamples);
-        for (PODVector<SoundSource*>::Iterator i = soundSources_.Begin(); i != soundSources_.End(); ++i)
-            (*i)->Mix(clipPtr, currentSamples, mixRate_, stereo_, interpolate_);
-        
-        mixSamples -= currentSamples;
-        if (stereo_)
-            clipPtr += currentSamples * 2;
-        else
-            clipPtr += currentSamples;
-        
-    }
+    for (PODVector<SoundSource*>::Iterator i = soundSources_.Begin(); i != soundSources_.End(); ++i)
+        (*i)->Mix(clipPtr, mixSamples, mixRate_, stereo_, interpolate_);
     
     // Copy output from clip buffer to destination
     clipPtr = clipBuffer_.GetPtr();
-    if (sixteenBit_)
-    {
-        short* destPtr = (short*)dest;
-        while (clipSamples--)
-            *destPtr++ = Clamp(*clipPtr++, -32768, 32767);
-    }
-    else
-    {
-        unsigned char* destPtr = (unsigned char*)dest;
-        while (clipSamples--)
-            *destPtr++ = Clamp(((*clipPtr++) >> 8) + 128, 0, 255);
-    }
+    short* destPtr = (short*)dest;
+    while (clipSamples--)
+        *destPtr++ = Clamp(*clipPtr++, -32768, 32767);
 }
 
 void Audio::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
@@ -295,7 +281,6 @@ void Audio::Release()
         
         stream_ = 0;
         clipBuffer_.Reset();
-        clipBufferSize_ = 0;
     }
 }
 

+ 1 - 7
Engine/Audio/Audio.h

@@ -46,7 +46,7 @@ public:
     virtual ~Audio();
     
     /// Initialize sound output with specified buffer length and output mode
-    bool SetMode(int mixRate, bool sixteenBit, bool stereo, bool interpolate = true);
+    bool SetMode(int bufferLengthMSec, int mixRate, bool stereo, bool interpolate = true);
     /// Run update on sound sources. Not required for continued playback, but frees unused sound sources & sounds and updates 3D positions.
     void Update(float timeStep);
     /// Restart sound output
@@ -68,8 +68,6 @@ public:
     unsigned GetSampleSize() const { return sampleSize_; }
     /// Return mixing rate
     int GetMixRate() const { return mixRate_; }
-    /// Return whether output is sixteen bit
-    bool IsSixteenBit() const { return sixteenBit_; }
     /// Return whether output is stereo
     bool IsStereo() const { return stereo_; }
     /// Return whether output is interpolated
@@ -111,14 +109,10 @@ private:
     SharedArrayPtr<int> clipBuffer_;
     /// Audio thread mutex
     Mutex audioMutex_;
-    /// Clipping buffer size in samples
-    unsigned clipBufferSize_;
     /// Sample size
     unsigned sampleSize_;
     /// Mixing rate
     int mixRate_;
-    /// Sixteen bit flag
-    bool sixteenBit_;
     /// Stereo flag
     bool stereo_;
     /// Interpolation flag

+ 1 - 2
Engine/Engine/AudioAPI.cpp

@@ -69,7 +69,7 @@ static Audio* GetAudio()
 void RegisterAudio(asIScriptEngine* engine)
 {
     RegisterObject<Audio>(engine, "Audio");
-    engine->RegisterObjectMethod("Audio", "void SetMode(int, bool, bool, bool)", asMETHOD(Audio, SetMode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Audio", "void SetMode(int, int, bool, bool)", 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 SetListenerTransform(const Vector3&in, const Quaternion&in)", asMETHOD(Audio, SetListenerTransform), asCALL_THISCALL);
@@ -81,7 +81,6 @@ void RegisterAudio(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Audio", "const Quaternion& get_listenerRotation() const", asMETHOD(Audio, GetListenerRotation), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "uint get_sampleSize() const", asMETHOD(Audio, GetSampleSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "int get_mixRate() const", asMETHOD(Audio, GetMixRate), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Audio", "bool get_sixteenBit() const", asMETHOD(Audio, IsSixteenBit), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "bool get_stereo() const", asMETHOD(Audio, IsStereo), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "bool get_interpolated() const", asMETHOD(Audio, IsInterpolated), asCALL_THISCALL);
     engine->RegisterObjectMethod("Audio", "bool get_playing() const", asMETHOD(Audio, IsPlaying), asCALL_THISCALL);

+ 8 - 5
Engine/Engine/Engine.cpp

@@ -75,14 +75,14 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
     int width = 0;
     int height = 0;
     int multiSample = 1;
+    int buffer = 100;
+    int mixRate = 44100;
     bool fullscreen = true;
     bool vsync = false;
     bool forceSM2 = false;
     bool shadows = true;
-    int mixRate = 44100;
     bool sound = true;
     bool stereo = true;
-    bool sixteenBit = true;
     bool interpolate = true;
     
     for (unsigned i = 0; i < arguments.Size(); ++i)
@@ -103,8 +103,6 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
                 interpolate = false;
             else if (argument == "mono")
                 stereo = false;
-            else if (argument == "8bit")
-                sixteenBit = false;
             else if (argument == "noshadows")
                 shadows = false;
             else if (argument == "sm2")
@@ -128,6 +126,11 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
                         multiSample = ToInt(argument.Substring(1));
                     break;
                     
+                case 'b':
+                    if (arguments[i].Length() > 1)
+                        buffer = ToInt(argument.Substring(1));
+                    break;
+                    
                 case 'r':
                     if (arguments[i].Length() > 1)
                         mixRate = ToInt(argument.Substring(1));
@@ -189,7 +192,7 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
         if (!shadows)
             GetSubsystem<Renderer>()->SetDrawShadows(false);
         if (sound)
-            GetSubsystem<Audio>()->SetMode(mixRate, sixteenBit, stereo, interpolate);
+            GetSubsystem<Audio>()->SetMode(buffer, mixRate, stereo, interpolate);
     }
     
     frameTimer_.Reset();