diff --git a/Integrated Presenter/AudioPlayer.xaml.cs b/Integrated Presenter/AudioPlayer.xaml.cs index e2bd40be..76a0f0a1 100644 --- a/Integrated Presenter/AudioPlayer.xaml.cs +++ b/Integrated Presenter/AudioPlayer.xaml.cs @@ -64,27 +64,37 @@ private void ClickLoadAudio(object sender, RoutedEventArgs e) if (ofd.ShowDialog() == true) { string filename = ofd.FileName; - try - { - tbFileName.Text = filename; - AudioSource = new Uri(filename); - audioplayer.Source = AudioSource; - } - catch (Exception) - { - } - Dispatcher.Invoke(() => - { - StopAudio(); - PlaybackTimer.Interval = 500; - PlaybackTimer.Start(); - BtnPlayAudio.Style = Application.Current.FindResource("SwitcherButton") as Style; - BtnPauseAudio.Style = Application.Current.FindResource("SwitcherButton") as Style; - BtnStopAudio.Style = Application.Current.FindResource("SwitcherButton") as Style; - BtnRestartAudio.Style = Application.Current.FindResource("SwitcherButton") as Style; - }); + openAudio(filename); + } + } + private void openAudio(string filename) + { + try + { + tbFileName.Text = filename; + AudioSource = new Uri(filename); + audioplayer.Source = AudioSource; } + catch (Exception) + { + } + Dispatcher.Invoke(() => + { + StopAudio(); + PlaybackTimer.Interval = 500; + PlaybackTimer.Start(); + BtnPlayAudio.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnPauseAudio.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnStopAudio.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnRestartAudio.Style = Application.Current.FindResource("SwitcherButton") as Style; + }); + + } + + public void OpenAudio(string filename) + { + openAudio(filename); } private void ClickRestartAudio(object sender, RoutedEventArgs e) diff --git a/Integrated Presenter/BMDSwitcher/BMDSwitcherAuxMonitor.cs b/Integrated Presenter/BMDSwitcher/BMDSwitcherAuxMonitor.cs new file mode 100644 index 00000000..7ab3ef75 --- /dev/null +++ b/Integrated Presenter/BMDSwitcher/BMDSwitcherAuxMonitor.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using BMDSwitcherAPI; + +namespace Integrated_Presenter.BMDSwitcher +{ + class BMDSwitcherAuxMonitor : IBMDSwitcherInputAuxCallback + { + public event SwitcherEventHandler OnAuxInputChanged; + public void Notify(_BMDSwitcherInputAuxEventType eventType) + { + switch (eventType) + { + case _BMDSwitcherInputAuxEventType.bmdSwitcherInputAuxEventTypeInputSourceChanged: + OnAuxInputChanged?.Invoke(this, null); + break; + default: + break; + } + } + } +} diff --git a/Integrated Presenter/BMDSwitcher/BMDSwitcherManager.cs b/Integrated Presenter/BMDSwitcher/BMDSwitcherManager.cs index 82b701e2..20965f02 100644 --- a/Integrated Presenter/BMDSwitcher/BMDSwitcherManager.cs +++ b/Integrated Presenter/BMDSwitcher/BMDSwitcherManager.cs @@ -23,6 +23,7 @@ public class BMDSwitcherManager : IBMDSwitcherManager private IBMDSwitcher _BMDSwitcher; private IBMDSwitcherMixEffectBlock _BMDSwitcherMixEffectBlock1; + private IBMDSwitcherInputAux _BMDSwitcherAuxInput; private IBMDSwitcherKey _BMDSwitcherUpstreamKey1; private IBMDSwitcherDownstreamKey _BMDSwitcherDownstreamKey1; private IBMDSwitcherDownstreamKey _BMDSwitcherDownstreamKey2; @@ -37,6 +38,7 @@ public class BMDSwitcherManager : IBMDSwitcherManager private SwitcherMonitor _switcherMonitor; private MixEffectBlockMonitor _mixEffectBlockMonitor; + private BMDSwitcherAuxMonitor _auxMonitor; private UpstreamKeyMonitor _upstreamKey1Monitor; private DownstreamKeyMonitor _dsk1Monitor; private DownstreamKeyMonitor _dsk2Monitor; @@ -71,6 +73,9 @@ public BMDSwitcherManager(Window parent) _mixEffectBlockMonitor.ProgramInputChanged += _mixEffectBlockMonitor_ProgramInputChanged; _mixEffectBlockMonitor.FateToBlackFullyChanged += _mixEffectBlockMonitor_FateToBlackFullyChanged; + _auxMonitor = new BMDSwitcherAuxMonitor(); + _auxMonitor.OnAuxInputChanged += _auxMonitor_OnAuxInputChanged; + _upstreamKey1Monitor = new UpstreamKeyMonitor(); _upstreamKey1Monitor.UpstreamKeyOnAirChanged += _upstreamKey1Monitor_UpstreamKeyOnAirChanged; _upstreamKey1Monitor.UpstreamKeyFillChanged += _upstreamKey1Monitor_UpstreamKeyFillChanged; @@ -98,6 +103,15 @@ public BMDSwitcherManager(Window parent) SwitcherDisconnected(); } + private void _auxMonitor_OnAuxInputChanged(object sender, object args) + { + _parent.Dispatcher.Invoke(() => + { + ForceStateUpdate(); + SwitcherStateChanged?.Invoke(_state); + }); + } + private void _upstreamKey1Monitor_UpstreamKeyTypeChanged(object sender, object args) { _parent.Dispatcher.Invoke(() => @@ -291,6 +305,33 @@ private bool InitializeInputSources() return true; } + private bool InitializeAuxInput() + { + // get all input sources + IBMDSwitcherInputIterator inputIterator = null; + IntPtr inputIteratorPtr; + Guid inputIteratorIID = typeof(IBMDSwitcherInputIterator).GUID; + _BMDSwitcher.CreateIterator(ref inputIteratorIID, out inputIteratorPtr); + if (inputIteratorPtr != null) + { + inputIterator = (IBMDSwitcherInputIterator)Marshal.GetObjectForIUnknown(inputIteratorPtr); + } + else + { + return false; + } + if (inputIterator != null) + { + IBMDSwitcherInput aux; + inputIterator.GetById((long)BMDSwitcherVideoSources.Auxillary1, out aux); + _BMDSwitcherAuxInput = (IBMDSwitcherInputAux)aux; + _BMDSwitcherAuxInput.AddCallback(_auxMonitor); + return true; + } + + return false; + } + private bool InitializeMultiView() { IntPtr multiViewPtr; @@ -443,12 +484,13 @@ private void SwitcherConnected() bool downstreamkeyers = InitializeDownstreamKeyers(); bool inputsources = InitializeInputSources(); + bool auxsource = InitializeAuxInput(); bool multiviewer = InitializeMultiView(); bool mediapool = InitializeMediaPool(); bool mediaplayers = InitializeMediaPlayers(); - GoodConnection = mixeffects && downstreamkeyers && upstreamkeyers && inputsources && multiviewer && mediaplayers && mediapool; + GoodConnection = mixeffects && auxsource && downstreamkeyers && upstreamkeyers && inputsources && multiviewer && mediaplayers && mediapool; MessageBox.Show("Connected to Switcher", "Connection Success"); @@ -490,6 +532,12 @@ private void SwitcherDisconnected() _BMDSwitcherDownstreamKey2 = null; } + if (_BMDSwitcherAuxInput != null) + { + _BMDSwitcherAuxInput.RemoveCallback(_auxMonitor); + _BMDSwitcherAuxInput = null; + } + if (_BMDSwitcher != null) { _BMDSwitcher.RemoveCallback(_switcherMonitor); @@ -522,6 +570,7 @@ public BMDSwitcherState ForceStateUpdate() // update state ForceStateUpdate_ProgramInput(); ForceStateUpdate_PreviewInput(); + ForceStateUpdate_AuxInput(); ForceStateUpdate_Transition(); ForceStateUpdate_USK1(); ForceStateUpdate_ChromaSettings(); @@ -533,7 +582,12 @@ public BMDSwitcherState ForceStateUpdate() return _state; } - + private void ForceStateUpdate_AuxInput() + { + long source; + _BMDSwitcherAuxInput.GetInputSource(out source); + _state.AuxID = source; + } private void ForceStateUpdate_USK1() { @@ -653,6 +707,7 @@ public void ConfigureSwitcher(BMDSwitcherConfigSettings config) { _config = config; ConfigureMixEffectBlock(); + ConfigureAux(); ConfigureCameraSources(); ConfigureDownstreamKeys(); ConfigureMultiviewer(); @@ -664,6 +719,11 @@ public void ConfigureSwitcher(BMDSwitcherConfigSettings config) ForceStateUpdate(); } + private void ConfigureAux() + { + _BMDSwitcherAuxInput.SetInputSource(_config.DefaultAuxSource); + } + private void ConfigureMixEffectBlock() { _BMDSwitcherMixEffectBlock1.SetFadeToBlackRate((uint)_config.MixEffectSettings.FTBRate); @@ -1249,5 +1309,14 @@ public void PerformSetKey1OffForNextTrans() { } + + public void PerformAuxSelect(int sourceID) + { + _parent.Dispatcher.Invoke(() => + { + _BMDSwitcherAuxInput?.SetInputSource(sourceID); + ForceStateUpdate(); + }); + } } } diff --git a/Integrated Presenter/BMDSwitcher/BMDSwitcherState.cs b/Integrated Presenter/BMDSwitcher/BMDSwitcherState.cs index 049bf2dc..7c5469cc 100644 --- a/Integrated Presenter/BMDSwitcher/BMDSwitcherState.cs +++ b/Integrated Presenter/BMDSwitcher/BMDSwitcherState.cs @@ -11,6 +11,7 @@ public class BMDSwitcherState public long PresetID { get; set; } public long ProgramID { get; set; } + public long AuxID { get; set; } public bool USK1OnAir { get; set; } public long USK1FillSource { get; set; } @@ -44,6 +45,7 @@ public void SetDefault() { PresetID = -1; ProgramID = -1; + AuxID = -1; DSK1OnAir = false; USK1OnAir = false; USK1KeyType = 1; @@ -115,6 +117,7 @@ public BMDSwitcherState Copy() { PresetID = this.PresetID, ProgramID = this.ProgramID, + AuxID = this.AuxID, USK1OnAir = this.USK1OnAir, USK1KeyType = this.USK1KeyType, USK1FillSource = this.USK1FillSource, diff --git a/Integrated Presenter/BMDSwitcher/BMDSwitcherVideoSources.cs b/Integrated Presenter/BMDSwitcher/BMDSwitcherVideoSources.cs index f2aed86d..9386eff9 100644 --- a/Integrated Presenter/BMDSwitcher/BMDSwitcherVideoSources.cs +++ b/Integrated Presenter/BMDSwitcher/BMDSwitcherVideoSources.cs @@ -36,7 +36,7 @@ public enum BMDSwitcherVideoSources DSK2Mask = 5020, SuperSource = 6000, CleanFeed1 = 7001, - CleenFeed2 = 7002, + CleanFeed2 = 7002, Auxillary1 = 8001, // Aux 800x Auxillary6 = 8006, diff --git a/Integrated Presenter/BMDSwitcher/Config/BMDSwitcherConfigSettings.cs b/Integrated Presenter/BMDSwitcher/Config/BMDSwitcherConfigSettings.cs index a428e1d9..661519c1 100644 --- a/Integrated Presenter/BMDSwitcher/Config/BMDSwitcherConfigSettings.cs +++ b/Integrated Presenter/BMDSwitcher/Config/BMDSwitcherConfigSettings.cs @@ -11,6 +11,8 @@ public class BMDSwitcherConfigSettings public List Routing { get; set; } = new List(); + public int DefaultAuxSource { get; set; } + public BMDDSKSettings DownstreamKey1Config { get; set; } = new BMDDSKSettings(); public BMDDSKSettings DownstreamKey2Config { get; set; } = new BMDDSKSettings(); diff --git a/Integrated Presenter/BMDSwitcher/IBMDSwitcherManager.cs b/Integrated Presenter/BMDSwitcher/IBMDSwitcherManager.cs index 50bda085..67c02e68 100644 --- a/Integrated Presenter/BMDSwitcher/IBMDSwitcherManager.cs +++ b/Integrated Presenter/BMDSwitcher/IBMDSwitcherManager.cs @@ -19,6 +19,7 @@ public interface IBMDSwitcherManager void PerformCutTransition(); void PerformPresetSelect(int sourceID); void PerformProgramSelect(int sourceID); + void PerformAuxSelect(int sourceID); void PerformTakeAutoDSK1(); void PerformTakeAutoDSK2(); void PerformTieDSK1(); diff --git a/Integrated Presenter/BMDSwitcher/Mock/MockBMDSwitcherManager.cs b/Integrated Presenter/BMDSwitcher/Mock/MockBMDSwitcherManager.cs index 0c7317d3..cb1158c4 100644 --- a/Integrated Presenter/BMDSwitcher/Mock/MockBMDSwitcherManager.cs +++ b/Integrated Presenter/BMDSwitcher/Mock/MockBMDSwitcherManager.cs @@ -24,14 +24,14 @@ public MockBMDSwitcherManager(MainWindow parent) _state.SetDefault(); Dictionary mapping = new Dictionary() { - [1] = "center", - [2] = "organ", - [3] = "cam3", + [1] = "cam1", + [2] = "pres", + [3] = "key", [4] = "slide", - [5] = "left", + [5] = "organ", [6] = "right", - [7] = "cam7", - [8] = "cam8" + [7] = "center", + [8] = "left" }; mockMultiviewer = new MockMultiviewer(mapping, parent.Config); parent.PresentationStateUpdated += Parent_PresentationStateUpdated; @@ -372,5 +372,11 @@ public void PerformSetKey1OffForNextTrans() mockMultiviewer.SetUSK1ForNextTrans(false, _state); SwitcherStateChanged?.Invoke(_state); } + + public void PerformAuxSelect(int sourceID) + { + _state.AuxID = sourceID; + SwitcherStateChanged?.Invoke(_state); + } } } diff --git a/Integrated Presenter/BMDSwitcher/Mock/MockMultiviewerWindow.xaml b/Integrated Presenter/BMDSwitcher/Mock/MockMultiviewerWindow.xaml index b6890aa2..f403b4b8 100644 --- a/Integrated Presenter/BMDSwitcher/Mock/MockMultiviewerWindow.xaml +++ b/Integrated Presenter/BMDSwitcher/Mock/MockMultiviewerWindow.xaml @@ -96,7 +96,7 @@ - + diff --git a/Integrated Presenter/BMDSwitcher/Mock/MockMultiviewerWindow.xaml.cs b/Integrated Presenter/BMDSwitcher/Mock/MockMultiviewerWindow.xaml.cs index 5bb7fee3..0f8b8cf9 100644 --- a/Integrated Presenter/BMDSwitcher/Mock/MockMultiviewerWindow.xaml.cs +++ b/Integrated Presenter/BMDSwitcher/Mock/MockMultiviewerWindow.xaml.cs @@ -65,6 +65,8 @@ private ImageSource InputSourceToImage(int inputID) return new BitmapImage(new Uri("pack://application:,,,/BMDSwitcher/Mock/Images/organshot.png")); case "slide": return ImgSlide.Source; + case "key": + return ImgKey.Source; case "colorbars": return new BitmapImage(new Uri("pack://application:,,,/BMDSwitcher/Mock/Images/cbars.png")); default: @@ -94,6 +96,7 @@ public void SetProgramSource(int inputID) public void UpdateAuxSource(Slide slide) { UpdateSourceFromAux(ImgSlide, slide); + UpdateSourceFromKey(ImgKey, slide); if (ProgramSource == 4) { UpdateSourceFromAux(ImgProgram, slide); @@ -103,6 +106,15 @@ public void UpdateAuxSource(Slide slide) UpdateSourceFromAux(ImgPreset, slide); } + if (ProgramSource == 3) + { + UpdateSourceFromAux(ImgProgram, slide); + } + if (PresetSource == 3) + { + UpdateSourceFromAux(ImgPreset, slide); + } + if (DSK1) { UpdateSourceFromAux(ImgProgramLowerThird, slide); @@ -113,6 +125,29 @@ public void UpdateAuxSource(Slide slide) } } + private void UpdateSourceFromKey(Image control, Slide slide) + { + if (slide.Type == SlideType.Empty) + { + control.Source = new BitmapImage(new Uri("pack://application:,,,/BMDSwitcher/Mock/Images/black.png")); + } + else if (slide.Type == SlideType.Action) + { + control.Source = new BitmapImage(new Uri("pack://application:,,,/BMDSwitcher/Mock/Images/black.png")); + } + else + { + if (slide.KeySource != null && slide.KeySource != "") + { + control.Source = new BitmapImage(new Uri(slide.KeySource)); + } + else + { + control.Source = new BitmapImage(new Uri("pack://application:,,,/BMDSwitcher/Mock/Images/black.png")); + } + } + } + private void UpdateSourceFromAux(Image control, Slide slide) { if (slide.Type == SlideType.Video) @@ -131,6 +166,10 @@ private void UpdateSourceFromAux(Image control, Slide slide) { control.Source = new BitmapImage(new Uri("pack://application:,,,/BMDSwitcher/Mock/Images/greenscreen1.png")); } + else if (slide.Type == SlideType.Action) + { + control.Source = new BitmapImage(new Uri("pack://application:,,,/BMDSwitcher/Mock/Images/black.png")); + } else { control.Source = new BitmapImage(new Uri(slide.Source)); diff --git a/Integrated Presenter/Icons/Mute2.png b/Integrated Presenter/Icons/Mute2.png new file mode 100644 index 00000000..6e3d989a Binary files /dev/null and b/Integrated Presenter/Icons/Mute2.png differ diff --git a/Integrated Presenter/Integrated Presenter.csproj b/Integrated Presenter/Integrated Presenter.csproj index e09b6009..e8349fe2 100644 --- a/Integrated Presenter/Integrated Presenter.csproj +++ b/Integrated Presenter/Integrated Presenter.csproj @@ -40,6 +40,8 @@ + + @@ -106,6 +108,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/Integrated Presenter/Keys/WhiteKey.png b/Integrated Presenter/Keys/WhiteKey.png new file mode 100644 index 00000000..7b751a98 Binary files /dev/null and b/Integrated Presenter/Keys/WhiteKey.png differ diff --git a/Integrated Presenter/MainWindow.xaml b/Integrated Presenter/MainWindow.xaml index 19730f87..330b3f89 100644 --- a/Integrated Presenter/MainWindow.xaml +++ b/Integrated Presenter/MainWindow.xaml @@ -42,7 +42,7 @@ - + @@ -82,17 +82,20 @@ - - + + - PROJECTOR + PROJECTOR - - - - - - + + + + + + + + + diff --git a/Integrated Presenter/MainWindow.xaml.cs b/Integrated Presenter/MainWindow.xaml.cs index 7887f4a3..b7fbf1d6 100644 --- a/Integrated Presenter/MainWindow.xaml.cs +++ b/Integrated Presenter/MainWindow.xaml.cs @@ -86,7 +86,7 @@ public MainWindow() HideAdvancedPresControls(); HideAdvancedPIPControls(); - HideAdvancedProjectorControls(); + HideAuxButtonConrols(); SlidePoolButtons = new List() { SlidePoolSource0, SlidePoolSource1, SlidePoolSource2, SlidePoolSource3 }; @@ -94,7 +94,7 @@ public MainWindow() UpdateSlideControls(); UpdateMediaControls(); UpdateSlideModeButtons(); - UpdateProjectorButtonStyles(); + DisableAuxControls(); UpdateProgramRowLockButtonUI(); UpdateRecordButtonUI(); @@ -106,6 +106,12 @@ public MainWindow() AfterPreview.AutoSilentReplay = true; PrevPreview.AutoSilentReplay = true; + NextPreview.ShowBlackForActions = false; + AfterPreview.ShowBlackForActions = false; + PrevPreview.ShowBlackForActions = false; + CurrentPreview.ShowBlackForActions = false; + CurrentPreview.ShowIfMute = true; + CurrentPreview.OnMediaPlaybackTimeUpdate += CurrentPreview_OnMediaPlaybackTimeUpdate; NextPreview.OnMediaLoaded += NextPreview_OnMediaLoaded; AfterPreview.OnMediaLoaded += AfterPreview_OnMediaLoaded; @@ -281,6 +287,11 @@ private void ClickPreset(int button) switcherManager?.PerformPresetSelect(ConvertButtonToSourceID(button)); } + private void ClickAux(int button) + { + switcherManager?.PerformAuxSelect(ConvertButtonToSourceID(button)); + } + private void ClickProgram(int button) { if (!IsProgramRowLocked) @@ -347,6 +358,7 @@ private void UpdateSwitcherUI() { UpdatePresetButtonStyles(); UpdateProgramButtonStyles(); + UpdateAuxButtonStyles(); UpdateTransButtonStyles(); UpdateUSK1Styles(); UpdateDSK1Styles(); @@ -413,6 +425,7 @@ private void EnableSwitcherControls() BtnTransKey1.Style = (Style)Application.Current.FindResource(style); EnableKeyerControls(); + EnableAuxButtons(); ShowKeyerUI(); } @@ -506,6 +519,20 @@ private void UpdateProgramButtonStyles() BtnProgram8.Background = (ConvertSourceIDToButton(switcherState.ProgramID) == 8 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; } + private void UpdateAuxButtonStyles() + { + BtnAux1.Background = (ConvertSourceIDToButton(switcherState.AuxID) == 1 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; + BtnAux2.Background = (ConvertSourceIDToButton(switcherState.AuxID) == 2 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; + BtnAux3.Background = (ConvertSourceIDToButton(switcherState.AuxID) == 3 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; + BtnAux4.Background = (ConvertSourceIDToButton(switcherState.AuxID) == 4 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; + BtnAux5.Background = (ConvertSourceIDToButton(switcherState.AuxID) == 5 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; + BtnAux6.Background = (ConvertSourceIDToButton(switcherState.AuxID) == 6 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; + BtnAux7.Background = (ConvertSourceIDToButton(switcherState.AuxID) == 7 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; + BtnAux8.Background = (ConvertSourceIDToButton(switcherState.AuxID) == 8 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; + BtnAuxPgm.Background = (ConvertSourceIDToButton(switcherState.AuxID) == 12 ? Application.Current.FindResource("RedLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; + } + + private void UpdateTransButtonStyles() { BtnBackgroundTrans.Background = (switcherState.TransNextBackground ? Application.Current.FindResource("YellowLight") : Application.Current.FindResource("GrayLight")) as RadialGradientBrush; @@ -799,6 +826,23 @@ private void WindowKeyDown(object sender, KeyEventArgs e) { ToggleViewAdvancedPresentation(); } + if (e.Key == Key.X) + { + ToggleAuxRow(); + } + + + // recording + + if (e.Key == Key.F5) + { + TryStartRecording(); + } + + if (e.Key == Key.F6) + { + TryStopRecording(); + } // audio @@ -828,6 +872,19 @@ private void WindowKeyDown(object sender, KeyEventArgs e) } } + if (e.Key == Key.M) + { + MediaMuted = !MediaMuted; + if (MediaMuted) + { + muteMedia(); + } + else + { + unmuteMedia(); + } + } + // D1-D8 + (LShift) #region program/preset bus if (e.Key == Key.D1) @@ -840,6 +897,8 @@ private void WindowKeyDown(object sender, KeyEventArgs e) TakeSlidePoolSlide(SlidePoolSource0.Slide, 0, false, SlidePoolSource0.Driven); else if (Keyboard.IsKeyDown(Key.R)) TakeSlidePoolSlide(SlidePoolSource0.Slide, 0, true, SlidePoolSource0.Driven); + else if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(1); else ClickPreset(1); } @@ -853,6 +912,8 @@ private void WindowKeyDown(object sender, KeyEventArgs e) TakeSlidePoolSlide(SlidePoolSource1.Slide, 1, false, SlidePoolSource1.Driven); else if (Keyboard.IsKeyDown(Key.R)) TakeSlidePoolSlide(SlidePoolSource1.Slide, 1, true, SlidePoolSource1.Driven); + else if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(2); else ClickPreset(2); } @@ -866,6 +927,8 @@ private void WindowKeyDown(object sender, KeyEventArgs e) TakeSlidePoolSlide(SlidePoolSource2.Slide, 2, false, SlidePoolSource2.Driven); else if (Keyboard.IsKeyDown(Key.R)) TakeSlidePoolSlide(SlidePoolSource2.Slide, 2, true, SlidePoolSource2.Driven); + else if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(3); else ClickPreset(3); } @@ -879,6 +942,8 @@ private void WindowKeyDown(object sender, KeyEventArgs e) TakeSlidePoolSlide(SlidePoolSource3.Slide, 3, false, SlidePoolSource3.Driven); else if (Keyboard.IsKeyDown(Key.R)) TakeSlidePoolSlide(SlidePoolSource3.Slide, 3, true, SlidePoolSource3.Driven); + else if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(4); else ClickPreset(4); } @@ -888,6 +953,8 @@ private void WindowKeyDown(object sender, KeyEventArgs e) ClickProgram(5); else if (Keyboard.IsKeyDown(Key.LeftCtrl)) ChangeUSK1FillSource(5); + else if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(5); else ClickPreset(5); } @@ -897,6 +964,8 @@ private void WindowKeyDown(object sender, KeyEventArgs e) ClickProgram(6); else if (Keyboard.IsKeyDown(Key.LeftCtrl)) ChangeUSK1FillSource(6); + else if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(6); else ClickPreset(6); } @@ -906,6 +975,8 @@ private void WindowKeyDown(object sender, KeyEventArgs e) ClickProgram(7); else if (Keyboard.IsKeyDown(Key.LeftCtrl)) ChangeUSK1FillSource(7); + else if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(7); else ClickPreset(7); } @@ -915,11 +986,29 @@ private void WindowKeyDown(object sender, KeyEventArgs e) ClickProgram(8); else if (Keyboard.IsKeyDown(Key.LeftCtrl)) ChangeUSK1FillSource(8); + else if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(8); else ClickPreset(8); } + if (e.Key == Key.D9) + { + if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(9); + } + if (e.Key == Key.D0) + { + if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(0); + } #endregion + if (e.Key == Key.OemTilde) + { + if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(12); + } + // arrow keys + (LCtrl) #region slide controls if (e.Key == Key.Left) @@ -1031,13 +1120,19 @@ private void WindowKeyDown(object sender, KeyEventArgs e) // fade to black if (e.Key == Key.B) { - switcherManager?.PerformToggleFTB(); + if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(10); + else + switcherManager?.PerformToggleFTB(); } // color bars if (e.Key == Key.C) { - SetProgramColorBars(); + if (Keyboard.IsKeyDown(Key.Z)) + ClickAux(11); + else + SetProgramColorBars(); } // transition controls @@ -1219,11 +1314,356 @@ private void ClickProgram1(object sender, RoutedEventArgs e) #region SlideDriveVideo + private bool SetupActionsCompleted = false; + private bool ActionsCompleted = false; + + private Guid currentslideforactions; + + private async Task ExecuteSetupActions(Slide s) + { + Dispatcher.Invoke(() => + { + SetupActionsCompleted = false; + CurrentPreview.SetupComplete(false); + }); + await Task.Run(async () => + { + foreach (var task in s.SetupActions) + { + await PerformAutomationAction(task); + } + }); + Dispatcher.Invoke(() => + { + SetupActionsCompleted = true; + CurrentPreview.SetupComplete(true); + }); + } + + private async Task ExecuteActionSlide(Slide s) + { + Dispatcher.Invoke(() => + { + ActionsCompleted = false; + CurrentPreview.ActionComplete(false); + }); + await Task.Run(async () => + { + foreach (var task in s.Actions) + { + await PerformAutomationAction(task); + } + }); + Dispatcher.Invoke(() => + { + ActionsCompleted = true; + CurrentPreview.ActionComplete(true); + }); + } + + private async Task PerformAutomationAction(AutomationAction task) + { + await Task.Run(async () => + { + switch (task.Action) + { + case AutomationActionType.PresetSelect: + Dispatcher.Invoke(() => + { + switcherManager?.PerformPresetSelect(task.DataI); + }); + break; + case AutomationActionType.ProgramSelect: + Dispatcher.Invoke(() => + { + switcherManager?.PerformProgramSelect(task.DataI); + }); + break; + case AutomationActionType.AuxSelect: + Dispatcher.Invoke(() => + { + switcherManager?.PerformAuxSelect(task.DataI); + }); + break; + case AutomationActionType.AutoTrans: + Dispatcher.Invoke(() => + { + switcherManager?.PerformAutoTransition(); + }); + break; + case AutomationActionType.CutTrans: + Dispatcher.Invoke(() => + { + switcherManager?.PerformCutTransition(); + }); + break; + case AutomationActionType.AutoTakePresetIfOnSlide: + // Take Preset if program source is fed from slides + if (switcherState.ProgramID == _config.Routing.Where(r => r.KeyName == "slide").First().PhysicalInputId) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformAutoTransition(); + }); + await Task.Delay((_config.MixEffectSettings.Rate / _config.VideoSettings.VideoFPS) * 1000); + } + break; + case AutomationActionType.DSK1On: + if (!switcherState.DSK1OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformToggleDSK1(); + }); + } + break; + case AutomationActionType.DSK1Off: + if (switcherState.DSK1OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformToggleDSK1(); + }); + } + break; + case AutomationActionType.DSK1FadeOn: + if (!switcherState.DSK1OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformAutoOnAirDSK1(); + }); + } + break; + case AutomationActionType.DSK1FadeOff: + if (switcherState.DSK1OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformAutoOffAirDSK1(); + }); + } + break; + case AutomationActionType.DSK2On: + if (!switcherState.DSK2OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformToggleDSK2(); + }); + } + break; + case AutomationActionType.DSK2Off: + if (switcherState.DSK2OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformToggleDSK2(); + }); + } + break; + case AutomationActionType.DSK2FadeOn: + if (!switcherState.DSK2OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformAutoOnAirDSK2(); + }); + } + break; + case AutomationActionType.DSK2FadeOff: + if (switcherState.DSK2OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformAutoOffAirDSK2(); + }); + } + break; + case AutomationActionType.RecordStart: + Dispatcher.Invoke(() => + { + TryStartRecording(); + }); + break; + case AutomationActionType.RecordStop: + Dispatcher.Invoke(() => + { + TryStopRecording(); + }); + break; + + case AutomationActionType.Timer1Restart: + if (automationtimer1enabled) + { + Dispatcher.Invoke(() => + { + timer1span = TimeSpan.Zero; + }); + } + break; + + case AutomationActionType.USK1On: + if (!switcherState.USK1OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformOnAirUSK1(); + }); + } + break; + case AutomationActionType.USK1Off: + if (switcherState.USK1OnAir) + { + Dispatcher.Invoke(() => + { + switcherManager?.PerformOffAirUSK1(); + }); + } + break; + case AutomationActionType.USK1SetTypeChroma: + Dispatcher.Invoke(() => + { + switcherManager?.SetUSK1TypeChroma(); + }); + break; + case AutomationActionType.USK1SetTypeDVE: + Dispatcher.Invoke(() => + { + switcherManager?.SetUSK1TypeDVE(); + }); + break; + + case AutomationActionType.OpenAudioPlayer: + Dispatcher.Invoke(() => + { + OpenAudioPlayer(); + Focus(); + }); + break; + case AutomationActionType.LoadAudio: + string filename = Path.Join(Presentation.Folder, task.DataS); + Dispatcher.Invoke(() => + { + audioPlayer.OpenAudio(filename); + }); + break; + case AutomationActionType.PlayAuxAudio: + Dispatcher.Invoke(() => + { + audioPlayer.PlayAudio(); + }); + break; + case AutomationActionType.StopAuxAudio: + Dispatcher.Invoke(() => + { + audioPlayer.StopAudio(); + }); + break; + case AutomationActionType.PauseAuxAudio: + Dispatcher.Invoke(() => + { + audioPlayer.PauseAudio(); + }); + break; + case AutomationActionType.ReplayAuxAudio: + Dispatcher.Invoke(() => + { + audioPlayer.RestartAudio(); + }); + break; + + case AutomationActionType.PlayMedia: + Dispatcher.Invoke(() => + { + playMedia(); + }); + break; + case AutomationActionType.PauseMedia: + Dispatcher.Invoke(() => + { + pauseMedia(); + }); + break; + case AutomationActionType.StopMedia: + Dispatcher.Invoke(() => + { + stopMedia(); + }); + break; + case AutomationActionType.RestartMedia: + Dispatcher.Invoke(() => + { + restartMedia(); + }); + break; + case AutomationActionType.MuteMedia: + Dispatcher.Invoke(() => + { + muteMedia(); + }); + break; + case AutomationActionType.UnMuteMedia: + Dispatcher.Invoke(() => + { + unmuteMedia(); + }); + break; + + + case AutomationActionType.DelayMs: + await Task.Delay(task.DataI); + break; + case AutomationActionType.None: + break; + default: + break; + } + }); + } + + private bool MediaMuted = false; + + private void unmuteMedia() + { + MediaMuted = false; + CurrentPreview.MarkUnMuted(); + _display?.UnMuteMedia(); + } + + private void muteMedia() + { + MediaMuted = true; + CurrentPreview.MarkMuted(); + _display?.MuteMedia(); + } + private async void SlideDriveVideo_Next() { if (Presentation?.Next != null) { - if (Presentation.Next.Type == SlideType.Liturgy) + if (Presentation.Next.Type == SlideType.Action) + { + SetupActionsCompleted = false; + ActionsCompleted = false; + // run stetup actions + await ExecuteSetupActions(Presentation.Next); + + if (Presentation.Next.AutoOnly) + { + // for now we won't support running 2 back to back fullauto slides. + // There really shouldn't be any need. + // We also cant run a script's setup actions immediatley afterward. + // again it shouldn't be nessecary, since in both cases you can add it to the fullauto slide's setup actions + Presentation.NextSlide(); + } + // Perform slide actions + Presentation.NextSlide(); + slidesUpdated(); + PresentationStateUpdated?.Invoke(Presentation.EffectiveCurrent); + await ExecuteActionSlide(Presentation.EffectiveCurrent); + } + else if (Presentation.Next.Type == SlideType.Liturgy) { // turn of usk1 if chroma keyer if (switcherState.USK1OnAir && switcherState.USK1KeyType == 2) @@ -1354,7 +1794,18 @@ private async void SlideDriveVideo_ToSlide(Slide s) { if (s != null && Presentation != null) { - if (s.Type == SlideType.Liturgy) + if (s.Type == SlideType.Action) + { + // Run Setup Actions + await ExecuteSetupActions(s); + // Execute Slide Actions + Presentation.Override = s; + Presentation.OverridePres = true; + slidesUpdated(); + PresentationStateUpdated?.Invoke(Presentation.EffectiveCurrent); + await ExecuteActionSlide(s); + } + else if (s.Type == SlideType.Liturgy) { // turn of usk1 if chroma keyer if (switcherState.USK1OnAir && switcherState.USK1KeyType == 2) @@ -1462,7 +1913,7 @@ private async void SlideDriveVideo_ToSlide(Slide s) private void SlideDriveVideo_Action(Slide s) { - switch (s.Action) + switch (s.PreAction) { case "t1restart": if (automationtimer1enabled) @@ -1493,7 +1944,16 @@ private async void SlideDriveVideo_Current() { DisableSlidePoolOverrides(); currentpoolsource = null; - if (Presentation.EffectiveCurrent.Type == SlideType.Liturgy) + if (Presentation.EffectiveCurrent.Type == SlideType.Action) + { + // Re-run setup actions + await ExecuteSetupActions(Presentation.EffectiveCurrent); + slidesUpdated(); + PresentationStateUpdated?.Invoke(Presentation.EffectiveCurrent); + // Run Actions + await ExecuteActionSlide(Presentation.EffectiveCurrent); + } + else if (Presentation.EffectiveCurrent.Type == SlideType.Liturgy) { // turn of usk1 if chroma keyer if (switcherState.USK1OnAir && switcherState.USK1KeyType == 2) @@ -1603,6 +2063,7 @@ private async void SlideDriveVideo_Current() public event PresentationStateUpdate PresentationStateUpdated; PresenterDisplay _display; + PresenterDisplay _keydisplay; private void OpenPresentation(object sender, RoutedEventArgs e) { @@ -1624,11 +2085,19 @@ private void OpenPresentation(object sender, RoutedEventArgs e) } else { - _display = new PresenterDisplay(this); + _display = new PresenterDisplay(this, false); _display.OnMediaPlaybackTimeUpdated += _display_OnMediaPlaybackTimeUpdated; // start display _display.Show(); } + + if (_keydisplay == null && !(_keydisplay?.IsWindowVisilbe ?? false)) + { + _keydisplay = new PresenterDisplay(this, true); + // no need to get playback event info + _keydisplay.Show(); + } + DisableSlidePoolOverrides(); slidesUpdated(); @@ -1662,16 +2131,26 @@ private void ClickToggleShowEffectiveCurrentPreview(object sender, RoutedEventAr private void slidesUpdated() { - _display?.ShowSlide(); + _display?.ShowSlide(false); + _keydisplay?.ShowSlide(true); + + // mark update for slides + if (currentslideforactions != currentGuid) + { + // new slide - mark changes + SetupActionsCompleted = false; + ActionsCompleted = false; + } + // update previews if (Presentation != null) { - PrevPreview.SetMedia(Presentation.Prev); + PrevPreview.SetMedia(Presentation.Prev, false); if (ShowEffectiveCurrentPreview) { if (currentGuid != Presentation.EffectiveCurrent.Guid) { - CurrentPreview.SetMedia(Presentation.EffectiveCurrent); + CurrentPreview.SetMedia(Presentation.EffectiveCurrent, false); currentGuid = Presentation.EffectiveCurrent.Guid; } } @@ -1679,14 +2158,19 @@ private void slidesUpdated() { if (currentGuid != Presentation.Current.Guid) { - CurrentPreview.SetMedia(Presentation.Current); + CurrentPreview.SetMedia(Presentation.Current, false); currentGuid = Presentation.Current.Guid; } } - NextPreview.SetMedia(Presentation.Next); - AfterPreview.SetMedia(Presentation.After); + NextPreview.SetupComplete(SetupActionsCompleted); + NextPreview.ActionComplete(false); + CurrentPreview.ActionComplete(ActionsCompleted); + CurrentPreview.SetupComplete(true); + NextPreview.SetMedia(Presentation.Next, false); + AfterPreview.SetMedia(Presentation.After, false); } UpdateSlidePreviewControls(); + currentslideforactions = currentGuid; } @@ -1759,6 +2243,7 @@ private void playMedia() Dispatcher.Invoke(() => { _display.StartMediaPlayback(); + _keydisplay.StartMediaPlayback(); if (!Presentation.OverridePres || ShowEffectiveCurrentPreview) { CurrentPreview.videoPlayer.Volume = 0; @@ -1776,6 +2261,7 @@ private void pauseMedia() if (activepresentation) { _display.PauseMediaPlayback(); + _keydisplay.PauseMediaPlayback(); if (!Presentation.OverridePres || ShowEffectiveCurrentPreview) { CurrentPreview.videoPlayer.Volume = 0; @@ -1792,6 +2278,7 @@ private void stopMedia() if (activepresentation) { _display.StopMediaPlayback(); + _keydisplay.StopMediaPlayback(); if (!Presentation.OverridePres || ShowEffectiveCurrentPreview) { CurrentPreview.videoPlayer.Volume = 0; @@ -1808,6 +2295,7 @@ private void restartMedia() if (activepresentation) { _display.RestartMediaPlayback(); + _keydisplay.RestartMediaPlayback(); if (!Presentation.OverridePres || ShowEffectiveCurrentPreview) { CurrentPreview.videoPlayer.Volume = 0; @@ -2006,7 +2494,7 @@ private void TakeSlidePoolSlide(Slide s, int num, bool replaceMode, bool driven) } } - SlidePoolButtons[1].Selected = true; + SlidePoolButtons[num].Selected = true; currentpoolsource = SlidePoolButtons[num]; @@ -2040,6 +2528,7 @@ private void DisableSlidePoolOverrides() private void OnClosing(object sender, CancelEventArgs e) { _display?.Close(); + _keydisplay?.Close(); switcherManager?.Close(); hyperDeckMonitorWindow?.Close(); audioPlayer?.Close(); @@ -2260,18 +2749,27 @@ private void UpdateUIButtonLabels() case 8: UpdateButton8Labels(btn); break; + case 12: + UpdateButtonPgmLabels(btn); + break; default: break; } } } + private void UpdateButtonPgmLabels(ButtonSourceMapping config) + { + BtnAuxPgm.Content = config.ButtonName; + } + private void UpdateButton1Labels(ButtonSourceMapping config) { BtnPreset1.Content = config.ButtonName; BtnProgram1.Content = config.ButtonName; BtnPIPFillProgram1.Content = config.ButtonName; BtnChromaFillProgram1.Content = config.ButtonName; + BtnAux1.Content = config.ButtonName; } private void UpdateButton2Labels(ButtonSourceMapping config) @@ -2280,6 +2778,7 @@ private void UpdateButton2Labels(ButtonSourceMapping config) BtnProgram2.Content = config.ButtonName; BtnPIPFillProgram2.Content = config.ButtonName; BtnChromaFillProgram2.Content = config.ButtonName; + BtnAux2.Content = config.ButtonName; } private void UpdateButton3Labels(ButtonSourceMapping config) { @@ -2287,6 +2786,7 @@ private void UpdateButton3Labels(ButtonSourceMapping config) BtnProgram3.Content = config.ButtonName; BtnPIPFillProgram3.Content = config.ButtonName; BtnChromaFillProgram3.Content = config.ButtonName; + BtnAux3.Content = config.ButtonName; } private void UpdateButton4Labels(ButtonSourceMapping config) { @@ -2294,6 +2794,7 @@ private void UpdateButton4Labels(ButtonSourceMapping config) BtnProgram4.Content = config.ButtonName; BtnPIPFillProgram4.Content = config.ButtonName; BtnChromaFillProgram4.Content = config.ButtonName; + BtnAux4.Content = config.ButtonName; } private void UpdateButton5Labels(ButtonSourceMapping config) @@ -2302,6 +2803,7 @@ private void UpdateButton5Labels(ButtonSourceMapping config) BtnProgram5.Content = config.ButtonName; BtnPIPFillProgram5.Content = config.ButtonName; BtnChromaFillProgram5.Content = config.ButtonName; + BtnAux5.Content = config.ButtonName; } private void UpdateButton6Labels(ButtonSourceMapping config) { @@ -2309,6 +2811,7 @@ private void UpdateButton6Labels(ButtonSourceMapping config) BtnProgram6.Content = config.ButtonName; BtnPIPFillProgram6.Content = config.ButtonName; BtnChromaFillProgram6.Content = config.ButtonName; + BtnAux6.Content = config.ButtonName; } private void UpdateButton7Labels(ButtonSourceMapping config) { @@ -2316,6 +2819,7 @@ private void UpdateButton7Labels(ButtonSourceMapping config) BtnProgram7.Content = config.ButtonName; BtnPIPFillProgram7.Content = config.ButtonName; BtnChromaFillProgram7.Content = config.ButtonName; + BtnAux7.Content = config.ButtonName; } private void UpdateButton8Labels(ButtonSourceMapping config) { @@ -2323,6 +2827,7 @@ private void UpdateButton8Labels(ButtonSourceMapping config) BtnProgram8.Content = config.ButtonName; BtnPIPFillProgram8.Content = config.ButtonName; BtnChromaFillProgram8.Content = config.ButtonName; + BtnAux8.Content = config.ButtonName; } @@ -2341,15 +2846,22 @@ private void SetDefaultConfig() ProgramOutGain = 2, XLRInputGain = 6, }, + DefaultAuxSource = (int)BMDSwitcherVideoSources.ME1Prog, Routing = new List() { - new ButtonSourceMapping() { KeyName = "left", ButtonId = 1, ButtonName = "PULPIT", PhysicalInputId = 5, LongName = "PULPIT", ShortName = "PLPT" }, - new ButtonSourceMapping() { KeyName = "center", ButtonId = 2, ButtonName = "CENTER", PhysicalInputId = 1, LongName = "CENTER", ShortName = "CNTR" }, + new ButtonSourceMapping() { KeyName = "left", ButtonId = 1, ButtonName = "PULPIT", PhysicalInputId = 8, LongName = "PULPIT", ShortName = "PLPT" }, + new ButtonSourceMapping() { KeyName = "center", ButtonId = 2, ButtonName = "CENTER", PhysicalInputId = 7, LongName = "CENTER", ShortName = "CNTR" }, new ButtonSourceMapping() { KeyName = "right", ButtonId = 3, ButtonName = "LECTERN", PhysicalInputId = 6, LongName = "LECTERN", ShortName = "LTRN" }, - new ButtonSourceMapping() { KeyName = "organ", ButtonId = 4, ButtonName = "ORGAN", PhysicalInputId = 2, LongName = "ORGAN", ShortName = "ORGN" }, + new ButtonSourceMapping() { KeyName = "organ", ButtonId = 4, ButtonName = "ORGAN", PhysicalInputId = 5, LongName = "ORGAN", ShortName = "ORGN" }, new ButtonSourceMapping() { KeyName = "slide", ButtonId = 5, ButtonName = "SLIDE", PhysicalInputId = 4, LongName = "SLIDESHOW", ShortName = "SLDE" }, - new ButtonSourceMapping() { KeyName = "c3", ButtonId = 6, ButtonName = "CAM3", PhysicalInputId = 3, LongName = "CAMERA 3", ShortName = "CAM3" }, - new ButtonSourceMapping() { KeyName = "c7", ButtonId = 7, ButtonName = "CAM7", PhysicalInputId = 7, LongName = "CAMERA 7", ShortName = "CAM7" }, - new ButtonSourceMapping() { KeyName = "c8", ButtonId = 8, ButtonName = "CAM8", PhysicalInputId = 8, LongName = "CAMERA 8", ShortName = "CAM8" }, + new ButtonSourceMapping() { KeyName = "key", ButtonId = 6, ButtonName = "AKEY", PhysicalInputId = 3, LongName = "ALPHA KEY", ShortName = "AKEY" }, + new ButtonSourceMapping() { KeyName = "proj", ButtonId = 7, ButtonName = "PROJ", PhysicalInputId = 2, LongName = "PROJECTOR", ShortName = "PROJ" }, + new ButtonSourceMapping() { KeyName = "c1", ButtonId = 8, ButtonName = "CAM1", PhysicalInputId = 1, LongName = "HDMI 1", ShortName = "CAM1" }, + new ButtonSourceMapping() { KeyName = "cf1", ButtonId = 9, ButtonName = "CLF1", PhysicalInputId = (int)BMDSwitcherVideoSources.CleanFeed1, LongName = "CLEAN FEED 1", ShortName = "CLF1" }, + new ButtonSourceMapping() { KeyName = "cf2", ButtonId = 0, ButtonName = "CLF2", PhysicalInputId = (int)BMDSwitcherVideoSources.CleanFeed2, LongName = "CLEAN FEED 2", ShortName = "CLF2" }, + new ButtonSourceMapping() { KeyName = "black", ButtonId = 10, ButtonName = "BLACK", PhysicalInputId = (int)BMDSwitcherVideoSources.Black, LongName = "BLACK", ShortName = "BLK" }, + new ButtonSourceMapping() { KeyName = "cbar", ButtonId = 11, ButtonName = "CBAR", PhysicalInputId = (int)BMDSwitcherVideoSources.ColorBars, LongName = "COLOR BARS", ShortName = "CBAR" }, + new ButtonSourceMapping() { KeyName = "program", ButtonId = 12, ButtonName = "PRGM", PhysicalInputId = (int)BMDSwitcherVideoSources.ME1Prog, LongName = "PROGRAM", ShortName = "PRGM" }, + new ButtonSourceMapping() { KeyName = "preview", ButtonId = 13, ButtonName = "PREV", PhysicalInputId = (int)BMDSwitcherVideoSources.ME1Prev, LongName = "PREVIEW", ShortName = "PREV" }, }, MixEffectSettings = new BMDMixEffectSettings() { @@ -2359,25 +2871,25 @@ private void SetDefaultConfig() MultiviewerConfig = new BMDMultiviewerSettings() { Layout = (int)_BMDSwitcherMultiViewLayout.bmdSwitcherMultiViewLayoutProgramTop, // 12 - Window2 = 5, - Window3 = 1, + Window2 = 8, + Window3 = 7, Window4 = 6, - Window5 = 2, + Window5 = 5, Window6 = 4, Window7 = 3, - Window8 = 7, - Window9 = 8 + Window8 = 2, + Window9 = 1 }, DownstreamKey1Config = new BMDDSKSettings() { InputFill = 4, - InputCut = 0, - Clip = 0.3, - Gain = 0.06, + InputCut = 3, + Clip = 0.5, + Gain = 0.35, Rate = 30, - Invert = 1, + Invert = 0, IsPremultipled = 0, - IsMasked = 1, + IsMasked = 0, MaskTop = -5.5f, MaskBottom = -9, MaskLeft = -16, @@ -2457,92 +2969,105 @@ private void SetDefaultConfig() public BMDSwitcherConfigSettings Config { get => _config; } - bool showAdvancedProjector = false; - private void ClickViewAdvancedProjector(object sender, RoutedEventArgs e) + bool showAuxButons = false; + private void ClickViewAuxOutput(object sender, RoutedEventArgs e) + { + ToggleAuxRow(); + } + + private void ToggleAuxRow() { - showAdvancedProjector = !showAdvancedProjector; - if (showAdvancedProjector) + showAuxButons = !showAuxButons; + if (showAuxButons) { - ShowAdvancedProjectorControls(); + ShowAuxButtonControls(); } else { - HideAdvancedProjectorControls(); + HideAuxButtonConrols(); } } - private void ClickProjector1(object sender, RoutedEventArgs e) + private void ClickAux1(object sender, RoutedEventArgs e) { - ClickProjectorButton(1); + ClickAux(1); } - private void ClickProjector2(object sender, RoutedEventArgs e) + private void ClickAux2(object sender, RoutedEventArgs e) { - ClickProjectorButton(2); + ClickAux(2); } - private void ClickProjector3(object sender, RoutedEventArgs e) + private void ClickAux3(object sender, RoutedEventArgs e) { - ClickProjectorButton(3); + ClickAux(3); } - private void ClickProjector4(object sender, RoutedEventArgs e) + private void ClickAux4(object sender, RoutedEventArgs e) { - ClickProjectorButton(4); + ClickAux(4); } - private void ClickProjector5(object sender, RoutedEventArgs e) + private void ClickAux5(object sender, RoutedEventArgs e) { - ClickProjectorButton(5); + ClickAux(5); } - private void ClickProjector6(object sender, RoutedEventArgs e) + private void ClickAux6(object sender, RoutedEventArgs e) { - ClickProjectorButton(6); + ClickAux(6); } - - private void ClickProjectorButton(int btn) + private void ClickAux7(object sender, RoutedEventArgs e) { - if (projectorSerialPort.IsOpen) - { - projectorSerialPort.Write(btn.ToString()); - } + ClickAux(7); } - - private void UpdateProjectorButtonNames() + private void ClickAux8(object sender, RoutedEventArgs e) { + ClickAux(8); + } + private void ClickAuxPgm(object sender, RoutedEventArgs e) + { + ClickAux(12); + } + + private void EnableAuxButtons() + { + BtnAux1.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnAux2.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnAux3.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnAux4.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnAux5.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnAux6.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnAux7.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnAux8.Style = Application.Current.FindResource("SwitcherButton") as Style; + BtnAuxPgm.Style = Application.Current.FindResource("SwitcherButton") as Style; } - private void UpdateProjectorButtonStyles() + private void DisableAuxControls() { - if (projectorconnected) - { - BtnProjector1.Style = Application.Current.FindResource("SwitcherButton") as Style; - BtnProjector2.Style = Application.Current.FindResource("SwitcherButton") as Style; - BtnProjector3.Style = Application.Current.FindResource("SwitcherButton") as Style; - BtnProjector4.Style = Application.Current.FindResource("SwitcherButton") as Style; - BtnProjector5.Style = Application.Current.FindResource("SwitcherButton") as Style; - BtnProjector6.Style = Application.Current.FindResource("SwitcherButton") as Style; - return; - } - BtnProjector1.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; - BtnProjector2.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; - BtnProjector3.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; - BtnProjector4.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; - BtnProjector5.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; - BtnProjector6.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; + BtnAux1.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; + BtnAux2.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; + BtnAux3.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; + BtnAux4.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; + BtnAux5.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; + BtnAux6.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; + BtnAux7.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; + BtnAux8.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; + BtnAuxPgm.Style = Application.Current.FindResource("SwitcherButton_Disabled") as Style; } - private void ShowAdvancedProjectorControls() + private void ShowAuxButtonControls() { + showAuxButons = true; gridbtns.Width = 770; gcAdvancedProjector.Width = new GridLength(1.2, GridUnitType.Star); } - private void HideAdvancedProjectorControls() + private void HideAuxButtonConrols() { + showAuxButons = false; gridbtns.Width = 660; gcAdvancedProjector.Width = new GridLength(0); } @@ -2576,7 +3101,6 @@ private void ConnectProjector() } projectorconnected = true; } - UpdateProjectorButtonStyles(); } private void ClickConnectProjector(object sender, RoutedEventArgs e) @@ -2735,6 +3259,26 @@ private void ClickToggleRecording(object sender, RoutedEventArgs e) } } + private void TryStartRecording() + { + if (mHyperdeckManager != null && mHyperdeckManager.IsConnected) + { + isRecording = true; + mHyperdeckManager?.StartRecording(); + } + UpdateRecordButtonUI(); + } + + private void TryStopRecording() + { + if (mHyperdeckManager != null && mHyperdeckManager.IsConnected) + { + isRecording = false; + mHyperdeckManager?.StopRecording(); + } + UpdateRecordButtonUI(); + } + bool automationtimer1enabled = true; bool automationrecordstartenabled = true; diff --git a/Integrated Presenter/MediaPlayer2.xaml b/Integrated Presenter/MediaPlayer2.xaml index 270746c5..e73f1781 100644 --- a/Integrated Presenter/MediaPlayer2.xaml +++ b/Integrated Presenter/MediaPlayer2.xaml @@ -7,8 +7,26 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - - - + + + + + + + + + + + + + + + + + SEQ + + Automated Slide + + diff --git a/Integrated Presenter/MediaPlayer2.xaml.cs b/Integrated Presenter/MediaPlayer2.xaml.cs index 74ee30d9..f2308d7e 100644 --- a/Integrated Presenter/MediaPlayer2.xaml.cs +++ b/Integrated Presenter/MediaPlayer2.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Timers; using System.Windows; @@ -38,18 +39,47 @@ public partial class MediaPlayer2 : UserControl public MediaPlayer2() { InitializeComponent(); + ShowBlackSource(); _playbacktimer = new Timer(500); _playbacktimer.Elapsed += _playbacktimer_Elapsed; videoPlayer.MediaOpened += VideoPlayer_MediaOpened; videoPlayer.MediaEnded += VideoPlayer_MediaEnded; + MuteIcon.Visibility = Visibility.Hidden; + } + + private bool showingmute = false; + + public void MarkMuted() + { + showingmute = true; + if (ShowIfMute) + { + MuteIcon.Visibility = Visibility.Visible; + } + } + + public void MarkUnMuted() + { + showingmute = false; + MuteIcon.Visibility = Visibility.Hidden; + } + + private void Mute() + { + videoPlayer.Volume = 0; + } + + private void UnMute() + { + videoPlayer.Volume = 1; } private void VideoPlayer_MediaEnded(object sender, RoutedEventArgs e) { if (AutoSilentReplay) { - videoPlayer.Volume = 0; + Mute(); ReplayMedia(); } } @@ -57,11 +87,15 @@ private void VideoPlayer_MediaEnded(object sender, RoutedEventArgs e) public bool AutoSilentReplay { get; set; } = false; public bool AutoSilentPlayback { get; set; } = false; + public bool ShowIfMute { get; set; } = false; + + public bool ShowBlackForActions { get; set; } = true; + private void VideoPlayer_MediaOpened(object sender, RoutedEventArgs e) { if (AutoSilentPlayback) { - videoPlayer.Volume = 0; + Mute(); PlayMedia(); } OnMediaLoaded?.Invoke(this, new MediaPlaybackTimeEventArgs(videoPlayer.Position, videoPlayer.NaturalDuration.TimeSpan, (videoPlayer.NaturalDuration - videoPlayer.Position).TimeSpan)); @@ -196,11 +230,29 @@ public void SetMedia(Uri source, SlideType type) } } - public void SetMedia(Slide slide) + public void SetMedia(Slide slide, bool asKey) { - if (slide.Source != string.Empty) + if (slide.Type == SlideType.Action) { - SetMedia(new Uri(slide.Source), slide.Type); + ShowActionCommands(slide); + } + else if (slide.Source != string.Empty) + { + if (asKey) + { + if (slide.KeySource != null && slide.KeySource != "") + { + SetMedia(new Uri(slide.KeySource), slide.Type); + } + else + { + ShowBlackSource(); + } + } + else + { + SetMedia(new Uri(slide.Source), slide.Type); + } } else { @@ -208,8 +260,80 @@ public void SetMedia(Slide slide) } } + public void SetupComplete(bool complete = true) + { + if (complete) + { + SetupMessages.Foreground = Brushes.Gray; + } + else + { + SetupMessages.Foreground = Brushes.LightGreen; + } + } + + public void ActionComplete(bool complete = true) + { + if (complete) + { + MainMessages.Foreground = Brushes.White; + } + else + { + MainMessages.Foreground = Brushes.Orange; + } + } + + private void ShowActionCommands(Slide slide) + { + ShowBlackSource(); + + if (!ShowBlackForActions) + { + // Show Commands + string description = ""; + foreach (var action in slide.Actions) + { + if (action?.Message != "") + { + description += action.Message + Environment.NewLine; + } + } + MainMessages.Text = description.Trim(); + MainMessages.Visibility = Visibility.Visible; + + description = ""; + foreach (var action in slide.SetupActions) + { + if (action?.Message != "") + { + description += action.Message + Environment.NewLine; + } + } + SetupMessages.Text = description.Trim(); + SetupMessages.Visibility = Visibility.Visible; + + SetupMessages.Foreground = Brushes.LightGreen; + MainMessages.Foreground = Brushes.Orange; + + + SequenceLabel.Text = slide.Title; + + SeqType.Text = slide.AutoOnly ? "AUTO" : "SEQ"; + + SequenceLabel.Visibility = Visibility.Visible; + ActionIndicator.Visibility = Visibility.Visible; + SeqType.Visibility = Visibility.Visible; + } + } + private void ShowImage() { + SequenceLabel.Visibility = Visibility.Hidden; + SeqType.Visibility = Visibility.Hidden; + ActionIndicator.Visibility = Visibility.Hidden; + MainMessages.Visibility = Visibility.Hidden; + SetupMessages.Visibility = Visibility.Hidden; BlackSource.Visibility = Visibility.Hidden; _playbacktimer.Stop(); videoPlayer.Stop(); @@ -228,6 +352,11 @@ private void ShowImage() private void ShowVideo() { + SequenceLabel.Visibility = Visibility.Hidden; + SeqType.Visibility = Visibility.Hidden; + ActionIndicator.Visibility = Visibility.Hidden; + MainMessages.Visibility = Visibility.Hidden; + SetupMessages.Visibility = Visibility.Hidden; BlackSource.Visibility = Visibility.Hidden; _playbacktimer.Start(); imagePlayer.Visibility = Visibility.Hidden; @@ -248,6 +377,13 @@ private void ShowVideo() public void ShowBlackSource() { BlackSource.Visibility = Visibility.Visible; + imagePlayer.Visibility = Visibility.Hidden; + videoPlayer.Visibility = Visibility.Hidden; + SequenceLabel.Visibility = Visibility.Hidden; + SeqType.Visibility = Visibility.Hidden; + ActionIndicator.Visibility = Visibility.Hidden; + MainMessages.Visibility = Visibility.Hidden; + SetupMessages.Visibility = Visibility.Hidden; } } diff --git a/Integrated Presenter/Presentation.cs b/Integrated Presenter/Presentation.cs index c5db1bcf..6e894216 100644 --- a/Integrated Presenter/Presentation.cs +++ b/Integrated Presenter/Presentation.cs @@ -22,12 +22,21 @@ public bool Create(string folder) Folder = folder; // get all files in directory and hope they are slides - var files = Directory.GetFiles(Folder).OrderBy(p => Convert.ToInt32(Regex.Match(Path.GetFileName(p), "(?\\d+).*").Groups["slidenum"].Value)).ToList(); + var files = Directory.GetFiles(Folder).Where(f => Regex.Match(f, "\\d+_.*").Success).OrderBy(p => Convert.ToInt32(Regex.Match(Path.GetFileName(p), "(?\\d+).*").Groups["slidenum"].Value)).ToList(); foreach (var file in files) { - var filename = Regex.Match(Path.GetFileName(file), "\\d+_(?[^-]*)-?(?.*)\\..*"); + var filename = Regex.Match(Path.GetFileName(file), "\\d+_(?[^-]*)-?(?.*)\\.(?.*)"); string name = filename.Groups["type"].Value; string action = filename.Groups["action"].Value; + string extension = filename.Groups["extension"].Value; + + // skip unrecognized files + List valid = new List() { "mp4", "png", "txt" }; + if (!valid.Contains(extension.ToLower())) + { + continue; + } + SlideType type; // look at the name to determine the type switch (name) @@ -47,14 +56,31 @@ public bool Create(string folder) case "ChromaKeyStill": type = SlideType.ChromaKeyStill; break; + case "Action": + type = SlideType.Action; + break; default: type = SlideType.Empty; break; } - Slide s = new Slide() { Source = file, Type = type, Action = action }; + Slide s = new Slide() { Source = file, Type = type, PreAction = action }; + if (s.Type == SlideType.Action) + { + s.LoadActions(); + } Slides.Add(s); } + // attack keyfiles to slides + files = Directory.GetFiles(folder).Where(f => Regex.Match(f, @"Key_\d+").Success).ToList(); + foreach (var file in files) + { + var num = Regex.Match(file, @"Key_(?\d+)").Groups["num"].Value; + int snum = Convert.ToInt32(num); + Slides[snum].KeySource = file; + } + + return false; } @@ -182,10 +208,283 @@ public class Slide { public SlideType Type { get; set; } public string Source { get; set; } - public string Action { get; set; } + public string KeySource { get; set; } + public string PreAction { get; set; } public Guid Guid { get; set; } = Guid.NewGuid(); + public List SetupActions { get; set; } = new List(); + public List Actions { get; set; } = new List(); + public string Title { get; set; } = ""; + public bool AutoOnly { get; set; } = false; + + public void LoadActions() + { + if (Type == SlideType.Action) + { + try + { + List parts = new List(); + using (StreamReader sr = new StreamReader(Source)) + { + string text = sr.ReadToEnd(); + var commands = text.Split(";", StringSplitOptions.RemoveEmptyEntries).Select(s => (s + ";").Trim()); + parts = commands.ToList(); + } + Title = "AUTO SEQ"; + foreach (var part in parts) + { + // parse into commands + if (part == ";") + { + + } + else if (part.StartsWith("!")) + { + if (part == ("!fullauto;")) + { + AutoOnly = true; + } + } + else if (part.StartsWith("@")) + { + var a = AutomationAction.Parse(part.Remove(0, 1)); + SetupActions.Add(a); + } + else if (part.StartsWith("#")) + { + var title = Regex.Match(part, @"#(?.*);").Groups["title"].Value; + Title = title; + } + else + { + var a = AutomationAction.Parse(part); + Actions.Add(a); + } + } + } + catch (Exception) + { + } + } + } + + } + + public class AutomationAction + { + public AutomationActionType Action { get; set; } = AutomationActionType.None; + public string Message { get; set; } = ""; + public int DataI { get; set; } = 0; + public string DataS { get; set; } = ""; + + + public static AutomationAction Parse(string command) + { + AutomationAction a = new AutomationAction(); + a.Action = AutomationActionType.None; + a.DataI = 0; + a.DataS = ""; + a.Message = ""; + + if (command.StartsWith("arg0:")) + { + var res = Regex.Match(command, @"arg0:(?<commandname>.*?)(\[(?<msg>.*)\])?;"); + string cmd = res.Groups["commandname"].Value; + string msg = res.Groups["msg"].Value; + a.Message = msg; + switch (cmd) + { + case "AutoTrans": + a.Action = AutomationActionType.AutoTrans; + break; + case "CutTrans": + a.Action = AutomationActionType.CutTrans; + break; + case "AutoTakePresetIfOnSlide": + a.Action = AutomationActionType.AutoTakePresetIfOnSlide; + break; + + case "DSK1On": + a.Action = AutomationActionType.DSK1On; + break; + case "DSK1Off": + a.Action = AutomationActionType.DSK1Off; + break; + case "DSK1FadeOn": + a.Action = AutomationActionType.DSK1FadeOn; + break; + case "DSK1FadeOff": + a.Action = AutomationActionType.DSK1FadeOff; + break; + + case "DSK2On": + a.Action = AutomationActionType.DSK2On; + break; + case "DSK2Off": + a.Action = AutomationActionType.DSK2Off; + break; + case "DSK2FadeOn": + a.Action = AutomationActionType.DSK2FadeOn; + break; + case "DSK2FadeOff": + a.Action = AutomationActionType.DSK2FadeOff; + break; + + case "USK1On": + a.Action = AutomationActionType.USK1On; + break; + case "USK1Off": + a.Action = AutomationActionType.USK1Off; + break; + case "USK1SetTypeChroma": + a.Action = AutomationActionType.USK1SetTypeChroma; + break; + case "USK1SetTypeDVE": + a.Action = AutomationActionType.USK1SetTypeDVE; + break; + + case "RecordStart": + a.Action = AutomationActionType.RecordStart; + break; + case "RecordStop": + a.Action = AutomationActionType.RecordStop; + break; + + case "OpenAudioPlayer": + a.Action = AutomationActionType.OpenAudioPlayer; + break; + case "PlayAuxAudio": + a.Action = AutomationActionType.PlayAuxAudio; + break; + case "StopAuxAudio": + a.Action = AutomationActionType.StopAuxAudio; + break; + case "PauseAuxAudio": + a.Action = AutomationActionType.PauseAuxAudio; + break; + case "ReplayAuxAudio": + a.Action = AutomationActionType.ReplayAuxAudio; + break; + + case "PlayMedia": + a.Action = AutomationActionType.PlayMedia; + break; + case "PauseMedia": + a.Action = AutomationActionType.PauseMedia; + break; + case "StopMedia": + a.Action = AutomationActionType.StopMedia; + break; + case "RestartMedia": + a.Action = AutomationActionType.RestartMedia; + break; + case "MuteMedia": + a.Action = AutomationActionType.MuteMedia; + break; + case "UnMuteMedia": + a.Action = AutomationActionType.UnMuteMedia; + break; + + case "DriveNextSlide": + a.Action = AutomationActionType.DriveNextSlide; + break; + case "Timer1Restart": + a.Action = AutomationActionType.Timer1Restart; + break; + } + } + if (command.StartsWith("arg1:")) + { + var res = Regex.Match(command, @"arg1:(?<commandname>.*?)\((?<param>.*)\)(\[(?<msg>.*)\])?;"); + string cmd = res.Groups["commandname"].Value; + string arg1 = res.Groups["param"].Value; + string msg = res.Groups["msg"].Value; + a.Message = msg; + switch (cmd) + { + case "PresetSelect": + a.Action = AutomationActionType.PresetSelect; + a.DataI = Convert.ToInt32(arg1); + break; + case "ProgramSelect": + a.Action = AutomationActionType.ProgramSelect; + a.DataI = Convert.ToInt32(arg1); + break; + case "AuxSelect": + a.Action = AutomationActionType.AuxSelect; + a.DataI = Convert.ToInt32(arg1); + break; + case "DelayMs": + a.Action = AutomationActionType.DelayMs; + a.DataI = Convert.ToInt32(arg1); + break; + case "LoadAudioFile": + a.Action = AutomationActionType.LoadAudio; + a.DataS = arg1; + break; + default: + break; + } + } + + + return a; + } + } + public enum AutomationActionType + { + PresetSelect, + ProgramSelect, + + AutoTrans, + CutTrans, + + DSK1On, + DSK1Off, + DSK1FadeOn, + DSK1FadeOff, + + DSK2On, + DSK2Off, + DSK2FadeOn, + DSK2FadeOff, + + USK1Off, + USK1On, + + USK1SetTypeChroma, + USK1SetTypeDVE, + + RecordStart, + RecordStop, + + AutoTakePresetIfOnSlide, + + + OpenAudioPlayer, + LoadAudio, + PlayAuxAudio, + StopAuxAudio, + PauseAuxAudio, + ReplayAuxAudio, + + + DelayMs, + + None, + DriveNextSlide, + Timer1Restart, + PlayMedia, + PauseMedia, + StopMedia, + RestartMedia, + MuteMedia, + UnMuteMedia, + AuxSelect, + } + + public enum SlideType { Full, @@ -193,6 +492,7 @@ public enum SlideType Video, ChromaKeyVideo, ChromaKeyStill, + Action, Empty } diff --git a/Integrated Presenter/PresenterDisplay.xaml.cs b/Integrated Presenter/PresenterDisplay.xaml.cs index 21bd77e6..60bd3cf4 100644 --- a/Integrated Presenter/PresenterDisplay.xaml.cs +++ b/Integrated Presenter/PresenterDisplay.xaml.cs @@ -32,7 +32,9 @@ public partial class PresenterDisplay : Window private Guid prevslide; private Guid nextslide; - public PresenterDisplay(MainWindow parent) + private bool ShowKey; + + public PresenterDisplay(MainWindow parent, bool ShowKey) { InitializeComponent(); _control = parent; @@ -46,7 +48,10 @@ public PresenterDisplay(MainWindow parent) blackcover.Visibility = Visibility.Hidden; - ShowSlide(); + this.ShowKey = ShowKey; + ShowSlide(this.ShowKey); + + Title = ShowKey ? "Presentation Key Source" : "Presentation Display"; } @@ -183,8 +188,22 @@ public void StopMediaPlayback() } } + public void MuteMedia() + { + mediaPlayerA.videoPlayer.Volume = 0; + mediaPlayerB.videoPlayer.Volume = 0; + mediaPlayerC.videoPlayer.Volume = 0; + } - public void ShowSlide() + public void UnMuteMedia() + { + mediaPlayerA.videoPlayer.Volume = 1; + mediaPlayerB.videoPlayer.Volume = 1; + mediaPlayerC.videoPlayer.Volume = 1; + } + + + public void ShowSlide(bool asKey) { Slide slidetoshow = _control.Presentation.EffectiveCurrent; @@ -245,6 +264,7 @@ public void ShowSlide() private void ShowActivePlayer() { + StopNonActiveMedia(); if (activeplayer == 1) { mediaPlayerA.Visibility = Visibility.Visible; @@ -271,13 +291,13 @@ private void SetSlideForPlayer(int player, Slide slide) switch (player) { case 1: - mediaPlayerA.SetMedia(slide); + mediaPlayerA.SetMedia(slide, ShowKey); break; case 2: - mediaPlayerB.SetMedia(slide); + mediaPlayerB.SetMedia(slide, ShowKey); break; case 3: - mediaPlayerC.SetMedia(slide); + mediaPlayerC.SetMedia(slide, ShowKey); break; } } diff --git a/Integrated Presenter/SlidePoolSource.xaml.cs b/Integrated Presenter/SlidePoolSource.xaml.cs index c0436854..6d4b631a 100644 --- a/Integrated Presenter/SlidePoolSource.xaml.cs +++ b/Integrated Presenter/SlidePoolSource.xaml.cs @@ -1,6 +1,7 @@ using Microsoft.Win32; using System; using System.Collections.Generic; +using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -260,7 +261,7 @@ private async void ClickLoadMedia(object sender, RoutedEventArgs e) break; } Source = new Uri(ofd.FileName); - Slide = new Slide() { Action = action, Guid = Guid.NewGuid(), Source = ofd.FileName, Type = Type, }; + Slide = new Slide() { PreAction = action, Guid = Guid.NewGuid(), Source = ofd.FileName, Type = Type, }; } else { @@ -271,7 +272,7 @@ private async void ClickLoadMedia(object sender, RoutedEventArgs e) { Type = SlideType.Video; } - Slide = new Slide() { Action = "", Guid = Guid.NewGuid(), Source = ofd.FileName, Type = Type }; + Slide = new Slide() { PreAction = "", Guid = Guid.NewGuid(), Source = ofd.FileName, Type = Type }; } mediapreview.SetMedia(Source, Type); @@ -284,6 +285,23 @@ private async void ClickLoadMedia(object sender, RoutedEventArgs e) UpdateDurationUI(); } + // ask to open a key file + + // uses a default white key (fully opaque) if not + + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Title = "Select Alpha Key"; + openFileDialog.Filter = "Image (*.png)|*.png"; + if (openFileDialog.ShowDialog() == true) + { + Slide.KeySource = openFileDialog.FileName; + } + else + { + Slide.KeySource = "pack://application:,,,/Keys/WhiteKey.png"; + } + + BtnTakeInsert.Style = (Style)Application.Current.FindResource("SwitcherButton"); BtnTakeReplace.Style = (Style)Application.Current.FindResource("SwitcherButton"); loaded = true; diff --git a/Integrated Presenter/version.json b/Integrated Presenter/version.json index 23ab0e71..e1fc0112 100644 --- a/Integrated Presenter/version.json +++ b/Integrated Presenter/version.json @@ -1 +1 @@ -{"MajorVersion": 1, "MinorVersion": 5, "Revision": 0, "Build": 8} \ No newline at end of file +{"MajorVersion": 1, "MinorVersion": 6, "Revision": 0, "Build": 4} \ No newline at end of file diff --git a/SlideCreater/MainWindow.xaml.cs b/SlideCreater/MainWindow.xaml.cs index 47528cc5..5278b1c4 100644 --- a/SlideCreater/MainWindow.xaml.cs +++ b/SlideCreater/MainWindow.xaml.cs @@ -347,7 +347,7 @@ private void ClickAddAssets(object sender, RoutedEventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Title = "Add Assets"; - ofd.Filter = "Images and Video (*.png;*.jpg;*.bmp;*.mp4)|*.png;*.jpg;*.bmp;*.mp4"; + ofd.Filter = "Images, Video and Audio (*.png;*.jpg;*.bmp;*.mp4;*.mp3;*.wav)|*.png;*.jpg;*.bmp;*.mp4;*.mp3;*.wav"; ofd.Multiselect = true; if (ofd.ShowDialog() == true) { @@ -367,14 +367,19 @@ private void ClickAddAssets(object sender, RoutedEventArgs e) } ProjectAsset asset = new ProjectAsset(); - if (Regex.IsMatch(System.IO.Path.GetExtension(file), "\\.mp4", RegexOptions.IgnoreCase)) + if (Regex.IsMatch(System.IO.Path.GetExtension(file).ToLower(), @"(\.mp3)|(\.wav)", RegexOptions.IgnoreCase)) { - asset = new ProjectAsset() { Id = Guid.NewGuid(), Name = System.IO.Path.GetFileNameWithoutExtension(file), OriginalPath = file, LoadedTempPath = tmpassetpath, Type = AssetType.Video }; + asset = new ProjectAsset() { Id = Guid.NewGuid(), Name = System.IO.Path.GetFileNameWithoutExtension(file), OriginalPath = file, LoadedTempPath = tmpassetpath, Type = AssetType.Audio }; } - else if (Regex.IsMatch(System.IO.Path.GetExtension(file), @"(\.png)|(\.jpg)|(\.bmp)", RegexOptions.IgnoreCase)) + else if (Regex.IsMatch(System.IO.Path.GetExtension(file).ToLower(), @"(\.png)|(\.jpg)|(\.bmp)", RegexOptions.IgnoreCase)) { asset = new ProjectAsset() { Id = Guid.NewGuid(), Name = System.IO.Path.GetFileNameWithoutExtension(file), OriginalPath = file, LoadedTempPath = tmpassetpath, Type = AssetType.Image }; } + else if (Regex.IsMatch(System.IO.Path.GetExtension(file).ToLower(), @"\.mp4", RegexOptions.IgnoreCase)) + { + asset = new ProjectAsset() { Id = Guid.NewGuid(), Name = System.IO.Path.GetFileNameWithoutExtension(file), OriginalPath = file, LoadedTempPath = tmpassetpath, Type = AssetType.Video }; + } + Assets.Add(asset); diff --git a/SlideCreater/version.json b/SlideCreater/version.json index 23ab0e71..e1fc0112 100644 --- a/SlideCreater/version.json +++ b/SlideCreater/version.json @@ -1 +1 @@ -{"MajorVersion": 1, "MinorVersion": 5, "Revision": 0, "Build": 8} \ No newline at end of file +{"MajorVersion": 1, "MinorVersion": 6, "Revision": 0, "Build": 4} \ No newline at end of file diff --git a/Xenon/AssetManagment/AssetType.cs b/Xenon/AssetManagment/AssetType.cs index be24182c..b3f9adf5 100644 --- a/Xenon/AssetManagment/AssetType.cs +++ b/Xenon/AssetManagment/AssetType.cs @@ -8,5 +8,6 @@ public enum AssetType { Image, Video, + Audio, } } diff --git a/Xenon/Compiler/AST/XenonAST2PartTitle.cs b/Xenon/Compiler/AST/XenonAST2PartTitle.cs index 18789ba7..9b4cc5ec 100644 --- a/Xenon/Compiler/AST/XenonAST2PartTitle.cs +++ b/Xenon/Compiler/AST/XenonAST2PartTitle.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Text; +using Xenon.Helpers; using Xenon.SlideAssembly; namespace Xenon.Compiler.AST @@ -49,6 +51,12 @@ public void Generate(Project project, IXenonASTElement _Parent) titleslide.Data["orientation"] = Orientation; + if (project.GetAttribute("alphatranscol").Count > 0) + { + titleslide.Colors.Add("keytrans", GraphicsHelper.ColorFromRGB(project.GetAttribute("alphatranscol").FirstOrDefault())); + } + + project.Slides.Add(titleslide); diff --git a/Xenon/Compiler/AST/XenonASTAnthemTitle.cs b/Xenon/Compiler/AST/XenonASTAnthemTitle.cs index 1e022fb5..3a042117 100644 --- a/Xenon/Compiler/AST/XenonASTAnthemTitle.cs +++ b/Xenon/Compiler/AST/XenonASTAnthemTitle.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Text; +using Xenon.Helpers; using Xenon.SlideAssembly; namespace Xenon.Compiler.AST @@ -19,7 +21,7 @@ public IXenonASTElement Compile(Lexer Lexer, XenonErrorLogger Logger) XenonASTAnthemTitle title = new XenonASTAnthemTitle(); Lexer.GobbleWhitespace(); - var args = Lexer.ConsumeArgList(true,"anthemtitle", "musician", "accompanianst", "credits"); + var args = Lexer.ConsumeArgList(true, "anthemtitle", "musician", "accompanianst", "credits"); title.AnthemTitle = args["anthemtitle"]; title.Musician = args["musician"]; title.Accompanianst = args["accompanianst"]; @@ -55,6 +57,12 @@ public void Generate(Project project, IXenonASTElement _Parent) titleslide.Lines.Add(slaccompanianst); titleslide.Lines.Add(slcredits); + if (project.GetAttribute("alphatranscol").Count > 0) + { + titleslide.Colors.Add("keytrans", GraphicsHelper.ColorFromRGB(project.GetAttribute("alphatranscol").FirstOrDefault())); + } + + project.Slides.Add(titleslide); } diff --git a/Xenon/Compiler/AST/XenonASTExpression.cs b/Xenon/Compiler/AST/XenonASTExpression.cs index 766c3c40..5bfb8d4a 100644 --- a/Xenon/Compiler/AST/XenonASTExpression.cs +++ b/Xenon/Compiler/AST/XenonASTExpression.cs @@ -35,6 +35,23 @@ public IXenonASTElement Compile(Lexer Lexer, XenonErrorLogger Logger) private XenonASTExpression CompileCommand(Lexer Lexer, XenonErrorLogger Logger) { XenonASTExpression expr = new XenonASTExpression(); + + if (Lexer.Inspect(LanguageKeywords.Commands[LanguageKeywordCommand.Resource])) + { + XenonASTResource resource = new XenonASTResource(); + Lexer.GobbleandLog(LanguageKeywords.Commands[LanguageKeywordCommand.Resource]); + expr.Command = (IXenonASTCommand)resource.Compile(Lexer, Logger); + return expr; + } + if (Lexer.Inspect(LanguageKeywords.Commands[LanguageKeywordCommand.Script])) + { + XenonASTScript script = new XenonASTScript(); + Lexer.GobbleandLog(LanguageKeywords.Commands[LanguageKeywordCommand.Script]); + expr.Command = (IXenonASTCommand)script.Compile(Lexer, Logger); + return expr; + } + + if (Lexer.Inspect(LanguageKeywords.Commands[LanguageKeywordCommand.SetVar])) { XenonASTSetVariable xenonASTSetVariable = new XenonASTSetVariable(); diff --git a/Xenon/Compiler/AST/XenonASTLiturgy.cs b/Xenon/Compiler/AST/XenonASTLiturgy.cs index 2d0d4029..027cf162 100644 --- a/Xenon/Compiler/AST/XenonASTLiturgy.cs +++ b/Xenon/Compiler/AST/XenonASTLiturgy.cs @@ -104,6 +104,7 @@ public void Generate(Project project, IXenonASTElement _Parent) System.Drawing.Color liturgyspeakercolor = new System.Drawing.Color(); System.Drawing.Color liturgytextcolor = new System.Drawing.Color(); System.Drawing.Color liturgybackgroundcolor = new System.Drawing.Color(); + System.Drawing.Color liturgytransppcolor = System.Drawing.Color.Gray; if (project.GetAttribute("litspeakertextcol").Count > 0) { @@ -120,6 +121,10 @@ public void Generate(Project project, IXenonASTElement _Parent) liturgybackgroundcolor = GraphicsHelper.ColorFromRGB(project.GetAttribute("litbackgroundcol").FirstOrDefault()); overridebackgroundcolor = true; } + if (project.GetAttribute("alphatranscol").Count > 0) + { + liturgytransppcolor = GraphicsHelper.ColorFromRGB(project.GetAttribute("alphatranscol").FirstOrDefault()); + } @@ -158,6 +163,7 @@ 3. If a logical-line requires wrapping the line must be the first line of the sl { liturgyslide.Colors["keybackground"] = liturgybackgroundcolor; } + liturgyslide.Colors["keytrans"] = liturgytransppcolor; double lineheight = -project.Layouts.LiturgyLayout.InterLineSpacing; @@ -202,6 +208,7 @@ 3. If a logical-line requires wrapping the line must be the first line of the sl { liturgyslide.Colors["keybackground"] = liturgybackgroundcolor; } + liturgyslide.Colors["keytrans"] = liturgytransppcolor; lineheight = 0; startspeaker = line.Speaker; diff --git a/Xenon/Compiler/AST/XenonASTLiturgyImage.cs b/Xenon/Compiler/AST/XenonASTLiturgyImage.cs index 0d3046cc..fa4e1d3b 100644 --- a/Xenon/Compiler/AST/XenonASTLiturgyImage.cs +++ b/Xenon/Compiler/AST/XenonASTLiturgyImage.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using Xenon.Helpers; +using System.Linq; namespace Xenon.Compiler { @@ -42,6 +44,11 @@ public void Generate(Project project, IXenonASTElement _Parent) imageslide.Asset = assetpath; imageslide.MediaType = MediaType.Image; + if (project.GetAttribute("alphatranscol").Count > 0) + { + imageslide.Colors.Add("keytrans", GraphicsHelper.ColorFromRGB(project.GetAttribute("alphatranscol").FirstOrDefault())); + } + project.Slides.Add(imageslide); } diff --git a/Xenon/Compiler/AST/XenonASTLiturgyVerse.cs b/Xenon/Compiler/AST/XenonASTLiturgyVerse.cs index 287c20de..8342baae 100644 --- a/Xenon/Compiler/AST/XenonASTLiturgyVerse.cs +++ b/Xenon/Compiler/AST/XenonASTLiturgyVerse.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Linq; using Xenon.Compiler.AST; +using Xenon.Helpers; using Xenon.LayoutEngine; using Xenon.SlideAssembly; @@ -84,6 +85,12 @@ public void Generate(Project project, IXenonASTElement _Parent) double lineheight = -project.Layouts.LiturgyLayout.InterLineSpacing; + if (project.GetAttribute("alphatranscol").Count > 0) + { + liturgyslide.Colors.Add("keytrans", GraphicsHelper.ColorFromRGB(project.GetAttribute("alphatranscol").FirstOrDefault())); + } + + foreach (var line in layoutEngine.LayoutLines) { @@ -103,6 +110,10 @@ public void Generate(Project project, IXenonASTElement _Parent) MediaType = MediaType.Image }; lineheight = 0; + if (project.GetAttribute("alphatranscol").Count > 0) + { + liturgyslide.Colors.Add("keytrans", GraphicsHelper.ColorFromRGB(project.GetAttribute("alphatranscol").FirstOrDefault())); + } } lineheight += project.Layouts.LiturgyLayout.InterLineSpacing + line.height; liturgyslide.Lines.Add( diff --git a/Xenon/Compiler/AST/XenonASTReading.cs b/Xenon/Compiler/AST/XenonASTReading.cs index 6f3c2e49..7a802545 100644 --- a/Xenon/Compiler/AST/XenonASTReading.cs +++ b/Xenon/Compiler/AST/XenonASTReading.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using Xenon.Helpers; +using System.Linq; namespace Xenon.Compiler { @@ -43,6 +45,11 @@ public void Generate(Project project, IXenonASTElement _Parent) readingslide.Lines.Add(slname); readingslide.Lines.Add(slref); + if (project.GetAttribute("alphatranscol").Count > 0) + { + readingslide.Colors.Add("keytrans", GraphicsHelper.ColorFromRGB(project.GetAttribute("alphatranscol").FirstOrDefault())); + } + project.Slides.Add(readingslide); } diff --git a/Xenon/Compiler/AST/XenonASTResouce.cs b/Xenon/Compiler/AST/XenonASTResouce.cs new file mode 100644 index 00000000..9304336d --- /dev/null +++ b/Xenon/Compiler/AST/XenonASTResouce.cs @@ -0,0 +1,63 @@ +using Xenon.SlideAssembly; +using System; +using System.Diagnostics; +using System.Runtime; +using System.Collections.Generic; +using System.Text; + +namespace Xenon.Compiler +{ + class XenonASTResource : IXenonASTCommand + { + + public string AssetName { get; set; } = ""; + public string Assettype { get; set; } = ""; + + public IXenonASTElement Compile(Lexer Lexer, XenonErrorLogger Logger) + { + XenonASTResource resource = new XenonASTResource(); + var args = Lexer.ConsumeArgList(true, "assetname", "type"); + resource.AssetName = args["assetname"]; + resource.Assettype = args["type"]; + return resource; + } + + public void Generate(Project project, IXenonASTElement _Parent) + { + Slide res = new Slide + { + Name = "UNNAMED_resource", + Number = -1, // Slide is not given a number since is not an ordered slide + Lines = new List<SlideLine>() + }; + res.Format = SlideFormat.ResourceCopy; + res.Asset = project.Assets.Find(p => p.Name == AssetName).CurrentPath; + if (Assettype == "audio") + { + res.MediaType = MediaType.Audio; + } + if (Assettype == "video") + { + res.MediaType = MediaType.Video; + } + if (Assettype == "image") + { + res.MediaType = MediaType.Image; + } + + project.Slides.Add(res); + } + + public void GenerateDebug(Project project) + { + Debug.WriteLine("<XenonASTResource>"); + Debug.WriteLine(AssetName); + Debug.WriteLine("</XenonASTResource>"); + } + + public XenonCompilerSyntaxReport Recognize(Lexer Lexer) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Xenon/Compiler/AST/XenonASTScript.cs b/Xenon/Compiler/AST/XenonASTScript.cs new file mode 100644 index 00000000..14ca6152 --- /dev/null +++ b/Xenon/Compiler/AST/XenonASTScript.cs @@ -0,0 +1,65 @@ +using Xenon.SlideAssembly; +using System; +using System.Diagnostics; +using System.Runtime; +using System.Collections.Generic; +using System.Text; + +namespace Xenon.Compiler +{ + class XenonASTScript : IXenonASTCommand + { + + public string Source { get; set; } = ""; + + public IXenonASTElement Compile(Lexer Lexer, XenonErrorLogger Logger) + { + XenonASTScript script = new XenonASTScript(); + Lexer.GobbleWhitespace(); + StringBuilder sb = new StringBuilder(); + Lexer.GobbleandLog("{"); + Lexer.GobbleWhitespace(); + while (!Lexer.InspectEOF()) + { + sb.Append(Lexer.Consume()); + if (Lexer.Inspect("}")) + { + script.Source = sb.ToString(); + Lexer.Consume(); + break; + } + } + return script; + + } + + public void Generate(Project project, IXenonASTElement _Parent) + { + // create a video slide + Slide script = new Slide + { + Name = "UNNAMED_script", + Number = project.NewSlideNumber, + Lines = new List<SlideLine>() + }; + script.Format = SlideFormat.Script; + script.Asset = ""; + script.MediaType = MediaType.Text; + script.Data["source"] = Source; + + project.Slides.Add(script); + } + + public void GenerateDebug(Project project) + { + Debug.WriteLine("<XenonASTScript>"); + Debug.WriteLine(Source); + Debug.WriteLine("</XenonASTScript>"); + } + + public XenonCompilerSyntaxReport Recognize(Lexer Lexer) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Xenon/Compiler/AST/XenonASTSermon.cs b/Xenon/Compiler/AST/XenonASTSermon.cs index 52063cb1..d3401a22 100644 --- a/Xenon/Compiler/AST/XenonASTSermon.cs +++ b/Xenon/Compiler/AST/XenonASTSermon.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using Xenon.Helpers; +using System.Linq; namespace Xenon.Compiler { @@ -50,6 +52,12 @@ public void Generate(Project project, IXenonASTElement _Parent) sermonslide.Lines.Add(slref); sermonslide.Lines.Add(slpreacher); + if (project.GetAttribute("alphatranscol").Count > 0) + { + sermonslide.Colors.Add("keytrans", GraphicsHelper.ColorFromRGB(project.GetAttribute("alphatranscol").FirstOrDefault())); + } + + project.Slides.Add(sermonslide); diff --git a/Xenon/Compiler/AST/XenonASTTitledLiturgyVerse.cs b/Xenon/Compiler/AST/XenonASTTitledLiturgyVerse.cs index 041eb6c8..a36f8c38 100644 --- a/Xenon/Compiler/AST/XenonASTTitledLiturgyVerse.cs +++ b/Xenon/Compiler/AST/XenonASTTitledLiturgyVerse.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Text; using System.Text.RegularExpressions; +using Xenon.Helpers; using Xenon.LayoutEngine; using Xenon.SlideAssembly; @@ -71,6 +73,12 @@ public void Generate(Project project, IXenonASTElement _Parent) slide.Data["reference"] = Reference; slide.Data["drawspeaker"] = DrawSpeaker; + if (project.GetAttribute("alphatranscol").Count > 0) + { + slide.Colors.Add("keytrans", GraphicsHelper.ColorFromRGB(project.GetAttribute("alphatranscol").FirstOrDefault())); + } + + project.Slides.Add(slide); } diff --git a/Xenon/Compiler/LanguageKeywords.cs b/Xenon/Compiler/LanguageKeywords.cs index 465c41d6..336fbafe 100644 --- a/Xenon/Compiler/LanguageKeywords.cs +++ b/Xenon/Compiler/LanguageKeywords.cs @@ -31,6 +31,8 @@ static class LanguageKeywords [LanguageKeywordCommand.ApostlesCreed] = "apostlescreed", [LanguageKeywordCommand.NiceneCreed] = "nicenecreed", [LanguageKeywordCommand.LordsPrayer] = "lordsprayer", + [LanguageKeywordCommand.Resource] = "resource", + [LanguageKeywordCommand.Script] = "script", }; @@ -60,6 +62,8 @@ enum LanguageKeywordCommand ApostlesCreed, NiceneCreed, LordsPrayer, + Resource, + Script, } } diff --git a/Xenon/Compiler/XenonBuildService.cs b/Xenon/Compiler/XenonBuildService.cs index 28c31c49..6d8eed29 100644 --- a/Xenon/Compiler/XenonBuildService.cs +++ b/Xenon/Compiler/XenonBuildService.cs @@ -59,7 +59,7 @@ await Task.Run(() => Parallel.ForEach(project.Slides, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, (Slide s) => { - slides.Add(sr.RenderSlide(s.Number, Messages)); + slides.Add(sr.RenderSlide(s, Messages)); Interlocked.Increment(ref completedslidecount); int prog = (int)(completedslidecount / (double)project.Slides.Count * 100); progress.Report(prog); diff --git a/Xenon/Helpers/GraphicsHelper.cs b/Xenon/Helpers/GraphicsHelper.cs index aa5e1c5a..db204306 100644 --- a/Xenon/Helpers/GraphicsHelper.cs +++ b/Xenon/Helpers/GraphicsHelper.cs @@ -6,7 +6,6 @@ using System.IO; using System.Text; using System.Text.RegularExpressions; -using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; @@ -216,6 +215,111 @@ public static SizeF MeasureStringCharacters(this Graphics gfx, string text, Font } + + public static Bitmap InvertImage(this Bitmap source) + { + /* + https://stackoverflow.com/questions/33024881/invert-image-faster-in-c-sharp + */ + Bitmap res = new Bitmap(source); + + for (int y = 0; y < res.Height; y++) + { + for (int x = 0; x < res.Width; x++) + { + Color inv = res.GetPixel(x, y); + inv = Color.FromArgb(255, 255 - inv.R, 255 - inv.G, 255 - inv.B); + res.SetPixel(x, y, inv); + } + } + return res; + } + + public static Bitmap CorrectImage(this Bitmap source, int cfactor, Color ccol) + { + Bitmap res = new Bitmap(source); + + for (int y = 0; y < res.Height; y++) + { + for (int x = 0; x < res.Width; x++) + { + Color inv = res.GetPixel(x, y); + if (Math.Abs(ccol.R - inv.R) < cfactor || Math.Abs(ccol.G - inv.G) < cfactor || Math.Abs(ccol.B - inv.B) < cfactor) + { + res.SetPixel(x, y, ccol); + } + } + } + return res; + + } + + public static Bitmap ForceMonoChromeImage(this Bitmap source, Color except, Color force) + { + Bitmap res = new Bitmap(source); + + for (int y = 0; y < res.Height; y++) + { + for (int x = 0; x < res.Width; x++) + { + Color inv = res.GetPixel(x, y); + if (inv.R != except.R || inv.G != except.G || inv.B != except.B) + { + res.SetPixel(x, y, force); + } + } + } + return res; + + } + + + public static Bitmap DichotimizeImage(this Bitmap source, Color match, int tolerance, Color force) + { + Bitmap res = new Bitmap(source); + + for (int y = 0; y < res.Height; y++) + { + for (int x = 0; x < res.Width; x++) + { + Color inv = res.GetPixel(x, y); + if (Math.Abs(match.R - inv.R) < tolerance || Math.Abs(match.G - inv.G) < tolerance || Math.Abs(match.B - inv.B) < tolerance) + { + res.SetPixel(x, y, match); + } + else + { + res.SetPixel(x, y, force); + } + } + } + return res; + + } + + + public static Bitmap SwapImageColors(this Bitmap source, Color scol, Color dcol) + { + /* + https://stackoverflow.com/questions/33024881/invert-image-faster-in-c-sharp + */ + Bitmap res = new Bitmap(source); + + for (int y = 0; y < res.Height; y++) + { + for (int x = 0; x < res.Width; x++) + { + Color inv = res.GetPixel(x, y); + if (inv.R == scol.R && inv.G == scol.G && inv.B == scol.B) + { + res.SetPixel(x, y, dcol); + } + } + } + return res; + } + + //public static Font LoadLSBSymbolFont() //{ diff --git a/Xenon/Renderer/AnthemTitleSlideRenderer.cs b/Xenon/Renderer/AnthemTitleSlideRenderer.cs index 8a39510c..60eba8e0 100644 --- a/Xenon/Renderer/AnthemTitleSlideRenderer.cs +++ b/Xenon/Renderer/AnthemTitleSlideRenderer.cs @@ -19,23 +19,33 @@ public RenderedSlide RenderSlide(Slide slide, List<Compiler.XenonCompilerMessage res.RenderedAs = "Liturgy"; Bitmap bmp = new Bitmap(Layouts.AnthemTitleLayout.Size.Width, Layouts.AnthemTitleLayout.Size.Height); + Bitmap kbmp = new Bitmap(Layouts.AnthemTitleLayout.Size.Width, Layouts.AnthemTitleLayout.Size.Height); Graphics gfx = Graphics.FromImage(bmp); + Graphics kgfx = Graphics.FromImage(kbmp); gfx.Clear(Color.Gray); + kgfx.Clear(Color.Black); gfx.FillRectangle(Brushes.Black, Layouts.SermonLayout.Key); + SolidBrush kb = new SolidBrush(slide.Colors["keytrans"]); + kgfx.FillRectangle(kb, Layouts.SermonLayout.Key); Font bf = new Font(Layouts.AnthemTitleLayout.Font, FontStyle.Bold); // musician gfx.DrawString(slide.Lines[1].Content[0].Data, bf, Brushes.White, Layouts.AnthemTitleLayout.TopLine.Move(Layouts.AnthemTitleLayout.Key.Location), GraphicsHelper.LeftVerticalCenterAlign); + kgfx.DrawString(slide.Lines[1].Content[0].Data, bf, Brushes.White, Layouts.AnthemTitleLayout.TopLine.Move(Layouts.AnthemTitleLayout.Key.Location), GraphicsHelper.LeftVerticalCenterAlign); // anthem title gfx.DrawString(slide.Lines[0].Content[0].Data, bf, Brushes.White, Layouts.AnthemTitleLayout.TopLine.Move(Layouts.AnthemTitleLayout.Key.Location), GraphicsHelper.RightVerticalCenterAlign); + kgfx.DrawString(slide.Lines[0].Content[0].Data, bf, Brushes.White, Layouts.AnthemTitleLayout.TopLine.Move(Layouts.AnthemTitleLayout.Key.Location), GraphicsHelper.RightVerticalCenterAlign); // accompanianist gfx.DrawString(slide.Lines[2].Content[0].Data, Layouts.AnthemTitleLayout.Font, Brushes.White, Layouts.AnthemTitleLayout.MainLine.Move(Layouts.AnthemTitleLayout.Key.Location), GraphicsHelper.LeftVerticalCenterAlign); + kgfx.DrawString(slide.Lines[2].Content[0].Data, Layouts.AnthemTitleLayout.Font, Brushes.White, Layouts.AnthemTitleLayout.MainLine.Move(Layouts.AnthemTitleLayout.Key.Location), GraphicsHelper.LeftVerticalCenterAlign); // credit gfx.DrawString(slide.Lines[3].Content[0].Data, Layouts.AnthemTitleLayout.Font, Brushes.White, Layouts.AnthemTitleLayout.MainLine.Move(Layouts.AnthemTitleLayout.Key.Location), GraphicsHelper.RightVerticalCenterAlign); + kgfx.DrawString(slide.Lines[3].Content[0].Data, Layouts.AnthemTitleLayout.Font, Brushes.White, Layouts.AnthemTitleLayout.MainLine.Move(Layouts.AnthemTitleLayout.Key.Location), GraphicsHelper.RightVerticalCenterAlign); res.Bitmap = bmp; + res.KeyBitmap = kbmp; return res; } } diff --git a/Xenon/Renderer/CopySlideRenderer.cs b/Xenon/Renderer/CopySlideRenderer.cs new file mode 100644 index 00000000..214552cc --- /dev/null +++ b/Xenon/Renderer/CopySlideRenderer.cs @@ -0,0 +1,33 @@ +using Xenon.LayoutEngine; +using Xenon.SlideAssembly; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Text; +using Xenon.Helpers; +using System.IO; + +namespace Xenon.Renderer +{ + class CopySlideRenderer + { + public SlideLayout Layouts { get; set; } + + public RenderedSlide RenderSlide(Slide slide, List<Compiler.XenonCompilerMessage> messages) + { + RenderedSlide res = new RenderedSlide(); + // only do audio for now + res.MediaType = MediaType.Audio; + res.RenderedAs = "Resource"; + res.Name = Path.GetFileNameWithoutExtension(slide.Asset); + res.CopyExtension = Path.GetExtension(slide.Asset); + res.AssetPath = slide.Asset; + return res; + } + + + + + + } +} diff --git a/Xenon/Renderer/HymnTextVerseRenderer.cs b/Xenon/Renderer/HymnTextVerseRenderer.cs index 7f9cefb5..617c505f 100644 --- a/Xenon/Renderer/HymnTextVerseRenderer.cs +++ b/Xenon/Renderer/HymnTextVerseRenderer.cs @@ -17,9 +17,12 @@ public RenderedSlide RenderSlide(HymnTextVerseRenderInfo renderInfo, Slide slide res.RenderedAs = "Full"; Bitmap bmp = new Bitmap(Layouts.TextHymnLayout.Size.Width, Layouts.TextHymnLayout.Size.Height); + Bitmap kbmp = new Bitmap(Layouts.TextHymnLayout.Size.Width, Layouts.TextHymnLayout.Size.Height); Graphics gfx = Graphics.FromImage(bmp); + Graphics kgfx = Graphics.FromImage(kbmp); gfx.Clear(Color.White); + kgfx.Clear(Color.White); // debug @@ -63,6 +66,7 @@ public RenderedSlide RenderSlide(HymnTextVerseRenderInfo renderInfo, Slide slide res.Bitmap = bmp; + res.KeyBitmap = kbmp; return res; } diff --git a/Xenon/Renderer/ImageSlideRenderer.cs b/Xenon/Renderer/ImageSlideRenderer.cs index fa0a943f..519b3ad0 100644 --- a/Xenon/Renderer/ImageSlideRenderer.cs +++ b/Xenon/Renderer/ImageSlideRenderer.cs @@ -6,6 +6,7 @@ using System.Text; using Xenon.Helpers; + namespace Xenon.Renderer { class ImageSlideRenderer @@ -19,6 +20,11 @@ public RenderedSlide RenderImageSlide(Slide slide, List<Compiler.XenonCompilerMe res.MediaType = MediaType.Image; res.AssetPath = slide.Asset; + Bitmap kbmp = new Bitmap(1920, 1080); + Graphics kgfx = Graphics.FromImage(kbmp); + kgfx.Clear(Color.White); + res.KeyBitmap = kbmp; + Bitmap sourceimage; try { @@ -75,6 +81,7 @@ public RenderedSlide RenderImageSlide(Slide slide, List<Compiler.XenonCompilerMe else if (slide.Format == SlideFormat.LiturgyImage) { res.Bitmap = RenderLiturgyImage(sourceimage); + res.KeyBitmap = RenderLiturgyImageKey(sourceimage, slide.Colors["keytrans"]); res.RenderedAs = "Liturgy"; } @@ -167,7 +174,7 @@ will use 93% space Bitmap img = sourceimage; if (invertblackandwhite) { - img = InvertImage(sourceimage); + img = GraphicsHelper.InvertImage(sourceimage); } gfx.DrawImage(img, new Rectangle(p, s), leftbound, topbound, (rightbound - leftbound), (bottombound - topbound), GraphicsUnit.Pixel); @@ -202,30 +209,46 @@ private Bitmap RenderLiturgyImage(Bitmap sourceimage) gfx.Clear(System.Drawing.Color.Gray); gfx.FillRectangle(System.Drawing.Brushes.Black, Layout.LiturgyLayout.Key); - gfx.DrawImage(InvertImage(trimmed), new Rectangle(p, s), new Rectangle(new Point(0, 0), trimmed.Size), GraphicsUnit.Pixel); + gfx.DrawImage(GraphicsHelper.InvertImage(trimmed), new Rectangle(p, s), new Rectangle(new Point(0, 0), trimmed.Size), GraphicsUnit.Pixel); return bmp; } - private Bitmap InvertImage(Bitmap source) + private Bitmap RenderLiturgyImageKey(Bitmap sourceimage, Color alpha) { - /* - https://stackoverflow.com/questions/33024881/invert-image-faster-in-c-sharp - */ - Bitmap res = new Bitmap(source); + Bitmap bmp = new Bitmap(Layout.LiturgyLayout.Size.Width, Layout.LiturgyLayout.Size.Height); + + Graphics gfx = Graphics.FromImage(bmp); + + double scale = 1; + Point p; + Size s; + + double xscale = 1; + double yscale = 1; + + double fillsize = 0.93; + + Bitmap trimmed = sourceimage.TrimBitmap(Color.White); + + xscale = (double)(Layout.LiturgyLayout.Key.Width * fillsize) / trimmed.Width; + yscale = (double)(Layout.LiturgyLayout.Key.Height * fillsize) / trimmed.Height; + + scale = xscale < yscale ? xscale : yscale; + + s = new Size((int)(trimmed.Width * scale), (int)(trimmed.Height * scale)); + p = new Point((Layout.LiturgyLayout.Key.Width - s.Width) / 2, (Layout.LiturgyLayout.Key.Height - s.Height) / 2).Add(Layout.LiturgyLayout.Key.Location); + + gfx.Clear(System.Drawing.Color.Black); + gfx.FillRectangle(new SolidBrush(alpha), Layout.LiturgyLayout.Key); + gfx.DrawImage(trimmed.DichotimizeImage(Color.Black, 150, Color.White).InvertImage().SwapImageColors(Color.Black, alpha), new Rectangle(p, s), new Rectangle(new Point(0, 0), trimmed.Size), GraphicsUnit.Pixel); + return bmp; - for (int y = 0; y < res.Height; y++) - { - for (int x = 0; x < res.Width; x++) - { - Color inv = res.GetPixel(x, y); - inv = Color.FromArgb(255, 255 - inv.R, 255 - inv.G, 255 - inv.B); - res.SetPixel(x, y, inv); - } - } - return res; } + + + } } diff --git a/Xenon/Renderer/LiturgySlideRenderer.cs b/Xenon/Renderer/LiturgySlideRenderer.cs index d17787b5..70964bc1 100644 --- a/Xenon/Renderer/LiturgySlideRenderer.cs +++ b/Xenon/Renderer/LiturgySlideRenderer.cs @@ -35,17 +35,25 @@ public RenderedSlide RenderSlide(LiturgyLayoutRenderInfo renderInfo, Slide slide // for now just draw the layout Bitmap bmp = new Bitmap(Layouts.LiturgyLayout.Size.Width, Layouts.LiturgyLayout.Size.Height); + Bitmap kbmp = new Bitmap(Layouts.LiturgyLayout.Size.Width, Layouts.LiturgyLayout.Size.Height); Graphics gfx = Graphics.FromImage(bmp); + Graphics kgfx = Graphics.FromImage(kbmp); gfx.Clear(slide.Colors["background"]); + // most of slide should be transparent + kgfx.Clear(Color.Black); SolidBrush textcol = new SolidBrush(slide.Colors["text"]); SolidBrush speakercol = new SolidBrush(slide.Colors["alttext"]); SolidBrush sb = new SolidBrush(slide.Colors["keybackground"]); + SolidBrush kb = new SolidBrush(slide.Colors["keytrans"]); + gfx.FillRectangle(sb, Layouts.LiturgyLayout.Key); + // make black somewhat transparent + kgfx.FillRectangle(kb, Layouts.LiturgyLayout.Key); StringFormat topleftalign = new StringFormat() { LineAlignment = StringAlignment.Near, Alignment = StringAlignment.Near }; StringFormat centeralign = new StringFormat() { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center }; @@ -95,6 +103,8 @@ public RenderedSlide RenderSlide(LiturgyLayoutRenderInfo renderInfo, Slide slide { float jog = 0.07f * (gfx.DpiY * gfx.MeasureStringCharacters(linewords.Speaker, flsbregular, speakerblock).Height / 72); gfx.DrawString(linewords.Speaker, flsbregular, speakercol, speakerblock.Move(0, -jog), centeralign); + // make speaker fully opaque + kgfx.DrawString(linewords.Speaker, flsbregular, Brushes.White, speakerblock.Move(0, -jog), centeralign); } @@ -103,6 +113,8 @@ public RenderedSlide RenderSlide(LiturgyLayoutRenderInfo renderInfo, Slide slide Font f = word.IsLSBSymbol ? (word.IsBold ? flsbbold : flsbregular) : (word.IsBold ? fbold : fregular); float jog = word.IsLSBSymbol ? 0.07f * (gfx.DpiY * gfx.MeasureStringCharacters(linewords.Speaker, f, speakerblock).Height / 72) : 0; gfx.DrawString(word.Value, f, textcol, text.Move(xoffset, linepos + interspace * linenum).Move(0, -jog).Location, GraphicsHelper.DefaultStringFormat()); + // make text fully opaque + kgfx.DrawString(word.Value, f, Brushes.White, text.Move(xoffset, linepos + interspace * linenum).Move(0, -jog).Location, GraphicsHelper.DefaultStringFormat()); xoffset += word.Size.Width; } @@ -116,6 +128,7 @@ public RenderedSlide RenderSlide(LiturgyLayoutRenderInfo renderInfo, Slide slide res.Bitmap = bmp; + res.KeyBitmap = kbmp; return res; } diff --git a/Xenon/Renderer/LiturgyVerseSlideRenderer.cs b/Xenon/Renderer/LiturgyVerseSlideRenderer.cs index 4cf2e5db..eb6de813 100644 --- a/Xenon/Renderer/LiturgyVerseSlideRenderer.cs +++ b/Xenon/Renderer/LiturgyVerseSlideRenderer.cs @@ -28,14 +28,18 @@ public RenderedSlide RenderSlide(LiturgyLayoutRenderInfo renderInfo, Slide slide // for now just draw the layout Bitmap bmp = new Bitmap(Layouts.LiturgyLayout.Size.Width, Layouts.LiturgyLayout.Size.Height); + Bitmap kbmp = new Bitmap(Layouts.LiturgyLayout.Size.Width, Layouts.LiturgyLayout.Size.Height); Graphics gfx = Graphics.FromImage(bmp); + Graphics kgfx = Graphics.FromImage(kbmp); Font LSBSymbolFont = new System.Drawing.Font("LSBSymbol", 36, System.Drawing.FontStyle.Regular); gfx.Clear(Color.Black); + kgfx.Clear(Color.Black); gfx.FillRectangle(Brushes.White, Layouts.LiturgyLayout.Key); + kgfx.FillRectangle(Brushes.White, Layouts.LiturgyLayout.Key); StringFormat topleftalign = new StringFormat() { LineAlignment = StringAlignment.Near, Alignment = StringAlignment.Near }; StringFormat centeralign = new StringFormat() { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center }; diff --git a/Xenon/Renderer/PrefabSlideRenderer.cs b/Xenon/Renderer/PrefabSlideRenderer.cs index aff81c1c..3262bf9c 100644 --- a/Xenon/Renderer/PrefabSlideRenderer.cs +++ b/Xenon/Renderer/PrefabSlideRenderer.cs @@ -23,9 +23,14 @@ public RenderedSlide RenderSlide(Slide slide, List<XenonCompilerMessage> message res.RenderedAs = "Full"; Bitmap bmp = new Bitmap(Layouts.PrefabLayout.Size.Width, Layouts.PrefabLayout.Size.Height); + Bitmap kbmp = new Bitmap(Layouts.PrefabLayout.Size.Width, Layouts.PrefabLayout.Size.Height); Graphics gfx = Graphics.FromImage(bmp); + Graphics kgfx = Graphics.FromImage(kbmp); gfx.Clear(Color.White); + kgfx.Clear(Color.White); + + res.KeyBitmap = kbmp; Bitmap src = null; diff --git a/Xenon/Renderer/ReadingSlideRenderer.cs b/Xenon/Renderer/ReadingSlideRenderer.cs index 1512fa83..8a6debf6 100644 --- a/Xenon/Renderer/ReadingSlideRenderer.cs +++ b/Xenon/Renderer/ReadingSlideRenderer.cs @@ -20,21 +20,28 @@ public RenderedSlide RenderSlide(ReadingLayoutRenderInfo renderInfo, Slide slide res.RenderedAs = "Liturgy"; Bitmap bmp = new Bitmap(Layouts.ReadingLayout.Size.Width, Layouts.ReadingLayout.Size.Height); + Bitmap kbmp = new Bitmap(Layouts.ReadingLayout.Size.Width, Layouts.ReadingLayout.Size.Height); Graphics gfx = Graphics.FromImage(bmp); + Graphics kgfx = Graphics.FromImage(kbmp); gfx.Clear(Color.Gray); + kgfx.Clear(Color.Black); gfx.FillRectangle(Brushes.Black, Layouts.ReadingLayout.Key); + kgfx.FillRectangle(new SolidBrush(slide.Colors["keytrans"]), Layouts.ReadingLayout.Key); // put name in center left gfx.DrawString(slide.Lines[0].Content[0].Data, renderInfo.RegularFont, Brushes.White, Layouts.ReadingLayout.TextAera.Move(Layouts.ReadingLayout.Key.Location).Move(150, 0), GraphicsHelper.LeftVerticalCenterAlign); + kgfx.DrawString(slide.Lines[0].Content[0].Data, renderInfo.RegularFont, Brushes.White, Layouts.ReadingLayout.TextAera.Move(Layouts.ReadingLayout.Key.Location).Move(150, 0), GraphicsHelper.LeftVerticalCenterAlign); // put reference in center right gfx.DrawString(slide.Lines[1].Content[0].Data, renderInfo.ItalicFont, Brushes.White, Layouts.ReadingLayout.TextAera.Move(Layouts.ReadingLayout.Key.Location).Move(-180, 0), GraphicsHelper.RightVerticalCenterAlign); + kgfx.DrawString(slide.Lines[1].Content[0].Data, renderInfo.ItalicFont, Brushes.White, Layouts.ReadingLayout.TextAera.Move(Layouts.ReadingLayout.Key.Location).Move(-180, 0), GraphicsHelper.RightVerticalCenterAlign); res.Bitmap = bmp; + res.KeyBitmap = kbmp; return res; } diff --git a/Xenon/Renderer/RenderedSlide.cs b/Xenon/Renderer/RenderedSlide.cs index dfed9a1c..3c7d0741 100644 --- a/Xenon/Renderer/RenderedSlide.cs +++ b/Xenon/Renderer/RenderedSlide.cs @@ -11,12 +11,16 @@ public class RenderedSlide public MediaType MediaType { get; set; } public string AssetPath { get; set; } public string RenderedAs { get; set; } + public string Name { get; set; } + public string CopyExtension { get; set; } + public string Text { get; set; } public Bitmap Bitmap {get; set;} + public Bitmap KeyBitmap {get; set;} public int Number { get; set; } public static RenderedSlide Default() { - return new RenderedSlide() { MediaType = MediaType.Empty, AssetPath = "", RenderedAs = "Default", Number = 0 }; + return new RenderedSlide() { MediaType = MediaType.Empty, AssetPath = "", CopyExtension = "", Text = "", Name = "", RenderedAs = "Default", Number = 0 }; } } } diff --git a/Xenon/Renderer/ScriptRenderer.cs b/Xenon/Renderer/ScriptRenderer.cs new file mode 100644 index 00000000..2f46d7f6 --- /dev/null +++ b/Xenon/Renderer/ScriptRenderer.cs @@ -0,0 +1,30 @@ +using Xenon.LayoutEngine; +using Xenon.SlideAssembly; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Text; +using Xenon.Helpers; + +namespace Xenon.Renderer +{ + class ScriptRenderer + { + public SlideLayout Layouts { get; set; } + + public RenderedSlide RenderSlide(Slide slide, List<Compiler.XenonCompilerMessage> messages) + { + RenderedSlide res = new RenderedSlide(); + res.MediaType = MediaType.Text; + res.AssetPath = ""; + res.RenderedAs = "Action"; + res.Text = (string)slide.Data["source"]; + return res; + } + + + + + + } +} diff --git a/Xenon/Renderer/SermonTitleSlideRenderer.cs b/Xenon/Renderer/SermonTitleSlideRenderer.cs index 08f331e9..3c94b74e 100644 --- a/Xenon/Renderer/SermonTitleSlideRenderer.cs +++ b/Xenon/Renderer/SermonTitleSlideRenderer.cs @@ -20,19 +20,27 @@ public RenderedSlide RenderSlide(SermonLayoutRenderInfo renderInfo, Slide slide, res.RenderedAs = "Liturgy-t1restart"; Bitmap bmp = new Bitmap(Layouts.SermonLayout.Size.Width, Layouts.SermonLayout.Size.Height); + Bitmap kbmp = new Bitmap(Layouts.SermonLayout.Size.Width, Layouts.SermonLayout.Size.Height); Graphics gfx = Graphics.FromImage(bmp); + Graphics kgfx = Graphics.FromImage(kbmp); gfx.Clear(Color.Gray); + kgfx.Clear(Color.Black); gfx.FillRectangle(Brushes.Black, Layouts.SermonLayout.Key); + kgfx.FillRectangle(new SolidBrush(slide.Colors["keytrans"]), Layouts.SermonLayout.Key); // put 'preacher' in top left gfx.DrawString(slide.Lines[2].Content[0].Data, renderInfo.RegularFont, Brushes.White, Layouts.SermonLayout.TopLine.Move(Layouts.SermonLayout.TextAera.Location).Move(Layouts.SermonLayout.Key.Location).Move(100, 0), GraphicsHelper.LeftVerticalCenterAlign); + kgfx.DrawString(slide.Lines[2].Content[0].Data, renderInfo.RegularFont, Brushes.White, Layouts.SermonLayout.TopLine.Move(Layouts.SermonLayout.TextAera.Location).Move(Layouts.SermonLayout.Key.Location).Move(100, 0), GraphicsHelper.LeftVerticalCenterAlign); // put reference in top right gfx.DrawString(slide.Lines[1].Content[0].Data, renderInfo.ItalicFont, Brushes.White, Layouts.SermonLayout.TopLine.Move(Layouts.SermonLayout.TextAera.Location).Move(Layouts.SermonLayout.Key.Location).Move(-100, 0), GraphicsHelper.RightVerticalCenterAlign); + kgfx.DrawString(slide.Lines[1].Content[0].Data, renderInfo.ItalicFont, Brushes.White, Layouts.SermonLayout.TopLine.Move(Layouts.SermonLayout.TextAera.Location).Move(Layouts.SermonLayout.Key.Location).Move(-100, 0), GraphicsHelper.RightVerticalCenterAlign); // put title in center mid gfx.DrawString(slide.Lines[0].Content[0].Data, renderInfo.BoldFont, Brushes.White, Layouts.SermonLayout.MainLine.Move(Layouts.SermonLayout.TextAera.Location).Move(Layouts.SermonLayout.Key.Location), GraphicsHelper.CenterAlign); + kgfx.DrawString(slide.Lines[0].Content[0].Data, renderInfo.BoldFont, Brushes.White, Layouts.SermonLayout.MainLine.Move(Layouts.SermonLayout.TextAera.Location).Move(Layouts.SermonLayout.Key.Location), GraphicsHelper.CenterAlign); res.Bitmap = bmp; + res.KeyBitmap = kbmp; return res; } } diff --git a/Xenon/Renderer/SlideExporter.cs b/Xenon/Renderer/SlideExporter.cs index 056dfb97..8ca1513a 100644 --- a/Xenon/Renderer/SlideExporter.cs +++ b/Xenon/Renderer/SlideExporter.cs @@ -16,17 +16,38 @@ public static void ExportSlides(string directory, Project proj, List<XenonCompil { SlideRenderer slideRenderer = new SlideRenderer(proj); // render the slide - RenderedSlide rs = slideRenderer.RenderSlide(slide.Number, messages); + RenderedSlide rs = slideRenderer.RenderSlide(slide, messages); + + if (rs.RenderedAs == "Resource") + { + // for now only allow audio files to be rendered as resource + string filename = Path.Join(directory, $"Resource_{rs.Name}{rs.CopyExtension}"); + File.Copy(rs.AssetPath, filename, true); + continue; + } if (rs.MediaType == MediaType.Image) { string filename = Path.Join(directory, $"{slide.Number}_{rs.RenderedAs}.png"); + string kfilename = Path.Join(directory, $"Key_{slide.Number}.png"); rs.Bitmap.Save(filename, ImageFormat.Png); + rs.KeyBitmap.Save(kfilename, ImageFormat.Png); } else if (rs.MediaType == MediaType.Video) { string filename = Path.Join(directory, $"{slide.Number}_{rs.RenderedAs}.mp4"); + string kfilename = Path.Join(directory, $"Key_{slide.Number}.png"); File.Copy(rs.AssetPath, filename); + rs.KeyBitmap.Save(kfilename, ImageFormat.Png); + } + else if (rs.MediaType == MediaType.Text) + { + // output text file + string filename = Path.Join(directory, $"{slide.Number}_{rs.RenderedAs}.txt"); + using (StreamWriter sw = new StreamWriter(filename, false)) + { + sw.Write(rs.Text); + } } } diff --git a/Xenon/Renderer/SlideRenderer.cs b/Xenon/Renderer/SlideRenderer.cs index 83709680..41f3092a 100644 --- a/Xenon/Renderer/SlideRenderer.cs +++ b/Xenon/Renderer/SlideRenderer.cs @@ -24,6 +24,8 @@ class SlideRenderer AnthemTitleSlideRenderer atsr = new AnthemTitleSlideRenderer(); TwoPartTitleSlideRenderer tpsr = new TwoPartTitleSlideRenderer(); TitledLiturgyVerseSlideRenderer tlvsr = new TitledLiturgyVerseSlideRenderer(); + CopySlideRenderer csr = new CopySlideRenderer(); + ScriptRenderer sr = new ScriptRenderer(); public SlideRenderer(Project proj) { @@ -40,19 +42,14 @@ public SlideRenderer(Project proj) tlvsr.Layouts = proj.Layouts; } - public RenderedSlide RenderSlide(int slidenum, List<XenonCompilerMessage> Messages) + public RenderedSlide RenderSlide(Slide s, List<XenonCompilerMessage> Messages) { - if (slidenum >= _project.Slides.Count) - { - throw new ArgumentOutOfRangeException("slidenum"); - //return RenderedSlide.Default(); - } - var s = RenderSlide(_project.Slides[slidenum], Messages); - s.Number = slidenum; - return s; + var res = _RenderSlide(s, Messages); + res.Number = s.Number; + return res; } - public RenderedSlide RenderSlide(Slide slide, List<XenonCompilerMessage> Messages) + private RenderedSlide _RenderSlide(Slide slide, List<XenonCompilerMessage> Messages) { // use an appropriate slide render for the task switch (slide.Format) @@ -85,6 +82,10 @@ public RenderedSlide RenderSlide(Slide slide, List<XenonCompilerMessage> Message return hvsr.RenderSlide(_project.Layouts.TextHymnLayout.GetRenderInfo(), slide, Messages); case SlideFormat.Prefab: return psr.RenderSlide(slide, Messages); + case SlideFormat.Script: + return sr.RenderSlide(slide, Messages); + case SlideFormat.ResourceCopy: + return csr.RenderSlide(slide, Messages); default: return RenderedSlide.Default(); } diff --git a/Xenon/Renderer/TitledLiturgyVerseSlideRenderer.cs b/Xenon/Renderer/TitledLiturgyVerseSlideRenderer.cs index 94594d35..db6e06f3 100644 --- a/Xenon/Renderer/TitledLiturgyVerseSlideRenderer.cs +++ b/Xenon/Renderer/TitledLiturgyVerseSlideRenderer.cs @@ -34,12 +34,16 @@ public RenderedSlide RenderSlide(Slide slide, List<XenonCompilerMessage> message // for now just draw the layout Bitmap bmp = new Bitmap(Layouts.TitleLiturgyVerseLayout.Size.Width, Layouts.TitleLiturgyVerseLayout.Size.Height); + Bitmap kbmp = new Bitmap(Layouts.TitleLiturgyVerseLayout.Size.Width, Layouts.TitleLiturgyVerseLayout.Size.Height); Graphics gfx = Graphics.FromImage(bmp); + Graphics kgfx = Graphics.FromImage(kbmp); gfx.Clear(Color.Gray); + kgfx.Clear(Color.Black); gfx.FillRectangle(Brushes.Black, Layouts.TitleLiturgyVerseLayout.Key); + kgfx.FillRectangle(new SolidBrush(slide.Colors["keytrans"]), Layouts.TitleLiturgyVerseLayout.Key); List<LiturgyTextLine> Lines = (List<LiturgyTextLine>)slide.Data["lines"]; @@ -47,7 +51,9 @@ public RenderedSlide RenderSlide(Slide slide, List<XenonCompilerMessage> message Font itf = new Font(Layouts.TitleLiturgyVerseLayout.Font, FontStyle.Italic); gfx.DrawString((string)slide.Data["title"], bf, Brushes.White, Layouts.TitleLiturgyVerseLayout.TitleLine.Move(Layouts.TitleLiturgyVerseLayout.Key.Location), GraphicsHelper.LeftVerticalCenterAlign); + kgfx.DrawString((string)slide.Data["title"], bf, Brushes.White, Layouts.TitleLiturgyVerseLayout.TitleLine.Move(Layouts.TitleLiturgyVerseLayout.Key.Location), GraphicsHelper.LeftVerticalCenterAlign); gfx.DrawString((string)slide.Data["reference"], itf, Brushes.White, Layouts.TitleLiturgyVerseLayout.TitleLine.Move(Layouts.TitleLiturgyVerseLayout.Key.Location), GraphicsHelper.RightVerticalCenterAlign); + kgfx.DrawString((string)slide.Data["reference"], itf, Brushes.White, Layouts.TitleLiturgyVerseLayout.TitleLine.Move(Layouts.TitleLiturgyVerseLayout.Key.Location), GraphicsHelper.RightVerticalCenterAlign); float alltextheight = 0; foreach (var line in Lines) @@ -77,6 +83,7 @@ public RenderedSlide RenderSlide(Slide slide, List<XenonCompilerMessage> message float jog = 0.07f * (gfx.DpiY * speakersize.Height / 72); xoffset = (Layouts.TitleLiturgyVerseLayout.Textbox.Width / 2) - ((line.Width) + speakersize.Width) / 2; gfx.DrawString(line.Speaker + " ", flsbregular, Brushes.Teal, Layouts.TitleLiturgyVerseLayout.Textbox.Move(Layouts.TitleLiturgyVerseLayout.Key.Location).Move((int)xoffset, (int)(vspace + interspace * linenum)).Move(0, (int)-jog).Location, GraphicsHelper.DefaultStringFormat()); + kgfx.DrawString(line.Speaker + " ", flsbregular, Brushes.White, Layouts.TitleLiturgyVerseLayout.Textbox.Move(Layouts.TitleLiturgyVerseLayout.Key.Location).Move((int)xoffset, (int)(vspace + interspace * linenum)).Move(0, (int)-jog).Location, GraphicsHelper.DefaultStringFormat()); xoffset += speakersize.Width; } @@ -88,6 +95,7 @@ public RenderedSlide RenderSlide(Slide slide, List<XenonCompilerMessage> message { Font f = word.IsLSBSymbol ? (word.IsBold ? flsbbold : flsbregular) : (word.IsBold ? fbold : fregular); gfx.DrawString(word.Value, f, Brushes.White, Layouts.TitleLiturgyVerseLayout.Textbox.Move(Layouts.TitleLiturgyVerseLayout.Key.Location).Move((int)xoffset, (int)(vspace + interspace * linenum)).Location, GraphicsHelper.DefaultStringFormat()); + kgfx.DrawString(word.Value, f, Brushes.White, Layouts.TitleLiturgyVerseLayout.Textbox.Move(Layouts.TitleLiturgyVerseLayout.Key.Location).Move((int)xoffset, (int)(vspace + interspace * linenum)).Location, GraphicsHelper.DefaultStringFormat()); xoffset += word.Size.Width; } linenum++; @@ -95,6 +103,7 @@ public RenderedSlide RenderSlide(Slide slide, List<XenonCompilerMessage> message } res.Bitmap = bmp; + res.KeyBitmap = kbmp; return res; } diff --git a/Xenon/Renderer/TwoPartTitleSlideRenderer.cs b/Xenon/Renderer/TwoPartTitleSlideRenderer.cs index 193f1ece..e1931b16 100644 --- a/Xenon/Renderer/TwoPartTitleSlideRenderer.cs +++ b/Xenon/Renderer/TwoPartTitleSlideRenderer.cs @@ -19,10 +19,14 @@ public RenderedSlide RenderSlide(Slide slide, List<Compiler.XenonCompilerMessage res.RenderedAs = "Liturgy"; Bitmap bmp = new Bitmap(Layouts.TwoPartTitleLayout.Size.Width, Layouts.TwoPartTitleLayout.Size.Height); + Bitmap kbmp = new Bitmap(Layouts.TwoPartTitleLayout.Size.Width, Layouts.TwoPartTitleLayout.Size.Height); Graphics gfx = Graphics.FromImage(bmp); + Graphics kgfx = Graphics.FromImage(kbmp); gfx.Clear(Color.Gray); + kgfx.Clear(Color.Black); gfx.FillRectangle(Brushes.Black, Layouts.TwoPartTitleLayout.Key); + kgfx.FillRectangle(new SolidBrush(slide.Colors["keytrans"]), Layouts.TwoPartTitleLayout.Key); Font bf = new Font(Layouts.TwoPartTitleLayout.Font, FontStyle.Bold); @@ -31,17 +35,22 @@ public RenderedSlide RenderSlide(Slide slide, List<Compiler.XenonCompilerMessage if ((string)slide.Data["orientation"] == "vertical") { gfx.DrawString(slide.Lines[0].Content[0].Data, bf, Brushes.White, Layouts.TwoPartTitleLayout.Line1.Move(Layouts.TwoPartTitleLayout.Key.Location), GraphicsHelper.CenterAlign); + kgfx.DrawString(slide.Lines[0].Content[0].Data, bf, Brushes.White, Layouts.TwoPartTitleLayout.Line1.Move(Layouts.TwoPartTitleLayout.Key.Location), GraphicsHelper.CenterAlign); gfx.DrawString(slide.Lines[1].Content[0].Data, Layouts.TwoPartTitleLayout.Font, Brushes.White, Layouts.TwoPartTitleLayout.Line2.Move(Layouts.TwoPartTitleLayout.Key.Location), GraphicsHelper.CenterAlign); + kgfx.DrawString(slide.Lines[1].Content[0].Data, Layouts.TwoPartTitleLayout.Font, Brushes.White, Layouts.TwoPartTitleLayout.Line2.Move(Layouts.TwoPartTitleLayout.Key.Location), GraphicsHelper.CenterAlign); } else { int ycord = (int)((Layouts.TwoPartTitleLayout.Key.Height / 2) - (lineheight.Height / 2)); gfx.DrawString(slide.Lines[0].Content[0].Data, bf, Brushes.White, Layouts.TwoPartTitleLayout.MainLine.Move(Layouts.TwoPartTitleLayout.Key.Location).Move(0, ycord), GraphicsHelper.LeftVerticalCenterAlign); + kgfx.DrawString(slide.Lines[0].Content[0].Data, bf, Brushes.White, Layouts.TwoPartTitleLayout.MainLine.Move(Layouts.TwoPartTitleLayout.Key.Location).Move(0, ycord), GraphicsHelper.LeftVerticalCenterAlign); gfx.DrawString(slide.Lines[1].Content[0].Data, Layouts.TwoPartTitleLayout.Font, Brushes.White, Layouts.TwoPartTitleLayout.MainLine.Move(Layouts.TwoPartTitleLayout.Key.Location).Move(0, ycord), GraphicsHelper.RightVerticalCenterAlign); + kgfx.DrawString(slide.Lines[1].Content[0].Data, Layouts.TwoPartTitleLayout.Font, Brushes.White, Layouts.TwoPartTitleLayout.MainLine.Move(Layouts.TwoPartTitleLayout.Key.Location).Move(0, ycord), GraphicsHelper.RightVerticalCenterAlign); } res.Bitmap = bmp; + res.KeyBitmap = kbmp; return res; } } diff --git a/Xenon/Renderer/VideoSlideRenderer.cs b/Xenon/Renderer/VideoSlideRenderer.cs index 86f9655f..c64dce46 100644 --- a/Xenon/Renderer/VideoSlideRenderer.cs +++ b/Xenon/Renderer/VideoSlideRenderer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Linq; using System.Text; @@ -30,6 +31,11 @@ public RenderedSlide RenderSlide(SlideAssembly.Slide slide, List<Compiler.XenonC messages.Add(new Compiler.XenonCompilerMessage() { ErrorMessage = $"Could not find file {res.AssetPath}", ErrorName = "Missing Video File", Level = Compiler.XenonCompilerMessageType.Error }); throw new FileNotFoundException(); } + + res.KeyBitmap = new Bitmap(1920, 1080); + Graphics gfx = Graphics.FromImage(res.KeyBitmap); + // for now videos are fully transparent all the time + gfx.Clear(Color.White); return res; } } diff --git a/Xenon/SlideAssembly/MediaType.cs b/Xenon/SlideAssembly/MediaType.cs index f11311d1..67732003 100644 --- a/Xenon/SlideAssembly/MediaType.cs +++ b/Xenon/SlideAssembly/MediaType.cs @@ -5,5 +5,7 @@ public enum MediaType Video, Image, Empty, + Text, + Audio, } } \ No newline at end of file diff --git a/Xenon/SlideAssembly/Slide.cs b/Xenon/SlideAssembly/Slide.cs index 48610ad3..75e0c8b8 100644 --- a/Xenon/SlideAssembly/Slide.cs +++ b/Xenon/SlideAssembly/Slide.cs @@ -27,6 +27,7 @@ public Slide() Colors.Add("alttext", Color.Teal); Colors.Add("background", Color.Gray); Colors.Add("keybackground", Color.Black); + Colors.Add("keytrans", Color.Gray); } } diff --git a/Xenon/SlideAssembly/SlideFormat.cs b/Xenon/SlideAssembly/SlideFormat.cs index 39889e23..44dc487e 100644 --- a/Xenon/SlideAssembly/SlideFormat.cs +++ b/Xenon/SlideAssembly/SlideFormat.cs @@ -22,6 +22,9 @@ public enum SlideFormat HymnTextVerse, Prefab, + Script, + ResourceCopy, + } }