DWORD FmodSoundEngine::PlayMusic(int sound_id, CHANNEL Ichannel, bool loop, float volume)
{
	if(Ichannel<0||Ichannel>=maxChannel)
	{
		Ichannel = GetNewChannel();
		if(Ichannel<0)
			return NULL;
	}
	FMOD::Sound *sound = (FMOD::Sound*)playMusic[sound_id];
	FMOD::Channel **channel = (FMOD::Channel**)this->channel;
	nowChannel = max(nowChannel,Ichannel+1);
	if(loop)
	{
		result = sound->setMode(FMOD_LOOP_NORMAL);
		//ERRCHECK(result);
	}
	if(Ichannel==0){
		float freq;
		sound->getDefaults(&freq,NULL);
		music_sampleRate = freq;
	}
	result = system->playSound(sound, NULL, true, &channel[Ichannel]);
	//ERRCHECK(result);
	volumeDes[Ichannel] = volume*defaultVolume;
	result = channel[Ichannel]->setVolume(volumeDes[Ichannel]);
	//ERRCHECK(result);
	SetPosition(Ichannel, sound_id, 0);
	//ERRCHECK(result);
	result = channel[Ichannel]->setPaused(false);
	//ERRCHECK(result);

	if(Ichannel==0)
		Update(0);

	return (DWORD)sound;
}
	SPtr<Resource> FMODImporter::import(const Path& filePath, SPtr<const ImportOptions> importOptions)
	{
		WString extension = filePath.getWExtension();
		StringUtil::toLowerCase(extension);

		AudioFileInfo info;

		FMOD::Sound* sound;
		String pathStr = filePath.toString();
		if(gFMODAudio()._getFMOD()->createSound(pathStr.c_str(), FMOD_CREATESAMPLE, nullptr, &sound) != FMOD_OK)
		{
			LOGERR("Failed importing audio file: " + pathStr);
			return nullptr;
		}

		FMOD_SOUND_FORMAT format;
		INT32 numChannels = 0;
		INT32 numBits = 0;

		sound->getFormat(nullptr, &format, &numChannels, &numBits);

		if(format != FMOD_SOUND_FORMAT_PCM8 && format != FMOD_SOUND_FORMAT_PCM16 && format != FMOD_SOUND_FORMAT_PCM24 
			&& format != FMOD_SOUND_FORMAT_PCM32 && format != FMOD_SOUND_FORMAT_PCMFLOAT)
		{
			LOGERR("Failed importing audio file, invalid imported format: " + pathStr);
			return nullptr;
		}

		float frequency = 0.0f;
		sound->getDefaults(&frequency, nullptr);

		UINT32 size;
		sound->getLength(&size, FMOD_TIMEUNIT_PCMBYTES);
		
		info.bitDepth = numBits;
		info.numChannels = numChannels;
		info.sampleRate = (UINT32)frequency;
		info.numSamples = size / (info.bitDepth / 8);

		UINT32 bytesPerSample = info.bitDepth / 8;
		UINT32 bufferSize = info.numSamples * bytesPerSample;
		UINT8* sampleBuffer = (UINT8*)bs_alloc(bufferSize);
		assert(bufferSize == size);
		
		UINT8* startData = nullptr;
		UINT8* endData = nullptr;
		UINT32 startSize = 0;
		UINT32 endSize = 0;
		sound->lock(0, size, (void**)&startData, (void**)&endData, &startSize, &endSize);

		if(format == FMOD_SOUND_FORMAT_PCMFLOAT)
		{
			assert(info.bitDepth == 32);

			UINT32* output = (UINT32*)sampleBuffer;
			for(UINT32 i = 0; i < info.numSamples; i++)
			{
				float value = *(((float*)startData) + i);
				*output = (UINT32)(value * 2147483647.0f);
				output++;
			}
		}
		else
		{
			memcpy(sampleBuffer, startData, bufferSize);
		}

		sound->unlock((void**)&startData, (void**)&endData, startSize, endSize);
		sound->release();

		SPtr<const AudioClipImportOptions> clipIO = std::static_pointer_cast<const AudioClipImportOptions>(importOptions);

		// If 3D, convert to mono
		if (clipIO->getIs3D() && info.numChannels > 1)
		{
			UINT32 numSamplesPerChannel = info.numSamples / info.numChannels;

			UINT32 monoBufferSize = numSamplesPerChannel * bytesPerSample;
			UINT8* monoBuffer = (UINT8*)bs_alloc(monoBufferSize);

			AudioUtility::convertToMono(sampleBuffer, monoBuffer, info.bitDepth, numSamplesPerChannel, info.numChannels);

			info.numSamples = numSamplesPerChannel;
			info.numChannels = 1;

			bs_free(sampleBuffer);

			sampleBuffer = monoBuffer;
			bufferSize = monoBufferSize;
		}

		// Convert bit depth if needed
		if (clipIO->getBitDepth() != info.bitDepth)
		{
			UINT32 outBufferSize = info.numSamples * (clipIO->getBitDepth() / 8);
			UINT8* outBuffer = (UINT8*)bs_alloc(outBufferSize);

			AudioUtility::convertBitDepth(sampleBuffer, info.bitDepth, outBuffer, clipIO->getBitDepth(), info.numSamples);

			info.bitDepth = clipIO->getBitDepth();

			bs_free(sampleBuffer);

			sampleBuffer = outBuffer;
			bufferSize = outBufferSize;
		}

		// Encode to Ogg Vorbis if needed
		if (clipIO->getFormat() == AudioFormat::VORBIS)
		{



			// TODO - Encode to Ogg Vorbis!




		}

		SPtr<MemoryDataStream> sampleStream = bs_shared_ptr_new<MemoryDataStream>(sampleBuffer, bufferSize);

		AUDIO_CLIP_DESC clipDesc;
		clipDesc.bitDepth = info.bitDepth;
		clipDesc.format = clipIO->getFormat();
		clipDesc.frequency = info.sampleRate;
		clipDesc.numChannels = info.numChannels;
		clipDesc.readMode = clipIO->getReadMode();
		clipDesc.is3D = clipIO->getIs3D();

		SPtr<AudioClip> clip = AudioClip::_createPtr(sampleStream, bufferSize, info.numSamples, clipDesc);

		WString fileName = filePath.getWFilename(false);
		clip->setName(fileName);

		return clip;
	}
Exemple #3
0
UBOOL USwFMOD::PlayMusic( UMusic* Music, FMOD::Channel* channel, BYTE SongSection, BYTE CdTrack, EMusicTransition Transition )
{
	guard(USwFMOD::PlayMusic);
	//SWF_LOG( NAME_DevSound, TEXT("%s >> %s :: [%s]"), SWF_PLOG, *ToStr(Music), *ToStr(SongSection), *ToStr(CdTrack), *ToStr(Transition)  );
	FMOD_RESULT result;

	if( !Music )
		return 0;

	/*if( channel )
	{
		// there can be only one music
		//SWF_LOG( NAME_DevSound, TEXT("%s :: %s :: StopMusic %s"), SWF_PLOG, *PrintChannel(musicchannel) );
		SWF_FMOD_RCALL0( musicchannel->setUserData(NULL) );
		SWF_FMOD_RCALL0( musicchannel->stop() );
	}*/


	// Non-modules use one UMusic object for each SongSection
	if( Music->FileType == SoundType_OGG
	||	Music->FileType == SoundType_MP2 
	||	Music->FileType == SoundType_MP3 )
	{
		Viewport->Actor->SongSection = 0;

		FString postfix = GetSongSectionPostfix(SongSection);
		if( postfix )
		{
			FString objname = FString::Printf( TEXT("%s%s.%s%s"), Music->GetName(), *postfix, Music->GetName(), *postfix);
			UMusic* newmusic = Cast<UMusic>(StaticLoadObject( Music->GetClass(), NULL, *objname, NULL, LOAD_NoWarn | LOAD_Quiet, NULL ));
			if( newmusic )
			{
				SWF_LOG( NAME_DevSound, TEXT("%s :: %s :: Dynamic Music: %s"), SWF_PLOG, *ToStr(objname) );
				Music = newmusic;
				Viewport->Actor->SongSection = SongSection;
			}
		}

		SongSection = Viewport->Actor->SongSection;
	}



	// Register sample
	FMOD::Sound* sample = RegisterMusicSample(Music);
	if( !sample )
		return 0;

	// Sample defaults
	FLOAT deffrequency;
	FLOAT defvolume;
	FLOAT defpanning;
	INT defpriority;
	FMOD_MODE fmode;
	SWF_FMOD_RCALL0( sample->getDefaults( &deffrequency, &defvolume, &defpanning, &defpriority ) );
	SWF_FMOD_RCALL0( sample->getMode(&fmode) );

	// Channel defaults
	FMOD::ChannelGroup* channelgroup = MusicChannels;
	FMOD_CHANNELINDEX channelindex = IsChannelValid(channel) ? FMOD_CHANNEL_REUSE : FMOD_CHANNEL_FREE;
	FLOAT frequency = deffrequency * 1.0f;
	FLOAT volume = defvolume * 1.0f;
	FLOAT panning = defpanning * 1.0f;
	INT priority = PriorityMusic;


	// Update FMOD to free finished channels
	guard(UpdateFMOD);
	SWF_FMOD_RCALL0( System->update() );
	unguard;

	// Play stream
	SWF_FMOD_RCALL0( System->playSound(channelindex, sample, true, &channel) );
//	SWF_FMOD_RCALL0( VerifyNewChannel(channel) );

	// Update channel
	SWF_FMOD_RCALL0( channel->setUserData(NULL) );
	SWF_FMOD_RCALL0( channel->setChannelGroup(channelgroup) );
	SWF_FMOD_RCALL0( channel->setMode(fmode) );
	if( !HasFlag(fmode,FMOD_3D) )
	{
		SWF_FMOD_RCALL0( channel->setPan(panning) );
	}
	SWF_FMOD_RCALL0( channel->setFrequency(frequency) );
	SWF_FMOD_RCALL0( channel->setVolume(volume) );
	SWF_FMOD_RCALL0( channel->setPriority(priority) );

	// restore position
	result = channel->setPosition(SongSection,FMOD_TIMEUNIT_MODORDER);
	FSwPosition pos = MusicPositions(SongSection);
	if( pos.ms != 0 )
	{
		channel->setPosition(pos.ms,FMOD_TIMEUNIT_MS);
	}
	else
	{
		// TODO:: doesn't work?!
		result = channel->setPosition(pos.pattern,FMOD_TIMEUNIT_MODPATTERN);
		result = channel->setPosition(pos.row,FMOD_TIMEUNIT_MODROW);
	}

	SWF_FMOD_RCALL0( channel->setPaused(false) );

	// Apply channel group's properties
	ApplyChannelGroup(channelgroup);

	// Update immediately
	Viewport->Actor->SongSection = SongSection;

	// Update FMOD to apply channel updates immediately
	guard(UpdateFMOD);
	SWF_FMOD_RCALL0( System->update() );
	unguard;

	//SWF_LOG( NAME_DevSound, TEXT("%s << %s :: [%s]"), SWF_PLOG, *ToStr(Music), *ToStr(SongSection), *ToStr(CdTrack), *ToStr(Transition)  );
	return 1;
	unguard;
}
Exemple #4
0
UBOOL USwFMOD::PlaySound
(
	AActor*	Actor,
	INT		Id,
	USound*	Sound,
	FVector	Location,
	FLOAT	Volume,
	FLOAT	Radius,
	FLOAT	Pitch
)
{
	guard(USwFMOD::PlaySound);
	//SWF_LOG( NAME_DevSound, TEXT("%s >> %s :: [%s],[%s],[%s],[%s],[%s],[%s],[%s]"), SWF_PLOG, *ToStr(FSwSoundId(Id)), *ToStr(Actor,true), *ToStr(Sound), *ToStr(Location), *ToStr(Volume), *ToStr(Radius), *ToStr(Pitch) );
	FMOD_RESULT result;

	if( !Viewport || !Sound )
		return 0;

	// Sound slot rules
	FSwSoundId SoundId = FSwSoundId(Id);
	if( SoundId.GetSlot() != SLOT_None )
	{
		for( FSwChannelEnumerator it(System); it; ++it )
		{
			FMOD::Channel* channel = *it;
			if( IsChannelValid(channel) )
			{
				FSwSoundId cid = GetChannelId(channel);

				// If Actor is the same and Slot is the same:
				// - prevent this sound from plaing if NoOverride
				// - stop the old sound otherwise
				if( cid.GetSlot() == SoundId.GetSlot()
				&&	cid.GetActor() == SoundId.GetActor() )
				{
					if( cid.GetNoOverride() )
					{
						//SWF_LOG( NAME_DevSound, TEXT("%s -- %s :: NO OVERRIDE <%s> <%s>"), SWF_PLOG, *ToStr(FSwSoundId(Id)), *PrintChannel(channel) );
						return 0;
					}
					//SWF_LOG( NAME_DevSound, TEXT("%s -- %s :: STOP <%s> <%s>"), SWF_PLOG, *ToStr(SoundId), *PrintChannel(channel) );
					SWF_FMOD_CALL( channel->setUserData(NULL) );
					SWF_FMOD_CALL( channel->stop() );
				}
			}
		}
	}

	// Register sample
	FMOD::Sound* sample = RegisterSoundSample(Sound);
	if( !sample )
		return 0;

	// Sample defaults
	FLOAT deffrequency;
	FLOAT defvolume;
	FLOAT defpanning;
	INT defpriority;
	FMOD_MODE fmode;
	SWF_FMOD_RCALL0( sample->getDefaults( &deffrequency, &defvolume, &defpanning, &defpriority ) );
	SWF_FMOD_RCALL0( sample->getMode(&fmode) );

	// Channel defaults
	FMOD::ChannelGroup* channelgroup = EffectChannels;
	FMOD_CHANNELINDEX channelindex = FMOD_CHANNEL_FREE;
	FLOAT mindist = ToFMODFloat(DistanceMin);
	FLOAT radius = ToFMODFloat(Radius);
	TSwSortMinMax( mindist, radius );
	FMOD_VECTOR location = ToFMODVector(Location); 
	FMOD_VECTOR velocity = ToFMODNormal(FVector(0,0,0));
	FLOAT frequency = deffrequency * Pitch;
	FLOAT volume = defvolume * Volume;
	FLOAT panning = defpanning * 1.0f;
	INT priority = PrioritySound; 

	// 2D or 3D?
	AActor* ViewActor = Viewport->Actor->ViewTarget ? Viewport->Actor->ViewTarget : Viewport->Actor;
	UBOOL bIs3D = Actor && (b3DCameraSounds || Actor != ViewActor);
	if( bIs3D )
	{
		fmode &= ~FMOD_2D;
		fmode |= FMOD_3D | FMOD_3D_LINEARROLLOFF;

		// Radius, location & Velocity
		FMOD_VECTOR location = ToFMODVector(Actor->Location); 
		FMOD_VECTOR velocity = ToFMODVector(Actor->Velocity); 

		SWF_VERIFY_FLOAT( mindist );
		SWF_VERIFY_FLOAT( radius );
		SWF_VERIFY_FMODVECTOR( location );
		SWF_VERIFY_FMODVECTOR( velocity );
	}
	else
	{
		fmode &= ~FMOD_3D;
		fmode |= FMOD_2D;
	}

	// Sound effects other than WAV go into compressed group (conversations most likely)
	if( Sound->FileType != SoundType_WAV )
	{
		priority = PrioritySpeech;
		channelgroup = CompressedChannels;
		//fmode |= FMOD_CREATESTREAM;
	}
	
	// Ambient sounds
	if( SoundId.GetSlot() == SLOT_Ambient )
	{
		priority = PriorityAmbient;
		channelgroup = AmbientChannels;
		fmode &= ~FMOD_LOOP_OFF;
		fmode |= FMOD_LOOP_NORMAL;
	}


	// Update FMOD to free finished channels
	guard(UpdateFMOD);
	SWF_FMOD_RCALL0( System->update() );
	unguard;

	// Play sound
	FMOD::Channel* channel;
    SWF_FMOD_RCALL0( System->playSound(channelindex, sample, true, &channel) );
//	SWF_FMOD_RCALL0( VerifyNewChannel(channel) );

	// Update channel
	SWF_FMOD_RCALL0( channel->setUserData( SoundId.ToUserData() ) );
	SWF_FMOD_RCALL0( channel->setChannelGroup(channelgroup) );
	SWF_FMOD_RCALL0( channel->setMode(fmode) );
	if( HasFlag(fmode,FMOD_3D) )
	{
		SWF_FMOD_RCALL0( channel->set3DMinMaxDistance(mindist, radius) );
		SWF_FMOD_RCALL0( channel->set3DAttributes(&location,&velocity) );
	}
	else
	{
		SWF_FMOD_RCALL0( channel->setPan(panning) );
	}
	SWF_FMOD_RCALL0( channel->setFrequency(frequency) );
	SWF_FMOD_RCALL0( channel->setVolume(volume) );
	SWF_FMOD_RCALL0( channel->setPriority(priority) );
	SWF_FMOD_RCALL0( channel->setPaused(false) );

	// Apply channel group's properties
	ApplyChannelGroup(channelgroup);

	// Update FMOD to apply channel updates immediately
	guard(UpdateFMOD);
	SWF_FMOD_RCALL0( System->update() );
	unguard;

	//SWF_LOG( NAME_DevSound, TEXT("%s << %s :: [%s],[%s],[%s],[%s],[%s],[%s],[%s]"), SWF_PLOG, *ToStr(Actor,true), *ToStr(Id), *ToStr(Sound), *ToStr(Location), *ToStr(Volume), *ToStr(Radius), *ToStr(Pitch) );
	return 1;
	unguard;
}
int FMOD_Main()
{
    FMOD::System           *system;
    FMOD::Sound            *sound[3];
    FMOD::Channel          *channel = 0;
    FMOD::ChannelGroup     *channelgroup = 0;
    FMOD_RESULT             result;
    unsigned int            version, dsp_block_len, count;
    int                     outputrate = 0;
    void                   *extradriverdata = 0;
    
    Common_Init(&extradriverdata);

    /*
        Create a System object and initialize.
    */
    result = FMOD::System_Create(&system);
    ERRCHECK(result);

    result = system->getVersion(&version);
    ERRCHECK(result);

    if (version < FMOD_VERSION)
    {
        Common_Fatal("FMOD lib version %08x doesn't match header version %08x", version, FMOD_VERSION);
    }
    
    result = system->init(100, FMOD_INIT_NORMAL, extradriverdata);
    ERRCHECK(result);

    /*
        Get information needed later for scheduling.  The mixer block size, and the output rate of the mixer.
    */
    result = system->getDSPBufferSize(&dsp_block_len, 0);
    ERRCHECK(result);

    result = system->getSoftwareFormat(&outputrate, 0, 0);
    ERRCHECK(result);

    /*
        Load 3 sounds - these are just sine wave tones at different frequencies.  C, D and E on the musical scale.
    */
    result = system->createSound(Common_MediaPath("c.ogg"), FMOD_DEFAULT, 0, &sound[NOTE_C]);
    ERRCHECK(result);
    result = system->createSound(Common_MediaPath("d.ogg"), FMOD_DEFAULT, 0, &sound[NOTE_D]);
    ERRCHECK(result);
    result = system->createSound(Common_MediaPath("e.ogg"), FMOD_DEFAULT, 0, &sound[NOTE_E]);
    ERRCHECK(result);

    /* 
        Create a channelgroup that the channels will play on.  We can use this channelgroup as our clock reference. 
        It also means we can pause and pitch bend the channelgroup, without affecting the offsets of the delays, because the channelgroup clock
        which the channels feed off, will be pausing and speeding up/slowing down and still keeping the children in sync.
    */
    result = system->createChannelGroup("Parent", &channelgroup);
    ERRCHECK(result);

    unsigned int numsounds = sizeof(note) / sizeof(note[0]);

    /*
        Play all the sounds at once! Space them apart with set delay though so that they sound like they play in order.
    */
    for (count = 0; count < numsounds; count++)
    {
        static unsigned long long clock_start = 0;
        unsigned int slen;
        FMOD::Sound *s = sound[note[count]];                            /* Pick a note from our tune. */

        result = system->playSound(s, channelgroup, true, &channel);    /* Play the sound on the channelgroup we want to use as the parent clock reference (for setDelay further down) */
        ERRCHECK(result);

        if (!clock_start)
        {
            result = channel->getDSPClock(0, &clock_start);
            ERRCHECK(result);

            clock_start += (dsp_block_len * 2);                         /* Start the sound into the future, by 2 mixer blocks worth. */
                                                                        /* Should be enough to avoid the mixer catching up and hitting the clock value before we've finished setting up everything. */
                                                                        /* Alternatively the channelgroup we're basing the clock on could be paused to stop it ticking. */
        }
        else
        {
            float freq;

            result = s->getLength(&slen, FMOD_TIMEUNIT_PCM);            /* Get the length of the sound in samples. */
            ERRCHECK(result);

            result = s->getDefaults(&freq, 0);                          /* Get the default frequency that the sound was recorded at. */
            ERRCHECK(result);

            slen = (unsigned int)((float)slen / freq * outputrate);     /* Convert the length of the sound to 'output samples' for the output timeline. */
            
            clock_start += slen;                                        /* Place the sound clock start time to this value after the last one. */
        }

        result = channel->setDelay(clock_start, 0, false);              /* Schedule the channel to start in the future at the newly calculated channelgroup clock value. */
        ERRCHECK(result);

        result = channel->setPaused(false);                             /* Unpause the sound.  Note that you won't hear the sounds, they are scheduled into the future. */
        ERRCHECK(result);
    }

    /*
        Main loop.
    */
    do
    {
        Common_Update();

        if (Common_BtnPress(BTN_ACTION1))                               /* Pausing the channelgroup as the clock parent, will pause any scheduled sounds from continuing */
        {                                                               /* If you paused the channel, this would not stop the clock it is delayed against from ticking,  */
            bool paused;                                                /* and you'd have to recalculate the delay for the channel into the future again before it was unpaused. */
            result = channelgroup->getPaused(&paused);
            ERRCHECK(result);
            result = channelgroup->setPaused(!paused);
            ERRCHECK(result);
        }
        if (Common_BtnPress(BTN_ACTION2)) 
        {                                 
            for (count = 0; count < 50; count++)
            {
                float pitch;
                result = channelgroup->getPitch(&pitch);
                ERRCHECK(result);
                pitch += 0.01f;
                result = channelgroup->setPitch(pitch);
                ERRCHECK(result);

                result = system->update();
                ERRCHECK(result);

                Common_Sleep(10);
            }
        }
        if (Common_BtnPress(BTN_ACTION3)) 
        {                                 
            for (count = 0; count < 50; count++)
            {
                float pitch;
                result = channelgroup->getPitch(&pitch);
                ERRCHECK(result);

                if (pitch > 0.1f)
                {
                    pitch -= 0.01f;
                }
                result = channelgroup->setPitch(pitch);
                ERRCHECK(result);
                
                result = system->update();
                ERRCHECK(result);

                Common_Sleep(10);
            }
        }

        result = system->update();
        ERRCHECK(result);

        /*
            Print some information
        */
        {
            bool    playing = false;
            bool    paused = false;
            int     chansplaying;

            if (channelgroup)
            {
                result = channelgroup->isPlaying(&playing);
                if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HANDLE))
                {
                    ERRCHECK(result);
                }

                result = channelgroup->getPaused(&paused);
                if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HANDLE))
                {
                    ERRCHECK(result);
                }
            }

            result = system->getChannelsPlaying(&chansplaying);
            ERRCHECK(result);

            Common_Draw("==================================================");
            Common_Draw("Gapless Playback example.");
            Common_Draw("Copyright (c) Firelight Technologies 2004-2014.");
            Common_Draw("==================================================");
            Common_Draw("");
            Common_Draw("Press %s to toggle pause", Common_BtnStr(BTN_ACTION1));
            Common_Draw("Press %s to increase pitch", Common_BtnStr(BTN_ACTION2));
            Common_Draw("Press %s to decrease pitch", Common_BtnStr(BTN_ACTION3));
            Common_Draw("Press %s to quit", Common_BtnStr(BTN_QUIT));
            Common_Draw("");
            Common_Draw("Channels Playing %d : %s", chansplaying, paused ? "Paused " : playing ? "Playing" : "Stopped");
        }

        Common_Sleep(50);
    } while (!Common_BtnPress(BTN_QUIT));

    /*
        Shut down
    */
    result = sound[NOTE_C]->release();
    ERRCHECK(result);
    result = sound[NOTE_D]->release();
    ERRCHECK(result);
    result = sound[NOTE_E]->release();
    ERRCHECK(result);

    result = system->close();
    ERRCHECK(result);
    result = system->release();
    ERRCHECK(result);

    Common_Close();

    return 0;
}
void USwFMOD::Update( FPointRegion Region, FCoords& Coords )
{
	guard(USwFMOD::Update);
	FMOD_RESULT result;
	//SWF_LOG( NAME_DevSound, TEXT("%s >> %s :: [%s],[%s]"), SWF_PLOG, *ToStr(Region), *ToStr(Coords) );

	if( !Viewport || !Viewport->IsValid() || !Viewport->Actor || !Viewport->Actor->IsValid() )
		return;

	// Load geometry
	if( bOcclusion )
	{
		static ULevel* level = NULL;
		if( Viewport->Actor->GetLevel() != level )
		{
			level = Viewport->Actor->GetLevel();
			LoadGeometry();
		}
	}

	// Time passes...
	DOUBLE DeltaTime = appSeconds() - LastTime;
	LastTime += DeltaTime;
	DeltaTime = Clamp( DeltaTime, 0.0, 1.0 );

	// Get viewactor
	AActor* ViewActor = Viewport->Actor->ViewTarget ? Viewport->Actor->ViewTarget : Viewport->Actor;
	
	// Is viewport realtime ?
	UBOOL Realtime = Viewport->IsRealtime() && Viewport->Actor->Level->Pauser == TEXT("");
	
	// Default viewport coords
	FVector location = FVector(0,0,0);
	FVector velocity = FVector(0,0,0);
	FVector forward = FVector(1,0,0);
	FVector up = FVector(0,0,1);


	//
	// Update listener
	//
	guard(USwFMODAudio::Update::UpdateListener);

	// Use ViewActor coords
	FCoords coords = GMath.UnitCoords / ViewActor->Rotation;;
	coords.Origin = ViewActor->Location;

	// Set viewport coords
	location = coords.Origin;
	velocity = ViewActor->Velocity;
	forward = coords.XAxis;
	up = coords.ZAxis;

	// verify
	SWF_VERIFY_FVECTOR(location);
	SWF_VERIFY_FVECTOR(velocity);
	SWF_VERIFY_FVECTOR_NORMAL(forward);
	SWF_VERIFY_FVECTOR_NORMAL(up);

	// Default listener coords
	FMOD_VECTOR fm_pos = {0,0,0};
	FMOD_VECTOR fm_vel = {0,0,0};
	FMOD_VECTOR fm_fwd = {0,0,1};
	FMOD_VECTOR fm_up  = {0,1,0};

	// Set listener coords
	fm_pos = ToFMODVector(location);
	fm_vel = ToFMODVector(velocity);
	fm_fwd = ToFMODNormal(forward);
	fm_up = ToFMODNormal(up);

	// verify
	SWF_VERIFY_FMODVECTOR(fm_pos);
	SWF_VERIFY_FMODVECTOR(fm_vel);
	SWF_VERIFY_FMODVECTOR_NORMAL(fm_fwd);
	SWF_VERIFY_FMODVECTOR_NORMAL(fm_up);

	// Update
	SWF_FMOD_CALL( System->set3DListenerAttributes( 0, &fm_pos, &fm_vel, &fm_fwd, &fm_up ) );
	unguard;


	//
	// Zone effects
	//
	guard(USwFMODAudio::Update::UpdateZone);

	/*// Default zone properties
	UBOOL bWaterZone = 0;
	UBOOL bReverbZone = 0;
	UBOOL bRaytraceReverb = 0;
	BYTE MasterGain = 100;
	INT CutoffHz = 6000;
	BYTE Delay[6] = {20,34,0,0,0,0};
	BYTE Gain[6] = {150,70,0,0,0,0};

	// Get zone properties
	if( Region.Zone && Region.Zone->IsValid() )
	{
		bWaterZone = Region.Zone->bWaterZone;
		bReverbZone = Region.Zone->bReverbZone;
		bRaytraceReverb = Region.Zone->bRaytraceReverb;
		MasterGain = Region.Zone->MasterGain;
		CutoffHz = Region.Zone->CutoffHz;
		appMemcpy(Delay, Region.Zone->Delay, 6*sizeof(BYTE));
		appMemcpy(Gain, Region.Zone->Gain, 6*sizeof(BYTE));
	}*/
	unguard;



	//
	// Update sounds.
	//
	guard(USwFMODAudio::Update::UpdateSounds);

	// Iterate through all channels
	for( FSwChannelEnumerator it(System); it; ++it )
	{
		FMOD::Channel* channel = *it;
		if( !IsChannelValid(channel) )
			continue;

		// Channel data
		FMOD::Sound* sample = GetChannelSample(channel);
		if( !sample )
			continue;

		UObject* object = GetSampleObject(sample);
		if( !object )
			continue;
		USound* sound = Cast<USound>(object);

		FSwSoundId id = GetChannelId(channel);
		AActor* actor = id.GetActor();

		FMOD_MODE fmode;
		SWF_FMOD_CALL( channel->getMode(&fmode) );
		UBOOL bIs3D = HasFlag(fmode,FMOD_3D);

		// Sample defaults
		FLOAT deffrequency;
		FLOAT defvolume;
		FLOAT defpanning;
		INT defpriority;
		SWF_FMOD_CALL( sample->getDefaults( &deffrequency, &defvolume, &defpanning, &defpriority ) );
	

		if( sound )
		{
			if( actor && actor->IsValid() )
			{
				// Ambient sounds
				if( id.GetSlot() == SLOT_Ambient )
				{
					if(	actor->AmbientSound != sound
					||	FDist(location, actor->Location) > actor->WorldSoundRadius() + AmbientHysteresis
					||  !Realtime )
					{
						// Stop ambient sound
						//SWF_LOG( NAME_DevSound, TEXT("%s -- %s :: Ambient OUT [%d]"), SWF_PLOG, *ToStr(id) );
						SWF_FMOD_CALL( channel->setUserData(NULL) );
						SWF_FMOD_CALL( channel->stop() );
						continue;
					}
					else
					{
						// Update ambient sound properties.
						FLOAT volume = actor->SoundVolume / 255.0f;
						FLOAT frequency = (actor->SoundPitch / 64.0f) * deffrequency;

						SWF_VERIFY_FLOAT(volume);
						SWF_VERIFY_FLOAT(frequency);

						SWF_FMOD_CALL( channel->setVolume( volume ) );
						SWF_FMOD_CALL( channel->setFrequency( frequency ) );

						if( bIs3D )
						{
							// Update 3D sound properties
							FLOAT mindist = ToFMODFloat(DistanceMin);
							FLOAT radius = ToFMODFloat( actor->WorldSoundRadius() );
							TSwSortMinMax( mindist, radius );

							SWF_VERIFY_FLOAT(radius);
							SWF_VERIFY_FLOAT(mindist);

							SWF_FMOD_CALL( channel->set3DMinMaxDistance( mindist, radius ) );
						}
					}
				}

				if( bIs3D )
				{
					// Update 3D sound properties
					FMOD_VECTOR snd_pos = ToFMODVector(actor->Location);
					FMOD_VECTOR snd_vel = ToFMODVector(actor->Velocity);
			
					SWF_VERIFY_FMODVECTOR(snd_pos);							
					SWF_VERIFY_FMODVECTOR(snd_vel);

					SWF_FMOD_CALL( channel->set3DAttributes(&snd_pos, &snd_vel) );
				}
			}
		}
	}
	unguard;


	//
	// Play ambient sounds
	//
	if( Realtime )
	{
		guard(USwFMODAudio::Update::PlayAmbient);
		for( INT i=0; i<Viewport->Actor->GetLevel()->Actors.Num(); ++i )
		{
			AActor* Actor = Viewport->Actor->GetLevel()->Actors(i);
			if(	Actor
			&&	Actor->AmbientSound
			&&	FDist(location, Actor->Location) <= Actor->WorldSoundRadius() )
			{
				FSwSoundId ambientid = FSwSoundId(Actor,SLOT_Ambient,0);
				//SWF_LOG( NAME_DevSound, TEXT("%s -- %s :: Ambient TEST IN [%s]"), SWF_PLOG, *ToStr(ambientid) );

				// Find this sound in currently playing ones
				FMOD::Channel* ambientchannel = NULL;
				for( FSwChannelEnumerator it(System,AmbientChannels); it; ++it )
				{
					FMOD::Channel* channel = *it;
					if( IsChannelValid(channel) && GetChannelId(channel) == ambientid )
					{
						//SWF_LOG( NAME_DevSound, TEXT("%s -- %s :: Ambient FOUND IN [%s]"), SWF_PLOG, *ToStr(GetChannelId(channel)) );
						ambientchannel = channel;
						break;
					}
				}

				// If not found play ambient
				if( !ambientchannel )
				{
					//SWF_LOG( NAME_DevSound, TEXT("%s -- %s :: Ambient PLAY IN [%s][%s]"), SWF_PLOG, *ToStr(ambientid), *ToStr(Actor->AmbientSound) );
					PlaySound( Actor, ambientid.GetId(), Actor->AmbientSound, Actor->Location, Actor->SoundVolume/255.0f, Actor->WorldSoundRadius(), Actor->SoundPitch/64.0f );
				}
			}
		}
		unguard;
	}
	

	//
	// Music
	//
	guard(UpdateMusic)

/*	REQUIREMENTS

	SongSection is updated at realtime by audio sys
	MTRAN_Fade* only fade out, not in
	music changes caused by transition only
	ttransition reset on change
	MTRAN_None = don't change
	MTRAN_Instant = instant change
	MTRAN_Segue = seamless?
	MTRAN_Fade = 1s fade
	MTRAN_FastFade = 1/3s fade
	MTRAN_SlowFade = 5s fade
*/
	// find music channel
	FMOD::Channel* musicchannel = NULL;
	for( FSwChannelEnumerator it(System,MusicChannels); it; ++it )
	{
		FMOD::Channel* channel = *it;
		if( !IsChannelValid(channel) )
			continue;

		if( !musicchannel )
		{
			musicchannel = channel;
		}
		else
		{
			// there can be only one music
			SWF_LOG( NAME_DevSound, TEXT("%s :: %s :: StopMusic %s"), SWF_PLOG, *PrintChannel(channel) );
			SWF_FMOD_CALL( channel->setUserData(NULL) );
			SWF_FMOD_CALL( channel->stop() );
		}
	}

	if( Viewport->Actor->Transition != MTRAN_None )
	{
		// init fading
		if( MusicFade < 0 )
		{
			SWF_LOG( NAME_DevSound, TEXT("%s :: %s :: Music transition %s S:%s T:%s "), SWF_PLOG
				, *ToStr(Viewport->Actor->Song)
				, *ToStr(Viewport->Actor->SongSection)
				, *ToStr(Viewport->Actor->Transition));

			switch( Viewport->Actor->Transition )
			{
				case MTRAN_Instant:		MusicFadeTime = -1.0f;	break;	// Instant
				case MTRAN_Segue:		MusicFadeTime = -1.0f;			// Instant precached
					if( Viewport->Actor->Song && !Viewport->Actor->Song->Handle )
						RegisterMusic(Viewport->Actor->Song);
					break;
				case MTRAN_Fade:		MusicFadeTime = 1.0f;	break;	// 1s fadeout
				case MTRAN_FastFade:	MusicFadeTime = 0.33f;	break;	// 1/3s fadeout
				case MTRAN_SlowFade:	MusicFadeTime = 5.0f;	break;	// 5s fadeout
				default:				MusicFadeTime = -1.0f;	break;	// Unknown,instant
					
			}
			MusicFade = MusicFadeTime;
		}

		// deduct delta
		MusicFade -= DeltaTime;
		//SWF_LOG( NAME_DevSound, TEXT("%s << %s :: MusicFade %s %s"), SWF_PLOG, *ToStr(MusicFade), *ToStr(MusicFadeTime) );

		if( MusicFade > 0 )
		{
			// fade volume
			if( musicchannel && MusicFadeTime > 0 )
			{
				SWF_FMOD_CALL( musicchannel->setVolume( MusicFade / MusicFadeTime) );
			}
		}
		else
		{
			// play new
			MusicFade = -1;
			Viewport->Actor->Transition = MTRAN_None;
			PlayMusic( Viewport->Actor->Song, musicchannel, Viewport->Actor->SongSection, Viewport->Actor->CdTrack, static_cast<EMusicTransition>(Viewport->Actor->Transition) );
		}
	}
	else
	{
		// Update section
		if( musicchannel )
		{
			// update section
			// FIXME:: getPosition doesn't work with volume 0 (virtual?)
			UINT sec = 0;
			result = musicchannel->getPosition(&sec,FMOD_TIMEUNIT_MODORDER);
			if( result == FMOD_OK )
			{
				Viewport->Actor->SongSection = sec;
			}

			// Update position
			if( IsChannelValid(musicchannel) )
			{
				UINT row = 0;
				result = musicchannel->getPosition(&row,FMOD_TIMEUNIT_MODROW);
				if( result == FMOD_OK )
				{
					// IT/MOD/XM
					UINT pattern = 0;
					result = musicchannel->getPosition(&pattern,FMOD_TIMEUNIT_MODPATTERN);
					if( result == FMOD_OK )
					{
						MusicPositions(Viewport->Actor->SongSection).row = row;
						MusicPositions(Viewport->Actor->SongSection).pattern = pattern;
					}
				}
				else
				{
					// MPEG/OGG
					UINT ms = 0;
					result = musicchannel->getPosition(&ms,FMOD_TIMEUNIT_MS);
					if( result == FMOD_OK )
					{
						MusicPositions(Viewport->Actor->SongSection).ms = ms;
					}
				}
			}
		}
		else if( Viewport->Actor->Song && VolumeMusic > 0 )
		{
			// Restart missing/dropped song (bad channel priorities?)
			SWF_LOG( NAME_DevSound, TEXT("%s :: %s :: Restarting missing song %s S:%s T:%s "), SWF_PLOG
				, *ToStr(Viewport->Actor->Song)
				, *ToStr(Viewport->Actor->SongSection)
				, *ToStr(Viewport->Actor->Transition));
			Viewport->Actor->Transition = MTRAN_Instant;
		}
	}



	unguard;

	// Update FMOD
	guard(UpdateFMOD);
	SWF_FMOD_CALL( System->update() );
	unguard;

	//SWF_LOG( NAME_DevSound, TEXT("%s << %s :: [%s],[%s]"), SWF_PLOG, *ToStr(Region), *ToStr(Coords) );
	unguard;
}