Raised This Month: $12 Target: $400
 3% 

Background Sounds


Post New Thread Reply   
 
Thread Tools Display Modes
vaan123
Veteran Member
Join Date: Sep 2009
Location: Kuwait
Old 09-10-2011 , 12:35   Re: Background Sounds
Reply With Quote #21

Here is a fixed version thanks to liinuus:



Code:
/*
*
*        Background Sounds
*            
*        H3avY Ra1n (nikhilgupta345)
*
*        Description
*        -----------
*
*            This plugin enables you to play background music on your server
*            during the round. It will start playing at the beginning of the 
*            round, and stop at when the round ends. 

*        Cvars
*        -----
*            ba_ambience <0/1> <default:1>                             - turns plugin on or off
*            ba_sound_change <any number greater than 1> <default:1> - how often a new sound is chosen to be played
*
*        Changelog
*        ---------
*        
*            September 05, 2011     - v1.0 -     Initial Release
*            September 06, 2011    - v1.0.1 -    Fixed bug with death sounds not playing at the end of the round
*        
*        Credits
*        -------
*        
*            Vaan123            -     Original Plugin Idea
*            ConnnorMcLeod    -     Helped me fix a problem I was having with trim()
*
*        
*        Plugin Thread: http://forums.alliedmods.net/showthread.php?p=1548443
*
*/

#include < amxmodx >
#include < amxmisc >
#include < cstrike >

#define TASK_LOOPSOUND 1000

new const g_szPlugin[ ] = "Background Sounds";
new const g_szVersion[ ] = "1.0";
new const g_szAuthor[ ] = "H3avY Ra1n";

new const g_szDefaultSounds[ ][ ] = 
{
    "mymusic/soundtrack1.mp3",
    "mymusic/soundtrack2.mp3",
    "mymusic/soundtrack3.wav",
    "mymusic/soundtrack4.wav"
};

new const g_szCtWinSounds[ ][ ] =
{
    "nightcrawlers/humans_win3.wav",
    "nightcrawlers/humans_win4.wav"
};
new const g_szTWinSounds[ ][ ] =
{
    "nightcrawlers/freshmeat.wav"
}

new Array:g_aSounds;

new bool:g_bFileExists;

new g_pPluginOn;
new g_pSoundChange;

new g_iRandom;

new g_iRoundNumber;

public plugin_init()
{
    register_plugin( g_szPlugin, g_szVersion, g_szAuthor );

    register_event( "HLTV", "Event_RoundStart", "a", "1=0", "2=0" );

    register_logevent( "LogEvent_RoundEnd", 2, "1=Round_End" );

    g_pPluginOn = register_cvar( "ba_ambience", "1" );
    g_pSoundChange = register_cvar( "ba_sound_changee", "1" );
}

public plugin_precache()
{
    g_aSounds = ArrayCreate( 256 );

    new szDirectory[ 256 ], szMapName[ 32 ];
    get_configsdir( szDirectory, charsmax( szDirectory ) );

    get_mapname( szMapName, charsmax( szMapName ) );

    format( szDirectory, charsmax( szDirectory ), "%s/sounds/%s.ini", szDirectory, szMapName );


    g_bFileExists = bool:file_exists( szDirectory );

    new szPath[ 256 ], bool:bSuccess;

    if( g_bFileExists )
    {
        new iFile = fopen( szDirectory, "rt" );
        
        new szBuffer[ 256 ];
        
        while( !feof( iFile ) )
        {
            fgets( iFile, szBuffer, charsmax( szBuffer ) );

            trim( szBuffer );
            
            remove_quotes( szBuffer );
            
            bSuccess = false;
            
            formatex( szPath, charsmax( szPath ), "sound/%s", szBuffer );
            
            if( !file_exists( szPath ) )
            {
                log_amx( "[Background Sounds] %s does not exist.", szPath );
            }
            
            else
            {
                if( contain( szBuffer, ".mp3" ) )
                {
                    precache_generic( szPath );
                    bSuccess = true;
                }
                
                else if( contain( szBuffer, ".wav" ) )
                {
                    precache_sound( szBuffer );
                    bSuccess = true;
                }
                
                else
                {
                    log_amx( "[Background Sounds] %s not a valid sound file.", szPath );
                }
            }
            
            if( bSuccess )
                ArrayPushString( g_aSounds, szBuffer );
        }
        
        fclose( iFile );
    }

    else
    {
        for( new i = 0; i < sizeof g_szDefaultSounds; i++ )
        {
            bSuccess = false;
            
            formatex( szPath, charsmax( szPath ), "sound/%s", g_szDefaultSounds[ i ] );
            
            if( !file_exists( szPath ) )
            {
                log_amx( "[Background Sounds] %s does not exist.", szPath );
            }
            
            else
            {
                if( contain( g_szDefaultSounds[ i ], ".mp3" ) )
                {
                    precache_generic( szPath );
                    bSuccess = true;
                }
                
                else if( contain( g_szDefaultSounds[ i ], ".wav" ) )
                {
                    precache_sound( g_szDefaultSounds[ i ] );
                    bSuccess = true;
                }
                
                else
                {
                    log_amx( "[Background Sounds] %s not a valid sound file.", szPath );
                }
            }
            
            if( bSuccess )
                ArrayPushString( g_aSounds, g_szDefaultSounds[ i ] );
        }
    }
    
    new iSize = ArraySize( g_aSounds );
    
    if( !iSize )
        set_fail_state( "No sound files found." );
    
    else
        g_iRandom = random( iSize );
        
    for( new i = 0; i < sizeof( g_szCtWinSounds ); i++ )
        precache_sound( g_szCtWinSounds[ i ] );
    for( new i = 0; i < sizeof( g_szTWinSounds ); i++ )
        precache_sound( g_szTWinSounds[ i ] )
}

public Event_RoundStart()
{
    if( !get_pcvar_num( g_pPluginOn ) )
        return;

    if( ++g_iRoundNumber % get_pcvar_num( g_pSoundChange ) == 0 && ArraySize( g_aSounds ) > 1 )
    {
        new iOldSound = g_iRandom;
        
        while( g_iRandom == iOldSound )
            g_iRandom = random( ArraySize( g_aSounds ) );
    }

    new szBuffer[ 256 ];
    ArrayGetString( g_aSounds, g_iRandom, szBuffer, charsmax( szBuffer ) );
    
    if( contain( szBuffer, ".mp3" ) != -1 )
    {
        client_cmd( 0, "mp3 loop ^"sound/%s^"", szBuffer );
    }

    else if( contain( szBuffer, ".wav" ) != -1 )
    {
        client_cmd( 0, "stopsound" );
        
        new szPath[ 256 ];
        formatex( szPath, charsmax( szPath ), "sound/%s", szBuffer );
        
        client_cmd( 0, "spk ^"%s^"", szBuffer );
        
        set_task( GetWavDuration( szPath ), "Task_LoopSound", TASK_LOOPSOUND, szBuffer, charsmax( szBuffer ), .flags="b" );
    }
}

public LogEvent_RoundEnd()
{    
    set_task( 0.2, "Task_EndSound" );

    remove_task( TASK_LOOPSOUND );
}

public Task_EndSound()
{
    client_cmd( 0, "stopsound" );
    client_cmd( 0, "mp3 stop" );    
    new iPlayers[ 32 ], iNum;
    get_players( iPlayers, iNum );
        
    new iNCAlive, iNC, iHumanAlive, iHuman;
        
    new iPlayer;
    for( new i = 0; i < iNum; i++ )
    {
        iPlayer = iPlayers[ i ];
        switch( cs_get_user_team( iPlayer ) )
        {
            case CS_TEAM_T:
            {
                    if( !iNCAlive )
                    {
                        if( !iNC )
                        {
                            iNC = iPlayer;
                        }
                        
                        if( is_user_alive( iPlayer ) )
                        {
                            iNCAlive = iPlayer;
                        }
                    }
            }
            case CS_TEAM_CT:
            {
                    if( !iHumanAlive )
                    {
                        if( !iHuman )
                        {
                            iHuman = iPlayer;
                        }
                        
                        if( is_user_alive( iPlayer ) )
                        {
                            iHumanAlive = iPlayer;
                        }
                    }
            }
        }
            
        if( iNCAlive
        && iNC
        && iHumanAlive
        && iHuman )
        {
            break;
        }
    }
        
    if( iNC
    && iHuman )
    {
            if( iHumanAlive )                
                client_cmd( 0, "spk ^"%s^"", g_szCtWinSounds[ random( sizeof( g_szCtWinSounds ) ) ] );
            else if( iNCAlive )                
                client_cmd( 0, "spk ^"%s^"", g_szTWinSounds[ random( sizeof( g_szTWinSounds ) ) ] );
        }
}
    
public Task_LoopSound( szSound[ ], iTaskID )
{
    client_cmd( 0, "stopsound" );
    client_cmd( 0, "spk ^"%s^"", szSound );
}

// Provided by Arkshine
Float:GetWavDuration( const WavFile[] )
{
    new Frequence [ 4 ];
    new Bitrate   [ 2 ];
    new DataLength[ 4 ];
    new File;
    
    // --| Open the file.
    File = fopen( WavFile, "rb" );
    
    // --| Get the frequence from offset 24. ( Read 4 bytes )
    fseek( File, 24, SEEK_SET );
    fread_blocks( File, Frequence, 4, BLOCK_INT );
    
    // --| Get the bitrate from offset 34. ( read 2 bytes )
    fseek( File, 34, SEEK_SET ); 
    fread_blocks( File, Bitrate, 2, BLOCK_BYTE );
    
    // --| Search 'data'. If the 'd' not on the offset 40, we search it.
    if ( fgetc( File ) != 'd' ) while( fgetc( File ) != 'd' && !feof( File ) ) {}
    
    // --| Get the data length from offset 44. ( after 'data', read 4 bytes )
    fseek( File, 3, SEEK_CUR ); 
    fread_blocks( File, DataLength, 4, BLOCK_INT );

    // --| Close file.
    fclose( File );
    
    // --| Calculate the time. ( Data length / ( frequence * bitrate ) / 8 ).
    return float( DataLength[ 0 ] ) / ( float( Frequence[ 0 ] * Bitrate[ 0 ] ) / 8.0 );
}
__________________

vaan123 is offline
Send a message via MSN to vaan123 Send a message via Yahoo to vaan123 Send a message via Skype™ to vaan123
liinuus
Senior Member
Join Date: Apr 2010
Old 09-16-2011 , 17:23   Re: Background Sounds
Reply With Quote #22

Quote:
Originally Posted by vaan123 View Post
Here is a fixed version thanks to liinuus:



Code:
/*
*
*        Background Sounds
*            
*        H3avY Ra1n (nikhilgupta345)
*
*        Description
*        -----------
*
*            This plugin enables you to play background music on your server
*            during the round. It will start playing at the beginning of the 
*            round, and stop at when the round ends. 

*        Cvars
*        -----
*            ba_ambience <0/1> <default:1>                             - turns plugin on or off
*            ba_sound_change <any number greater than 1> <default:1> - how often a new sound is chosen to be played
*
*        Changelog
*        ---------
*        
*            September 05, 2011     - v1.0 -     Initial Release
*            September 06, 2011    - v1.0.1 -    Fixed bug with death sounds not playing at the end of the round
*        
*        Credits
*        -------
*        
*            Vaan123            -     Original Plugin Idea
*            ConnnorMcLeod    -     Helped me fix a problem I was having with trim()
*
*        
*        Plugin Thread: http://forums.alliedmods.net/showthread.php?p=1548443
*
*/

#include < amxmodx >
#include < amxmisc >
#include < cstrike >

#define TASK_LOOPSOUND 1000

new const g_szPlugin[ ] = "Background Sounds";
new const g_szVersion[ ] = "1.0";
new const g_szAuthor[ ] = "H3avY Ra1n";

new const g_szDefaultSounds[ ][ ] = 
{
    "mymusic/soundtrack1.mp3",
    "mymusic/soundtrack2.mp3",
    "mymusic/soundtrack3.wav",
    "mymusic/soundtrack4.wav"
};

new const g_szCtWinSounds[ ][ ] =
{
    "nightcrawlers/humans_win3.wav",
    "nightcrawlers/humans_win4.wav"
};
new const g_szTWinSounds[ ][ ] =
{
    "nightcrawlers/freshmeat.wav"
}

new Array:g_aSounds;

new bool:g_bFileExists;

new g_pPluginOn;
new g_pSoundChange;

new g_iRandom;

new g_iRoundNumber;

public plugin_init()
{
    register_plugin( g_szPlugin, g_szVersion, g_szAuthor );

    register_event( "HLTV", "Event_RoundStart", "a", "1=0", "2=0" );

    register_logevent( "LogEvent_RoundEnd", 2, "1=Round_End" );

    g_pPluginOn = register_cvar( "ba_ambience", "1" );
    g_pSoundChange = register_cvar( "ba_sound_changee", "1" );
}

public plugin_precache()
{
    g_aSounds = ArrayCreate( 256 );

    new szDirectory[ 256 ], szMapName[ 32 ];
    get_configsdir( szDirectory, charsmax( szDirectory ) );

    get_mapname( szMapName, charsmax( szMapName ) );

    format( szDirectory, charsmax( szDirectory ), "%s/sounds/%s.ini", szDirectory, szMapName );


    g_bFileExists = bool:file_exists( szDirectory );

    new szPath[ 256 ], bool:bSuccess;

    if( g_bFileExists )
    {
        new iFile = fopen( szDirectory, "rt" );
        
        new szBuffer[ 256 ];
        
        while( !feof( iFile ) )
        {
            fgets( iFile, szBuffer, charsmax( szBuffer ) );

            trim( szBuffer );
            
            remove_quotes( szBuffer );
            
            bSuccess = false;
            
            formatex( szPath, charsmax( szPath ), "sound/%s", szBuffer );
            
            if( !file_exists( szPath ) )
            {
                log_amx( "[Background Sounds] %s does not exist.", szPath );
            }
            
            else
            {
                if( contain( szBuffer, ".mp3" ) )
                {
                    precache_generic( szPath );
                    bSuccess = true;
                }
                
                else if( contain( szBuffer, ".wav" ) )
                {
                    precache_sound( szBuffer );
                    bSuccess = true;
                }
                
                else
                {
                    log_amx( "[Background Sounds] %s not a valid sound file.", szPath );
                }
            }
            
            if( bSuccess )
                ArrayPushString( g_aSounds, szBuffer );
        }
        
        fclose( iFile );
    }

    else
    {
        for( new i = 0; i < sizeof g_szDefaultSounds; i++ )
        {
            bSuccess = false;
            
            formatex( szPath, charsmax( szPath ), "sound/%s", g_szDefaultSounds[ i ] );
            
            if( !file_exists( szPath ) )
            {
                log_amx( "[Background Sounds] %s does not exist.", szPath );
            }
            
            else
            {
                if( contain( g_szDefaultSounds[ i ], ".mp3" ) )
                {
                    precache_generic( szPath );
                    bSuccess = true;
                }
                
                else if( contain( g_szDefaultSounds[ i ], ".wav" ) )
                {
                    precache_sound( g_szDefaultSounds[ i ] );
                    bSuccess = true;
                }
                
                else
                {
                    log_amx( "[Background Sounds] %s not a valid sound file.", szPath );
                }
            }
            
            if( bSuccess )
                ArrayPushString( g_aSounds, g_szDefaultSounds[ i ] );
        }
    }
    
    new iSize = ArraySize( g_aSounds );
    
    if( !iSize )
        set_fail_state( "No sound files found." );
    
    else
        g_iRandom = random( iSize );
        
    for( new i = 0; i < sizeof( g_szCtWinSounds ); i++ )
        precache_sound( g_szCtWinSounds[ i ] );
    for( new i = 0; i < sizeof( g_szTWinSounds ); i++ )
        precache_sound( g_szTWinSounds[ i ] )
}

public Event_RoundStart()
{
    if( !get_pcvar_num( g_pPluginOn ) )
        return;

    if( ++g_iRoundNumber % get_pcvar_num( g_pSoundChange ) == 0 && ArraySize( g_aSounds ) > 1 )
    {
        new iOldSound = g_iRandom;
        
        while( g_iRandom == iOldSound )
            g_iRandom = random( ArraySize( g_aSounds ) );
    }

    new szBuffer[ 256 ];
    ArrayGetString( g_aSounds, g_iRandom, szBuffer, charsmax( szBuffer ) );
    
    if( contain( szBuffer, ".mp3" ) != -1 )
    {
        client_cmd( 0, "mp3 loop ^"sound/%s^"", szBuffer );
    }

    else if( contain( szBuffer, ".wav" ) != -1 )
    {
        client_cmd( 0, "stopsound" );
        
        new szPath[ 256 ];
        formatex( szPath, charsmax( szPath ), "sound/%s", szBuffer );
        
        client_cmd( 0, "spk ^"%s^"", szBuffer );
        
        set_task( GetWavDuration( szPath ), "Task_LoopSound", TASK_LOOPSOUND, szBuffer, charsmax( szBuffer ), .flags="b" );
    }
}

public LogEvent_RoundEnd()
{    
    set_task( 0.2, "Task_EndSound" );

    remove_task( TASK_LOOPSOUND );
}

public Task_EndSound()
{
    client_cmd( 0, "stopsound" );
    client_cmd( 0, "mp3 stop" );    
    new iPlayers[ 32 ], iNum;
    get_players( iPlayers, iNum );
        
    new iNCAlive, iNC, iHumanAlive, iHuman;
        
    new iPlayer;
    for( new i = 0; i < iNum; i++ )
    {
        iPlayer = iPlayers[ i ];
        switch( cs_get_user_team( iPlayer ) )
        {
            case CS_TEAM_T:
            {
                    if( !iNCAlive )
                    {
                        if( !iNC )
                        {
                            iNC = iPlayer;
                        }
                        
                        if( is_user_alive( iPlayer ) )
                        {
                            iNCAlive = iPlayer;
                        }
                    }
            }
            case CS_TEAM_CT:
            {
                    if( !iHumanAlive )
                    {
                        if( !iHuman )
                        {
                            iHuman = iPlayer;
                        }
                        
                        if( is_user_alive( iPlayer ) )
                        {
                            iHumanAlive = iPlayer;
                        }
                    }
            }
        }
            
        if( iNCAlive
        && iNC
        && iHumanAlive
        && iHuman )
        {
            break;
        }
    }
        
    if( iNC
    && iHuman )
    {
            if( iHumanAlive )                
                client_cmd( 0, "spk ^"%s^"", g_szCtWinSounds[ random( sizeof( g_szCtWinSounds ) ) ] );
            else if( iNCAlive )                
                client_cmd( 0, "spk ^"%s^"", g_szTWinSounds[ random( sizeof( g_szTWinSounds ) ) ] );
        }
}
    
public Task_LoopSound( szSound[ ], iTaskID )
{
    client_cmd( 0, "stopsound" );
    client_cmd( 0, "spk ^"%s^"", szSound );
}

// Provided by Arkshine
Float:GetWavDuration( const WavFile[] )
{
    new Frequence [ 4 ];
    new Bitrate   [ 2 ];
    new DataLength[ 4 ];
    new File;
    
    // --| Open the file.
    File = fopen( WavFile, "rb" );
    
    // --| Get the frequence from offset 24. ( Read 4 bytes )
    fseek( File, 24, SEEK_SET );
    fread_blocks( File, Frequence, 4, BLOCK_INT );
    
    // --| Get the bitrate from offset 34. ( read 2 bytes )
    fseek( File, 34, SEEK_SET ); 
    fread_blocks( File, Bitrate, 2, BLOCK_BYTE );
    
    // --| Search 'data'. If the 'd' not on the offset 40, we search it.
    if ( fgetc( File ) != 'd' ) while( fgetc( File ) != 'd' && !feof( File ) ) {}
    
    // --| Get the data length from offset 44. ( after 'data', read 4 bytes )
    fseek( File, 3, SEEK_CUR ); 
    fread_blocks( File, DataLength, 4, BLOCK_INT );

    // --| Close file.
    fclose( File );
    
    // --| Calculate the time. ( Data length / ( frequence * bitrate ) / 8 ).
    return float( DataLength[ 0 ] ) / ( float( Frequence[ 0 ] * Bitrate[ 0 ] ) / 8.0 );
}
this is not a fixed version, the old one worked just fine, the only thing changed is some things u personally wanted
liinuus is offline
darkbad945
Senior Member
Join Date: May 2008
Old 10-06-2011 , 20:56   Re: Background Sounds
Reply With Quote #23

Missing RIFF/WAVE chunks
Missing RIFF/WAVE chunks
Missing RIFF/WAVE chunks
Missing RIFF/WAVE chunks
Missing RIFF/WAVE chunks


Fix ? Sound doesn't play... i have them in sound folder...
darkbad945 is offline
nikhilgupta345
Veteran Member
Join Date: Aug 2009
Location: Virginia
Old 10-06-2011 , 22:17   Re: Background Sounds
Reply With Quote #24

Quote:
Originally Posted by darkbad945 View Post
Missing RIFF/WAVE chunks
Missing RIFF/WAVE chunks
Missing RIFF/WAVE chunks
Missing RIFF/WAVE chunks
Missing RIFF/WAVE chunks


Fix ? Sound doesn't play... i have them in sound folder...
Post your sounds config file.

Make sure that you are not including the sound directory on each line.
__________________
Quote:
Originally Posted by DarkGod View Post
nikhilgupta generates his plugins using sheer awesome.
If you like my work, please
nikhilgupta345 is offline
Send a message via ICQ to nikhilgupta345 Send a message via Yahoo to nikhilgupta345
darkbad945
Senior Member
Join Date: May 2008
Old 10-07-2011 , 06:50   Re: Background Sounds
Reply With Quote #25

Hmm.... I don't know what is happening BUT when i try to play the sound via the command

play music1.mp3 it gives me this error BUT when i get in the server the sound works... O.o
darkbad945 is offline
nikhilgupta345
Veteran Member
Join Date: Aug 2009
Location: Virginia
Old 10-07-2011 , 07:54   Re: Background Sounds
Reply With Quote #26

use 'spk' for wav sounds

and 'mp3 play' for mp3 sounds.
__________________
Quote:
Originally Posted by DarkGod View Post
nikhilgupta generates his plugins using sheer awesome.
If you like my work, please
nikhilgupta345 is offline
Send a message via ICQ to nikhilgupta345 Send a message via Yahoo to nikhilgupta345
mix97mix
Member
Join Date: Sep 2011
Old 10-07-2011 , 08:27   Re: Background Sounds
Reply With Quote #27

Very good man.
I realy needed this.
Thanks ;)
return PLUGIN_CONTINUE;
xD
mix97mix is offline
Send a message via MSN to mix97mix Send a message via Skype™ to mix97mix
BrownBear
Member
Join Date: May 2011
Location: Malaysia
Old 12-30-2011 , 09:38   Re: Background Sounds
Reply With Quote #28

A good plugin for those who love to hear song while playing games.
BrownBear is offline
AmineKyo
فوق سريرك
Join Date: Oct 2011
Location: Morocco
Old 01-13-2012 , 17:16   Re: Background Sounds
Reply With Quote #29

Is This One Can Play Sounds On All Maps
__________________
AmineKyo is offline
nikhilgupta345
Veteran Member
Join Date: Aug 2009
Location: Virginia
Old 01-14-2012 , 10:27   Re: Background Sounds
Reply With Quote #30

Quote:
Originally Posted by AmineKyo View Post
Is This One Can Play Sounds On All Maps
Yes, but you have to edit the source code.

PHP Code:
new const g_szDefaultSounds[ ][ ] = 
{
    
"music1.mp3",
        
"music2.mp3"
}; 
You have to change that to what you want (the stuff in quotes).

For example, if you only want the sound newsound.mp3 to play, you would do

PHP Code:
new const g_szDefaultSounds[ ][ ] = 
{
    
"newsound.mp3"
}; 
Then, for every new song you want to add, you have to add a colon to the end of the previous sound, and then put the next sound in quotes as well. Every sound should have a comma at the end of it except for the last sound.

Finally, you have to recompile it, and use the .amxx file that you get instead of the one that's on this thread.
__________________
Quote:
Originally Posted by DarkGod View Post
nikhilgupta generates his plugins using sheer awesome.
If you like my work, please

Last edited by nikhilgupta345; 01-14-2012 at 10:27.
nikhilgupta345 is offline
Send a message via ICQ to nikhilgupta345 Send a message via Yahoo to nikhilgupta345
Old 01-17-2012, 04:11
reko_o
This message has been deleted by xPaw. Reason: Flooding "thanks" posts
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 12:38.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode