~ubuntu-branches/ubuntu/trusty/aegisub/trusty

« back to all changes in this revision

Viewing changes to src/audio_display.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel
  • Date: 2012-03-16 22:58:00 UTC
  • Revision ID: package-import@ubuntu.com-20120316225800-yfb8h9e5n04rk46a
Tags: upstream-2.1.9
ImportĀ upstreamĀ versionĀ 2.1.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2005, Rodrigo Braz Monteiro
 
2
// All rights reserved.
 
3
//
 
4
// Redistribution and use in source and binary forms, with or without
 
5
// modification, are permitted provided that the following conditions are met:
 
6
//
 
7
//   * Redistributions of source code must retain the above copyright notice,
 
8
//     this list of conditions and the following disclaimer.
 
9
//   * Redistributions in binary form must reproduce the above copyright notice,
 
10
//     this list of conditions and the following disclaimer in the documentation
 
11
//     and/or other materials provided with the distribution.
 
12
//   * Neither the name of the Aegisub Group nor the names of its contributors
 
13
//     may be used to endorse or promote products derived from this software
 
14
//     without specific prior written permission.
 
15
//
 
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
17
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
18
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
19
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
20
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
21
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
22
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
23
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
24
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
25
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
26
// POSSIBILITY OF SUCH DAMAGE.
 
27
//
 
28
// -----------------------------------------------------------------------------
 
29
//
 
30
// AEGISUB
 
31
//
 
32
// Website: http://aegisub.cellosoft.com
 
33
// Contact: mailto:zeratul@cellosoft.com
 
34
//
 
35
 
 
36
 
 
37
///////////
 
38
// Headers
 
39
#include "config.h"
 
40
 
 
41
#include <wx/tglbtn.h>
 
42
#include <wx/filename.h>
 
43
#include <math.h>
 
44
#include <vector>
 
45
#include "audio_display.h"
 
46
#include "audio_provider_stream.h"
 
47
#include "main.h"
 
48
#include "ass_dialogue.h"
 
49
#include "subs_grid.h"
 
50
#include "ass_file.h"
 
51
#include "subs_edit_box.h"
 
52
#include "options.h"
 
53
#include "audio_karaoke.h"
 
54
#include "audio_box.h"
 
55
#include "fft.h"
 
56
#include "video_context.h"
 
57
#include "vfr.h"
 
58
#include "colorspace.h"
 
59
#include "hotkeys.h"
 
60
#include "utils.h"
 
61
#include "timeedit_ctrl.h"
 
62
#include "standard_paths.h"
 
63
#ifdef _DEBUG
 
64
#include "audio_provider_dummy.h"
 
65
#endif
 
66
 
 
67
 
 
68
#ifdef __WXMAC__
 
69
# define AudioDisplayWindowStyle wxWANTS_CHARS
 
70
#else
 
71
# define AudioDisplayWindowStyle wxSUNKEN_BORDER | wxWANTS_CHARS
 
72
#endif
 
73
 
 
74
///////////////
 
75
// Constructor
 
76
AudioDisplay::AudioDisplay(wxWindow *parent)
 
77
: wxWindow (parent, -1, wxDefaultPosition, wxSize(200,Options.AsInt(_T("Audio Display Height"))), AudioDisplayWindowStyle , _T("Audio Display"))
 
78
{
 
79
        // Set variables
 
80
        origImage = NULL;
 
81
        spectrumDisplay = NULL;
 
82
        spectrumDisplaySelected = NULL;
 
83
        spectrumRenderer = NULL;
 
84
        ScrollBar = NULL;
 
85
        dialogue = NULL;
 
86
        karaoke = NULL;
 
87
        peak = NULL;
 
88
        min = NULL;
 
89
        hasSel = false;
 
90
        diagUpdated = false;
 
91
        NeedCommit = false;
 
92
        loaded = false;
 
93
        temporary = false;
 
94
        blockUpdate = false;
 
95
        dontReadTimes = false;
 
96
        holding = false;
 
97
        draggingScale = false;
 
98
        scrubbing = false;
 
99
        Position = 0;
 
100
        PositionSample = 0;
 
101
        oldCurPos = 0;
 
102
        scale = 1.0f;
 
103
        provider = NULL;
 
104
        player = NULL;
 
105
        hold = 0;
 
106
        samples = 0;
 
107
        samplesPercent = 100;
 
108
        hasFocus = (wxWindow::FindFocus() == this);
 
109
        needImageUpdate = false;
 
110
        needImageUpdateWeak = true;
 
111
        playingToEnd = false;
 
112
 
 
113
        // Init
 
114
        UpdateTimer.SetOwner(this,Audio_Update_Timer);
 
115
        GetClientSize(&w,&h);
 
116
        h -= Options.AsBool(_T("Audio Draw Timeline")) ? 20 : 0;
 
117
        SetSamplesPercent(50,false);
 
118
 
 
119
        // Set cursor
 
120
        //wxCursor cursor(wxCURSOR_BLANK);
 
121
        //SetCursor(cursor);
 
122
 
 
123
        //wxLog::SetActiveTarget(new wxLogWindow(NULL,_T("Log"),true,false));
 
124
}
 
125
 
 
126
 
 
127
//////////////
 
128
// Destructor
 
129
AudioDisplay::~AudioDisplay() {
 
130
        if (player) player->CloseStream();
 
131
        delete provider;
 
132
        delete player;
 
133
        delete origImage;
 
134
        delete spectrumRenderer;
 
135
        delete spectrumDisplay;
 
136
        delete spectrumDisplaySelected;
 
137
        delete[] peak;
 
138
        delete[] min;
 
139
        provider = NULL;
 
140
        player = NULL;
 
141
        origImage = NULL;
 
142
        spectrumRenderer = NULL;
 
143
        spectrumDisplay = NULL;
 
144
        spectrumDisplaySelected = NULL;
 
145
        peak = NULL;
 
146
        min = NULL;
 
147
}
 
148
 
 
149
 
 
150
/////////
 
151
// Reset
 
152
void AudioDisplay::Reset() {
 
153
        hasSel = false;
 
154
        diagUpdated = false;
 
155
        NeedCommit = false;
 
156
        karaoke->enabled = false;
 
157
        karaoke->syllables.clear();
 
158
        box->karaokeMode = false;
 
159
        box->KaraokeButton->SetValue(false);
 
160
        dialogue = NULL;
 
161
}
 
162
 
 
163
 
 
164
////////////////
 
165
// Update image
 
166
void AudioDisplay::UpdateImage(bool weak) {
 
167
        // Update samples
 
168
        UpdateSamples();
 
169
 
 
170
        // Set image as needing to be redrawn
 
171
        needImageUpdate = true;
 
172
        if (weak == false && needImageUpdateWeak == true) {
 
173
                needImageUpdateWeak = false;
 
174
        }
 
175
        Refresh(false);
 
176
}
 
177
 
 
178
void AudioDisplay::DoUpdateImage() {
 
179
        // Loaded?
 
180
        if (!loaded || !provider) return;
 
181
 
 
182
        // Needs updating?
 
183
        if (!needImageUpdate) return;
 
184
        bool weak = needImageUpdateWeak;
 
185
 
 
186
        // Prepare bitmap
 
187
        int timelineHeight = Options.AsBool(_T("Audio Draw Timeline")) ? 20 : 0;
 
188
        int displayH = h+timelineHeight;
 
189
        if (origImage) {
 
190
                if (origImage->GetWidth() != w || origImage->GetHeight() != displayH) {
 
191
                        delete origImage;
 
192
                        origImage = NULL;
 
193
                }
 
194
        }
 
195
 
 
196
        // Options
 
197
        bool draw_boundary_lines = Options.AsBool(_T("Audio Draw Secondary Lines"));
 
198
        bool draw_selection_background = Options.AsBool(_T("Audio Draw Selection Background"));
 
199
        bool drawKeyframes = Options.AsBool(_T("Audio Draw Keyframes"));
 
200
 
 
201
        // Invalid dimensions
 
202
        if (w == 0 || displayH == 0) return;
 
203
 
 
204
        // New bitmap
 
205
        if (!origImage) origImage = new wxBitmap(w,displayH,-1);
 
206
 
 
207
        // Is spectrum?
 
208
        bool spectrum = false;
 
209
        if (provider && Options.AsBool(_T("Audio Spectrum"))) {
 
210
                spectrum = true;
 
211
        }
 
212
 
 
213
        // Draw image to be displayed
 
214
        wxMemoryDC dc;
 
215
        dc.SelectObject(*origImage);
 
216
 
 
217
        // Black background
 
218
        dc.SetPen(*wxTRANSPARENT_PEN);
 
219
        dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Background"))));
 
220
        dc.DrawRectangle(0,0,w,h);
 
221
 
 
222
        // Selection position
 
223
        hasSel = false;
 
224
        hasKaraoke = karaoke->enabled;
 
225
        selStart = 0;
 
226
        selEnd = 0;
 
227
        lineStart = 0;
 
228
        lineEnd = 0;
 
229
        selStartCap = 0;
 
230
        selEndCap = 0;
 
231
        int64_t drawSelStart = 0;
 
232
        int64_t drawSelEnd = 0;
 
233
        if (dialogue) {
 
234
                GetDialoguePos(lineStart,lineEnd,false);
 
235
                hasSel = true;
 
236
                if (hasKaraoke) {
 
237
                        GetKaraokePos(selStartCap,selEndCap,true);
 
238
                        GetKaraokePos(drawSelStart,drawSelEnd,false);
 
239
                        selStart = lineStart;
 
240
                        selEnd = lineEnd;
 
241
                }
 
242
                else {
 
243
                        GetDialoguePos(selStartCap,selEndCap,true);
 
244
                        selStart = lineStart;
 
245
                        selEnd = lineEnd;
 
246
                        drawSelStart = lineStart;
 
247
                        drawSelEnd = lineEnd;
 
248
                }
 
249
        }
 
250
 
 
251
        // Draw selection bg
 
252
        if (hasSel && drawSelStart < drawSelEnd && draw_selection_background) {
 
253
                if (NeedCommit && !karaoke->enabled) dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Selection Background Modified"))));
 
254
                else dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Selection Background"))));
 
255
                dc.DrawRectangle(drawSelStart,0,drawSelEnd-drawSelStart,h);
 
256
        }
 
257
 
 
258
        // Draw spectrum
 
259
        if (spectrum) {
 
260
                DrawSpectrum(dc,weak);
 
261
        }
 
262
 
 
263
        // Waveform
 
264
        else if (provider) {
 
265
                DrawWaveform(dc,weak);
 
266
        }
 
267
 
 
268
        // Nothing
 
269
        else {
 
270
                dc.DrawLine(0,h/2,w,h/2);
 
271
        }
 
272
 
 
273
        // Draw seconds boundaries
 
274
        if (draw_boundary_lines) {
 
275
                int64_t start = Position*samples;
 
276
                int rate = provider->GetSampleRate();
 
277
                int pixBounds = rate / samples;
 
278
                dc.SetPen(wxPen(Options.AsColour(_T("Audio Seconds Boundaries")),1,wxDOT));
 
279
                if (pixBounds >= 8) {
 
280
                        for (int x=0;x<w;x++) {
 
281
                                if (((x*samples)+start) % rate < samples) {
 
282
                                        dc.DrawLine(x,0,x,h);
 
283
                                }
 
284
                        }
 
285
                }
 
286
        }
 
287
 
 
288
        // Draw current frame
 
289
        if (Options.AsBool(_T("Audio Draw Video Position"))) {
 
290
                if (VideoContext::Get()->IsLoaded()) {
 
291
                        dc.SetPen(wxPen(Options.AsColour(_T("Audio Play Cursor")),2,wxLONG_DASH));
 
292
                        int x = GetXAtMS(VFR_Output.GetTimeAtFrame(VideoContext::Get()->GetFrameN()));
 
293
                        dc.DrawLine(x,0,x,h);
 
294
                }
 
295
        }
 
296
 
 
297
        // Draw keyframes
 
298
        if (drawKeyframes && VideoContext::Get()->KeyFramesLoaded()) {
 
299
                DrawKeyframes(dc);
 
300
        }
 
301
 
 
302
        // Draw previous line
 
303
        DrawInactiveLines(dc);
 
304
 
 
305
        if (hasSel) {
 
306
                // Draw boundaries
 
307
                if (true) {
 
308
                        // Draw start boundary
 
309
                        int selWidth = Options.AsInt(_T("Audio Line boundaries Thickness"));
 
310
                        dc.SetPen(wxPen(Options.AsColour(_T("Audio Line boundary start"))));
 
311
                        dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Line boundary start"))));
 
312
                        dc.DrawRectangle(lineStart-selWidth/2+1,0,selWidth,h);
 
313
                        wxPoint points1[3] = { wxPoint(lineStart,0), wxPoint(lineStart+10,0), wxPoint(lineStart,10) };
 
314
                        wxPoint points2[3] = { wxPoint(lineStart,h-1), wxPoint(lineStart+10,h-1), wxPoint(lineStart,h-11) };
 
315
                        dc.DrawPolygon(3,points1);
 
316
                        dc.DrawPolygon(3,points2);
 
317
 
 
318
                        // Draw end boundary
 
319
                        dc.SetPen(wxPen(Options.AsColour(_T("Audio Line boundary end"))));
 
320
                        dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Line boundary end"))));
 
321
                        dc.DrawRectangle(lineEnd-selWidth/2+1,0,selWidth,h);
 
322
                        wxPoint points3[3] = { wxPoint(lineEnd,0), wxPoint(lineEnd-10,0), wxPoint(lineEnd,10) };
 
323
                        wxPoint points4[3] = { wxPoint(lineEnd,h-1), wxPoint(lineEnd-10,h-1), wxPoint(lineEnd,h-11) };
 
324
                        dc.DrawPolygon(3,points3);
 
325
                        dc.DrawPolygon(3,points4);
 
326
                }
 
327
 
 
328
                // Draw karaoke
 
329
                if (hasKaraoke) {
 
330
                        try {
 
331
                                // Prepare
 
332
                                wxPen curPen(Options.AsColour(_T("Audio Syllable boundaries")),1,wxDOT);
 
333
                                dc.SetPen(curPen);
 
334
                                wxFont curFont(9,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD,false,_T("Verdana"),wxFONTENCODING_SYSTEM);
 
335
                                dc.SetFont(curFont);
 
336
                                if (!spectrum) dc.SetTextForeground(Options.AsColour(_T("Audio Syllable text")));
 
337
                                else dc.SetTextForeground(wxColour(255,255,255));
 
338
                                size_t karn = karaoke->syllables.size();
 
339
                                int64_t pos1,pos2;
 
340
                                int len,curpos;
 
341
                                wxCoord tw=0,th=0;
 
342
                                AudioKaraokeSyllable *curSyl;
 
343
                                wxString temptext;
 
344
 
 
345
                                // Draw syllables
 
346
                                for (size_t i=0;i<karn;i++) {
 
347
                                        curSyl = &karaoke->syllables.at(i);
 
348
                                        len = curSyl->duration*10;
 
349
                                        curpos = curSyl->start_time*10;
 
350
                                        if (len != -1) {
 
351
                                                pos1 = GetXAtMS(curStartMS+curpos);
 
352
                                                pos2 = GetXAtMS(curStartMS+len+curpos);
 
353
                                                dc.DrawLine(pos2,0,pos2,h);
 
354
                                                temptext = curSyl->text;
 
355
                                                temptext.Trim(true);
 
356
                                                temptext.Trim(false);
 
357
                                                GetTextExtent(temptext,&tw,&th,NULL,NULL,&curFont);
 
358
                                                dc.DrawText(temptext,(pos1+pos2-tw)/2,4);
 
359
                                        }
 
360
                                }
 
361
                        }
 
362
                        catch (...) {
 
363
                                // FIXME?
 
364
                        }
 
365
                }
 
366
        }
 
367
 
 
368
        // Modified text
 
369
        if (NeedCommit) {
 
370
                dc.SetFont(wxFont(9,wxDEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD,false,_T("Verdana"))); // FIXME: hardcoded font name
 
371
                dc.SetTextForeground(wxColour(255,0,0));
 
372
                if (selStart <= selEnd) {
 
373
                        dc.DrawText(_T("Modified"),4,4);
 
374
                }
 
375
                else {
 
376
                        dc.DrawText(_T("Negative time"),4,4);
 
377
                }
 
378
        }
 
379
 
 
380
        // Draw timescale
 
381
        if (timelineHeight) {
 
382
                DrawTimescale(dc);
 
383
        }
 
384
 
 
385
        // Draw selection border
 
386
        if (hasFocus) {
 
387
                dc.SetPen(*wxGREEN_PEN);
 
388
                dc.SetBrush(*wxTRANSPARENT_BRUSH);
 
389
                dc.DrawRectangle(0,0,w,h);
 
390
        }
 
391
 
 
392
        // Done
 
393
        needImageUpdate = false;
 
394
        needImageUpdateWeak = true;
 
395
}
 
396
 
 
397
 
 
398
///////////////////////
 
399
// Draw Inactive Lines
 
400
void AudioDisplay::DrawInactiveLines(wxDC &dc) {
 
401
        // Check if there is anything to do
 
402
        int shadeType = Options.AsInt(_T("Audio Inactive Lines Display Mode"));
 
403
        if (shadeType == 0) return;
 
404
 
 
405
        // Spectrum?
 
406
        bool spectrum = false;
 
407
        if (provider && Options.AsBool(_T("Audio Spectrum"))) {
 
408
                spectrum = true;
 
409
        }
 
410
 
 
411
        // Set options
 
412
        dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Line boundary inactive line"))));
 
413
        int selWidth = Options.AsInt(_T("Audio Line boundaries Thickness"));
 
414
        AssDialogue *shade;
 
415
        int shadeX1,shadeX2;
 
416
        int shadeFrom,shadeTo;
 
417
 
 
418
        // Only previous
 
419
        if (shadeType == 1) {
 
420
                shadeFrom = this->line_n-1;
 
421
                shadeTo = shadeFrom+1;
 
422
        }
 
423
 
 
424
        // All
 
425
        else {
 
426
                shadeFrom = 0;
 
427
                shadeTo = grid->GetRows();
 
428
        }
 
429
        
 
430
        for (int j=shadeFrom;j<shadeTo;j++) {
 
431
                if (j == line_n) continue;
 
432
                if (j < 0) continue;
 
433
                shade = grid->GetDialogue(j);
 
434
 
 
435
                if (shade) {
 
436
                        // Get coordinates
 
437
                        shadeX1 = GetXAtMS(shade->Start.GetMS());
 
438
                        shadeX2 = GetXAtMS(shade->End.GetMS());
 
439
                        if (shadeX2 < 0 || shadeX1 > w) continue;
 
440
 
 
441
                        // Draw over waveform
 
442
                        if (!spectrum) {
 
443
                                // Selection
 
444
                                int selX1 = MAX(0,GetXAtMS(curStartMS));
 
445
                                int selX2 = MIN(w,GetXAtMS(curEndMS));
 
446
 
 
447
                                // Get ranges (x1->x2, x3->x4).
 
448
                                int x1 = MAX(0,shadeX1);
 
449
                                int x2 = MIN(w,shadeX2);
 
450
                                int x3 = MAX(x1,selX2);
 
451
                                int x4 = MAX(x2,selX2);
 
452
 
 
453
                                // Clip first range
 
454
                                x1 = MIN(x1,selX1);
 
455
                                x2 = MIN(x2,selX1);
 
456
 
 
457
                                // Set pen and draw
 
458
                                dc.SetPen(wxPen(Options.AsColour(_T("Audio Waveform Inactive"))));
 
459
                                for (int i=x1;i<x2;i++) dc.DrawLine(i,peak[i],i,min[i]-1);
 
460
                                for (int i=x3;i<x4;i++) dc.DrawLine(i,peak[i],i,min[i]-1);
 
461
                        }
 
462
 
 
463
                        // Draw boundaries
 
464
                        dc.SetPen(wxPen(Options.AsColour(_T("Audio Line boundary inactive line"))));
 
465
                        dc.DrawRectangle(shadeX1-selWidth/2+1,0,selWidth,h);
 
466
                        dc.DrawRectangle(shadeX2-selWidth/2+1,0,selWidth,h);
 
467
                }
 
468
        }
 
469
}
 
470
 
 
471
 
 
472
//////////////////
 
473
// Draw keyframes
 
474
void AudioDisplay::DrawKeyframes(wxDC &dc) {
 
475
        const wxArrayInt & KeyFrames = VideoContext::Get()->GetKeyFrames();
 
476
        int nKeys = (int)KeyFrames.Count();
 
477
        dc.SetPen(wxPen(wxColour(255,0,255),1));
 
478
 
 
479
        // Get min and max frames to care about
 
480
        int minFrame = VFR_Output.GetFrameAtTime(GetMSAtX(0),true);
 
481
        int maxFrame = VFR_Output.GetFrameAtTime(GetMSAtX(w),true);
 
482
 
 
483
        // Scan list
 
484
        for (int i=0;i<nKeys;i++) {
 
485
                int cur = KeyFrames[i];
 
486
                if (cur >= minFrame && cur <= maxFrame) {
 
487
                        int x = GetXAtMS(VFR_Output.GetTimeAtFrame(cur,true));
 
488
                        dc.DrawLine(x,0,x,h);
 
489
                }
 
490
                else if (cur > maxFrame) break;
 
491
        }
 
492
}
 
493
 
 
494
 
 
495
//////////////////
 
496
// Draw timescale
 
497
void AudioDisplay::DrawTimescale(wxDC &dc) {
 
498
        // Set size
 
499
        int timelineHeight = Options.AsBool(_T("Audio Draw Timeline")) ? 20 : 0;
 
500
 
 
501
        // Set colours
 
502
        dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
 
503
        dc.SetPen(*wxTRANSPARENT_PEN);
 
504
        dc.DrawRectangle(0,h,w,timelineHeight);
 
505
        dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT));
 
506
        dc.DrawLine(0,h,w,h);
 
507
        dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT));
 
508
        dc.DrawLine(0,h+1,w,h+1);
 
509
        dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
 
510
        dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
 
511
        wxFont scaleFont;
 
512
        scaleFont.SetFaceName(_T("Tahoma")); // FIXME: hardcoded font name
 
513
        if (!scaleFont.IsOk())
 
514
                scaleFont.SetFamily(wxFONTFAMILY_SWISS );
 
515
        scaleFont.SetPointSize(8);
 
516
        dc.SetFont(scaleFont);
 
517
 
 
518
        // Timescale ticks
 
519
        int64_t start = Position*samples;
 
520
        int rate = provider->GetSampleRate();
 
521
        for (int i=1;i<32;i*=2) {
 
522
                int pixBounds = rate / (samples * 4 / i);
 
523
                if (pixBounds >= 8) {
 
524
                        for (int x=0;x<w;x++) {
 
525
                                int64_t pos = (x*samples)+start;
 
526
                                // Second boundary
 
527
                                if (pos % rate < samples) {
 
528
                                        dc.DrawLine(x,h+2,x,h+8);
 
529
 
 
530
                                        // Draw text
 
531
                                        wxCoord textW,textH;
 
532
                                        int hr = 0;
 
533
                                        int m = 0;
 
534
                                        int s = pos/rate;
 
535
                                        while (s >= 3600) {
 
536
                                                s -= 3600;
 
537
                                                hr++;
 
538
                                        }
 
539
                                        while (s >= 60) {
 
540
                                                s -= 60;
 
541
                                                m++;
 
542
                                        }
 
543
                                        wxString text;
 
544
                                        if (hr) text = wxString::Format(_T("%i:%02i:%02i"),hr,m,s);
 
545
                                        else if (m) text = wxString::Format(_T("%i:%02i"),m,s);
 
546
                                        else text = wxString::Format(_T("%i"),s);
 
547
                                        dc.GetTextExtent(text,&textW,&textH,NULL,NULL,&scaleFont);
 
548
                                        dc.DrawText(text,MAX(0,x-textW/2)+1,h+8);
 
549
                                }
 
550
 
 
551
                                // Other
 
552
                                else if (pos % (rate / 4 * i) < samples) {
 
553
                                        dc.DrawLine(x,h+2,x,h+5);
 
554
                                }
 
555
                        }
 
556
                        break;
 
557
                }
 
558
        }
 
559
}
 
560
 
 
561
 
 
562
////////////
 
563
// Waveform
 
564
void AudioDisplay::DrawWaveform(wxDC &dc,bool weak) {
 
565
        // Prepare Waveform
 
566
        if (!weak || peak == NULL || min == NULL) {
 
567
                if (peak) delete[] peak;
 
568
                if (min) delete[] min;
 
569
                peak = new int[w];
 
570
                min = new int[w];
 
571
        }
 
572
 
 
573
        // Get waveform
 
574
        if (!weak) {
 
575
                provider->GetWaveForm(min,peak,Position*samples,w,h,samples,scale);
 
576
        }
 
577
 
 
578
        // Draw pre-selection
 
579
        if (!hasSel) selStartCap = w;
 
580
        dc.SetPen(wxPen(Options.AsColour(_T("Audio Waveform"))));
 
581
        for (int64_t i=0;i<selStartCap;i++) {
 
582
                dc.DrawLine(i,peak[i],i,min[i]-1);
 
583
        }
 
584
 
 
585
        if (hasSel) {
 
586
                // Draw selection
 
587
                if (Options.AsBool(_T("Audio Draw Selection Background"))) {
 
588
                        if (NeedCommit && !karaoke->enabled) dc.SetPen(wxPen(Options.AsColour(_T("Audio Waveform Modified"))));
 
589
                        else dc.SetPen(wxPen(Options.AsColour(_T("Audio Waveform Selected"))));
 
590
                }
 
591
                for (int64_t i=selStartCap;i<selEndCap;i++) {
 
592
                        dc.DrawLine(i,peak[i],i,min[i]-1);
 
593
                }
 
594
 
 
595
                // Draw post-selection
 
596
                dc.SetPen(wxPen(Options.AsColour(_T("Audio Waveform"))));
 
597
                for (int64_t i=selEndCap;i<w;i++) {
 
598
                        dc.DrawLine(i,peak[i],i,min[i]-1);
 
599
                }
 
600
        }
 
601
}
 
602
 
 
603
 
 
604
//////////////////////////
 
605
// Draw spectrum analyzer
 
606
void AudioDisplay::DrawSpectrum(wxDC &finaldc,bool weak) {
 
607
        if (!weak || !spectrumDisplay || spectrumDisplay->GetWidth() != w || spectrumDisplay->GetHeight() != h) {
 
608
                if (spectrumDisplay) {
 
609
                        delete spectrumDisplay;
 
610
                        delete spectrumDisplaySelected;
 
611
                        spectrumDisplay = 0;
 
612
                        spectrumDisplaySelected = 0;
 
613
                }
 
614
                weak = false;
 
615
        }
 
616
 
 
617
        if (!weak) {
 
618
                if (!spectrumRenderer)
 
619
                        spectrumRenderer = new AudioSpectrum(provider);
 
620
 
 
621
                spectrumRenderer->SetScaling(scale);
 
622
 
 
623
                unsigned char *img = (unsigned char *)malloc(h*w*3); // wxImage requires using malloc
 
624
 
 
625
                // Use a slightly slower, but simple way
 
626
                // Always draw the spectrum for the entire width
 
627
                // Hack: without those divs by 2 the display is horizontally compressed
 
628
                spectrumRenderer->RenderRange(Position*samples, (Position+w)*samples, false, img, 0, w, w, h);
 
629
 
 
630
                // The spectrum bitmap will have been deleted above already, so just make a new one
 
631
                wxImage imgobj(w, h, img, false);
 
632
                spectrumDisplay = new wxBitmap(imgobj);
 
633
        }
 
634
 
 
635
        if (hasSel && selStartCap < selEndCap && !spectrumDisplaySelected) {
 
636
                // There is a visible selection and we don't have a rendered one
 
637
                // This should be done regardless whether we're "weak" or not
 
638
                // Assume a few things were already set up when things were first rendered though
 
639
                unsigned char *img = (unsigned char *)malloc(h*w*3);
 
640
                spectrumRenderer->RenderRange(Position*samples, (Position+w)*samples, true, img, 0, w, w, h);
 
641
                wxImage imgobj(w, h, img, false);
 
642
                spectrumDisplaySelected = new wxBitmap(imgobj);
 
643
        }
 
644
 
 
645
        // Draw
 
646
        wxMemoryDC dc;
 
647
        dc.SelectObject(*spectrumDisplay);
 
648
        finaldc.Blit(0,0,w,h,&dc,0,0);
 
649
 
 
650
        if (hasSel && spectrumDisplaySelected && selStartCap < selEndCap) {
 
651
                dc.SelectObject(*spectrumDisplaySelected);
 
652
                finaldc.Blit(selStartCap, 0, selEndCap-selStartCap, h, &dc, selStartCap, 0);
 
653
        }
 
654
}
 
655
 
 
656
//////////////////////////
 
657
// Get selection position
 
658
void AudioDisplay::GetDialoguePos(int64_t &selStart,int64_t &selEnd, bool cap) {
 
659
        selStart = GetXAtMS(curStartMS);
 
660
        selEnd = GetXAtMS(curEndMS);
 
661
 
 
662
        if (cap) {
 
663
                if (selStart < 0) selStart = 0;
 
664
                if (selEnd < 0) selEnd = 0;
 
665
                if (selStart >= w) selStart = w-1;
 
666
                if (selEnd >= w) selEnd = w-1;
 
667
        }
 
668
}
 
669
 
 
670
 
 
671
////////////////////////
 
672
// Get karaoke position
 
673
void AudioDisplay::GetKaraokePos(int64_t &karStart,int64_t &karEnd, bool cap) {
 
674
        try {
 
675
                // Wrap around
 
676
                int nsyls = (int)karaoke->syllables.size();
 
677
                if (karaoke->curSyllable == -1) {
 
678
                        karaoke->SetSyllable(nsyls-1);
 
679
                }
 
680
                if (karaoke->curSyllable >= nsyls) karaoke->curSyllable = nsyls-1;
 
681
 
 
682
                // Get positions
 
683
                int pos = karaoke->syllables.at(karaoke->curSyllable).start_time;
 
684
                int len = karaoke->syllables.at(karaoke->curSyllable).duration;
 
685
                karStart = GetXAtMS(curStartMS+pos*10);
 
686
                karEnd = GetXAtMS(curStartMS+pos*10+len*10);
 
687
 
 
688
                // Cap
 
689
                if (cap) {
 
690
                        if (karStart < 0) karStart = 0;
 
691
                        if (karEnd < 0) karEnd = 0;
 
692
                        if (karStart >= w) karStart = w-1;
 
693
                        if (karEnd >= w) karEnd = w-1;
 
694
                }
 
695
        }
 
696
        catch (...) {
 
697
        }
 
698
}
 
699
 
 
700
 
 
701
//////////
 
702
// Update
 
703
void AudioDisplay::Update() {
 
704
        if (blockUpdate) return;
 
705
        if (loaded) {
 
706
                if (Options.AsBool(_T("Audio Autoscroll")))
 
707
                        MakeDialogueVisible();
 
708
                else
 
709
                        UpdateImage(true);
 
710
        }
 
711
}
 
712
 
 
713
 
 
714
//////////////////////
 
715
// Recreate the image
 
716
void AudioDisplay::RecreateImage() {
 
717
        GetClientSize(&w,&h);
 
718
        h -= Options.AsBool(_T("Audio Draw Timeline")) ? 20 : 0;
 
719
        delete origImage;
 
720
        origImage = NULL;
 
721
        UpdateImage(false);
 
722
}
 
723
 
 
724
 
 
725
/////////////////////////
 
726
// Make dialogue visible
 
727
void AudioDisplay::MakeDialogueVisible(bool force) {
 
728
        // Variables
 
729
        int startShow=0, endShow=0;
 
730
        if (karaoke->enabled) {
 
731
                // In karaoke mode the syllable and as much as possible towards the end of the line should be shown
 
732
                int dummy = 0;
 
733
                GetTimesSelection(startShow, dummy);
 
734
                GetTimesDialogue(dummy, endShow);
 
735
        } else {
 
736
                GetTimesSelection(startShow,endShow);
 
737
        }
 
738
        int startPos = GetSampleAtMS(startShow);
 
739
        int endPos = GetSampleAtMS(endShow);
 
740
        int startX = GetXAtMS(startShow);
 
741
        int endX = GetXAtMS(endShow);
 
742
 
 
743
        if (force || startX < 50 || endX > w-50) {
 
744
                if (startX < 50 || endX - startX >= w) {
 
745
                        // Make sure the left edge of the selection is at least 50 pixels from the edge of the display
 
746
                        UpdatePosition(startPos - 50*samples, true);
 
747
                } else {
 
748
                        // Otherwise center the selection in display
 
749
                        UpdatePosition((startPos+endPos-w*samples)/2,true);
 
750
                }
 
751
        }
 
752
 
 
753
        // Update
 
754
        UpdateImage();
 
755
}
 
756
 
 
757
 
 
758
////////////////
 
759
// Set position
 
760
void AudioDisplay::SetPosition(int pos) {
 
761
        Position = pos;
 
762
        PositionSample = pos * samples;
 
763
        UpdateImage();
 
764
}
 
765
 
 
766
 
 
767
///////////////////
 
768
// Update position
 
769
void AudioDisplay::UpdatePosition (int pos,bool IsSample) {
 
770
        // Safeguards
 
771
        if (!provider) return;
 
772
        if (IsSample) pos /= samples;
 
773
        int len = provider->GetNumSamples() / samples;
 
774
        if (pos < 0) pos = 0;
 
775
        if (pos >= len) pos = len-1;
 
776
 
 
777
        // Set
 
778
        Position = pos;
 
779
        PositionSample = pos*samples;
 
780
        UpdateScrollbar();
 
781
}
 
782
 
 
783
 
 
784
/////////////////////////////
 
785
// Set samples in percentage
 
786
// Note: aka Horizontal Zoom
 
787
void AudioDisplay::SetSamplesPercent(int percent,bool update,float pivot) {
 
788
        // Calculate
 
789
        if (percent < 1) percent = 1;
 
790
        if (percent > 100) percent = 100;
 
791
        if (samplesPercent == percent) return;
 
792
        samplesPercent = percent;
 
793
 
 
794
        // Update
 
795
        if (update) {
 
796
                // Center scroll
 
797
                int oldSamples = samples;
 
798
                UpdateSamples();
 
799
                PositionSample += int64_t((oldSamples-samples)*w*pivot);
 
800
                if (PositionSample < 0) PositionSample = 0;
 
801
 
 
802
                // Update
 
803
                UpdateSamples();
 
804
                UpdateScrollbar();
 
805
                UpdateImage();
 
806
                Refresh(false);
 
807
        }
 
808
}
 
809
 
 
810
 
 
811
//////////////////
 
812
// Update samples
 
813
void AudioDisplay::UpdateSamples() {
 
814
        // Set samples
 
815
        if (!provider) return;
 
816
        if (w) {
 
817
                int64_t totalSamples = provider->GetNumSamples();
 
818
                int total = totalSamples / w;
 
819
                int max = 5760000 / w;  // 2 minutes at 48 kHz maximum
 
820
                if (total > max) total = max;
 
821
                int min = 8;
 
822
                if (total < min) total = min;
 
823
                int range = total-min;
 
824
                samples = int(range*pow(samplesPercent/100.0,3)+min);
 
825
 
 
826
                // Set position
 
827
                int length = w * samples;
 
828
                if (PositionSample + length > totalSamples) {
 
829
                        PositionSample = totalSamples - length;
 
830
                        if (PositionSample < 0) PositionSample = 0;
 
831
                        if (samples) Position = PositionSample / samples;
 
832
                }
 
833
        }
 
834
}
 
835
 
 
836
 
 
837
/////////////
 
838
// Set scale
 
839
void AudioDisplay::SetScale(float _scale) {
 
840
        if (scale == _scale) return;
 
841
        scale = _scale;
 
842
        UpdateImage();
 
843
}
 
844
 
 
845
 
 
846
//////////////////
 
847
// Load from file
 
848
void AudioDisplay::SetFile(wxString file) {
 
849
        // Unload
 
850
        if (file.IsEmpty()) try {
 
851
                try {
 
852
                        if (player) player->CloseStream();
 
853
                }
 
854
                catch (const wxChar *e) {
 
855
                        wxLogError(e);
 
856
                }
 
857
                delete provider;
 
858
                delete player;
 
859
                delete spectrumRenderer;
 
860
                provider = NULL;
 
861
                player = NULL;
 
862
                spectrumRenderer = NULL;
 
863
                try {
 
864
                        Reset();
 
865
                }
 
866
                catch (const wxChar *e) {
 
867
                        wxLogError(e);
 
868
                }
 
869
 
 
870
                loaded = false;
 
871
                temporary = false;
 
872
                StandardPaths::SetPathValue(_T("?audio"),_T(""));
 
873
        }
 
874
        catch (wxString e) {
 
875
                wxLogError(e);
 
876
        }
 
877
        catch (const wxChar *e) {
 
878
                wxLogError(e);
 
879
        }
 
880
        catch (...) {
 
881
                wxLogError(_T("Unknown error unloading audio"));
 
882
        }
 
883
 
 
884
        // Load
 
885
        else {
 
886
                SetFile(_T(""));
 
887
                try {
 
888
                        // Get provider
 
889
                        bool is_dummy = false;
 
890
#ifdef _DEBUG
 
891
                        if (file == _T("?dummy")) {
 
892
                                is_dummy = true;
 
893
                                provider = new DummyAudioProvider(150*60*1000, false); // 150 minutes non-noise
 
894
                        } else if (file == _T("?noise")) {
 
895
                                is_dummy = true;
 
896
                                provider = new DummyAudioProvider(150*60*1000, true); // 150 minutes noise
 
897
                        } else {
 
898
                                provider = AudioProviderFactoryManager::GetAudioProvider(file);
 
899
                        }
 
900
#else
 
901
                        provider = AudioProviderFactoryManager::GetAudioProvider(file);
 
902
#endif
 
903
 
 
904
                        // Get player
 
905
                        player = AudioPlayerFactoryManager::GetAudioPlayer();
 
906
                        player->SetDisplayTimer(&UpdateTimer);
 
907
                        player->SetProvider(provider);
 
908
                        player->OpenStream();
 
909
                        loaded = true;
 
910
 
 
911
                        // Add to recent
 
912
                        if (!is_dummy) {
 
913
                                Options.AddToRecentList(file,_T("Recent aud"));
 
914
                                wxFileName fn(file);
 
915
                                StandardPaths::SetPathValue(_T("?audio"),fn.GetPath());
 
916
                        }
 
917
 
 
918
                        // Update
 
919
                        UpdateImage();
 
920
                }
 
921
                catch (AudioProvider::CancelAudioLoadException &) {
 
922
                        if (player) { delete player; player = 0; }
 
923
                        if (provider) { delete provider; provider = 0; }
 
924
                }
 
925
                catch (const wxChar *e) {
 
926
                        if (player) { delete player; player = 0; }
 
927
                        if (provider) { delete provider; provider = 0; }
 
928
                        wxLogError(e);
 
929
                }
 
930
                catch (wxString &err) {
 
931
                        if (player) { delete player; player = 0; }
 
932
                        if (provider) { delete provider; provider = 0; }
 
933
                        wxMessageBox(err,_T("Error loading audio"),wxICON_ERROR | wxOK);
 
934
                }
 
935
                catch (...) {
 
936
                        if (player) { delete player; player = 0; }
 
937
                        if (provider) { delete provider; provider = 0; }
 
938
                        wxLogError(_T("Unknown error loading audio"));
 
939
                }
 
940
        }
 
941
        
 
942
        if (!loaded) return;
 
943
 
 
944
        assert(loaded == (provider != NULL));
 
945
 
 
946
        // Set default selection
 
947
        int n = grid->editBox->linen;
 
948
        SetDialogue(grid,grid->GetDialogue(n),n);
 
949
}
 
950
 
 
951
 
 
952
///////////////////
 
953
// Load from video
 
954
void AudioDisplay::SetFromVideo() {
 
955
        if (VideoContext::Get()->IsLoaded()) {
 
956
                wxString extension = VideoContext::Get()->videoName.Right(4);
 
957
                extension.LowerCase();
 
958
 
 
959
                if (extension != _T(".d2v")) SetFile(VideoContext::Get()->videoName);
 
960
        }
 
961
}
 
962
 
 
963
 
 
964
////////////////
 
965
// Reload audio
 
966
void AudioDisplay::Reload() {
 
967
        if (provider) SetFile(provider->GetFilename());
 
968
}
 
969
 
 
970
 
 
971
////////////////////
 
972
// Update scrollbar
 
973
void AudioDisplay::UpdateScrollbar() {
 
974
        if (!provider) return;
 
975
        int page = w/12;
 
976
        int len = provider->GetNumSamples() / samples / 12;
 
977
        Position = PositionSample / samples;
 
978
        ScrollBar->SetScrollbar(Position/12,page,len,int(page*0.7),true);
 
979
}
 
980
 
 
981
 
 
982
//////////////////////////////////////////////
 
983
// Gets the sample number at the x coordinate
 
984
int64_t AudioDisplay::GetSampleAtX(int x) {
 
985
        return (x+Position)*samples;
 
986
}
 
987
 
 
988
 
 
989
/////////////////////////////////////////////////
 
990
// Gets the x coordinate corresponding to sample
 
991
int AudioDisplay::GetXAtSample(int64_t n) {
 
992
        return samples ? (n/samples)-Position : 0;
 
993
}
 
994
 
 
995
 
 
996
/////////////////
 
997
// Get MS from X
 
998
int AudioDisplay::GetMSAtX(int64_t x) {
 
999
        return (PositionSample+(x*samples)) * 1000 / provider->GetSampleRate();
 
1000
}
 
1001
 
 
1002
 
 
1003
/////////////////
 
1004
// Get X from MS
 
1005
int AudioDisplay::GetXAtMS(int64_t ms) {
 
1006
        return ((ms * provider->GetSampleRate() / 1000)-PositionSample)/samples;
 
1007
}
 
1008
 
 
1009
 
 
1010
////////////////////
 
1011
// Get MS At sample
 
1012
int AudioDisplay::GetMSAtSample(int64_t x) {
 
1013
        return x * 1000 / provider->GetSampleRate();
 
1014
}
 
1015
 
 
1016
 
 
1017
////////////////////
 
1018
// Get Sample at MS
 
1019
int64_t AudioDisplay::GetSampleAtMS(int64_t ms) {
 
1020
        return ms * provider->GetSampleRate() / 1000;
 
1021
}
 
1022
 
 
1023
 
 
1024
////////
 
1025
// Play
 
1026
void AudioDisplay::Play(int start,int end) {
 
1027
        Stop();
 
1028
 
 
1029
        // Check provider
 
1030
        if (!provider) {
 
1031
                return;
 
1032
        }
 
1033
 
 
1034
        // Set defaults
 
1035
        playingToEnd = end < 0;
 
1036
        int64_t num_samples = provider->GetNumSamples();
 
1037
        start = GetSampleAtMS(start);
 
1038
        if (end != -1) end = GetSampleAtMS(end);
 
1039
        else end = num_samples-1;
 
1040
 
 
1041
        // Sanity checking
 
1042
        if (start < 0) start = 0;
 
1043
        if (start >= num_samples) start = num_samples-1;
 
1044
        if (end >= num_samples) end = num_samples-1;
 
1045
        if (end < start) end = start;
 
1046
 
 
1047
        // Redraw the image to avoid any junk left over from mouse movements etc
 
1048
        // See issue #598
 
1049
        UpdateImage(true);
 
1050
 
 
1051
        // Call play
 
1052
        player->Play(start,end-start);
 
1053
}
 
1054
 
 
1055
 
 
1056
////////
 
1057
// Stop
 
1058
void AudioDisplay::Stop() {
 
1059
        if (VideoContext::Get()->IsPlaying()) VideoContext::Get()->Stop();
 
1060
        if (player) player->Stop();
 
1061
}
 
1062
 
 
1063
 
 
1064
///////////////////////////
 
1065
// Get samples of dialogue
 
1066
void AudioDisplay::GetTimesDialogue(int &start,int &end) {
 
1067
        if (!dialogue) {
 
1068
                start = 0;
 
1069
                end = 0;
 
1070
                return;
 
1071
        }
 
1072
 
 
1073
        start = dialogue->Start.GetMS();
 
1074
        end = dialogue->End.GetMS();
 
1075
}
 
1076
 
 
1077
 
 
1078
////////////////////////////
 
1079
// Get samples of selection
 
1080
void AudioDisplay::GetTimesSelection(int &start,int &end) {
 
1081
        start = 0;
 
1082
        end = 0;
 
1083
        if (!dialogue) return;
 
1084
 
 
1085
        try {
 
1086
                if (karaoke->enabled) {
 
1087
                        int pos = karaoke->syllables.at(karaoke->curSyllable).start_time;
 
1088
                        int len = karaoke->syllables.at(karaoke->curSyllable).duration;
 
1089
                        start = curStartMS+pos*10;
 
1090
                        end = curStartMS+pos*10+len*10;
 
1091
                }
 
1092
                else {
 
1093
                        start = curStartMS;
 
1094
                        end = curEndMS;
 
1095
                }
 
1096
        }
 
1097
        catch (...) {}
 
1098
}
 
1099
 
 
1100
 
 
1101
/////////////////////////////
 
1102
// Set the current selection
 
1103
void AudioDisplay::SetSelection(int start, int end) {
 
1104
        curStartMS = start;
 
1105
        curEndMS = end;
 
1106
        Update();
 
1107
}
 
1108
 
 
1109
 
 
1110
////////////////
 
1111
// Set dialogue
 
1112
void AudioDisplay::SetDialogue(SubtitlesGrid *_grid,AssDialogue *diag,int n) {
 
1113
        // Actual parameters
 
1114
        if (_grid) {
 
1115
                // Set variables
 
1116
                grid = _grid;
 
1117
                line_n = n;
 
1118
                dialogue = diag;
 
1119
 
 
1120
                // Set flags
 
1121
                diagUpdated = false;
 
1122
                NeedCommit = false;
 
1123
 
 
1124
                // Set times
 
1125
                if (dialogue && !dontReadTimes && Options.AsBool(_T("Audio grab times on select"))) {
 
1126
                        int s = dialogue->Start.GetMS();
 
1127
                        int e = dialogue->End.GetMS();
 
1128
 
 
1129
                        // Never do it for 0:00:00.00->0:00:00.00 lines
 
1130
                        if (s != 0 || e != 0) {
 
1131
                                curStartMS = s;
 
1132
                                curEndMS = e;
 
1133
                        }
 
1134
                }
 
1135
        }
 
1136
 
 
1137
        // Read karaoke data
 
1138
        if (dialogue && karaoke->enabled) {
 
1139
                NeedCommit = karaoke->LoadFromDialogue(dialogue);
 
1140
 
 
1141
                // Reset karaoke pos
 
1142
                if (karaoke->curSyllable == -1) karaoke->SetSyllable((int)karaoke->syllables.size()-1);
 
1143
                else karaoke->SetSyllable(0);
 
1144
        }
 
1145
 
 
1146
        // Update
 
1147
        Update();
 
1148
}
 
1149
 
 
1150
 
 
1151
//////////////////
 
1152
// Commit changes
 
1153
void AudioDisplay::CommitChanges (bool nextLine) {
 
1154
        // Loaded?
 
1155
        if (!loaded) return;
 
1156
 
 
1157
        // Check validity
 
1158
        bool textNeedsCommit = grid->GetDialogue(line_n)->Text != grid->editBox->TextEdit->GetText();
 
1159
        bool timeNeedsCommit = grid->GetDialogue(line_n)->Start.GetMS() != curStartMS || grid->GetDialogue(line_n)->End.GetMS() != curEndMS;
 
1160
        if (timeNeedsCommit || textNeedsCommit) NeedCommit = true;
 
1161
        bool wasKaraSplitting = false;
 
1162
        bool validCommit = true;
 
1163
        if (!karaoke->enabled && !karaoke->splitting) {
 
1164
                if (!NeedCommit || curEndMS < curStartMS) validCommit = false;
 
1165
        }
 
1166
 
 
1167
        // Update karaoke
 
1168
        int karaSelStart = 0, karaSelEnd = -1;
 
1169
        if (karaoke->enabled) {
 
1170
                wasKaraSplitting = karaoke->splitting;
 
1171
                karaoke->Commit();
 
1172
                // Get karaoke selection
 
1173
                karaSelStart = karaoke->syllables.size();
 
1174
                for (size_t k = 0; k < karaoke->syllables.size(); ++k) {
 
1175
                        if (karaoke->syllables[k].selected) {
 
1176
                                if ((signed)k < karaSelStart) karaSelStart = k;
 
1177
                                if ((signed)k > karaSelEnd) karaSelEnd = k;
 
1178
                        }
 
1179
                }
 
1180
        }
 
1181
        
 
1182
        // Get selected rows
 
1183
        wxArrayInt sel = grid->GetSelection();
 
1184
 
 
1185
        // Commit ok?
 
1186
        if (validCommit) {
 
1187
                // Reset flags
 
1188
                diagUpdated = false;
 
1189
                NeedCommit = false;
 
1190
 
 
1191
                // Update dialogues
 
1192
                blockUpdate = true;
 
1193
                AssDialogue *curDiag;
 
1194
                for (size_t i=0;i<sel.GetCount();i++) {
 
1195
                        if (grid->IsInSelection(line_n)) curDiag = grid->GetDialogue(sel[i]);
 
1196
                        else curDiag = grid->GetDialogue(line_n);
 
1197
                        if (timeNeedsCommit) {
 
1198
                                curDiag->Start.SetMS(curStartMS);
 
1199
                                curDiag->End.SetMS(curEndMS);
 
1200
                        }
 
1201
                        if (!karaoke->enabled && textNeedsCommit) {
 
1202
                                // If user was editing karaoke stuff, that should take precedence of manual changes in the editbox,
 
1203
                                // so only update from editbox when not in kara mode
 
1204
                                curDiag->Text = grid->editBox->TextEdit->GetText();
 
1205
                        }
 
1206
                        curDiag->UpdateData();
 
1207
                        if (!grid->IsInSelection(line_n)) break;
 
1208
                }
 
1209
 
 
1210
                // Update edit box
 
1211
                grid->editBox->StartTime->Update();
 
1212
                grid->editBox->EndTime->Update();
 
1213
                grid->editBox->Duration->Update();
 
1214
 
 
1215
                // Update grid
 
1216
                grid->editBox->Update(!karaoke->enabled);
 
1217
                grid->ass->FlagAsModified(_T(""));
 
1218
                grid->CommitChanges();
 
1219
                karaoke->SetSelection(karaSelStart, karaSelEnd);
 
1220
                blockUpdate = false;
 
1221
        }
 
1222
 
 
1223
        // Next line (ugh what a condition, can this be simplified?)
 
1224
        if (nextLine && !karaoke->enabled && Options.AsBool(_T("Audio Next Line on Commit")) && !wasKaraSplitting) {
 
1225
                // Insert a line if it doesn't exist
 
1226
                int nrows = grid->GetRows();
 
1227
                if (nrows == line_n + 1) {
 
1228
                        AssDialogue *def = new AssDialogue;
 
1229
                        def->Start = grid->GetDialogue(line_n)->End;
 
1230
                        def->End = grid->GetDialogue(line_n)->End;
 
1231
                        def->End.SetMS(def->End.GetMS()+Options.AsInt(_T("Timing Default Duration")));
 
1232
                        def->Style = grid->GetDialogue(line_n)->Style;
 
1233
                        grid->InsertLine(def,line_n,true);
 
1234
                        curStartMS = curEndMS;
 
1235
                        curEndMS = curStartMS + Options.AsInt(_T("Timing Default Duration"));
 
1236
                }
 
1237
                else if (grid->GetDialogue(line_n+1)->Start.GetMS() == 0 && grid->GetDialogue(line_n+1)->End.GetMS() == 0) {
 
1238
                        curStartMS = curEndMS;
 
1239
                        curEndMS = curStartMS + Options.AsInt(_T("Timing Default Duration"));
 
1240
                }
 
1241
                else {
 
1242
                        curStartMS = grid->GetDialogue(line_n+1)->Start.GetMS();
 
1243
                        curEndMS = grid->GetDialogue(line_n+1)->End.GetMS();
 
1244
                }
 
1245
                
 
1246
                // Go to next
 
1247
                dontReadTimes = true;
 
1248
                ChangeLine(1,sel.GetCount() > 1 ? true : false);
 
1249
                dontReadTimes = false;
 
1250
        }
 
1251
 
 
1252
        Update();
 
1253
}
 
1254
 
 
1255
 
 
1256
////////////
 
1257
// Add lead
 
1258
void AudioDisplay::AddLead(bool in,bool out) {
 
1259
        // Lead in
 
1260
        if (in) {
 
1261
                curStartMS -= Options.AsInt(_T("Audio Lead in"));
 
1262
                if (curStartMS < 0) curStartMS = 0;
 
1263
        }
 
1264
 
 
1265
        // Lead out
 
1266
        if (out) {
 
1267
                curEndMS += Options.AsInt(_T("Audio Lead out"));
 
1268
        }
 
1269
 
 
1270
        // Set changes
 
1271
        UpdateTimeEditCtrls();
 
1272
        NeedCommit = true;
 
1273
        if (Options.AsBool(_T("Audio Autocommit"))) CommitChanges();
 
1274
        Update();
 
1275
}
 
1276
 
 
1277
 
 
1278
///////////////
 
1279
// Event table
 
1280
BEGIN_EVENT_TABLE(AudioDisplay, wxWindow)
 
1281
    EVT_MOUSE_EVENTS(AudioDisplay::OnMouseEvent)
 
1282
    EVT_PAINT(AudioDisplay::OnPaint)
 
1283
        EVT_SIZE(AudioDisplay::OnSize)
 
1284
        EVT_TIMER(Audio_Update_Timer,AudioDisplay::OnUpdateTimer)
 
1285
        EVT_KEY_DOWN(AudioDisplay::OnKeyDown)
 
1286
        EVT_SET_FOCUS(AudioDisplay::OnGetFocus)
 
1287
        EVT_KILL_FOCUS(AudioDisplay::OnLoseFocus)
 
1288
END_EVENT_TABLE()
 
1289
 
 
1290
 
 
1291
/////////
 
1292
// Paint
 
1293
void AudioDisplay::OnPaint(wxPaintEvent& event) {
 
1294
        if (w == 0 || h == 0) return;
 
1295
        DoUpdateImage();
 
1296
 
 
1297
        wxPaintDC dc(this);
 
1298
        if (origImage) dc.DrawBitmap(*origImage,0,0);
 
1299
        
 
1300
#ifdef __WXMAC__
 
1301
        // Hack/workaround for wxMac limitation
 
1302
        // Painting to a wxClientDC doesn't work on wxMac so use this hack
 
1303
        // instead to paint the playback cursor in OnPaint, and shoot Refresh()
 
1304
        // in the OnUpdateTimer handler.
 
1305
        // The playback cursor doesn't disappear after stopping in every case,
 
1306
        // but most.
 
1307
        if (player && !player->IsPlaying())
 
1308
                playbackCursorPos = -1;
 
1309
        if (playbackCursorPos >= 0) {
 
1310
                dc.SetPen(wxPen(Options.AsColour(_T("Audio Play cursor"))));
 
1311
                dc.DrawLine(playbackCursorPos,0,playbackCursorPos,h);
 
1312
        }
 
1313
#endif
 
1314
}
 
1315
 
 
1316
 
 
1317
///////////////
 
1318
// Mouse event
 
1319
void AudioDisplay::OnMouseEvent(wxMouseEvent& event) {
 
1320
        // Get x,y
 
1321
        int64_t x = event.GetX();
 
1322
        int64_t y = event.GetY();
 
1323
        bool karMode = karaoke->enabled;
 
1324
        bool shiftDown = event.m_shiftDown;
 
1325
        int timelineHeight = Options.AsBool(_T("Audio Draw Timeline")) ? 20 : 0;
 
1326
 
 
1327
        // Leaving event
 
1328
        if (event.Leaving()) {
 
1329
                event.Skip();
 
1330
                return;
 
1331
        }
 
1332
 
 
1333
        if (!player || !provider) {
 
1334
                event.Skip();
 
1335
                return;
 
1336
        }
 
1337
 
 
1338
        // Is inside?
 
1339
        bool inside = false;
 
1340
        bool onScale = false;
 
1341
        if (x >= 0 && y >= 0 && x < w) {
 
1342
                if (y < h) {
 
1343
                        inside = true;
 
1344
 
 
1345
                        // Get focus
 
1346
                        if (wxWindow::FindFocus() != this && Options.AsBool(_T("Audio Autofocus"))) SetFocus();
 
1347
                }
 
1348
                else if (y < h+timelineHeight) onScale = true;
 
1349
        }
 
1350
 
 
1351
        // Buttons
 
1352
        bool leftIsDown = event.ButtonIsDown(wxMOUSE_BTN_LEFT);
 
1353
        bool rightIsDown = event.ButtonIsDown(wxMOUSE_BTN_RIGHT);
 
1354
        bool buttonIsDown = leftIsDown || rightIsDown;
 
1355
        bool leftClick = event.ButtonDown(wxMOUSE_BTN_LEFT);
 
1356
        bool rightClick = event.ButtonDown(wxMOUSE_BTN_RIGHT);
 
1357
        bool middleClick = event.Button(wxMOUSE_BTN_MIDDLE);
 
1358
        bool buttonClick = leftClick || rightClick;
 
1359
        bool defCursor = true;
 
1360
 
 
1361
        // Click type
 
1362
        if (buttonClick && !holding) {
 
1363
                holding = true;
 
1364
                CaptureMouse();
 
1365
        }
 
1366
        if (!buttonIsDown && holding) {
 
1367
                holding = false;
 
1368
                if (HasCapture()) ReleaseMouse();
 
1369
        }
 
1370
 
 
1371
        // Mouse wheel
 
1372
        if (event.GetWheelRotation() != 0) {
 
1373
                // Zoom or scroll?
 
1374
                bool zoom = shiftDown;
 
1375
                if (Options.AsBool(_T("Audio Wheel Default To Zoom"))) zoom = !zoom;
 
1376
 
 
1377
                // Zoom
 
1378
                if (zoom) {
 
1379
#ifdef __APPLE__
 
1380
                        // Reverse scroll directions on Apple... ugly hack
 
1381
                        // Otherwise left=right and right=left on systems that support four-way scrolling.
 
1382
                        int step = -event.GetWheelRotation() / event.GetWheelDelta();
 
1383
#else
 
1384
                        int step = event.GetWheelRotation() / event.GetWheelDelta();
 
1385
#endif
 
1386
                        int value = box->HorizontalZoom->GetValue()+step;
 
1387
                        box->HorizontalZoom->SetValue(value);
 
1388
                        SetSamplesPercent(value,true,float(x)/float(w));
 
1389
                }
 
1390
 
 
1391
                // Scroll
 
1392
                else {
 
1393
                        int step = -event.GetWheelRotation() * w / 360;
 
1394
                        UpdatePosition(Position+step,false);
 
1395
                        UpdateImage();
 
1396
                }
 
1397
        }
 
1398
 
 
1399
        // Cursor drawing
 
1400
        if (player && !player->IsPlaying() && origImage) {
 
1401
                // Draw bg
 
1402
                wxClientDC dc(this);
 
1403
                dc.DrawBitmap(*origImage,0,0);
 
1404
 
 
1405
                if (inside) {
 
1406
                        // Draw cursor
 
1407
                        dc.SetLogicalFunction(wxINVERT);
 
1408
                        dc.DrawLine(x,0,x,h);
 
1409
 
 
1410
                        // Time
 
1411
                        if (Options.AsBool(_T("Audio Draw Cursor Time"))) {
 
1412
                                // Time string
 
1413
                                AssTime time;
 
1414
                                time.SetMS(GetMSAtX(x));
 
1415
                                wxString text = time.GetASSFormated();
 
1416
 
 
1417
                                // Calculate metrics
 
1418
                                // FIXME: Hardcoded font name
 
1419
                                wxFont font(10,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD,false,_T("Verdana"));
 
1420
                                dc.SetFont(font);
 
1421
                                int tw,th;
 
1422
                                GetTextExtent(text,&tw,&th,NULL,NULL,&font);
 
1423
 
 
1424
                                // Set inversion
 
1425
                                bool left = true;
 
1426
                                if (x > w/2) left = false;
 
1427
 
 
1428
                                // Text coordinates
 
1429
                                int dx;
 
1430
                                dx = x - tw/2;
 
1431
                                if (dx < 4) dx = 4;
 
1432
                                int max = w - tw - 4;
 
1433
                                if (dx > max) dx = max;
 
1434
                                int dy = 4;
 
1435
                                if (karMode) dy += th;
 
1436
 
 
1437
                                // Draw text
 
1438
                                dc.SetTextForeground(wxColour(64,64,64));
 
1439
                                dc.DrawText(text,dx+1,dy-1);
 
1440
                                dc.DrawText(text,dx+1,dy+1);
 
1441
                                dc.DrawText(text,dx-1,dy-1);
 
1442
                                dc.DrawText(text,dx-1,dy+1);
 
1443
                                dc.SetTextForeground(wxColour(255,255,255));
 
1444
                                dc.DrawText(text,dx,dy);
 
1445
                        }
 
1446
                }
 
1447
        }
 
1448
 
 
1449
        // Scale dragging
 
1450
        if ((hold == 0 && onScale) || draggingScale) {
 
1451
                if (event.ButtonDown(wxMOUSE_BTN_LEFT)) {
 
1452
                        lastDragX = x;
 
1453
                        draggingScale = true;
 
1454
                }
 
1455
                else if (holding) {
 
1456
                        int delta = lastDragX - x;
 
1457
                        lastDragX = x;
 
1458
                        UpdatePosition(Position + delta);
 
1459
                        UpdateImage();
 
1460
                        Refresh(false);
 
1461
                        SetCursor(wxNullCursor);
 
1462
                        return;
 
1463
                }
 
1464
                else draggingScale = false;
 
1465
        }
 
1466
 
 
1467
        // Outside
 
1468
        if (!inside && hold == 0) return;
 
1469
 
 
1470
        // Left click
 
1471
        if (leftClick) {
 
1472
                SetFocus();
 
1473
        }
 
1474
 
 
1475
        // Right click
 
1476
        if (rightClick) {
 
1477
                SetFocus();
 
1478
                if (karaoke->enabled) {
 
1479
                        int syl = GetSyllableAtX(x);
 
1480
                        if (syl != -1) {
 
1481
                                int start = karaoke->syllables.at(syl).start_time * 10 + dialogue->Start.GetMS();
 
1482
                                int count = karaoke->syllables.at(syl).duration * 10;
 
1483
                                Play(start, start+count);
 
1484
                        }
 
1485
                }
 
1486
        }
 
1487
 
 
1488
        // Middle click
 
1489
        if (middleClick) {
 
1490
                SetFocus();
 
1491
                if (VideoContext::Get()->IsLoaded()) {
 
1492
                        VideoContext::Get()->JumpToTime(GetMSAtX(x),true);
 
1493
                }
 
1494
        }
 
1495
 
 
1496
        // Timing
 
1497
        if (hasSel) {
 
1498
                bool updated = false;
 
1499
 
 
1500
                // Grab start/end
 
1501
                if (hold == 0) {
 
1502
                        bool gotGrab = false;
 
1503
                        bool karTime = karMode && !
 
1504
#ifdef __APPLE__
 
1505
                                event.CmdDown();
 
1506
#else
 
1507
                                event.ControlDown();
 
1508
#endif
 
1509
 
 
1510
                        // Line timing mode
 
1511
                        if (!karTime) {
 
1512
                                // Grab start
 
1513
                                if (abs64 (x - selStart) < 6 && Options.AsBool(_T("Disable Dragging Times"))==false) {
 
1514
                                        wxCursor cursor(wxCURSOR_SIZEWE);
 
1515
                                        SetCursor(cursor);
 
1516
                                        defCursor = false;
 
1517
                                        if (buttonClick) {
 
1518
                                                hold = 1;
 
1519
                                                gotGrab = true;
 
1520
                                        }
 
1521
                                }
 
1522
 
 
1523
                                // Grab end
 
1524
                                else if (abs64 (x - selEnd) < 6 && Options.AsBool(_T("Disable Dragging Times"))==false) {
 
1525
                                        wxCursor cursor(wxCURSOR_SIZEWE);
 
1526
                                        SetCursor(cursor);
 
1527
                                        defCursor = false;
 
1528
                                        if (buttonClick) {
 
1529
                                                hold = 2;
 
1530
                                                gotGrab = true;
 
1531
                                        }
 
1532
                                }
 
1533
 
 
1534
                                // Dragging nothing, time from scratch
 
1535
                                else {
 
1536
                                        if (buttonClick) {
 
1537
                                                if (leftClick) hold = 3;
 
1538
                                                else hold = 2;
 
1539
                                                lastX = x;
 
1540
                                                gotGrab = true;
 
1541
                                        }
 
1542
                                }
 
1543
                        }
 
1544
 
 
1545
                        // Karaoke mode
 
1546
                        else {
 
1547
                                // Look for a syllable
 
1548
                                int64_t pos,len,curpos;
 
1549
                                AudioKaraokeSyllable *curSyl;
 
1550
                                size_t karn = karaoke->syllables.size();
 
1551
                                for (size_t i=0;i<karn;i++) {
 
1552
                                        curSyl = &karaoke->syllables.at(i);
 
1553
                                        len = curSyl->duration*10;
 
1554
                                        curpos = curSyl->start_time*10;
 
1555
                                        if (len != -1) {
 
1556
                                                pos = GetXAtMS(curStartMS+len+curpos);
 
1557
 
 
1558
                                                // Grabbing syllable boundary
 
1559
                                                if (abs64 (x - pos) < 7) {
 
1560
                                                        wxCursor cursor(wxCURSOR_SIZEWE);
 
1561
                                                        SetCursor(cursor);
 
1562
                                                        defCursor = false;
 
1563
                                                        if (event.LeftIsDown()) {
 
1564
                                                                hold = 4;
 
1565
                                                                holdSyl = (int)i;
 
1566
                                                                gotGrab = true;
 
1567
                                                        }
 
1568
                                                        break;
 
1569
                                                }
 
1570
                                        }
 
1571
                                }
 
1572
 
 
1573
                                // No syllable found, select if possible
 
1574
                                if (hold == 0 && leftClick) {
 
1575
                                        int syl = GetSyllableAtX(x);
 
1576
                                        if (syl != -1) {
 
1577
                                                karaoke->SetSyllable(syl);
 
1578
                                                updated = true;
 
1579
                                        }
 
1580
                                }
 
1581
                        }
 
1582
                }
 
1583
 
 
1584
                // Drag start/end
 
1585
                if (hold != 0) {
 
1586
                        // Dragging
 
1587
                        if (buttonIsDown) {
 
1588
                                // Drag from nothing or straight timing
 
1589
                                if (hold == 3 && buttonIsDown) {
 
1590
                                        if (!karMode) {
 
1591
                                                if (leftIsDown) curStartMS = GetMSAtX(x);
 
1592
                                                else curEndMS = GetMSAtX(x);
 
1593
                                                updated = true;
 
1594
                                                diagUpdated = true;
 
1595
 
 
1596
                                                if (leftIsDown && abs((long)(x-lastX)) > Options.AsInt(_T("Audio Start Drag Sensitivity"))) {
 
1597
                                                        selStart = lastX;
 
1598
                                                        selEnd = x;
 
1599
                                                        curStartMS = GetBoundarySnap(GetMSAtX(lastX),10,event.ShiftDown(),true);
 
1600
                                                        curEndMS = GetMSAtX(x);
 
1601
                                                        hold = 2;
 
1602
                                                }
 
1603
                                        }
 
1604
                                }
 
1605
 
 
1606
                                // Drag start
 
1607
                                if (hold == 1 && buttonIsDown) {
 
1608
                                        // Set new value
 
1609
                                        if (x != selStart) {
 
1610
                                                int snapped = GetBoundarySnap(GetMSAtX(x),10,event.ShiftDown(),true);
 
1611
                                                selStart = GetXAtMS(snapped);
 
1612
                                                if (selStart > selEnd) {
 
1613
                                                        int temp = selStart;
 
1614
                                                        selStart = selEnd;
 
1615
                                                        selEnd = temp;
 
1616
                                                        hold = 2;
 
1617
                                                        curEndMS = snapped;
 
1618
                                                        snapped = GetMSAtX(selStart);
 
1619
                                                }
 
1620
                                                curStartMS = snapped;
 
1621
                                                updated = true;
 
1622
                                                diagUpdated = true;
 
1623
                                        }
 
1624
                                }
 
1625
 
 
1626
                                // Drag end
 
1627
                                if (hold == 2 && buttonIsDown) {
 
1628
                                        // Set new value
 
1629
                                        if (x != selEnd) {
 
1630
                                                int snapped = GetBoundarySnap(GetMSAtX(x),10,event.ShiftDown(),false);
 
1631
                                                selEnd = GetXAtMS(snapped);
 
1632
                                                //selEnd = GetBoundarySnap(x,event.ShiftDown()?0:10,false);
 
1633
                                                if (selStart > selEnd) {
 
1634
                                                        int temp = selStart;
 
1635
                                                        selStart = selEnd;
 
1636
                                                        selEnd = temp;
 
1637
                                                        hold = 1;
 
1638
                                                        curStartMS = snapped;
 
1639
                                                        snapped = GetMSAtX(selEnd);
 
1640
                                                }
 
1641
                                                curEndMS = snapped;
 
1642
                                                updated = true;
 
1643
                                                diagUpdated = true;
 
1644
                                        }
 
1645
                                }
 
1646
 
 
1647
                                // Drag karaoke
 
1648
                                if (hold == 4 && leftIsDown) {
 
1649
                                        // Set new value
 
1650
                                        int curpos,len,pos,nkar;
 
1651
                                        AudioKaraokeSyllable *curSyl=NULL,*nextSyl=NULL;
 
1652
                                        curSyl = &karaoke->syllables.at(holdSyl);
 
1653
                                        nkar = (int)karaoke->syllables.size();
 
1654
                                        if (holdSyl < nkar-1) {
 
1655
                                                nextSyl = &karaoke->syllables.at(holdSyl+1);
 
1656
                                        }
 
1657
                                        curpos = curSyl->start_time;
 
1658
                                        len = curSyl->duration;
 
1659
                                        pos = GetXAtMS(curStartMS+(len+curpos)*10);
 
1660
                                        if (x != pos) {
 
1661
                                                // Calculate delta in centiseconds
 
1662
                                                int delta = ((int64_t)(x-pos)*samples*100)/provider->GetSampleRate();
 
1663
 
 
1664
                                                // Apply delta
 
1665
                                                int deltaMode = 0;
 
1666
                                                if (shiftDown) deltaMode = 1;
 
1667
                                                // else if (ctrlDown) deltaMode = 2;
 
1668
                                                bool result = karaoke->SyllableDelta(holdSyl,delta,deltaMode);
 
1669
                                                if (result) {
 
1670
                                                        updated = true;
 
1671
                                                        diagUpdated = true;
 
1672
                                                }
 
1673
                                        }
 
1674
                                }
 
1675
                        }
 
1676
 
 
1677
                        // Release
 
1678
                        else {
 
1679
                                // Prevent negative times
 
1680
                                curStartMS = MAX(0, curStartMS);
 
1681
                                curEndMS = MAX(0, curEndMS);
 
1682
                                selStart = MAX(0, selStart);
 
1683
                                selEnd = MAX(0, selEnd);
 
1684
 
 
1685
                                // Commit changes
 
1686
                                diagUpdated = false;
 
1687
                                // Time edit controls changed, or it was a karaoke syllable drag
 
1688
                                NeedCommit = UpdateTimeEditCtrls() || (hold == 4);
 
1689
 
 
1690
                                if (NeedCommit && Options.AsBool(_T("Audio Autocommit"))) 
 
1691
                                        CommitChanges();
 
1692
                                else
 
1693
                                        UpdateImage(true);
 
1694
 
 
1695
                                // Update stuff
 
1696
                                SetCursor(wxNullCursor);
 
1697
                                hold = 0;
 
1698
                        }
 
1699
                }
 
1700
 
 
1701
                // Update stuff
 
1702
                if (updated) {
 
1703
                        if (diagUpdated) NeedCommit = true;
 
1704
                        if (karaoke->enabled && !playingToEnd) {
 
1705
                                AudioKaraokeSyllable &syl = karaoke->syllables[karaoke->curSyllable];
 
1706
                                player->SetEndPosition(GetSampleAtMS(curStartMS + (syl.start_time+syl.duration)*10));
 
1707
                        } else if (!playingToEnd) {
 
1708
                                player->SetEndPosition(GetSampleAtX(selEnd));
 
1709
                        }
 
1710
                        if (hold != 0) {
 
1711
                                wxCursor cursor(wxCURSOR_SIZEWE);
 
1712
                                SetCursor(cursor);
 
1713
                        }
 
1714
                        UpdateImage(true);
 
1715
                }
 
1716
        }
 
1717
 
 
1718
        // Not holding
 
1719
        else {
 
1720
                hold = 0;
 
1721
        }
 
1722
 
 
1723
        // Restore cursor
 
1724
        if (defCursor) SetCursor(wxNullCursor);
 
1725
}
 
1726
 
 
1727
 
 
1728
////////////////////////
 
1729
// Get snap to boundary
 
1730
int AudioDisplay::GetBoundarySnap(int ms,int rangeX,bool shiftHeld,bool start) {
 
1731
        // Range?
 
1732
        if (rangeX <= 0) return ms;
 
1733
 
 
1734
        // Convert range into miliseconds
 
1735
        int rangeMS = rangeX*samples*1000 / provider->GetSampleRate();
 
1736
 
 
1737
        // Keyframe boundaries
 
1738
        wxArrayInt boundaries;
 
1739
        bool snapKey = Options.AsBool(_T("Audio snap to keyframes"));
 
1740
        if (shiftHeld) snapKey = !snapKey;
 
1741
        if (snapKey && VideoContext::Get()->KeyFramesLoaded() && Options.AsBool(_T("Audio Draw Keyframes"))) {
 
1742
                int64_t keyMS;
 
1743
                wxArrayInt keyFrames = VideoContext::Get()->GetKeyFrames();
 
1744
                int frame;
 
1745
                for (unsigned int i=0;i<keyFrames.Count();i++) {
 
1746
                        frame = keyFrames[i];
 
1747
                        if (!start) frame--;
 
1748
                        if (frame < 0) frame = 0;
 
1749
                        keyMS = VFR_Output.GetTimeAtFrame(frame,start);
 
1750
                        //if (start) keyX++;
 
1751
                        if (GetXAtMS(keyMS) >= 0 && GetXAtMS(keyMS) < w) boundaries.Add(keyMS);
 
1752
                }
 
1753
        }
 
1754
 
 
1755
        // Other subtitles' boundaries
 
1756
        int inactiveType = Options.AsInt(_T("Audio Inactive Lines Display Mode"));
 
1757
        bool snapLines = Options.AsBool(_T("Audio snap to other lines"));
 
1758
        if (shiftHeld) snapLines = !snapLines;
 
1759
        if (snapLines && (inactiveType == 1 || inactiveType == 2)) {
 
1760
                AssDialogue *shade;
 
1761
                int shadeX1,shadeX2;
 
1762
                int shadeFrom,shadeTo;
 
1763
 
 
1764
                // Get range
 
1765
                if (inactiveType == 1) {
 
1766
                        shadeFrom = this->line_n-1;
 
1767
                        shadeTo = shadeFrom+1;
 
1768
                }
 
1769
                else {
 
1770
                        shadeFrom = 0;
 
1771
                        shadeTo = grid->GetRows();
 
1772
                }
 
1773
 
 
1774
                for (int j=shadeFrom;j<shadeTo;j++) {
 
1775
                        if (j == line_n) continue;
 
1776
                        shade = grid->GetDialogue(j);
 
1777
 
 
1778
                        if (shade) {
 
1779
                                // Get coordinates
 
1780
                                shadeX1 = GetXAtMS(shade->Start.GetMS());
 
1781
                                shadeX2 = GetXAtMS(shade->End.GetMS());
 
1782
                                if (shadeX1 >= 0 && shadeX1 < w) boundaries.Add(shade->Start.GetMS());
 
1783
                                if (shadeX2 >= 0 && shadeX2 < w) boundaries.Add(shade->End.GetMS());
 
1784
                        }
 
1785
                }
 
1786
        }
 
1787
 
 
1788
        // See if ms falls within range of any of them
 
1789
        int minDist = rangeMS+1;
 
1790
        int bestMS = ms;
 
1791
        for (unsigned int i=0;i<boundaries.Count();i++) {
 
1792
                if (abs(ms-boundaries[i]) < minDist) {
 
1793
                        bestMS = boundaries[i];
 
1794
                        minDist = abs(ms-boundaries[i]);
 
1795
                }
 
1796
        }
 
1797
 
 
1798
        // Return best match
 
1799
        return bestMS;
 
1800
}
 
1801
 
 
1802
 
 
1803
//
 
1804
// SCRUBBING CODE, REMOVED FROM THE FUNCTION ABOVE
 
1805
/*
 
1806
        // Stop scrubbing
 
1807
        bool scrubButton = false && event.ButtonIsDown(wxMOUSE_BTN_MIDDLE);
 
1808
        if (scrubbing && !scrubButton) {
 
1809
                // Release mouse
 
1810
                scrubbing = false;
 
1811
                if (HasCapture()) ReleaseMouse();
 
1812
 
 
1813
                // Stop player
 
1814
                player->Stop();
 
1815
                player->SetProvider(provider);
 
1816
                delete scrubProvider;
 
1817
        }
 
1818
 
 
1819
        // Start scrubbing
 
1820
        if (!scrubbing && scrubButton && provider->GetChannels() == 1) {
 
1821
                // Get mouse
 
1822
                CaptureMouse();
 
1823
                scrubbing = true;
 
1824
 
 
1825
                // Initialize provider
 
1826
                player->Stop();
 
1827
                scrubProvider = new StreamAudioProvider();
 
1828
                scrubProvider->SetParams(provider->GetChannels(),provider->GetSampleRate(),provider->GetBytesPerSample());
 
1829
                player->SetProvider(scrubProvider);
 
1830
 
 
1831
                // Set variables
 
1832
                scrubLastPos = GetSampleAtX(x);
 
1833
                scrubTime = clock();
 
1834
                scrubLastRate = provider->GetSampleRate();
 
1835
        }
 
1836
 
 
1837
        // Scrub
 
1838
        if (scrubbing && scrubButton) {
 
1839
                // Get current data
 
1840
                int64_t exactPos = MAX(0,GetSampleAtX(x));
 
1841
                int curScrubTime = clock();
 
1842
                int scrubDeltaTime = curScrubTime - scrubTime;
 
1843
                bool invert = exactPos < scrubLastPos;
 
1844
                int64_t curScrubPos = exactPos;
 
1845
 
 
1846
                if (scrubDeltaTime > 0) {
 
1847
                        // Get derived data
 
1848
                        int rateChange = provider->GetSampleRate()/20;
 
1849
                        int curRate = MID(int(scrubLastRate-rateChange),abs(int(exactPos - scrubLastPos)) * CLOCKS_PER_SEC / scrubDeltaTime,int(scrubLastRate+rateChange));
 
1850
                        if (abs(curRate-scrubLastRate) < rateChange) curRate = scrubLastRate;
 
1851
                        curScrubPos = scrubLastPos + (curRate * scrubDeltaTime / CLOCKS_PER_SEC * (invert ? -1 : 1));
 
1852
                        int64_t scrubDelta = curScrubPos - scrubLastPos;
 
1853
                        scrubLastRate = curRate;
 
1854
 
 
1855
                        // Copy data to buffer
 
1856
                        if (scrubDelta != 0) {
 
1857
                                // Create buffer
 
1858
                                int bufSize = scrubDeltaTime * scrubProvider->GetSampleRate() / CLOCKS_PER_SEC;
 
1859
                                short *buf = new short[bufSize];
 
1860
 
 
1861
                                // Flag as inverted, if necessary
 
1862
                                if (invert) scrubDelta = -scrubDelta;
 
1863
 
 
1864
                                // Copy data from original provider to temp buffer
 
1865
                                short *temp = new short[scrubDelta];
 
1866
                                provider->GetAudio(temp,MIN(curScrubPos,scrubLastPos),scrubDelta);
 
1867
 
 
1868
                                // Scale
 
1869
                                float scale = float(double(scrubDelta) / double(bufSize));
 
1870
                                float start,end;
 
1871
                                int istart,iend;
 
1872
                                float tempfinal;
 
1873
                                for (int i=0;i<bufSize;i++) {
 
1874
                                        start = i*scale;
 
1875
                                        end = (i+1)*scale;
 
1876
                                        istart = (int) start;
 
1877
                                        iend = MIN((int) end,scrubDelta-1);
 
1878
                                        if (istart == iend) tempfinal = temp[istart] * (end - start);
 
1879
                                        else {
 
1880
                                                tempfinal = temp[istart] * (1 + istart - start) + temp[iend] * (end - iend);
 
1881
                                                for (int j=istart+1;j<iend;j++) tempfinal += temp[i];
 
1882
                                        }
 
1883
                                        buf[i] = tempfinal / scale;
 
1884
                                }
 
1885
                                //int len = MIN(bufSize,scrubDelta);
 
1886
                                //for (int i=0;i<len;i++) buf[i] = temp[i];
 
1887
                                //for (int i=len;i<bufSize;i++) buf[i] = 0;
 
1888
                                delete temp;
 
1889
 
 
1890
                                // Invert
 
1891
                                if (invert) {
 
1892
                                        short aux;
 
1893
                                        for (int i=0;i<bufSize/2;i++) {
 
1894
                                                aux = buf[i];
 
1895
                                                buf[i] = buf[bufSize-i-1];
 
1896
                                                buf[bufSize-i-1] = aux;
 
1897
                                        }
 
1898
                                }
 
1899
 
 
1900
                                // Send data to provider
 
1901
                                scrubProvider->Append(buf,bufSize);
 
1902
                                if (!player->IsPlaying()) player->Play(0,~0ULL);
 
1903
                                delete buf;
 
1904
                        }
 
1905
                }
 
1906
 
 
1907
                // Update last pos and time
 
1908
                scrubLastPos = curScrubPos;
 
1909
                scrubTime = curScrubTime;
 
1910
 
 
1911
                // Return
 
1912
                return;
 
1913
        }
 
1914
 
 
1915
*/
 
1916
 
 
1917
 
 
1918
//////////////
 
1919
// Size event
 
1920
void AudioDisplay::OnSize(wxSizeEvent &event) {
 
1921
        // Set size
 
1922
        GetClientSize(&w,&h);
 
1923
        h -= Options.AsBool(_T("Audio Draw Timeline")) ? 20 : 0;
 
1924
 
 
1925
        // Update image
 
1926
        UpdateSamples();
 
1927
        if (samples) {
 
1928
                UpdatePosition(PositionSample / samples);
 
1929
        }
 
1930
        UpdateImage();
 
1931
        
 
1932
        // Update scrollbar
 
1933
        UpdateScrollbar();
 
1934
}
 
1935
 
 
1936
 
 
1937
///////////////
 
1938
// Timer event
 
1939
void AudioDisplay::OnUpdateTimer(wxTimerEvent &event) {
 
1940
        if (!origImage)
 
1941
                return;
 
1942
 
 
1943
        // Get lock and check if it's OK
 
1944
        if (player->GetMutex()) {
 
1945
                wxMutexLocker locker(*player->GetMutex());
 
1946
                if (!locker.IsOk()) return;
 
1947
        }
 
1948
                
 
1949
        if (!player->IsPlaying()) return;
 
1950
 
 
1951
        // Get DCs
 
1952
        //wxMutexGuiEnter();
 
1953
        wxClientDC dc(this);
 
1954
 
 
1955
        // Draw cursor
 
1956
        int curpos = -1;
 
1957
        if (player->IsPlaying()) {
 
1958
                int64_t curPos = player->GetCurrentPosition();
 
1959
                if (curPos > player->GetStartPosition() && curPos < player->GetEndPosition()) {
 
1960
                        // Scroll if needed
 
1961
                        int posX = GetXAtSample(curPos);
 
1962
                        bool fullDraw = false;
 
1963
                        bool centerLock = false;
 
1964
                        bool scrollToCursor = Options.AsBool(_T("Audio lock scroll on cursor"));
 
1965
                        if (centerLock) {
 
1966
                                int goTo = MAX(0,curPos - w*samples/2);
 
1967
                                if (goTo >= 0) {
 
1968
                                        UpdatePosition(goTo,true);
 
1969
                                        UpdateImage();
 
1970
                                        fullDraw = true;
 
1971
                                }
 
1972
                        }
 
1973
                        else {
 
1974
                                if (scrollToCursor) {
 
1975
                                        if (posX < 80 || posX > w-80) {
 
1976
                                                int goTo = MAX(0,curPos - 80*samples);
 
1977
                                                if (goTo >= 0) {
 
1978
                                                        UpdatePosition(goTo,true);
 
1979
                                                        UpdateImage();
 
1980
                                                        fullDraw = true;
 
1981
                                                }
 
1982
                                        }
 
1983
                                }
 
1984
                        }
 
1985
 
 
1986
                        // Draw cursor
 
1987
                        wxMemoryDC src;
 
1988
                        curpos = GetXAtSample(curPos);
 
1989
                        if (curpos >= 0 && curpos < GetClientSize().GetWidth()) {
 
1990
#ifdef __WXMAC__
 
1991
                                playbackCursorPos = curpos;
 
1992
                                Refresh();
 
1993
#else
 
1994
                                dc.SetPen(wxPen(Options.AsColour(_T("Audio Play cursor"))));
 
1995
                                src.SelectObject(*origImage);
 
1996
                                if (fullDraw) {
 
1997
                                        //dc.Blit(0,0,w,h,&src,0,0);
 
1998
                                        dc.DrawLine(curpos,0,curpos,h);
 
1999
                                        //dc.Blit(0,0,curpos-10,h,&src,0,0);
 
2000
                                        //dc.Blit(curpos+10,0,w-curpos-10,h,&src,curpos+10,0);
 
2001
                                }
 
2002
                                else {
 
2003
                                        dc.Blit(oldCurPos,0,1,h,&src,oldCurPos,0);
 
2004
                                        dc.DrawLine(curpos,0,curpos,h);
 
2005
                                }
 
2006
#endif
 
2007
                        }
 
2008
                }
 
2009
                else {
 
2010
                        if (curPos > player->GetEndPosition() + 8192) {
 
2011
                                player->Stop();
 
2012
                        }
 
2013
#ifdef __WXMAC__
 
2014
                        playbackCursorPos = -1;
 
2015
                        Refresh();
 
2016
#else
 
2017
                        wxMemoryDC src;
 
2018
                        src.SelectObject(*origImage);
 
2019
                        dc.Blit(oldCurPos,0,1,h,&src,oldCurPos,0);
 
2020
#endif
 
2021
                }
 
2022
        }
 
2023
 
 
2024
        // Restore background
 
2025
        else {
 
2026
#ifdef __WXMAC__
 
2027
                playbackCursorPos = -1;
 
2028
                Refresh();
 
2029
#else
 
2030
                wxMemoryDC src;
 
2031
                src.SelectObject(*origImage);
 
2032
                dc.Blit(oldCurPos,0,1,h,&src,oldCurPos,0);
 
2033
#endif
 
2034
        }
 
2035
        oldCurPos = curpos;
 
2036
        if (oldCurPos < 0) oldCurPos = 0;
 
2037
}
 
2038
 
 
2039
 
 
2040
////////////
 
2041
// Key down
 
2042
void AudioDisplay::OnKeyDown(wxKeyEvent &event) {
 
2043
        int key = event.GetKeyCode();
 
2044
#ifdef __APPLE__
 
2045
        Hotkeys.SetPressed(key,event.m_metaDown,event.m_altDown,event.m_shiftDown);
 
2046
#else
 
2047
        Hotkeys.SetPressed(key,event.m_controlDown,event.m_altDown,event.m_shiftDown);
 
2048
#endif
 
2049
 
 
2050
        // Accept
 
2051
        if (Hotkeys.IsPressed(_T("Audio Commit"))) {
 
2052
                CommitChanges(true);
 
2053
                //ChangeLine(1);
 
2054
        }
 
2055
 
 
2056
        // Accept (SSA's "Grab times")
 
2057
        if (Hotkeys.IsPressed(_T("Audio Commit Alt"))) {
 
2058
                CommitChanges(true);
 
2059
        }
 
2060
 
 
2061
        // Accept (stay)
 
2062
        if (Hotkeys.IsPressed(_T("Audio Commit (Stay)"))) {
 
2063
                CommitChanges();
 
2064
        }
 
2065
 
 
2066
        // Previous
 
2067
        if (Hotkeys.IsPressed(_T("Audio Prev Line")) || Hotkeys.IsPressed(_T("Audio Prev Line Alt"))) {
 
2068
                Prev();
 
2069
        }
 
2070
 
 
2071
        // Next
 
2072
        if (Hotkeys.IsPressed(_T("Audio Next Line")) || Hotkeys.IsPressed(_T("Audio Next Line Alt"))) {
 
2073
                Next();
 
2074
        }
 
2075
 
 
2076
        // Play
 
2077
        if (Hotkeys.IsPressed(_T("Audio Play")) || Hotkeys.IsPressed(_T("Audio Play Alt"))) {
 
2078
                int start=0,end=0;
 
2079
                GetTimesSelection(start,end);
 
2080
                Play(start,end);
 
2081
        }
 
2082
 
 
2083
        // Play/Stop
 
2084
        if (Hotkeys.IsPressed(_T("Audio Play or Stop"))) {
 
2085
                if (player->IsPlaying()) Stop();
 
2086
                else {
 
2087
                        int start=0,end=0;
 
2088
                        GetTimesSelection(start,end);
 
2089
                        Play(start,end);
 
2090
                }
 
2091
        }
 
2092
 
 
2093
        // Stop
 
2094
        if (Hotkeys.IsPressed(_T("Audio Stop"))) {
 
2095
                Stop();
 
2096
        }
 
2097
 
 
2098
        // Increase length
 
2099
        if (Hotkeys.IsPressed(_T("Audio Karaoke Increase Len"))) {
 
2100
                if (karaoke->enabled) {
 
2101
                        bool result = karaoke->SyllableDelta(karaoke->curSyllable,1,0);
 
2102
                        if (result) diagUpdated = true;
 
2103
                }
 
2104
        }
 
2105
 
 
2106
        // Increase length (shift)
 
2107
        if (Hotkeys.IsPressed(_T("Audio Karaoke Increase Len Shift"))) {
 
2108
                if (karaoke->enabled) {
 
2109
                        bool result = karaoke->SyllableDelta(karaoke->curSyllable,1,1);
 
2110
                        if (result) diagUpdated = true;
 
2111
                }
 
2112
        }
 
2113
 
 
2114
        // Decrease length
 
2115
        if (Hotkeys.IsPressed(_T("Audio Karaoke Decrease Len"))) {
 
2116
                if (karaoke->enabled) {
 
2117
                        bool result = karaoke->SyllableDelta(karaoke->curSyllable,-1,0);
 
2118
                        if (result) diagUpdated = true;
 
2119
                }
 
2120
        }
 
2121
 
 
2122
        // Decrease length (shift)
 
2123
        if (Hotkeys.IsPressed(_T("Audio Karaoke Decrease Len Shift"))) {
 
2124
                if (karaoke->enabled) {
 
2125
                        bool result = karaoke->SyllableDelta(karaoke->curSyllable,-1,1);
 
2126
                        if (result) diagUpdated = true;
 
2127
                }
 
2128
        }
 
2129
 
 
2130
        // Move backwards
 
2131
        if (Hotkeys.IsPressed(_T("Audio Scroll Left"))) {
 
2132
                UpdatePosition(Position-128,false);
 
2133
                UpdateImage();
 
2134
        }
 
2135
 
 
2136
        // Move forward
 
2137
        if (Hotkeys.IsPressed(_T("Audio Scroll Right"))) {
 
2138
                UpdatePosition(Position+128,false);
 
2139
                UpdateImage();
 
2140
        }
 
2141
 
 
2142
        // Play first 500 ms
 
2143
        if (Hotkeys.IsPressed(_T("Audio Play First 500ms"))) {
 
2144
                int start=0,end=0;
 
2145
                GetTimesSelection(start,end);
 
2146
                int e = start+500;
 
2147
                if (e > end) e = end;
 
2148
                Play(start,e);
 
2149
        }
 
2150
 
 
2151
        // Play last 500 ms
 
2152
        if (Hotkeys.IsPressed(_T("Audio Play Last 500ms"))) {
 
2153
                int start=0,end=0;
 
2154
                GetTimesSelection(start,end);
 
2155
                int s = end-500;
 
2156
                if (s < start) s = start;
 
2157
                Play(s,end);
 
2158
        }
 
2159
 
 
2160
        // Play 500 ms before
 
2161
        if (Hotkeys.IsPressed(_T("Audio Play 500ms Before"))) {
 
2162
                int start=0,end=0;
 
2163
                GetTimesSelection(start,end);
 
2164
                Play(start-500,start);
 
2165
        }
 
2166
 
 
2167
        // Play 500 ms after
 
2168
        if (Hotkeys.IsPressed(_T("Audio Play 500ms After"))) {
 
2169
                int start=0,end=0;
 
2170
                GetTimesSelection(start,end);
 
2171
                Play(end,end+500);
 
2172
        }
 
2173
 
 
2174
        // Play to end of file
 
2175
        if (Hotkeys.IsPressed(_T("Audio Play To End"))) {
 
2176
                int start=0,end=0;
 
2177
                GetTimesSelection(start,end);
 
2178
                Play(start,-1);
 
2179
        }
 
2180
 
 
2181
        // Play original line
 
2182
        if (Hotkeys.IsPressed(_T("Audio Play Original Line"))) {
 
2183
                int start=0,end=0;
 
2184
                GetTimesDialogue(start,end);
 
2185
                SetSelection(start, end);
 
2186
                Play(start,end);
 
2187
        }
 
2188
 
 
2189
        // Lead in
 
2190
        if (Hotkeys.IsPressed(_T("Audio Add Lead In"))) {
 
2191
                AddLead(true,false);
 
2192
        }
 
2193
 
 
2194
        // Lead out
 
2195
        if (Hotkeys.IsPressed(_T("Audio Add Lead Out"))) {
 
2196
                AddLead(false,true);
 
2197
        }
 
2198
 
 
2199
        // Update
 
2200
        if (diagUpdated) {
 
2201
                diagUpdated = false;
 
2202
                NeedCommit = true;
 
2203
                if (Options.AsBool(_T("Audio Autocommit")) && curStartMS <= curEndMS) CommitChanges();
 
2204
                else UpdateImage(true);
 
2205
        }
 
2206
}
 
2207
 
 
2208
 
 
2209
///////////////
 
2210
// Change line
 
2211
void AudioDisplay::ChangeLine(int delta, bool block) {
 
2212
        if (dialogue) {
 
2213
                // Get next line number and make sure it's within bounds
 
2214
                int next;
 
2215
                if (block && grid->IsInSelection(line_n)) next = grid->GetLastSelRow()+delta;
 
2216
                else next = line_n+delta;
 
2217
 
 
2218
                if (next == -1) next = 0;
 
2219
                if (next == grid->GetRows()) next = grid->GetRows() - 1;
 
2220
 
 
2221
                // Set stuff
 
2222
                NeedCommit = false;
 
2223
                dialogue = NULL;
 
2224
                grid->editBox->SetToLine(next);
 
2225
                grid->SelectRow(next);
 
2226
                grid->MakeCellVisible(next,0,true);
 
2227
                if (!dialogue) UpdateImage(true);
 
2228
                else UpdateImage(false);
 
2229
                line_n = next;
 
2230
        }
 
2231
}
 
2232
 
 
2233
 
 
2234
////////
 
2235
// Next
 
2236
void AudioDisplay::Next(bool play) {
 
2237
        // Karaoke
 
2238
        if (karaoke->enabled) {
 
2239
                int nextSyl = karaoke->curSyllable+1;
 
2240
                bool needsUpdate = true;
 
2241
 
 
2242
                // Last syllable; jump to next
 
2243
                if (nextSyl >= (signed int)karaoke->syllables.size()) {
 
2244
                        // Already last?
 
2245
                        if (line_n == grid->GetRows()-1) return;
 
2246
 
 
2247
                        if (NeedCommit) {
 
2248
                                int result = wxMessageBox(_("Do you want to commit your changes? If you choose No, they will be discarded."),_("Commit?"),wxYES_NO | wxCANCEL | wxICON_QUESTION);
 
2249
                                //int result = wxNO;
 
2250
                                if (result == wxYES) {
 
2251
                                        CommitChanges();
 
2252
                                }
 
2253
                                else if (result == wxCANCEL) {
 
2254
                                        karaoke->curSyllable = (int)karaoke->syllables.size()-1;
 
2255
                                        return;
 
2256
                                }
 
2257
                        }
 
2258
                        nextSyl = 0;
 
2259
                        karaoke->curSyllable = 0;
 
2260
                        ChangeLine(1);
 
2261
                        needsUpdate = false;
 
2262
                }
 
2263
 
 
2264
                // Set syllable
 
2265
                karaoke->SetSyllable(nextSyl);
 
2266
                if (needsUpdate) Update();
 
2267
                int start=0,end=0;
 
2268
                GetTimesSelection(start,end);
 
2269
                if (play) Play(start,end);
 
2270
        }
 
2271
 
 
2272
        // Plain mode
 
2273
        else {
 
2274
                ChangeLine(1);
 
2275
        }
 
2276
}
 
2277
 
 
2278
 
 
2279
////////////
 
2280
// Previous
 
2281
void AudioDisplay::Prev(bool play) {
 
2282
        // Karaoke
 
2283
        if (karaoke->enabled) {
 
2284
                int nextSyl = karaoke->curSyllable-1;
 
2285
                bool needsUpdate = true;
 
2286
 
 
2287
                // First syllable; jump line
 
2288
                if (nextSyl < 0) {
 
2289
                        // Already first?
 
2290
                        if (line_n == 0) return;
 
2291
 
 
2292
                        if (NeedCommit) {
 
2293
                                int result = wxMessageBox(_("Do you want to commit your changes? If you choose No, they will be discarded."),_("Commit?"),wxYES_NO | wxCANCEL);
 
2294
                                if (result == wxYES) {
 
2295
                                        CommitChanges();
 
2296
                                }
 
2297
                                else if (result == wxCANCEL) {
 
2298
                                        karaoke->curSyllable = 0;
 
2299
                                        return;
 
2300
                                }
 
2301
                        }
 
2302
                        karaoke->curSyllable = -1;
 
2303
                        ChangeLine(-1);
 
2304
                        needsUpdate = false;
 
2305
                }
 
2306
 
 
2307
                // Set syllable
 
2308
                karaoke->SetSyllable(nextSyl);
 
2309
                if (needsUpdate) Update();
 
2310
                int start=0,end=0;
 
2311
                GetTimesSelection(start,end);
 
2312
                if (play) Play(start,end);
 
2313
        }
 
2314
 
 
2315
        // Plain mode
 
2316
        else {
 
2317
                ChangeLine(-1);
 
2318
        }
 
2319
}
 
2320
 
 
2321
 
 
2322
///////////////////////////////
 
2323
// Gets syllable at x position
 
2324
int AudioDisplay::GetSyllableAtX(int x) {
 
2325
        if (!karaoke->enabled) return -1;
 
2326
        int ms = GetMSAtX(x);
 
2327
        size_t syllables = karaoke->syllables.size();;
 
2328
        int sylstart,sylend;
 
2329
 
 
2330
        // Find a matching syllable
 
2331
        for (size_t i=0;i<syllables;i++) {
 
2332
                sylstart = karaoke->syllables.at(i).start_time*10 + curStartMS;
 
2333
                sylend = karaoke->syllables.at(i).duration*10 + sylstart;
 
2334
                if (ms >= sylstart && ms < sylend) {
 
2335
                        return (int)i;
 
2336
                }
 
2337
        }
 
2338
        return -1;
 
2339
}
 
2340
 
 
2341
 
 
2342
////////////////
 
2343
// Focus events
 
2344
void AudioDisplay::OnGetFocus(wxFocusEvent &event) {
 
2345
        if (!hasFocus) {
 
2346
                hasFocus = true;
 
2347
                UpdateImage(true);
 
2348
        }
 
2349
}
 
2350
 
 
2351
void AudioDisplay::OnLoseFocus(wxFocusEvent &event) {
 
2352
        if (hasFocus && loaded) {
 
2353
                hasFocus = false;
 
2354
                UpdateImage(true);
 
2355
                Refresh(false);
 
2356
        }
 
2357
}
 
2358
 
 
2359
 
 
2360
//////////////////////////////
 
2361
// Update time edit controls
 
2362
bool AudioDisplay::UpdateTimeEditCtrls() {
 
2363
        // Make sure this does NOT get short-circuit evaluation,
 
2364
        // this is why binary OR instead of logical OR is used.
 
2365
        // All three time edits must always be updated.
 
2366
        return
 
2367
                grid->editBox->StartTime->SetTime(curStartMS,true) |
 
2368
                grid->editBox->EndTime->SetTime(curEndMS,true) |
 
2369
                grid->editBox->Duration->SetTime(curEndMS-curStartMS,true);
 
2370
}