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 }