int main( int argc, char **argv )
{

    osg::notify(osg::WARN) << "\n\n" << osgAudio::getLibraryName() << " demo" << std::endl;
    osg::notify(osg::WARN) << "Version: " << osgAudio::getVersion() << "\n\n" << std::endl;

    osg::notify(osg::WARN) << "Demonstrates occluders" << std::endl;


    try {
        // use an ArgumentParser object to manage the program arguments.
        osg::ArgumentParser arguments(&argc,argv);

        // set up the usage document, in case we need to print out how to use this program.
        arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" demonstrates the use of the osgAudio toolkit for spatial sound.");
        arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
        arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");

        // initialize the viewer.
        osgViewer::Viewer viewer(arguments);

        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
        keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
        viewer.setCameraManipulator( keyswitchManipulator.get() );

        // add the window size toggle handler
        viewer.addEventHandler(new osgViewer::WindowSizeHandler);

        // add the stats handler
        viewer.addEventHandler(new osgViewer::StatsHandler);

        // add the help handler
        viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));

        // get details on keyboard and mouse bindings used by the viewer.
        viewer.getUsage(*arguments.getApplicationUsage());

        // if user request help write it out to cout.
        if (arguments.read("-h") || arguments.read("--help"))
        {
            arguments.getApplicationUsage()->write(std::cout);
            return 1;
        }

        // any option left unread are converted into errors to write out later.
        arguments.reportRemainingOptionsAsUnrecognized();

        arguments.getApplicationUsage()->addKeyboardMouseBinding("RETURN", "Play a sound");


        // report any errors if they have occured when parsing the program aguments.
        if (arguments.errors())
        {
            arguments.writeErrorMessages(std::cout);
            return 1;
        }

        // initialize the SoundManager before loading any files
        osgAudio::SoundManager::instance()->init(16);
        osgAudio::SoundManager::instance()->getEnvironment()->setDistanceModel(osgAudio::InverseDistance);
        osgAudio::SoundManager::instance()->getEnvironment()->setDopplerFactor(1);


        osg::ref_ptr<osg::Group> rootnode = new osg::Group;
        // load the nodes from the commandline arguments.
        osg::Node* model = osgDB::readNodeFiles(arguments); //createModel();
        if (!model)
        {
            osg::notify(osg::FATAL) << "Error loading models from commandline" << std::endl;
            return 1;
        }
        osg::ref_ptr<osg::PositionAttitudeTransform> loaded_transform = new osg::PositionAttitudeTransform;
        loaded_transform->addChild(model);
        rootnode->addChild(loaded_transform.get());


        // Create ONE (only one, otherwise the transformation of the listener and update for SoundManager will be
        // called several times, which is not catastrophic, but unnecessary) 
        // SoundRoot that will make sure the listener is updated and
        // to keep the internal state of the SoundManager updated
        // This could also be done manually, this is just a handy way of doing it.
        osg::ref_ptr<osgAudio::SoundRoot> sound_root = new osgAudio::SoundRoot;
        sound_root->setCamera( viewer.getCamera() );



        // The position in the scenegraph of this node is not important.
        // Just as long as the cull traversal should be called after any changes to the SoundManager are made.
        rootnode->addChild(sound_root.get());


        bool occlude = true;

        osg::ref_ptr<osg::PositionAttitudeTransform> sound_transform = createSoundNode("a.wav", occlude, rootnode.get(), false);

        rootnode->addChild(sound_transform.get());



        // run optimization over the scene graph
        //osgUtil::Optimizer optimizer;
        //optimizer.optimize(rootnode.get());

        // set the scene to render
        viewer.setSceneData(rootnode.get());


        // create the windows and run the threads.
        viewer.realize();

        osg::Timer_t start = osg::Timer::instance()->tick();
        float rate=10; // degrees per second
        while( !viewer.done() )
        {
            osg::Timer_t now = osg::Timer::instance()->tick();
            double dt = osg::Timer::instance()->delta_s(start, now);
            double angle = rate*dt;

            osg::Quat quat;
            quat.makeRotate(osg::inDegrees(angle), osg::Vec3(0,0,1));

            loaded_transform->setAttitude(quat);

            // update the scene by traversing it with the the update visitor which will
            // call all node update callbacks and animations.

            // fire off the cull and draw traversals of the scene.
            viewer.frame();
        }
    }
    catch (std::exception& e) {
        osg::notify(osg::WARN) << "Caught: " << e.what() << std::endl;
    }
    // Very important to call this before end of main.
    // Otherwise OpenAL will do all sorts of strange things after end of main
    // in the destructor of soundmanager.
    if (osg::Referenced::getDeleteHandler()) {
        osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
        osg::Referenced::getDeleteHandler()->flushAll();
    }
    osgAudio::SoundManager::instance()->shutdown();

    return 0;

}
void VoiceReceiver::update( float deltaTime )
{
    assert( _p_udpTransport && "network transport is not available!" );

    // update sound system
    _p_soundSystem->update();

    static char  s_buffer[ 512 ];
    VoicePaket*  p_data   = NULL;
    unsigned int senderID = 0xFFFFFFFF;

    // check for new connecting senders
	RNReplicaNet::Transport* p_sender = _p_udpTransport->Accept();
    if ( p_sender )
    {
        log_debug << "  <- connection accepted from sender: " << p_sender->GetPeerURL() << std::endl;
        
        SoundNode* p_soundnode = createSoundNode();
        _soundNodeMap[ p_sender ] = p_soundnode;
    }

    SenderMap::iterator p_beg = _soundNodeMap.begin(), p_end = _soundNodeMap.end();
    for ( ; p_beg != p_end; ++p_beg )
    {
        SoundNode* p_sendernode = p_beg->second;
        p_sender = p_beg->first;

        // first check for dead senders
        if ( ( p_sendernode->_pingTimer += deltaTime ) > VOICE_LIFESIGN_PERIOD )
        {
            // removing sender 
            log_verbose << "  <- remove sender from receiver list!" << std::endl;

            // remove senders which do not respond anymore
            delete p_sendernode;
            delete p_sender;
            _soundNodeMap.erase( p_beg );
            break;
        }

        // poll incoming data from senders
        if ( p_sender->Recv( s_buffer, 512 ) )
        {
            p_data = reinterpret_cast< VoicePaket* >( s_buffer );
            senderID = p_data->_senderID;
        }
        else
        {
            continue;
        }

        // check if the incoming voice paket belogs to current sender node
        if ( senderID != p_sendernode->_senderID )
        {
            log_error << "internal error on voice paket receiver! sender id mismatch" << std::endl;
            continue;
        }
        switch( p_data->_typeId )
        {
            case NETWORKSOUND_PAKET_TYPE_CON_REQ:
            {
                // get the network id of connecting sender
                int sid = *( reinterpret_cast< int* >( p_data->_p_buffer ) );

                // find the ghost of the sender in voice enabled player list using its network id
                EnPlayer* p_player = NULL;
                EnPlayer* p_senderplayer = NULL;
                const std::vector< yaf3d::BaseEntity* >& players = gameutils::PlayerUtils::get()->getRemotePlayersVoiceChat();
                std::vector< yaf3d::BaseEntity* >::const_iterator p_beg = players.begin(), p_end = players.end();
                for ( ; p_beg != p_end; ++p_beg )
                {
                    p_player = dynamic_cast< EnPlayer* >( *p_beg );
                    if ( p_player->getNetworkID() == sid )
                    {
                        p_senderplayer = p_player;
                    }
                }

                if ( !p_senderplayer )
                    log_error << "Voice Receiver cannot find sending player!" << std::endl;

                p_sendernode->_p_senderPlayer = p_senderplayer;

                p_data->_length   = 0;
                // send back a GRANT if less senders exit then the max allowed
                if ( _soundNodeMap.size() > MAX_SENDERS_CONNECTED )
                {
                    p_data->_typeId   = NETWORKSOUND_PAKET_TYPE_CON_DENY;
                } 
                else
                {
                    p_data->_typeId   = NETWORKSOUND_PAKET_TYPE_CON_GRANT;
                }

                // assign a unique ID to new sender
                ++_senderID;

                // check the id overflow ( this is very uncommon, but we want to be on safe side )
                if ( _senderID > 0xFFFFFFFE )
                    _senderID = 1;

                p_data->_senderID = _senderID;

                // store the sender ID into node
                p_sendernode->_senderID = _senderID;

                p_sender->SendReliable( reinterpret_cast< char* >( p_data ), VOICE_PAKET_HEADER_SIZE  + p_data->_length );

                log_debug << "  <- joining request granted ( " << _soundNodeMap.size() << " )" << std::endl;
            }
            break;

            case NETWORKSOUND_PAKET_TYPE_CON_CLOSE:
            {
                delete p_beg->first;
                delete p_beg->second;
                _soundNodeMap.erase( p_beg );
                p_beg = _soundNodeMap.begin(), p_end = _soundNodeMap.end();
                log_debug << "  <- voice sender leaves ( " << _soundNodeMap.size() << " )" << std::endl;
                break;
            }
            break;

            case NETWORKSOUND_PAKET_TYPE_VOICE_DATA:
            {
                // drop obsolete packets
                if ( p_data->_paketStamp < p_sendernode->_lastPaketStamp )
                {
                    std::stringstream msg;
                    msg << "  <- *** paket loss detected (stamps received/current)" << p_data->_paketStamp << "/" << p_sendernode->_lastPaketStamp;
                    log_debug << msg.str() << std::endl;
                    // update our stamp also when a loss has been detected
                    // currently we cannot know if there was an actual loss or an addition overflow occured in stamp variable
                    // one possible way to handle this issue would be that the protocol would support a sort of "reset stamp number"
                    p_sendernode->_lastPaketStamp = p_data->_paketStamp;
                    continue;
                }
                // udpate sound node's stamp
                p_sendernode->_lastPaketStamp = p_data->_paketStamp;

                // attenuate the voice volume considering a cutoff area where the volume is at maximum
                float attenuation = 1.0;
                if ( p_sendernode->_p_senderPlayer )
                {
                    yaf3d::BaseEntity* p_localplayer = gameutils::PlayerUtils::get()->getLocalPlayer();
                    assert( p_localplayer && "local player is not set in PlayerUtils!" );
                    osg::Vec3f diff = p_sendernode->_p_senderPlayer->getPosition() - p_localplayer->getPosition();
                    float distance = diff.length();
                    attenuation = ( distance < _cutoffRange ) ? 1.0f : std::max( 1.0f - ( ( distance - _cutoffRange ) / ( _spotRange - _cutoffRange ) ), 0.0f );
                }

                // decode and enqueue the samples
                if ( !p_sendernode->_p_codec->decode( p_data->_p_buffer, p_data->_length, *p_sendernode->_p_sampleQueue, _outputGain * attenuation ) )
                {
                    log_debug << "decoder queue overrun, flushing queue!" << std::endl;
                    delete p_sendernode->_p_sampleQueue;
                    p_sendernode->_p_sampleQueue = new SoundSampleQueue;
                }
            }
            break;

            case NETWORKSOUND_PAKET_TYPE_CON_PING:
            {
                // send back a pong
                p_data->_length = 0;
                p_data->_typeId = NETWORKSOUND_PAKET_TYPE_CON_PONG;
                p_sender->SendReliable( reinterpret_cast< char* >( p_data ), VOICE_PAKET_HEADER_SIZE  + p_data->_length );
                // reset the ping timer
                p_sendernode->_pingTimer = 0.0f;
            }
            break;

            default:
                ;
        }
    }
}