/// <summary> /// Play previous Midi in list /// </summary> public virtual void MPTK_Previous() { try { if (MidiPlayerGlobal.CurrentMidiSet.MidiFiles != null && MidiPlayerGlobal.CurrentMidiSet.MidiFiles.Count > 0) { int selectedMidi = 0; if (!string.IsNullOrEmpty(MPTK_MidiName)) { selectedMidi = MidiPlayerGlobal.CurrentMidiSet.MidiFiles.FindIndex(s => s == MPTK_MidiName); } if (selectedMidi >= 0) { selectedMidi--; if (selectedMidi < 0) { selectedMidi = MidiPlayerGlobal.CurrentMidiSet.MidiFiles.Count - 1; } MPTK_MidiName = MidiPlayerGlobal.CurrentMidiSet.MidiFiles[selectedMidi]; MPTK_RePlay(); } } else { Debug.LogWarning("MidiFilePlayer - no Midi defined, go to menu 'Tools/MPTK - Midi File Setup' or alt-m"); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
/// <summary> /// Remove AudioSource not playing /// </summary> protected IEnumerator DestroyAllAudioSource() { try { AudioSource[] audios = GetComponentsInChildren <AudioSource>(); if (audios != null) { //int i = 0; foreach (AudioSource audio in audios) { try { //Debug.Log("Destroy " + i++ + " " + audio.name + " " + (audio.clip != null ? audio.clip.name : "no clip")); // Don't delete audio source template, the only one without a clip if (audio.clip != null) { Destroy(audio.gameObject); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } } } audiosources = new List <AudioSource>(); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } yield return(0); }
new void Start() { try { base.Start(); DestroyAllAudioSource(); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
/// <summary> /// Pause the current playing /// </summary> public virtual void MPTK_Pause(float timeToPauseMS = -1f) { try { timeToPauseMilliSeconde = timeToPauseMS; playPause = true; MPTK_ClearAllSound(); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
protected virtual void Awake() { try { HelperNoteLabel.Init(); //MidiPlayerGlobal.Init(); audiosources = new List <AudioSource>(); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
/// <summary> /// Play a list of notes with a thread (Coriutine). Return immediately. /// If you want be able to stop note, use rather the Methods Play and Stop of MPTKNote class /// </summary> public virtual void MPTK_Play(List <MPTKNote> notes) { try { if (MidiPlayerGlobal.SoundFontLoaded) { StartCoroutine(TheadPlay(notes)); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
static public void BuildDrumList() { List <string> drums = new List <string>(); try { //Debug.Log(">>> Load Preset - b:" + ibank + " p:" + ipatch); if (ImSFCurrent != null && CurrentMidiSet != null) { if (ImSFCurrent.DrumKitBankNumber >= 0 && ImSFCurrent.DrumKitBankNumber < ImSFCurrent.Banks.Length) { int ibank = ImSFCurrent.DrumKitBankNumber; if (ImSFCurrent.Banks[ibank] != null && ImSFCurrent.Banks[ibank].Presets[0] != null) { for (int key = 0; key < 127; key++) { string waveName; ImSample sample = GetImSample(ibank, 0, key, 127); if (sample != null) { waveName = Path.GetFileNameWithoutExtension(sample.WaveFile); } else { waveName = ""; } drums.Add(waveName); } } else { //Debug.LogWarning("Bank don't exists."); } } else { //Debug.LogWarning("No bank defined for instrument."); } } else { Debug.LogWarning("BuildDrumList: No MidiSet defined, go to menu 'Tools/MPTK - Midi File Setup' or alt-m"); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } listDrum = drums.AsReadOnly(); }
/// <summary> /// Stop playing /// </summary> public virtual void MPTK_Stop() { try { midiIsPlaying = false; playPause = false; stopMidiToPlay = true; MPTK_ClearAllSound(); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
/// <summary> /// Build list of presets found in the SoundFont /// </summary> static public void BuildPresetList() { List <string> presets = new List <string>(); try { //Debug.Log(">>> Load Preset - b:" + ibank + " p:" + ipatch); if (ImSFCurrent != null && CurrentMidiSet != null) { if (ImSFCurrent.DefaultBankNumber >= 0 && ImSFCurrent.DefaultBankNumber < ImSFCurrent.Banks.Length) { int ibank = ImSFCurrent.DefaultBankNumber; if (ImSFCurrent.Banks[ibank] != null) { for (int ipreset = 0; ipreset < ImSFCurrent.Banks[ibank].Presets.Length; ipreset++) { string presetName; if (ImSFCurrent.Banks[ibank].Presets[ipreset] != null) { presetName = ImSFCurrent.Banks[ibank].Presets[ipreset].Name; } else { presetName = ""; } presets.Add(presetName); } } else { Debug.LogWarning("BuildPresetList: Bank don't exists."); } } else { Debug.LogWarning("BuildPresetList: No bank defined for instrument."); } } else { Debug.LogWarning("BuildPresetList: No MidiSet defined, go to menu 'Tools/MPTK - Midi File Setup' or alt-m"); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } listPreset = presets.AsReadOnly(); }
protected virtual void Start() { try { AudioSourceTemplate = GetComponentInChildren <AudioSource>(); if (AudioSourceTemplate == null) { Debug.LogWarning("No AudioSource found."); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
new void Start() { //Debug.Log("Start midiIsPlaying:" + midiIsPlaying); base.Start(); try { if (MPTK_PlayOnStart) { StartCoroutine(TheadPlayIfReady()); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
public static List <ImSample> GetImMultiSample(int idxbank, int idxpreset, int key, int vel) { List <ImSample> samples = new List <ImSample>(); try { if (ImSFCurrent.Banks[idxbank] != null) { ImPreset preset = ImSFCurrent.Banks[idxbank].Presets[idxpreset]; if (preset != null && preset.Instruments != null) { foreach (ImInstrument instrument in preset.Instruments) { if (!instrument.HasKey || (key >= instrument.KeyStart && key <= instrument.KeyEnd)) { if (!instrument.HasVel || (vel >= instrument.VelStart && vel <= instrument.VelEnd)) { foreach (ImSample sample in instrument.Samples) { if (sample.WaveFile != null) { if (!sample.HasKey || (key >= sample.KeyStart && key <= sample.KeyEnd)) { if (!sample.HasVel || (vel >= sample.VelStart && vel <= sample.VelEnd)) { samples.Add(sample); if (!MidiPlayerGlobal.CurrentMidiSet.ActiveSounFontInfo.MultiWaves) { return(samples); } } } } } } } } } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } return(samples); }
/// <summary> /// Calculate distance with the AudioListener /// </summary> /// <param name="trf"></param> /// <returns></returns> public static float MPTK_DistanceToListener(Transform trf) { float distance = 0f; try { if (AudioListener != null) { distance = Vector3.Distance(AudioListener.transform.position, trf.position); //Debug.Log("Camera:" + AudioListener.name + " " + distance); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } return(distance); }
/// <summary> /// Load samples associated to a patch /// </summary> /// <param name="ibank"></param> /// <param name="ipatch"></param> private static void LoadSamples(int ibank, int ipatch) { try { float start = Time.realtimeSinceStartup; //Debug.Log(">>> Load Preset - b:" + ibank + " p:" + ipatch); if (ImSFCurrent != null) { ImPreset preset = ImSFCurrent.Banks[ibank].Presets[ipatch]; //Debug.Log("Loading Preset - " + index + " '" + preset.Name + "'"); // Load each sample associated with this preset in DicAudioClip foreach (ImInstrument inst in preset.Instruments) { foreach (ImSample smpl in inst.Samples) { if (smpl.WaveFile != null) { if (!DicAudioClip.Exist(smpl.WaveFile)) { AudioClip ac = Resources.Load <AudioClip>(WavePath + "/" + Path.GetFileNameWithoutExtension(smpl.WaveFile)); if (ac != null) { DicAudioClip.Add(smpl.WaveFile, ac); MPTK_CountWaveLoaded++; } //else Debug.LogWarning("Wave " + smpl.WaveFile + " not found"); } } } } //Debug.Log("--- Loaded Preset - " + ipatch + " '" + preset.Name + "' " + count + " samples loaded"); } else { Debug.Log("Presets not loaded "); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
/// <summary> /// Load setup MPTK from resource /// </summary> private static void LoadMidiSetFromRsc() { try { TextAsset sf = Resources.Load <TextAsset>(MidiPlayerGlobal.FilenameMidiSet); if (sf == null) { Debug.LogWarning("No Midi set found. Create a midi set from the menu Tools/Midi Player Toolkit"); } else { //UnityEngine.Debug.Log(sf.text); CurrentMidiSet = MidiSet.LoadRsc(sf.text); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
/// <summary> /// Find index of a Midi file which contains "name". /// return -1 if not found. /// </summary> /// <param name="name"></param> /// <returns></returns> public static int MPTK_FindMidi(string name) { int index = -1; try { if (!string.IsNullOrEmpty(name)) { if (CurrentMidiSet != null && CurrentMidiSet.MidiFiles != null) { index = CurrentMidiSet.MidiFiles.FindIndex(s => s.Contains(name)); } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } return(index); }
/// <summary> /// Restart playing the current midi file /// </summary> public virtual void MPTK_RePlay() { try { playPause = false; if (midiIsPlaying) { MPTK_ClearAllSound(); newMidiToPlay = true; } else { MPTK_Play(); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
/// <summary> /// Return sample to play according preset, key and velocity /// </summary> /// <param name="idxbank"></param> /// <param name="idxpreset"></param> /// <param name="key"></param> /// <param name="vel"></param> /// <returns></returns> public static ImSample GetImSample(int idxbank, int idxpreset, int key, int vel) { try { if (ImSFCurrent.Banks[idxbank] != null) { ImPreset preset = ImSFCurrent.Banks[idxbank].Presets[idxpreset]; if (preset != null && preset.Instruments != null) { foreach (ImInstrument instrument in preset.Instruments) { if (!instrument.HasKey || (key >= instrument.KeyStart && key <= instrument.KeyEnd)) { if (!instrument.HasVel || (vel >= instrument.VelStart && vel <= instrument.VelEnd)) { foreach (ImSample sample in instrument.Samples) { if (sample.WaveFile != null) { if (!sample.HasKey || (key >= sample.KeyStart && key <= sample.KeyEnd)) { if (!sample.HasVel || (vel >= sample.VelStart && vel <= sample.VelEnd)) { return(sample); } } } } } } } } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } return(null); }
/// <summary> /// Play the midi file defined in MPTK_MidiName /// </summary> public virtual void MPTK_Play() { try { if (MidiPlayerGlobal.SoundFontLoaded) { playPause = false; if (!midiIsPlaying) { // Load description of available soundfont if (MidiPlayerGlobal.ImSFCurrent != null && MidiPlayerGlobal.CurrentMidiSet != null && MidiPlayerGlobal.CurrentMidiSet.MidiFiles != null && MidiPlayerGlobal.CurrentMidiSet.MidiFiles.Count > 0) { //Debug.Log(MPTK_MidiName); if (string.IsNullOrEmpty(MPTK_MidiName)) { MPTK_MidiName = MidiPlayerGlobal.CurrentMidiSet.MidiFiles[0]; } int selectedMidi = MidiPlayerGlobal.CurrentMidiSet.MidiFiles.FindIndex(s => s == MPTK_MidiName); if (selectedMidi < 0) { Debug.LogWarning("MidiFilePlayer - MidiFile " + MPTK_MidiName + " not found. Try with the first in list."); selectedMidi = 0; MPTK_MidiName = MidiPlayerGlobal.CurrentMidiSet.MidiFiles[0]; } StartCoroutine(ThreadPlay()); //StartCoroutine(TestWithDelay()); } else { Debug.LogWarning("MidiFilePlayer - no SoundFont or Midi set defined, go to Unity menu Tools to setup MPTK"); } } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }
private IEnumerator TheadPlay(List <MPTKNote> notes) { if (notes != null && notes.Count > 0) { try { if (MPTK_PauseOnDistance) { distanceEditorModeOnly = MidiPlayerGlobal.MPTK_DistanceToListener(this.transform); if (distanceEditorModeOnly > AudioSourceTemplate.maxDistance) { notes.Clear(); } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } try { List <MidiNote> midinotes = new List <MidiNote>(); foreach (MPTKNote note in notes) { midinotes.Add(note.ToMidiNote()); } MPTK_PlayNotes(midinotes); //Debug.Log("---------------- play count:" + notes.Count + " " + timeFromStartMS ); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } } yield return(0); }
protected IEnumerator ThreadPlay(byte[] midibytestoplay = null) { midiIsPlaying = true; stopMidiToPlay = false; newMidiToPlay = false; bool first = true; //Debug.Log("Start play"); try { miditoplay = new MidiLoad(); // No midi byte array, try to load from MidiFilesDN from resource if (midibytestoplay == null || midibytestoplay.Length == 0) { TextAsset mididata = Resources.Load <TextAsset>(Path.Combine(MidiPlayerGlobal.MidiFilesDB, MPTK_MidiName)); midibytestoplay = mididata.bytes; } miditoplay.KeepNoteOff = MPTK_KeepNoteOff; miditoplay.Load(midibytestoplay); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } if (miditoplay != null) { yield return(StartCoroutine(MPTK_ClearAllSound(true))); try { OnEventStartPlayMidi.Invoke(); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } try { miditoplay.ChangeSpeed(MPTK_Speed); miditoplay.ChangeQuantization(MPTK_Quantization); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } lastTimePlay = Time.realtimeSinceStartup; timeFromStartPlay = 0d; // Loop on each events midi do { miditoplay.LogEvents = MPTK_LogEvents; miditoplay.EnableChangeTempo = MPTK_EnableChangeTempo; miditoplay.EnablePanChange = MPTK_EnablePanChange; if (MPTK_PauseOnDistance) { distanceEditorModeOnly = MidiPlayerGlobal.MPTK_DistanceToListener(this.transform); if (distanceEditorModeOnly > AudioSourceTemplate.maxDistance) { lastTimePlay = Time.realtimeSinceStartup; yield return(new WaitForSeconds(0.2f)); continue; } } if (playPause) { lastTimePlay = Time.realtimeSinceStartup; yield return(new WaitForSeconds(0.2f)); if (miditoplay.EndMidiEvent || newMidiToPlay || stopMidiToPlay) { break; } if (timeToPauseMilliSeconde > -1f) { timeToPauseMilliSeconde -= 0.2f; if (timeToPauseMilliSeconde <= 0f) { playPause = false; } } continue; } if (!first) { timeFromStartPlay += (Time.realtimeSinceStartup - lastTimePlay) * 1000f; } else { timeFromStartPlay = 0d; first = false; } lastTimePlay = Time.realtimeSinceStartup; //Debug.Log("---------------- " + timeFromStartPlay ); // Read midi events until this time List <MidiNote> notes = miditoplay.ReadMidiEvents(timeFromStartPlay); if (miditoplay.EndMidiEvent || newMidiToPlay || stopMidiToPlay) { break; } // Play notes read if (notes != null && notes.Count > 0) { try { if (OnEventNotesMidi != null) { OnEventNotesMidi.Invoke(notes); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } if (MPTK_DirectSendToPlayer) { MPTK_PlayNotes(notes); } //Debug.Log("---------------- play count:" + notes.Count + " " + timeFromStartMS ); } if (Application.isEditor) { TimeSpan times = TimeSpan.FromMilliseconds(timeFromStartPlay); playTimeEditorModeOnly = string.Format("{0:00}:{1:00}:{2:00}:{3:000}", times.Hours, times.Minutes, times.Seconds, times.Milliseconds); durationEditorModeOnly = string.Format("{0:00}:{1:00}:{2:00}:{3:000}", MPTK_Duration.Hours, MPTK_Duration.Minutes, MPTK_Duration.Seconds, MPTK_Duration.Milliseconds); } yield return(new WaitForSeconds(delayMilliSeconde / 1000f));// 0.01f); }while (true); } midiIsPlaying = false; try { if (OnEventEndPlayMidi != null && !stopMidiToPlay && !newMidiToPlay) { OnEventEndPlayMidi.Invoke(); } if ((MPTK_Loop || newMidiToPlay) && !stopMidiToPlay) { MPTK_Play(); } //stopMidiToPlay = false; } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } //Debug.Log("Stop play"); }
public static void LoadCurrentSF() { // Load simplfied soundfont try { DateTime start = DateTime.Now; if (CurrentMidiSet == null) { Debug.LogWarning("No SoundFont defined, go to Unity menu Tools to add a Soundfont"); } else { SoundFontInfo sfi = CurrentMidiSet.ActiveSounFontInfo; if (sfi == null) { Debug.LogWarning("No SoundFont defined, go to Unity menu Tools to add a Soundfont"); } else { // Path to the soundfonts directory for this SF, start from resource folder string pathToImSF = Path.Combine(SoundfontsDB + "/", sfi.Name); // Path to the soundfonts file for this SF TextAsset sf = Resources.Load <TextAsset>(Path.Combine(pathToImSF + "/", sfi.Name)); if (sf == null) { Debug.LogWarning("No SoundFont found " + pathToImSF); } else { WavePath = Path.Combine(pathToImSF + "/", PathToWave); // Load all presets defined in the XML sf ImSFCurrent = ImSoundFont.Load(sf.text); timeToLoadSoundFont = DateTime.Now - start; BuildPresetList(); BuildDrumList(); } } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } if (ImSFCurrent == null) { Debug.LogWarning("SoundFont not loaded."); return; } // Load samples only in run mode if (Application.isPlaying) { try { MPTK_CountWaveLoaded = 0; MPTK_CountPresetLoaded = 0; DateTime start = DateTime.Now; for (int ibank = 0; ibank < ImSFCurrent.Banks.Length; ibank++) { if (ImSFCurrent.Banks[ibank] != null) { for (int ipreset = 0; ipreset < ImSFCurrent.Banks[ibank].Presets.Length; ipreset++) { MPTK_CountPresetLoaded++; if (ImSFCurrent.Banks[ibank].Presets[ipreset] != null) { LoadSamples(ibank, ipreset); } } } } timeToLoadWave = DateTime.Now - start; } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } } if (OnEventPresetLoaded != null) { OnEventPresetLoaded.Invoke(); } }
/// <summary> /// Call by the first MidiPlayer awake /// </summary> //public static void Init() //{ // Instance.StartCoroutine(Instance.InitThread()); //} /// <summary> /// Call by the first MidiPlayer awake /// </summary> private IEnumerator InitThread() { if (!Initialized) { //Debug.Log("MidiPlayerGlobal InitThread"); SoundFontLoaded = false; Initialized = true; ImSFCurrent = null; try { AudioListener = Component.FindObjectOfType <AudioListener>(); if (AudioListener == null) { Debug.LogWarning("No audio listener found. Add one and only one AudioListener component to your hierarchy."); //return; } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } try { AudioListener[] listeners = Component.FindObjectsOfType <AudioListener>(); if (listeners != null && listeners.Length > 1) { Debug.LogWarning("More than one audio listener found. Some unexpected behaviors could happen."); } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } try { LoadMidiSetFromRsc(); DicAudioClip.Init(); } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } if (CurrentMidiSet == null) { Debug.LogWarning("No Midi defined, go to menu 'Tools/MPTK - Midi File Setup' or alt-m"); yield return(0); } else if (CurrentMidiSet.ActiveSounFontInfo == null) { Debug.LogWarning("No Active SoundFont found. Define SoundFont from the menu 'Tools/MPTK - SoundFont Setup' or alt-f"); yield return(0); } LoadCurrentSF(); //Debug.Log(""); if (ImSFCurrent != null) { SoundFontLoaded = true; } } }
/// <summary> /// Play one note - [New 1.7] /// </summary> /// <param name="note"></param> public void MPTK_PlayNote(MidiNote note) { try { // Search sample associated to the preset, midi note and velocity int selectedBank = note.Drum ? MidiPlayerGlobal.CurrentMidiSet.ActiveSounFontInfo.DrumKitBankNumber : selectedBank = MidiPlayerGlobal.CurrentMidiSet.ActiveSounFontInfo.DefaultBankNumber; int noteMidi = note.Midi; if (!note.Drum) { noteMidi += MPTK_Transpose; } //ImSample smpl = MidiPlayerGlobal.GetImSample(selectedBank, note.Patch, noteMidi, note.Velocity); //if (smpl != null) { List <ImSample> samples = MidiPlayerGlobal.GetImMultiSample(selectedBank, note.Patch, noteMidi, note.Velocity); //LogInfoSample(note, null, " Found " + samples.Count + " samples"); foreach (ImSample smpl in samples) { note.Pitch = Mathf.Pow(_ratioHalfTone, (float)(noteMidi - smpl.OriginalPitch + smpl.CoarseTune) + (float)smpl.FineTune / 100f); // Load wave from audioclip AudioClip clip = DicAudioClip.Get(smpl.WaveFile); if (clip != null && clip.loadState == AudioDataLoadState.Loaded) { if (MPTK_LogWaves) { LogInfoSample(note, smpl); } AudioSource audioSelected = null; // Search audioclip not playing with the same wave try { foreach (AudioSource audio in audiosources) { //Debug.Log(audio.isPlaying + " " + audio.clip.name + " " + clip.name); if (!audio.isPlaying && audio.clip.name == clip.name) { audioSelected = audio; break; } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } if (audioSelected == null) { // No audiosource available, create a new audiosource audioSelected = Instantiate <AudioSource>(AudioSourceTemplate); audioSelected.Stop(); audioSelected.transform.position = AudioSourceTemplate.transform.position; audioSelected.transform.SetParent(this.transform); audiosources.Add(audioSelected); // Assign sound to audioclip audioSelected.clip = clip; } // Play note StartCoroutine(PlayNote(audioSelected, note.Drum, smpl, note, timeToRelease)); } else { if (MPTK_LogWaves) { LogInfoSample(note, null, smpl.WaveFile + " ******** Clip not ready to play or not found ******"); } } } //else if (samples.Count == 0) { if (MPTK_LogWaves) { LogInfoSample(note, null, " ********* Sample not found *********"); } } } } catch (System.Exception ex) { MidiPlayerGlobal.ErrorDetail(ex); } }