Ticket #1223: good_sounds.patch

File good_sounds.patch, 194.5 KB (added by stwf, 11 months ago)

most recent code version

  • build/premake/premake4.lua

     
    519519        "ps/Network", 
    520520        "ps/GameSetup", 
    521521        "ps/XML", 
    522         "sound", 
     522        "soundmanager", 
     523        "soundmanager/data", 
     524        "soundmanager/items", 
     525        "soundmanager/js", 
    523526        "scripting", 
    524527        "maths", 
    525528        "maths/scripting", 
     
    533536        "boost", 
    534537        "enet", 
    535538        "libcurl", 
     539        "vorbis", 
     540        "openal" 
    536541    } 
    537542    setup_static_lib_project("engine", source_dirs, extern_libs, {}) 
    538543 
     
    553558    end 
    554559    setup_static_lib_project("graphics", source_dirs, extern_libs, {}) 
    555560 
    556  
    557561    source_dirs = { 
    558562        "tools/atlas/GameInterface", 
    559563        "tools/atlas/GameInterface/Handlers" 
  • source/ps/GameSetup/Config.cpp

     
    2121#include "ps/CConsole.h" 
    2222#include "ps/GameSetup/CmdLineArgs.h" 
    2323#include "lib/timer.h" 
    24 #include "lib/res/sound/snd_mgr.h" 
    2524#include "Config.h" 
     25#include "soundmanager/CSoundManager.h" 
    2626 
    27  
    2827// (these variables are documented in the header.) 
    2928 
    3029CStrW g_CursorName = L"test"; 
     
    8180    CFG_GET_USER_VAL("particles", Bool, g_Particles); 
    8281 
    8382    float gain = -1.0f; 
     83    float musicGain = -1.0f; 
     84    float ambientGain = -1.0f; 
     85    float actionGain = -1.0f; 
     86    int bufferCount = 50; 
     87    unsigned long   bufferSize = 65536; 
     88     
    8489    CFG_GET_USER_VAL("sound.mastergain", Float, gain); 
    85     if(gain >= 0.0f) 
    86         WARN_IF_ERR(snd_set_master_gain(gain)); 
     90    CFG_GET_USER_VAL("sound.musicgain", Float, musicGain); 
     91    CFG_GET_USER_VAL("sound.ambientgain", Float, ambientGain); 
     92    CFG_GET_USER_VAL("sound.actiongain", Float, actionGain); 
     93 
     94    CFG_GET_USER_VAL("sound.bufferCount", Int, bufferCount); 
     95    CFG_GET_USER_VAL("sound.bufferSize", UnsignedLong, bufferSize); 
     96 
     97    g_SoundManager->SetMasterGain( gain ); 
     98    g_SoundManager->SetMusicGain( musicGain ); 
     99    g_SoundManager->SetAmbientGain( ambientGain ); 
     100    g_SoundManager->SetActionGain( actionGain ); 
     101 
     102    g_SoundManager->SetMemoryUsage( bufferSize, bufferCount); 
    87103} 
    88104 
    89105 
  • source/ps/GameSetup/GameSetup.cpp

     
    2525#include "lib/file/common/file_stats.h" 
    2626#include "lib/res/h_mgr.h" 
    2727#include "lib/res/graphics/cursor.h" 
    28 #include "lib/res/sound/snd_mgr.h" 
     28 
    2929#include "lib/sysdep/cursor.h" 
    3030#include "lib/sysdep/cpu.h" 
    3131#include "lib/sysdep/gfx.h" 
     
    8686#include "gui/scripting/JSInterface_GUITypes.h" 
    8787#include "gui/scripting/ScriptFunctions.h" 
    8888 
    89 #include "sound/JSI_Sound.h" 
    90  
    9189#include "network/NetServer.h" 
    9290#include "network/NetClient.h" 
    9391 
     
    102100#include "tools/atlas/GameInterface/GameLoop.h" 
    103101#include "tools/atlas/GameInterface/View.h" 
    104102 
     103#include "soundmanager/CSoundManager.h" 
    105104 
    106105#if !(OS_WIN || OS_MACOSX || OS_ANDROID) // assume all other platforms use X11 for wxWidgets 
    107106#define MUST_INIT_X11 1 
     
    203202{ 
    204203    PROFILE3("render"); 
    205204 
     205    g_SoundManager->IdleTask(); 
     206 
    206207    ogl_WarnIfError(); 
    207208 
    208209    g_Profiler2.RecordGPUFrameStart(); 
     
    330331{ 
    331332    // maths 
    332333    JSI_Vector3D::init(); 
    333  
     334     
     335    CSoundManager::ScriptingInit(); 
    334336    // graphics 
    335337    CGameView::ScriptingInit(); 
    336338 
     
    338340    CRenderer::ScriptingInit(); 
    339341 
    340342    // sound 
    341     JSI_Sound::ScriptingInit(); 
     343//  JSI_Sound::ScriptingInit(); 
    342344 
    343345    // ps 
    344346    JSI_Console::init(); 
     
    476478        g_VFS->Mount(L"", modLoosePath / modName/"", flags, priority); 
    477479        g_VFS->Mount(L"", modArchivePath / modName/"", flags, priority); 
    478480    } 
     481     
     482    g_SoundManager = new CSoundManager(); 
    479483 
    480484    // note: don't bother with g_VFS->TextRepresentation - directories 
    481485    // haven't yet been populated and are empty. 
     
    691695    // resource 
    692696    // first shut down all resource owners, and then the handle manager. 
    693697    TIMER_BEGIN(L"resource modules"); 
    694         snd_shutdown(); 
     698        delete g_SoundManager; 
    695699 
    696700        g_VFS.reset(); 
    697701 
     
    930934        // speed up startup by disabling all sound 
    931935        // (OpenAL init will be skipped). 
    932936        // must be called before first snd_open. 
    933         snd_disable(true); 
     937        g_SoundManager->SetEnabled( false ); 
    934938    } 
    935939 
    936940    g_GUI = new CGUIManager(g_ScriptingHost.GetScriptInterface()); 
  • source/ps/Game.cpp

     
    4545#include "simulation2/components/ICmpPlayerManager.h" 
    4646 
    4747#include "gui/GUIManager.h" 
     48#include "soundmanager/CSoundManager.h" 
    4849 
    4950extern bool g_GameRestarted; 
    5051 
     
    299300    if (doInterpolate) 
    300301    { 
    301302        m_TurnManager->Interpolate(deltaTime); 
     303        g_SoundManager->idleTask(); 
    302304    } 
    303305     
    304306    // TODO: maybe we should add a CCmpParticleInterface that passes the interpolation commands 
  • source/soundmanager/items/CSoundItem.h

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18 
     19#ifndef SoundTester_CSoundItem_h 
     20#define SoundTester_CSoundItem_h 
     21 
     22#include "CSoundBase.h" 
     23#include "soundmanager/data/CSoundData.h" 
     24 
     25 
     26class CSoundItem :public CSoundBase 
     27{ 
     28protected: 
     29     
     30public: 
     31    CSoundItem      (); 
     32    CSoundItem      (CSoundData* sndData); 
     33     
     34    virtual ~CSoundItem     (); 
     35    void    Attach          ( CSoundData* itemData ); 
     36    bool    IdleTask        (); 
     37 
     38protected: 
     39 
     40     
     41}; 
     42 
     43 
     44 
     45 
     46 
     47 
     48#endif 
  • source/soundmanager/items/CBufferItem.h

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18 
     19#ifndef SoundTester_CBufferItem_h 
     20#define SoundTester_CBufferItem_h 
     21 
     22#include "CSoundBase.h" 
     23 
     24class CBufferItem : public CSoundBase 
     25{ 
     26public: 
     27    CBufferItem             (CSoundData* sndData); 
     28    virtual ~CBufferItem    (); 
     29     
     30    virtual void    SetLooping   ( bool loops ); 
     31    virtual bool    IdleTask     (); 
     32     
     33protected:     
     34    virtual void    Attach       ( CSoundData* itemData ); 
     35 
     36     
     37}; 
     38 
     39 
     40#endif 
  • source/soundmanager/items/CStreamItem.h

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18#ifndef SoundTester_CStreamItem_h 
     19#define SoundTester_CStreamItem_h 
     20 
     21#include "soundmanager/data/CSoundData.h" 
     22#include "CSoundBase.h" 
     23 
     24class CStreamItem : public CSoundBase 
     25{ 
     26public: 
     27    CStreamItem                 (CSoundData* sndData); 
     28    virtual ~CStreamItem        (); 
     29     
     30    virtual void    SetLooping  ( bool loops ); 
     31    virtual bool    IdleTask    (); 
     32     
     33protected:     
     34    virtual void    Attach      ( CSoundData* itemData ); 
     35 
     36}; 
     37 
     38#endif 
  • source/soundmanager/items/ISoundItem.h

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18#ifndef SoundTester_ISoundItem_h 
     19#define SoundTester_ISoundItem_h 
     20 
     21#include <string> 
     22#include "lib/external_libraries/openal.h" 
     23#include "maths/Vector3D.h" 
     24 
     25 
     26class ISoundItem 
     27{ 
     28     
     29public: 
     30    virtual ~ISoundItem(){}; 
     31    virtual bool GetLooping     () = 0; 
     32    virtual void    SetLooping       (bool loop) = 0; 
     33    virtual bool    IsPlaying        () = 0; 
     34     
     35     
     36    virtual std::string    GetName         () = 0; 
     37    virtual bool    IdleTask         () = 0; 
     38     
     39    virtual void    Play             () = 0; 
     40    virtual void    Stop             () = 0; 
     41 
     42    virtual void    EnsurePlay       () = 0; 
     43    virtual void    PlayAsMusic      () = 0; 
     44    virtual void    PlayAsAmbient    () = 0; 
     45 
     46    virtual void    PlayAndDelete    () = 0; 
     47    virtual void    StopAndDelete    () = 0; 
     48    virtual void    FadeToIn        ( ALfloat newVolume, double fadeDuration) = 0; 
     49    virtual void    FadeAndDelete    ( double fadeTime ) = 0; 
     50    virtual void    PlayLoop         () = 0; 
     51 
     52    virtual void    SetCone     (ALfloat innerCone, ALfloat outerCone, ALfloat coneGain) = 0; 
     53    virtual void    SetPitch    (ALfloat pitch) = 0; 
     54    virtual void    SetGain     (ALfloat gain) = 0; 
     55    virtual void    SetLocation (const CVector3D& position) = 0; 
     56    virtual void    SetRollOff     (ALfloat gain) = 0; 
     57}; 
     58 
     59 
     60#endif //SoundTester_ISoundItem_h 
     61 No newline at end of file 
  • source/soundmanager/items/CSoundBase.cpp

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17#include "precompiled.h" 
     18 
     19#include "CSoundBase.h" 
     20#include "soundmanager/CSoundManager.h" 
     21#include "soundmanager/data/CSoundData.h" 
     22 
     23#include <iostream> 
     24 
     25#include "lib/timer.h" 
     26 
     27 
     28CSoundBase::CSoundBase() 
     29{ 
     30    ResetVars(); 
     31} 
     32 
     33CSoundBase::~CSoundBase() 
     34{ 
     35    Stop(); 
     36    if ( m_ALSource != 0 ) 
     37    { 
     38        alDeleteSources( 1, &m_ALSource); 
     39        m_ALSource = 0; 
     40    } 
     41    if ( m_SoundData != 0 ) 
     42    { 
     43        CSoundData::ReleaseSoundData( m_SoundData ); 
     44        m_SoundData = 0; 
     45    } 
     46    if ( m_Name ) 
     47        delete m_Name; 
     48} 
     49 
     50void CSoundBase::ResetVars() 
     51{ 
     52    m_ALSource = 0; 
     53    m_SoundData = 0; 
     54    m_LastPlay = false; 
     55    m_Looping = false; 
     56    m_StartFadeTime = 0; 
     57    m_EndFadeTime = 0; 
     58    m_StartVolume = 0; 
     59    m_EndVolume = 0; 
     60 
     61    ResetFade(); 
     62    m_Name = new std::string( "sound name" ); 
     63} 
     64 
     65void CSoundBase::ResetFade() 
     66{ 
     67    m_StartFadeTime = 0; 
     68    m_EndFadeTime = 0; 
     69    m_StartVolume = 0; 
     70    m_EndVolume = 0; 
     71    m_ShouldBePlaying = false; 
     72} 
     73 
     74void CSoundBase::SetGain(ALfloat gain) 
     75{ 
     76    alSourcef(m_ALSource, AL_GAIN, gain); 
     77} 
     78 
     79void CSoundBase::SetRollOff(ALfloat rolls) 
     80{ 
     81   alSourcef(m_ALSource, AL_ROLLOFF_FACTOR, rolls); 
     82} 
     83 
     84void CSoundBase::EnsurePlay() 
     85{ 
     86    if ( m_ShouldBePlaying && !IsPlaying() ) 
     87        Play(); 
     88} 
     89 
     90void CSoundBase::SetCone(ALfloat innerCone, ALfloat outerCone, ALfloat coneGain) 
     91{ 
     92    alSourcef( m_ALSource, innerCone, AL_CONE_INNER_ANGLE); 
     93    alSourcef( m_ALSource, outerCone, AL_CONE_OUTER_ANGLE); 
     94    alSourcef( m_ALSource, coneGain, AL_CONE_OUTER_GAIN); 
     95} 
     96 
     97void CSoundBase::SetPitch(ALfloat pitch) 
     98{ 
     99    alSourcef( m_ALSource, AL_PITCH, pitch); 
     100} 
     101 
     102void CSoundBase::SetDirection(const CVector3D& direction) 
     103{ 
     104    alSourcefv( m_ALSource, AL_DIRECTION, direction.GetFloatArray() ); 
     105} 
     106 
     107bool CSoundBase::InitOpenAL() 
     108{ 
     109    alGetError(); /* clear error */ 
     110    alGenSources( 1, &m_ALSource); 
     111    long anErr = alGetError(); 
     112    if ( anErr != AL_NO_ERROR)  
     113    { 
     114        printf("- Error creating sources %ld !!\n", anErr ); 
     115    } 
     116    else 
     117    { 
     118        ALfloat source0Pos[]={ -2.0, 0.0, 0.0}; 
     119        ALfloat source0Vel[]={ 0.0, 0.0, 0.0}; 
     120         
     121        alSourcef( m_ALSource,AL_PITCH,1.0f); 
     122        alSourcef( m_ALSource,AL_GAIN,1.0f); 
     123        alSourcefv( m_ALSource,AL_POSITION,source0Pos); 
     124        alSourcefv( m_ALSource,AL_VELOCITY,source0Vel); 
     125        alSourcei( m_ALSource,AL_LOOPING,AL_FALSE); 
     126        return true; 
     127    } 
     128    return false; 
     129} 
     130 
     131bool CSoundBase::IsPlaying() 
     132{ 
     133    int proc_state; 
     134    alGetSourceiv( m_ALSource, AL_SOURCE_STATE, &proc_state); 
     135 
     136    return ( proc_state == AL_PLAYING ); 
     137} 
     138 
     139void CSoundBase::SetLastPlay( bool last ) 
     140{ 
     141    m_LastPlay = last; 
     142} 
     143 
     144bool CSoundBase::IdleTask() 
     145{ 
     146    return true; 
     147} 
     148 
     149void CSoundBase::SetLocation (const CVector3D& position) 
     150{ 
     151    alSourcefv( m_ALSource,AL_POSITION, position.GetFloatArray() ); 
     152} 
     153 
     154bool CSoundBase::HandleFade() 
     155{ 
     156    if ( m_StartFadeTime != 0 ) 
     157    { 
     158        double currTime = timer_Time(); 
     159        double pctDone = std::min( 1.0, (currTime - m_StartFadeTime) / (m_EndFadeTime - m_StartFadeTime) ); 
     160        pctDone = std::max( 0.0, pctDone ); 
     161        ALfloat curGain = ((m_EndVolume - m_StartVolume ) * pctDone) + m_StartVolume; 
     162 
     163        if  (curGain == 0 ) 
     164            Stop(); 
     165        else if ( curGain == m_EndVolume ) 
     166        { 
     167            alSourcef( m_ALSource, AL_GAIN, curGain);      
     168            ResetFade(); 
     169        } 
     170        else 
     171            alSourcef( m_ALSource, AL_GAIN, curGain);      
     172    } 
     173    return true; 
     174} 
     175 
     176bool CSoundBase::GetLooping() 
     177{ 
     178    return m_Looping; 
     179} 
     180 
     181void CSoundBase::SetLooping( bool loops ) 
     182{ 
     183    m_Looping = loops; 
     184    alSourcei( m_ALSource, AL_LOOPING, loops ? AL_TRUE : AL_FALSE ); 
     185} 
     186 
     187void CSoundBase::Play() 
     188{ 
     189    m_ShouldBePlaying = true; 
     190    if ( m_ALSource != 0 ) 
     191        alSourcePlay( m_ALSource ); 
     192} 
     193 
     194void CSoundBase::PlayAndDelete() 
     195{ 
     196    SetLastPlay( true ); 
     197    Play(); 
     198} 
     199 
     200void CSoundBase::FadeAndDelete( double fadeTime ) 
     201{ 
     202    SetLastPlay( true ); 
     203    FadeToIn( 0, fadeTime ); 
     204} 
     205 
     206void    CSoundBase::StopAndDelete() 
     207{ 
     208    SetLastPlay( true ); 
     209    Stop(); 
     210} 
     211 
     212void CSoundBase::PlayLoop() 
     213{ 
     214    if ( m_ALSource != 0 ) 
     215    { 
     216        SetLooping( true ); 
     217        Play(); 
     218    } 
     219} 
     220 
     221void CSoundBase::FadeToIn( ALfloat newVolume, double fadeDuration) 
     222{ 
     223    int proc_state; 
     224    alGetSourceiv( m_ALSource, AL_SOURCE_STATE, &proc_state); 
     225    if ( proc_state == AL_PLAYING ) 
     226    { 
     227        m_StartFadeTime = timer_Time(); 
     228        m_EndFadeTime = m_StartFadeTime + fadeDuration; 
     229        alGetSourcef( m_ALSource, AL_GAIN, &m_StartVolume); 
     230        m_EndVolume = newVolume; 
     231    } 
     232} 
     233 
     234void CSoundBase::PlayAsMusic() 
     235{ 
     236    g_SoundManager->SetMusicItem( this ); 
     237} 
     238 
     239void CSoundBase::PlayAsAmbient() 
     240{ 
     241    g_SoundManager->SetAmbientItem( this ); 
     242} 
     243 
     244void CSoundBase::Stop() 
     245{ 
     246    m_ShouldBePlaying = false; 
     247    if ( m_ALSource != 0 ) 
     248    { 
     249        int proc_state; 
     250        alSourcei( m_ALSource, AL_LOOPING, AL_FALSE ); 
     251        alGetSourceiv( m_ALSource, AL_SOURCE_STATE, &proc_state); 
     252        if ( proc_state == AL_PLAYING ) 
     253            alSourceStop( m_ALSource ); 
     254    } 
     255} 
     256 
     257const char* CSoundBase::Name() 
     258{ 
     259    return m_Name->c_str(); 
     260} 
     261 
     262std::string CSoundBase::GetName() 
     263{ 
     264    return std::string( m_Name->c_str() ); 
     265} 
     266 
     267void CSoundBase::SetNameFromPath(  char* fileLoc ) 
     268{ 
     269    std::string anst( fileLoc ); 
     270    size_t pos = anst.find_last_of("/"); 
     271    if(pos != std::wstring::npos) 
     272        m_Name->assign(anst.begin() + pos + 1, anst.end()); 
     273    else 
     274        m_Name->assign(anst.begin(), anst.end()); 
     275} 
     276 
  • source/soundmanager/items/CSoundItem.cpp

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17#include "precompiled.h" 
     18 
     19#include "CSoundItem.h" 
     20#include "soundmanager/data/CSoundData.h" 
     21 
     22#include <iostream> 
     23 
     24 
     25CSoundItem::CSoundItem() 
     26{ 
     27    ResetVars(); 
     28} 
     29 
     30CSoundItem::CSoundItem(CSoundData* sndData) 
     31{ 
     32    ResetVars(); 
     33    if ( InitOpenAL() ) 
     34        Attach( sndData ); 
     35} 
     36 
     37CSoundItem::~CSoundItem() 
     38{ 
     39    ALuint al_buf; 
     40     
     41    Stop(); 
     42    alSourceUnqueueBuffers(m_ALSource, 1, &al_buf); 
     43} 
     44 
     45bool CSoundItem::IdleTask() 
     46{ 
     47    HandleFade(); 
     48 
     49    if ( m_LastPlay ) 
     50    { 
     51        int proc_state; 
     52        alGetSourceiv( m_ALSource, AL_SOURCE_STATE, &proc_state); 
     53        return ( proc_state != AL_STOPPED ); 
     54    } 
     55    return true; 
     56} 
     57 
     58void CSoundItem::Attach( CSoundData* itemData ) 
     59{ 
     60    if ( itemData != NULL ) 
     61    { 
     62        m_SoundData = itemData->IncrementCount(); 
     63        alSourcei( m_ALSource, AL_BUFFER, m_SoundData->GetBuffer() ); 
     64    } 
     65} 
  • source/soundmanager/items/CBufferItem.cpp

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17#include "precompiled.h" 
     18#include "CBufferItem.h" 
     19#include "soundmanager/data/CSoundData.h" 
     20 
     21#include <iostream> 
     22 
     23CBufferItem::CBufferItem(CSoundData* sndData) 
     24{ 
     25    ResetVars(); 
     26    if ( InitOpenAL() ) 
     27        Attach( sndData ); 
     28} 
     29 
     30 
     31CBufferItem::~CBufferItem() 
     32{ 
     33    Stop(); 
     34    int num_processed; 
     35    alGetSourcei( m_ALSource, AL_BUFFERS_PROCESSED, &num_processed); 
     36     
     37    if (num_processed > 0) 
     38    { 
     39        ALuint* al_buf = new ALuint[num_processed]; 
     40        alSourceUnqueueBuffers(m_ALSource, num_processed, al_buf); 
     41 
     42        delete[] al_buf; 
     43    } 
     44} 
     45 
     46 
     47bool CBufferItem::IdleTask() 
     48{ 
     49    HandleFade(); 
     50     
     51    if ( m_LastPlay ) 
     52    { 
     53        int proc_state; 
     54        alGetSourceiv( m_ALSource, AL_SOURCE_STATE, &proc_state); 
     55        return ( proc_state != AL_STOPPED ); 
     56    } 
     57     
     58    if ( GetLooping() ) 
     59    { 
     60        int num_processed; 
     61        alGetSourcei( m_ALSource, AL_BUFFERS_PROCESSED, &num_processed); 
     62         
     63        for ( int i = 0; i < num_processed; i++ ) 
     64        { 
     65            ALuint al_buf; 
     66            alSourceUnqueueBuffers(m_ALSource, 1, &al_buf); 
     67            alSourceQueueBuffers(m_ALSource, 1, &al_buf); 
     68        } 
     69    } 
     70 
     71    return true; 
     72} 
     73 
     74void CBufferItem::Attach( CSoundData* itemData ) 
     75{ 
     76    if ( itemData != NULL ) 
     77    { 
     78        m_SoundData = itemData->IncrementCount(); 
     79        alSourceQueueBuffers(m_ALSource, m_SoundData->GetBufferCount(),(const ALuint *) m_SoundData->GetBufferPtr()); 
     80    } 
     81} 
     82 
     83void CBufferItem::SetLooping( bool loops ) 
     84{ 
     85    m_Looping = loops; 
     86} 
     87 
  • source/soundmanager/items/CSoundBase.h

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18 
     19#ifndef SoundTester_CSoundBase_h 
     20#define SoundTester_CSoundBase_h 
     21 
     22#include <string> 
     23#include "lib/external_libraries/openal.h" 
     24#include "soundmanager/items/ISoundItem.h" 
     25#include "soundmanager/data/CSoundData.h" 
     26 
     27 
     28class CSoundBase :public ISoundItem 
     29{ 
     30protected: 
     31     
     32    ALuint              m_ALSource; 
     33    CSoundData*         m_SoundData; 
     34 
     35    std::string*        m_Name; 
     36    bool                m_LastPlay; 
     37    bool                m_Looping; 
     38    bool                m_ShouldBePlaying; 
     39     
     40    double          m_StartFadeTime; 
     41    double          m_EndFadeTime; 
     42    ALfloat         m_StartVolume; 
     43    ALfloat         m_EndVolume; 
     44 
     45public: 
     46    CSoundBase      (); 
     47     
     48    virtual ~CSoundBase      (); 
     49     
     50    virtual bool    InitOpenAL(); 
     51    virtual void    ResetVars(); 
     52    virtual void    EnsurePlay(); 
     53 
     54    virtual void SetGain     (ALfloat gain); 
     55    virtual void SetRollOff     (ALfloat gain); 
     56    virtual void SetPitch(ALfloat pitch); 
     57    virtual void SetDirection(const CVector3D& direction); 
     58    virtual void SetCone(ALfloat innerCone, ALfloat outerCone, ALfloat coneGain); 
     59    virtual void SetLastPlay( bool last ); 
     60 
     61    void    Play            (); 
     62    void    PlayAndDelete   (); 
     63    bool    IdleTask        (); 
     64    void    PlayLoop        (); 
     65    void    Stop            (); 
     66    void    StopAndDelete    (); 
     67    void    FadeToIn        ( ALfloat newVolume, double fadeDuration); 
     68 
     69    void    PlayAsMusic      (); 
     70    void    PlayAsAmbient    (); 
     71 
     72    const char*   Name(); 
     73    std::string GetName(); 
     74 
     75    virtual bool GetLooping     (); 
     76    virtual void SetLooping     ( bool loops ); 
     77    virtual bool IsPlaying(); 
     78    virtual void SetLocation (const CVector3D& position); 
     79    virtual void FadeAndDelete    ( double fadeTime ); 
     80 
     81protected: 
     82 
     83    void SetNameFromPath(  char* fileLoc ); 
     84    void ResetFade(); 
     85    bool HandleFade(); 
     86 
     87     
     88}; 
     89 
     90 
     91 
     92 
     93 
     94 
     95#endif 
  • source/soundmanager/items/CStreamItem.cpp

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17#include "precompiled.h" 
     18 
     19#include "CStreamItem.h" 
     20#include "soundmanager/data/COggData.h" 
     21 
     22#include <iostream> 
     23 
     24CStreamItem::CStreamItem(CSoundData* sndData) 
     25{ 
     26    ResetVars(); 
     27    if ( InitOpenAL() ) 
     28        Attach( sndData ); 
     29} 
     30 
     31CStreamItem::~CStreamItem() 
     32{ 
     33    Stop(); 
     34 
     35    int num_processed; 
     36    alGetSourcei( m_ALSource, AL_BUFFERS_PROCESSED, &num_processed); 
     37     
     38    if (num_processed > 0) 
     39    { 
     40        ALuint* al_buf = new ALuint[num_processed]; 
     41        alSourceUnqueueBuffers(m_ALSource, num_processed, al_buf); 
     42        delete[] al_buf; 
     43    } 
     44} 
     45 
     46bool CStreamItem::IdleTask() 
     47{ 
     48    HandleFade(); 
     49 
     50    int proc_state; 
     51    alGetSourceiv( m_ALSource, AL_SOURCE_STATE, &proc_state); 
     52     
     53    if ( proc_state == AL_STOPPED ) 
     54    { 
     55        if ( m_LastPlay ) 
     56            return ( proc_state != AL_STOPPED ); 
     57    } 
     58    else 
     59    { 
     60        COggData* tmp = (COggData*)m_SoundData; 
     61         
     62        if ( ! tmp->IsFileFinished() ) 
     63        { 
     64            int num_processed; 
     65            alGetSourcei( m_ALSource, AL_BUFFERS_PROCESSED, &num_processed); 
     66             
     67            if (num_processed > 0) 
     68            { 
     69                ALuint* al_buf = new ALuint[num_processed]; 
     70                alSourceUnqueueBuffers(m_ALSource, num_processed, al_buf); 
     71                int didWrite = tmp->FetchDataIntoBuffer( num_processed, al_buf); 
     72                alSourceQueueBuffers( m_ALSource, didWrite, al_buf); 
     73                delete[] al_buf; 
     74            } 
     75        } 
     76        else if ( GetLooping() ) 
     77        { 
     78            tmp->ResetFile(); 
     79        } 
     80    } 
     81    return true; 
     82} 
     83 
     84void CStreamItem::Attach( CSoundData* itemData ) 
     85{ 
     86    if ( itemData != NULL ) 
     87    { 
     88        m_SoundData = itemData->IncrementCount(); 
     89        alSourceQueueBuffers(m_ALSource, m_SoundData->GetBufferCount(), (const ALuint *)m_SoundData->GetBufferPtr()); 
     90    } 
     91} 
     92 
     93void CStreamItem::SetLooping( bool loops ) 
     94{ 
     95    m_Looping = loops; 
     96} 
     97 
  • source/soundmanager/CSoundManager.cpp

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18#include "precompiled.h" 
     19 
     20#include "CSoundManager.h" 
     21#include "soundmanager/items/CSoundItem.h" 
     22#include "soundmanager/items/CBufferItem.h" 
     23#include "soundmanager/items/CStreamItem.h" 
     24#include "soundmanager/js/JSoundPlayer.h" 
     25#include "soundmanager/js/JAmbientSound.h" 
     26#include "soundmanager/js/JMusicSound.h" 
     27#include "soundmanager/js/JSound.h" 
     28#include "soundmanager/data/CSoundData.h" 
     29 
     30 
     31CSoundManager*  g_SoundManager; 
     32 
     33void CSoundManager::ScriptingInit() 
     34{ 
     35    JAmbientSound::ScriptingInit(); 
     36    JMusicSound::ScriptingInit(); 
     37    JSound::ScriptingInit(); 
     38    JSoundPlayer::ScriptingInit(); 
     39} 
     40 
     41CSoundManager::CSoundManager() 
     42{ 
     43    m_Items = new ItemsList; 
     44    m_CurrentEnvirons    = 0; 
     45    m_CurrentTune        = 0; 
     46    m_Gain               = 1; 
     47    m_MusicGain          = 1; 
     48    m_AmbientGain        = 1; 
     49    m_ActionGain         = 1; 
     50    m_Enabled            = true; 
     51    m_BufferCount       = 50; 
     52    m_BufferSize            = 65536; 
     53    m_MusicEnabled      = true; 
     54    AlcInit(); 
     55} 
     56 
     57CSoundManager::~CSoundManager() 
     58{     
     59    ItemsList::iterator lstr = m_Items->begin(); 
     60    while ( lstr != m_Items->end() ) 
     61    { 
     62        (*lstr)->Stop(); 
     63        delete *lstr; 
     64        lstr++; 
     65    } 
     66 
     67    alcDestroyContext( m_Context ); 
     68    alcCloseDevice( m_Device ); 
     69 
     70    delete m_Items; 
     71    m_Items = 0L; 
     72    m_CurrentEnvirons    = 0; 
     73    m_CurrentTune        = 0; 
     74} 
     75 
     76 
     77Status CSoundManager::AlcInit() 
     78{    
     79    Status ret = INFO::OK; 
     80 
     81    m_Device = alcOpenDevice(NULL); 
     82    if(m_Device) 
     83    { 
     84        m_Context = alcCreateContext(m_Device, 0);  // no attrlist needed 
     85        if(m_Context) 
     86            alcMakeContextCurrent(m_Context); 
     87    } 
     88 
     89    // check if init succeeded. 
     90    // some OpenAL implementations don't indicate failure here correctly; 
     91    // we need to check if the device and context pointers are actually valid. 
     92    ALCenum err = alcGetError(m_Device); 
     93    if(err != ALC_NO_ERROR || !m_Device || !m_Context) 
     94    { 
     95#if OS_UNIX 
     96        ret = INFO::OK; 
     97#else 
     98        ret = ERR::FAIL; 
     99#endif 
     100    } 
     101 
     102    const char* dev_name = (const char*)alcGetString(m_Device, ALC_DEVICE_SPECIFIER); 
     103    wchar_t buf[200]; 
     104    swprintf(buf, ARRAY_SIZE(buf), L"SND| alc_init: success, using %hs\n", dev_name); 
     105 
     106    return ret; 
     107} 
     108void CSoundManager::SetMemoryUsage( long bufferSize, int bufferCount ) 
     109{ 
     110    m_BufferCount = bufferCount; 
     111    m_BufferSize = bufferSize; 
     112} 
     113long CSoundManager::GetBufferCount() 
     114{ 
     115    return m_BufferCount; 
     116} 
     117long CSoundManager::GetBufferSize() 
     118{ 
     119    return m_BufferSize; 
     120} 
     121 
     122 
     123void CSoundManager::SetMasterGain( float gain) 
     124{ 
     125    m_Gain = gain; 
     126} 
     127void CSoundManager::SetMusicGain( float gain) 
     128{ 
     129    m_MusicGain = gain; 
     130} 
     131void CSoundManager::SetAmbientGain( float gain) 
     132{ 
     133    m_AmbientGain = gain; 
     134} 
     135void CSoundManager::SetActionGain( float gain) 
     136{ 
     137    m_ActionGain = gain; 
     138} 
     139 
     140 
     141ISoundItem* CSoundManager::LoadItem( const VfsPath* itemPath ) 
     142{     
     143    CSoundData*   itemData = CSoundData::SoundDataFromFile( itemPath ); 
     144    ISoundItem*   answer  = NULL; 
     145     
     146    if ( itemData != NULL ) 
     147    { 
     148        if ( itemData->IsOneShot() ) 
     149        { 
     150            if ( itemData->GetBufferCount() == 1 ) 
     151                answer = new CSoundItem( itemData ); 
     152            else 
     153                answer = new CBufferItem( itemData ); 
     154        } 
     155        else 
     156        { 
     157            answer = new CStreamItem( itemData ); 
     158        } 
     159 
     160        if ( answer != NULL ) 
     161            m_Items->push_back( answer ); 
     162    } 
     163 
     164     
     165    return answer; 
     166} 
     167 
     168unsigned long CSoundManager::Count() 
     169{ 
     170    return m_Items->size(); 
     171} 
     172 
     173void CSoundManager::IdleTask() 
     174{ 
     175    if ( m_Items ) 
     176    { 
     177        ItemsList::iterator lstr = m_Items->begin(); 
     178        ItemsList  deadItemList; 
     179        ItemsList* nextItemList = new ItemsList; 
     180 
     181 
     182        while ( lstr != m_Items->end() ) { 
     183            if ( (*lstr)->IdleTask() ) 
     184                nextItemList->push_back( *lstr ); 
     185            else 
     186                deadItemList.push_back( *lstr ); 
     187            lstr++; 
     188        } 
     189        delete m_Items; 
     190        m_Items = nextItemList; 
     191 
     192        ItemsList::iterator deadItems = deadItemList.begin(); 
     193        while ( deadItems != deadItemList.end() ) 
     194        {    
     195            delete *deadItems; 
     196            deadItems++; 
     197        } 
     198    } 
     199    if ( m_CurrentTune ) 
     200        m_CurrentTune->EnsurePlay(); 
     201    if ( m_CurrentEnvirons ) 
     202        m_CurrentEnvirons->EnsurePlay(); 
     203} 
     204 
     205void CSoundManager::DeleteItem( long itemNum ) 
     206{ 
     207    ItemsList::iterator lstr = m_Items->begin(); 
     208    lstr += itemNum; 
     209     
     210    delete *lstr; 
     211     
     212    m_Items->erase( lstr ); 
     213} 
     214 
     215ISoundItem* CSoundManager::GetSoundItem( unsigned long itemRow ) 
     216{ 
     217   return (*m_Items)[itemRow]; 
     218} 
     219 
     220void CSoundManager::InitListener() 
     221{ 
     222    ALfloat listenerPos[]={0.0,0.0,0.0}; 
     223    ALfloat listenerVel[]={0.0,0.0,0.0}; 
     224    ALfloat listenerOri[]={0.0,0.0,-1.0, 0.0,1.0,0.0}; 
     225 
     226    alListenerfv(AL_POSITION,listenerPos); 
     227    alListenerfv(AL_VELOCITY,listenerVel); 
     228    alListenerfv(AL_ORIENTATION,listenerOri); 
     229 
     230    alDistanceModel(AL_EXPONENT_DISTANCE); 
     231} 
     232 
     233void CSoundManager::SetEnabled( bool doEnable ) 
     234{ 
     235    m_Enabled = doEnable; 
     236} 
     237 
     238void CSoundManager::PlayActionItem( ISoundItem* anItem ) 
     239{ 
     240    if ( anItem ) 
     241    { 
     242        if ( m_Enabled && ( m_ActionGain > 0 ) ) 
     243        { 
     244            anItem->SetGain( m_Gain * m_ActionGain ); 
     245            anItem->Play(); 
     246        } 
     247    } 
     248} 
     249void CSoundManager::PlayGroupItem( ISoundItem* anItem, ALfloat groupGain) 
     250{ 
     251    if ( anItem ) 
     252    { 
     253        if ( m_Enabled && ( m_ActionGain > 0 ) ) { 
     254            anItem->SetGain( m_Gain * groupGain ); 
     255            anItem->Play(); 
     256        } 
     257    } 
     258} 
     259 
     260void CSoundManager::SetMusicEnabled (bool isEnabled) 
     261{ 
     262    if ( m_CurrentTune && !isEnabled ) 
     263    { 
     264        m_CurrentTune->FadeAndDelete(1.00); 
     265        m_CurrentTune = 0L; 
     266    } 
     267    m_MusicEnabled = isEnabled; 
     268} 
     269 
     270void CSoundManager::SetMusicItem( ISoundItem* anItem ) 
     271{ 
     272    if ( m_CurrentTune ) 
     273    { 
     274        m_CurrentTune->FadeAndDelete(3.00); 
     275        m_CurrentTune = 0L; 
     276    } 
     277    IdleTask(); 
     278    if ( anItem ) 
     279    { 
     280        if ( m_MusicEnabled && m_Enabled ) 
     281        { 
     282            m_CurrentTune = anItem; 
     283            m_CurrentTune->PlayLoop(); 
     284            m_CurrentTune->FadeToIn( m_Gain * m_MusicGain, 3.00 ); 
     285        } 
     286        else 
     287        { 
     288            anItem->StopAndDelete(); 
     289        } 
     290    } 
     291} 
     292 
     293void CSoundManager::SetAmbientItem( ISoundItem* anItem ) 
     294{ 
     295    if ( m_CurrentEnvirons ) 
     296    { 
     297        m_CurrentEnvirons->FadeAndDelete(3.00); 
     298        m_CurrentEnvirons = 0L; 
     299    } 
     300    IdleTask(); 
     301     
     302    if ( anItem ) 
     303    { 
     304        if ( m_Enabled && ( m_AmbientGain > 0 ) ) 
     305        { 
     306            m_CurrentEnvirons = anItem; 
     307            m_CurrentEnvirons->SetGain( 0 ); 
     308            m_CurrentEnvirons->PlayLoop(); 
     309            m_CurrentEnvirons->FadeToIn( m_Gain * m_AmbientGain, 3.00 ); 
     310        } 
     311    } 
     312} 
     313 
  • source/soundmanager/data/COggData.h

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18 
     19#ifndef SoundTester_COggData_h 
     20#define SoundTester_COggData_h 
     21 
     22#include "CSoundData.h" 
     23#include "lib/external_libraries/openal.h" 
     24#include "vorbis/vorbisfile.h" 
     25 
     26class COggData : public CSoundData 
     27{ 
     28    ALuint m_Format; 
     29    long m_Frequency; 
     30 
     31public: 
     32    COggData      (); 
     33    virtual ~COggData      ();                                      
     34                                      
     35    virtual bool InitOggFile( const wchar_t* fileLoc ); 
     36    virtual bool IsFileFinished(); 
     37    virtual bool IsOneShot(); 
     38 
     39    virtual int FetchDataIntoBuffer( int count, ALuint* buffers); 
     40    virtual void ResetFile(); 
     41 
     42protected: 
     43    OggVorbis_File  m_vf; 
     44    int            m_current_section; 
     45    bool           m_FileFinished; 
     46    bool           m_OneShot; 
     47    ALuint         m_Buffer[100]; 
     48    int            m_BuffersUsed; 
     49 
     50    bool AddDataBuffer( char* data, long length); 
     51    void SetFormatAndFreq( int form, ALsizei freq); 
     52    ALsizei  GetBufferCount(); 
     53    ALuint GetBuffer(); 
     54    ALuint* GetBufferPtr();   
     55}; 
     56 
     57 
     58 
     59#endif 
  • source/soundmanager/data/CSoundData.cpp

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18#include "precompiled.h" 
     19#include "CSoundData.h" 
     20 
     21 
     22#include <iostream> 
     23#include "COggData.h" 
     24#include "ps/Filesystem.h" 
     25#include "lib/file/vfs/vfs_util.h" 
     26 
     27DataMap* CSoundData::sSoundData = NULL; 
     28 
     29CSoundData::CSoundData() 
     30{ 
     31    InitProperties(); 
     32} 
     33 
     34CSoundData::~CSoundData() 
     35{ 
     36    if ( m_ALBuffer != 0 ) 
     37        alDeleteBuffers( 1, &m_ALBuffer ); 
     38} 
     39 
     40void CSoundData::InitProperties() 
     41{ 
     42    m_ALBuffer = 0; 
     43    m_RetentionCount = 0; 
     44} 
     45 
     46void CSoundData::ReleaseSoundData( CSoundData* theData ) 
     47{ 
     48    DataMap::iterator   itemFind; 
     49 
     50    if ( theData->DecrementCount() ) 
     51    { 
     52        if ( ( itemFind = CSoundData::sSoundData->find( theData->GetFileName() ) ) != sSoundData->end() ) 
     53        { 
     54            CSoundData* dier = itemFind->second; 
     55            CSoundData::sSoundData->erase( itemFind ); 
     56            delete dier; 
     57        } 
     58    } 
     59} 
     60 
     61CSoundData* CSoundData::SoundDataFromFile( const VfsPath* itemPath ) 
     62{ 
     63    if ( CSoundData::sSoundData == NULL ) 
     64        CSoundData::sSoundData = new DataMap; 
     65     
     66    Path                fExt = itemPath->Extension(); 
     67    DataMap::iterator   itemFind; 
     68    CSoundData*          answer = NULL; 
     69 
     70 
     71    if ( ( itemFind = CSoundData::sSoundData->find( itemPath->string() ) ) != sSoundData->end() ) 
     72    { 
     73        answer = itemFind->second; 
     74    } 
     75    else 
     76    { 
     77        if ( fExt == ".ogg" ) 
     78            answer = SoundDataFromOgg( itemPath ); 
     79     
     80        if ( answer && answer->IsOneShot() ) 
     81            (*CSoundData::sSoundData)[itemPath->string()] = answer; 
     82     
     83    } 
     84    return answer; 
     85} 
     86 
     87bool CSoundData::IsOneShot() 
     88{ 
     89    return true; 
     90} 
     91 
     92 
     93CSoundData* CSoundData::SoundDataFromOgg( const VfsPath* itemPath ) 
     94{ 
     95    CSoundData* answer = NULL; 
     96    COggData*   oggAnswer = new COggData(); 
     97 
     98    OsPath realPath; 
     99    Status ret = g_VFS->GetRealPath( *itemPath, realPath); 
     100    if ( ret == INFO::OK ) 
     101    { 
     102        if ( oggAnswer->InitOggFile( realPath.string().c_str() ) ) 
     103        { 
     104            answer = oggAnswer; 
     105        } 
     106    }     
     107    return answer; 
     108} 
     109 
     110 
     111ALsizei CSoundData::GetBufferCount() 
     112{ 
     113    return 1; 
     114} 
     115 
     116CStrW     CSoundData::GetFileName() 
     117{ 
     118    return m_FileName; 
     119} 
     120 
     121 
     122 
     123CSoundData* CSoundData::IncrementCount() 
     124{ 
     125    m_RetentionCount++; 
     126    return this; 
     127} 
     128 
     129bool CSoundData::DecrementCount() 
     130{ 
     131    m_RetentionCount--; 
     132     
     133    return ( m_RetentionCount <= 0 ); 
     134} 
     135 
     136ALuint CSoundData::GetBuffer() 
     137{ 
     138    return m_ALBuffer; 
     139} 
     140ALuint* CSoundData::GetBufferPtr() 
     141{ 
     142    return &m_ALBuffer; 
     143} 
     144 
  • source/soundmanager/data/COggData.cpp

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17#include "precompiled.h" 
     18 
     19 
     20#include "COggData.h" 
     21 
     22 
     23#include <wchar.h> 
     24#include <iostream> 
     25#include "soundmanager/CSoundManager.h" 
     26 
     27COggData::COggData() 
     28{ 
     29    m_OneShot = false; 
     30} 
     31 
     32COggData::~COggData() 
     33{ 
     34    alDeleteBuffers( m_BuffersUsed, m_Buffer ); 
     35    ov_clear(&m_vf); 
     36} 
     37 
     38void COggData::SetFormatAndFreq( int form, ALsizei freq) 
     39{ 
     40    m_Format = form; 
     41    m_Frequency = freq; 
     42} 
     43 
     44bool COggData::InitOggFile( const wchar_t* fileLoc ) 
     45{ 
     46    int buffersToStart = g_SoundManager->GetBufferCount(); 
     47    char nameH[300]; 
     48    sprintf( nameH, "%ls", fileLoc ); 
     49     
     50    FILE* f = fopen( nameH, "rb"); 
     51    m_current_section = 0; 
     52    int err = ov_open_callbacks(f, &m_vf, NULL, 0, OV_CALLBACKS_DEFAULT); 
     53    if ( err < 0) 
     54    { 
     55        fprintf(stderr,"Input does not appear to be an Ogg bitstream :%d :%d.\n", err, ferror(f) ); 
     56        return false; 
     57    } 
     58 
     59    m_FileName       = CStrW(fileLoc); 
     60 
     61    m_FileFinished = false; 
     62    SetFormatAndFreq( (m_vf.vi->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16 , (ALsizei)m_vf.vi->rate ); 
     63 
     64    alGetError(); /* clear error */ 
     65    alGenBuffers( buffersToStart, m_Buffer); 
     66     
     67    if(alGetError() != AL_NO_ERROR)  
     68    { 
     69        printf("- Error creating initial buffer !!\n"); 
     70        return false; 
     71    } 
     72    else 
     73    { 
     74        m_BuffersUsed = FetchDataIntoBuffer( buffersToStart, m_Buffer); 
     75        if ( m_FileFinished ) 
     76        { 
     77            m_OneShot = true; 
     78            if ( m_BuffersUsed < buffersToStart ) 
     79            { 
     80                alDeleteBuffers( buffersToStart - m_BuffersUsed, &m_Buffer[m_BuffersUsed] ); 
     81            } 
     82        } 
     83    } 
     84    return true; 
     85} 
     86 
     87ALsizei COggData::GetBufferCount() 
     88{ 
     89    return m_BuffersUsed; 
     90} 
     91 
     92bool COggData::IsFileFinished() 
     93{ 
     94    return m_FileFinished; 
     95} 
     96 
     97void COggData::ResetFile() 
     98{ 
     99    ov_time_seek( &m_vf, 0 ); 
     100    m_current_section = 0; 
     101    m_FileFinished = false; 
     102} 
     103 
     104bool COggData::IsOneShot() 
     105{ 
     106    return m_OneShot; 
     107} 
     108 
     109int COggData::FetchDataIntoBuffer( int count, ALuint* buffers) 
     110{ 
     111    long bufferSize = g_SoundManager->GetBufferSize(); 
     112     
     113    char* pcmout = new char[bufferSize + 5000]; 
     114    int buffersWritten = 0; 
     115     
     116    for (int i = 0; ( i < count ) && !m_FileFinished; i++) 
     117    { 
     118        char*   readDest = pcmout; 
     119        long  totalRet = 0; 
     120        while (totalRet < bufferSize ) 
     121        { 
     122            long ret=ov_read(&m_vf,readDest, 4096,0,2,1, &m_current_section); 
     123            if (ret == 0) 
     124            { 
     125                m_FileFinished=true; 
     126                break; 
     127            } 
     128            else if (ret < 0) 
     129            { 
     130                /* error in the stream.  Not a problem, just reporting it in 
     131                 case we (the app) cares.  In this case, we don't. */ 
     132            } 
     133            else 
     134            { 
     135                totalRet += ret; 
     136                readDest += ret; 
     137            } 
     138        } 
     139        if ( totalRet > 0 ) 
     140        { 
     141            buffersWritten++; 
     142            alBufferData( buffers[i], m_Format, pcmout, (ALsizei)totalRet, (int)m_Frequency); 
     143        } 
     144    } 
     145    delete[] pcmout; 
     146    return buffersWritten; 
     147} 
     148 
     149 
     150ALuint COggData::GetBuffer() 
     151{ 
     152    return m_Buffer[0]; 
     153} 
     154 
     155ALuint* COggData::GetBufferPtr() 
     156{ 
     157    return m_Buffer; 
     158} 
     159 
     160 
     161 
     162 
     163 
  • source/soundmanager/data/CSoundData.h

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18 
     19#ifndef SoundTester_CSoundData_h 
     20#define SoundTester_CSoundData_h 
     21#include "lib/os_path.h" 
     22 
     23#include "string" 
     24#include "map" 
     25#include "lib/external_libraries/openal.h" 
     26#include "lib/file/vfs/vfs_path.h" 
     27 
     28class CSoundData; 
     29typedef std::map<std::wstring, CSoundData*> DataMap; 
     30 
     31 
     32 
     33class CSoundData 
     34{ 
     35public: 
     36    static CSoundData* SoundDataFromFile( const VfsPath* itemPath ); 
     37    static CSoundData* SoundDataFromOgg( const VfsPath* itemPath ); 
     38 
     39    static void ReleaseSoundData( CSoundData* theData ); 
     40 
     41    CSoundData      (); 
     42    CSoundData      (ALuint dataSource); 
     43    virtual ~CSoundData     (); 
     44     
     45    CSoundData*     IncrementCount(); 
     46    bool            DecrementCount(); 
     47    void            InitProperties(); 
     48    virtual bool IsOneShot(); 
     49 
     50     
     51    virtual ALuint      GetBuffer(); 
     52    virtual ALsizei     GetBufferCount(); 
     53    CStrW        GetFileName(); 
     54    virtual ALuint*     GetBufferPtr(); 
     55 
     56protected: 
     57    static     DataMap*  sSoundData; 
     58 
     59    ALuint          m_ALBuffer; 
     60    int             m_RetentionCount; 
     61    CStrW    m_FileName; 
     62     
     63     
     64     
     65}; 
     66 
     67 
     68 
     69 
     70 
     71 
     72#endif 
  • source/soundmanager/CSoundManager.h

     
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18 
     19#ifndef SoundTester_CSoundManager_h 
     20#define SoundTester_CSoundManager_h 
     21 
     22#include "vector" 
     23#include "map" 
     24 
     25#include "soundmanager/items/ISoundItem.h" 
     26#include "lib/file/vfs/vfs_path.h" 
     27 
     28typedef std::vector<ISoundItem*> ItemsList; 
     29 
     30 
     31class CSoundManager 
     32{ 
     33protected: 
     34 
     35    ALuint              m_ALEnvironment; 
     36    ALCcontext*         m_Context; 
     37    ALCdevice*          m_Device; 
     38    ISoundItem*         m_CurrentTune; 
     39    ISoundItem*         m_CurrentEnvirons; 
     40    ItemsList*          m_Items; 
     41    float               m_Gain; 
     42    float               m_MusicGain; 
     43    float               m_AmbientGain; 
     44    float               m_ActionGain; 
     45    bool                m_Enabled; 
     46    long                m_BufferSize; 
     47    int                 m_BufferCount; 
     48    bool                m_MusicEnabled; 
     49 
     50public: 
     51     CSoundManager      (); 
     52    virtual ~CSoundManager      (); 
     53 
     54    ISoundItem* LoadItem( const VfsPath* itemPath ); 
     55 
     56    static void ScriptingInit(); 
     57 
     58 
     59    void SetMusicEnabled (bool isEnabled); 
     60 
     61    ISoundItem*     ItemFromWAV     ( VfsPath& fname); 
     62    ISoundItem*     ItemFromOgg     ( VfsPath& fname); 
     63 
     64    ISoundItem*     GetSoundItem    ( unsigned long itemRow ); 
     65    unsigned long   Count           (); 
     66    void            IdleTask        (); 
     67    void            DeleteItem      ( long itemNum ); 
     68     
     69    void    SetMemoryUsage( long bufferSize, int bufferCount ); 
     70    long    GetBufferCount(); 
     71    long    GetBufferSize(); 
     72 
     73    void        SetMusicItem( ISoundItem* anItem ); 
     74    void        SetAmbientItem( ISoundItem* anItem ); 
     75    void        PlayActionItem( ISoundItem* anItem ); 
     76    void        PlayGroupItem( ISoundItem* anItem, ALfloat groupGain); 
     77 
     78    void        SetMasterGain( float gain); 
     79    void        SetMusicGain( float gain); 
     80    void        SetAmbientGain( float gain); 
     81    void        SetActionGain( float gain); 
     82     
     83    void        SetEnabled( bool doEnable ); 
     84protected: 
     85    void    InitListener(); 
     86    virtual Status AlcInit(); 
     87 
     88}; 
     89 
     90 
     91 
     92extern CSoundManager*  g_SoundManager; 
     93 
     94 
     95 
     96 
     97 
     98 
     99 
     100#endif 
  • source/soundmanager/js/JAmbientSound.cpp

     
     1/* Copyright (C) 2009 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17#include "precompiled.h" 
     18 
     19#include "JAmbientSound.h" 
     20#include "maths/Vector3D.h" 
     21 
     22#include "lib/utf8.h" 
     23#include "ps/Filesystem.h" 
     24 
     25#include "soundmanager/CSoundManager.h" 
     26 
     27JAmbientSound::JAmbientSound(const VfsPath& pathname) 
     28{ 
     29    m_FileName = new VfsPath( pathname.string().c_str() ); 
     30} 
     31 
     32JAmbientSound::~JAmbientSound() 
     33{ 
     34} 
     35 
     36 
     37// start playing the sound, all ambient sounds loop 
     38bool JAmbientSound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     39{ 
     40    ISoundItem* aSnd = g_SoundManager->LoadItem( m_FileName ); 
     41 
     42    aSnd->PlayAsAmbient(); 
     43 
     44    return true; 
     45} 
     46 
     47// start playing the sound, all ambient sounds loop 
     48bool JAmbientSound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     49{ 
     50    ISoundItem* aSnd = g_SoundManager->LoadItem( m_FileName ); 
     51 
     52    aSnd->PlayAsAmbient(); 
     53    return true; 
     54} 
     55bool JAmbientSound::Free(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     56{ 
     57    g_SoundManager->SetAmbientItem( 0L ); 
     58 
     59    return true; 
     60} 
     61 
     62// Script-bound functions 
     63 
     64 
     65void JAmbientSound::ScriptingInit() 
     66{ 
     67    AddMethod<CStr, &JAmbientSound::ToString>("toString", 0); 
     68    AddMethod<bool, &JAmbientSound::Play>("play", 0); 
     69    AddMethod<bool, &JAmbientSound::Loop>("loop", 0); 
     70    AddMethod<bool, &JAmbientSound::Free>("free", 0); 
     71     
     72    CJSObject<JAmbientSound>::ScriptingInit("AmbientSound", &JAmbientSound::Construct, 1); 
     73} 
     74 
     75CStr JAmbientSound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     76{ 
     77    std::ostringstream stringStream; 
     78    stringStream << "[object AmbientSound: "; 
     79    stringStream << m_FileName->string().c_str(); 
     80     
     81    return stringStream.str(); 
     82} 
     83 
     84JSBool JAmbientSound::Construct(JSContext* cx, uintN UNUSED(argc), jsval* vp) 
     85{ 
     86//  JSU_REQUIRE_MIN_PARAMS(1); 
     87     
     88    CStrW filename; 
     89    if (! ToPrimitive<CStrW>(cx, JS_ARGV(cx, vp)[0], filename)) 
     90        return JS_FALSE; 
     91     
     92    JAmbientSound* newObject = new JAmbientSound(filename); 
     93    newObject->m_EngineOwned = false; 
     94 
     95    JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript())); 
     96     
     97    return JS_TRUE; 
     98} 
  • source/soundmanager/js/JSound.cpp

     
     1/* Copyright (C) 2009 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17#include "precompiled.h" 
     18#include "JSound.h" 
     19#include "maths/Vector3D.h" 
     20 
     21#include "lib/utf8.h" 
     22#include "ps/Filesystem.h" 
     23 
     24#include "soundmanager/CSoundManager.h" 
     25 
     26 
     27JSound::JSound(const VfsPath& pathname) 
     28{ 
     29    m_SndItem = g_SoundManager->LoadItem( &pathname ); 
     30} 
     31 
     32JSound::~JSound() 
     33{ 
     34    if ( m_SndItem ) 
     35    { 
     36        m_SndItem->FadeAndDelete(0.2); 
     37        m_SndItem = 0; 
     38    } 
     39} 
     40 
     41bool JSound::ClearSoundItem() 
     42{ 
     43    m_SndItem = 0L; 
     44    return true; 
     45} 
     46 
     47bool JSound::SetGain(JSContext* cx, uintN UNUSED(argc), jsval* argv) 
     48{ 
     49    if (! m_SndItem ) 
     50        return false; 
     51 
     52    float gain; 
     53    if (! ToPrimitive<float>(cx, argv[0], gain)) 
     54        return false; 
     55     
     56    m_SndItem->SetGain( gain );  
     57    return true; 
     58} 
     59 
     60bool JSound::SetPitch(JSContext* cx, uintN UNUSED(argc), jsval* argv) 
     61{ 
     62    if (! m_SndItem ) 
     63        return false; 
     64 
     65    float pitch; 
     66    if (! ToPrimitive<float>(cx, argv[0], pitch)) 
     67        return false; 
     68     
     69    m_SndItem->SetPitch( pitch ); 
     70    return true; 
     71} 
     72 
     73bool JSound::SetPosition(JSContext* cx, uintN argc, jsval* argv) 
     74{ 
     75    if (! m_SndItem ) 
     76        return false; 
     77     
     78    ENSURE(argc >= 1); // FIXME 
     79     
     80    CVector3D pos; 
     81    // absolute world coords 
     82    if (!ToPrimitive<CVector3D>(cx, argv[0], pos)) 
     83        return false; 
     84 
     85    m_SndItem->SetLocation( pos ); 
     86     
     87    return true; 
     88} 
     89 
     90 
     91bool JSound::Fade(JSContext* cx, uintN UNUSED(argc), jsval* argv) 
     92{ 
     93    if (! m_SndItem ) 
     94        return false; 
     95     
     96//  ENSURE(argc >= 3); // FIXME 
     97    float initial_gain, final_gain; 
     98    float length; 
     99    if (! (ToPrimitive<float>(cx, argv[0], initial_gain) 
     100           && ToPrimitive<float>(cx, argv[1], final_gain) 
     101           && ToPrimitive<float>(cx, argv[2], length))) 
     102        return false; 
     103     
     104    m_SndItem->SetGain( initial_gain ); 
     105    m_SndItem->FadeToIn( final_gain, length ); 
     106 
     107    return true; 
     108} 
     109 
     110bool JSound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     111{ 
     112    if (! m_SndItem ) 
     113        return false; 
     114 
     115    m_SndItem->Play(); 
     116 
     117    return true; 
     118} 
     119 
     120bool JSound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     121{ 
     122    if (! m_SndItem ) 
     123        return false; 
     124 
     125    m_SndItem->PlayLoop(); 
     126 
     127    return true; 
     128} 
     129 
     130bool JSound::Free(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     131{ 
     132    if ( m_SndItem ) 
     133    { 
     134        m_SndItem->FadeAndDelete(0.2); 
     135        m_SndItem = 0; 
     136    } 
     137 
     138    return true; 
     139} 
     140 
     141void JSound::ScriptingInit() 
     142{ 
     143    AddMethod<CStr, &JSound::ToString>("toString", 0); 
     144    AddMethod<bool, &JSound::Play>("play", 0); 
     145    AddMethod<bool, &JSound::Loop>("loop", 0); 
     146    AddMethod<bool, &JSound::Free>("free", 0); 
     147    AddMethod<bool, &JSound::SetGain>("setGain", 0); 
     148    AddMethod<bool, &JSound::SetPitch>("setPitch", 0); 
     149    AddMethod<bool, &JSound::SetPosition>("setPosition", 0); 
     150    AddMethod<bool, &JSound::Fade>("fade", 0); 
     151     
     152    CJSObject<JSound>::ScriptingInit("Sound", &JSound::Construct, 1); 
     153} 
     154 
     155CStr JSound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     156{ 
     157    return "[object Sound: " + ( m_SndItem ? m_SndItem->GetName() : "(null)" ) + "]"; 
     158} 
     159 
     160JSBool JSound::Construct(JSContext* cx, uintN UNUSED(argc), jsval* vp) 
     161{ 
     162//  JSU_REQUIRE_MIN_PARAMS(1); 
     163     
     164    CStrW filename; 
     165    if (! ToPrimitive<CStrW>(cx, JS_ARGV(cx, vp)[0], filename)) 
     166        return JS_FALSE; 
     167     
     168    JSound* newObject = new JSound(filename); 
     169    newObject->m_EngineOwned = false; 
     170    JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript())); 
     171     
     172    return JS_TRUE; 
     173} 
  • source/soundmanager/js/JMusicSound.cpp

     
     1/* Copyright (C) 2009 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17#include "precompiled.h" 
     18 
     19#include "JMusicSound.h" 
     20#include "maths/Vector3D.h" 
     21 
     22#include "lib/utf8.h" 
     23#include "ps/Filesystem.h" 
     24 
     25#include "soundmanager/CSoundManager.h" 
     26 
     27 
     28JMusicSound::JMusicSound(const VfsPath& pathname) 
     29{ 
     30    m_FileName = new VfsPath( pathname.string().c_str() ); 
     31} 
     32 
     33JMusicSound::~JMusicSound() 
     34{ 
     35    delete m_FileName; 
     36} 
     37 
     38bool JMusicSound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     39{ 
     40    ISoundItem* aSnd = g_SoundManager->LoadItem( m_FileName ); 
     41    if ( aSnd != NULL ) 
     42        aSnd->PlayAsMusic(); 
     43 
     44    return true; 
     45} 
     46 
     47// request the sound be played until free() is called. returns immediately. 
     48bool JMusicSound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     49{ 
     50    ISoundItem* aSnd = g_SoundManager->LoadItem( m_FileName ); 
     51    if ( aSnd != NULL ) 
     52        aSnd->PlayAsMusic(); 
     53 
     54    return true; 
     55} 
     56 
     57void JMusicSound::ScriptingInit() 
     58{ 
     59    AddMethod<CStr, &JMusicSound::ToString>("toString", 0); 
     60    AddMethod<bool, &JMusicSound::Play>("play", 0); 
     61    AddMethod<bool, &JMusicSound::Loop>("loop", 0); 
     62     
     63    CJSObject<JMusicSound>::ScriptingInit("MusicSound", &JMusicSound::Construct, 1); 
     64} 
     65 
     66CStr JMusicSound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
     67{ 
     68    std::ostringstream stringStream; 
     69    stringStream << "[object MusicSound: "; 
     70    stringStream << m_FileName->string().c_str(); 
     71     
     72    return stringStream.str(); 
     73} 
     74 
     75JSBool JMusicSound::Construct(JSContext* cx, uintN UNUSED(argc), jsval* vp) 
     76{     
     77    CStrW filename; 
     78    if (! ToPrimitive<CStrW>(cx, JS_ARGV(cx, vp)[0], filename)) 
     79        return JS_FALSE; 
     80     
     81    JMusicSound* newObject = new JMusicSound(filename); 
     82    newObject->m_EngineOwned = false; 
     83    JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript())); 
     84     
     85    return JS_TRUE; 
     86} 
  • source/soundmanager/js/SMSoundGroup.cpp

     
     1/* Copyright (C) 2010 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18/** 
     19* ========================================================================= 
     20* File        : SoundGroup.cpp 
     21* Project     : 0 A.D. 
     22* Description : Loads up a group of sound files with shared properties,  
     23*               and provides a simple interface for playing them.         
     24* ========================================================================= 
     25*/ 
     26 
     27#include "precompiled.h" 
     28#include "soundmanager/CSoundManager.h" 
     29#include "SMSoundGroup.h" 
     30 
     31#include <algorithm> 
     32 
     33#include "lib/rand.h" 
     34 
     35#include "ps/XML/Xeromyces.h" 
     36#include "ps/CLogger.h" 
     37#include "ps/Filesystem.h" 
     38#include "ps/Util.h" 
     39#include "ps/Game.h" 
     40#include "ps/CLogger.h" 
     41#include "graphics/GameView.h" 
     42#include "graphics/Camera.h" 
     43#include "soundmanager/items/ISoundItem.h" 
     44 
     45extern CGame *g_Game; 
     46 
     47#define PI 3.14126f 
     48 
     49 
     50static const bool DISABLE_INTENSITY = true; // disable for now since it's broken 
     51 
     52void CSMSoundGroup::SetGain(float gain) 
     53{ 
     54    gain = std::min(gain, 1.0f); 
     55    m_Gain = gain; 
     56} 
     57 
     58void CSMSoundGroup::SetDefaultValues() 
     59{ 
     60    m_index = 0; 
     61    m_Flags = 0; 
     62    m_Intensity = 0; 
     63    m_CurTime = 0.0f; 
     64 
     65    // sane defaults; will probably be replaced by the values read during LoadSoundGroup. 
     66    SetGain(0.7f); 
     67    m_Pitch = 1.0f; 
     68    m_Priority = 60; 
     69    m_PitchUpper = 1.1f; 
     70    m_PitchLower = 0.9f; 
     71    m_GainUpper = 1.0f; 
     72    m_GainLower = 0.8f; 
     73    m_ConeOuterGain = 0.0f; 
     74    m_ConeInnerAngle = 360.0f; 
     75    m_ConeOuterAngle = 360.0f; 
     76    m_Decay = 3.0f; 
     77    m_IntensityThreshold = 3; 
     78    // WARNING: m_TimeWindow is currently unused and uninitialized 
     79} 
     80 
     81CSMSoundGroup::CSMSoundGroup() 
     82{ 
     83    SetDefaultValues(); 
     84} 
     85 
     86CSMSoundGroup::CSMSoundGroup(const VfsPath& pathnameXML) 
     87{ 
     88    SetDefaultValues(); 
     89    LoadSoundGroup(pathnameXML); 
     90} 
     91 
     92CSMSoundGroup::~CSMSoundGroup() 
     93{ 
     94    // clean up all the handles from this group. 
     95    ReleaseGroup(); 
     96} 
     97 
     98static float RandFloat(float min, float max) 
     99{ 
     100    return float(rand(min*100.0f, max*100.0f) / 100.0f); 
     101} 
     102 
     103float CSMSoundGroup::RadiansOffCenter( const CVector3D& position, bool& onScreen, float& itemRollOff ) 
     104{ 
     105    float x, y; 
     106    float answer = 0.0; 
     107    const size_t screenWidth = g_Game->GetView()->GetCamera()->GetViewPort().m_Width; 
     108    const size_t screenHeight = g_Game->GetView()->GetCamera()->GetViewPort().m_Height; 
     109    float bufferSize = screenWidth * 0.10; 
     110    const size_t audioWidth = screenWidth; 
     111    float radianCap = PI / 3; 
     112     
     113    g_Game->GetView()->GetCamera()->GetScreenCoordinates(position, x, y); 
     114     
     115    onScreen = true; 
     116 
     117    if ( x < -bufferSize ) 
     118    { 
     119        onScreen = false; 
     120        answer = -radianCap; 
     121    } 
     122    else if ( x > screenWidth + bufferSize ) 
     123    { 
     124        onScreen = false; 
     125        answer = radianCap; 
     126    } 
     127    else { 
     128        if ( ( x < 0 ) || ( x > screenWidth ) ) 
     129        { 
     130            itemRollOff = 0.5; 
     131        } 
     132        float pixPerRadian = audioWidth / ( radianCap * 2 ); 
     133        answer = ( x - (screenWidth/2) ) / pixPerRadian; 
     134    } 
     135 
     136    if ( y < -bufferSize ) 
     137    { 
     138        onScreen = false; 
     139    } 
     140    else if ( y > screenHeight + bufferSize ) 
     141    { 
     142        onScreen = false; 
     143    } 
     144    else { 
     145        if ( ( y < 0 ) || ( y > screenHeight ) ) 
     146        { 
     147            itemRollOff = 0.5; 
     148        } 
     149    } 
     150 
     151         
     152 //   debug_printf(L"do play at x portion:%f pts x:%f, y=%f at radians=%f\n\n", answer, x, y, answer ); 
     153 
     154    return answer; 
     155} 
     156 
     157void CSMSoundGroup::UploadPropertiesAndPlay(int theIndex, const CVector3D& position) 
     158{ 
     159    bool    isOnscreen; 
     160    ALfloat initialRolllOff = 0.02f; 
     161    ALfloat itemRollOff = initialRolllOff; 
     162 
     163    float   offSet = RadiansOffCenter( position, isOnscreen, itemRollOff); 
     164 
     165    if ( isOnscreen || TestFlag(eDistanceless) || TestFlag(eOmnipresent) ) 
     166    { 
     167        if ( snd_group.size() == 0 ) 
     168            Reload(); 
     169 
     170        ISoundItem* hSound = snd_group[theIndex]; 
     171        CVector3D origin = g_Game->GetView()->GetCamera()->GetOrientation().GetTranslation(); 
     172        float sndDist = origin.Y; 
     173 
     174        if (!TestFlag(eOmnipresent)) 
     175        { 
     176            hSound->SetLocation( CVector3D(  (sndDist * sin(offSet)), 0, sndDist * cos(offSet) )); 
     177            if ( TestFlag(eDistanceless) ) 
     178                hSound->SetRollOff( initialRolllOff ); 
     179            else 
     180                hSound->SetRollOff( itemRollOff ); 
     181        } 
     182 
     183        if ( TestFlag(eRandPitch) ) 
     184            hSound->SetPitch( RandFloat( m_PitchLower, m_PitchUpper ) ); 
     185        else 
     186            hSound->SetPitch( m_Pitch ); 
     187 
     188        ALfloat theGain = m_Gain; 
     189        if ( TestFlag(eRandGain) ) 
     190            theGain = RandFloat( m_GainLower, m_GainUpper); 
     191 
     192        hSound->SetCone( m_ConeInnerAngle, m_ConeOuterAngle, m_ConeOuterGain); 
     193 
     194        g_SoundManager->PlayGroupItem( hSound, theGain ); 
     195    } 
     196} 
     197 
     198 
     199static void HandleError(const CStrW& message, const VfsPath& pathname, Status err) 
     200{ 
     201    if (err == ERR::AGAIN) 
     202        return; // open failed because sound is disabled (don't log this) 
     203    LOGERROR(L"%ls: pathname=%ls, error=%ls", message.c_str(), pathname.string().c_str(), ErrorString(err)); 
     204} 
     205 
     206void CSMSoundGroup::PlayNext(const CVector3D& position) 
     207{ 
     208    // if no sounds, return 
     209    if (filenames.size() == 0) 
     210        return; 
     211     
     212    m_index = (size_t)rand(0, (size_t)filenames.size()); 
     213    UploadPropertiesAndPlay( m_index, position); 
     214} 
     215 
     216void CSMSoundGroup::Reload() 
     217{ 
     218    m_index = 0; // reset our index 
     219 
     220    snd_group.clear(); 
     221 
     222    for (size_t i = 0; i < filenames.size(); i++) 
     223    { 
     224        VfsPath  thePath =  m_filepath/filenames[i]; 
     225        ISoundItem* temp = g_SoundManager->LoadItem( &thePath ); 
     226 
     227        if ( temp == NULL ) 
     228            HandleError( L"error loading sound", thePath, NULL); 
     229        else 
     230            snd_group.push_back(temp); 
     231    } 
     232 
     233    if (TestFlag(eRandOrder)) 
     234        random_shuffle(snd_group.begin(), snd_group.end()); 
     235} 
     236 
     237void CSMSoundGroup::ReleaseGroup() 
     238{ 
     239    for (size_t i = 0; i < snd_group.size(); i++) 
     240    { 
     241        snd_group[i]->FadeAndDelete(0.2); 
     242    } 
     243    snd_group.clear(); 
     244} 
     245 
     246void CSMSoundGroup::Update(float UNUSED(TimeSinceLastFrame)) 
     247{ 
     248} 
     249 
     250bool CSMSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML) 
     251{ 
     252//    LOGERROR(L"loading new sound group '%ls'", pathnameXML.string().c_str()); 
     253 
     254    CXeromyces XeroFile; 
     255    if (XeroFile.Load(g_VFS, pathnameXML) != PSRETURN_OK) 
     256    { 
     257        HandleError( L"error loading file", pathnameXML, NULL); 
     258        return false; 
     259    } 
     260    // Define elements used in XML file 
     261    #define EL(x) int el_##x = XeroFile.GetElementID(#x) 
     262    #define AT(x) int at_##x = XeroFile.GetAttributeID(#x) 
     263    EL(soundgroup); 
     264    EL(gain); 
     265    EL(looping); 
     266    EL(omnipresent); 
     267    EL(distanceless); 
     268    EL(pitch); 
     269    EL(priority); 
     270    EL(randorder); 
     271    EL(randgain); 
     272    EL(randpitch); 
     273    EL(conegain); 
     274    EL(coneinner); 
     275    EL(coneouter); 
     276    EL(sound); 
     277    EL(gainupper); 
     278    EL(gainlower); 
     279    EL(pitchupper); 
     280    EL(pitchlower); 
     281    EL(path); 
     282    EL(threshold); 
     283    EL(decay); 
     284    #undef AT 
     285    #undef EL 
     286 
     287    XMBElement root = XeroFile.GetRoot(); 
     288 
     289    if (root.GetNodeName() != el_soundgroup) 
     290    { 
     291        LOGERROR(L"Invalid SoundGroup format (unrecognised root element '%hs')", XeroFile.GetElementString(root.GetNodeName()).c_str()); 
     292        return false; 
     293    } 
     294     
     295    XERO_ITER_EL(root, child) 
     296    { 
     297     
     298        int child_name = child.GetNodeName();            
     299         
     300        if(child_name == el_gain) 
     301        { 
     302            SetGain(child.GetText().ToFloat()); 
     303        } 
     304        else if(child_name == el_looping) 
     305        { 
     306            if(child.GetText().ToInt() == 1) 
     307                SetFlag(eLoop); 
     308        } 
     309        else if(child_name == el_omnipresent) 
     310        { 
     311            if(child.GetText().ToInt() == 1) 
     312                SetFlag(eOmnipresent); 
     313        } 
     314        else if(child_name == el_distanceless) 
     315        { 
     316            if(child.GetText().ToInt() == 1) 
     317                SetFlag(eDistanceless); 
     318        } 
     319        else if(child_name == el_pitch) 
     320        { 
     321            this->m_Pitch = child.GetText().ToFloat(); 
     322        } 
     323        else if(child_name == el_priority) 
     324        { 
     325            this->m_Priority = child.GetText().ToFloat(); 
     326        } 
     327        else if(child_name == el_randorder) 
     328        { 
     329            if(child.GetText().ToInt() == 1) 
     330                SetFlag(eRandOrder); 
     331        } 
     332        else if(child_name == el_randgain) 
     333        { 
     334            if(child.GetText().ToInt() == 1) 
     335                SetFlag(eRandGain); 
     336        } 
     337        else if(child_name == el_gainupper) 
     338        { 
     339            this->m_GainUpper = child.GetText().ToFloat(); 
     340        } 
     341        else if(child_name == el_gainlower) 
     342        { 
     343            this->m_GainLower = child.GetText().ToFloat(); 
     344        } 
     345        else if(child_name == el_randpitch) 
     346        { 
     347            if(child.GetText().ToInt() == 1) 
     348                SetFlag(eRandPitch); 
     349        } 
     350        else if(child_name == el_pitchupper) 
     351        { 
     352            this->m_PitchUpper = child.GetText().ToFloat(); 
     353        } 
     354        else if(child_name == el_pitchlower) 
     355        { 
     356            this->m_PitchLower = child.GetText().ToFloat(); 
     357        } 
     358        else if(child_name == el_conegain) 
     359        { 
     360            this->m_ConeOuterGain = child.GetText().ToFloat(); 
     361        } 
     362        else if(child_name == el_coneinner) 
     363        { 
     364            this->m_ConeInnerAngle = child.GetText().ToFloat(); 
     365        } 
     366        else if(child_name == el_coneouter) 
     367        { 
     368            this->m_ConeOuterAngle = child.GetText().ToFloat(); 
     369        } 
     370        else if(child_name == el_sound) 
     371        { 
     372            this->filenames.push_back(child.GetText().FromUTF8()); 
     373        } 
     374        else if(child_name == el_path) 
     375        { 
     376            m_filepath = child.GetText().FromUTF8(); 
     377        } 
     378        else if(child_name == el_threshold) 
     379        { 
     380            m_IntensityThreshold = child.GetText().ToFloat(); 
     381        } 
     382        else if(child_name == el_decay) 
     383        { 
     384            m_Decay = child.GetText().ToFloat(); 
     385        } 
     386    } 
     387 
     388    return true; 
     389} 
  • source/soundmanager/js/JAmbientSound.h

     
     1/* Copyright (C) 2009 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18#ifndef INCLUDED_JAMBIENTSOUND 
     19#define INCLUDED_JAMBIENTSOUND 
     20 
     21#include "scripting/ScriptableObject.h" 
     22#include "soundmanager/items/ISoundItem.h" 
     23 
     24class JAmbientSound : public CJSObject<JAmbientSound> 
     25{ 
     26public:     
     27             JAmbientSound  (const VfsPath& pathname); 
     28    virtual ~JAmbientSound  (); 
     29 
     30     
     31    CStr ToString(JSContext* cx, uintN argc, jsval* argv); 
     32     
     33    bool Play(JSContext* cx, uintN argc, jsval* argv); 
     34     
     35    bool Loop(JSContext* cx, uintN argc, jsval* argv); 
     36    bool Free(JSContext* cx, uintN argc, jsval* argv); 
     37   
     38    bool SetGain(JSContext* cx, uintN argc, jsval* argv); 
     39    bool SetPitch(JSContext* cx, uintN argc, jsval* argv);     
     40    bool Fade(JSContext* cx, uintN argc, jsval* argv); 
     41     
     42    static JSBool Construct(JSContext* cx, uintN argc, jsval* vp); 
     43    void clearSoundItem(); 
     44    static void ScriptingInit(); 
     45protected: 
     46     
     47    VfsPath*    m_FileName; 
     48 
     49}; 
     50 
     51#endif  // #ifndef INCLUDED_JAMBIENTSOUND 
  • source/soundmanager/js/JSound.h

     
     1/* Copyright (C) 2009 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18// JS sound binding 
     19 
     20// interface rationale: 
     21// - can't just expose fire and forget playSound to script code: 
     22//   we sometimes need to loop until a certain condition is met 
     23//   (e.g. building is complete) => need means of access (Handle) to sound. 
     24// 
     25// - the current 64-bit Handle can't be stored as-is by JS code; 
     26//   we could make it 32 bit, but that limits its usefulness 
     27//   (barely enough tag bits). 
     28// 
     29// - instead, we provide a thin class wrapper (using scriptableobject.h) 
     30//   on top of the snd API that encapsulates the Handle. 
     31 
     32#ifndef INCLUDED_JSOUND 
     33#define INCLUDED_JSOUND 
     34 
     35#include "scripting/ScriptableObject.h" 
     36#include "soundmanager/items/ISoundItem.h" 
     37 
     38class JSound : public CJSObject<JSound> 
     39{ 
     40public: 
     41         
     42    // note: filename is stored by handle manager; no need to keep a copy here. 
     43     
     44             JSound(const VfsPath& pathname); 
     45    virtual ~JSound(); 
     46     
     47    CStr ToString(JSContext* cx, uintN argc, jsval* argv); 
     48     
     49    bool Play(JSContext* cx, uintN argc, jsval* argv); 
     50    bool Loop(JSContext* cx, uintN argc, jsval* argv); 
     51     
     52    bool Free(JSContext* cx, uintN argc, jsval* argv); 
     53    bool SetGain(JSContext* cx, uintN argc, jsval* argv); 
     54    bool SetPitch(JSContext* cx, uintN argc, jsval* argv); 
     55    bool SetPosition(JSContext* cx, uintN argc, jsval* argv); 
     56    bool ClearSoundItem(); 
     57     
     58    bool Fade(JSContext* cx, uintN argc, jsval* argv); 
     59     
     60    static JSBool Construct(JSContext* cx, uintN argc, jsval* vp); 
     61    static void ScriptingInit(); 
     62 
     63protected: 
     64    ISoundItem*     m_SndItem; 
     65}; 
     66 
     67#endif  // #ifndef INCLUDED_JSOUND 
  • source/soundmanager/js/JMusicSound.h

     
     1/* Copyright (C) 2009 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18// JS sound binding 
     19 
     20// interface rationale: 
     21// - can't just expose fire and forget playSound to script code: 
     22//   we sometimes need to loop until a certain condition is met 
     23//   (e.g. building is complete) => need means of access (Handle) to sound. 
     24// 
     25// - the current 64-bit Handle can't be stored as-is by JS code; 
     26//   we could make it 32 bit, but that limits its usefulness 
     27//   (barely enough tag bits). 
     28// 
     29// - instead, we provide a thin class wrapper (using scriptableobject.h) 
     30//   on top of the snd API that encapsulates the Handle. 
     31 
     32#ifndef INCLUDED_JMUSICSOUND 
     33#define INCLUDED_JMUSICSOUND 
     34 
     35#include "scripting/ScriptableObject.h" 
     36#include "soundmanager/items/ISoundItem.h" 
     37 
     38class JMusicSound : public CJSObject<JMusicSound> 
     39{ 
     40public:     
     41             JMusicSound(const VfsPath& pathname); 
     42    virtual ~JMusicSound(); 
     43     
     44    // Script-bound functions 
     45     
     46    CStr ToString(JSContext* cx, uintN argc, jsval* argv); 
     47     
     48    bool Play(JSContext* cx, uintN argc, jsval* argv); 
     49    bool Loop(JSContext* cx, uintN argc, jsval* argv); 
     50     
     51    static JSBool Construct(JSContext* cx, uintN argc, jsval* vp); 
     52 
     53    static void ScriptingInit(); 
     54     
     55protected: 
     56    VfsPath*    m_FileName; 
     57}; 
     58 
     59#endif  // #ifndef INCLUDED_JMUSICSOUND 
  • source/soundmanager/js/SMSoundGroup.h

     
     1/* Copyright (C) 2009 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18/** 
     19* ========================================================================= 
     20* File        : SoundGroup.h 
     21* Project     : 0 A.D. 
     22* Description : Loads up a group of sound files with shared properties,  
     23*               and provides a simple interface for playing them.         
     24* ========================================================================= 
     25*/ 
     26 
     27/* 
     28Example usage: 
     29 
     30 
     31Example SoundGroup.xml 
     32    <?xml version="1.0" encoding="utf-8"?> 
     33    <SoundGroup> 
     34        <Gain>1.0</Gain> 
     35        <Looping>0</Looping> 
     36        <Pitch>1.0</Pitch> 
     37        <Priority>100</Priority> 
     38        <RandOrder>0</RandOrder> 
     39        <RandGain>0</RandGain> 
     40        <RandPitch>0</RandPitch> 
     41        <ConeGain>1.0</ConeGain> 
     42        <ConeInner>360</ConeInner> 
     43        <ConeOuter>360</ConeOuter> 
     44        <Sound>audio/voice/hellenes/soldier/Attack_Attackx.ogg</Sound> 
     45        <Sound>audio/voice/hellenes/soldier/Attack_Chargex.ogg</Sound> 
     46        <Sound>audio/voice/hellenes/soldier/Attack_Engagex.ogg</Sound> 
     47        <Sound>audio/voice/hellenes/soldier/Attack_ForMyFamily.ogg</Sound> 
     48    </SoundGroup> 
     49 
     50*/ 
     51 
     52#ifndef INCLUDED_SMSOUNDGROUP 
     53#define INCLUDED_SMSOUNDGROUP 
     54 
     55#include "lib/file/vfs/vfs_path.h" 
     56 
     57#include <vector> 
     58 
     59class ISoundItem; 
     60 
     61enum eSndGrpFlags 
     62{ 
     63    eRandOrder     = 0x01, 
     64    eRandGain      = 0x02, 
     65    eRandPitch     = 0x04, 
     66    eLoop          = 0x08, 
     67    eOmnipresent   = 0x10, 
     68    eDistanceless  = 0x20 
     69}; 
     70 
     71 
     72class CSMSoundGroup 
     73{ 
     74    NONCOPYABLE(CSMSoundGroup); 
     75public: 
     76        CSMSoundGroup(const VfsPath& pathnameXML); 
     77    CSMSoundGroup(void); 
     78    ~CSMSoundGroup(void); 
     79 
     80    // Play next sound in group 
     81    // @param position world position of the entity generating the sound 
     82    // (ignored if the eOmnipresent flag is set) 
     83    void PlayNext(const CVector3D& position); 
     84 
     85    float RadiansOffCenter( const CVector3D& position, bool& onScreen, float& itemRollOff  ); 
     86 
     87    // Load a group 
     88    bool LoadSoundGroup(const VfsPath& pathnameXML); 
     89 
     90    void Reload(); 
     91 
     92    // Release all remaining loaded handles 
     93    void ReleaseGroup(); 
     94 
     95    // Update SoundGroup, remove dead sounds from intensity count 
     96    void Update(float TimeSinceLastFrame); 
     97 
     98    // Set a flag using a value from eSndGrpFlags 
     99    inline void SetFlag(int flag) { m_Flags = (unsigned char)(m_Flags | flag); } 
     100 
     101    // Test flag, returns true if flag is set. 
     102    inline bool TestFlag(int flag) { return (m_Flags & flag) != 0; } 
     103     
     104private: 
     105    void SetGain(float gain); 
     106    void UploadPropertiesAndPlay(int theIndex, const CVector3D& position); 
     107    void SetDefaultValues(); 
     108 
     109    size_t m_index;  // index of the next sound to play 
     110         
     111    std::vector<ISoundItem*> snd_group;  // we store the handles so we can load now and play later 
     112    std::vector<std::wstring> filenames; // we need the filenames so we can reload when necessary. 
     113 
     114    VfsPath m_filepath; // the file path for the list of sound file resources 
     115 
     116    float m_CurTime; // Time elapsed since soundgroup was created 
     117    float m_TimeWindow; // The Intensity Threshold Window 
     118    size_t m_IntensityThreshold; // the allowable intensity before a sound switch    
     119    size_t m_Intensity;  // our current intensity(number of sounds played since m_CurTime - m_TimeWindow)  
     120    float m_Decay; //  
     121    unsigned char m_Flags; // up to eight individual parameters, use with eSndGrpFlags. 
     122     
     123    float m_Gain;   
     124    float m_Pitch; 
     125    float m_Priority; 
     126    float m_ConeOuterGain;  
     127    float m_PitchUpper; 
     128    float m_PitchLower; 
     129    float m_GainUpper; 
     130    float m_GainLower; 
     131    float m_ConeInnerAngle; 
     132    float m_ConeOuterAngle; 
     133}; 
     134 
     135#endif //#ifndef INCLUDED_SOUNDGROUP 
  • source/sound/JSI_Sound.h

     
    1 /* Copyright (C) 2009 Wildfire Games. 
    2  * This file is part of 0 A.D. 
    3  * 
    4  * 0 A.D. is free software: you can redistribute it and/or modify 
    5  * it under the terms of the GNU General Public License as published by 
    6  * the Free Software Foundation, either version 2 of the License, or 
    7  * (at your option) any later version. 
    8  * 
    9  * 0 A.D. is distributed in the hope that it will be useful, 
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    12  * GNU General Public License for more details. 
    13  * 
    14  * You should have received a copy of the GNU General Public License 
    15  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
    16  */ 
    17  
    18 // JS sound binding 
    19  
    20 // interface rationale: 
    21 // - can't just expose fire and forget playSound to script code: 
    22 //   we sometimes need to loop until a certain condition is met 
    23 //   (e.g. building is complete) => need means of access (Handle) to sound. 
    24 // 
    25 // - the current 64-bit Handle can't be stored as-is by JS code; 
    26 //   we could make it 32 bit, but that limits its usefulness 
    27 //   (barely enough tag bits). 
    28 // 
    29 // - instead, we provide a thin class wrapper (using scriptableobject.h) 
    30 //   on top of the snd API that encapsulates the Handle. 
    31  
    32 #ifndef INCLUDED_JSI_SOUND 
    33 #define INCLUDED_JSI_SOUND 
    34  
    35 #include "scripting/ScriptableObject.h" 
    36 #include "lib/res/handle.h" 
    37  
    38 class JSI_Sound : public CJSObject<JSI_Sound> 
    39 { 
    40 public: 
    41  
    42     Handle m_Handle; 
    43  
    44     // note: filename is stored by handle manager; no need to keep a copy here. 
    45  
    46     JSI_Sound(const VfsPath& pathname); 
    47     ~JSI_Sound(); 
    48  
    49     // Script-bound functions 
    50  
    51     CStr ToString(JSContext* cx, uintN argc, jsval* argv); 
    52  
    53     // start playing the sound (one-shot). 
    54     // it will automatically be freed when done. 
    55     bool Play(JSContext* cx, uintN argc, jsval* argv); 
    56  
    57     // request the sound be played until free() is called. returns immediately. 
    58     bool Loop(JSContext* cx, uintN argc, jsval* argv); 
    59  
    60     // stop sound if currently playing and free resources. 
    61     // doesn't need to be called unless played via loop() - 
    62     // sounds are freed automatically when done playing. 
    63     bool Free(JSContext* cx, uintN argc, jsval* argv); 
    64  
    65     bool SetGain(JSContext* cx, uintN argc, jsval* argv); 
    66  
    67     bool SetPitch(JSContext* cx, uintN argc, jsval* argv); 
    68  
    69     bool SetPosition(JSContext* cx, uintN argc, jsval* argv); 
    70  
    71     bool Fade(JSContext* cx, uintN argc, jsval* argv); 
    72  
    73     static JSBool Construct(JSContext* cx, uintN argc, jsval* vp); 
    74  
    75     static void ScriptingInit(); 
    76 }; 
    77  
    78 #endif  // #ifndef INCLUDED_JSI_SOUND 
  • source/sound/SoundGroup.cpp

     
    1 /* Copyright (C) 2010 Wildfire Games. 
    2  * This file is part of 0 A.D. 
    3  * 
    4  * 0 A.D. is free software: you can redistribute it and/or modify 
    5  * it under the terms of the GNU General Public License as published by 
    6  * the Free Software Foundation, either version 2 of the License, or 
    7  * (at your option) any later version. 
    8  * 
    9  * 0 A.D. is distributed in the hope that it will be useful, 
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    12  * GNU General Public License for more details. 
    13  * 
    14  * You should have received a copy of the GNU General Public License 
    15  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
    16  */ 
    17  
    18 /** 
    19 * ========================================================================= 
    20 * File        : SoundGroup.cpp 
    21 * Project     : 0 A.D. 
    22 * Description : Loads up a group of sound files with shared properties,  
    23 *               and provides a simple interface for playing them.         
    24 * ========================================================================= 
    25 */ 
    26  
    27 #include "precompiled.h" 
    28 #include "SoundGroup.h" 
    29  
    30 #include <algorithm> 
    31  
    32 #include "lib/rand.h" 
    33  
    34 #include "ps/XML/Xeromyces.h" 
    35 #include "ps/CLogger.h" 
    36 #include "ps/Filesystem.h" 
    37 #include "ps/Util.h" 
    38  
    39  
    40 static const bool DISABLE_INTENSITY = true; // disable for now since it's broken 
    41  
    42 void CSoundGroup::SetGain(float gain) 
    43 { 
    44     gain = std::min(gain, 1.0f); 
    45     m_Gain = gain; 
    46 } 
    47  
    48 void CSoundGroup::SetDefaultValues() 
    49 { 
    50     m_index = 0; 
    51     m_Flags = 0; 
    52     m_Intensity = 0; 
    53     m_CurTime = 0.0f; 
    54  
    55     // sane defaults; will probably be replaced by the values read during LoadSoundGroup. 
    56     SetGain(0.5f); 
    57     m_Pitch = 1.0f; 
    58     m_Priority = 60; 
    59     m_PitchUpper = 1.1f; 
    60     m_PitchLower = 0.9f; 
    61     m_GainUpper = 1.0f; 
    62     m_GainLower = 0.8f; 
    63     m_ConeOuterGain = 0.0f; 
    64     m_ConeInnerAngle = 360.0f; 
    65     m_ConeOuterAngle = 360.0f; 
    66     m_Decay = 3.0f; 
    67     m_IntensityThreshold = 3; 
    68     // WARNING: m_TimeWindow is currently unused and uninitialized 
    69 } 
    70  
    71 CSoundGroup::CSoundGroup() 
    72 { 
    73     SetDefaultValues(); 
    74 } 
    75  
    76 CSoundGroup::CSoundGroup(const VfsPath& pathnameXML) 
    77 { 
    78     SetDefaultValues(); 
    79     LoadSoundGroup(pathnameXML); 
    80 } 
    81  
    82 CSoundGroup::~CSoundGroup() 
    83 { 
    84     // clean up all the handles from this group. 
    85     ReleaseGroup(); 
    86 } 
    87  
    88 static float RandFloat(float min, float max) 
    89 { 
    90     return float(rand(min*100.0f, max*100.0f) / 100.0f); 
    91 } 
    92  
    93 void CSoundGroup::UploadPropertiesAndPlay(Handle hSound, const CVector3D& position) 
    94 { 
    95     // interface/UI sounds should always be played at the listener's 
    96     // position, which is achieved by setting position to 0 and 
    97     // having that treated as relative to the listener. 
    98     float x = 0.0f, y = 0.0f, z = 0.0f; 
    99     bool relative = true; 
    100     if(!TestFlag(eOmnipresent)) 
    101     { 
    102         x = position.X; 
    103         y = position.Y; 
    104         z = position.Z; 
    105         relative = false; 
    106     } 
    107  
    108     snd_set_pos(hSound, x, y, z, relative); 
    109  
    110     float gain = TestFlag(eRandGain)? RandFloat(m_GainLower, m_GainUpper) : m_Gain; 
    111     gain = std::min(gain, 1.0f);    // guard against roundoff error in RandFloat or too high m_GainUpper 
    112     snd_set_gain(hSound, gain); 
    113  
    114     const float pitch = TestFlag(eRandPitch)? RandFloat(m_PitchLower, m_PitchUpper) : m_Pitch; 
    115     snd_set_pitch(hSound, pitch); 
    116  
    117     snd_play(hSound, m_Priority); 
    118 } 
    119  
    120  
    121 static void HandleError(const std::wstring& message, const VfsPath& pathname, Status err) 
    122 { 
    123     if(err == ERR::AGAIN) 
    124         return; // open failed because sound is disabled (don't log this) 
    125     LOGERROR(L"%ls: pathname=%ls, error=%ls", message.c_str(), pathname.string().c_str(), ErrorString(err)); 
    126 } 
    127  
    128 void CSoundGroup::PlayNext(const CVector3D& position) 
    129 { 
    130     if(m_Intensity >= m_IntensityThreshold && !DISABLE_INTENSITY) 
    131     { 
    132         if(!snd_is_playing(m_hReplacement)) 
    133         { 
    134             // load up replacement file 
    135             const VfsPath pathname(m_filepath / m_intensity_file); 
    136             m_hReplacement = snd_open(g_VFS, pathname); 
    137             if(m_hReplacement < 0) 
    138             { 
    139                 HandleError(L"PlayNext: snd_open for replacement file failed", pathname, (Status)m_hReplacement); 
    140                 return; 
    141             } 
    142  
    143             UploadPropertiesAndPlay(m_hReplacement, position); 
    144         } 
    145     } 
    146     else 
    147     { 
    148         // if no sounds, return 
    149         if (filenames.size() == 0) 
    150             return; 
    151  
    152         // try loading on the fly only when we need the sound to see if that fixes release problems... 
    153         if(TestFlag(eRandOrder)) 
    154             m_index = (size_t)rand(0, (size_t)filenames.size()); 
    155         // (note: previously snd_group[m_index] was used in place of hs) 
    156         const VfsPath pathname(m_filepath / filenames[m_index]); 
    157         Handle hs = snd_open(g_VFS, pathname); 
    158         if(hs < 0) 
    159         { 
    160             HandleError(L"PlayNext: snd_open failed", pathname, (Status)hs); 
    161             return; 
    162         } 
    163  
    164         UploadPropertiesAndPlay(hs, position); 
    165     } 
    166      
    167     playtimes.at(m_index) = 0.0f; 
    168     m_index++; 
    169     m_Intensity++; 
    170     if(m_Intensity > m_IntensityThreshold) 
    171         m_Intensity = m_IntensityThreshold; 
    172  
    173     if(m_index >= filenames.size()) 
    174         Reload(); 
    175 } 
    176  
    177 void CSoundGroup::Reload() 
    178 { 
    179     m_index = 0; // reset our index 
    180     // get rid of the used handles 
    181     snd_group.clear(); 
    182     // clear out the old timers 
    183     playtimes.clear(); 
    184     //Reload the sounds 
    185     /*for(size_t i = 0; i < filenames.size(); i++) 
    186     { 
    187         string szTemp = m_filepath + filenames[i]; 
    188         Handle temp = snd_open(m_filepath + filenames[i]); 
    189         snd_set_gain(temp, m_Gain);  
    190         snd_set_pitch(temp, m_Pitch); 
    191         snd_set_cone(temp, m_ConeInnerAngle, m_ConeOuterAngle, m_ConeOuterGain); 
    192         snd_group.push_back(temp); 
    193  
    194     }*/ 
    195     while(playtimes.size() < filenames.size()) 
    196         playtimes.push_back(-1.0f); 
    197     //if(TestFlag(eRandOrder)) 
    198         //random_shuffle(snd_group.begin(), snd_group.end()); 
    199 } 
    200  
    201 void CSoundGroup::ReleaseGroup() 
    202 { 
    203     for(size_t i = m_index; i<snd_group.size(); i++) 
    204     { 
    205         //if(!snd_is_playing(snd_group[i])) 
    206             snd_free(snd_group[i]); 
    207     } 
    208     snd_group.clear(); 
    209     playtimes.clear(); 
    210     //if(snd_is_playing(m_hReplacement)) 
    211     //  snd_free(m_hReplacement); 
    212     m_index = 0; 
    213  
    214 } 
    215  
    216 void CSoundGroup::Update(float TimeSinceLastFrame) 
    217 { 
    218     for(size_t i = 0; i < playtimes.size(); i++) 
    219     { 
    220         if(playtimes[i] >= 0.0f) 
    221             playtimes[i] += TimeSinceLastFrame; 
    222  
    223         if(playtimes[i] >= m_Decay) 
    224         { 
    225             playtimes[i] = -1.0f; 
    226             m_Intensity--; 
    227         }    
    228     } 
    229 } 
    230  
    231 bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML) 
    232 { 
    233     CXeromyces XeroFile; 
    234     if (XeroFile.Load(g_VFS, pathnameXML) != PSRETURN_OK) 
    235         return false; 
    236  
    237     // Define elements used in XML file 
    238     #define EL(x) int el_##x = XeroFile.GetElementID(#x) 
    239     #define AT(x) int at_##x = XeroFile.GetAttributeID(#x) 
    240     EL(soundgroup); 
    241     EL(gain); 
    242     EL(looping); 
    243     EL(omnipresent); 
    244     EL(pitch); 
    245     EL(priority); 
    246     EL(randorder); 
    247     EL(randgain); 
    248     EL(randpitch); 
    249     EL(conegain); 
    250     EL(coneinner); 
    251     EL(coneouter); 
    252     EL(sound); 
    253     EL(gainupper); 
    254     EL(gainlower); 
    255     EL(pitchupper); 
    256     EL(pitchlower); 
    257     EL(path); 
    258     EL(threshold); 
    259     EL(decay); 
    260     EL(replacement); 
    261     #undef AT 
    262     #undef EL 
    263  
    264     XMBElement root = XeroFile.GetRoot(); 
    265  
    266     if (root.GetNodeName() != el_soundgroup) 
    267     { 
    268         LOGERROR(L"Invalid SoundGroup format (unrecognised root element '%hs')", XeroFile.GetElementString(root.GetNodeName()).c_str()); 
    269         return false; 
    270     } 
    271      
    272     XERO_ITER_EL(root, child) 
    273     { 
    274      
    275         int child_name = child.GetNodeName();            
    276          
    277         if(child_name == el_gain) 
    278         { 
    279             SetGain(child.GetText().ToFloat()); 
    280         } 
    281         else if(child_name == el_looping) 
    282         { 
    283             if(child.GetText().ToInt() == 1) 
    284                 SetFlag(eLoop); 
    285         } 
    286         else if(child_name == el_omnipresent) 
    287         { 
    288             if(child.GetText().ToInt() == 1) 
    289                 SetFlag(eOmnipresent); 
    290         } 
    291         else if(child_name == el_pitch) 
    292         { 
    293             this->m_Pitch = child.GetText().ToFloat(); 
    294         } 
    295         else if(child_name == el_priority) 
    296         { 
    297             this->m_Priority = child.GetText().ToFloat(); 
    298         } 
    299         else if(child_name == el_randorder) 
    300         { 
    301             if(child.GetText().ToInt() == 1) 
    302                 SetFlag(eRandOrder); 
    303         } 
    304         else if(child_name == el_randgain) 
    305         { 
    306             if(child.GetText().ToInt() == 1) 
    307                 SetFlag(eRandGain); 
    308         } 
    309         else if(child_name == el_gainupper) 
    310         { 
    311             this->m_GainUpper = child.GetText().ToFloat(); 
    312         } 
    313         else if(child_name == el_gainlower) 
    314         { 
    315             this->m_GainLower = child.GetText().ToFloat(); 
    316         } 
    317         else if(child_name == el_randpitch) 
    318         { 
    319             if(child.GetText().ToInt() == 1) 
    320                 SetFlag(eRandPitch); 
    321         } 
    322         else if(child_name == el_pitchupper) 
    323         { 
    324             this->m_PitchUpper = child.GetText().ToFloat(); 
    325         } 
    326         else if(child_name == el_pitchlower) 
    327         { 
    328             this->m_PitchLower = child.GetText().ToFloat(); 
    329         } 
    330         else if(child_name == el_conegain) 
    331         { 
    332             this->m_ConeOuterGain = child.GetText().ToFloat(); 
    333         } 
    334         else if(child_name == el_coneinner) 
    335         { 
    336             this->m_ConeInnerAngle = child.GetText().ToFloat(); 
    337         } 
    338         else if(child_name == el_coneouter) 
    339         { 
    340             this->m_ConeOuterAngle = child.GetText().ToFloat(); 
    341         } 
    342         else if(child_name == el_sound) 
    343         { 
    344             this->filenames.push_back(child.GetText().FromUTF8()); 
    345         } 
    346         else if(child_name == el_path) 
    347         { 
    348             m_filepath = child.GetText().FromUTF8(); 
    349         } 
    350         else if(child_name == el_threshold) 
    351         { 
    352             m_IntensityThreshold = child.GetText().ToFloat(); 
    353         } 
    354         else if(child_name == el_decay) 
    355         { 
    356             m_Decay = child.GetText().ToFloat(); 
    357         } 
    358         else if(child_name == el_replacement) 
    359         { 
    360             m_intensity_file = child.GetText().FromUTF8(); 
    361         } 
    362     } 
    363  
    364     Reload(); 
    365     return true; 
    366 } 
  • source/sound/ambient_design.txt

     
    1 PLAYING AMBIENT SOUND WHEN CAMERA IS OVER BUILDING 
    2 ================================================== 
    3  
    4 data flow:  
    5 C++                             JS 
    6 > list of visible entities (1) 
    7                                 < list of sound requests and their weights 
    8 list of changed sounds 
    9  
    10 C++ code generates list of entities on screen 
    11     rationale: done in C++ for performance and to avoid having to expose the "patch" subdivision scheme to JS. 
    12     UPDATE: this is much thornier than expected. 
    13     1) we only want stuff that is visible to be played. hearing things off screen would be distressing and weird 
    14     2) don't play sound for buildings in FoW, to prevent "audio spying" 
    15     most straightforward way is simply scan all entities; if building, check against visible frustum. 
    16         if inside AND within 3d distance cutoff of viewer pos, add to visible_building 
    17     optimization for later: get all patches within 3d distance cutoff; discard all not within frustum; only test 
    18         entities within those remaining patches. 
    19 pass that to JS decideWhatToPlay 
    20 it returns list of sounds it wants playing 
    21     format: see AmbientSoundReq below 
    22 C++ sound engine compares its list of currently sounds; 
    23     fades out those no longer wanted and starts new ones immediately 
    24  
    25  
    26 new entity properties needed: 
    27 ----------------------------- 
    28 - ambientGroup: a soundGroup of several sounds; one is picked at random to be played (see below) 
    29 - priority: to determine which entity should trump the others in a crowded city. 
    30  
    31  
    32 // weight is so that we can play several ambient sounds at a time, but ones for buildings at edge of screen are quieter 
    33 // values: 0..1; probably calculated from distance to camera 
    34 type AmbientSoundReq = (SoundGroupString, weight) 
    35  
    36 // (suggestion only; may be revised if too much cacophony/constant sound results) 
    37 JS_decideWhatToPlay(in HEntity visible_ents[], out AmbientSoundReq desired_ambient_sounds[]) 
    38 { 
    39 // * "important" means the building is at center of attention and should mostly override the other sounds. 
    40 // this is complicated as well. the camera may have any orientation, especially in cinematic mode; 
    41 // that means we can't just rely on casting a ray from viewer through middle screen pixel to terrain. 
    42 // our building list already covers only the visible ones, so we can use 3d distance from viewer 
    43 // (without worrying about including buildings behind us i.e. out of sight). 
    44 // important := fairly small distance (covers the case where users zoom really close to a building) OR 
    45 // building is close to center of screen (as determined via ray cast method; skip this if the camera 
    46 // is weird, i.e. looking above the horizon, in which case the ray cast would fail). 
    47 // note: not playing sounds when looking down vertically at the town but zoomed out very far is ok (desirable even). 
    48     if an entity is important(*) 
    49         desired_ambient_sounds[i++] = ent.ambientGroup 
    50  
    51     forall ambient types (farm, dock etc.) in decreasing order of priority 
    52         stop if >= 3 sounds in list 
    53         if enough buildings on screen 
    54             desired_ambient_sounds[i++] = ent.ambientGroup 
    55  
    56 maybe add some random variation to spice things up? (i.e. also play some other building sounds; 
    57 don't always have one building override the others) 
    58 } 
    59  
    60  
    61 C++ code keeps a list of all active sounds: 
    62 type ActiveSound = (Handle, SoundGroupString) 
    63 ActiveSound active_sound_list[] 
    64  
    65 Handle startPlayingAmbient(soundGroup, weight) 
    66 { 
    67     filename = soundGroup.pickRandom() 
    68     return snd_play(filename, globalAmbientVolume*soundGroup.volume*weight) 
    69 } 
    70  
    71 updateAmbientSounds(desired_ambient_sounds) 
    72 { 
    73     for all in desired_ambient_sounds but not active_sound_list: 
    74         handle = startPlayingAmbient(amb.group, amb.weight) 
    75         active_sound_list[i++] = (handle, amb.group) 
    76  
    77     for all in active_sound_list but not desired_ambient_sounds: 
    78         snd_fade(OUT, active.handle) 
    79 } 
    80  
    81  
    82  
    83 RANDOMIZED SOUNDS 
    84 ================================================== 
    85 (needed for ambient and normal battle sounds) 
    86  No newline at end of file 
  • source/sound/SoundGroup.h

     
    1 /* Copyright (C) 2009 Wildfire Games. 
    2  * This file is part of 0 A.D. 
    3  * 
    4  * 0 A.D. is free software: you can redistribute it and/or modify 
    5  * it under the terms of the GNU General Public License as published by 
    6  * the Free Software Foundation, either version 2 of the License, or 
    7  * (at your option) any later version. 
    8  * 
    9  * 0 A.D. is distributed in the hope that it will be useful, 
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    12  * GNU General Public License for more details. 
    13  * 
    14  * You should have received a copy of the GNU General Public License 
    15  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
    16  */ 
    17  
    18 /** 
    19 * ========================================================================= 
    20 * File        : SoundGroup.h 
    21 * Project     : 0 A.D. 
    22 * Description : Loads up a group of sound files with shared properties,  
    23 *               and provides a simple interface for playing them.         
    24 * ========================================================================= 
    25 */ 
    26  
    27 /* 
    28 Example usage: 
    29  
    30  
    31 Example SoundGroup.xml 
    32     <?xml version="1.0" encoding="utf-8"?> 
    33     <SoundGroup> 
    34         <Gain>1.0</Gain> 
    35         <Looping>0</Looping> 
    36         <Pitch>1.0</Pitch> 
    37         <Priority>100</Priority> 
    38         <RandOrder>0</RandOrder> 
    39         <RandGain>0</RandGain> 
    40         <RandPitch>0</RandPitch> 
    41         <ConeGain>1.0</ConeGain> 
    42         <ConeInner>360</ConeInner> 
    43         <ConeOuter>360</ConeOuter> 
    44         <Sound>audio/voice/hellenes/soldier/Attack_Attackx.ogg</Sound> 
    45         <Sound>audio/voice/hellenes/soldier/Attack_Chargex.ogg</Sound> 
    46         <Sound>audio/voice/hellenes/soldier/Attack_Engagex.ogg</Sound> 
    47         <Sound>audio/voice/hellenes/soldier/Attack_ForMyFamily.ogg</Sound> 
    48     </SoundGroup> 
    49  
    50 */ 
    51  
    52 #ifndef INCLUDED_SOUNDGROUP 
    53 #define INCLUDED_SOUNDGROUP 
    54  
    55 #include "lib/res/handle.h" 
    56 #include "lib/file/vfs/vfs_path.h" 
    57 #include "ps/CStr.h" 
    58 #include "maths/Vector3D.h" 
    59 #include "lib/res/sound/snd_mgr.h" 
    60  
    61 #include <vector> 
    62  
    63 enum eSndGrpFlags 
    64 { 
    65     eRandOrder     = 0x01, 
    66     eRandGain      = 0x02, 
    67     eRandPitch     = 0x04, 
    68     eLoop          = 0x08, 
    69     eOmnipresent   = 0x10 
    70 }; 
    71  
    72  
    73 class CSoundGroup 
    74 { 
    75     NONCOPYABLE(CSoundGroup); 
    76 public: 
    77     CSoundGroup(const VfsPath& pathnameXML); 
    78     CSoundGroup(void); 
    79     ~CSoundGroup(void); 
    80  
    81     // Play next sound in group 
    82     // @param position world position of the entity generating the sound 
    83     // (ignored if the eOmnipresent flag is set) 
    84     void PlayNext(const CVector3D& position); 
    85  
    86     // Load a group 
    87     bool LoadSoundGroup(const VfsPath& pathnameXML); 
    88  
    89     void Reload(); 
    90  
    91     // Release all remaining loaded handles 
    92     void ReleaseGroup(); 
    93  
    94     // Update SoundGroup, remove dead sounds from intensity count 
    95     void Update(float TimeSinceLastFrame); 
    96  
    97     // Set a flag using a value from eSndGrpFlags 
    98     inline void SetFlag(int flag) { m_Flags = (unsigned char)(m_Flags | flag); } 
    99  
    100     // Test flag, returns true if flag is set. 
    101     inline bool TestFlag(int flag) { return (m_Flags & flag) != 0; } 
    102      
    103 private: 
    104     void SetGain(float gain); 
    105     void UploadPropertiesAndPlay(Handle hSound, const CVector3D& position); 
    106     void SetDefaultValues(); 
    107  
    108     size_t m_index;  // index of the next sound to play 
    109      
    110     Handle m_hReplacement; 
    111      
    112     std::vector<Handle> snd_group;  // we store the handles so we can load now and play later 
    113     std::vector<std::wstring> filenames; // we need the filenames so we can reload when necessary. 
    114     std::vector<float> playtimes; // it would be better to store this in with the Handles perhaps? 
    115     VfsPath m_filepath; // the file path for the list of sound file resources 
    116     std::wstring m_intensity_file; // the replacement aggregate 'intense' sound 
    117  
    118     float m_CurTime; // Time elapsed since soundgroup was created 
    119     float m_TimeWindow; // The Intensity Threshold Window 
    120     size_t m_IntensityThreshold; // the allowable intensity before a sound switch    
    121     size_t m_Intensity;  // our current intensity(number of sounds played since m_CurTime - m_TimeWindow)  
    122     float m_Decay; //  
    123     unsigned char m_Flags; // up to eight individual parameters, use with eSndGrpFlags. 
    124      
    125     float m_Gain;   
    126     float m_Pitch; 
    127     float m_Priority; 
    128     float m_ConeOuterGain;  
    129     float m_PitchUpper; 
    130     float m_PitchLower; 
    131     float m_GainUpper; 
    132     float m_GainLower; 
    133     float m_ConeInnerAngle; 
    134     float m_ConeOuterAngle; 
    135 }; 
    136  
    137 #endif //#ifndef INCLUDED_SOUNDGROUP 
  • source/sound/JSI_Sound.cpp

     
    1 /* Copyright (C) 2009 Wildfire Games. 
    2  * This file is part of 0 A.D. 
    3  * 
    4  * 0 A.D. is free software: you can redistribute it and/or modify 
    5  * it under the terms of the GNU General Public License as published by 
    6  * the Free Software Foundation, either version 2 of the License, or 
    7  * (at your option) any later version. 
    8  * 
    9  * 0 A.D. is distributed in the hope that it will be useful, 
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    12  * GNU General Public License for more details. 
    13  * 
    14  * You should have received a copy of the GNU General Public License 
    15  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
    16  */ 
    17  
    18 #include "precompiled.h" 
    19 #include "JSI_Sound.h" 
    20 #include "maths/Vector3D.h" 
    21  
    22 #include "lib/utf8.h" 
    23 #include "lib/res/sound/snd_mgr.h" 
    24 #include "lib/res/h_mgr.h"  // h_filename 
    25 #include "ps/Filesystem.h" 
    26  
    27  
    28 JSI_Sound::JSI_Sound(const VfsPath& pathname) 
    29 { 
    30     m_Handle = snd_open(g_VFS, pathname); 
    31  
    32     // if open failed, we still have to return a valid non-null object to 
    33     // the script, so just reset the handle to 0 so all subsequent method 
    34     // calls will do nothing 
    35     if (m_Handle < 0) 
    36     { 
    37         m_Handle = 0; 
    38         return; 
    39     } 
    40  
    41     (void)snd_set_pos(m_Handle, 0,0,0, true); 
    42 } 
    43  
    44 JSI_Sound::~JSI_Sound() 
    45 { 
    46     (void)this->Free(0, 0, 0); 
    47 } 
    48  
    49  
    50 bool JSI_Sound::SetGain(JSContext* cx, uintN argc, jsval* argv) 
    51 { 
    52     if (! m_Handle) 
    53         return false; 
    54  
    55     ENSURE(argc >= 1); // FIXME 
    56     float gain; 
    57     if (! ToPrimitive<float>(cx, argv[0], gain)) 
    58         return false; 
    59  
    60     (void)snd_set_gain(m_Handle, gain); 
    61     return true; 
    62 } 
    63  
    64 bool JSI_Sound::SetPitch(JSContext* cx, uintN argc, jsval* argv) 
    65 { 
    66     if (! m_Handle) 
    67         return false; 
    68  
    69     ENSURE(argc >= 1); // FIXME 
    70     float pitch; 
    71     if (! ToPrimitive<float>(cx, argv[0], pitch)) 
    72         return false; 
    73  
    74     (void)snd_set_pitch(m_Handle, pitch); 
    75     return true; 
    76 } 
    77  
    78 bool JSI_Sound::SetPosition(JSContext* cx, uintN argc, jsval* argv) 
    79 { 
    80     if (! m_Handle) 
    81         return false; 
    82  
    83     ENSURE(argc >= 1); // FIXME 
    84  
    85     CVector3D pos; 
    86     // absolute world coords 
    87     if (ToPrimitive<CVector3D>(cx, argv[0], pos)) 
    88         (void)snd_set_pos(m_Handle, pos[0], pos[1], pos[2]); 
    89     // relative, 0 offset - right on top of the listener 
    90     // (we don't need displacement from the listener, e.g. always behind) 
    91     else 
    92         (void)snd_set_pos(m_Handle, 0,0,0, true); 
    93  
    94     return true; 
    95 } 
    96  
    97  
    98 bool JSI_Sound::Fade(JSContext* cx, uintN argc, jsval* argv) 
    99 { 
    100     if (! m_Handle) 
    101         return false; 
    102  
    103     ENSURE(argc >= 3); // FIXME 
    104     float initial_gain, final_gain; 
    105     float length; 
    106     if (! (ToPrimitive<float>(cx, argv[0], initial_gain) 
    107         && ToPrimitive<float>(cx, argv[1], final_gain) 
    108         && ToPrimitive<float>(cx, argv[2], length))) 
    109         return false; 
    110  
    111     (void)snd_fade(m_Handle, initial_gain, final_gain, length, FT_S_CURVE); 
    112  
    113     // HACK: snd_fade causes <m_Handle> to be automatically freed when a 
    114     // fade to 0 has completed. however, we're still holding on to a 
    115     // reference, which will cause a double-free warning when Free() is 
    116     // called from the dtor. solution is to neuter our Handle by 
    117     // setting it to 0 (ok since it'll be freed). this does mean that 
    118     // no further operations can be carried out during that final fade. 
    119     if (final_gain == 0.0f) 
    120         m_Handle = 0; 
    121  
    122     return true; 
    123 } 
    124  
    125 // start playing the sound (one-shot). 
    126 // it will automatically be freed when done. 
    127 bool JSI_Sound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
    128 { 
    129     if (! m_Handle) 
    130         return false; 
    131  
    132     (void)snd_play(m_Handle); 
    133     // We can't do anything else with this sound now, since it's impossible to 
    134     // know whether or not it's still valid (since it might have finished playing 
    135     // already). So set it to 0, so we don't try doing anything (like freeing it) 
    136     // in the future. 
    137     m_Handle = 0; 
    138     return true; 
    139 } 
    140  
    141 // request the sound be played until free() is called. returns immediately. 
    142 bool JSI_Sound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
    143 { 
    144     if (! m_Handle) 
    145         return false; 
    146  
    147     (void)snd_set_loop(m_Handle, true); 
    148     (void)snd_play(m_Handle); 
    149     return true; 
    150 } 
    151  
    152 // stop sound if currently playing and free resources. 
    153 // doesn't need to be called unless played via loop() - 
    154 // sounds are freed automatically when done playing. 
    155 bool JSI_Sound::Free(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
    156 { 
    157     if (! m_Handle) 
    158         return false; 
    159  
    160     (void)snd_free(m_Handle);   // resets it to 0 
    161     return true; 
    162 } 
    163  
    164  
    165 // Script-bound functions 
    166  
    167  
    168 void JSI_Sound::ScriptingInit() 
    169 { 
    170     AddMethod<CStr, &JSI_Sound::ToString>("toString", 0); 
    171     AddMethod<bool, &JSI_Sound::Play>("play", 0); 
    172     AddMethod<bool, &JSI_Sound::Loop>("loop", 0); 
    173     AddMethod<bool, &JSI_Sound::Free>("free", 0); 
    174     AddMethod<bool, &JSI_Sound::SetGain>("setGain", 0); 
    175     AddMethod<bool, &JSI_Sound::SetPitch>("setPitch", 0); 
    176     AddMethod<bool, &JSI_Sound::SetPosition>("setPosition", 0); 
    177     AddMethod<bool, &JSI_Sound::Fade>("fade", 0); 
    178  
    179     CJSObject<JSI_Sound>::ScriptingInit("Sound", &JSI_Sound::Construct, 1); 
    180 } 
    181  
    182 CStr JSI_Sound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv)) 
    183 { 
    184     return "[object Sound: " + (m_Handle ? utf8_from_wstring(h_filename(m_Handle).string()) : "(null)") + "]"; 
    185 } 
    186  
    187 JSBool JSI_Sound::Construct(JSContext* cx, uintN argc, jsval* vp) 
    188 { 
    189     JSU_REQUIRE_MIN_PARAMS(1); 
    190  
    191     CStrW filename; 
    192     if (! ToPrimitive<CStrW>(cx, JS_ARGV(cx, vp)[0], filename)) 
    193         return JS_FALSE; 
    194  
    195     JSI_Sound* newObject = new JSI_Sound(filename); 
    196     newObject->m_EngineOwned = false; 
    197     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript())); 
    198  
    199     return JS_TRUE; 
    200 } 
  • source/main.cpp

     
    372372        g_Game->Update(TimeSinceLastFrame); 
    373373 
    374374        g_Game->GetView()->Update(float(TimeSinceLastFrame)); 
    375  
    376         CCamera* camera = g_Game->GetView()->GetCamera(); 
    377         CMatrix3D& orientation = camera->m_Orientation; 
    378         float* pos = &orientation._data[12]; 
    379         float* dir = &orientation._data[8]; 
    380         float* up  = &orientation._data[4]; 
    381         // HACK: otherwise sound effects are L/R flipped. No idea what else 
    382         // is going wrong, because the listener and camera are supposed to 
    383         // coincide in position and orientation. 
    384         float down[3] = { -up[0], -up[1], -up[2] }; 
    385  
    386         { 
    387             PROFILE3("sound update"); 
    388             if (snd_update(pos, dir, down) < 0) 
    389                 debug_printf(L"snd_update failed\n"); 
    390         } 
    391375    } 
    392     else 
    393     { 
    394         PROFILE3("sound update (0)"); 
    395         if (snd_update(0, 0, 0) < 0) 
    396             debug_printf(L"snd_update (pos=0 version) failed\n"); 
    397     } 
    398376 
    399377    // Immediately flush any messages produced by simulation code 
    400378    if (g_NetClient) 
     
    477455    // run non-visual simulation replay if requested 
    478456    if (args.Has("replay")) 
    479457    { 
    480         snd_disable(true); 
    481  
    482458        Paths paths(args); 
    483459        g_VFS = CreateVfs(20 * MiB); 
    484460        g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); 
  • source/lib/res/sound/snd_mgr.h

     
    1 /* Copyright (c) 2010 Wildfire Games 
    2  * 
    3  * Permission is hereby granted, free of charge, to any person obtaining 
    4  * a copy of this software and associated documentation files (the 
    5  * "Software"), to deal in the Software without restriction, including 
    6  * without limitation the rights to use, copy, modify, merge, publish, 
    7  * distribute, sublicense, and/or sell copies of the Software, and to 
    8  * permit persons to whom the Software is furnished to do so, subject to 
    9  * the following conditions: 
    10  *  
    11  * The above copyright notice and this permission notice shall be included 
    12  * in all copies or substantial portions of the Software. 
    13  *  
    14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
    15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
    16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
    17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
    18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
    19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
    20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
    21  */ 
    22  
    23 /* 
    24  * OpenAL sound engine. handles sound I/O, buffer suballocation and 
    25  * voice management/prioritization. 
    26  */ 
    27  
    28 #ifndef INCLUDED_SND_MGR 
    29 #define INCLUDED_SND_MGR 
    30  
    31 #include "lib/res/handle.h" 
    32 #include "lib/file/vfs/vfs.h" 
    33  
    34 /** 
    35  
    36 overview 
    37 -------- 
    38  
    39 this module provides a moderately high-level sound interface. basic usage 
    40 is opening a sound and requesting it be played; it is closed automatically 
    41 when playback has finished (fire and forget). 
    42 any number of sound play requests may be issued; the most 'important' ones 
    43 are actually played (necessary due to limited hardware mixing capacity). 
    44 3d positional sounds (heard to emanate from a given spot) are supported. 
    45 active sound instances are referenced by Handles, so changing volume etc. 
    46 during playback is possible (useful for fadeout). 
    47  
    48  
    49 sound setup 
    50 ----------- 
    51  
    52 OpenAL provides portable access to the underlying sound hardware, and 
    53 falls back to software mixing if no acceleration is provided. 
    54 we allow the user to specify the device to use (in case the default 
    55 has problems) and maximum number of sources (to reduce mixing cost). 
    56  
    57  
    58 performance 
    59 ----------- 
    60 much effort has been invested in efficiency: all sound data is cached, 
    61 so every open() after the first is effectively free. large sound files are 
    62 streamed from disk to reduce load time and memory usage. hardware mixing 
    63 resources are suballocated to avoid delays when starting to play. 
    64 therefore, the user can confidently fire off hundreds of sound requests. 
    65 finally, lengthy initialization steps are delayed until the sound engine 
    66 is actually needed (i.e. upon first open()). perceived startup time is 
    67 therefore reduced - the user sees e.g. our main menu earlier. 
    68  
    69  
    70 terminology 
    71 ----------- 
    72  
    73 "hardware voice" refers to mixing resources on the DSP. strictly 
    74   speaking, we mean 'OpenAL source', but this term is more clear. 
    75   voice ~= source, unless expensive effects (e.g. EAX) are enabled. 
    76   note: software mixing usually doesn't have a fixed 'source' cap. 
    77 "gain" is quantified volume. 1 is unattenuated, 0.5 corresponds to -6 dB, 
    78   and 0 is silence. this can be set per-source as well as globally. 
    79 "position" of a sound is within the app's coordinate system, 
    80   the orientation of which is passed to snd_update. 
    81 "importance" of a sound derives from the app-assigned priority 
    82   (e.g. voiceover must not be skipped in favor of seagulls) and 
    83   distance from the listener. it's calculated by our prioritizer. 
    84 "virtual source" denotes a sound play request issued by the app. 
    85   this is in contrast to an actual AL source, which will be mixed 
    86   into the output channel. the most important VSrc receive an al_src. 
    87 "sound instances" store playback parameters (e.g. position), and 
    88   reference the (centrally cached) "sound data" that will be played. 
    89  
    90 **/ 
    91  
    92  
    93 // 
    94 // device enumeration 
    95 // 
    96  
    97 /** 
    98  * prepare to enumerate all device names (this resets the list returned by 
    99  * snd_dev_next). 
    100  * may be called each time the device list is needed. 
    101  * 
    102  * @return Status; fails iff the requisite OpenAL extension isn't available. 
    103  * in that case, a "cannot enum device" message should be displayed, but 
    104  * snd_dev_set need not be called; OpenAL will use its default device. 
    105  **/ 
    106 extern Status snd_dev_prepare_enum(); 
    107  
    108 /** 
    109  * get next device name in list. 
    110  * 
    111  * do not call unless snd_dev_prepare_enum succeeded! 
    112  * not thread-safe! (static data from snd_dev_prepare_enum is used) 
    113  * 
    114  * @return device name string, or 0 if all have been returned. 
    115  **/ 
    116 extern const char* snd_dev_next(); 
    117  
    118  
    119 // 
    120 // sound system setup 
    121 // 
    122  
    123 /** 
    124  * tell OpenAL to use the specified device in future. 
    125  * 
    126  * @param alc_new_dev_name device name string. if 0, revert to 
    127  * OpenAL's default choice, which will also be used if 
    128  * this routine is never called. 
    129  * the device name is typically taken from a config file at init-time; 
    130  * the snd_dev* enumeration routines above are used to present a list 
    131  * of choices to the user in the options screen. 
    132  * 
    133  * if OpenAL hasn't yet been initialized (i.e. no sounds have been opened), 
    134  *   this just stores the device name for use when init does occur. 
    135  *   note: we can't check now if it's invalid (if so, init will fail). 
    136  * otherwise, we shut OpenAL down (thereby stopping all sounds) and 
    137  * re-initialize with the new device. that's fairly time-consuming, 
    138  * so preferably call this routine before sounds are loaded. 
    139  * 
    140  * @return Status (the status returned by OpenAL re-init) 
    141  **/ 
    142 extern Status snd_dev_set(const char* alc_new_dev_name); 
    143  
    144 /** 
    145  * Set maximum number of voices to play simultaneously; 
    146  * this can be used to reduce mixing cost on low-end systems. 
    147  * 
    148  * @param limit Maximum number of voices. Ignored if higher than 
    149  *        an implementation-defined limit anyway. 
    150  * @return Status 
    151  **/ 
    152 extern Status snd_set_max_voices(size_t limit); 
    153  
    154 /** 
    155  * set amplitude modifier, which is effectively applied to all sounds. 
    156  * this is akin to a global "volume" control. 
    157  * 
    158  * @param gain amplitude modifier. must be non-negative; 
    159  * 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence. 
    160  * @return Status 
    161  **/ 
    162 extern Status snd_set_master_gain(float gain); 
    163  
    164  
    165 // 
    166 // sound instance 
    167 // 
    168  
    169 /** 
    170  * Open and return a handle to a sound instance. 
    171  * This loads the sound data and makes it ready for other snd_* APIs. 
    172  * 
    173  * @param vfs 
    174  * @param pathname If a text file (extension ".txt"), it is 
    175  *        assumed to be a definition file containing the sound file name and 
    176  *        its gain (0.0 .. 1.0). 
    177  *        Otherwise, it is taken to be the sound file name and 
    178  *        gain is set to the default of 1.0 (no attenuation). 
    179  * @return Handle or Status 
    180  **/ 
    181 extern Handle snd_open(const PIVFS& vfs, const VfsPath& pathname); 
    182  
    183 /** 
    184  * Close the sound instance. If it was playing, it will be stopped. 
    185  * 
    186  * Rationale: sounds are already closed automatically when done playing; 
    187  * this API is provided for completeness only. 
    188  * 
    189  * @param hvs Handle to sound instance. Zeroed afterwards. 
    190  * @return Status 
    191  **/ 
    192 extern Status snd_free(Handle& hvs); 
    193  
    194 /** 
    195  * Start playing the sound. 
    196  * 
    197  * Notes: 
    198  * - once done playing, the sound is automatically closed (allows 
    199  *   fire-and-forget play code). 
    200  * - if no hardware voice is available, this sound may not be 
    201  *   played at all, or in the case of looped sounds, start later. 
    202  * 
    203  * @param hvs Handle to VSrc. 
    204  * @param static_pri (min 0 .. max 1, default 0) indicates which sounds are 
    205  * considered more important (i.e. will override others when no hardware 
    206  * voices are available). the static priority is attenuated by 
    207  * distance to the listener; see snd_update. 
    208  * 
    209  * @return Status 
    210  **/ 
    211 extern Status snd_play(Handle hvs, float static_pri = 0.0f); 
    212  
    213 /** 
    214  * Change 3d position of the sound source. 
    215  * 
    216  * May be called at any time; fails with invalid handle return if 
    217  * the sound has already been closed (e.g. it never played). 
    218  * 
    219  * @param hvs Handle to the sound.  
    220  * @param x,y,z  
    221  * @param relative treat (x,y,z) as relative to the listener; 
    222  *        if false (the default), it is the position in world coordinates. 
    223  * @return Status 
    224  **/ 
    225 extern Status snd_set_pos(Handle hvs, float x, float y, float z, bool relative = false); 
    226  
    227 /** 
    228  * change gain (amplitude modifier) of the sound source. 
    229  * 
    230  * should not be called during a fade (see note in implementation); 
    231  * fails with invalid handle return if the sound has already been 
    232  * closed (e.g. it never played). 
    233  * 
    234  * @param gain amplitude modifier. must be non-negative; 
    235  * 1 -\> unattenuated, 0.5 -\> -6 dB, 0 -\> silence. 
    236  * @return Status 
    237  **/ 
    238 extern Status snd_set_gain(Handle hs, float gain); 
    239  
    240 /** 
    241  * change pitch shift of the sound source. 
    242  * 
    243  * may be called at any time; fails with invalid handle return if 
    244  * the sound has already been closed (e.g. it never played). 
    245  * 
    246  * @param pitch shift: 1.0 means no change; each doubling/halving equals a 
    247  * pitch shift of +/-12 semitones (one octave). zero is invalid. 
    248  * @return Status 
    249  **/ 
    250 extern Status snd_set_pitch(Handle hs, float pitch); 
    251  
    252 /** 
    253  * Enable/disable looping on the sound source. 
    254  * Used to implement variable-length sounds (e.g. while building). 
    255  * 
    256  * May be called at any time; fails with invalid handle return if 
    257  * the sound has already been closed (e.g. it never played). 
    258  * 
    259  * Notes: 
    260  * - looping sounds are not discarded if they cannot be played for 
    261  * - lack of a hardware voice at the moment play was requested. 
    262  * - once looping is again disabled and the sound has reached its end, 
    263  *   the sound instance is freed automatically (as if never looped). 
    264  * 
    265  * @param hvs Handle to the sound. 
    266  * @param loop Boolean to enable/disable lopping on the sound. 
    267  * @return Status 
    268  **/ 
    269 extern Status snd_set_loop(Handle hvs, bool loop); 
    270  
    271 /// types of fade in/out operations 
    272 enum FadeType 
    273 { 
    274     FT_NONE,        /// currently no fade in progres 
    275     FT_LINEAR,      /// f(t) = t 
    276     FT_EXPONENTIAL, /// f(t) = t**3 
    277     FT_S_CURVE,     /// cosine curve 
    278  
    279     FT_ABORT        /// abort and mark pending fade as complete 
    280 }; 
    281  
    282 /** 
    283  * Fade the sound source in or out over time. 
    284  * 
    285  * May be called at any time; fails with invalid handle return if 
    286  * the sound has already been closed (e.g. it never played). 
    287  * 
    288  * Gain starts at \<initial_gain\> (immediately) and is moved toward 
    289  * \<final_gain\> over \<length\> seconds. 
    290  *  
    291  * @param hvs Handle to the sound. 
    292  * @param initial_gain 
    293  * @param final_gain 
    294  * @param length 
    295  * @param type Type of fade curve: linear, exponential or S-curve. 
    296  * 
    297  * For guidance on which to use, see 
    298  * http://www.transom.org/tools/editing_mixing/200309.stupidfadetricks.html 
    299  * you can also pass FT_ABORT to stop fading (if in progress) and 
    300  * set gain to the current \<final_gain\> parameter. 
    301  * Special cases: 
    302  * - if \<initial_gain\> \< 0 (an otherwise illegal value), the sound's 
    303  *   current gain is used as the start value (useful for fading out). 
    304  * - if \<final_gain\> is 0, the sound is freed when the fade completes or 
    305  *   is aborted, thus allowing fire-and-forget fadeouts. no cases are 
    306  *   foreseen where this is undesirable, and it is easier to implement 
    307  *   than an extra set-free-after-fade-flag function. 
    308  * 
    309  * Note that this function doesn't busy-wait until the fade is complete; 
    310  * any number of fades may be active at a time (allows cross-fading). 
    311  * Each snd_update calculates a new gain value for all pending fades. 
    312  * It is safe to start another fade on the same sound source while 
    313  * one is already in progress; the old one will be discarded. 
    314  * 
    315  * @return Status 
    316  **/ 
    317 extern Status snd_fade(Handle hvs, float initial_gain, float final_gain, 
    318     float length, FadeType type); 
    319  
    320  
    321 // 
    322 // sound engine 
    323 // 
    324  
    325 /** 
    326  * (temporarily) disable all sound output. 
    327  * 
    328  * because it causes future snd_open calls to immediately abort before they 
    329  * demand-initialize OpenAL, startup is sped up considerably (500..1000ms). 
    330  * therefore, this must be called before the first snd_open to have 
    331  * any effect; otherwise, the cat will already be out of the bag and 
    332  * we raise a warning. 
    333  * 
    334  * rationale: this is a quick'n dirty way of speeding up startup during 
    335  * development without having to change the game's sound code. 
    336  * 
    337  * can later be called to reactivate sound; all settings ever changed 
    338  * will be applied and subsequent sound load / play requests will work. 
    339  * 
    340  * @param disabled 
    341  * @return Status 
    342  **/ 
    343 extern Status snd_disable(bool disabled); 
    344  
    345 /** 
    346  * Perform housekeeping (e.g. streaming); call once a frame. 
    347  * 
    348  * All parameters are expressed in world coordinates. they can all be NULL 
    349  * to avoid updating the listener data; this is useful when the game world 
    350  * has not been initialized yet. 
    351  * @param pos listener's position 
    352  * @param dir listener view direction 
    353  * @param up listener's local up vector 
    354  * @return Status 
    355  **/ 
    356 extern Status snd_update(const float* pos, const float* dir, const float* up); 
    357  
    358 /** 
    359  * find out if a sound is still playing 
    360  * 
    361  * @param hvs Handle to the snd to check. 
    362  * @return bool true if playing 
    363  **/ 
    364 extern bool snd_is_playing(Handle hvs); 
    365  
    366  
    367 /** 
    368  * free all resources and shut down the sound system. 
    369  * call before h_mgr_shutdown. 
    370  **/ 
    371 extern void snd_shutdown(); 
    372  
    373 #endif  // #ifndef INCLUDED_SND_MGR 
  • source/lib/res/sound/ogg.h

     
    1 #ifndef INCLUDED_OGG 
    2 #define INCLUDED_OGG 
    3  
    4 #include "lib/config2.h" 
    5  
    6 #if CONFIG2_AUDIO 
    7  
    8 #include "lib/external_libraries/openal.h" 
    9 #include "lib/file/vfs/vfs.h" 
    10  
    11 class OggStream 
    12 { 
    13 public: 
    14     virtual ~OggStream() { } 
    15     virtual ALenum Format() = 0; 
    16     virtual ALsizei SamplingRate() = 0; 
    17  
    18     /** 
    19      * @return bytes read (<= size) or a (negative) Status 
    20      **/ 
    21     virtual Status GetNextChunk(u8* buffer, size_t size) = 0; 
    22 }; 
    23  
    24 typedef shared_ptr<OggStream> OggStreamPtr; 
    25  
    26 extern Status OpenOggStream(const OsPath& pathname, OggStreamPtr& stream); 
    27  
    28 /** 
    29  * A non-streaming OggStream (reading the whole file in advance) 
    30  * that can cope with archived/compressed files. 
    31  */ 
    32 extern Status OpenOggNonstream(const PIVFS& vfs, const VfsPath& pathname, OggStreamPtr& stream); 
    33  
    34 #endif  // CONFIG2_AUDIO 
    35  
    36 #endif // INCLUDED_OGG 
  • source/lib/res/sound/snd_mgr.cpp

     
    1 /* Copyright (c) 2010 Wildfire Games 
    2  * 
    3  * Permission is hereby granted, free of charge, to any person obtaining 
    4  * a copy of this software and associated documentation files (the 
    5  * "Software"), to deal in the Software without restriction, including 
    6  * without limitation the rights to use, copy, modify, merge, publish, 
    7  * distribute, sublicense, and/or sell copies of the Software, and to 
    8  * permit persons to whom the Software is furnished to do so, subject to 
    9  * the following conditions: 
    10  * 
    11  * The above copyright notice and this permission notice shall be included 
    12  * in all copies or substantial portions of the Software. 
    13  * 
    14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
    15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
    16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
    17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
    18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
    19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
    20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
    21  */ 
    22  
    23 /* 
    24  * OpenAL sound engine. handles sound I/O, buffer suballocation and 
    25  * voice management/prioritization. 
    26  */ 
    27  
    28 #include "precompiled.h" 
    29 #include "snd_mgr.h" 
    30  
    31 #include <sstream>  // to extract snd_open's definition file contents 
    32 #include <string> 
    33 #include <vector> 
    34 #include <algorithm> 
    35 #include <deque> 
    36 #include <cmath> 
    37 #include <cstdio> 
    38 #include <cfloat> 
    39  
    40 #include "lib/alignment.h" 
    41 #include "lib/res/h_mgr.h" 
    42 #include "lib/file/vfs/vfs.h" 
    43  
    44  
    45 // for DLL-load hack in alc_init 
    46 #if OS_WIN 
    47 # include "lib/sysdep/os/win/win.h" 
    48 #endif 
    49  
    50 #include "lib/timer.h" 
    51 #include "lib/app_hooks.h" 
    52 #include "lib/sysdep/cpu.h" // cpu_CAS 
    53  
    54 #if CONFIG2_AUDIO 
    55  
    56 #include "ogg.h" 
    57 #include "lib/external_libraries/openal.h" 
    58  
    59  
    60 static const size_t maxBufferSize = 64*KiB; 
    61  
    62 // components: 
    63 // - alc*: OpenAL context 
    64 //   readies OpenAL for use; allows specifying the device. 
    65 // - al_listener*: OpenAL listener 
    66 //   owns position/orientation and master gain. 
    67 // - al_buf*: OpenAL buffer suballocator 
    68 //   for convenience; also makes sure all have been freed at exit. 
    69 // - al_src*: OpenAL source suballocator 
    70 //   avoids high source alloc cost. also enforces user-set source limit. 
    71 // - al_init*: OpenAL startup mechanism 
    72 //   allows deferred init (speeding up start time) and runtime reset. 
    73 // - snd_dev*: device enumeration 
    74 //   lists names of all available devices (for sound options screen). 
    75 // - hsd_list*: list of SndData instances 
    76 //   ensures all are freed when desired (despite being cached). 
    77 // - snd_data*: sound data provider 
    78 //   holds audio data (clip or stream) and returns OpenAL buffers on request. 
    79 // - list*: list of active sounds. 
    80 //   sorts by priority for voice management, and has each VSrc update itself. 
    81 // - vsrc*: audio source 
    82 //   owns source properties and queue, references SndData. 
    83 // - vm*: voice management 
    84 //   grants the currently most 'important' sounds a hardware voice. 
    85  
    86  
    87 // indicates OpenAL is ready for use. checked by other components 
    88 // when deciding if they can pass settings changes to OpenAL directly, 
    89 // or whether they need to be saved until init. 
    90 static bool al_initialized = false; 
    91  
    92  
    93 // used by snd_dev_set to reset OpenAL after device has been changed. 
    94 static Status al_reinit(); 
    95  
    96 // used by al_shutdown to free all VSrc and SndData objects, respectively, 
    97 // so that they release their OpenAL sources and buffers. 
    98 static Status list_free_all(); 
    99 static void hsd_list_free_all(); 
    100  
    101  
    102 static void al_ReportError(ALenum err, const char* caller, int line) 
    103 { 
    104     ENSURE(al_initialized); 
    105  
    106     debug_printf(L"OpenAL error: %hs; called from %hs (line %d)\n", alGetString(err), caller, line); 
    107     DEBUG_WARN_ERR(ERR::LOGIC); 
    108 } 
    109  
    110 /** 
    111  * check if OpenAL indicates an error has occurred. it can only report one 
    112  * error at a time, so this is called before and after every OpenAL request. 
    113  * 
    114  * @param caller Name of calling function (typically passed via __func__) 
    115  * @param line line number of the call site (typically passed via __LINE__) 
    116  * (identifies the exact call site since there may be several per caller) 
    117  */ 
    118 static void al_check(const char* caller, int line) 
    119 { 
    120     ALenum err = alGetError(); 
    121     if(err != AL_NO_ERROR) 
    122         al_ReportError(err, caller, line); 
    123 } 
    124  
    125 // convenience version that automatically passes in function name. 
    126 #define AL_CHECK al_check(__func__, __LINE__) 
    127  
    128  
    129 //----------------------------------------------------------------------------- 
    130 // OpenAL context: readies OpenAL for use; allows specifying the device, 
    131 // in case there are problems with OpenAL's default choice. 
    132 //----------------------------------------------------------------------------- 
    133  
    134 // default (0): use OpenAL default device. 
    135 // note: that's why this needs to be a pointer instead of an array. 
    136 static const char* alc_dev_name = 0; 
    137  
    138  
    139 /** 
    140  * tell OpenAL to use the specified device in future. 
    141  * 
    142  * @param alc_new_dev_name Device name; if 0, it reverts to OpenAL's default 
    143  *  choice, which will also be used if this routine is never called. 
    144  * @return Status 
    145  * 
    146  * the device name is typically taken from a config file at init-time; 
    147  * the snd_dev * enumeration routines below are used to present a list 
    148  * of choices to the user in the options screen. 
    149  * 
    150  * if OpenAL hasn't yet been initialized (i.e. no sounds have been opened), 
    151  *   this just stores the device name for use when init does occur. 
    152  *   note: we can't check now if it's invalid (if so, init will fail). 
    153  * otherwise, we shut OpenAL down (thereby stopping all sounds) and 
    154  * re-initialize with the new device. that's fairly time-consuming, 
    155  * so preferably call this routine before sounds are loaded. 
    156  */ 
    157 Status snd_dev_set(const char* alc_new_dev_name) 
    158 { 
    159     // requesting a specific device 
    160     if(alc_new_dev_name) 
    161     { 
    162         // already using that device - done. (don't re-init) 
    163         if(alc_dev_name && !strcmp(alc_dev_name, alc_new_dev_name)) 
    164             return INFO::OK; 
    165  
    166         // store name (need to copy it, since alc_init is called later, 
    167         // and it must then still be valid) 
    168         static char buf[32]; 
    169         strcpy_s(buf, sizeof(buf), alc_new_dev_name); 
    170         alc_dev_name = buf; 
    171     } 
    172     // requesting default device 
    173     else 
    174     { 
    175         // already using default device - done. (don't re-init) 
    176         if(alc_dev_name == 0) 
    177             return INFO::OK; 
    178  
    179         alc_dev_name = 0; 
    180     } 
    181  
    182     // no-op if not initialized yet, otherwise re-init. 
    183     return al_reinit(); 
    184 } 
    185  
    186  
    187 static ALCcontext* alc_ctx = 0; 
    188 static ALCdevice* alc_dev = 0; 
    189  
    190  
    191 /** 
    192  * free the OpenAL context and device. 
    193  */ 
    194 static void alc_shutdown() 
    195 { 
    196     if(alc_ctx) 
    197     { 
    198         alcMakeContextCurrent(0); 
    199         alcDestroyContext(alc_ctx); 
    200     } 
    201  
    202     if(alc_dev) 
    203         alcCloseDevice(alc_dev); 
    204 } 
    205  
    206  
    207 /** 
    208  * Ready OpenAL for use by setting up a device and context. 
    209  * 
    210  * @return Status 
    211  */ 
    212 static Status alc_init() 
    213 { 
    214     Status ret = INFO::OK; 
    215  
    216     alc_dev = alcOpenDevice((alcString)alc_dev_name); 
    217     if(alc_dev) 
    218     { 
    219         alc_ctx = alcCreateContext(alc_dev, 0); // no attrlist needed 
    220         if(alc_ctx) 
    221             alcMakeContextCurrent(alc_ctx); 
    222     } 
    223  
    224     // check if init succeeded. 
    225     // some OpenAL implementations don't indicate failure here correctly; 
    226     // we need to check if the device and context pointers are actually valid. 
    227     ALCenum err = alcGetError(alc_dev); 
    228     if(err != ALC_NO_ERROR || !alc_dev || !alc_ctx) 
    229     { 
    230         debug_printf(L"alc_init failed. alc_dev=%p alc_ctx=%p alc_dev_name=%hs err=%d\n", alc_dev, alc_ctx, alc_dev_name, err); 
    231 // FIXME Hack to get around exclusive access to the sound device 
    232 #if OS_UNIX 
    233         ret = INFO::OK; 
    234 #else 
    235         ret = ERR::FAIL; 
    236 #endif 
    237     } 
    238  
    239     // make note of which sound device is actually being used 
    240     // (e.g. DS3D, native, MMSYSTEM) - needed when reporting OpenAL bugs. 
    241     const char* dev_name = (const char*)alcGetString(alc_dev, ALC_DEVICE_SPECIFIER); 
    242     wchar_t buf[200]; 
    243     swprintf_s(buf, ARRAY_SIZE(buf), L"SND| alc_init: success, using %hs\n", dev_name); 
    244     ah_log(buf); 
    245  
    246     return ret; 
    247 } 
    248  
    249  
    250 //----------------------------------------------------------------------------- 
    251 // listener: owns position/orientation and master gain. 
    252 // if they're set before al_initialized, we pass the saved values to 
    253 // OpenAL immediately after init (instead of waiting until next update). 
    254 //----------------------------------------------------------------------------- 
    255  
    256 static float al_listener_gain = 1.0f; 
    257 static float al_listener_position[3] = { 0.0f, 0.0f, 0.0f }; 
    258 static float al_listener_velocity[3] = { 0.0f, 0.0f, 0.0f }; 
    259  
    260 // float view_direction[3], up_vector[3]; passed directly to OpenAL 
    261 static float al_listener_orientation[6] = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f }; 
    262  
    263  
    264 /** 
    265  * send the current listener properties to OpenAL. 
    266  * 
    267  * also called from al_init. 
    268  */ 
    269 static void al_listener_latch() 
    270 { 
    271     if(al_initialized) 
    272     { 
    273         AL_CHECK; 
    274  
    275         alListenerf(AL_GAIN, al_listener_gain); 
    276         alListenerfv(AL_POSITION, al_listener_position); 
    277         alListenerfv(AL_VELOCITY, al_listener_velocity); 
    278         alListenerfv(AL_ORIENTATION, al_listener_orientation); 
    279  
    280         AL_CHECK; 
    281     } 
    282 } 
    283  
    284  
    285 /** 
    286  * set amplitude modifier, which is effectively applied to all sounds. 
    287  * in layman's terms, this is the global "volume". 
    288  * 
    289  * @param gain Modifier: must be non-negative; 
    290  * 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence. 
    291  * @return Status 
    292  */ 
    293 Status snd_set_master_gain(float gain) 
    294 { 
    295     if(gain < 0) 
    296         WARN_RETURN(ERR::INVALID_PARAM); 
    297  
    298     al_listener_gain = gain; 
    299  
    300     // position will get sent too. 
    301     // this isn't called often, so we don't care. 
    302     al_listener_latch(); 
    303  
    304     return INFO::OK; 
    305 } 
    306  
    307  
    308 /** 
    309  * set position of the listener (corresponds to camera in graphics). 
    310  * coordinates are in world space; the system doesn't matter. 
    311  * 
    312  * @param pos position support vector 
    313  * @param dir view direction 
    314  * @param up up vector 
    315  */ 
    316 static void al_listener_set_pos(const float pos[3], const float dir[3], const float up[3]) 
    317 { 
    318     int i; 
    319     for(i = 0; i < 3; i++) 
    320         al_listener_position[i] = pos[i]; 
    321     for(i = 0; i < 3; i++) 
    322         al_listener_orientation[i] = dir[i]; 
    323     for(i = 0; i < 3; i++) 
    324         al_listener_orientation[3+i] = up[i]; 
    325  
    326     al_listener_latch(); 
    327 } 
    328  
    329  
    330 /** 
    331  * get distance between listener and point. 
    332  * this is used to determine sound priority. 
    333  * 
    334  * @param point position support vector 
    335  * @return float euclidean distance squared 
    336  */ 
    337 static float al_listener_dist_2(const float point[3]) 
    338 { 
    339     const float dx = al_listener_position[0] - point[0]; 
    340     const float dy = al_listener_position[1] - point[1]; 
    341     const float dz = al_listener_position[2] - point[2]; 
    342     return dx*dx + dy*dy + dz*dz; 
    343 } 
    344  
    345  
    346 //----------------------------------------------------------------------------- 
    347 // AL buffer suballocator: allocates buffers as needed (alGenBuffers is fast). 
    348 // this interface is a bit more convenient than the OpenAL routines, and we 
    349 // verify that all buffers have been freed at exit. 
    350 //----------------------------------------------------------------------------- 
    351  
    352 static int al_bufs_outstanding; 
    353  
    354  
    355 /** 
    356  * allocate a new buffer, and fill it with the specified data. 
    357  * 
    358  * @param data raw sound data buffer 
    359  * @param size size of buffer in bytes 
    360  * @param fmt AL_FORMAT_ * describing the sound data 
    361  * @param freq sampling frequency (typically 22050 Hz) 
    362  * @return ALuint buffer name 
    363  */ 
    364 static ALuint al_buf_alloc(ALvoid* data, ALsizei size, ALenum fmt, ALsizei freq) 
    365 { 
    366     AL_CHECK; 
    367     ALuint al_buf; 
    368     alGenBuffers(1, &al_buf); 
    369     alBufferData(al_buf, fmt, data, size, freq); 
    370     AL_CHECK; 
    371  
    372     ENSURE(alIsBuffer(al_buf)); 
    373     al_bufs_outstanding++; 
    374  
    375     return al_buf; 
    376 } 
    377  
    378  
    379 /** 
    380  * free the buffer and its contained sound data. 
    381  * 
    382  * @param al_buf buffer name 
    383  */ 
    384 static void al_buf_free(ALuint al_buf) 
    385 { 
    386     // no-op if 0 (needed in case SndData_reload fails - 
    387     // sd->al_buf will not have been set) 
    388     if(!al_buf) 
    389         return; 
    390  
    391     ENSURE(alIsBuffer(al_buf)); 
    392  
    393     AL_CHECK; 
    394     alDeleteBuffers(1, &al_buf); 
    395     AL_CHECK; 
    396  
    397     al_bufs_outstanding--; 
    398 } 
    399  
    400  
    401 /** 
    402  * make sure all buffers have been returned to us via al_buf_free. 
    403  * called from al_shutdown. 
    404  */ 
    405 static void al_buf_shutdown() 
    406 { 
    407     ENSURE(al_bufs_outstanding == 0); 
    408 } 
    409  
    410  
    411 //----------------------------------------------------------------------------- 
    412 // AL source suballocator: allocate all available sources up-front and 
    413 // pass them out as needed (alGenSources is quite slow, taking 3..5 ms per 
    414 // source returned). also responsible for enforcing user-specified limit 
    415 // on total number of sources (to reduce mixing cost on low-end systems). 
    416 //----------------------------------------------------------------------------- 
    417  
    418 // regardless of HW capabilities, we won't use more than this ("enough"). 
    419 // necessary in case OpenAL doesn't limit #sources (e.g. if SW mixing). 
    420 static const size_t AL_SRC_MAX = 64; 
    421  
    422 // (allow changing at runtime) 
    423 static size_t al_src_maxNumToUse = AL_SRC_MAX; 
    424  
    425 static size_t al_src_numPreallocated; 
    426  
    427 enum AllocationState 
    428 { 
    429     kAvailable = 0, // (must match zero-initialization of al_srcs_allocationStates) 
    430     kInUse 
    431 }; 
    432  
    433 // note: we want to catch double-free bugs and ensure all sources 
    434 // are released at exit, but OpenAL doesn't specify an always-invalid 
    435 // source name, so we need a separate array of AllocationState. 
    436 static ALuint al_srcs[AL_SRC_MAX]; 
    437 static intptr_t al_srcs_allocationStates[AL_SRC_MAX]; 
    438  
    439 /** 
    440  * grab as many sources as possible up to the limit. 
    441  * called from al_init. 
    442  */ 
    443 static void al_src_init() 
    444 { 
    445     // grab as many sources as possible and count how many we get. 
    446     for(size_t i = 0; i < al_src_maxNumToUse; i++) 
    447     { 
    448         ALuint al_src; 
    449         alGenSources(1, &al_src); 
    450         // we've reached the limit, no more are available. 
    451         if(alGetError() != AL_NO_ERROR) 
    452             break; 
    453         ENSURE(alIsSource(al_src)); 
    454         al_srcs[i] = al_src; 
    455         al_src_numPreallocated++; 
    456     } 
    457  
    458     // limit user's cap to what we actually got. 
    459     // (in case snd_set_max_src was called before this) 
    460     if(al_src_maxNumToUse > al_src_numPreallocated) 
    461         al_src_maxNumToUse = al_src_numPreallocated; 
    462  
    463     // make sure we got the minimum guaranteed by OpenAL. 
    464     ENSURE(al_src_numPreallocated >= 16); 
    465 } 
    466  
    467  
    468 /** 
    469  * release all sources on free list. 
    470  * all sources must already have been released via al_src_free. 
    471  * called from al_shutdown. 
    472  */ 
    473 static void al_src_shutdown() 
    474 { 
    475     for(size_t i = 0; i < al_src_numPreallocated; i++) 
    476         ENSURE(al_srcs_allocationStates[i] == kAvailable); 
    477  
    478     AL_CHECK; 
    479     alDeleteSources((ALsizei)al_src_numPreallocated, al_srcs); 
    480     AL_CHECK; 
    481  
    482     al_src_numPreallocated = 0; 
    483 } 
    484  
    485  
    486 /** 
    487  * try to allocate a source. 
    488  * 
    489  * @return whether a source was allocated (see al_srcs_allocationStates). 
    490  * @param al_src receives the new source name iff true is returned. 
    491  */ 
    492 static bool al_src_alloc(ALuint& al_src) 
    493 { 
    494     for(size_t i = 0; i < al_src_numPreallocated; i++) 
    495     { 
    496         if(cpu_CAS(&al_srcs_allocationStates[i], kAvailable, kInUse)) 
    497         { 
    498             al_src = al_srcs[i]; 
    499             return true; 
    500         } 
    501     } 
    502  
    503     return false;   // no more to give 
    504 } 
    505  
    506  
    507 /** 
    508  * mark a source as free and available for reuse. 
    509  * 
    510  * @param al_src source name 
    511  */ 
    512 static void al_src_free(ALuint al_src) 
    513 { 
    514     ENSURE(alIsSource(al_src)); 
    515  
    516     const ALuint* pos = std::find(al_srcs, al_srcs+al_src_numPreallocated, al_src); 
    517     if(pos != al_srcs+al_src_numPreallocated)   // found it 
    518     { 
    519         const size_t i = pos - al_srcs; 
    520         ENSURE(cpu_CAS(&al_srcs_allocationStates[i], kInUse, kAvailable)); 
    521     } 
    522     else 
    523         DEBUG_WARN_ERR(ERR::LOGIC); // al_src wasn't in al_srcs 
    524 } 
    525  
    526  
    527 /** 
    528  * set maximum number of voices to play simultaneously, 
    529  * to reduce mixing cost on low-end systems. 
    530  * this limit may be ignored if e.g. there's a stricter 
    531  * implementation- defined ceiling anyway. 
    532  * 
    533  * @param limit max. number of sources 
    534  * @return Status 
    535  */ 
    536 Status snd_set_max_voices(size_t limit) 
    537 { 
    538     // valid if cap is legit (less than what we allocated in al_src_init), 
    539     // or if al_src_init hasn't been called yet. note: we accept anything 
    540     // in the second case, as al_src_init will sanity-check al_src_cap. 
    541     if(!al_src_numPreallocated || limit < al_src_numPreallocated) 
    542     { 
    543         al_src_maxNumToUse = limit; 
    544         return INFO::OK; 
    545     } 
    546     // user is requesting a cap higher than what we actually allocated. 
    547     // that's fine (not an error), but we won't set the cap, since it 
    548     // determines how many sources may be returned. 
    549     // there's no return value to indicate this because the cap is 
    550     // precisely that - an upper limit only, we don't care if it can't be met. 
    551     else 
    552         return INFO::OK; 
    553 } 
    554  
    555  
    556 //----------------------------------------------------------------------------- 
    557 // OpenAL startup mechanism: allows deferring init until sounds are actually 
    558 // played, therefore speeding up perceived game start time. 
    559 // also resets OpenAL when settings (e.g. device) are changed at runtime. 
    560 //----------------------------------------------------------------------------- 
    561  
    562 /** 
    563  * master OpenAL init; makes sure all subsystems are ready for use. 
    564  * called from each snd_open; no harm if called more than once. 
    565  * 
    566  * @return Status 
    567  */ 
    568 static Status al_init() 
    569 { 
    570     // only take action on first call, OR when re-initializing. 
    571     if(al_initialized) 
    572         return INFO::OK; 
    573  
    574     RETURN_STATUS_IF_ERR(alc_init()); 
    575  
    576     al_initialized = true; 
    577  
    578     // these can't fail: 
    579     al_src_init(); 
    580     al_listener_latch(); 
    581  
    582     alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); 
    583  
    584     return INFO::OK; 
    585 } 
    586  
    587  
    588 /** 
    589  * shut down all module subsystems. 
    590  */ 
    591 static void al_shutdown() 
    592 { 
    593     // was never initialized - nothing to do. 
    594     if(!al_initialized) 
    595         return; 
    596  
    597     // somewhat tricky: go through gyrations to free OpenAL resources. 
    598  
    599     // .. free all active sounds so that they release their source. 
    600     //    the SndData reference is also removed, 
    601     //    but these remain open, since they are cached. 
    602     list_free_all(); 
    603  
    604     // .. actually free all (still cached) SndData instances. 
    605     hsd_list_free_all(); 
    606  
    607     // .. all sources and buffers have been returned to their suballocators. 
    608     //    now free them all. 
    609     al_src_shutdown(); 
    610     al_buf_shutdown(); 
    611  
    612     alc_shutdown(); 
    613  
    614     al_initialized = false; 
    615 } 
    616  
    617  
    618 /** 
    619  * re-initialize OpenAL. currently only required for changing devices. 
    620  * 
    621  * @return Status 
    622  */ 
    623 static Status al_reinit() 
    624 { 
    625     // not yet initialized. settings have been saved, and will be 
    626     // applied by the component init routines called from al_init. 
    627     if(!al_initialized) 
    628         return INFO::OK; 
    629  
    630     // re-init (stops all currently playing sounds) 
    631     al_shutdown(); 
    632     return al_init(); 
    633 } 
    634  
    635  
    636 // prevent OpenAL from being initialized when snd_init is called. 
    637 static bool snd_disabled = false; 
    638  
    639 /** 
    640  * extra layer on top of al_init that allows 'disabling' sound. 
    641  * called from each snd_open. 
    642  * 
    643  * @return Status from al_init, or ERR::AGAIN if sound disabled 
    644  */ 
    645 static Status snd_init() 
    646 { 
    647     // (note: each VSrc_reload and therefore snd_open will fail) 
    648     if(snd_disabled) 
    649         return ERR::AGAIN;  // NOWARN 
    650  
    651     return al_init(); 
    652 } 
    653  
    654  
    655 Status snd_disable(bool disabled) 
    656 { 
    657     snd_disabled = disabled; 
    658  
    659     if(snd_disabled) 
    660     { 
    661         ENSURE(!al_initialized);    // already initialized => disable is pointless 
    662         return INFO::OK; 
    663     } 
    664     else 
    665         return snd_init();  // note: won't return ERR::AGAIN, since snd_disabled == false 
    666 } 
    667  
    668  
    669 /** 
    670  * free all resources and shut down the sound system. 
    671  * call before h_mgr_shutdown. 
    672  */ 
    673 void snd_shutdown() 
    674 { 
    675     al_shutdown();  // calls list_free_all 
    676 } 
    677  
    678  
    679 //----------------------------------------------------------------------------- 
    680 // device enumeration: list all devices and allow the user to choose one, 
    681 // in case the default device has problems. 
    682 //----------------------------------------------------------------------------- 
    683  
    684 // set by snd_dev_prepare_enum; used by snd_dev_next. 
    685 // consists of back-to-back C strings, terminated by an extra '\0'. 
    686 // (this is taken straight from OpenAL; the spec says the format may change). 
    687 static const char* devs; 
    688  
    689 /** 
    690  * Prepare to enumerate all device names (this resets the list returned by 
    691  * snd_dev_next). 
    692  * 
    693  * may be called each time the device list is needed. 
    694  * 
    695  * @return Status; always successful unless the requisite device 
    696  * enumeration extension isn't available. in the latter case, 
    697  * a "cannot enum device" message should be presented to the user, 
    698  * and snd_dev_set need not be called; OpenAL will use its default device. 
    699  */ 
    700 Status snd_dev_prepare_enum() 
    701 { 
    702     if(alcIsExtensionPresent(0, (alcString)"ALC_ENUMERATION_EXT") != AL_TRUE) 
    703         WARN_RETURN(ERR::NOT_SUPPORTED); 
    704  
    705     devs = (const char*)alcGetString(0, ALC_DEVICE_SPECIFIER); 
    706     return INFO::OK; 
    707 } 
    708  
    709  
    710 /** 
    711  * Get next device name. 
    712  * 
    713  * do not call unless snd_dev_prepare_enum succeeded! 
    714  * not thread-safe! (static data from snd_dev_prepare_enum is used) 
    715  * 
    716  * @return const char* device name, or 0 if all have been returned 
    717  */ 
    718 const char* snd_dev_next() 
    719 { 
    720     if(!*devs) 
    721         return 0; 
    722     const char* dev = devs; 
    723     devs += strlen(dev)+1; 
    724     return dev; 
    725 } 
    726  
    727  
    728 //----------------------------------------------------------------------------- 
    729 // sound data provider: holds audio data (clip or stream) and returns 
    730 // OpenAL buffers on request. 
    731 //----------------------------------------------------------------------------- 
    732  
    733 // rationale for separate VSrc (instance) and SndData resources: 
    734 // - we need to be able to fade out and cancel loops. 
    735 //   => VSrc isn't fire and forget; we need to access sounds at runtime. 
    736 // - allowing access via direct pointer is unsafe 
    737 //   => Handle-based access is required 
    738 // - we don't want to reload sound data on every play() 
    739 //   => need either a separate caching mechanism or one central data resource. 
    740 // - we want to support reloading (for consistency if not necessity) 
    741 //   => can't hack via h_find / setting fn_key to 0; need a separate instance. 
    742  
    743 /** 
    744  * rationale for supporting both clips and streams: 
    745  * streams avoid delays while reading+decompressing large files, but 
    746  * playing multiple instances of them would require separate positions etc. 
    747  * since the same clip is often played concurrently and we can't guarantee 
    748  * they will never exceed the size of a stream, it makes sense to support a 
    749  * separate "clip" data type that allocates enough storage and avoids needing 
    750  * the stream position/list of buffers. 
    751  */ 
    752 enum SndDataType 
    753 { 
    754     SD_CLIP, 
    755     SD_STREAM 
    756 }; 
    757  
    758 /** 
    759  * Holder for sound data - either a clip, or stream. 
    760  */ 
    761 struct SndData 
    762 { 
    763     ALenum al_fmt; 
    764     ALsizei al_freq; 
    765  
    766     SndDataType type; 
    767  
    768     ALuint al_buf;  // valid if type == SD_CLIP 
    769     OggStreamPtr ogg;   // valid if type == SD_STREAM 
    770 }; 
    771  
    772 H_TYPE_DEFINE(SndData); 
    773  
    774  
    775 //----------------------------------------------------------------------------- 
    776 // SndData instance list: ensures all allocated since last al_shutdown 
    777 // are freed when desired (they are cached => extra work needed). 
    778  
    779 // rationale: all SndData objects (actually, their OpenAL buffers) must be 
    780 // freed during al_shutdown, to prevent leaks. we can't rely on list* 
    781 // to free all VSrc, and thereby their associated SndData objects - 
    782 // completed sounds are no longer in the list. 
    783 // 
    784 // nor can we use the h_mgr_shutdown automatic leaked resource cleanup: 
    785 // we need to be able to al_shutdown at runtime 
    786 // (when resetting OpenAL, after e.g. device change). 
    787 // 
    788 // h_mgr support is required to forcibly close SndData objects, 
    789 // since they are cached (kept open). it requires that this come after 
    790 // H_TYPE_DEFINE(SndData), since h_force_free needs the handle type. 
    791 // 
    792 // we never need to delete single entries: hsd_list_free_all 
    793 // (called by al_shutdown) frees each entry and clears the entire list. 
    794  
    795 typedef std::vector<Handle> Handles; 
    796 static Handles hsd_list; 
    797  
    798 /** 
    799  * Add hsd to the list. 
    800  * called from SndData_reload; will later be removed via hsd_list_free_all. 
    801  * @param hsd Handle to SndData 
    802  */ 
    803 static void hsd_list_add(Handle hsd) 
    804 { 
    805     hsd_list.push_back(hsd); 
    806 } 
    807  
    808  
    809 /** 
    810  * Free all sounds on list. 
    811  * called by al_shutdown (at exit, or when reinitializing OpenAL). 
    812  * (definition must come after H_TYPE_DEFINE(SndData)) 
    813  */ 
    814 static void hsd_list_free_all() 
    815 { 
    816     for(Handles::iterator it = hsd_list.begin(); it != hsd_list.end(); ++it) 
    817     { 
    818         Handle& hsd = *it; 
    819  
    820         (void)h_force_free(hsd, H_SndData); 
    821         // ignore errors - if hsd was a stream, and its associated source 
    822         // was active when al_shutdown was called, it will already have been 
    823         // freed (list_free_all would free the source; it then releases 
    824         // its SndData reference, which closes the instance because it's 
    825         // RES_UNIQUE). 
    826         // 
    827         // NB: re-initializing the sound library (e.g. after changing 
    828         // HW settings) requires all handles to be freed, even if cached. 
    829         // hence, we use h_force_free. unfortunately this causes the 
    830         // handle's tag to be ignored. it is conceivable that the wrong 
    831         // handle could be freed here. 
    832         // 
    833         // we rule this out with the following argument. either we're 
    834         // called when re-initializing sound or at exit. in the former 
    835         // case, h_force_free does check the handle type: only sounds are 
    836         // ever freed. we don't care if the wrong one is closed since all 
    837         // must be stomped upon. in the latter case, it definitely doesn't 
    838         // matter what we free. hence, no problem. 
    839     } 
    840  
    841     // leave its memory intact, so we don't have to reallocate it later 
    842     // if we are now reinitializing OpenAL (not exiting). 
    843     hsd_list.resize(0); 
    844 } 
    845  
    846  
    847 static void SndData_init(SndData* UNUSED(sd), va_list UNUSED(args)) 
    848 { 
    849 } 
    850  
    851 static void SndData_dtor(SndData* sd) 
    852 { 
    853     if(sd->type == SD_CLIP) 
    854         al_buf_free(sd->al_buf); 
    855     else 
    856         sd->ogg.reset(); 
    857 } 
    858  
    859 static Status SndData_reload(SndData* sd, const PIVFS& vfs, const VfsPath& pathname, Handle hsd) 
    860 { 
    861 #if 0 // HACK: streaming disabled because it breaks archives 
    862  
    863     // (OGG streaming requires a real POSIX pathname - see OpenOggStream) 
    864     fs::wpath real_pathname; 
    865     RETURN_STATUS_IF_ERR(vfs->GetRealPath(pathname, real_pathname)); 
    866  
    867     // currently only supports OGG; WAV is no longer supported. writing our own loader is infeasible 
    868     // due to a seriously watered down spec with many incompatible variants. 
    869     // pulling in an external library (e.g. freealut) is deemed not worth the 
    870     // effort - OGG should be better in all cases. 
    871  
    872     RETURN_STATUS_IF_ERR(OpenOggStream(real_pathname, sd->ogg)); 
    873     const size_t size = fs::file_size(real_pathname); 
    874 #else 
    875     RETURN_STATUS_IF_ERR(OpenOggNonstream(vfs, pathname, sd->ogg)); 
    876     FileInfo fileInfo; 
    877     RETURN_STATUS_IF_ERR(vfs->GetFileInfo(pathname, &fileInfo)); 
    878     const size_t size = fileInfo.Size(); 
    879 #endif 
    880  
    881     sd->al_freq = sd->ogg->SamplingRate(); 
    882     sd->al_fmt  = sd->ogg->Format(); 
    883  
    884     // HACK - it would be nicer for callers to confirm they won't 
    885     // open the same (streamed) file multiple times, 
    886     // but that's not possible with the current JSI_Sound. 
    887     sd->type = (size > 500*KiB)? SD_STREAM : SD_CLIP; 
    888  
    889     if(sd->type == SD_CLIP) 
    890     { 
    891         std::vector<u8> data(50*MiB);   // max. size of any clip (anything larger should be streamed) 
    892         const Status ret = sd->ogg->GetNextChunk(&data[0], data.size()); 
    893         RETURN_STATUS_IF_ERR(ret); 
    894         const size_t size = (size_t)ret; 
    895         ENSURE(size != 0);  // must have read something 
    896         ENSURE(size != data.size());    // shouldn't be limited by buffer size 
    897         sd->al_buf = al_buf_alloc(&data[0], (ALsizei)size, sd->al_fmt, sd->al_freq); 
    898         sd->ogg.reset(); 
    899     } 
    900     else 
    901         sd->al_buf = 0; 
    902  
    903     // note: to avoid polluting hsd_list with invalid handles, we ensure 
    904     // all of the above succeeded before adding to the list. 
    905     // (c.f. topic#10719, "Problem freeing sounds loaded by JavaScript") 
    906     hsd_list_add(hsd); 
    907  
    908     return INFO::OK; 
    909 } 
    910  
    911 static Status SndData_validate(const SndData* sd) 
    912 { 
    913     if(sd->al_fmt == 0) 
    914         WARN_RETURN(ERR::_11); 
    915     if((size_t)sd->al_freq > 100000)    // suspicious 
    916         WARN_RETURN(ERR::_12); 
    917     if(sd->type == SD_CLIP) 
    918     { 
    919         if(!sd->al_buf) 
    920             WARN_RETURN(ERR::_13); 
    921     } 
    922     else if(sd->type == SD_STREAM) 
    923     { 
    924         if(!sd->ogg) 
    925             WARN_RETURN(ERR::_14); 
    926     } 
    927     else 
    928         WARN_RETURN(ERR::_15);  // invalid type 
    929     return INFO::OK; 
    930 } 
    931  
    932  
    933 static Status SndData_to_string(const SndData* sd, wchar_t* buf) 
    934 { 
    935     const wchar_t* type = (sd->type == SD_CLIP)? L"clip" : L"stream"; 
    936     swprintf_s(buf, H_STRING_LEN, L"%ls; al_buf=%u", type, sd->al_buf); 
    937     return INFO::OK; 
    938 } 
    939  
    940  
    941 /** 
    942  * open and return a handle to a sound file's data. 
    943  * 
    944  * @return Handle or Status on failure 
    945  */ 
    946 static Handle snd_data_load(const PIVFS& vfs, const VfsPath& pathname) 
    947 { 
    948     return h_alloc(H_SndData, vfs, pathname); 
    949 } 
    950  
    951 /** 
    952  * Free the sound. 
    953  * 
    954  * @param hsd Handle to SndData; set to 0 afterwards. 
    955  * @return Status 
    956  */ 
    957 static Status snd_data_free(Handle& hsd) 
    958 { 
    959     return h_free(hsd, H_SndData); 
    960 } 
    961  
    962  
    963 //----------------------------------------------------------------------------- 
    964  
    965 /** 
    966  * Get the sound's AL buffer (typically to play it) 
    967  * 
    968  * @param hsd Handle to SndData. 
    969  * @param al_buf buffer name. 
    970  * @return Status, most commonly: 
    971  * INFO::OK = buffer has been returned; more are expected to be available. 
    972  * INFO::ALL_COMPLETE = buffer has been returned but is the last one (EOF). 
    973  */ 
    974 static Status snd_data_buf_get(Handle hsd, ALuint& al_buf) 
    975 { 
    976     H_DEREF(hsd, SndData, sd); 
    977     if(sd->type == SD_CLIP) 
    978     { 
    979         al_buf = sd->al_buf; 
    980         return INFO::ALL_COMPLETE;  // "EOF" 
    981     } 
    982  
    983     if(!sd->ogg) 
    984         WARN_RETURN(ERR::INVALID_HANDLE); 
    985     u8 data[maxBufferSize]; 
    986     const Status ret = sd->ogg->GetNextChunk(data, maxBufferSize); 
    987     RETURN_STATUS_IF_ERR(ret); 
    988     const size_t size = (size_t)ret; 
    989     al_buf = al_buf_alloc(data, (ALsizei)size, sd->al_fmt, sd->al_freq); 
    990  
    991     return (size < maxBufferSize)? INFO::ALL_COMPLETE : INFO::OK; 
    992 } 
    993  
    994  
    995 /** 
    996  * Indicate the sound's buffer is no longer needed. 
    997  * 
    998  * @param hsd Handle to SndData. 
    999  * @param al_buf buffer name 
    1000  * @return Status 
    1001  */ 
    1002 static Status snd_data_buf_free(Handle hsd, ALuint al_buf) 
    1003 { 
    1004     H_DEREF(hsd, SndData, sd); 
    1005  
    1006     if(sd->type == SD_CLIP) 
    1007     { 
    1008         // no-op (caller will later release hsd reference; 
    1009         // when hsd actually unloads, sd->al_buf will be freed). 
    1010     } 
    1011     else 
    1012         al_buf_free(al_buf); 
    1013  
    1014     return INFO::OK; 
    1015 } 
    1016  
    1017  
    1018 //----------------------------------------------------------------------------- 
    1019 // fading 
    1020 //----------------------------------------------------------------------------- 
    1021  
    1022 /** 
    1023  * control block for a fade operation. 
    1024  */ 
    1025 struct FadeInfo 
    1026 { 
    1027     double start_time; 
    1028     FadeType type; 
    1029     float length; 
    1030     float initial_val; 
    1031     float final_val; 
    1032 }; 
    1033  
    1034 static float fade_factor_linear(float t) 
    1035 { 
    1036     return t; 
    1037 } 
    1038  
    1039 static float fade_factor_exponential(float t) 
    1040 { 
    1041     // t**3 
    1042     return t*t*t; 
    1043 } 
    1044  
    1045 static float fade_factor_s_curve(float t) 
    1046 { 
    1047     // cosine curve 
    1048     const double pi = 3.14159265358979323846; 
    1049     float y = cos(t*pi + pi); 
    1050     // map [-1,1] to [0,1] 
    1051     return (y + 1.0f) / 2.0f; 
    1052 } 
    1053  
    1054  
    1055 /** 
    1056  * fade() return value; indicates if the fade operation is complete. 
    1057  */ 
    1058 enum FadeRet 
    1059 { 
    1060     FADE_NO_CHANGE, 
    1061     FADE_CHANGED, 
    1062     FADE_TO_0_FINISHED 
    1063 }; 
    1064  
    1065 /** 
    1066  * Carry out the requested fade operation. 
    1067  * 
    1068  * This is called for each active VSrc; if they have no fade operation 
    1069  * active, nothing happens. Note: as an optimization, we could make a 
    1070  * list of VSrc with fade active and only call this for those; 
    1071  * not yet necessary, though. 
    1072  * 
    1073  * @param fi Describes the fade operation 
    1074  * @param cur_time typically returned via timer_Time() 
    1075  * @param out_val Output gain value, i.e. the current result of the fade. 
    1076  * @return FadeRet 
    1077  */ 
    1078 static FadeRet fade(FadeInfo& fi, double cur_time, float& out_val) 
    1079 { 
    1080     // no fade in progress - abort immediately. this check is necessary to 
    1081     // avoid division-by-zero below. 
    1082     if(fi.type == FT_NONE) 
    1083         return FADE_NO_CHANGE; 
    1084  
    1085     ENSURE(0.0f <= fi.initial_val && fi.initial_val <= 1.0f); 
    1086     ENSURE(0.0f <= fi.final_val && fi.final_val <= 1.0f); 
    1087  
    1088     // end reached - if fi.length is 0, but the fade is "in progress", do the 
    1089     // processing here, and skip the dangerous division 
    1090     if(fi.type == FT_ABORT || (cur_time >= fi.start_time + fi.length)) 
    1091     { 
    1092         // make sure exact value is hit 
    1093         out_val = fi.final_val; 
    1094  
    1095         // special case: we were fading out; caller will free the sound. 
    1096         if(fi.final_val == 0.0f) 
    1097             return FADE_TO_0_FINISHED; 
    1098  
    1099         // wipe out all values amd mark as no longer actively fading 
    1100         memset(&fi, 0, sizeof(fi)); 
    1101         fi.type = FT_NONE; 
    1102  
    1103         return FADE_CHANGED; 
    1104     } 
    1105  
    1106     // how far into the fade are we? [0,1] 
    1107     const float t = (cur_time - fi.start_time) / fi.length; 
    1108     ENSURE(0.0f <= t && t <= 1.0f); 
    1109  
    1110     float factor; 
    1111     switch(fi.type) 
    1112     { 
    1113     case FT_LINEAR: 
    1114         factor = fade_factor_linear(t); 
    1115         break; 
    1116     case FT_EXPONENTIAL: 
    1117         factor = fade_factor_exponential(t); 
    1118         break; 
    1119     case FT_S_CURVE: 
    1120         factor = fade_factor_s_curve(t); 
    1121         break; 
    1122  
    1123     // initialize with anything at all, just so that the calculation 
    1124     // below runs through; we reset out_val after that. 
    1125     case FT_ABORT: 
    1126         factor = 0.0f; 
    1127         break; 
    1128  
    1129     NODEFAULT; 
    1130     } 
    1131  
    1132     out_val = fi.initial_val + factor*(fi.final_val - fi.initial_val); 
    1133  
    1134     return FADE_CHANGED; 
    1135 } 
    1136  
    1137  
    1138 /** 
    1139  * Is the fade operation currently active? 
    1140  * 
    1141  * @return bool 
    1142  */ 
    1143 static bool fade_is_active(FadeInfo& fi) 
    1144 { 
    1145     return (fi.type != FT_NONE); 
    1146 } 
    1147  
    1148  
    1149 //----------------------------------------------------------------------------- 
    1150 // virtual sound source: a sound the user wants played. 
    1151 // owns source properties, buffer queue, and references SndData. 
    1152 //----------------------------------------------------------------------------- 
    1153  
    1154 // rationale: combine Src and VSrc - best interface, due to needing hsd, 
    1155 // buffer queue (# processed) in update 
    1156  
    1157 enum VSrcFlags 
    1158 { 
    1159     // (we can't just test if al_src is zero because that might be a 
    1160     // valid source name!) 
    1161     VS_HAS_AL_SRC  = 1, 
    1162  
    1163     // SndData has reported EOF. will close down after last buffer completes. 
    1164     VS_EOF         = 2, 
    1165  
    1166     // this VSrc was added via list_add and needs to be removed with 
    1167     // list_remove in VSrc_dtor. 
    1168     // not set if load fails somehow (avoids list_remove "not found" error). 
    1169     VS_IN_LIST     = 4, 
    1170  
    1171     VS_SHOULD_STOP = 8, 
    1172  
    1173     VS_ALL_FLAGS = VS_HAS_AL_SRC|VS_EOF|VS_IN_LIST|VS_SHOULD_STOP 
    1174 }; 
    1175  
    1176 /** 
    1177  * control block for a virtual source, which represents a sound that the 
    1178  * application wants played. it may or may not be played, depending on 
    1179  * priority and whether an actual OpenAL source is available. 
    1180  */ 
    1181 struct VSrc 
    1182 { 
    1183     bool HasSource() const 
    1184     { 
    1185         if((flags & VS_HAS_AL_SRC) == 0) 
    1186             return false; 
    1187         ENSURE(alIsSource(al_src)); 
    1188         return true; 
    1189     } 
    1190  
    1191     /// handle to this VSrc, so that it can close itself. 
    1192     Handle hvs; 
    1193  
    1194     /// associated sound data 
    1195     Handle hsd; 
    1196  
    1197     // AL source properties (set via snd_set*) 
    1198     ALfloat pos[3]; 
    1199     ALfloat gain;   /// [0,inf) 
    1200     ALfloat pitch;  /// (0,1] 
    1201     ALboolean loop; 
    1202     ALboolean relative; 
    1203  
    1204     /// controls vsrc_update behavior (VSrcFlags) 
    1205     size_t flags; 
    1206  
    1207     // valid iff HasSource() 
    1208     ALuint al_src; 
    1209  
    1210     // priority for voice management 
    1211     float static_pri;   /// as given by snd_play 
    1212     float cur_pri;      /// holds newly calculated value 
    1213  
    1214     FadeInfo fade; 
    1215 }; 
    1216  
    1217 H_TYPE_DEFINE(VSrc); 
    1218  
    1219 static void VSrc_init(VSrc* vs, va_list UNUSED(args)) 
    1220 { 
    1221     vs->flags = 0; 
    1222     vs->fade.type = FT_NONE; 
    1223 } 
    1224  
    1225 static void list_remove(VSrc* vs); 
    1226 static Status vsrc_reclaim(VSrc* vs); 
    1227  
    1228 static void VSrc_dtor(VSrc* vs) 
    1229 { 
    1230     // only remove if added (not the case if load failed) 
    1231     if(vs->flags & VS_IN_LIST) 
    1232     { 
    1233         list_remove(vs); 
    1234         vs->flags &= ~VS_IN_LIST; 
    1235     } 
    1236  
    1237     // these are safe, even if reload (partially) failed: 
    1238     vsrc_reclaim(vs); 
    1239     (void)snd_data_free(vs->hsd); 
    1240 } 
    1241  
    1242 static Status VSrc_reload(VSrc* vs, const PIVFS& vfs, const VfsPath& pathname, Handle hvs) 
    1243 { 
    1244     // cannot wait till play(), need to init here: 
    1245     // must load OpenAL so that snd_data_load can check for OGG extension. 
    1246     Status err = snd_init(); 
    1247     // .. don't complain if sound is disabled; fail silently. 
    1248     if(err == ERR::AGAIN) 
    1249         return err; 
    1250     // .. catch genuine errors during init. 
    1251     RETURN_STATUS_IF_ERR(err); 
    1252  
    1253     VfsPath dataPathname; 
    1254  
    1255     // pathname is a definition file containing the data file name and 
    1256     // its gain. 
    1257     if(pathname.Extension() == L".txt") 
    1258     { 
    1259         shared_ptr<u8> buf; size_t size; 
    1260         RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, buf, size)); 
    1261         std::wistringstream def(std::wstring((wchar_t*)buf.get(), (int)size)); 
    1262  
    1263         def >> dataPathname; 
    1264         def >> vs->gain; 
    1265         vs->gain /= 100.0f; // is stored as percent 
    1266     } 
    1267     // read the sound file directly and assume default gain (1.0). 
    1268     else 
    1269     { 
    1270         dataPathname = pathname; 
    1271         vs->gain = 1.0f; 
    1272     } 
    1273  
    1274     // note: vs->gain can legitimately be > 1.0 - don't clamp. 
    1275  
    1276     vs->pitch = 1.0f; 
    1277  
    1278     vs->hvs = hvs;  // allows calling snd_free when done playing. 
    1279  
    1280     vsrc_reclaim(vs); 
    1281  
    1282     vs->hsd = snd_data_load(vfs, dataPathname); 
    1283     RETURN_STATUS_IF_ERR(vs->hsd); 
    1284  
    1285     return INFO::OK; 
    1286 } 
    1287  
    1288 static bool IsValidBoolean(ALboolean b) 
    1289 { 
    1290     return (b == AL_FALSE || b == AL_TRUE); 
    1291 } 
    1292  
    1293 static Status VSrc_validate(const VSrc* vs) 
    1294 { 
    1295     // al_src can legitimately be 0 (if vs is low-pri) 
    1296     if(vs->flags & ~VS_ALL_FLAGS) 
    1297         WARN_RETURN(ERR::_1); 
    1298     // no limitations on <pos> 
    1299     if(!(0.0f <= vs->gain && vs->gain <= 1.0f)) 
    1300         WARN_RETURN(ERR::_2); 
    1301     if(!(0.0f < vs->pitch && vs->pitch <= 2.0f)) 
    1302         WARN_RETURN(ERR::_3); 
    1303     if(!IsValidBoolean(vs->loop) || !IsValidBoolean(vs->relative)) 
    1304         WARN_RETURN(ERR::_4); 
    1305     // <static_pri> and <cur_pri> have no invariant we could check. 
    1306     return INFO::OK; 
    1307 } 
    1308  
    1309 static Status VSrc_to_string(const VSrc* vs, wchar_t* buf) 
    1310 { 
    1311     swprintf_s(buf, H_STRING_LEN, L"al_src = %u", vs->al_src); 
    1312     return INFO::OK; 
    1313 } 
    1314  
    1315  
    1316 /** 
    1317  * open and return a handle to a sound instance. 
    1318  * 
    1319  * @param pathname if a text file (extension ".txt"), 
    1320  * it is assumed to be a definition file containing the 
    1321  * sound file name and its gain (0.0 .. 1.0). 
    1322  * otherwise, it is taken to be the sound file name and 
    1323  * gain is set to the default of 1.0 (no attenuation). 
    1324  * @return Handle or Status on failure 
    1325  */ 
    1326 Handle snd_open(const PIVFS& vfs, const VfsPath& pathname) 
    1327 { 
    1328     // note: RES_UNIQUE forces each instance to get a new resource 
    1329     // (which is of course what we want). 
    1330     return h_alloc(H_VSrc, vfs, pathname, RES_UNIQUE); 
    1331 } 
    1332  
    1333  
    1334 /** 
    1335  * Free the sound; if it was playing, it will be stopped. 
    1336  * Note: sounds are closed automatically when done playing; 
    1337  * this is provided for completeness only. 
    1338  * 
    1339  * @param hvs Handle to VSrc. will be set to 0 afterwards. 
    1340  * @return Status 
    1341  */ 
    1342 Status snd_free(Handle& hvs) 
    1343 { 
    1344     if(!hvs) 
    1345         return INFO::OK; 
    1346     return h_free(hvs, H_VSrc); 
    1347 } 
    1348  
    1349  
    1350 //----------------------------------------------------------------------------- 
    1351 // list of active sounds. used by voice management component, 
    1352 // and to have each VSrc update itself (queue new buffers). 
    1353  
    1354 // VSrc fields are used -> must come after struct VSrc 
    1355  
    1356 // sorted in descending order of current priority 
    1357 // (we sometimes remove low pri items, which requires moving down 
    1358 // everything that comes after them, so we want those to come last). 
    1359 // 
    1360 // don't use list, to avoid lots of allocs (expect thousands of VSrcs). 
    1361 typedef std::deque<VSrc*> VSrcs; 
    1362 typedef VSrcs::iterator VSrcIt; 
    1363 static VSrcs vsrcs; 
    1364  
    1365 // don't need to sort now - caller will list_SortByDescendingPriority() during update. 
    1366 static void list_add(VSrc* vs) 
    1367 { 
    1368     vsrcs.push_back(vs); 
    1369 } 
    1370  
    1371  
    1372 /** 
    1373  * call back for each VSrc entry in the list. 
    1374  * 
    1375  * @param end_idx if not the default value of 0, stop before that entry. 
    1376  */ 
    1377 template<class Func> 
    1378 static void list_foreach(Func callback, size_t numToSkip = 0, size_t end_idx = 0) 
    1379 { 
    1380     const VSrcIt begin = vsrcs.begin() + numToSkip; 
    1381     const VSrcIt end = end_idx? begin+end_idx : vsrcs.end(); 
    1382  
    1383     // can't use std::for_each: some entries may have been deleted 
    1384     // (i.e. set to 0) since last update. 
    1385     for(VSrcIt it = begin; it != end; ++it) 
    1386     { 
    1387         VSrc* vs = *it; 
    1388         if(vs) 
    1389             callback(vs); 
    1390     } 
    1391 } 
    1392  
    1393 struct GreaterPriority 
    1394 { 
    1395     bool operator()(VSrc* vs1, VSrc* vs2) const 
    1396     { 
    1397         return vs1->cur_pri > vs2->cur_pri; 
    1398     } 
    1399 }; 
    1400  
    1401 /// sort list by decreasing 'priority' (most important first) 
    1402 static void list_SortByDescendingPriority() 
    1403 { 
    1404     std::sort(vsrcs.begin(), vsrcs.end(), GreaterPriority()); 
    1405 } 
    1406  
    1407  
    1408 /** 
    1409  * scan list and remove the given VSrc (by setting it to 0; list will be 
    1410  * pruned later (see rationale below). 
    1411  * O(N)! 
    1412  * 
    1413  * @param vs VSrc to remove. 
    1414  */ 
    1415 static void list_remove(VSrc* vs) 
    1416 { 
    1417     for(size_t i = 0; i < vsrcs.size(); i++) 
    1418     { 
    1419         if(vsrcs[i] == vs) 
    1420         { 
    1421             // found it; several ways we could remove: 
    1422             // - shift everything else down (slow) -> no 
    1423             // - fill the hole with e.g. the last element 
    1424             //   (vsrcs would no longer be sorted by priority) -> no 
    1425             // - replace with 0 (will require prune_removed and 
    1426             //   more work in foreach) -> best alternative 
    1427             vsrcs[i] = 0; 
    1428             return; 
    1429         } 
    1430     } 
    1431  
    1432     DEBUG_WARN_ERR(ERR::LOGIC); // VSrc not found 
    1433 } 
    1434  
    1435  
    1436 struct IsNull 
    1437 { 
    1438     bool operator()(VSrc* vs) const 
    1439     { 
    1440         return (vs == 0); 
    1441     } 
    1442 }; 
    1443  
    1444 /** 
    1445  * remove entries that were set to 0 by list_remove, so that 
    1446  * code below can grant the first al_src_cap entries a source. 
    1447  */ 
    1448 static void list_prune_removed() 
    1449 { 
    1450     VSrcIt new_end = remove_if(vsrcs.begin(), vsrcs.end(), IsNull()); 
    1451     vsrcs.erase(new_end, vsrcs.end()); 
    1452 } 
    1453  
    1454  
    1455 static void vsrc_free(VSrc* vs) 
    1456 { 
    1457     snd_free(vs->hvs); 
    1458 } 
    1459  
    1460 static Status list_free_all() 
    1461 { 
    1462     list_foreach(vsrc_free); 
    1463     return INFO::OK; 
    1464 } 
    1465  
    1466  
    1467 //----------------------------------------------------------------------------- 
    1468  
    1469 /** 
    1470  * Send the VSrc properties to OpenAL (when we actually have a source). 
    1471  * called by snd_set * and vsrc_grant. 
    1472  * 
    1473  */ 
    1474 static void vsrc_latch(VSrc* vs) 
    1475 { 
    1476     if(!vs->HasSource()) 
    1477         return; 
    1478  
    1479     float rolloff = 1.0f; 
    1480     float referenceDistance = 125.0f; 
    1481     float maxDistance = 500.0f; 
    1482     if(vs->relative) 
    1483     { 
    1484         rolloff = 0.0f; 
    1485         referenceDistance = 1.0f; 
    1486         maxDistance = FLT_MAX; 
    1487     } 
    1488  
    1489     AL_CHECK; 
    1490  
    1491     alSourcefv(vs->al_src, AL_POSITION,           vs->pos); 
    1492     alSource3f(vs->al_src, AL_VELOCITY,           0.0f, 0.0f, 0.0f); 
    1493     alSourcei (vs->al_src, AL_SOURCE_RELATIVE,    vs->relative); 
    1494     alSourcef (vs->al_src, AL_ROLLOFF_FACTOR,     rolloff); 
    1495     alSourcef (vs->al_src, AL_REFERENCE_DISTANCE, referenceDistance); 
    1496     alSourcef (vs->al_src, AL_MAX_DISTANCE,       maxDistance); 
    1497     alSourcef (vs->al_src, AL_GAIN,               vs->gain); 
    1498     alSourcef (vs->al_src, AL_PITCH,              vs->pitch); 
    1499     alSourcei (vs->al_src, AL_LOOPING,            vs->loop); 
    1500  
    1501     //alSourcei (vs->al_src, AL_MIN_GAIN,           0.0f); 
    1502     //alSourcei (vs->al_src, AL_MAX_GAIN,           1.0f); 
    1503     //alSource3f(vs->al_src, AL_DIRECTION,          0.0f, 0.0f, 0.0f); 
    1504     //alSourcef (vs->al_src, AL_CONE_INNER_ANGLE,   360.0f); 
    1505     //alSourcef (vs->al_src, AL_CONE_OUTER_ANGLE,   360.0f); 
    1506     //alSourcef (vs->al_src, AL_CONE_OUTER_GAIN,    0.0f); 
    1507     //alSourcef (vs->al_src, AL_SEC_OFFSET,         0.0f); 
    1508     //alSourcef (vs->al_src, AL_SAMPLE_OFFSET,      0.0f); 
    1509     //alSourcef (vs->al_src, AL_BYTE_OFFSET,        0.0f); 
    1510  
    1511     ALenum err = alGetError(); 
    1512     if(err != AL_NO_ERROR) 
    1513     { 
    1514         debug_printf(L"vsrc_latch: one of the below is invalid:\n"); 
    1515         debug_printf(L"  al_src: 0x%x\n", vs->al_src); 
    1516         debug_printf(L"  position: %f %f %f\n", vs->pos[0], vs->pos[1], vs->pos[2]); 
    1517         debug_printf(L"  velocity: %f %f %f\n", 0.0f, 0.0f, 0.0f); 
    1518         debug_printf(L"  relative: %d\n", (int)vs->relative); 
    1519         debug_printf(L"  rolloff: %f\n", rolloff); 
    1520         debug_printf(L"  ref dist: %f\n", referenceDistance); 
    1521         debug_printf(L"  max dist: %f\n", maxDistance); 
    1522         debug_printf(L"  gain: %f\n", vs->gain); 
    1523         debug_printf(L"  pitch: %f\n", vs->pitch); 
    1524         debug_printf(L"  loop: %d\n", (int)vs->loop); 
    1525  
    1526         al_ReportError(err, __func__, __LINE__); 
    1527     } 
    1528 } 
    1529  
    1530  
    1531 /** 
    1532  * Dequeue any of the VSrc's sound buffers that are finished playing. 
    1533  * 
    1534  * @return int number of entries that were removed. 
    1535  */ 
    1536 static int vsrc_deque_finished_bufs(VSrc* vs) 
    1537 { 
    1538     ENSURE(vs->HasSource());    // (otherwise there's no sense in calling this function) 
    1539  
    1540     AL_CHECK; 
    1541     int num_processed; 
    1542     alGetSourcei(vs->al_src, AL_BUFFERS_PROCESSED, &num_processed); 
    1543     AL_CHECK; 
    1544  
    1545     for(int i = 0; i < num_processed; i++) 
    1546     { 
    1547         ALuint al_buf; 
    1548         alSourceUnqueueBuffers(vs->al_src, 1, &al_buf); 
    1549         snd_data_buf_free(vs->hsd, al_buf); 
    1550     } 
    1551  
    1552     AL_CHECK; 
    1553     return num_processed; 
    1554 } 
    1555  
    1556  
    1557 /** 
    1558  * Update the VSrc - perform fade (if active), queue/unqueue buffers. 
    1559  * Called once a frame. 
    1560  * must be a functor so that each call receives the same time (avoids repeated 
    1561  * calls to timer_Time and inconsistencies when crossfading) 
    1562  */ 
    1563 class VsrcUpdater 
    1564 { 
    1565 public: 
    1566     VsrcUpdater(double time) 
    1567         : time(time) 
    1568     { 
    1569     } 
    1570  
    1571     Status operator()(VSrc* vs) const 
    1572     { 
    1573         if(!vs->HasSource()) 
    1574             return INFO::OK; 
    1575  
    1576         FadeRet fade_ret = fade(vs->fade, time, vs->gain); 
    1577         // auto-free after fadeout. 
    1578         if(fade_ret == FADE_TO_0_FINISHED) 
    1579         { 
    1580             vsrc_free(vs); 
    1581             return INFO::OK;    // don't continue - <vs> has been freed. 
    1582         } 
    1583         // fade in progress; latch current gain value. 
    1584         else if(fade_ret == FADE_CHANGED) 
    1585             vsrc_latch(vs); 
    1586  
    1587         int num_queued; 
    1588         alGetSourcei(vs->al_src, AL_BUFFERS_QUEUED, &num_queued); 
    1589         AL_CHECK; 
    1590  
    1591         int num_processed = vsrc_deque_finished_bufs(vs); 
    1592         UNUSED2(num_processed); 
    1593  
    1594         if(vs->flags & VS_EOF) 
    1595         { 
    1596             // no more buffers left, and EOF reached - done playing. 
    1597             if(num_queued == 0) 
    1598             { 
    1599                 snd_free(vs->hvs); 
    1600                 return INFO::OK; 
    1601             } 
    1602         } 
    1603         // can still read from SndData 
    1604         else 
    1605         { 
    1606             // get next buffer 
    1607             ALuint al_buf; 
    1608             Status ret = snd_data_buf_get(vs->hsd, al_buf); 
    1609             RETURN_STATUS_IF_ERR(ret); 
    1610             if(ret == INFO::ALL_COMPLETE)   // no further buffers will be forthcoming 
    1611                 vs->flags |= VS_EOF; 
    1612  
    1613             alSourceQueueBuffers(vs->al_src, 1, &al_buf); 
    1614             AL_CHECK; 
    1615  
    1616             // HACK: OpenAL stops the source if reloading took too long 
    1617             ALint state; 
    1618             alGetSourcei(vs->al_src, AL_SOURCE_STATE, &state); 
    1619             if((state == AL_STOPPED) && !(vs->flags & VS_SHOULD_STOP)) 
    1620                 alSourcePlay(vs->al_src); 
    1621         } 
    1622  
    1623         return INFO::OK; 
    1624     } 
    1625  
    1626 private: 
    1627     double time; 
    1628 }; 
    1629  
    1630  
    1631 /** 
    1632  * Try to give the VSrc an AL source so that it can (re)start playing. 
    1633  * called by snd_play and voice management. 
    1634  * 
    1635  * @return Status (ERR::FAIL if no AL source is available) 
    1636  */ 
    1637 static Status vsrc_grant(VSrc* vs) 
    1638 { 
    1639     if(vs->HasSource()) // already playing 
    1640         return INFO::OK; 
    1641  
    1642     // try to allocate a source. snd_play calls us in the hope that a source 
    1643     // happens to be free, but if not, just skip the remaining steps and 
    1644     // wait for the next update. 
    1645     if(!al_src_alloc(vs->al_src)) 
    1646         return ERR::FAIL;   // NOWARN 
    1647     vs->flags |= VS_HAS_AL_SRC; 
    1648  
    1649     // pass (user-specifiable) properties on to OpenAL. 
    1650     vsrc_latch(vs); 
    1651  
    1652     // queue up some buffers (enough to start playing, at least). 
    1653     VsrcUpdater updater(timer_Time()); 
    1654     updater(vs); 
    1655     AL_CHECK; 
    1656  
    1657     alSourcePlay(vs->al_src); 
    1658     AL_CHECK; 
    1659     return INFO::OK; 
    1660 } 
    1661  
    1662  
    1663 /** 
    1664  * stop playback, and reclaim the OpenAL source. 
    1665  * called when closing the VSrc, or when voice management decides 
    1666  * this VSrc must yield to others of higher priority. 
    1667  */ 
    1668 static Status vsrc_reclaim(VSrc* vs) 
    1669 { 
    1670     if(!vs->HasSource()) 
    1671         return ERR::FAIL;   // NOWARN 
    1672  
    1673     // clear the source's buffer queue (necessary because buffers cannot 
    1674     // be deleted at shutdown while still attached to a source). 
    1675     // note: OpenAL 1.1 says all buffers become "processed" when the 
    1676     // source is stopped (so vsrc_deque_finished_bufs ought to have the 
    1677     // desired effect), but that isn't the case on some Linux 
    1678     // implementations (OpenALsoft and PulseAudio with on-board NVidia). 
    1679     // wiping out the entire queue by attaching the null buffer is safer, 
    1680     // but still doesn't cause versions of OpenALsoft older than 2009-08-11 
    1681     // to correctly reset AL_BUFFERS_PROCESSED. in "Re: [Openal-devel] 
    1682     // Questionable "invalid value" from alSourceUnqueueBuffers", the 
    1683     // developer recommended working around this bug by rewinding the 
    1684     // source instead of merely issuing alSourceStop. 
    1685     // reference: http://trac.wildfiregames.com/ticket/297 
    1686     vs->loop = false; 
    1687     vsrc_latch(vs); 
    1688  
    1689     vs->flags |= VS_SHOULD_STOP; 
    1690  
    1691     alSourceStop(vs->al_src); 
    1692  
    1693     vsrc_deque_finished_bufs(vs); 
    1694     alSourcei(vs->al_src, AL_BUFFER, AL_NONE); 
    1695  
    1696     alSourceRewind(vs->al_src); 
    1697  
    1698     al_src_free(vs->al_src); 
    1699     vs->flags &= ~VS_HAS_AL_SRC; 
    1700  
    1701     return INFO::OK; 
    1702 } 
    1703  
    1704  
    1705 //----------------------------------------------------------------------------- 
    1706 // snd_mgr API 
    1707 //----------------------------------------------------------------------------- 
    1708  
    1709 /** 
    1710  * Request the sound be played. 
    1711  * 
    1712  * Once done playing, the sound is automatically closed (allows 
    1713  * fire-and-forget play code). 
    1714  * if no hardware voice is available, this sound may not be played at all, 
    1715  * or in the case of looped sounds, start later. 
    1716  * 
    1717  * @param hvs Handle to VSrc 
    1718  * @param static_pri (min 0 .. max 1, default 0) indicates which sounds are 
    1719  * considered more important; this is attenuated by distance to the 
    1720  * listener (see snd_update). 
    1721  * @return Status 
    1722  */ 
    1723 Status snd_play(Handle hvs, float static_pri) 
    1724 { 
    1725     H_DEREF(hvs, VSrc, vs); 
    1726  
    1727     // note: vs->hsd is valid, otherwise snd_open would have failed 
    1728     // and returned an invalid handle (caught above). 
    1729  
    1730     vs->static_pri = static_pri; 
    1731     list_add(vs); 
    1732     vs->flags |= VS_IN_LIST; 
    1733  
    1734     // optimization (don't want to do full update here - too slow) 
    1735     // either we get a source and playing begins immediately, 
    1736     // or it'll be taken care of on next update. 
    1737     vsrc_grant(vs); 
    1738     return INFO::OK; 
    1739 } 
    1740  
    1741  
    1742 /** 
    1743  * Change 3d position of the sound source. 
    1744  * 
    1745  * May be called at any time; fails with invalid handle return if 
    1746  * the sound has already been closed (e.g. it never played). 
    1747  * 
    1748  * @param hvs Handle to VSrc 
    1749  * @param x,y,z coordinates (interpretation: see below) 
    1750  * @param relative if true, (x,y,z) is treated as relative to the listener; 
    1751  * otherwise, it is the position in world coordinates (default). 
    1752  * @return Status 
    1753  */ 
    1754 Status snd_set_pos(Handle hvs, float x, float y, float z, bool relative) 
    1755 { 
    1756     H_DEREF(hvs, VSrc, vs); 
    1757  
    1758     vs->pos[0] = x; vs->pos[1] = y; vs->pos[2] = z; 
    1759     vs->relative = relative; 
    1760  
    1761     vsrc_latch(vs); 
    1762     return INFO::OK; 
    1763 } 
    1764  
    1765  
    1766 /** 
    1767  * Change gain (amplitude modifier) of the sound source. 
    1768  * 
    1769  * should not be called during a fade (see note in implementation); 
    1770  * fails with invalid handle return if the sound has already been 
    1771  * closed (e.g. it never played). 
    1772  * 
    1773  * @param hvs Handle to VSrc 
    1774  * @param gain modifier; must be non-negative; 
    1775  * 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence. 
    1776  * @return Status 
    1777  */ 
    1778 Status snd_set_gain(Handle hvs, float gain) 
    1779 { 
    1780     H_DEREF(hvs, VSrc, vs); 
    1781  
    1782     if(!(0.0f <= gain && gain <= 1.0f)) 
    1783         WARN_RETURN(ERR::INVALID_PARAM); 
    1784  
    1785     // if fading, gain changes would be overridden during the next 
    1786     // snd_update. attempting this indicates a logic error. we abort to 
    1787     // avoid undesired jumps in gain that might surprise (and deafen) users. 
    1788     if(fade_is_active(vs->fade)) 
    1789         WARN_RETURN(ERR::LOGIC); 
    1790  
    1791     vs->gain = gain; 
    1792  
    1793     vsrc_latch(vs); 
    1794     return INFO::OK; 
    1795 } 
    1796  
    1797  
    1798 /** 
    1799  * Change pitch shift of the sound source. 
    1800  * 
    1801  * may be called at any time; fails with invalid handle return if 
    1802  * the sound has already been closed (e.g. it never played). 
    1803  * 
    1804  * @param hvs Handle to VSrc 
    1805  * @param pitch shift: 1.0 means no change; each doubling/halving equals a 
    1806  * pitch shift of +/-12 semitones (one octave). zero is invalid. 
    1807  * @return Status 
    1808  */ 
    1809 Status snd_set_pitch(Handle hvs, float pitch) 
    1810 { 
    1811     H_DEREF(hvs, VSrc, vs); 
    1812  
    1813     if(pitch <= 0.0f) 
    1814         WARN_RETURN(ERR::INVALID_PARAM); 
    1815  
    1816     vs->pitch = pitch; 
    1817  
    1818     vsrc_latch(vs); 
    1819     return INFO::OK; 
    1820 } 
    1821  
    1822  
    1823 /** 
    1824  * Enable/disable looping on the sound source. 
    1825  * used to implement variable-length sounds (e.g. while building). 
    1826  * 
    1827  * may be called at any time; fails with invalid handle return if 
    1828  * the sound has already been closed (e.g. it never played). 
    1829  * 
    1830  * notes: 
    1831  * - looping sounds are not discarded if they cannot be played for lack of 
    1832  *   a hardware voice at the moment play was requested. 
    1833  * - once looping is again disabled and the sound has reached its end, 
    1834  *   the sound instance is freed automatically (as if never looped). 
    1835  * 
    1836  * @param hvs Handle to VSrc 
    1837  */ 
    1838 Status snd_set_loop(Handle hvs, bool loop) 
    1839 { 
    1840     H_DEREF(hvs, VSrc, vs); 
    1841  
    1842     vs->loop = loop; 
    1843  
    1844     vsrc_latch(vs); 
    1845     return INFO::OK; 
    1846 } 
    1847  
    1848  
    1849 /** 
    1850  * Fade the sound source in or out over time. 
    1851  * Its gain starts at \<initial_gain\> immediately and is moved toward 
    1852  * \<final_gain\> over \<length\> seconds. 
    1853  * 
    1854  * may be called at any time; fails with invalid handle return if 
    1855  * the sound has already been closed (e.g. it never played). 
    1856  * 
    1857  * note that this function doesn't busy-wait until the fade is complete; 
    1858  * any number of fades may be active at a time (allows cross-fading). 
    1859  * each snd_update calculates a new gain value for all pending fades. 
    1860  * it is safe to start another fade on the same sound source while 
    1861  * one is currently in progress; the old one is dropped. 
    1862  * 
    1863  * @param hvs Handle to VSrc 
    1864  * @param initial_gain gain. if < 0 (an otherwise illegal value), the sound's 
    1865  * current gain is used as the start value (useful for fading out). 
    1866  * @param final_gain gain. if 0, the sound is freed when the fade completes or 
    1867  * is aborted, thus allowing fire-and-forget fadeouts. no cases are 
    1868  * foreseen where this is undesirable, and it is easier to implement 
    1869  * than an extra set-free-after-fade-flag function. 
    1870  * @param length duration of fade [s] 
    1871  * @param type determines the fade curve: linear, exponential or S-curve. 
    1872  * for guidance on which to use, see 
    1873  * http://www.transom.org/tools/editing_mixing/200309.stupidfadetricks.html 
    1874  * you can also pass FT_ABORT to stop fading (if in progress) and 
    1875  * set gain to the final_gain parameter passed here. 
    1876  * @return Status 
    1877  */ 
    1878 Status snd_fade(Handle hvs, float initial_gain, float final_gain, 
    1879     float length, FadeType type) 
    1880 { 
    1881     H_DEREF(hvs, VSrc, vs); 
    1882  
    1883     if(type != FT_LINEAR && type != FT_EXPONENTIAL && type != FT_S_CURVE && type != FT_ABORT) 
    1884         WARN_RETURN(ERR::INVALID_PARAM); 
    1885  
    1886     // special case - set initial value to current gain (see above). 
    1887     if(initial_gain < 0.0f) 
    1888         initial_gain = vs->gain; 
    1889  
    1890     const double cur_time = timer_Time(); 
    1891  
    1892     FadeInfo& fi = vs->fade; 
    1893     fi.type        = type; 
    1894     fi.start_time  = cur_time; 
    1895     fi.initial_val = initial_gain; 
    1896     fi.final_val   = final_gain; 
    1897     fi.length      = length; 
    1898  
    1899     (void)fade(fi, cur_time, vs->gain); 
    1900     vsrc_latch(vs); 
    1901  
    1902     return INFO::OK; 
    1903 } 
    1904  
    1905  
    1906 /** --TODO: Test to ensure this works (not currently necessary for intensity) 
    1907  * find out if a sound is still playing 
    1908  * 
    1909  * @param hvs - handle to the snd to check 
    1910  * @return bool true if playing 
    1911  **/ 
    1912 bool snd_is_playing(Handle hvs) 
    1913 { 
    1914     // (can't use H_DEREF due to bool return value) 
    1915     VSrc* vs = H_USER_DATA(hvs, VSrc); 
    1916  
    1917     // sound has played and was already freed or is otherwise not loaded. 
    1918     if(!vs) 
    1919         return false; 
    1920  
    1921     // 'just' finished playing 
    1922     if(!vs->HasSource()) 
    1923         return false; 
    1924  
    1925     return true; 
    1926 } 
    1927  
    1928  
    1929 //----------------------------------------------------------------------------- 
    1930 // voice management: grants the currently most 'important' sounds 
    1931 // a hardware voice. 
    1932 //----------------------------------------------------------------------------- 
    1933  
    1934 /// length of vector squared (avoids costly sqrt) 
    1935 static float magnitude_2(const float v[3]) 
    1936 { 
    1937     return v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; 
    1938 } 
    1939  
    1940  
    1941 /** 
    1942  * Determine new priority of the VSrc based on distance to listener and 
    1943  * static priority. 
    1944  * Called via list_foreach. 
    1945  */ 
    1946 static void calc_cur_pri(VSrc* vs) 
    1947 { 
    1948     const float MAX_DIST_2 = 1000.0f; 
    1949     const float falloff = 10.0f; 
    1950  
    1951     float d_2;  // euclidean distance to listener (squared) 
    1952     if(vs->relative) 
    1953         d_2 = magnitude_2(vs->pos); 
    1954     else 
    1955         d_2 = al_listener_dist_2(vs->pos); 
    1956  
    1957     // scale priority down exponentially 
    1958     float e = d_2 / MAX_DIST_2; // 0.0f (close) .. 1.0f (far) 
    1959  
    1960     // assume farther away than OpenAL cutoff - no sound contribution 
    1961     float cur_pri = -1.0f; 
    1962     if(e < 1.0f) 
    1963         cur_pri = vs->static_pri / pow(falloff, e); 
    1964     vs->cur_pri = cur_pri; 
    1965 } 
    1966  
    1967  
    1968 /** 
    1969  * convenience function that strips all unimportant VSrc of their AL source. 
    1970  * called via list_foreach; also immediately frees discarded clips. 
    1971  */ 
    1972 static void reclaim(VSrc* vs) 
    1973 { 
    1974     vsrc_reclaim(vs); 
    1975  
    1976     if(!vs->loop) 
    1977         snd_free(vs->hvs); 
    1978 } 
    1979  
    1980  
    1981 /** 
    1982  * update voice management, i.e. recalculate priority and assign AL sources. 
    1983  * no-op if OpenAL not yet initialized. 
    1984  */ 
    1985 static Status vm_update() 
    1986 { 
    1987     list_prune_removed(); 
    1988  
    1989     // update current priorities (a function of static priority and distance). 
    1990     list_foreach(calc_cur_pri); 
    1991  
    1992     list_SortByDescendingPriority(); 
    1993  
    1994     // partition list; the first ones will be granted a source 
    1995     // (if they don't have one already), after reclaiming all sources from 
    1996     // the remainder of the VSrc list entries. 
    1997     size_t first_unimportant = std::min((size_t)vsrcs.size(), al_src_maxNumToUse); 
    1998     list_foreach(reclaim, first_unimportant, 0); 
    1999     list_foreach(vsrc_grant, 0, first_unimportant); 
    2000  
    2001     return INFO::OK; 
    2002 } 
    2003  
    2004  
    2005 //----------------------------------------------------------------------------- 
    2006  
    2007 /** 
    2008  * perform housekeeping (e.g. streaming); call once a frame. 
    2009  * 
    2010  * @param pos position support vector. if NULL, all parameters are 
    2011  * ignored and listener position unchanged; this is useful in case the 
    2012  * world isn't initialized yet. 
    2013  * @param dir view direction 
    2014  * @param up up vector 
    2015  * @return Status 
    2016  */ 
    2017 Status snd_update(const float* pos, const float* dir, const float* up) 
    2018 { 
    2019     // there's no sense in updating anything if we weren't initialized 
    2020     // yet (most notably, if sound is disabled). we check for this to 
    2021     // avoid confusing the code below. the caller should complain if 
    2022     // this fails, so report success here (everything will work once 
    2023     // sound is re-enabled). 
    2024     if(!al_initialized) 
    2025         return INFO::OK; 
    2026  
    2027     if(pos) 
    2028         al_listener_set_pos(pos, dir, up); 
    2029  
    2030     vm_update(); 
    2031  
    2032     // for each source: add / remove buffers; carry out fading. 
    2033     list_foreach(VsrcUpdater(timer_Time())); 
    2034  
    2035     return INFO::OK; 
    2036 } 
    2037  
    2038  
    2039 #else   // CONFIG2_AUDIO 
    2040  
    2041 // Stub implementations of snd_mgr API: 
    2042  
    2043 Status snd_dev_prepare_enum() { return ERR::NOT_SUPPORTED; } 
    2044 const char* snd_dev_next() { return NULL; } 
    2045 Status snd_dev_set(const char* alc_new_dev_name) { return INFO::OK; } 
    2046 Status snd_set_max_voices(size_t limit) { return INFO::OK; } 
    2047 Status snd_set_master_gain(float gain) { return INFO::OK; } 
    2048 Handle snd_open(const PIVFS& vfs, const VfsPath& pathname) { return 0; } 
    2049 Status snd_free(Handle& hvs) { return INFO::OK; } 
    2050 Status snd_play(Handle hvs, float static_pri) { return INFO::OK; } 
    2051 Status snd_set_pos(Handle hvs, float x, float y, float z, bool relative) { return INFO::OK; } 
    2052 Status snd_set_gain(Handle hs, float gain) { return INFO::OK; } 
    2053 Status snd_set_pitch(Handle hs, float pitch) { return INFO::OK; } 
    2054 Status snd_set_loop(Handle hvs, bool loop) { return INFO::OK; } 
    2055 Status snd_fade(Handle hvs, float initial_gain, float final_gain, float length, FadeType type) { return INFO::OK; } 
    2056 Status snd_disable(bool disabled) { return INFO::OK; } 
    2057 Status snd_update(const float* pos, const float* dir, const float* up) { return INFO::OK; } 
    2058 bool snd_is_playing(Handle hvs) { return false; } 
    2059 void snd_shutdown() { } 
    2060  
    2061 #endif  // CONFIG2_AUDIO 
  • source/lib/res/sound/ogg.cpp

     
    1 #include "precompiled.h" 
    2 #include "ogg.h" 
    3  
    4 #if CONFIG2_AUDIO 
    5  
    6 #include "lib/external_libraries/openal.h" 
    7 #include "lib/external_libraries/vorbis.h" 
    8  
    9 #include "lib/byte_order.h" 
    10 #include "lib/file/io/io.h" 
    11 #include "lib/file/file_system.h" 
    12  
    13  
    14 static Status LibErrorFromVorbis(int err) 
    15 { 
    16     switch(err) 
    17     { 
    18     case 0: 
    19         return INFO::OK; 
    20     case OV_HOLE: 
    21         return ERR::AGAIN; 
    22     case OV_EREAD: 
    23         return ERR::IO; 
    24     case OV_EFAULT: 
    25         return ERR::LOGIC; 
    26     case OV_EIMPL: 
    27         return ERR::NOT_SUPPORTED; 
    28     case OV_EINVAL: 
    29         return ERR::INVALID_PARAM; 
    30     case OV_ENOTVORBIS: 
    31         return ERR::NOT_SUPPORTED; 
    32     case OV_EBADHEADER: 
    33         return ERR::CORRUPTED; 
    34     case OV_EVERSION: 
    35         return ERR::INVALID_VERSION; 
    36     case OV_ENOTAUDIO: 
    37         return ERR::_1; 
    38     case OV_EBADPACKET: 
    39         return ERR::_2; 
    40     case OV_EBADLINK: 
    41         return ERR::_3; 
    42     case OV_ENOSEEK: 
    43         return ERR::_4; 
    44     default: 
    45         return ERR::FAIL; 
    46     } 
    47 } 
    48  
    49  
    50 //----------------------------------------------------------------------------- 
    51  
    52 class VorbisFileAdapter 
    53 { 
    54 public: 
    55     VorbisFileAdapter(const PFile& openedFile) 
    56         : file(openedFile) 
    57         , size(FileSize(openedFile->Pathname())) 
    58         , offset(0) 
    59     { 
    60     } 
    61  
    62     static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context) 
    63     { 
    64         VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; 
    65         const off_t sizeRequested = numItems*itemSize; 
    66         const off_t sizeRemaining = adapter->size - adapter->offset; 
    67         const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining); 
    68  
    69         io::Operation op(*adapter->file.get(), bufferToFill, sizeToRead, adapter->offset); 
    70         if(io::Run(op) == INFO::OK) 
    71         { 
    72             adapter->offset += sizeToRead; 
    73             return sizeToRead; 
    74         } 
    75  
    76         errno = EIO; 
    77         return 0; 
    78     } 
    79  
    80     static int Seek(void* context, ogg_int64_t offset, int whence) 
    81     { 
    82         VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; 
    83  
    84         off_t origin = 0; 
    85         switch(whence) 
    86         { 
    87         case SEEK_SET: 
    88             origin = 0; 
    89             break; 
    90         case SEEK_CUR: 
    91             origin = adapter->offset; 
    92             break; 
    93         case SEEK_END: 
    94             origin = adapter->size+1; 
    95             break; 
    96             NODEFAULT; 
    97         } 
    98  
    99         adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size); 
    100         return 0; 
    101     } 
    102  
    103     static int Close(void* context) 
    104     { 
    105         VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; 
    106         adapter->file.reset(); 
    107         return 0;   // return value is ignored 
    108     } 
    109  
    110     static long Tell(void* context) 
    111     { 
    112         VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; 
    113         return adapter->offset; 
    114     } 
    115  
    116 private: 
    117     PFile file; 
    118     off_t size; 
    119     off_t offset; 
    120 }; 
    121  
    122 //----------------------------------------------------------------------------- 
    123  
    124 class VorbisBufferAdapter 
    125 { 
    126 public: 
    127     VorbisBufferAdapter(const shared_ptr<u8>& buffer, size_t size) 
    128         : buffer(buffer) 
    129         , size(size) 
    130         , offset(0) 
    131     { 
    132     } 
    133  
    134     static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context) 
    135     { 
    136         VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context; 
    137         const off_t sizeRequested = numItems*itemSize; 
    138         const off_t sizeRemaining = adapter->size - adapter->offset; 
    139         const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining); 
    140  
    141         memcpy(bufferToFill, adapter->buffer.get() + adapter->offset, sizeToRead); 
    142  
    143         adapter->offset += sizeToRead; 
    144         return sizeToRead; 
    145     } 
    146  
    147     static int Seek(void* context, ogg_int64_t offset, int whence) 
    148     { 
    149         VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context; 
    150  
    151         off_t origin = 0; 
    152         switch(whence) 
    153         { 
    154         case SEEK_SET: 
    155             origin = 0; 
    156             break; 
    157         case SEEK_CUR: 
    158             origin = adapter->offset; 
    159             break; 
    160         case SEEK_END: 
    161             origin = adapter->size+1; 
    162             break; 
    163             NODEFAULT; 
    164         } 
    165  
    166         adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size); 
    167         return 0; 
    168     } 
    169  
    170     static int Close(void* context) 
    171     { 
    172         VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context; 
    173         adapter->buffer.reset(); 
    174         return 0;   // return value is ignored 
    175     } 
    176  
    177     static long Tell(void* context) 
    178     { 
    179         VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context; 
    180         return adapter->offset; 
    181     } 
    182  
    183 private: 
    184     shared_ptr<u8> buffer; 
    185     off_t size; 
    186     off_t offset; 
    187 }; 
    188  
    189  
    190 //----------------------------------------------------------------------------- 
    191  
    192 template <typename Adapter> 
    193 class OggStreamImpl : public OggStream 
    194 { 
    195 public: 
    196     OggStreamImpl(const Adapter& adapter) 
    197         : adapter(adapter) 
    198     { 
    199     } 
    200  
    201     Status Open() 
    202     { 
    203         ov_callbacks callbacks; 
    204         callbacks.read_func = Adapter::Read; 
    205         callbacks.close_func = Adapter::Close; 
    206         callbacks.seek_func = Adapter::Seek; 
    207         callbacks.tell_func = Adapter::Tell; 
    208         const int ret = ov_open_callbacks(&adapter, &vf, 0, 0, callbacks); 
    209         if(ret != 0) 
    210             WARN_RETURN(LibErrorFromVorbis(ret)); 
    211  
    212         const int link = -1;    // retrieve info for current bitstream 
    213         info = ov_info(&vf, link); 
    214         if(!info) 
    215             WARN_RETURN(ERR::INVALID_HANDLE); 
    216  
    217         return INFO::OK; 
    218     } 
    219  
    220     virtual ALenum Format() 
    221     { 
    222         return (info->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; 
    223     } 
    224  
    225     virtual ALsizei SamplingRate() 
    226     { 
    227         return info->rate; 
    228     } 
    229  
    230     virtual Status GetNextChunk(u8* buffer, size_t size) 
    231     { 
    232         // we may have to call ov_read multiple times because it 
    233         // treats the buffer size "as a limit and not a request" 
    234         size_t bytesRead = 0; 
    235         for(;;) 
    236         { 
    237             const int isBigEndian = (BYTE_ORDER == BIG_ENDIAN); 
    238             const int wordSize = sizeof(i16); 
    239             const int isSigned = 1; 
    240             int bitstream;  // unused 
    241             const int ret = ov_read(&vf, (char*)buffer+bytesRead, int(size-bytesRead), isBigEndian, wordSize, isSigned, &bitstream); 
    242             if(ret == 0)    // EOF 
    243                 return (Status)bytesRead; 
    244             else if(ret < 0) 
    245                 WARN_RETURN(LibErrorFromVorbis(ret)); 
    246             else    // success 
    247             { 
    248                 bytesRead += ret; 
    249                 if(bytesRead == size) 
    250                     return (Status)bytesRead; 
    251             } 
    252         } 
    253     } 
    254  
    255 private: 
    256     Adapter adapter; 
    257     OggVorbis_File vf; 
    258     vorbis_info* info; 
    259 }; 
    260  
    261  
    262 //----------------------------------------------------------------------------- 
    263  
    264 Status OpenOggStream(const OsPath& pathname, OggStreamPtr& stream) 
    265 { 
    266     PFile file(new File); 
    267     RETURN_STATUS_IF_ERR(file->Open(pathname, L'r')); 
    268  
    269     shared_ptr<OggStreamImpl<VorbisFileAdapter> > tmp(new OggStreamImpl<VorbisFileAdapter>(VorbisFileAdapter(file))); 
    270     RETURN_STATUS_IF_ERR(tmp->Open()); 
    271     stream = tmp; 
    272     return INFO::OK; 
    273 } 
    274  
    275 Status OpenOggNonstream(const PIVFS& vfs, const VfsPath& pathname, OggStreamPtr& stream) 
    276 { 
    277     shared_ptr<u8> contents; 
    278     size_t size; 
    279     RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, contents, size)); 
    280  
    281     shared_ptr<OggStreamImpl<VorbisBufferAdapter> > tmp(new OggStreamImpl<VorbisBufferAdapter>(VorbisBufferAdapter(contents, size))); 
    282     RETURN_STATUS_IF_ERR(tmp->Open()); 
    283     stream = tmp; 
    284     return INFO::OK; 
    285 } 
    286  
    287 #endif  // CONFIG2_AUDIO 
  • source/simulation2/components/CCmpSoundManager.cpp

     
    2424#include "simulation2/MessageTypes.h" 
    2525#include "simulation2/components/ICmpPosition.h" 
    2626#include "simulation2/components/ICmpRangeManager.h" 
    27 #include "sound/SoundGroup.h" 
     27#include "soundmanager/js/SMSoundGroup.h" 
    2828 
    2929class CCmpSoundManager : public ICmpSoundManager 
    3030{ 
     
    3636 
    3737    DEFAULT_COMPONENT_ALLOCATOR(SoundManager) 
    3838 
    39     std::map<std::wstring, CSoundGroup*> m_SoundGroups; 
     39    std::map<std::wstring, CSMSoundGroup*> m_SoundGroups; 
    4040 
    4141    static std::string GetSchema() 
    4242    { 
     
    4949 
    5050    virtual void Deinit() 
    5151    { 
    52         for (std::map<std::wstring, CSoundGroup*>::iterator it = m_SoundGroups.begin(); it != m_SoundGroups.end(); ++it) 
     52        for (std::map<std::wstring, CSMSoundGroup*>::iterator it = m_SoundGroups.begin(); it != m_SoundGroups.end(); ++it) 
    5353            delete it->second; 
    5454        m_SoundGroups.clear(); 
    5555    } 
     
    7676            // or on some other timer? 
    7777            const CMessageUpdate& msgData = static_cast<const CMessageUpdate&> (msg); 
    7878            float t = msgData.turnLength.ToFloat(); 
    79             for (std::map<std::wstring, CSoundGroup*>::iterator it = m_SoundGroups.begin(); it != m_SoundGroups.end(); ++it) 
     79            for (std::map<std::wstring, CSMSoundGroup*>::iterator it = m_SoundGroups.begin(); it != m_SoundGroups.end(); ++it) 
    8080                if (it->second) 
    8181                    it->second->Update(t); 
    8282            break; 
     
    8787    virtual void PlaySoundGroup(std::wstring name, entity_id_t source) 
    8888    { 
    8989        // Make sure the sound group is loaded 
    90         CSoundGroup* group; 
     90        CSMSoundGroup* group; 
    9191        if (m_SoundGroups.find(name) == m_SoundGroups.end()) 
    9292        { 
    93             group = new CSoundGroup(); 
     93            group = new CSMSoundGroup(); 
    9494            if (!group->LoadSoundGroup(L"audio/" + name)) 
    9595            { 
    9696                LOGERROR(L"Failed to load sound group '%ls'", name.c_str()); 
  • binaries/data/mods/public/audio/voice/hellenes/civ/civ_male_attack.xml

     
    88    <ConeOuter>360</ConeOuter> 
    99    <Looping>0</Looping> 
    1010    <RandOrder>1</RandOrder> 
     11    <Distanceless>1</Distanceless> 
    1112    <RandGain>1</RandGain> 
    1213    <GainUpper>1</GainUpper> 
    1314    <GainLower>0.8</GainLower> 
  • binaries/data/mods/public/audio/voice/hellenes/civ/civ_female_select.xml

     
    99    <Looping>0</Looping> 
    1010    <RandOrder>1</RandOrder> 
    1111    <RandGain>1</RandGain> 
     12    <Distanceless>1</Distanceless> 
    1213    <GainUpper>1</GainUpper> 
    1314    <GainLower>0.8</GainLower> 
    1415    <RandPitch>1</RandPitch> 
  • binaries/data/mods/public/audio/voice/hellenes/civ/civ_male_ack.xml

     
    88    <ConeOuter>360</ConeOuter> 
    99    <Looping>0</Looping> 
    1010    <RandOrder>1</RandOrder> 
     11    <Distanceless>1</Distanceless> 
    1112    <RandGain>1</RandGain> 
    1213    <GainUpper>1</GainUpper> 
    1314    <GainLower>0.8</GainLower> 
  • binaries/data/mods/public/audio/voice/hellenes/civ/civ_male_select.xml

     
    88    <ConeOuter>360</ConeOuter> 
    99    <Looping>0</Looping> 
    1010    <RandOrder>1</RandOrder> 
     11    <Distanceless>1</Distanceless> 
    1112    <RandGain>1</RandGain> 
    1213    <GainUpper>1</GainUpper> 
    1314    <GainLower>0.8</GainLower> 
  • binaries/data/mods/public/gui/session/session.js

     
    408408//          currentAmbient = newRandomSound("ambient", "temperate_", "dayscape"); 
    409409 
    410410            const AMBIENT = "audio/ambient/dayscape/day_temperate_gen_03.ogg"; 
    411             currentAmbient = new Sound(AMBIENT); 
     411            currentAmbient = new AmbientSound(AMBIENT); 
    412412 
    413413            if (currentAmbient) 
    414414            { 
    415415                currentAmbient.loop(); 
    416                 currentAmbient.setGain(0.8); 
    417416            } 
    418417            break; 
    419418 
     
    428427{ 
    429428    if (currentAmbient) 
    430429    { 
    431         currentAmbient.fade(-1, 0.0, 5.0); 
     430        currentAmbient.free(); 
    432431        currentAmbient = null; 
    433432    } 
    434433} 
  • binaries/data/mods/public/gui/common/music.js

     
    7272        switch (this.currentState) 
    7373        { 
    7474            case this.states.OFF: 
    75                     if (this.isPlaying()) 
    7675                { 
    77                     this.currentMusic.fade(-1, 0.0, 3.0); 
    78                     this.currentMusic = null; 
     76                    var thePlayer = SoundPlayer(); 
     77                    thePlayer.stopMusic(); 
    7978                } 
    8079                break; 
    8180 
     
    146145 
    147146Music.prototype.switchMusic = function(track, fadeInPeriod, isLooping) 
    148147{ 
    149     if (this.currentMusic) 
    150     { 
    151         this.currentMusic.fade(-1, 0.0, 5.0); 
    152         this.currentMusic = null; 
    153     } 
     148    this.currentMusic = new MusicSound(this.RELATIVE_MUSIC_PATH + track); 
    154149 
    155     this.currentMusic = new Sound(this.RELATIVE_MUSIC_PATH + track); 
    156  
    157150    if (this.currentMusic) 
    158151    { 
    159152        if (isLooping) 
    160153            this.currentMusic.loop(); 
    161154        else 
    162155            this.currentMusic.play(); 
    163  
    164         if (fadeInPeriod) 
    165             this.currentMusic.fade(0.0, this.musicGain, fadeInPeriod); 
    166156    } 
    167157}; 
    168158 
     
    180170 
    181171Music.prototype.start = function() 
    182172{ 
     173    var thePlayer = SoundPlayer(); 
     174    thePlayer.startMusic(); 
    183175    this.setState(this.states.PEACE); 
    184176}; 
    185177 
  • binaries/data/mods/public/gui/common/functions_utility_music.js

     
    6666 
    6767    //console.write("Playing " + randomSoundPath + " ..."); 
    6868 
     69    switch (soundType) 
     70    { 
     71        case "music": 
     72            return new MusicSound(randomSoundPath); 
     73        break; 
     74        case "ambient": 
     75            return new AmbientSound(randomSoundPath); 
     76        break; 
     77        case "effect": 
     78            console.write ("am loading effect '*"+randomSoundPath+"*'"); 
     79        break; 
     80        default: 
     81        break; 
     82    } 
    6983    return new Sound(randomSoundPath); 
    7084} 
    7185 
  • binaries/data/mods/public/hwdetect/hwdetect.js

     
    213213    } 
    214214 
    215215    // http://trac.wildfiregames.com/ticket/685 
    216     if (os_macosx) 
    217     { 
    218         warnings.push("Audio has been disabled, due to problems with OpenAL on OS X."); 
    219         disable_audio = true; 
    220     } 
     216//  if (os_macosx) 
     217//  { 
     218//      warnings.push("Audio has been disabled, due to problems with OpenAL on OS X."); 
     219//      disable_audio = true; 
     220//  } 
    221221 
    222222    // http://trac.wildfiregames.com/ticket/684 
    223223    // https://bugs.freedesktop.org/show_bug.cgi?id=24047 
  • binaries/data/config/default.cfg

     
    7373 
    7474; GENERAL PREFERENCES: 
    7575 
    76 sound.mastergain = 0.5 
     76sound.mastergain = 0.9 
     77sound.musicgain = 0.2 
     78sound.ambientgain = 0.6 
     79sound.actiongain = 0.7 
     80sound.bufferCount = 50 
     81sound.bufferSize = 65536 
    7782 
    7883; Camera control settings 
    7984view.scroll.speed = 120.0