diff --git a/doc/SOUNDPACKS.md b/doc/SOUNDPACKS.md index c15498cbee3c4..7051bd5629c1c 100644 --- a/doc/SOUNDPACKS.md +++ b/doc/SOUNDPACKS.md @@ -35,11 +35,32 @@ Sound effects can be included with a format like this: "variant": "bio_laser_gun", "season": "summer", "files": [ "guns/energy_generic/weapon_fire_laser.ogg" ] + }, + { + "type": "sound_effect", + "id": "open_door", + "volume": 75, + "variant": "t_door_c", + "season": "summer", + "is_indoors": true, + "is_night": false, + "files": [ "indoors/open_door_daytime.ogg" ] } ] ``` -Adding variety: If for a certain `id`'s `variant` multiple `files` are defined, they will be chosen at random when `variant` is played. An optional `season` may also be defined to only play that sound during the specified season (possible values are `spring`, `summer`, `autumn`, and `winter`). +##### Adding variety + +Several optional fields can be defined to target specific situations: + +| Field | Purpose +|--- |--- +| `variant` | Defines a specific subset of the id's sound group. (ex: `"id": "environment", "variant": "WEATHER_DRIZZLE"`) +| `season` | If defined, the sound will only play on the specified season. (possible values are `spring`, `summer`, `autumn`, and `winter`). +| `is_indoors` | If defined, the sound will only play if the player is indoors/outdoors when true/false. +| `is_night` | If defined, the sound will only play if the current time is night/day when true/false. + +If multiple `files` are defined for the sound effect, one will be chosen at random when the sound is played. The volume key may range from 0-100. @@ -54,7 +75,7 @@ Sound effects can be included for preloading with a format like this: { "type": "sound_effect_preload", "preload": [ - { "id": "environment", "variant": "daytime", "season": "spring" }, + { "id": "environment", "variant": "thunder_near", "season": "spring", "is_night": false }, { "id": "environment" } ] } diff --git a/src/sdlsound.cpp b/src/sdlsound.cpp index 0d218ea2b1e55..bdeab75049571 100644 --- a/src/sdlsound.cpp +++ b/src/sdlsound.cpp @@ -35,13 +35,20 @@ #define dbg(x) DebugLog((x),D_SDL) << __FILE__ << ":" << __LINE__ << ": " -struct id_variant_season { +struct sfx_args { std::string id; std::string variant; std::string season; + cata::optional indoors; + cata::optional night; - bool operator<( const id_variant_season &rhs ) const { - return std::tie( id, variant, season ) < std::tie( rhs.id, rhs.variant, rhs.season ); + bool operator<( const sfx_args &rhs ) const { + int r_ind = rhs.indoors.value_or( -1 ); + int r_nit = rhs.night.value_or( -1 ); + int l_ind = indoors.value_or( -1 ); + int l_nit = night.value_or( -1 ); + return std::tie( id, variant, season, l_ind, l_nit ) < + std::tie( rhs.id, rhs.variant, rhs.season, r_ind, r_nit ); } }; struct sound_effect_resource { @@ -60,7 +67,7 @@ struct sound_effect { }; struct sfx_resources_t { std::vector resource; - std::map> sound_effects; + std::map> sound_effects; }; struct music_playlist { // list of filenames relative to the soundpack location @@ -87,7 +94,7 @@ static std::string current_soundpack_path; static std::unordered_map unique_paths; static sfx_resources_t sfx_resources; -static std::vector sfx_preload; +static std::vector sfx_preload; bool sounds::sound_enabled = false; @@ -340,9 +347,19 @@ void sfx::load_sound_effects( const JsonObject &jsobj ) if( !sound_init_success ) { return; } - const id_variant_season key = { jsobj.get_string( "id" ), - jsobj.get_string( "variant", "default" ), jsobj.get_string( "season", "" ) - }; + sfx_args key = { + jsobj.get_string( "id" ), + jsobj.get_string( "variant", "default" ), + jsobj.get_string( "season", "" ), + cata::optional(), + cata::optional() + }; + if( jsobj.has_bool( "is_indoors" ) ) { + key.indoors = jsobj.get_bool( "is_indoors" ); + } + if( jsobj.has_bool( "is_night" ) ) { + key.indoors = jsobj.get_bool( "is_night" ); + } const int volume = jsobj.get_int( "volume", 100 ); auto &effects = sfx_resources.sound_effects[ key ]; @@ -361,9 +378,19 @@ void sfx::load_sound_effect_preload( const JsonObject &jsobj ) } for( JsonObject aobj : jsobj.get_array( "preload" ) ) { - const id_variant_season preload_key = { aobj.get_string( "id" ), - aobj.get_string( "variant", "default" ), aobj.get_string( "season", "" ) - }; + sfx_args preload_key = { + jsobj.get_string( "id" ), + jsobj.get_string( "variant", "default" ), + jsobj.get_string( "season", "" ), + cata::optional(), + cata::optional() + }; + if( jsobj.has_bool( "is_indoors" ) ) { + preload_key.indoors = jsobj.get_bool( "is_indoors" ); + } + if( jsobj.has_bool( "is_night" ) ) { + preload_key.indoors = jsobj.get_bool( "is_night" ); + } sfx_preload.push_back( preload_key ); } } @@ -390,7 +417,7 @@ void sfx::load_playlist( const JsonObject &jsobj ) // Returns a random sound effect matching given id and variant or `nullptr` if there is no // matching sound effect. -static const sound_effect *find_random_effect( const id_variant_season &id_var_seas ) +static const sound_effect *find_random_effect( const sfx_args &id_var_seas ) { const auto iter = sfx_resources.sound_effects.find( id_var_seas ); if( iter == sfx_resources.sound_effects.end() ) { @@ -401,27 +428,90 @@ static const sound_effect *find_random_effect( const id_variant_season &id_var_s // Same as above, but with fallback to "default" variant. May still return `nullptr` static const sound_effect *find_random_effect( const std::string &id, const std::string &variant, - const std::string &season ) + const std::string &season, const cata::optional &is_indoors, + const cata::optional &is_night ) { - const sound_effect *eff1 = find_random_effect( { id, variant, season } ); - if( eff1 != nullptr ) { - return eff1; + using sfx_ptr = std::pair *>; + + auto get_valid = []( const std::function &is_valid, + const std::vector &vlist ) { + std::vector ret; + for( const sfx_ptr &s : vlist ) { + if( is_valid( s.first ) ) { + ret.emplace_back( s ); + } + } + return ret; + }; + + std::vector valid_ids; + std::vector *valid; + // Check for matching id + for( const auto &s : sfx_resources.sound_effects ) { + if( s.first.id == id ) { + valid_ids.emplace_back( sfx_ptr( &s.first, &s.second ) ); + } } - const sound_effect *eff2 = find_random_effect( { id, variant, "" } ); - if( eff2 != nullptr ) { - return eff2; + if( valid_ids.empty() ) { + return nullptr; } - const sound_effect *eff3 = find_random_effect( { id, "default", season } ); - if( eff3 != nullptr ) { - return eff3; + valid = &valid_ids; + + // Check for matching variant + std::string t_var = variant; + std::vector tmp1 = get_valid( [&variant]( const sfx_args * sp ) { + return sp->variant == variant; + }, *valid ); + if( !tmp1.empty() ) { + valid = &tmp1; + } else { + t_var = "default"; + } + + // Check for matching indoors + cata::optional t_ind = is_indoors; + std::vector tmp2 = get_valid( [&is_indoors]( const sfx_args * sp ) { + return sp->indoors == is_indoors; + }, *valid ); + if( !tmp2.empty() ) { + valid = &tmp2; + } else { + t_ind.reset(); + } + + // Check for matching night + cata::optional t_nit = is_night; + std::vector tmp3 = get_valid( [&is_night]( const sfx_args * sp ) { + return sp->night == is_night; + }, *valid ); + if( !tmp3.empty() ) { + valid = &tmp3; + } else { + t_nit.reset(); + } + + // Check for matching season + std::string t_sea = season; + std::vector tmp4 = get_valid( [&season]( const sfx_args * sp ) { + return sp->season == season; + }, *valid ); + if( !tmp4.empty() ) { + valid = &tmp4; + } else { + t_sea = ""; + } + + if( valid->empty() ) { + return nullptr; } - return find_random_effect( { id, "default", "" } ); + return find_random_effect( { id, t_var, t_sea, t_ind, t_nit } ); } bool sfx::has_variant_sound( const std::string &id, const std::string &variant, - const std::string &season ) + const std::string &season, const cata::optional &is_indoors, + const cata::optional &is_night ) { - return find_random_effect( id, variant, season ) != nullptr; + return find_random_effect( id, variant, season, is_indoors, is_night ) != nullptr; } // Deletes the dynamically created chunk (if such a chunk had been played). @@ -482,7 +572,8 @@ static Mix_Chunk *do_pitch_shift( Mix_Chunk *s, float pitch ) } void sfx::play_variant_sound( const std::string &id, const std::string &variant, - const std::string &season, int volume ) + const std::string &season, const cata::optional &is_indoors, + const cata::optional &is_night, int volume ) { if( test_mode ) { return; @@ -493,9 +584,9 @@ void sfx::play_variant_sound( const std::string &id, const std::string &variant, if( !check_sound( volume ) ) { return; } - const sound_effect *eff = find_random_effect( id, variant, season ); + const sound_effect *eff = find_random_effect( id, variant, season, is_indoors, is_night ); if( eff == nullptr ) { - eff = find_random_effect( id, "default", "" ); + eff = find_random_effect( id, "default", "", cata::optional(), cata::optional() ); if( eff == nullptr ) { return; } @@ -513,8 +604,9 @@ void sfx::play_variant_sound( const std::string &id, const std::string &variant, } void sfx::play_variant_sound( const std::string &id, const std::string &variant, - const std::string &season, - int volume, units::angle angle, double pitch_min, double pitch_max ) + const std::string &season, const cata::optional &is_indoors, + const cata::optional &is_night, int volume, units::angle angle, + double pitch_min, double pitch_max ) { if( test_mode ) { return; @@ -525,7 +617,7 @@ void sfx::play_variant_sound( const std::string &id, const std::string &variant, if( !check_sound( volume ) ) { return; } - const sound_effect *eff = find_random_effect( id, variant, season ); + const sound_effect *eff = find_random_effect( id, variant, season, is_indoors, is_night ); if( eff == nullptr ) { return; } @@ -566,7 +658,8 @@ void sfx::play_variant_sound( const std::string &id, const std::string &variant, } void sfx::play_ambient_variant_sound( const std::string &id, const std::string &variant, - const std::string &season, int volume, + const std::string &season, const cata::optional &is_indoors, + const cata::optional &is_night, int volume, channel channel, int fade_in_duration, double pitch, int loops ) { if( test_mode ) { @@ -578,7 +671,7 @@ void sfx::play_ambient_variant_sound( const std::string &id, const std::string & if( is_channel_playing( channel ) ) { return; } - const sound_effect *eff = find_random_effect( id, variant, season ); + const sound_effect *eff = find_random_effect( id, variant, season, is_indoors, is_night ); if( eff == nullptr ) { return; } @@ -649,7 +742,7 @@ void load_soundset() } // Preload sound effects - for( const id_variant_season &preload : sfx_preload ) { + for( const sfx_args &preload : sfx_preload ) { const auto find_result = sfx_resources.sound_effects.find( preload ); if( find_result != sfx_resources.sound_effects.end() ) { for( const auto &sfx : find_result->second ) { @@ -669,7 +762,7 @@ void load_soundset() // to force deallocation of resources. { sfx_preload.clear(); - std::vector t_swap; + std::vector t_swap; sfx_preload.swap( t_swap ); } } diff --git a/src/sounds.cpp b/src/sounds.cpp index 53c83bd0078ed..d74a837849bbc 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -673,8 +673,11 @@ void sounds::process_sound_markers( Character *you ) const std::string &sfx_id = sound.id; const std::string &sfx_variant = sound.variant; const std::string &sfx_season = sound.season; + const bool indoors = !is_creature_outside( get_player_character() ); + const bool night = is_night( calendar::turn ); if( !sfx_id.empty() ) { - sfx::play_variant_sound( sfx_id, sfx_variant, sfx_season, sfx::get_heard_volume( pos ) ); + sfx::play_variant_sound( sfx_id, sfx_variant, sfx_season, indoors, night, + sfx::get_heard_volume( pos ) ); } // Place footstep markers. @@ -860,11 +863,14 @@ void sfx::do_vehicle_engine_sfx() std::pair id_and_variant; const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); + const bool indoors = !is_creature_outside( player_character ); + const bool night = is_night( calendar::turn ); for( size_t e = 0; e < veh->engines.size(); ++e ) { if( veh->is_engine_on( e ) ) { if( sfx::has_variant_sound( "engine_working_internal", - veh->part_info( veh->engines[ e ] ).get_id().str(), seas_str ) ) { + veh->part_info( veh->engines[ e ] ).get_id().str(), + seas_str, indoors, night ) ) { id_and_variant = std::make_pair( "engine_working_internal", veh->part_info( veh->engines[ e ] ).get_id().str() ); } else if( veh->is_engine_type( e, fuel_type_muscle ) ) { @@ -880,7 +886,8 @@ void sfx::do_vehicle_engine_sfx() } if( !is_channel_playing( ch ) ) { - play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, seas_str, + play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, + seas_str, indoors, night, sfx::get_heard_volume( player_character.pos() ), ch, 1000 ); add_msg_debug( debugmode::DF_SOUND, "START %s %s", id_and_variant.first, id_and_variant.second ); } else { @@ -918,12 +925,12 @@ void sfx::do_vehicle_engine_sfx() } if( current_gear > previous_gear ) { - play_variant_sound( "vehicle", "gear_shift", seas_str, get_heard_volume( player_character.pos() ), - 0_degrees, 0.8, 0.8 ); + play_variant_sound( "vehicle", "gear_shift", seas_str, indoors, night, + get_heard_volume( player_character.pos() ), 0_degrees, 0.8, 0.8 ); add_msg_debug( debugmode::DF_SOUND, "GEAR UP" ); } else if( current_gear < previous_gear ) { - play_variant_sound( "vehicle", "gear_shift", seas_str, get_heard_volume( player_character.pos() ), - 0_degrees, 1.2, 1.2 ); + play_variant_sound( "vehicle", "gear_shift", seas_str, indoors, night, + get_heard_volume( player_character.pos() ), 0_degrees, 1.2, 1.2 ); add_msg_debug( debugmode::DF_SOUND, "GEAR DOWN" ); } if( safe_speed != 0 ) { @@ -942,7 +949,8 @@ void sfx::do_vehicle_engine_sfx() if( current_speed != previous_speed ) { Mix_HaltChannel( static_cast( ch ) ); add_msg_debug( debugmode::DF_SOUND, "STOP speed %d =/= %d", current_speed, previous_speed ); - play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, seas_str, + play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, + seas_str, indoors, night, sfx::get_heard_volume( player_character.pos() ), ch, 1000, pitch ); add_msg_debug( debugmode::DF_SOUND, "PITCH %f", pitch ); } @@ -998,11 +1006,14 @@ void sfx::do_vehicle_exterior_engine_sfx() std::pair id_and_variant; const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); + const bool indoors = !is_creature_outside( player_character ); + const bool night = is_night( calendar::turn ); for( size_t e = 0; e < veh->engines.size(); ++e ) { if( veh->is_engine_on( e ) ) { if( sfx::has_variant_sound( "engine_working_external", - veh->part_info( veh->engines[ e ] ).get_id().str(), seas_str ) ) { + veh->part_info( veh->engines[ e ] ).get_id().str(), + seas_str, indoors, night ) ) { id_and_variant = std::make_pair( "engine_working_external", veh->part_info( veh->engines[ e ] ).get_id().str() ); } else if( veh->is_engine_type( e, fuel_type_muscle ) ) { @@ -1027,7 +1038,8 @@ void sfx::do_vehicle_exterior_engine_sfx() engine_external_id_and_variant = id_and_variant; Mix_HaltChannel( ch_int ); add_msg_debug( debugmode::DF_SOUND, "STOP exterior_engine_sound, change id/var" ); - play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, seas_str, 128, ch, 0 ); + play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, + seas_str, indoors, night, 128, ch, 0 ); Mix_SetPosition( ch_int, to_degrees( get_heard_angle( veh->global_pos3() ) ), 0 ); set_channel_volume( ch, vol ); add_msg_debug( debugmode::DF_SOUND, "START exterior_engine_sound %s %s vol: %d", @@ -1036,7 +1048,8 @@ void sfx::do_vehicle_exterior_engine_sfx() Mix_Volume( ch_int, -1 ) ); } } else { - play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, seas_str, 128, ch, 0 ); + play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, + seas_str, indoors, night, 128, ch, 0 ); add_msg_debug( debugmode::DF_SOUND, "Vol: %d %d", vol, Mix_Volume( ch_int, -1 ) ); Mix_SetPosition( ch_int, to_degrees( get_heard_angle( veh->global_pos3() ) ), 0 ); add_msg_debug( debugmode::DF_SOUND, "Vol: %d %d", vol, Mix_Volume( ch_int, -1 ) ); @@ -1069,17 +1082,21 @@ void sfx::do_ambient() const bool weather_changed = get_weather().weather_id != previous_weather; const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); + const bool indoors = !is_creature_outside( player_character ); + const bool night = is_night( calendar::turn ); // Step in at night time / we are not indoors - if( is_night( calendar::turn ) && !is_sheltered && + if( night && !is_sheltered && !is_channel_playing( channel::nighttime_outdoors_env ) && !is_deaf ) { fade_audio_group( group::time_of_day, 1000 ); - play_ambient_variant_sound( "environment", "nighttime", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "nighttime", seas_str, + indoors, night, heard_volume, channel::nighttime_outdoors_env, 1000 ); // Step in at day time / we are not indoors - } else if( !is_night( calendar::turn ) && !is_channel_playing( channel::daytime_outdoors_env ) && + } else if( !night && !is_channel_playing( channel::daytime_outdoors_env ) && !is_sheltered && !is_deaf ) { fade_audio_group( group::time_of_day, 1000 ); - play_ambient_variant_sound( "environment", "daytime", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "daytime", seas_str, + indoors, night, heard_volume, channel::daytime_outdoors_env, 1000 ); } // We are underground @@ -1088,7 +1105,8 @@ void sfx::do_ambient() weather_changed && !is_deaf ) ) { fade_audio_group( group::weather, 1000 ); fade_audio_group( group::time_of_day, 1000 ); - play_ambient_variant_sound( "environment", "underground", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "underground", seas_str, + indoors, night, heard_volume, channel::underground_env, 1000 ); // We are indoors } else if( ( is_sheltered && !is_underground && @@ -1097,16 +1115,17 @@ void sfx::do_ambient() weather_changed && !is_deaf ) ) { fade_audio_group( group::weather, 1000 ); fade_audio_group( group::time_of_day, 1000 ); - play_ambient_variant_sound( "environment", "indoors", seas_str, heard_volume, channel::indoors_env, - 1000 ); + play_ambient_variant_sound( "environment", "indoors", seas_str, + indoors, night, heard_volume, + channel::indoors_env, 1000 ); } // We are indoors and it is also raining if( get_weather().weather_id->rains && get_weather().weather_id->precip != precip_class::very_light && !is_underground && is_sheltered && !is_channel_playing( channel::indoors_rain_env ) ) { - play_ambient_variant_sound( "environment", "indoors_rain", seas_str, heard_volume, - channel::indoors_rain_env, 1000 ); + play_ambient_variant_sound( "environment", "indoors_rain", seas_str, indoors, + night, heard_volume, channel::indoors_rain_env, 1000 ); } if( ( !is_sheltered && get_weather().weather_id->sound_category != weather_sound_category::silent && !is_deaf && @@ -1125,45 +1144,55 @@ void sfx::do_ambient() // We are outside and there is precipitation switch( get_weather().weather_id->sound_category ) { case weather_sound_category::drizzle: - play_ambient_variant_sound( "environment", "WEATHER_DRIZZLE", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_DRIZZLE", seas_str, + indoors, night, heard_volume, channel::outdoors_drizzle_env, 1000 ); break; case weather_sound_category::rainy: - play_ambient_variant_sound( "environment", "WEATHER_RAINY", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_RAINY", seas_str, + indoors, night, heard_volume, channel::outdoors_rain_env, 1000 ); break; case weather_sound_category::thunder: - play_ambient_variant_sound( "environment", "WEATHER_THUNDER", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_THUNDER", seas_str, + indoors, night, heard_volume, channel::outdoors_thunderstorm_env, 1000 ); break; case weather_sound_category::flurries: - play_ambient_variant_sound( "environment", "WEATHER_FLURRIES", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_FLURRIES", seas_str, + indoors, night, heard_volume, channel::outdoors_flurry_env, 1000 ); break; case weather_sound_category::snowstorm: - play_ambient_variant_sound( "environment", "WEATHER_SNOWSTORM", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_SNOWSTORM", seas_str, + indoors, night, heard_volume, channel::outdoor_blizzard, 1000 ); break; case weather_sound_category::snow: - play_ambient_variant_sound( "environment", "WEATHER_SNOW", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_SNOW", seas_str, + indoors, night, heard_volume, channel::outdoors_snow_env, 1000 ); break; case weather_sound_category::silent: break; case weather_sound_category::portal_storm: - play_ambient_variant_sound( "environment", "WEATHER_PORTAL_STORM", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_PORTAL_STORM", seas_str, + indoors, night, heard_volume, channel::outdoors_portal_storm_env, 1000 ); break; case weather_sound_category::clear: - play_ambient_variant_sound( "environment", "WEATHER_CLEAR", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_CLEAR", seas_str, + indoors, night, heard_volume, channel::outdoors_clear_env, 1000 ); break; case weather_sound_category::sunny: - play_ambient_variant_sound( "environment", "WEATHER_SUNNY", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_SUNNY", seas_str, + indoors, night, heard_volume, channel::outdoors_sunny_env, 1000 ); break; case weather_sound_category::cloudy: - play_ambient_variant_sound( "environment", "WEATHER_CLOUDY", seas_str, heard_volume, + play_ambient_variant_sound( "environment", "WEATHER_CLOUDY", seas_str, + indoors, night, heard_volume, channel::outdoors_cloudy_env, 1000 ); break; case weather_sound_category::last: @@ -1202,6 +1231,8 @@ void sfx::generate_gun_sound( const Character &source_arg, const item &firing ) const Character &player_character = get_player_character(); const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); + const bool indoors = !is_creature_outside( player_character ); + const bool night = is_night( calendar::turn ); // this does not mean p == avatar (it could be a vehicle turret) if( player_character.pos() == source ) { selected_sound = "fire_gun"; @@ -1224,7 +1255,8 @@ void sfx::generate_gun_sound( const Character &source_arg, const item &firing ) } } - play_variant_sound( selected_sound, weapon_id.str(), seas_str, heard_volume, angle, 0.8, 1.2 ); + play_variant_sound( selected_sound, weapon_id.str(), seas_str, indoors, night, + heard_volume, angle, 0.8, 1.2 ); start_sfx_timestamp = std::chrono::high_resolution_clock::now(); } @@ -1313,40 +1345,50 @@ void sfx::sound_thread::operator()() const std::string variant_used; const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); + const bool indoors = !is_creature_outside( get_player_character() ); + const bool night = is_night( calendar::turn ); if( weapon_skill == skill_bashing && weapon_volume <= 8 ) { variant_used = "small_bash"; - play_variant_sound( "melee_swing", "small_bash", seas_str, vol_src, ang_src, 0.8, 1.2 ); + play_variant_sound( "melee_swing", "small_bash", seas_str, indoors, night, + vol_src, ang_src, 0.8, 1.2 ); } else if( weapon_skill == skill_bashing && weapon_volume >= 9 ) { variant_used = "big_bash"; - play_variant_sound( "melee_swing", "big_bash", seas_str, vol_src, ang_src, 0.8, 1.2 ); + play_variant_sound( "melee_swing", "big_bash", seas_str, indoors, night, + vol_src, ang_src, 0.8, 1.2 ); } else if( ( weapon_skill == skill_cutting || weapon_skill == skill_stabbing ) && weapon_volume <= 6 ) { variant_used = "small_cutting"; - play_variant_sound( "melee_swing", "small_cutting", seas_str, vol_src, ang_src, 0.8, 1.2 ); + play_variant_sound( "melee_swing", "small_cutting", seas_str, indoors, night, + vol_src, ang_src, 0.8, 1.2 ); } else if( ( weapon_skill == skill_cutting || weapon_skill == skill_stabbing ) && weapon_volume >= 7 ) { variant_used = "big_cutting"; - play_variant_sound( "melee_swing", "big_cutting", seas_str, vol_src, ang_src, 0.8, 1.2 ); + play_variant_sound( "melee_swing", "big_cutting", seas_str, indoors, night, + vol_src, ang_src, 0.8, 1.2 ); } else { variant_used = "default"; - play_variant_sound( "melee_swing", "default", seas_str, vol_src, ang_src, 0.8, 1.2 ); + play_variant_sound( "melee_swing", "default", seas_str, indoors, night, + vol_src, ang_src, 0.8, 1.2 ); } if( hit ) { if( targ_mon ) { if( material == "steel" ) { std::this_thread::sleep_for( std::chrono::milliseconds( rng( weapon_volume * 12, weapon_volume * 16 ) ) ); - play_variant_sound( "melee_hit_metal", variant_used, seas_str, vol_targ, ang_targ, 0.8, 1.2 ); + play_variant_sound( "melee_hit_metal", variant_used, seas_str, indoors, + night, vol_targ, ang_targ, 0.8, 1.2 ); } else { std::this_thread::sleep_for( std::chrono::milliseconds( rng( weapon_volume * 12, weapon_volume * 16 ) ) ); - play_variant_sound( "melee_hit_flesh", variant_used, seas_str, vol_targ, ang_targ, 0.8, 1.2 ); + play_variant_sound( "melee_hit_flesh", variant_used, seas_str, indoors, + night, vol_targ, ang_targ, 0.8, 1.2 ); } } else { std::this_thread::sleep_for( std::chrono::milliseconds( rng( weapon_volume * 9, weapon_volume * 12 ) ) ); - play_variant_sound( "melee_hit_flesh", variant_used, seas_str, vol_targ, ang_targ, 0.8, 1.2 ); + play_variant_sound( "melee_hit_flesh", variant_used, seas_str, indoors, + night, vol_targ, ang_targ, 0.8, 1.2 ); } } } @@ -1361,6 +1403,8 @@ void sfx::do_projectile_hit( const Creature &target ) const units::angle angle = get_heard_angle( target.pos() ); const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); + const bool indoors = !is_creature_outside( get_player_character() ); + const bool night = is_night( calendar::turn ); if( target.is_monster() ) { const monster &mon = dynamic_cast( target ); static const std::set fleshy = { @@ -1375,17 +1419,21 @@ void sfx::do_projectile_hit( const Creature &target ) } ); if( !is_fleshy && mon.made_of( material_stone ) ) { - play_variant_sound( "bullet_hit", "hit_wall", seas_str, heard_volume, angle, 0.8, 1.2 ); + play_variant_sound( "bullet_hit", "hit_wall", seas_str, indoors, night, + heard_volume, angle, 0.8, 1.2 ); return; } else if( !is_fleshy && mon.made_of( material_steel ) ) { - play_variant_sound( "bullet_hit", "hit_metal", seas_str, heard_volume, angle, 0.8, 1.2 ); + play_variant_sound( "bullet_hit", "hit_metal", seas_str, indoors, night, + heard_volume, angle, 0.8, 1.2 ); return; } else { - play_variant_sound( "bullet_hit", "hit_flesh", seas_str, heard_volume, angle, 0.8, 1.2 ); + play_variant_sound( "bullet_hit", "hit_flesh", seas_str, indoors, night, + heard_volume, angle, 0.8, 1.2 ); return; } } - play_variant_sound( "bullet_hit", "hit_flesh", seas_str, heard_volume, angle, 0.8, 1.2 ); + play_variant_sound( "bullet_hit", "hit_flesh", seas_str, indoors, night, + heard_volume, angle, 0.8, 1.2 ); } void sfx::do_player_death_hurt( const Character &target, bool death ) @@ -1396,16 +1444,18 @@ void sfx::do_player_death_hurt( const Character &target, bool death ) const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); + const bool indoors = !is_creature_outside( get_player_character() ); + const bool night = is_night( calendar::turn ); int heard_volume = get_heard_volume( target.pos() ); const bool male = target.male; if( !male && !death ) { - play_variant_sound( "deal_damage", "hurt_f", seas_str, heard_volume ); + play_variant_sound( "deal_damage", "hurt_f", seas_str, indoors, night, heard_volume ); } else if( male && !death ) { - play_variant_sound( "deal_damage", "hurt_m", seas_str, heard_volume ); + play_variant_sound( "deal_damage", "hurt_m", seas_str, indoors, night, heard_volume ); } else if( !male && death ) { - play_variant_sound( "clean_up_at_end", "death_f", seas_str, heard_volume ); + play_variant_sound( "clean_up_at_end", "death_f", seas_str, indoors, night, heard_volume ); } else if( male && death ) { - play_variant_sound( "clean_up_at_end", "death_m", seas_str, heard_volume ); + play_variant_sound( "clean_up_at_end", "death_m", seas_str, indoors, night, heard_volume ); } } @@ -1418,6 +1468,8 @@ void sfx::do_danger_music() const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); Character &player_character = get_player_character(); + const bool indoors = !is_creature_outside( player_character ); + const bool night = is_night( calendar::turn ); if( player_character.in_sleep_state() && !audio_muted ) { fade_audio_channel( channel::any, 100 ); audio_muted = true; @@ -1443,27 +1495,27 @@ void sfx::do_danger_music() return; } else if( hostiles >= 5 && hostiles <= 9 && !is_channel_playing( channel::danger_low_theme ) ) { fade_audio_group( group::context_themes, 1000 ); - play_ambient_variant_sound( "danger_low", "default", seas_str, 100, channel::danger_low_theme, - 1000 ); + play_ambient_variant_sound( "danger_low", "default", seas_str, indoors, + night, 100, channel::danger_low_theme, 1000 ); prev_hostiles = hostiles; return; } else if( hostiles >= 10 && hostiles <= 14 && !is_channel_playing( channel::danger_medium_theme ) ) { fade_audio_group( group::context_themes, 1000 ); - play_ambient_variant_sound( "danger_medium", "default", seas_str, 100, channel::danger_medium_theme, - 1000 ); + play_ambient_variant_sound( "danger_medium", "default", seas_str, indoors, + night, 100, channel::danger_medium_theme, 1000 ); prev_hostiles = hostiles; return; } else if( hostiles >= 15 && hostiles <= 19 && !is_channel_playing( channel::danger_high_theme ) ) { fade_audio_group( group::context_themes, 1000 ); - play_ambient_variant_sound( "danger_high", "default", seas_str, 100, channel::danger_high_theme, - 1000 ); + play_ambient_variant_sound( "danger_high", "default", seas_str, indoors, + night, 100, channel::danger_high_theme, 1000 ); prev_hostiles = hostiles; return; } else if( hostiles >= 20 && !is_channel_playing( channel::danger_extreme_theme ) ) { fade_audio_group( group::context_themes, 1000 ); - play_ambient_variant_sound( "danger_extreme", "default", seas_str, 100, - channel::danger_extreme_theme, 1000 ); + play_ambient_variant_sound( "danger_extreme", "default", seas_str, indoors, + night, 100, channel::danger_extreme_theme, 1000 ); prev_hostiles = hostiles; return; } @@ -1479,6 +1531,8 @@ void sfx::do_fatigue() const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); Character &player_character = get_player_character(); + const bool indoors = !is_creature_outside( player_character ); + const bool night = is_night( calendar::turn ); /*15: Stamina 75% 16: Stamina 50% 17: Stamina 25%*/ @@ -1489,37 +1543,43 @@ void sfx::do_fatigue() player_character.get_stamina() >= player_character.get_stamina_max() * .5 && player_character.male && !is_channel_playing( channel::stamina_75 ) ) { fade_audio_group( group::fatigue, 1000 ); - play_ambient_variant_sound( "plmove", "fatigue_m_low", seas_str, 100, channel::stamina_75, 1000 ); + play_ambient_variant_sound( "plmove", "fatigue_m_low", seas_str, indoors, + night, 100, channel::stamina_75, 1000 ); return; } else if( player_character.get_stamina() <= player_character.get_stamina_max() * .49 && player_character.get_stamina() >= player_character.get_stamina_max() * .25 && player_character.male && !is_channel_playing( channel::stamina_50 ) ) { fade_audio_group( group::fatigue, 1000 ); - play_ambient_variant_sound( "plmove", "fatigue_m_med", seas_str, 100, channel::stamina_50, 1000 ); + play_ambient_variant_sound( "plmove", "fatigue_m_med", seas_str, indoors, + night, 100, channel::stamina_50, 1000 ); return; } else if( player_character.get_stamina() <= player_character.get_stamina_max() * .24 && player_character.get_stamina() >= 0 && player_character.male && !is_channel_playing( channel::stamina_35 ) ) { fade_audio_group( group::fatigue, 1000 ); - play_ambient_variant_sound( "plmove", "fatigue_m_high", seas_str, 100, channel::stamina_35, 1000 ); + play_ambient_variant_sound( "plmove", "fatigue_m_high", seas_str, indoors, + night, 100, channel::stamina_35, 1000 ); return; } else if( player_character.get_stamina() <= player_character.get_stamina_max() * .74 && player_character.get_stamina() >= player_character.get_stamina_max() * .5 && !player_character.male && !is_channel_playing( channel::stamina_75 ) ) { fade_audio_group( group::fatigue, 1000 ); - play_ambient_variant_sound( "plmove", "fatigue_f_low", seas_str, 100, channel::stamina_75, 1000 ); + play_ambient_variant_sound( "plmove", "fatigue_f_low", seas_str, indoors, + night, 100, channel::stamina_75, 1000 ); return; } else if( player_character.get_stamina() <= player_character.get_stamina_max() * .49 && player_character.get_stamina() >= player_character.get_stamina_max() * .25 && !player_character.male && !is_channel_playing( channel::stamina_50 ) ) { fade_audio_group( group::fatigue, 1000 ); - play_ambient_variant_sound( "plmove", "fatigue_f_med", seas_str, 100, channel::stamina_50, 1000 ); + play_ambient_variant_sound( "plmove", "fatigue_f_med", seas_str, indoors, + night, 100, channel::stamina_50, 1000 ); return; } else if( player_character.get_stamina() <= player_character.get_stamina_max() * .24 && player_character.get_stamina() >= 0 && !player_character.male && !is_channel_playing( channel::stamina_35 ) ) { fade_audio_group( group::fatigue, 1000 ); - play_ambient_variant_sound( "plmove", "fatigue_f_high", seas_str, 100, channel::stamina_35, 1000 ); + play_ambient_variant_sound( "plmove", "fatigue_f_high", seas_str, indoors, + night, 100, channel::stamina_35, 1000 ); return; } } @@ -1532,6 +1592,8 @@ void sfx::do_hearing_loss( int turns ) const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); + const bool indoors = !is_creature_outside( get_player_character() ); + const bool night = is_night( calendar::turn ); g_sfx_volume_multiplier = .1; fade_audio_group( group::weather, 50 ); fade_audio_group( group::time_of_day, 50 ); @@ -1540,17 +1602,17 @@ void sfx::do_hearing_loss( int turns ) if( turns == -1 ) { return; } - play_variant_sound( "environment", "deafness_shock", seas_str, 100 ); - play_variant_sound( "environment", "deafness_tone_start", seas_str, 100 ); + play_variant_sound( "environment", "deafness_shock", seas_str, indoors, night, 100 ); + play_variant_sound( "environment", "deafness_tone_start", seas_str, indoors, night, 100 ); if( turns <= 35 ) { - play_ambient_variant_sound( "environment", "deafness_tone_light", seas_str, 90, - channel::deafness_tone, 100 ); + play_ambient_variant_sound( "environment", "deafness_tone_light", seas_str, + indoors, night, 90, channel::deafness_tone, 100 ); } else if( turns <= 90 ) { - play_ambient_variant_sound( "environment", "deafness_tone_medium", seas_str, 90, - channel::deafness_tone, 100 ); + play_ambient_variant_sound( "environment", "deafness_tone_medium", seas_str, + indoors, night, 90, channel::deafness_tone, 100 ); } else if( turns >= 91 ) { - play_ambient_variant_sound( "environment", "deafness_tone_heavy", seas_str, 90, - channel::deafness_tone, 100 ); + play_ambient_variant_sound( "environment", "deafness_tone_heavy", seas_str, + indoors, night, 90, channel::deafness_tone, 100 ); } } @@ -1668,8 +1730,11 @@ void sfx::do_footstep() }; const auto play_plmove_sound_variant = [&]( const std::string & variant, - const std::string & season ) { - play_variant_sound( "plmove", variant, season, heard_volume, 0_degrees, 0.8, 1.2 ); + const std::string & season, + const cata::optional &indoors, + const cata::optional &night ) { + play_variant_sound( "plmove", variant, season, indoors, night, + heard_volume, 0_degrees, 0.8, 1.2 ); start_sfx_timestamp = std::chrono::high_resolution_clock::now(); }; @@ -1677,48 +1742,50 @@ void sfx::do_footstep() const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); + const bool indoors = !is_creature_outside( player_character ); + const bool night = is_night( calendar::turn ); if( !veh_displayed_part && ( terrain->has_flag( ter_furn_flag::TFLAG_DEEP_WATER ) || terrain->has_flag( ter_furn_flag::TFLAG_SHALLOW_WATER ) ) ) { - play_plmove_sound_variant( "walk_water", seas_str ); + play_plmove_sound_variant( "walk_water", seas_str, indoors, night ); return; } if( !player_character.wearing_something_on( bodypart_id( "foot_l" ) ) ) { - play_plmove_sound_variant( "walk_barefoot", seas_str ); + play_plmove_sound_variant( "walk_barefoot", seas_str, indoors, night ); return; } if( veh_displayed_part ) { const std::string &part_id = veh_displayed_part->part().info().get_id().str(); - if( has_variant_sound( "plmove", part_id, seas_str ) ) { - play_plmove_sound_variant( part_id, seas_str ); + if( has_variant_sound( "plmove", part_id, seas_str, indoors, night ) ) { + play_plmove_sound_variant( part_id, seas_str, indoors, night ); } else if( veh_displayed_part->has_feature( VPFLAG_AISLE ) ) { - play_plmove_sound_variant( "walk_tarmac", seas_str ); + play_plmove_sound_variant( "walk_tarmac", seas_str, indoors, night ); } else { - play_plmove_sound_variant( "clear_obstacle", seas_str ); + play_plmove_sound_variant( "clear_obstacle", seas_str, indoors, night ); } return; } - if( sfx::has_variant_sound( "plmove", terrain.str(), seas_str ) ) { - play_plmove_sound_variant( terrain.str(), seas_str ); + if( sfx::has_variant_sound( "plmove", terrain.str(), seas_str, indoors, night ) ) { + play_plmove_sound_variant( terrain.str(), seas_str, indoors, night ); return; } if( grass.count( terrain ) > 0 ) { - play_plmove_sound_variant( "walk_grass", seas_str ); + play_plmove_sound_variant( "walk_grass", seas_str, indoors, night ); return; } if( dirt.count( terrain ) > 0 ) { - play_plmove_sound_variant( "walk_dirt", seas_str ); + play_plmove_sound_variant( "walk_dirt", seas_str, indoors, night ); return; } if( metal.count( terrain ) > 0 ) { - play_plmove_sound_variant( "walk_metal", seas_str ); + play_plmove_sound_variant( "walk_metal", seas_str, indoors, night ); return; } if( chain_fence.count( terrain ) > 0 ) { - play_plmove_sound_variant( "clear_obstacle", seas_str ); + play_plmove_sound_variant( "clear_obstacle", seas_str, indoors, night ); return; } - play_plmove_sound_variant( "walk_tarmac", seas_str ); + play_plmove_sound_variant( "walk_tarmac", seas_str, indoors, night ); } } @@ -1728,17 +1795,23 @@ void sfx::do_obstacle( const std::string &obst ) return; } + const Character &player_character = get_player_character(); const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); - int heard_volume = sfx::get_heard_volume( get_player_character().pos() ); - if( sfx::has_variant_sound( "plmove", obst, seas_str ) ) { - play_variant_sound( "plmove", obst, seas_str, heard_volume, 0_degrees, 0.8, 1.2 ); + const bool indoors = !is_creature_outside( player_character ); + const bool night = is_night( calendar::turn ); + int heard_volume = sfx::get_heard_volume( player_character.pos() ); + if( sfx::has_variant_sound( "plmove", obst, seas_str, indoors, night ) ) { + play_variant_sound( "plmove", obst, seas_str, indoors, night, + heard_volume, 0_degrees, 0.8, 1.2 ); } else if( ter_str_id( obst ).is_valid() && ( ter_id( obst )->has_flag( ter_furn_flag::TFLAG_SHALLOW_WATER ) || ter_id( obst )->has_flag( ter_furn_flag::TFLAG_DEEP_WATER ) ) ) { - play_variant_sound( "plmove", "walk_water", seas_str, heard_volume, 0_degrees, 0.8, 1.2 ); + play_variant_sound( "plmove", "walk_water", seas_str, indoors, night, + heard_volume, 0_degrees, 0.8, 1.2 ); } else { - play_variant_sound( "plmove", "clear_obstacle", seas_str, heard_volume, 0_degrees, 0.8, 1.2 ); + play_variant_sound( "plmove", "clear_obstacle", seas_str, indoors, + night, heard_volume, 0_degrees, 0.8, 1.2 ); } // prevent footsteps from triggering start_sfx_timestamp = std::chrono::high_resolution_clock::now(); @@ -1758,9 +1831,12 @@ void sfx::play_activity_sound( const std::string &id, const std::string &variant return; } Character &player_character = get_player_character(); + const bool indoors = !is_creature_outside( player_character ); + const bool night = is_night( calendar::turn ); if( act != player_character.activity.id() ) { act = player_character.activity.id(); - play_ambient_variant_sound( id, variant, season, volume, channel::player_activities, 0 ); + play_ambient_variant_sound( id, variant, season, indoors, night, + volume, channel::player_activities, 0 ); } } @@ -1777,7 +1853,9 @@ void sfx::play_variant_sound( const std::string &id, const std::string &variant, { const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); - play_variant_sound( id, variant, seas_str, volume ); + const bool indoors = !is_creature_outside( get_player_character() ); + const bool night = is_night( calendar::turn ); + play_variant_sound( id, variant, seas_str, indoors, night, volume ); } void sfx::play_variant_sound( const std::string &id, const std::string &variant, int volume, @@ -1785,7 +1863,10 @@ void sfx::play_variant_sound( const std::string &id, const std::string &variant, { const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); - play_variant_sound( id, variant, seas_str, volume, angle, pitch_min, pitch_max ); + const bool indoors = !is_creature_outside( get_player_character() ); + const bool night = is_night( calendar::turn ); + play_variant_sound( id, variant, seas_str, indoors, night, + volume, angle, pitch_min, pitch_max ); } void sfx::play_ambient_variant_sound( const std::string &id, const std::string &variant, int volume, @@ -1793,15 +1874,19 @@ void sfx::play_ambient_variant_sound( const std::string &id, const std::string & { const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); - play_ambient_variant_sound( id, variant, seas_str, volume, channel, fade_in_duration, pitch, - loops ); + const bool indoors = !is_creature_outside( get_player_character() ); + const bool night = is_night( calendar::turn ); + play_ambient_variant_sound( id, variant, seas_str, indoors, night, + volume, channel, fade_in_duration, pitch, loops ); } bool sfx::has_variant_sound( const std::string &id, const std::string &variant ) { const season_type seas = season_of_year( calendar::turn ); const std::string seas_str = season_str( seas ); - return has_variant_sound( id, variant, seas_str ); + const bool indoors = !is_creature_outside( get_player_character() ); + const bool night = is_night( calendar::turn ); + return has_variant_sound( id, variant, seas_str, indoors, night ); } #else // if defined(SDL_SOUND) @@ -1848,7 +1933,8 @@ void sfx::stop_sound_effect_timed( channel, int ) { } void sfx::do_player_death_hurt( const Character &, bool ) { } void sfx::do_fatigue() { } void sfx::do_obstacle( const std::string & ) { } -void sfx::play_variant_sound( const std::string &, const std::string &, const std::string &, int ) { } +void sfx::play_variant_sound( const std::string &, const std::string &, const std::string &, + const cata::optional &, const cata::optional &, int ) { } /*@}*/ #endif // if defined(SDL_SOUND) diff --git a/src/sounds.h b/src/sounds.h index a65a13e1a4eb5..895354756e16d 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -7,6 +7,7 @@ #include #include "units_fwd.h" +#include "optional.h" class Character; class Creature; @@ -139,16 +140,19 @@ void load_playlist( const JsonObject &jsobj ); void play_variant_sound( const std::string &id, const std::string &variant, int volume, units::angle angle, double pitch_min = -1.0, double pitch_max = -1.0 ); void play_variant_sound( const std::string &id, const std::string &variant, - const std::string &season, int volume, - units::angle angle, double pitch_min = -1.0, double pitch_max = -1.0 ); + const std::string &season, const cata::optional &is_indoors, + const cata::optional &is_night, int volume, units::angle angle, + double pitch_min = -1.0, double pitch_max = -1.0 ); void play_variant_sound( const std::string &id, const std::string &variant, int volume ); void play_variant_sound( const std::string &id, const std::string &variant, - const std::string &season, int volume ); + const std::string &season, const cata::optional &is_indoors, + const cata::optional &is_night, int volume ); void play_ambient_variant_sound( const std::string &id, const std::string &variant, int volume, channel channel, int fade_in_duration, double pitch = -1.0, int loops = -1 ); void play_ambient_variant_sound( const std::string &id, const std::string &variant, - const std::string &season, - int volume, channel channel, int fade_in_duration, double pitch = -1.0, int loops = -1 ); + const std::string &season, const cata::optional &is_indoors, + const cata::optional &is_night, int volume, + channel channel, int fade_in_duration, double pitch = -1.0, int loops = -1 ); void play_activity_sound( const std::string &id, const std::string &variant, int volume ); void play_activity_sound( const std::string &id, const std::string &variant, const std::string &season, int volume ); @@ -171,7 +175,8 @@ void fade_audio_channel( channel channel, int duration ); bool is_channel_playing( channel channel ); bool has_variant_sound( const std::string &id, const std::string &variant ); bool has_variant_sound( const std::string &id, const std::string &variant, - const std::string &season ); + const std::string &season, const cata::optional &is_indoors, + const cata::optional &is_night ); void stop_sound_effect_fade( channel channel, int duration ); void stop_sound_effect_timed( channel channel, int time ); int set_channel_volume( channel channel, int volume );