// Copyright (c) 2005, Rodrigo Braz Monteiro // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Aegisub Group nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // ----------------------------------------------------------------------------- // // AEGISUB // // Website: http://aegisub.cellosoft.com // Contact: mailto:zeratul@cellosoft.com // /////////// // Headers #include #include "audio_display.h" #include "main.h" #include "ass_dialogue.h" #include "subs_grid.h" #include "ass_file.h" #include "subs_edit_box.h" #include "options.h" #include "audio_karaoke.h" #include "audio_box.h" #include "fft.h" #include "video_display.h" #include "vfr.h" #include "colorspace.h" #include "hotkeys.h" #include "utils.h" /////////////// // Constructor AudioDisplay::AudioDisplay(wxWindow *parent,VideoDisplay *display) : wxWindow (parent, -1, wxDefaultPosition, wxSize(200,Options.AsInt(_T("Audio Display Height"))), wxSUNKEN_BORDER | wxWANTS_CHARS , _T("Audio Display")) { // Set variables origImage = NULL; spectrumDisplay = NULL; ScrollBar = NULL; dialogue = NULL; karaoke = NULL; peak = NULL; min = NULL; hasSel = false; diagUpdated = false; NeedCommit = false; loaded = false; blockUpdate = false; dontReadTimes = false; Position = 0; PositionSample = 0; oldCurPos = 0; scale = 1.0f; provider = NULL; video = display; hold = 0; hasFocus = (wxWindow::FindFocus() == this); // Init UpdateTimer.SetOwner(this,Audio_Update_Timer); GetVirtualSize(&w,&h); SetSamplesPercent(50,false); // Set cursor //wxCursor cursor(wxCURSOR_BLANK); //SetCursor(cursor); //wxLog::SetActiveTarget(new wxLogWindow(NULL,_T("Log"),true,false)); } ////////////// // Destructor AudioDisplay::~AudioDisplay() { if (provider) delete provider; if (origImage) delete origImage; if (spectrumDisplay) delete spectrumDisplay; if (peak) delete peak; if (min) delete min; } ///////// // Reset void AudioDisplay::Reset() { hasSel = false; diagUpdated = false; NeedCommit = false; karaoke->enabled = false; karaoke->syllables.clear(); box->karaokeMode = false; box->KaraokeButton->SetValue(false); dialogue = NULL; } //////////////// // Update image void AudioDisplay::UpdateImage(bool weak) { // Prepare bitmap if (origImage) { if (origImage->GetWidth() != w || origImage->GetHeight() != h) { delete origImage; origImage = NULL; } } bool draw_boundary_lines = Options.AsBool(_T("Audio Draw Secondary Lines")); bool draw_selection_background = Options.AsBool(_T("Audio Draw Selection Background")); // Invalid dimensions if (w == 0 || h == 0) return; // New bitmap if (!origImage) origImage = new wxBitmap(w,h,-1); // Is spectrum? bool spectrum = false; if (provider && Options.AsBool(_T("Audio Spectrum"))) { spectrum = true; } // Update samples UpdateSamples(); // Draw image to be displayed wxMemoryDC dc; dc.SelectObject(*origImage); dc.BeginDrawing(); // Black background dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Background")))); dc.DrawRectangle(0,0,w,h); // Selection position hasSel = false; hasKaraoke = karaoke->enabled; selStart = 0; selEnd = 0; lineStart = 0; lineEnd = 0; selStartCap = 0; selEndCap = 0; if (dialogue) { GetDialoguePos(lineStart,lineEnd,false); hasSel = true; if (hasKaraoke) { GetKaraokePos(selStartCap,selEndCap,true); GetKaraokePos(selStart,selEnd,false); } else { GetDialoguePos(selStartCap,selEndCap,true); selStart = lineStart; selEnd = lineEnd; } } // Draw selection bg if (hasSel && selStart < selEnd && draw_selection_background) { if (NeedCommit && !karaoke->enabled) dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Selection Background Modified")))); else dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Selection Background")))); dc.DrawRectangle(selStart,0,selEnd-selStart,h); } // Draw spectrum if (spectrum) { DrawSpectrum(dc,weak); // Invert the selection, if any if (hasSel && selStart < selEnd && Options.AsBool(_T("Audio Spectrum invert selection"))) { dc.Blit(selStart, 0, selEnd-selStart, h, &dc, selStart, 0, wxSRC_INVERT); } } // Draw seconds boundaries if (draw_boundary_lines) { __int64 start = Position*samples; int rate = provider ? provider->GetSampleRate() : 0; //fix me? int pixBounds = rate / samples; dc.SetPen(wxPen(Options.AsColour(_T("Audio Seconds Boundaries")),1,wxDOT)); if (pixBounds >= 8) { for (int x=0;xloaded && draw_boundary_lines) { int nKeys = video->KeyFrames.Count(); dc.SetPen(wxPen(wxColour(255,0,255),1)); // Get min and max frames to care about int minFrame = VFR_Output.CorrectFrameAtTime(GetMSAtX(0),true); int maxFrame = VFR_Output.CorrectFrameAtTime(GetMSAtX(w),true); // Scan list for (int i=0;iKeyFrames[i]; if (cur >= minFrame && cur <= maxFrame) { int x = GetXAtMS(VFR_Output.CorrectTimeAtFrame(cur,true)); dc.DrawLine(x,0,x,h); } else if (cur > maxFrame) break; } } // Waveform if (provider) { if (!spectrum) DrawWaveform(dc,weak); } // Nothing else { dc.DrawLine(0,h/2,w,h/2); } // Draw previous line int shadeType = Options.AsInt(_T("Audio Inactive Lines Display Mode")); if (shadeType == 1 || shadeType == 2) { dc.SetBrush(wxBrush(Options.AsColour(_T("Audio Line boundary inactive line")))); int selWidth = Options.AsInt(_T("Audio Line boundaries Thickness")); AssDialogue *shade; int shadeX1,shadeX2; int shadeFrom,shadeTo; // Only previous if (shadeType == 1) { shadeFrom = this->line_n-1; shadeTo = shadeFrom+1; } // All else { shadeFrom = 0; shadeTo = grid->GetRows(); } for (int j=shadeFrom;jGetDialogue(j); if (shade) { // Get coordinates shadeX1 = GetXAtMS(shade->Start.GetMS()); shadeX2 = GetXAtMS(shade->End.GetMS()); if (shadeX2 < 0 || shadeX1 > w) continue; // Draw over waveform if (!spectrum) { int x1 = MAX(0,shadeX1); int x2 = MIN(w,shadeX2); dc.SetPen(wxPen(Options.AsColour(_T("Audio Waveform Inactive")))); for (__int64 i=x1;isyllables.size(); __int64 pos1,pos2; int len,curpos; wxCoord tw=0,th=0; KaraokeSyllable *curSyl; wxString temptext; // Draw syllables for (size_t i=0;isyllables.at(i); len = curSyl->length*10; curpos = curSyl->position*10; if (len != -1) { pos1 = GetXAtMS(curStartMS+curpos); pos2 = GetXAtMS(curStartMS+len+curpos); dc.DrawLine(pos2,0,pos2,h); temptext = curSyl->contents; temptext.Trim(true); temptext.Trim(false); GetTextExtent(temptext,&tw,&th,NULL,NULL,&curFont); dc.DrawText(temptext,(pos1+pos2-tw)/2,h-th-4); } } catch (...) { } } } } // Modified text if (NeedCommit) { dc.SetFont(wxFont(9,wxDEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD,false,_T("Verdana"))); dc.SetTextForeground(wxColour(255,0,0)); if (selStart <= selEnd) { dc.DrawText(_T("Modified"),4,4); } else { dc.DrawText(_T("Negative time"),4,4); } } // Draw selection border if (hasFocus) { dc.SetPen(*wxGREEN_PEN); dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.DrawRectangle(0,0,w,h); } // Done dc.EndDrawing(); } //////////// // Waveform void AudioDisplay::DrawWaveform(wxDC &dc,bool weak) { // Prepare Waveform if (!weak || peak == NULL || min == NULL) { if (peak) delete peak; if (min) delete min; peak = new int[w]; min = new int[w]; } // Get waveform if (!weak) { provider->GetWaveForm(min,peak,Position*samples,w,h,samples,scale); } // Draw pre-selection if (!hasSel) selStartCap = w; dc.SetPen(wxPen(Options.AsColour(_T("Audio Waveform")))); for (__int64 i=0;ienabled) dc.SetPen(wxPen(Options.AsColour(_T("Audio Waveform Modified")))); else dc.SetPen(wxPen(Options.AsColour(_T("Audio Waveform Selected")))); } for (__int64 i=selStartCap;iGetWidth() != w || spectrumDisplay->GetHeight() != h) { if (spectrumDisplay) delete spectrumDisplay; //spectrumDisplay = new wxBitmap(w,h); weak = false; } if (!weak) { // Generate colors //int colorMap[256]; //unsigned short colorMap16[256]; if (!colorMapsGenerated) { unsigned char r,g,b; for (int i=0;i<256;i++) { //HSVtoRGB(255-i,1.0f - 0.3f*(i/255.0f),0.3f + 0.7f*(i/255.0f),r,g,b); //hsv_to_rgb(255-i, 1.0f - 0.3f * (i/255.0f), 0.3f + 0.7f * (i/255.0f), &r, &g, &b); hsv_to_rgb(255 - i, 255 - i * 3/10, 255*3/10 + i * 7/10, &r, &g, &b); spectrumColorMap[i] = b | (g<<8) | (r<<16); spectrumColorMap16[i] = ((r>>3)<<11) | ((g>>2)<<5) | b>>3; } colorMapsGenerated = true; } int depth = wxDisplayDepth(); // Prepare arrays int cutOff = Options.AsInt(_T("Audio Spectrum Cutoff")); int window = 1 << Options.AsInt(_T("Audio Spectrum Window")); int totalLen = w*samples+window; float *raw_float = new float[totalLen]; short *raw_int = new short[totalLen]; float *in = raw_float; // Fill input __int64 start = Position*samples; provider->GetAudio(raw_int,start,totalLen); for (int j=0;j 255) intensity = 255; if (intensity < 0) intensity = 0; // Write and go to next row if (depth == 32) { write_ptr -= w; *write_ptr = spectrumColorMap[intensity]; } else if (depth == 16) { write_ptr16 -= w; *write_ptr16 = spectrumColorMap16[intensity]; } //dc.SetPen(pen[intensity]); //dc.DrawPoint(i,y); y--; if (y == 0) break; accumPos = posThres; accum = 0; } } } // Clear top of image int filly = h - (halfwindow-cutOff)/posThres; if (filly < 0) filly = 0; if (depth == 32) { int color = spectrumColorMap[0]; for (int y=0;y= w) selStart = w-1; if (selEnd >= w) selEnd = w-1; } } //////////////////////// // Get karaoke position void AudioDisplay::GetKaraokePos(__int64 &karStart,__int64 &karEnd, bool cap) { // Wrap around int nsyls = karaoke->syllables.size(); if (karaoke->curSyllable == -1) { karaoke->SetSyllable(nsyls-1); } if (karaoke->curSyllable >= nsyls) karaoke->curSyllable = nsyls-1; // Get positions int pos = karaoke->syllables.at(karaoke->curSyllable).position; int len = karaoke->syllables.at(karaoke->curSyllable).length; karStart = GetXAtMS(curStartMS+pos*10); karEnd = GetXAtMS(curStartMS+pos*10+len*10); // Cap if (cap) { if (karStart < 0) karStart = 0; if (karEnd < 0) karEnd = 0; if (karStart >= w) karStart = w-1; if (karEnd >= w) karEnd = w-1; } } ////////// // Update void AudioDisplay::Update() { if (blockUpdate) return; if (loaded) { if (Options.AsBool(_T("Audio Autoscroll"))) MakeDialogueVisible(); else { UpdateImage(true); Refresh(false); } } } ///////////////////////// // Make dialogue visible void AudioDisplay::MakeDialogueVisible(bool force) { // Variables int temp1=0,temp2=0; GetTimesSelection(temp1,temp2); int startPos = GetSampleAtMS(temp1); int endPos = GetSampleAtMS(temp2); int startX = GetXAtMS(temp1); int endX = GetXAtMS(temp2); if (force || startX < 50 || endX > w-50) { UpdatePosition((startPos+endPos-w*samples)/2,true); } // Update UpdateImage(); Refresh(false); } //////////////// // Set position void AudioDisplay::SetPosition(int pos) { Position = pos; PositionSample = pos * samples; UpdateImage(); Refresh(false); } /////////////////// // Update position void AudioDisplay::UpdatePosition (int pos,bool IsSample) { // Safeguards if (IsSample) pos /= samples; int len = provider->GetNumSamples() / samples; if (pos < 0) pos = 0; if (pos >= len) pos = len-1; // Set Position = pos; PositionSample = pos*samples; UpdateScrollbar(); } ///////////////////////////// // Set samples in percentage // Note: aka Horizontal Zoom void AudioDisplay::SetSamplesPercent(int percent,bool update,float pivot) { // Calculate if (percent < 1) percent = 1; if (percent > 100) percent = 100; if (samplesPercent == percent) return; samplesPercent = percent; // Update if (update) { // Center scroll int oldSamples = samples; UpdateSamples(); PositionSample += (oldSamples-samples)*w*pivot; if (PositionSample < 0) PositionSample = 0; // Update UpdateSamples(); UpdateScrollbar(); UpdateImage(); Refresh(false); } } ////////////////// // Update samples void AudioDisplay::UpdateSamples() { // Set samples __int64 totalSamples = provider ? provider->GetNumSamples() : 0; //fix me? int total = totalSamples / w; int max = 5760000 / w; // 2 minutes at 48 kHz maximum if (total > max) total = max; int min = 8; if (total < min) total = min; int range = total-min; samples = range*pow(samplesPercent/100.0,3)+min; // Set position int length = w * samples; if (PositionSample + length > totalSamples) { PositionSample = totalSamples - length; if (PositionSample < 0) PositionSample = 0; Position = PositionSample / samples; } } ///////////// // Set scale void AudioDisplay::SetScale(float _scale) { if (scale == _scale) return; scale = _scale; UpdateImage(); Refresh(false); } ////////////////// // Load from file void AudioDisplay::SetFile(wxString file) { if (file == _T("")) { if (provider) delete provider; provider = NULL; Reset(); } else { SetFile(_T("")); try { provider = new AudioProvider(file, this); // Add to recent Options.AddToRecentList(file,_T("Recent aud")); } catch (wxString &err) { wxMessageBox(err,_T("Error loading audio"),wxICON_ERROR | wxOK); } } loaded = (provider != NULL); // Set default selection int n = grid->editBox->linen; SetDialogue(grid,grid->GetDialogue(n),n); // Update UpdateImage(); Refresh(false); } /////////////////// // Load from video void AudioDisplay::SetFromVideo() { if (video->loaded) { wxString extension = video->videoName.Right(4); extension.LowerCase(); if (extension != _T(".d2v")) SetFile(video->videoName); } } //////////////////// // Update scrollbar void AudioDisplay::UpdateScrollbar() { int page = w/12; int len = provider->GetNumSamples() / samples / 12; Position = PositionSample / samples; ScrollBar->SetScrollbar(Position/12,page,len,page*0.7,true); } ////////////////////////////////////////////// // Gets the sample number at the x coordinate __int64 AudioDisplay::GetSampleAtX(int x) { return (x+Position)*samples; } ///////////////////////////////////////////////// // Gets the x coordinate corresponding to sample int AudioDisplay::GetXAtSample(__int64 n) { return (n/samples)-Position; } ///////////////// // Get MS from X int AudioDisplay::GetMSAtX(__int64 x) { //fix me? if (provider) return (PositionSample+(x*samples)) * 1000 / provider->GetSampleRate(); return 0; } ///////////////// // Get X from MS int AudioDisplay::GetXAtMS(__int64 ms) { //fix me? if (provider) return ((ms * provider->GetSampleRate() / 1000)-PositionSample)/samples; return 0; } //////////////////// // Get MS At sample int AudioDisplay::GetMSAtSample(__int64 x) { //fix me? if (provider) return x * 1000 / provider->GetSampleRate(); return 0; } //////////////////// // Get Sample at MS __int64 AudioDisplay::GetSampleAtMS(__int64 ms) { //fix me? if (provider) return ms * provider->GetSampleRate() / 1000; return 0; } //////// // Play void AudioDisplay::Play(int start,int end) { // Check provider if (!provider) return; // Set defaults __int64 num_samples = provider->GetNumSamples(); start = GetSampleAtMS(start); if (end != -1) end = GetSampleAtMS(end); else end = num_samples-1; // Sanity checking if (start < 0) start = 0; if (start >= num_samples) start = num_samples-1; if (end < 0) end = 0; if (end >= num_samples) end = num_samples-1; if (end < start) end = start; // Call play provider->Play(start,end-start); } //////// // Stop void AudioDisplay::Stop() { if (!provider) return; provider->Stop(); } /////////////////////////// // Get samples of dialogue void AudioDisplay::GetTimesDialogue(int &start,int &end) { if (!dialogue) { start = 0; end = 0; return; } start = dialogue->Start.GetMS(); end = dialogue->End.GetMS(); } //////////////////////////// // Get samples of selection void AudioDisplay::GetTimesSelection(int &start,int &end) { if (!dialogue) { start = 0; end = 0; return; } if (karaoke->enabled) { int pos = karaoke->syllables.at(karaoke->curSyllable).position; int len = karaoke->syllables.at(karaoke->curSyllable).length; start = curStartMS+pos*10; end = curStartMS+pos*10+len*10; } else { start = curStartMS; end = curEndMS; } } ///////////////////////////// // Set the current selection void AudioDisplay::SetSelection(int start, int end) { curStartMS = start; curEndMS = end; Update(); } //////////////// // Set dialogue void AudioDisplay::SetDialogue(SubtitlesGrid *_grid,AssDialogue *diag,int n) { // Actual parameters if (_grid) { // Set variables grid = _grid; line_n = n; dialogue = diag; // Set flags diagUpdated = false; NeedCommit = false; // Set times if (dialogue && !dontReadTimes) { curStartMS = dialogue->Start.GetMS(); curEndMS = dialogue->End.GetMS(); } } // Read karaoke data if (dialogue && karaoke->enabled) { NeedCommit = karaoke->LoadFromDialogue(dialogue); // Reset karaoke pos if (karaoke->curSyllable == -1) karaoke->SetSyllable(karaoke->syllables.size()-1); else karaoke->SetSyllable(0); } // Update Update(); } ////////////////// // Commit changes void AudioDisplay::CommitChanges () { if (!Options.AsBool(_T("Audio SSA Mode"))) { // Check if there's any need to commit if (!NeedCommit) return; // Check if selection is valid if (curEndMS < curStartMS) { wxMessageBox(_T("Start time must be before end time!"),_T("Error commiting"),wxICON_ERROR); return; } } // Reset flags diagUpdated = false; NeedCommit = false; // Update karaoke int karSyl = 0; bool wasKaraSplitting = false; if (karaoke->enabled) { wasKaraSplitting = box->audioKaraoke->splitting; karaoke->Commit(); karSyl = karaoke->curSyllable; } // Update dialogue blockUpdate = true; dialogue->Start.SetMS(curStartMS); dialogue->End.SetMS(curEndMS); dialogue->UpdateData(); grid->SetRowToLine(line_n,dialogue); grid->editBox->Update(!karaoke->enabled); grid->ass->FlagAsModified(); grid->CommitChanges(); karaoke->curSyllable = karSyl; blockUpdate = false; // If in SSA mode, select next line and "move timing forward" (unless the user was splitting karaoke) if (Options.AsBool(_T("Audio SSA Mode")) && Options.AsBool(_T("Audio SSA Next Line on Commit")) && !wasKaraSplitting) { dontReadTimes = true; Next(); dontReadTimes = false; curStartMS = curEndMS; curEndMS = curStartMS + Options.AsInt(_T("Timing Default Duration")); } Update(); } //////////// // Add lead void AudioDisplay::AddLead(bool in,bool out) { // Lead in if (in) { curStartMS -= Options.AsInt(_T("Audio Lead in")); if (curStartMS < 0) curStartMS = 0; } // Lead out if (out) { curEndMS += Options.AsInt(_T("Audio Lead out")); } // Set changes NeedCommit = true; if (Options.AsBool(_T("Audio Autocommit"))) CommitChanges(); Update(); } /////////////// // Event table BEGIN_EVENT_TABLE(AudioDisplay, wxWindow) EVT_MOUSE_EVENTS(AudioDisplay::OnMouseEvent) EVT_PAINT(AudioDisplay::OnPaint) EVT_SIZE(AudioDisplay::OnSize) EVT_TIMER(Audio_Update_Timer,AudioDisplay::OnUpdateTimer) EVT_KEY_DOWN(AudioDisplay::OnKeyDown) EVT_SET_FOCUS(AudioDisplay::OnGetFocus) EVT_KILL_FOCUS(AudioDisplay::OnLoseFocus) END_EVENT_TABLE() ///////// // Paint void AudioDisplay::OnPaint(wxPaintEvent& event) { if (w == 0 || h == 0) return; wxPaintDC dc(this); dc.BeginDrawing(); dc.DrawBitmap(*origImage,0,0); dc.EndDrawing(); } /////////////// // Mouse event void AudioDisplay::OnMouseEvent(wxMouseEvent& event) { // Get x,y __int64 x = event.GetX(); __int64 y = event.GetY(); bool karMode = karaoke->enabled; bool shiftDown = event.m_shiftDown; bool ctrlDown = event.m_controlDown; // Is inside? bool inside = false; if (x >= 0 && y >= 0 && x < w && y < h) { inside = true; // Get focus if (wxWindow::FindFocus() != this && Options.AsBool(_T("Audio Autofocus"))) SetFocus(); } // Mouse wheel if (event.GetWheelRotation() != 0) { // Zoom or scroll? bool zoom = shiftDown; if (Options.AsBool(_T("Audio Wheel Default To Zoom"))) zoom = !zoom; // Zoom if (zoom) { int step = -event.GetWheelRotation() / event.GetWheelDelta(); int value = box->HorizontalZoom->GetValue()+step; box->HorizontalZoom->SetValue(value); SetSamplesPercent(value,true,float(x)/float(w)); } // Scroll else { int step = event.GetWheelRotation() * w / 360; UpdatePosition(Position+step,false); //SetFocus(); UpdateImage(); Refresh(false); } } // Cursor drawing if (!provider->playing) { // Draw bg wxClientDC dc(this); dc.BeginDrawing(); dc.DrawBitmap(*origImage,0,0); if (inside) { // Draw cursor dc.SetLogicalFunction(wxINVERT); dc.DrawLine(x,0,x,h); // Time string AssTime time; time.SetMS(GetMSAtX(x)); wxString text = time.GetASSFormated(); // Calculate metrics wxFont font(10,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD,false,_T("Verdana")); dc.SetFont(font); int tw,th; GetTextExtent(text,&tw,&th,NULL,NULL,&font); // Set inversion bool left = true; if (x > w/2) left = false; // Text coordinates int dx; dx = x - tw/2; if (dx < 4) dx = 4; int max = w - tw - 4; if (dx > max) dx = max; int dy = 4; // Draw text dc.SetTextForeground(wxColour(64,64,64)); dc.DrawText(text,dx+1,dy-1); dc.DrawText(text,dx+1,dy+1); dc.DrawText(text,dx-1,dy-1); dc.DrawText(text,dx-1,dy+1); dc.SetTextForeground(wxColour(255,255,255)); dc.DrawText(text,dx,dy); } // End drawing dc.EndDrawing(); } // Left click if (event.ButtonDown(wxMOUSE_BTN_LEFT)) { if (wxWindow::FindFocus() != this) { SetFocus(); return; } } // Right click if (event.ButtonDown(wxMOUSE_BTN_RIGHT)) { if (karaoke->enabled) { int syl = GetSyllableAtX(x); if (syl != -1) { int start = karaoke->syllables.at(syl).position * 10 + dialogue->Start.GetMS(); int count = karaoke->syllables.at(syl).length * 10; provider->Play(GetSampleAtMS(start),GetSampleAtMS(count)); } } } bool defCursor = true; // Substation alpha mode if (Options.AsBool(_T("Audio SSA Mode")) && !karaoke->enabled && dialogue) { bool mod = false; // Set start if (event.ButtonDown(wxMOUSE_BTN_LEFT)) { curStartMS = GetMSAtX(x); mod = true; } // Set end if (event.ButtonDown(wxMOUSE_BTN_RIGHT)) { curEndMS = GetMSAtX(x); mod = true; provider->endPos = GetSampleAtX(x); } // Modified, commit changes if (mod) { // Swap if needed if (false && curStartMS > curEndMS) { int aux = curStartMS; curStartMS = curEndMS; curEndMS = aux; } // Commit NeedCommit = true; if (Options.AsBool(_T("Audio SSA Allow Autocommit")) && Options.AsBool(_T("Audio Autocommit")) && curStartMS <= curEndMS) { CommitChanges(); } else { UpdateImage(true); Refresh(false); } } } // Standard mode else { // Moving around if (hasSel) { // Grab start/end if (hold == 0) { bool gotGrab = false; // Grab start if (!karMode) { if (abs64 (x - selStart) < 6) { wxCursor cursor(wxCURSOR_SIZEWE); SetCursor(cursor); defCursor = false; if (event.LeftIsDown()) { hold = 1; gotGrab = true; } } // Grab end else if (abs64 (x - selEnd) < 6) { wxCursor cursor(wxCURSOR_SIZEWE); SetCursor(cursor); defCursor = false; if (event.LeftIsDown()) { hold = 2; gotGrab = true; } } } // Grabbing a syllable if (!gotGrab && karMode) { __int64 pos,len,curpos; KaraokeSyllable *curSyl; size_t karn = karaoke->syllables.size(); for (size_t i=0;isyllables.at(i); len = curSyl->length*10; curpos = curSyl->position*10; if (len != -1) { pos = GetXAtMS(curStartMS+len+curpos); if (abs64 (x - pos) < 4) { wxCursor cursor(wxCURSOR_SIZEWE); SetCursor(cursor); defCursor = false; if (event.LeftIsDown()) { hold = 4; holdSyl = i; gotGrab = true; } break; } } } } // Dragging nothing, time from scratch if (!gotGrab) { if (event.ButtonIsDown(wxMOUSE_BTN_LEFT)) { hold = 3; lastX = x; gotGrab = true; } } } // Drag start/end if (hold != 0) { // Dragging if (event.ButtonIsDown(wxMOUSE_BTN_LEFT)) { bool updated = false; // Drag from nothing if (hold == 3) { if (!karMode) { if (x != lastX) { selStart = x; selEnd = x; curEndMS = GetMSAtX(selEnd); curStartMS = GetMSAtX(selStart); hold = 2; } } } // Drag start if (hold == 1) { // Set new value if (x != selStart) { selStart = x; if (selStart > selEnd) { int temp = selStart; selStart = selEnd; selEnd = temp; hold = 2; curEndMS = GetMSAtX(selEnd); } curStartMS = GetMSAtX(selStart); updated = true; diagUpdated = true; } } // Drag end if (hold == 2) { // Set new value if (x != selEnd) { selEnd = x; if (selStart > selEnd) { int temp = selStart; selStart = selEnd; selEnd = temp; hold = 1; curStartMS = GetMSAtX(selStart); } curEndMS = GetMSAtX(selEnd); updated = true; diagUpdated = true; } } // Drag karaoke if (hold == 4) { // Set new value int curpos,len,pos,nkar; KaraokeSyllable *curSyl=NULL,*nextSyl=NULL; curSyl = &karaoke->syllables.at(holdSyl); nkar = karaoke->syllables.size(); if (holdSyl < nkar-1) { nextSyl = &karaoke->syllables.at(holdSyl+1); } curpos = curSyl->position; len = curSyl->length; pos = GetXAtMS(curStartMS+(len+curpos)*10); if (x != pos) { // Calculate delta in centiseconds int delta = ((__int64)(x-pos)*samples*100)/provider->GetSampleRate(); // Apply delta int deltaMode = 0; if (shiftDown) deltaMode = 1; // else if (ctrlDown) deltaMode = 2; bool result = karaoke->SyllableDelta(holdSyl,delta,deltaMode); if (result) { updated = true; diagUpdated = true; } } } // Update stuff if (updated) { provider->endPos = GetSampleAtX(selEnd); wxCursor cursor(wxCURSOR_SIZEWE); SetCursor(cursor); UpdateImage(true); Refresh(false); } } // Release else { // Commit changes if (diagUpdated) { diagUpdated = false; NeedCommit = true; if (Options.AsBool(_T("Audio Autocommit")) && curStartMS <= curEndMS) { CommitChanges(); } else { UpdateImage(true); Refresh(false); } } // Single click on nothing else if (hold == 3) { // Select syllable if (karaoke->enabled) { int syl = GetSyllableAtX(x); if (syl != -1) { karaoke->SetSyllable(syl); UpdateImage(true); Refresh(false); } } } // Update stuff SetCursor(wxNullCursor); hold = 0; } } } else { hold = 0; } } // Restore cursor if (defCursor) SetCursor(wxNullCursor); } ////////////// // Size event void AudioDisplay::OnSize(wxSizeEvent &event) { // Set size //GetVirtualSize(&w,&h); GetClientSize(&w,&h); //w = event.GetSize().GetWidth(); //h = event.GetSize().GetHeight(); // Update image UpdateImage(); Refresh(false); // Update scrollbar UpdateScrollbar(); } /////////////// // Timer event void AudioDisplay::OnUpdateTimer(wxTimerEvent &event) { // Get lock and check if it's OK wxMutexLocker locker(provider->PAMutex); if (!locker.IsOk() || !provider->playing) return; // Get DCs //wxMutexGuiEnter(); wxClientDC dc(this); wxMemoryDC src; src.SelectObject(*origImage); dc.BeginDrawing(); // Restore background dc.Blit(oldCurPos,0,1,h,&src,oldCurPos,0); // Draw cursor int curpos = -1; if (provider->playing) { if (provider->realPlayPos > provider->startPos && provider->realPlayPos < provider->endPos) { dc.SetPen(wxPen(Options.AsColour(_T("Audio Play cursor")))); curpos = GetXAtSample(provider->realPlayPos); dc.DrawLine(curpos,0,curpos,h); } else if (provider->realPlayPos > provider->endPos + 8192) { provider->Stop(); } } oldCurPos = curpos; // Done dc.EndDrawing(); //wxMutexGuiLeave(); } //////////// // Key down void AudioDisplay::OnKeyDown(wxKeyEvent &event) { int key = event.GetKeyCode(); Hotkeys.SetPressed(key,event.m_controlDown,event.m_altDown,event.m_shiftDown); // Accept if (Hotkeys.IsPressed(_T("Audio Commit"))) { CommitChanges(); ChangeLine(1); } // Accept (SSA's "Grab times") if (Hotkeys.IsPressed(_T("Audio Commit Alt"))) { CommitChanges(); } // Accept (stay) if (Hotkeys.IsPressed(_T("Audio Commit (Stay)"))) { CommitChanges(); } // Previous if (Hotkeys.IsPressed(_T("Audio Prev Line")) || Hotkeys.IsPressed(_T("Audio Prev Line Alt"))) { Prev(); } // Next if (Hotkeys.IsPressed(_T("Audio Next Line")) || Hotkeys.IsPressed(_T("Audio Next Line Alt"))) { Next(); } // Play if (Hotkeys.IsPressed(_T("Audio Play")) || Hotkeys.IsPressed(_T("Audio Play Alt"))) { if (provider->playing) Stop(); else { int start=0,end=0; GetTimesSelection(start,end); Play(start,end); } } // Increase length if (Hotkeys.IsPressed(_T("Audio Karaoke Increase Len"))) { if (karaoke->enabled) { bool result = karaoke->SyllableDelta(karaoke->curSyllable,1,0); if (result) diagUpdated = true; } } // Increase length (shift) if (Hotkeys.IsPressed(_T("Audio Karaoke Increase Len Shift"))) { if (karaoke->enabled) { bool result = karaoke->SyllableDelta(karaoke->curSyllable,1,1); if (result) diagUpdated = true; } } // Decrease length if (Hotkeys.IsPressed(_T("Audio Karaoke Decrease Len"))) { if (karaoke->enabled) { bool result = karaoke->SyllableDelta(karaoke->curSyllable,-1,0); if (result) diagUpdated = true; } } // Decrease length (shift) if (Hotkeys.IsPressed(_T("Audio Karaoke Decrease Len Shift"))) { if (karaoke->enabled) { bool result = karaoke->SyllableDelta(karaoke->curSyllable,-1,1); if (result) diagUpdated = true; } } // Move backwards if (Hotkeys.IsPressed(_T("Audio Scroll Left"))) { UpdatePosition(Position-128,false); UpdateImage(); Refresh(false); } // Move forward if (Hotkeys.IsPressed(_T("Audio Scroll Right"))) { UpdatePosition(Position+128,false); UpdateImage(); Refresh(false); } // Play first 500 ms if (Hotkeys.IsPressed(_T("Audio Play First 500ms"))) { int start=0,end=0; GetTimesSelection(start,end); int e = start+500; if (e > end) e = end; Play(start,e); } // Play last 500 ms if (Hotkeys.IsPressed(_T("Audio Play Last 500ms"))) { int start=0,end=0; GetTimesSelection(start,end); int s = end-500; if (s < start) s = start; Play(s,end); } // Play 500 ms before if (Hotkeys.IsPressed(_T("Audio Play 500ms Before"))) { int start=0,end=0; GetTimesSelection(start,end); Play(start-500,start); } // Play 500 ms after if (Hotkeys.IsPressed(_T("Audio Play 500ms After"))) { int start=0,end=0; GetTimesSelection(start,end); Play(end,end+500); } // Play original line if (Hotkeys.IsPressed(_T("Audio Play Original Line"))) { int start=0,end=0; GetTimesDialogue(start,end); if (Options.AsBool(_T("Audio SSA Mode"))) { SetSelection(start, end); } Play(start,end); } // Lead in if (Hotkeys.IsPressed(_T("Audio Add Lead In"))) { AddLead(true,false); } // Lead out if (Hotkeys.IsPressed(_T("Audio Add Lead Out"))) { AddLead(false,true); } // Update if (diagUpdated) { diagUpdated = false; NeedCommit = true; if ((Options.AsBool(_T("Audio SSA Allow Autocommit")) || Options.AsBool(_T("Audio SSA Mode")) == false) && Options.AsBool(_T("Audio Autocommit")) && curStartMS <= curEndMS) { CommitChanges(); } else { UpdateImage(true); Refresh(false); } } } /////////////// // Change line void AudioDisplay::ChangeLine(int delta) { if (dialogue) { // Get next line number and make sure it's within bounds int next = line_n+delta; if (next == -1) return; if (next == grid->GetRows()) return; // Set stuff NeedCommit = false; dialogue = NULL; grid->editBox->SetToLine(next); grid->SelectRow(next); if (!dialogue) { UpdateImage(true); Refresh(false); } line_n = next; } } //////// // Next void AudioDisplay::Next() { // Karaoke if (karaoke->enabled) { karaoke->curSyllable++; // Last syllable; jump to next if (karaoke->curSyllable >= (signed int)karaoke->syllables.size()) { if (NeedCommit) { int result = wxMessageBox(_("Do you want to commit your changes? If you choose No, they will be discarded."),_("Commit?"),wxYES_NO | wxCANCEL | wxICON_QUESTION); //int result = wxNO; if (result == wxYES) { CommitChanges(); } else if (result == wxCANCEL) { karaoke->curSyllable--; return; } } karaoke->curSyllable = 0; ChangeLine(1); } else Update(); // Set syllable karaoke->SetSyllable(karaoke->curSyllable); int start=0,end=0; GetTimesSelection(start,end); Play(start,end); } // Plain mode else { ChangeLine(1); } } //////////// // Previous void AudioDisplay::Prev() { // Karaoke if (karaoke->enabled) { karaoke->curSyllable--; // First syllable; jump line if (karaoke->curSyllable < 0) { if (NeedCommit) { int result = wxMessageBox(_("Do you want to commit your changes? If you choose No, they will be discarded."),_("Commit?"),wxYES_NO | wxCANCEL); if (result == wxYES) { CommitChanges(); } else if (result == wxCANCEL) { karaoke->curSyllable++; return; } } ChangeLine(-1); } else Update(); // Set syllable karaoke->SetSyllable(karaoke->curSyllable); int start=0,end=0; GetTimesSelection(start,end); Play(start,end); } // Plain mode else { ChangeLine(-1); } } /////////////////////////////// // Gets syllable at x position int AudioDisplay::GetSyllableAtX(int x) { if (!karaoke->enabled) return -1; int ms = GetMSAtX(x); size_t syllables = karaoke->syllables.size();; int sylstart,sylend; // Find a matching syllable for (size_t i=0;isyllables.at(i).position*10 + curStartMS; sylend = karaoke->syllables.at(i).length*10 + sylstart; if (ms >= sylstart && ms < sylend) { return i; } } return -1; } //////////////// // Focus events void AudioDisplay::OnGetFocus(wxFocusEvent &event) { if (!hasFocus) { hasFocus = true; UpdateImage(true); Refresh(false); } } void AudioDisplay::OnLoseFocus(wxFocusEvent &event) { if (hasFocus) { hasFocus = false; UpdateImage(true); Refresh(false); } }