indexing description: "[ Implements a channel part in the mixer. Use this class to create a channel. Note: You need to give a master for creating a channel, because the channel needs to access it. ]" date: "$Date$" revision: "$Revision$" class MIXER_CHANNEL inherit MIXER_CONSTANTS export {NONE} all end EM_AUDIO_CONSTANTS export {NONE} all end EM_SHARED_AUDIO_FACTORY export {NONE} all end EM_PANEL DOUBLE_MATH export {NONE} all end create make_with_identifier feature {NONE} -- Initialization make_with_identifier (an_identifier: like identifier; a_master: like master) is -- Initialize `Current'. -- `an_identifier': id for current channel (from 1-6). -- `a_master': information from the master for the channels. require a_master_not_void: a_master /= Void do make_void_surface master := a_master identifier := an_identifier create active.make_with_image_positioned ("active.png", 18, 66) active.clicked_event.subscribe (agent active_button_clicked) add_widget (active) channel := audio_subsystem.mixer.channels.i_th (identifier) make_groups make_playback make_effects make_panning make_volume end make_groups is -- Make group buttons to select one for each channel. local i: INTEGER group_button: MIXER_BUTTON_SMALL group: EM_AUDIO_GROUP do -- Create group lamps audio_subsystem.mixer.channels.groups.extend (6) create group_buttons.make (1, 6) from i := 1 until i > 6 loop create group.make (i) audio_subsystem.mixer.channels.groups.put (group, i) create group_button.make_with_image_positioned ("group.png", 15 + (i - 1) * 13, 138) group_button.clicked_event.subscribe (agent group_button_clicked (i)) add_widget (group_button) group_buttons.put (group_button, i) i := i + 1 end groups := audio_subsystem.mixer.channels.groups end make_playback is -- Create the playback area in the channels. do -- Create combobox make_combobox -- create play button create play.make_with_image_positioned (Void, 18, 218) play.clicked_event.subscribe (agent play_button_clicked) add_widget (play) -- create stop button create stop.make_with_image_positioned (Void, 57, 218) stop.clicked_event.subscribe (agent stop_button_clicked) add_widget (stop) -- create mute button create mute.make_with_image_positioned ("mute.png", 18, 245) mute.clicked_event.subscribe (agent mute_button_clicked) add_widget (mute) -- create reverse button create reverse.make_with_image_positioned ("reverse.png", 57, 245) reverse.clicked_event.subscribe (agent reverse_button_clicked) add_widget (reverse) -- create fade button create fade.make_with_image_positioned ("fade.png", 18, 272) fade.clicked_event.subscribe (agent fade_button_clicked) add_widget (fade) -- create fade_value textbox create fade_value.make_with_text_positioned ("", 57, 272) fade_value.key_down_event.subscribe (agent fade_value_changed) add_widget (fade_value) create old_fade_value.make_empty end make_effects is -- Create the two effect buttons for the channel. do -- create echo button create echo.make_with_image_positioned ("echo.png", 18, 335) echo.clicked_event.subscribe (agent echo_button_clicked) add_widget (echo) create echo_effect.make (500, 0.4) -- create phasing button create phasing.make_with_image_positioned ("phasing.png", 18, 361) phasing.clicked_event.subscribe (agent phasing_button_clicked) add_widget (phasing) create phasing_effect.make (500, 0.4) end make_panning is -- Create the panning slider for the channel. do create panning.make_positioned (16, 424) panning.position_changed_event.subscribe (agent panning_changed) add_widget (panning) panning.disable end make_volume is -- Create the volume slider for the channel. do create volume.make_positioned (18, 500) volume.position_changed_event.subscribe (agent volume_changed) add_widget (volume) volume.disable end make_combobox is -- Create the combobox where you can choose a sound for the channel. local extension_list: DS_LINKED_LIST [STRING] i: INTEGER filename: STRING playlist: EM_PLAYLIST do create extension_list.make extension_list.put_last (".ogg") extension_list.put_last (".wav") extension_list.put_last (".mid") extension_list.put_last (".mod") create playlist.make_with_path (sound_directory, extension_list, True) create combobox.make_empty from i := 1 until i > playlist.count loop filename := playlist.get_i_th (i) combobox.put (filename.substring (sound_directory.count + 2, filename.count)) i := i + 1 end combobox.set_position (18, 191) combobox.set_dimension (72, 21) combobox.set_transparent combobox.set_border (create {EM_NO_BORDER}) combobox.set_font (create {EM_TTF_FONT}.make_from_ttf_file ("slicker.ttf", 10)) combobox.set_selection_background_color (create {EM_COLOR}.make_with_rgb (102, 102, 102)) add_widget (combobox) end feature -- Access identifier: INTEGER -- Current Channel identifier active: MIXER_BUTTON_LARGE -- Active button echo: MIXER_BUTTON_LARGE -- Echo button phasing: MIXER_BUTTON_LARGE -- Phasing button stop: MIXER_BUTTON_MEDIUM -- Stop button play: MIXER_BUTTON_MEDIUM -- Play button mute: MIXER_BUTTON_MEDIUM -- Mute button reverse: MIXER_BUTTON_MEDIUM -- Reverse button fade: MIXER_BUTTON_MEDIUM -- Fade button fade_value: MIXER_TEXTBOX -- Fade textbox combobox: EM_COMBOBOX [STRING] -- Combobox for channels group_buttons: ARRAY [MIXER_BUTTON_SMALL] -- Group buttons panning: MIXER_SLIDER_PANNING -- Panning slider volume: MIXER_SLIDER_VOLUME -- Volume slider group_active: INTEGER -- Current active group channel: EM_CHANNEL -- Access to EM_CHANNEL functions groups: EM_AUDIO_GROUPS -- Access to EM_GOUPS functions track: EM_SOUND -- Current sound track master: MIXER_MASTER -- Access to Master of Mixer feature -- event active_button_clicked is -- Event for the active button. -- With a non-active channel you can't change anything. do active.toggle_active if active.active then fade_value.set_text ("1000") create old_fade_value.make_from_string (fade_value.text) group_active := 0 panning.enable volume.enable else if channel.is_playing then if fade.active then channel.fade_out (fade_value.text.to_integer) from until not channel.is_fading_out loop end else channel.stop end end combobox.unselect volume.set_current_value (Em_max_volume) panning.set_current_value (0) if group_active > 0 then groups.i_th (group_active).remove_channel (channel) group_buttons.item (group_active).toggle_active end if mute.active then mute.toggle_active end if reverse.active then reverse.toggle_active end if fade.active then fade.toggle_active end if echo.active then echo.toggle_active end if phasing.active then phasing.toggle_active end fade_value.set_text ("") panning.disable volume.disable end ensure active_changed: active.active = not old active.active end group_button_clicked (an_index: INTEGER) is -- Event for the group button. -- Sets the options in a channel is added/removed from a group. -- `an_index': number of selected group. local chan: MIXER_CHANNEL do if active.active then group_buttons.item (an_index).toggle_active if group_active = 0 then if groups.i_th (an_index).count > 0 then chan := master.channels.item (groups.i_th (an_index).channels.item (1) + 1) if mute.active /= chan.mute.active then mute_button_clicked end if reverse.active /= chan.reverse.active then reverse_button_clicked end if fade.active /= chan.fade.active then fade_button_clicked end if not fade_value.text.is_equal (chan.fade_value.text) then fade_value.set_text (chan.fade_value.text) end if echo.active /= chan.echo.active then echo_button_clicked end if phasing.active /= chan.phasing.active then phasing_button_clicked end if panning.current_value /= chan.panning.current_value then panning.set_current_value (chan.panning.current_value) end if volume.current_value /= chan.volume.current_value then volume.set_current_value (chan.volume.current_value) end end groups.i_th (an_index).add_channel (channel) group_active := an_index else groups.i_th (group_active).remove_channel (channel) if an_index /= group_active then if groups.i_th (an_index).count > 0 then chan := master.channels.item (groups.i_th (an_index).channels.item (1) + 1) if mute.active /= chan.mute.active then mute_button_clicked end if reverse.active /= chan.reverse.active then reverse_button_clicked end if fade.active /= chan.fade.active then fade_button_clicked end if not fade_value.text.is_equal (chan.fade_value.text) then fade_value.set_text (chan.fade_value.text) end if echo.active /= chan.echo.active then echo_button_clicked end if phasing.active /= chan.phasing.active then phasing_button_clicked end if panning.current_value /= chan.panning.current_value then panning.set_current_value (chan.panning.current_value) end if volume.current_value /= chan.volume.current_value then volume.set_current_value (chan.volume.current_value) end end groups.i_th (an_index).add_channel (channel) group_buttons.item (group_active).toggle_active group_active := an_index else group_active := 0 end end end end play_button_clicked is -- Event for the play button. -- Plays a new sound file or pause/resume one. local i: INTEGER chan: MIXER_CHANNEL error_occured: BOOLEAN do if not error_occured then if master.active.active and then active.active then if channel.is_playing and not channel.is_paused then channel.pause else if channel.is_paused then channel.resume else if combobox.selected_element /= Void then audio_factory.create_sound_from_file (sound_directory + "/" + combobox.selected_element) track := audio_factory.last_sound track.set_volume (volume.current_volume) channel.set_volume (master.volume.current_volume) if fade.active then channel.fade_in_timed (track, -1, fade_value.text.to_integer, -1) if group_active = 0 then from until not channel.is_fading_in loop end end else channel.play (track, -1) end end end end if group_active > 0 then from i := 1 until i > 6 loop if i /= identifier and then group_active = master.channels.item (i).group_active then chan := master.channels.item (i) if chan.combobox.selected_element /= Void then if chan.channel.is_playing then chan.channel.stop end audio_factory.create_sound_from_file (sound_directory + "/" + chan.combobox.selected_element) chan.set_track (audio_factory.last_sound) chan.track.set_volume (chan.volume.current_volume) chan.channel.set_volume (master.volume.current_volume) if chan.fade.active then chan.channel.fade_in_timed (chan.track, -1, chan.fade_value.text.to_integer, -1) else chan.channel.play (chan.track, -1) end end end i := i + 1 end from until chan = Void or else not chan.channel.is_fading_in loop end end end end rescue error_occured := true retry end stop_button_clicked is -- Event for stop button. -- Stops an active playing channel. do if active.active then if group_active > 0 then if fade.active then groups.i_th (group_active).fade_out (fade_value.text.to_integer) from until not channel.is_fading_out loop end else groups.i_th (group_active).stop end else if channel.is_playing then if fade.active then channel.fade_out (fade_value.text.to_integer) from until not channel.is_fading_out loop end else channel.stop end end end end end mute_button_clicked is -- Event for mute button. -- Mute or unmute a channel. local i: INTEGER chan: MIXER_CHANNEL do if active.active then mute.toggle_active if track /= Void then if mute.active then track.set_volume (0) else track.set_volume (volume.current_volume) end end if group_active > 0 then from i := 1 until i > 6 loop if i /= identifier and then group_active = master.channels.item (i).group_active then chan := master.channels.item (i) chan.mute.toggle_active if chan.track /= Void then if chan.mute.active then chan.track.set_volume (0) else chan.track.set_volume (chan.volume.current_volume) end end end i := i + 1 end end end end reverse_button_clicked is -- Event for reverse button. -- Reverse sound of the channel. local i: INTEGER chan: MIXER_CHANNEL do if active.active then reverse.toggle_active if not master.reverse.active then channel.toggle_reverse_stereo end if group_active > 0 then from i := 1 until i > 6 loop if i /= identifier and then group_active = master.channels.item (i).group_active then chan := master.channels.item (i) chan.reverse.toggle_active if not master.reverse.active then chan.channel.toggle_reverse_stereo end end i := i + 1 end end end end fade_button_clicked is -- Event for fade button. -- If fade is activated, the fade effect is used in play and stop with current fade_value. local i: INTEGER do if active.active then fade.toggle_active if group_active > 0 then from i := 1 until i > 6 loop if i /= identifier and then group_active = master.channels.item (i).group_active then master.channels.item (i).fade.toggle_active end i := i + 1 end end end end fade_value_changed (a_keyboard_event: EM_KEYBOARD_EVENT) is -- Event for fade value changed. -- Sets the value for the fade effect. local i: INTEGER do if active.active and then fade_value.text.is_integer then create old_fade_value.make_from_string (fade_value.text) elseif fade_value.text.is_empty then fade_value.set_text ("0") else fade_value.set_text (create {STRING}.make_from_string (old_fade_value)) end if group_active > 0 then from i := 1 until i > 6 loop if i /= identifier and then group_active = master.channels.item (i).group_active then master.channels.item (i).fade_value.set_text (fade_value.text) end i := i + 1 end end end echo_button_clicked is -- Event for the echo button. -- If echo is active, an echo effect will register to current channel sound. local i: INTEGER chan: MIXER_CHANNEL do if active.active then echo.toggle_active if echo.active then channel.register_effect (echo_effect, default_pointer) else channel.unregister_effect (echo_effect) end if group_active > 0 then from i := 1 until i > 6 loop if i /= identifier and then group_active = master.channels.item (i).group_active then chan := master.channels.item (i) chan.echo.toggle_active if chan.echo.active then chan.channel.register_effect (chan.echo_effect, default_pointer) else chan.channel.unregister_effect (chan.echo_effect) end end i := i + 1 end end end end phasing_button_clicked is -- Event for phasing button. -- If phasing is active, a phasing effect will register to the channel sound. local i: INTEGER chan: MIXER_CHANNEL do if active.active then phasing.toggle_active if phasing.active then channel.register_effect (phasing_effect, default_pointer) else channel.unregister_effect (phasing_effect) end if group_active > 0 then from i := 1 until i > 6 loop if i /= identifier and then group_active = master.channels.item (i).group_active then chan := master.channels.item (i) chan.phasing.toggle_active if chan.phasing.active then chan.channel.register_effect (chan.phasing_effect, default_pointer) else chan.channel.unregister_effect (chan.phasing_effect) end end i := i + 1 end end end end panning_changed (a_channel_panning: INTEGER) is -- Event if panning's changed. -- Updates the panning slider and the panning value. -- `a_channel_panning': is the current panning value given from the subscribe event. require a_panning_is_valid: a_channel_panning <= 255 and a_channel_panning >= -255 local i: INTEGER left: INTEGER right: INTEGER master_left: INTEGER master_right: INTEGER result_left: INTEGER result_right: INTEGER left_percent: DOUBLE right_percent: DOUBLE master_left_percent: DOUBLE master_right_percent: DOUBLE new_left_percent: DOUBLE new_right_percent: DOUBLE chan: MIXER_CHANNEL do panning.update_lights if active.active then panning.update_lights if a_channel_panning < 0 then left := 128 + ((- a_channel_panning - 1) / 2).rounded right := 128 - (- a_channel_panning / 2).rounded else left := 128 - (a_channel_panning / 2).rounded right := 128 + ((a_channel_panning - 1) / 2).rounded end if master.active.active and then master.panning.current_value /= 0 then if master.panning.current_value < 0 then master_left := 128 + ((- master.panning.current_value - 1) / 2).rounded master_right := 128 - (- master.panning.current_value / 2).rounded else master_left := 128 - (master.panning.current_value / 2).rounded master_right := 128 + ((master.panning.current_value - 1) / 2).rounded end left_percent := left / (left + right) right_percent := right / (left + right) master_left_percent := master_left / (master_left + master_right) master_right_percent := master_right / (master_left + master_right) new_left_percent := left_percent * master_left_percent new_right_percent := right_percent * master_right_percent if new_left_percent /= 0 or new_right_percent /= 0 then result_left := (sqrt(new_left_percent) * 255).rounded result_right := (sqrt(new_right_percent) * 255).rounded channel.set_panning (result_left , result_right) else result_left := 0 result_right := 0 channel.set_panning (0, 0) end else result_left := left result_right := right channel.set_panning (left, right) end if group_active > 0 then from i := 1 until i > 6 loop if i /= identifier and then group_active = master.channels.item (i).group_active then chan := master.channels.item (i) chan.panning.position_changed_event.suspend_subscription if chan.active.active then chan.channel.set_panning (result_left, result_right) chan.panning.set_current_value (a_channel_panning) end chan.panning.update_lights chan.panning.position_changed_event.restore_subscription end i := i + 1 end end end end volume_changed (a_volume: INTEGER) is -- Event if volume's changed. -- Updates the volume slider and the volume value. -- `a_volume': is the current volume value given by the subscribe event. require a_volume_is_valid: a_volume <= 255 and a_volume >= 0 local i: INTEGER chan: MIXER_CHANNEL do volume.update_lights if active.active then if track /= Void then if not mute.active then track.set_volume (volume.current_volume) end end if group_active > 0 then from i := 1 until i > 6 loop if i /= identifier and then group_active = master.channels.item (i).group_active then chan := master.channels.item (i) chan.volume.position_changed_event.suspend_subscription if chan.active.active then chan.volume.set_current_value (a_volume) if chan.track /= Void then if not chan.mute.active then chan.track.set_volume (chan.volume.current_volume) end end end chan.volume.update_lights chan.volume.position_changed_event.restore_subscription end i := i + 1 end end end end feature {MIXER_CHANNEL, MIXER_MASTER} -- Setters set_track (a_track: like track) is -- Set the channel track. do track := a_track end feature {MIXER_CHANNEL, MIXER_MASTER} -- Effects echo_effect: EM_ECHO_EFFECT -- Echo effect phasing_effect: EM_PHASING_EFFECT -- Phasing effect feature {NONE} -- Internal variables old_fade_value: STRING -- Old fade value end