indexing description: "[ Implements channels for use with audio mixer. Use this class to play and pause sound files {EM_SOUND}. You may also apply effects and do other manipulations. Note: Whenever possible, don't create channels on your own. Use {EM_CHANNELS}.extend instead, as channel numbering must stay unique. Fading is blocking as your application may crash if closed while fading in or out. ]" date: "$Date$" revision: "$Revision$" class EM_CHANNEL inherit EM_SHARED_ERROR_HANDLER export {NONE} all end EM_SHARED_AUDIO_FACTORY export {NONE} all end EM_AUDIO_CONSTANTS export {NONE} all {ANY} Em_max_volume end SDL_MIXER_FUNCTIONS_EXTERNAL export {NONE} all end create make feature {NONE} -- Initialization make (a_channel_volume: like volume; a_channel_number: like number) is -- Initalize channel with `a_channel_volume' and `a_channel_number'. require volume_in_range: 0 <= a_channel_volume and then a_channel_volume <= Em_max_volume channel_number_positiv: a_channel_number > 0 do internal_number := a_channel_number - 1 set_volume (a_channel_volume) has_reversed_stereo := False create effects.make_empty ensure number_set: number = a_channel_number internal_number_set: internal_number = a_channel_number - 1 volume_set: volume = a_channel_volume end feature -- Setters set_number (a_number: like number) is -- Set a number for the channel. -- -- Note: Only use, if you know what you're doing. require number_positiv: a_number >= 0 do internal_number := a_number - 1 ensure internal_number_set: internal_number = a_number - 1 number_set: number = a_number end set_volume (a_channel_volume: like volume) is -- Set volume in range [0, `Em_max_volume'] of this channel. require volume_in_range: 0 <= a_channel_volume and then a_channel_volume <= Em_max_volume do -- TODO: There is a bug when setting volume in SDL! -- Documentation tells that the new value is returned after setting volume. -- But it is returned the value before setting the new one. -- That's why we call this function twice. volume := mix_volume_external (internal_number, a_channel_volume) volume := mix_volume_external (internal_number, a_channel_volume) ensure volume_set: volume = a_channel_volume end set_panning (left_volume: INTEGER; right_volume: INTEGER) is -- Set panning for this channel. -- Volume range for both sides is [0..255]. -- -- Set `left_volume' and `right_volume' to 255 to disable panning. require left_volume_valid: left_volume >= 0 and then left_volume <= 255 right_volume_valid: right_volume >= 0 and then right_volume <= 255 local i: INTEGER do i := mix_set_panning_external (internal_number, left_volume, right_volume) if i = 0 then error_handler.raise_error (error_handler.Em_error_registring_effect, [left_volume, right_volume]) end end set_distance (a_distance: INTEGER) is -- Set a distance factor for this channel. -- Distance range is [0..255]. -- -- Set `a_distance' to 0 to disable effect. require distance_valid: a_distance >= 0 and then a_distance <= 255 local i: INTEGER do i := mix_set_distance_external (internal_number, a_distance) if i = 0 then error_handler.raise_error (error_handler.Em_error_registring_effect, [a_distance]) end end set_position (an_angle: INTEGER; a_distance: INTEGER) is -- Set a postion with `an_angle' and `a_distance' for the channel. -- Range of `an_angle' is [0..360] and range of `a_distance is [0..255]. -- -- Values for `an_angle': 0 = directly in front of you. -- 90 = directly to your right. -- 180 = directly behind you. -- 270 = directly to your left. -- -- Set `an_angle' and `a_distance' to 0 to disable positioning. require distance_valid: a_distance >= 0 and then a_distance <= 255 angle_valid: an_angle >= 0 and then an_angle <= 360 local i: INTEGER do i := mix_set_position_external (internal_number, an_angle, a_distance) if i = 0 then error_handler.raise_error (error_handler.Em_error_registring_effect, [an_angle, a_distance]) end end toggle_reverse_stereo is -- Reverse stereo for the channel according to `has_reversed_stereo'. local i: INTEGER do if has_reversed_stereo then i := mix_set_reverse_stereo_external (internal_number, 0) else i := mix_set_reverse_stereo_external (internal_number, 1) end has_reversed_stereo := not has_reversed_stereo if i = 0 then error_handler.raise_error (error_handler.Em_error_registring_effect, []) end ensure toggled_stereo: has_reversed_stereo = not old has_reversed_stereo end toggle_mute is -- Mute or unmute channel based on `is_mute'. do if is_mute then set_volume (mute_volume) else mute_volume := volume set_volume (0) end is_mute := not is_mute ensure toggled_mute: is_mute = not old is_mute end feature -- Effects register_effect (an_effect: EM_EFFECT; an_argument: POINTER) is -- Apply `an_effect' to this channel. -- Pass in `an_argument' if the effect uses some userparameters. -- -- The effect will be added into `effects' for easy accessing. require effect_not_void: an_effect /= Void local i: INTEGER fx_func: MIX_EFFECT_FUNC_T_DISPATCHER fx_done: MIX_EFFECT_DONE_T_DISPATCHER do create fx_func.make (an_effect) create fx_done.make (an_effect) an_effect.set_effect_function (fx_func.c_dispatcher) an_effect.set_effect_done (fx_done.c_dispatcher) i := mix_register_effect_external (internal_number, an_effect.effect_function, an_effect.effect_done, an_argument) effects.extend (an_effect) ensure effect_added: effects.count = old effects.count + 1 end unregister_effect (an_effect: EM_EFFECT) is -- Remove all effects corresponding to `an_effect' from the effect list. require effect_not_void: an_effect /= Void local i: INTEGER do i := mix_unregister_effect_external (internal_number, an_effect.effect_function) if i = 0 then error_handler.raise_error (error_handler.Em_error_unregistring_effect, []) end effects.remove (an_effect) ensure effect_removed: effects.count < old effects.count end unregister_all_effects is -- Removes all registered effects. local i: INTEGER do i := mix_unregister_all_effects_external (internal_number) effects.wipe_out ensure effect_removed: effects.count = 0 end feature -- Access volume: INTEGER -- Current channel volume number: INTEGER is -- Current channel number do Result := internal_number + 1 ensure Result_set: Result = internal_number + 1 end has_reversed_stereo: BOOLEAN -- Is stereo reversed? is_mute: BOOLEAN -- is channel mute? effects: EM_EFFECTS -- All applied effects to this channel is_playing: BOOLEAN is -- Is channel playing? -- -- Attention: A paused channel is also playing! do Result := mix_playing_external (internal_number) = 1 end is_paused: BOOLEAN is -- Is channel paused? -- -- Attention: If channel was halted after pausing, channel is still paused. do Result := mix_paused_external (internal_number) = 1 end is_fading: BOOLEAN is -- Is channel fading in or out? do Result := is_fading_in or else is_fading_out end is_fading_in: BOOLEAN is -- Is channel fading in? do Result := mix_fading_channel_external (internal_number) = Em_fading_in end is_fading_out: BOOLEAN is -- Is channel fading out? do Result:= mix_fading_channel_external (internal_number) = Em_fading_out end feature -- Playing play (a_sound: EM_SOUND; a_loop_count: INTEGER) is -- Play `a_sound' for `a_loop_count' times on this channel. -- -- Passing in -1 for `a_loop_count' will loop infinite times. -- Any other value will play the sample `a_loop_count' + 1 times. require sound_not_void: a_sound /= Void local i: INTEGER do i := mix_play_channel_timed_external (internal_number, a_sound.to_pointer, a_loop_count, -1) if i = -1 then error_handler.raise_error (error_handler.Em_error_playing_channel, [a_loop_count]) end end play_timed (a_sound: EM_SOUND; a_loop_count: INTEGER; a_tick_count: INTEGER) is -- Play `a_sound' for `a_loop_count' times on this channel for a maximum -- of `a_tick_count' milliseconds. -- -- Passing in -1 for `a_loop_count' will loop infinite times. -- Any other value will play the sample `a_loop_count' + 1 times. -- -- Passing in -1 for `a_tick_count' will play sound forever. require sound_not_void: a_sound /= Void local i: INTEGER do i := mix_play_channel_timed_external (internal_number, a_sound.to_pointer, a_loop_count, a_tick_count) if i = -1 then error_handler.raise_error (error_handler.em_error_playing_channel_timed, [a_loop_count, a_tick_count]) end end fade_in (a_sound: EM_SOUND; a_loop_count: INTEGER; a_duration: INTEGER) is -- Play `a_sound' for `a_loop_count' times with a fade in effect of -- length `a_duration' milliseconds. -- -- Passing in -1 for `a_loop_count' will loop infinite times. -- Any other value will play the sample `a_loop_count' + 1 times. -- -- Attention: This function is non-blocking. -- Please check if the channel is fading before -- you quit your application. require valid_duration: a_duration >= 0 sound_not_void: a_sound /= Void local i: INTEGER do i := mix_fade_in_channel_timed_external (internal_number, a_sound.to_pointer, a_loop_count, a_duration, -1) if i = -1 then error_handler.raise_error (error_handler.em_error_playing_channel_timed, [a_loop_count, a_duration]) end end fade_in_timed (a_sound: EM_SOUND; a_loop_count: INTEGER; a_duration: INTEGER; a_tick_count: INTEGER) is -- Play `a_sound' for `a_loop_count' times with a fade in effect of -- length `a_duration' milliseconds and maximum time of `a_tick_count' -- milliseconds. -- -- Passing in -1 `a_loop_count' will loop infinite times. -- Any other value will play the sample `a_loop_count' + 1 times. -- -- Passing in -1 for `a_tick_count' will play sound forever. -- -- Attention: This function is non-blocking. -- Please check if the channel is fading before -- you quit your application. require valid_duration: a_duration >= 0 sound_not_void: a_sound /= Void local i: INTEGER do i := mix_fade_in_channel_timed_external (internal_number, a_sound.to_pointer, a_loop_count, a_duration, a_tick_count) if i = -1 then error_handler.raise_error (error_handler.em_error_playing_channel_timed, [a_loop_count, a_duration, a_tick_count]) end end feature -- Pausing pause is -- Pause channel if actively playing. require is_playing: is_playing do mix_pause_external (internal_number) ensure is_paused: is_paused end resume is -- Resume paused channel. require is_paused: is_paused do mix_resume_external (internal_number) ensure is_playing: is_playing end feature -- Stopping stop is -- Stop playing channel. require not_stopped: is_paused or else is_playing local i: INTEGER do -- TODO: As mix_halt_channel_external does not reset -- paused status flag when stopping, we resume -- the channel first. if is_paused then resume end i := mix_halt_channel_external (internal_number) ensure stopped: not (is_paused or else is_playing or else is_fading) end expire (a_tick_count: INTEGER) is -- Halt channel after `a_tick_count' milliseconds. require valid_tick_count: a_tick_count >= 0 local i: INTEGER do i := mix_expire_channel_external (internal_number, a_tick_count) end fade_out (a_duration: INTEGER) is -- Stop playing channel after fade out of `a_duration' milliseconds. -- -- Attention: This function is non-blocking. -- Please check if the channel is fading before -- you quit your application. require valid_duration: a_duration >= 0 local i: INTEGER do i := mix_fade_out_channel_external (internal_number, a_duration) end feature {NONE} -- Internal variables internal_number: INTEGER -- Internal channel number mute_volume: INTEGER -- Volume backup for mute function invariant number_is_positiv: internal_number >= 0 effects_created: effects /= Void end