1 module soundtab.ui.frame; 2 3 import dlangui.core.events; 4 import dlangui.core.stdaction; 5 import dlangui.widgets.widget; 6 import dlangui.widgets.appframe; 7 import dlangui.widgets.menu; 8 import dlangui.dialogs.dialog; 9 import dlangui.dialogs.settingsdialog; 10 import dlangui.dialogs.filedlg; 11 12 13 import soundtab.ui.actions; 14 import soundtab.ui.settings; 15 16 import derelict.wintab.wintab; 17 import derelict.wintab.tablet; 18 import soundtab.ui.synthwidget; 19 import soundtab.audio.playback; 20 21 // uncomment to enable wacom tablet support 22 //version = EnableWacom; 23 24 class SoundFrame : AppFrame { 25 import dlangui.platforms.windows.winapp; 26 27 SynthWidget _synth; 28 Tablet _tablet; 29 AudioPlayback _playback; 30 AudioSettings _settings; 31 long _statusTimer; 32 33 this(Win32Window window) { 34 _tablet = new Tablet(); 35 window.onUnknownWindowMessage = &_tablet.onUnknownWindowMessage; 36 version(EnableWacom) 37 _tablet.init(window.windowHandle); 38 super(); 39 applySettings(_settings); 40 window.mainWidget = this; 41 _statusTimer = setTimer(100); 42 _playback.start(); 43 } 44 45 private string _lastStatusText; 46 override bool onTimer(ulong id) { 47 if (id == _statusTimer) { 48 import std.utf : toUTF32; 49 string status = _playback.stateString; 50 if (status != _lastStatusText) { 51 statusLine.setStatusText(status.toUTF32); 52 _lastStatusText = status; 53 } 54 _synth.updatePlayPosition(); 55 return true; 56 } else { 57 return super.onTimer(id); 58 } 59 } 60 61 ~this() { 62 _settings.save(); 63 _tablet.uninit(); 64 if (_playback) { 65 destroy(_playback); 66 } 67 } 68 69 override protected void initialize() { 70 import std.path; 71 _playback = new AudioPlayback(); 72 _appName = "SoundTab"; 73 //_editorTool = new DEditorTool(this); 74 _settings = new AudioSettings(buildNormalizedPath(settingsDir, "settings.json")); 75 _settings.load(); 76 _settings.updateDefaults(); 77 _settings.save(); 78 super.initialize(); 79 } 80 81 /// returns global IDE settings 82 @property AudioSettings settings() { return _settings; } 83 84 /// apply changed settings 85 void applySettings(AudioSettings settings) { 86 // apply audio playback device settings 87 string dev = settings.playbackDevice; 88 bool exclusive = settings.exclusiveMode; 89 int minFrame = settings.minFrameMillis; 90 import wasapi.comutils : MMDevice; 91 import wasapi.comutils : MMDevice; 92 import std.utf : toUTF32; 93 MMDevice[] devices = _playback.getDevices(); 94 MMDevice device = devices.length > 0 ? devices[0] : null; 95 foreach(d; devices) { 96 if (d.id == dev) 97 device = d; 98 } 99 _playback.setDevice(device, exclusive, minFrame); 100 } 101 102 void showPreferences() { 103 StringListValue[] deviceList; 104 deviceList ~= StringListValue("default", "Default"d); 105 import wasapi.comutils : MMDevice; 106 import std.utf : toUTF32; 107 MMDevice[] devices = _playback.getDevices(); 108 string dev = _settings.playbackDevice; 109 bool deviceFound = false; 110 foreach(d; devices) { 111 deviceList ~= StringListValue(d.id, d.friendlyName.toUTF32); 112 if (d.id == dev) 113 deviceFound = true; 114 } 115 if (!deviceFound) 116 _settings.playbackDevice = "default"; 117 //Log.d("settings before copy:\n", _settings.setting.toJSON(true)); 118 Setting s = _settings.copySettings(); 119 //Log.d("settings after copy:\n", s.toJSON(true)); 120 SettingsDialog dlg = new SettingsDialog(UIString("SoundTab settings"d), window, s, createSettingsPages(deviceList)); 121 dlg.dialogResult = delegate(Dialog dlg, const Action result) { 122 if (result.id == ACTION_APPLY.id) { 123 //Log.d("settings after edit:\n", s.toJSON(true)); 124 _settings.applySettings(s); 125 applySettings(_settings); 126 _settings.save(); 127 } 128 }; 129 dlg.show(); 130 } 131 132 /// create main menu 133 override protected MainMenu createMainMenu() { 134 MenuItem mainMenuItems = new MenuItem(); 135 136 MenuItem fileItem = new MenuItem(new Action(1, "File"d)); 137 fileItem.add(ACTION_FILE_OPTIONS, ACTION_FILE_OPEN_ACCOMPANIMENT, ACTION_FILE_EXIT); 138 mainMenuItems.add(fileItem); 139 140 MenuItem playItem = new MenuItem(new Action(2, "Play"d)); 141 playItem.add(ACTION_FILE_PLAY_PAUSE_ACCOMPANIMENT); 142 mainMenuItems.add(playItem); 143 144 MenuItem instrItem = new MenuItem(new Action(3, "Instrument"d)); 145 instrItem.add(ACTION_INSTRUMENT_EDITOR); 146 mainMenuItems.add(instrItem); 147 148 149 MainMenu mainMenu = new MainMenu(mainMenuItems); 150 return mainMenu; 151 } 152 153 /// create app body widget 154 override protected Widget createBody() { 155 _synth = new SynthWidget(this, _tablet, _playback); 156 return _synth; 157 } 158 159 protected void openAccompanimentFile() { 160 import std.file; 161 FileDialog dlg = new FileDialog(UIString("Open accompaniment MP3 file"d), window, null); 162 dlg.addFilter(FileFilterEntry(UIString("MP3 files (*.mp3)"d), "*.mp3")); 163 dlg.dialogResult = delegate(Dialog dlg, const Action result) { 164 if (result.id == ACTION_OPEN.id) { 165 string filename = result.stringParam; 166 if (filename.exists && filename.isFile) { 167 _settings.accompanimentFile = filename; 168 _synth.openAccompanimentFile(filename); 169 } 170 } 171 }; 172 dlg.show(); 173 } 174 175 176 void openInstrumentEditor() { 177 import soundtab.ui.instredit; 178 InstrumentEditorDialog dlg = new InstrumentEditorDialog(this.window, _synth.mixer, 900, 800); 179 dlg.show(); 180 } 181 182 /// override to handle specific actions 183 override bool handleAction(const Action a) { 184 if (a) { 185 switch (a.id) { 186 case Actions.FileExit: 187 //if (onCanClose()) 188 window.close(); 189 return true; 190 case Actions.FileOptions: 191 showPreferences(); 192 return true; 193 case Actions.FileOpenAccompaniment: 194 openAccompanimentFile(); 195 return true; 196 case Actions.FilePlayPauseAccompaniment: 197 _synth.playPauseAccomp(); 198 return true; 199 case Actions.InstrumentEditor: 200 openInstrumentEditor(); 201 return true; 202 default: 203 break; 204 } 205 } 206 return false; 207 } 208 }