mirror of https://github.com/odrling/Aegisub
Merge audio_display_rewrite branch to trunk. This is not a complete work, don't expect to time anything for a while.
Originally committed to SVN as r4903.
This commit is contained in:
parent
0e6d8631fd
commit
c15777f844
|
@ -47,6 +47,7 @@
|
|||
MinimalRebuild="true"
|
||||
UsePrecompiledHeader="2"
|
||||
PrecompiledHeaderThrough="agi_pre.h"
|
||||
DebugInformationFormat="3"
|
||||
DisableSpecificWarnings="4267"
|
||||
ForcedIncludeFiles="agi_pre.h"
|
||||
/>
|
||||
|
@ -113,6 +114,7 @@
|
|||
MinimalRebuild="true"
|
||||
UsePrecompiledHeader="2"
|
||||
PrecompiledHeaderThrough="agi_pre.h"
|
||||
DebugInformationFormat="3"
|
||||
DisableSpecificWarnings="4267"
|
||||
ForcedIncludeFiles="agi_pre.h"
|
||||
/>
|
||||
|
@ -715,6 +717,14 @@
|
|||
RelativePath="..\..\src\aegisublocale.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\block_cache.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\block_cache.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\charset_conv.cpp"
|
||||
>
|
||||
|
@ -1699,6 +1709,22 @@
|
|||
RelativePath="..\..\src\audio_box.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_colorscheme.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_colorscheme.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_colorscheme.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_colorscheme.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_display.cpp"
|
||||
>
|
||||
|
@ -1731,10 +1757,34 @@
|
|||
RelativePath="..\..\src\audio_renderer_spectrum.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_renderer_waveform.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_renderer_waveform.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_timing_dialogue.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Video UI"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_renderer_waveform.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_renderer_waveform.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_timing_dialogue.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\video_box.cpp"
|
||||
>
|
||||
|
@ -1799,6 +1849,18 @@
|
|||
<Filter
|
||||
Name="Controllers"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_controller.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_controller.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\audio_timing.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\selection_controller.h"
|
||||
>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalOptions="/Zm120"
|
||||
EnableIntrinsicFunctions="true"
|
||||
RuntimeLibrary="2"
|
||||
OpenMP="true"
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
OutputDirectory="$(SolutionDir)build/$(ProjectName)/$(PlatformName)/$(ConfigurationName)/"
|
||||
IntermediateDirectory="$(OutDir)"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories=""$(SolutionDir)include/";"$(SolutionDir)include/$(PlatformName)/";"$(SolutionDir)include/$(ConfigurationName)/";"$(SolutionDir)include/$(PlatformName)/$(ConfigurationName)/""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(LibraryOutDir)/$(ProjectName).lib"
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
CommandLine="cd "$(ExecutableOutDir)"
"$(ProjectDir)\..\..\tests\setup.bat" "$(ProjectDir)\..\..\tests"
"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
|
|
|
@ -157,6 +157,7 @@
|
|||
#include <wx/dataobj.h>
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/dc.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/dcclient.h>
|
||||
#include <wx/dcmemory.h>
|
||||
#include <wx/dcscreen.h>
|
||||
|
@ -199,6 +200,7 @@
|
|||
#include <wx/mstream.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/power.h>
|
||||
#include <wx/protocol/http.h>
|
||||
#include <wx/radiobox.h>
|
||||
#include <wx/radiobut.h>
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "ass_export_filter.h"
|
||||
#include "ass_exporter.h"
|
||||
#include "ass_file.h"
|
||||
#include "audio_controller.h"
|
||||
#include "frame_main.h"
|
||||
|
||||
/// @brief Constructor
|
||||
|
|
|
@ -47,9 +47,13 @@
|
|||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "include/aegisub/audio_player.h"
|
||||
#include "selection_controller.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_box.h"
|
||||
#include "audio_display.h"
|
||||
#include "audio_karaoke.h"
|
||||
#include "audio_timing.h"
|
||||
#include "frame_main.h"
|
||||
#include "hotkeys.h"
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
@ -58,60 +62,79 @@
|
|||
#include "toggle_bitmap.h"
|
||||
#include "tooltip_manager.h"
|
||||
|
||||
// Stuff defines "min" and "max" as macros and breaks std::min and std::max in the process
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
|
||||
enum AudioBoxControlIDs {
|
||||
Audio_Scrollbar = 1600,
|
||||
Audio_Horizontal_Zoom,
|
||||
Audio_Vertical_Zoom,
|
||||
Audio_Volume,
|
||||
Audio_Sash,
|
||||
Audio_Vertical_Link,
|
||||
Audio_Button_Play,
|
||||
Audio_Button_Stop,
|
||||
Audio_Button_Prev,
|
||||
Audio_Button_Next,
|
||||
Audio_Button_Play_500ms_Before,
|
||||
Audio_Button_Play_500ms_After,
|
||||
Audio_Button_Play_500ms_First,
|
||||
Audio_Button_Play_500ms_Last,
|
||||
Audio_Button_Play_Row,
|
||||
Audio_Button_Play_To_End,
|
||||
Audio_Button_Commit,
|
||||
Audio_Button_Karaoke,
|
||||
Audio_Button_Goto,
|
||||
|
||||
Audio_Button_Join, /// Karaoke -> Enter join mode.
|
||||
Audio_Button_Split, /// Karaoke -> Enter split mode.
|
||||
Audio_Button_Accept, /// Karaoke -> Split/Join mode -> Accept.
|
||||
Audio_Button_Cancel, /// KAraoke -> Split/Join mode -> Cancel.
|
||||
|
||||
Audio_Button_Leadin,
|
||||
Audio_Button_Leadout,
|
||||
|
||||
Audio_Check_AutoCommit,
|
||||
Audio_Check_NextCommit,
|
||||
Audio_Check_AutoGoto,
|
||||
Audio_Check_Medusa,
|
||||
Audio_Check_Spectrum
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param parent
|
||||
///
|
||||
AudioBox::AudioBox(wxWindow *parent, SubtitlesGrid *grid) :
|
||||
wxPanel(parent,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_RAISED)
|
||||
AudioBox::AudioBox(wxWindow *parent, AudioController *_controller, SelectionController<AssDialogue> *selection_controller)
|
||||
: wxPanel(parent,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_RAISED)
|
||||
, selection_controller(selection_controller)
|
||||
, controller(_controller)
|
||||
{
|
||||
// Setup
|
||||
loaded = false;
|
||||
karaokeMode = false;
|
||||
|
||||
// Sash and Display
|
||||
audioScroll = new wxScrollBar(this,Audio_Scrollbar);
|
||||
audioScroll->PushEventHandler(new FocusEvent());
|
||||
audioScroll->SetToolTip(_("Seek bar"));
|
||||
Sash = new wxSashWindow(this,Audio_Sash,wxDefaultPosition,wxDefaultSize,wxCLIP_CHILDREN | wxSW_3DBORDER);
|
||||
sashSizer = new wxBoxSizer(wxVERTICAL);
|
||||
audioDisplay = new AudioDisplay(Sash, grid);
|
||||
sashSizer->Add(audioDisplay,1,wxEXPAND,0);
|
||||
Sash->SetSizer(sashSizer);
|
||||
Sash->SetSashVisible(wxSASH_BOTTOM,true);
|
||||
//Sash->SetSashBorder(wxSASH_BOTTOM,true);
|
||||
Sash->SetMinimumSizeY(50);
|
||||
audioDisplay->ScrollBar = audioScroll;
|
||||
audioDisplay->box = this;
|
||||
int _w,_h;
|
||||
audioDisplay->GetSize(&_w,&_h);
|
||||
audioDisplay->SetSizeHints(-1,_h,-1,_h);
|
||||
audioDisplay = new AudioDisplay(this, controller);
|
||||
|
||||
// Zoom
|
||||
HorizontalZoom = new wxSlider(this,Audio_Horizontal_Zoom,50,0,100,wxDefaultPosition,wxSize(-1,20),wxSL_VERTICAL|wxSL_BOTH);
|
||||
HorizontalZoom->PushEventHandler(new FocusEvent());
|
||||
HorizontalZoom = new wxSlider(this,Audio_Horizontal_Zoom,0,-50,30,wxDefaultPosition,wxSize(-1,20),wxSL_VERTICAL|wxSL_BOTH);
|
||||
HorizontalZoom->SetToolTip(_("Horizontal zoom"));
|
||||
VerticalZoom = new wxSlider(this,Audio_Vertical_Zoom,50,0,100,wxDefaultPosition,wxSize(-1,20),wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE);
|
||||
VerticalZoom->PushEventHandler(new FocusEvent());
|
||||
VerticalZoom->SetToolTip(_("Vertical zoom"));
|
||||
VolumeBar = new wxSlider(this,Audio_Volume,50,0,100,wxDefaultPosition,wxSize(-1,20),wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE);
|
||||
VolumeBar->PushEventHandler(new FocusEvent());
|
||||
VolumeBar->SetToolTip(_("Audio Volume"));
|
||||
bool link = OPT_GET("Audio/Link")->GetBool();
|
||||
if (link) {
|
||||
VolumeBar->SetValue(VerticalZoom->GetValue());
|
||||
VolumeBar->Enable(false);
|
||||
}
|
||||
VerticalLink = new ToggleBitmap(this,Audio_Vertical_Link,GETIMAGE(toggle_audio_link_24));
|
||||
VerticalLink = new ToggleBitmap(this,Audio_Vertical_Link,GETIMAGE(toggle_audio_link_16));
|
||||
VerticalLink->SetToolTip(_("Link vertical zoom and volume sliders"));
|
||||
VerticalLink->SetValue(link);
|
||||
|
||||
// Display sizer
|
||||
DisplaySizer = new wxBoxSizer(wxVERTICAL);
|
||||
//DisplaySizer->Add(audioDisplay,1,wxEXPAND,0);
|
||||
DisplaySizer->Add(Sash,0,wxEXPAND,0);
|
||||
DisplaySizer->Add(audioScroll,0,wxEXPAND,0);
|
||||
|
||||
// VertVol sider
|
||||
wxSizer *VertVol = new wxBoxSizer(wxHORIZONTAL);
|
||||
VertVol->Add(VerticalZoom,1,wxEXPAND,0);
|
||||
|
@ -122,99 +145,93 @@ wxPanel(parent,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_RAISE
|
|||
|
||||
// Top sizer
|
||||
TopSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
TopSizer->Add(DisplaySizer,1,wxEXPAND,0);
|
||||
TopSizer->Add(audioDisplay,1,wxEXPAND,0);
|
||||
TopSizer->Add(HorizontalZoom,0,wxEXPAND,0);
|
||||
TopSizer->Add(VertVolArea,0,wxEXPAND,0);
|
||||
|
||||
// Buttons sizer
|
||||
wxSizer *ButtonSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxButton *temp;
|
||||
temp = new wxBitmapButton(this,Audio_Button_Prev,GETIMAGE(button_prev_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Prev,GETIMAGE(button_prev_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Previous line or syllable (%KEY%/%KEY%)"),_T("Audio Prev Line"),_T("Audio Prev Line Alt"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Next,GETIMAGE(button_next_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Next,GETIMAGE(button_next_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Next line/syllable (%KEY%/%KEY%)"),_T("Audio Next Line"),_T("Audio Next Line Alt"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play,GETIMAGE(button_playsel_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play,GETIMAGE(button_playsel_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Play selection (%KEY%/%KEY%)"),_T("Audio Play"),_T("Audio Play Alt"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_Row,GETIMAGE(button_playline_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_Row,GETIMAGE(button_playline_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Play current line (%KEY%)"),_T("Audio Play Original Line"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Stop,GETIMAGE(button_stop_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Stop,GETIMAGE(button_stop_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Stop (%KEY%)"),_T("Audio Stop"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,10);
|
||||
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_Before,GETIMAGE(button_playfivehbefore_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_Before,GETIMAGE(button_playfivehbefore_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Play 500 ms before selection (%KEY%)"),_T("Audio Play 500ms Before"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_After,GETIMAGE(button_playfivehafter_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_After,GETIMAGE(button_playfivehafter_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Play 500 ms after selection (%KEY%)"),_T("Audio Play 500ms after"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_First,GETIMAGE(button_playfirstfiveh_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_First,GETIMAGE(button_playfirstfiveh_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Play first 500ms of selection (%KEY%)"),_T("Audio Play First 500ms"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_Last,GETIMAGE(button_playlastfiveh_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_Last,GETIMAGE(button_playlastfiveh_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Play last 500ms of selection (%KEY%)"),_T("Audio Play Last 500ms"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_To_End,GETIMAGE(button_playtoend_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_To_End,GETIMAGE(button_playtoend_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Play from selection start to end of file (%KEY%)"),_T("Audio Play To End"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,10);
|
||||
|
||||
temp = new wxBitmapButton(this,Audio_Button_Leadin,GETIMAGE(button_leadin_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Leadin,GETIMAGE(button_leadin_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Add lead in (%KEY%)"),_T("Audio Add Lead In"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Leadout,GETIMAGE(button_leadout_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Leadout,GETIMAGE(button_leadout_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Add lead out (%KEY%)"),_T("Audio Add Lead Out"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,10);
|
||||
|
||||
temp = new wxBitmapButton(this,Audio_Button_Commit,GETIMAGE(button_audio_commit_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Commit,GETIMAGE(button_audio_commit_16),wxDefaultPosition,wxDefaultSize);
|
||||
ToolTipManager::Bind(temp,_("Commit changes (%KEY%/%KEY%)"),_T("Audio Commit (Stay)"),_T("Audio Commit Alt"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Goto,GETIMAGE(button_audio_goto_24),wxDefaultPosition,wxSize(30,-1));
|
||||
temp = new wxBitmapButton(this,Audio_Button_Goto,GETIMAGE(button_audio_goto_16),wxDefaultPosition,wxDefaultSize);
|
||||
temp->SetToolTip(_("Go to selection"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,10);
|
||||
|
||||
AutoCommit = new ToggleBitmap(this,Audio_Check_AutoCommit,GETIMAGE(toggle_audio_autocommit_24),wxSize(30,-1));
|
||||
AutoCommit = new ToggleBitmap(this,Audio_Check_AutoCommit,GETIMAGE(toggle_audio_autocommit_16), wxSize(20, -1));
|
||||
AutoCommit->SetToolTip(_("Automatically commit all changes"));
|
||||
AutoCommit->SetValue(OPT_GET("Audio/Auto/Commit")->GetBool());
|
||||
ButtonSizer->Add(AutoCommit,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0);
|
||||
NextCommit = new ToggleBitmap(this,Audio_Check_NextCommit,GETIMAGE(toggle_audio_nextcommit_24),wxSize(30,-1));
|
||||
NextCommit = new ToggleBitmap(this,Audio_Check_NextCommit,GETIMAGE(toggle_audio_nextcommit_16), wxSize(20, -1));
|
||||
NextCommit->SetToolTip(_("Auto goes to next line on commit"));
|
||||
NextCommit->SetValue(OPT_GET("Audio/Next Line on Commit")->GetBool());
|
||||
ButtonSizer->Add(NextCommit,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0);
|
||||
AutoScroll = new ToggleBitmap(this,Audio_Check_AutoGoto,GETIMAGE(toggle_audio_autoscroll_24),wxSize(30,-1));
|
||||
AutoScroll = new ToggleBitmap(this,Audio_Check_AutoGoto,GETIMAGE(toggle_audio_autoscroll_16), wxSize(20, -1));
|
||||
AutoScroll->SetToolTip(_("Auto scrolls audio display to selected line"));
|
||||
AutoScroll->SetValue(OPT_GET("Audio/Auto/Scroll")->GetBool());
|
||||
ButtonSizer->Add(AutoScroll,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0);
|
||||
SpectrumMode = new ToggleBitmap(this,Audio_Check_Spectrum,GETIMAGE(toggle_audio_spectrum_24),wxSize(30,-1));
|
||||
SpectrumMode->SetToolTip(_("Spectrum analyzer mode"));
|
||||
SpectrumMode->SetValue(OPT_GET("Audio/Spectrum")->GetBool());
|
||||
ButtonSizer->Add(SpectrumMode,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0);
|
||||
MedusaMode = new ToggleBitmap(this,Audio_Check_Medusa,GETIMAGE(toggle_audio_medusa_24),wxSize(30,-1));
|
||||
MedusaMode->SetToolTip(_("Enable Medusa-Style Timing Shortcuts"));
|
||||
MedusaMode->SetValue(OPT_GET("Audio/Medusa Timing Hotkeys")->GetBool());
|
||||
ButtonSizer->Add(MedusaMode,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0);
|
||||
ButtonSizer->Add(AutoScroll,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,10);
|
||||
|
||||
ButtonSizer->AddStretchSpacer(1);
|
||||
|
||||
KaraokeButton = new wxBitmapToggleButton(this,Audio_Button_Karaoke,GETIMAGE(kara_mode_16),wxDefaultPosition,wxDefaultSize);
|
||||
KaraokeButton->SetToolTip(_("Toggle karaoke mode"));
|
||||
ButtonSizer->Add(KaraokeButton,0,wxRIGHT|wxEXPAND,0);
|
||||
|
||||
// Karaoke sizer
|
||||
karaokeSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
KaraokeButton = new wxBitmapToggleButton(this,Audio_Button_Karaoke,GETIMAGE(kara_mode_24),wxDefaultPosition,wxSize(33,30));
|
||||
KaraokeButton->SetToolTip(_("Toggle karaoke mode"));
|
||||
karaokeSizer->Add(KaraokeButton,0,wxRIGHT|wxEXPAND,0);
|
||||
|
||||
JoinSplitSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
JoinButton = new wxBitmapButton(this,Audio_Button_Join,GETIMAGE(kara_join_24),wxDefaultPosition,wxSize(33,30));
|
||||
JoinButton = new wxBitmapButton(this,Audio_Button_Join,GETIMAGE(kara_join_16),wxDefaultPosition,wxDefaultSize);
|
||||
JoinButton->SetToolTip(_("Join selected syllables"));
|
||||
SplitButton = new wxBitmapButton(this,Audio_Button_Split,GETIMAGE(kara_split_24),wxDefaultPosition,wxSize(33,30));
|
||||
SplitButton = new wxBitmapButton(this,Audio_Button_Split,GETIMAGE(kara_split_16),wxDefaultPosition,wxDefaultSize);
|
||||
SplitButton->SetToolTip(_("Enter split-mode"));
|
||||
JoinSplitSizer->Add(JoinButton,0,wxRIGHT|wxEXPAND,0);
|
||||
JoinSplitSizer->Add(SplitButton,0,wxRIGHT|wxEXPAND,0);
|
||||
|
||||
CancelAcceptSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
CancelButton = new wxBitmapButton(this,Audio_Button_Cancel,GETIMAGE(kara_split_accept_24),wxDefaultPosition,wxSize(33,30));
|
||||
CancelButton = new wxBitmapButton(this,Audio_Button_Cancel,GETIMAGE(kara_split_accept_16),wxDefaultPosition,wxDefaultSize);
|
||||
CancelButton->SetToolTip(_("Commit splits and leave split-mode"));
|
||||
AcceptButton = new wxBitmapButton(this,Audio_Button_Accept,GETIMAGE(kara_split_cancel_24),wxDefaultPosition,wxSize(33,30));
|
||||
AcceptButton = new wxBitmapButton(this,Audio_Button_Accept,GETIMAGE(kara_split_cancel_16),wxDefaultPosition,wxDefaultSize);
|
||||
AcceptButton->SetToolTip(_("Discard all splits and leave split-mode"));
|
||||
CancelAcceptSizer->Add(CancelButton,0,wxRIGHT|wxEXPAND,0);
|
||||
CancelAcceptSizer->Add(AcceptButton,0,wxRIGHT|wxEXPAND,0);
|
||||
|
@ -225,70 +242,40 @@ wxPanel(parent,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_RAISE
|
|||
audioKaraoke = new AudioKaraoke(this);
|
||||
audioKaraoke->box = this;
|
||||
audioKaraoke->display = audioDisplay;
|
||||
audioDisplay->karaoke = audioKaraoke;
|
||||
karaokeSizer->Add(audioKaraoke,1,wxEXPAND,0);
|
||||
|
||||
SetKaraokeButtons(); // Decide which one to show or hide.
|
||||
|
||||
// Main sizer
|
||||
MainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
MainSizer->Add(TopSizer,0,wxEXPAND,0);
|
||||
MainSizer->Add(ButtonSizer,0,wxEXPAND,0);
|
||||
MainSizer->Add(new wxStaticLine(this),0,wxEXPAND|wxTOP|wxBOTTOM,2);
|
||||
MainSizer->Add(karaokeSizer,0,wxEXPAND,0);
|
||||
MainSizer->Add(TopSizer,1,wxEXPAND|wxALL,3);
|
||||
MainSizer->Add(ButtonSizer,0,wxEXPAND|wxBOTTOM|wxLEFT|wxRIGHT,3);
|
||||
//MainSizer->Add(new wxStaticLine(this),0,wxEXPAND|wxTOP|wxBOTTOM,2);
|
||||
MainSizer->Add(karaokeSizer,0,wxEXPAND|wxBOTTOM|wxLEFT|wxRIGHT,3);
|
||||
MainSizer->AddSpacer(3);
|
||||
//MainSizer->SetSizeHints(this);
|
||||
SetSizer(MainSizer);
|
||||
|
||||
SetKaraokeButtons(); // Decide which one to show or hide.
|
||||
|
||||
timing_controller_dialogue = CreateDialogueTimingController(controller, selection_controller);
|
||||
controller->SetTimingController(timing_controller_dialogue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Destructor
|
||||
///
|
||||
AudioBox::~AudioBox() {
|
||||
audioScroll->PopEventHandler(true);
|
||||
HorizontalZoom->PopEventHandler(true);
|
||||
VerticalZoom->PopEventHandler(true);
|
||||
VolumeBar->PopEventHandler(true);
|
||||
AudioBox::~AudioBox()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Set file
|
||||
/// @param file
|
||||
/// @param FromVideo
|
||||
/// @return
|
||||
///
|
||||
void AudioBox::SetFile(wxString file,bool FromVideo) {
|
||||
LOG_D("audio/box") << "file=" << file << " FromVideo: " << FromVideo;
|
||||
loaded = false;
|
||||
|
||||
if (FromVideo) {
|
||||
audioDisplay->SetFromVideo();
|
||||
loaded = audioDisplay->loaded;
|
||||
audioName = _T("?video");
|
||||
}
|
||||
|
||||
else {
|
||||
audioDisplay->SetFile(file);
|
||||
if (file != _T("")) loaded = audioDisplay->loaded;
|
||||
audioName = file;
|
||||
}
|
||||
|
||||
LOG_D("audio/box") << "setting up acceleraters in frameMain";
|
||||
frameMain->SetAccelerators();
|
||||
LOG_D("audio/box") << "finished setting up accelerators in frameMain";
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Event table
|
||||
BEGIN_EVENT_TABLE(AudioBox,wxPanel)
|
||||
EVT_COMMAND_SCROLL(Audio_Scrollbar, AudioBox::OnScrollbar)
|
||||
EVT_COMMAND_SCROLL(Audio_Horizontal_Zoom, AudioBox::OnHorizontalZoom)
|
||||
EVT_COMMAND_SCROLL(Audio_Vertical_Zoom, AudioBox::OnVerticalZoom)
|
||||
EVT_COMMAND_SCROLL(Audio_Volume, AudioBox::OnVolume)
|
||||
EVT_SASH_DRAGGED(Audio_Sash,AudioBox::OnSash)
|
||||
|
||||
EVT_BUTTON(Audio_Button_Play, AudioBox::OnPlaySelection)
|
||||
EVT_BUTTON(Audio_Button_Play_Row, AudioBox::OnPlayDialogue)
|
||||
|
@ -312,28 +299,19 @@ BEGIN_EVENT_TABLE(AudioBox,wxPanel)
|
|||
EVT_TOGGLEBUTTON(Audio_Vertical_Link, AudioBox::OnVerticalLink)
|
||||
EVT_TOGGLEBUTTON(Audio_Button_Karaoke, AudioBox::OnKaraoke)
|
||||
EVT_TOGGLEBUTTON(Audio_Check_AutoGoto,AudioBox::OnAutoGoto)
|
||||
EVT_TOGGLEBUTTON(Audio_Check_Medusa,AudioBox::OnMedusaMode)
|
||||
EVT_TOGGLEBUTTON(Audio_Check_Spectrum,AudioBox::OnSpectrumMode)
|
||||
EVT_TOGGLEBUTTON(Audio_Check_AutoCommit,AudioBox::OnAutoCommit)
|
||||
EVT_TOGGLEBUTTON(Audio_Check_NextCommit,AudioBox::OnNextLineCommit)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
|
||||
/// @brief Scrollbar changed
|
||||
/// @param event
|
||||
///
|
||||
void AudioBox::OnScrollbar(wxScrollEvent &event) {
|
||||
audioDisplay->SetPosition(event.GetPosition()*12);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Horizontal zoom bar changed
|
||||
/// @param event
|
||||
///
|
||||
void AudioBox::OnHorizontalZoom(wxScrollEvent &event) {
|
||||
audioDisplay->SetSamplesPercent(event.GetPosition());
|
||||
// Negate the value, we want zoom out to be on bottom and zoom in on top,
|
||||
// but the control doesn't want negative on bottom and positive on top.
|
||||
audioDisplay->SetZoomLevel(-event.GetPosition());
|
||||
}
|
||||
|
||||
|
||||
|
@ -346,9 +324,9 @@ void AudioBox::OnVerticalZoom(wxScrollEvent &event) {
|
|||
if (pos < 1) pos = 1;
|
||||
if (pos > 100) pos = 100;
|
||||
float value = pow(float(pos)/50.0f,3);
|
||||
audioDisplay->SetScale(value);
|
||||
audioDisplay->SetAmplitudeScale(value);
|
||||
if (VerticalLink->GetValue()) {
|
||||
audioDisplay->player->SetVolume(value);
|
||||
controller->SetVolume(value);
|
||||
VolumeBar->SetValue(pos);
|
||||
}
|
||||
}
|
||||
|
@ -363,7 +341,7 @@ void AudioBox::OnVolume(wxScrollEvent &event) {
|
|||
int pos = event.GetPosition();
|
||||
if (pos < 1) pos = 1;
|
||||
if (pos > 100) pos = 100;
|
||||
audioDisplay->player->SetVolume(pow(float(pos)/50.0f,3));
|
||||
controller->SetVolume(pow(float(pos)/50.0f,3));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,7 +356,7 @@ void AudioBox::OnVerticalLink(wxCommandEvent &event) {
|
|||
if (pos > 100) pos = 100;
|
||||
float value = pow(float(pos)/50.0f,3);
|
||||
if (VerticalLink->GetValue()) {
|
||||
audioDisplay->player->SetVolume(value);
|
||||
controller->SetVolume(value);
|
||||
VolumeBar->SetValue(pos);
|
||||
}
|
||||
VolumeBar->Enable(!VerticalLink->GetValue());
|
||||
|
@ -388,63 +366,11 @@ void AudioBox::OnVerticalLink(wxCommandEvent &event) {
|
|||
|
||||
|
||||
|
||||
/// @brief Sash
|
||||
/// @param event
|
||||
/// @return
|
||||
///
|
||||
void AudioBox::OnSash(wxSashEvent& event) {
|
||||
// OK?
|
||||
if (event.GetDragStatus() == wxSASH_STATUS_OUT_OF_RANGE) return;
|
||||
|
||||
// Recursion guard
|
||||
static wxRecursionGuardFlag inside;
|
||||
wxRecursionGuard guard(inside);
|
||||
if (guard.IsInside()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get size
|
||||
wxRect newSize = event.GetDragRect();
|
||||
int w = newSize.GetWidth();
|
||||
int h = newSize.GetHeight();
|
||||
if (h < 50) h = 50;
|
||||
int oldh = audioDisplay->GetSize().GetHeight();
|
||||
if (oldh == h) return;
|
||||
|
||||
// Resize
|
||||
audioDisplay->SetSizeHints(w,h,-1,h);
|
||||
audioDisplay->SetSize(w,h);
|
||||
sashSizer->Layout();
|
||||
Sash->GetParent()->Layout();
|
||||
|
||||
// Store new size
|
||||
OPT_SET("Audio/Display Height")->SetInt(h);
|
||||
|
||||
// Fix layout
|
||||
frameMain->Freeze();
|
||||
DisplaySizer->Layout();
|
||||
//TopSizer->Layout();
|
||||
//MainSizer->Layout();
|
||||
Layout();
|
||||
frameMain->ToolSizer->Layout();
|
||||
frameMain->MainSizer->Layout();
|
||||
frameMain->Layout();
|
||||
frameMain->Refresh();
|
||||
frameMain->Thaw();
|
||||
|
||||
//event.Skip();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Play selection
|
||||
/// @param event
|
||||
///
|
||||
void AudioBox::OnPlaySelection(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
audioDisplay->Play(start,end);
|
||||
controller->PlayPrimaryRange();
|
||||
}
|
||||
|
||||
|
||||
|
@ -453,11 +379,9 @@ void AudioBox::OnPlaySelection(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void AudioBox::OnPlayDialogue(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesDialogue(start,end);
|
||||
audioDisplay->SetSelection(start, end);
|
||||
audioDisplay->Play(start,end);
|
||||
if (controller->GetTimingController())
|
||||
controller->GetTimingController()->Revert();
|
||||
controller->PlayPrimaryRange();
|
||||
}
|
||||
|
||||
|
||||
|
@ -466,8 +390,7 @@ void AudioBox::OnPlayDialogue(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void AudioBox::OnStop(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->Stop();
|
||||
controller->Stop();
|
||||
}
|
||||
|
||||
|
||||
|
@ -476,9 +399,11 @@ void AudioBox::OnStop(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void AudioBox::OnNext(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->Stop();
|
||||
audioDisplay->Next();
|
||||
//audioDisplay->SetFocus();
|
||||
controller->Stop();
|
||||
if (controller->GetTimingController())
|
||||
controller->GetTimingController()->Next();
|
||||
controller->PlayPrimaryRange();
|
||||
}
|
||||
|
||||
|
||||
|
@ -487,9 +412,11 @@ void AudioBox::OnNext(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void AudioBox::OnPrev(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->Stop();
|
||||
audioDisplay->Prev();
|
||||
//audioDisplay->SetFocus();
|
||||
controller->Stop();
|
||||
if (controller->GetTimingController())
|
||||
controller->GetTimingController()->Prev();
|
||||
controller->PlayPrimaryRange();
|
||||
}
|
||||
|
||||
|
||||
|
@ -498,10 +425,10 @@ void AudioBox::OnPrev(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void AudioBox::OnPlay500Before(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
audioDisplay->Play(start-500,start);
|
||||
AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
|
||||
controller->PlayRange(AudioController::SampleRange(
|
||||
times.begin() - controller->SamplesFromMilliseconds(500),
|
||||
times.begin()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -510,10 +437,10 @@ void AudioBox::OnPlay500Before(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void AudioBox::OnPlay500After(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
audioDisplay->Play(end,end+500);
|
||||
AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
|
||||
controller->PlayRange(AudioController::SampleRange(
|
||||
times.end(),
|
||||
times.end() + controller->SamplesFromMilliseconds(500)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -522,12 +449,12 @@ void AudioBox::OnPlay500After(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void AudioBox::OnPlay500First(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
int endp = start+500;
|
||||
if (endp > end) endp = end;
|
||||
audioDisplay->Play(start,endp);
|
||||
AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
|
||||
controller->PlayRange(AudioController::SampleRange(
|
||||
times.begin(),
|
||||
times.begin() + std::min(
|
||||
controller->SamplesFromMilliseconds(500),
|
||||
times.length())));
|
||||
}
|
||||
|
||||
|
||||
|
@ -536,12 +463,12 @@ void AudioBox::OnPlay500First(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void AudioBox::OnPlay500Last(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
int startp = end-500;
|
||||
if (startp < start) startp = start;
|
||||
audioDisplay->Play(startp,end);
|
||||
AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
|
||||
controller->PlayRange(AudioController::SampleRange(
|
||||
times.end() - std::min(
|
||||
controller->SamplesFromMilliseconds(500),
|
||||
times.length()),
|
||||
times.end()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -550,10 +477,7 @@ void AudioBox::OnPlay500Last(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void AudioBox::OnPlayToEnd(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
audioDisplay->Play(start,-1);
|
||||
controller->PlayToEnd(controller->GetPrimaryPlaybackRange().begin());
|
||||
}
|
||||
|
||||
|
||||
|
@ -566,7 +490,8 @@ void AudioBox::OnCommit(wxCommandEvent &event) {
|
|||
LOG_D("audio/box") << "OnCommit";
|
||||
audioDisplay->SetFocus();
|
||||
LOG_D("audio/box") << "has set focus, now committing changes";
|
||||
audioDisplay->CommitChanges(true);
|
||||
/// @todo Commit changes and go to next line if appropriate
|
||||
//audioDisplay->CommitChanges(true);
|
||||
LOG_D("audio/box") << "returning";
|
||||
}
|
||||
|
||||
|
@ -586,7 +511,8 @@ void AudioBox::OnKaraoke(wxCommandEvent &event) {
|
|||
}
|
||||
karaokeMode = false;
|
||||
audioKaraoke->enabled = false;
|
||||
audioDisplay->SetDialogue();
|
||||
/// @todo Replace this with changing timing controller
|
||||
//audioDisplay->SetDialogue();
|
||||
audioKaraoke->Refresh(false);
|
||||
}
|
||||
|
||||
|
@ -594,7 +520,8 @@ void AudioBox::OnKaraoke(wxCommandEvent &event) {
|
|||
LOG_D("audio/box") << "karaoke disabled, enabling";
|
||||
karaokeMode = true;
|
||||
audioKaraoke->enabled = true;
|
||||
audioDisplay->SetDialogue();
|
||||
/// @todo Replace this with changing timing controller
|
||||
//audioDisplay->SetDialogue();
|
||||
}
|
||||
|
||||
SetKaraokeButtons();
|
||||
|
@ -618,15 +545,9 @@ void AudioBox::SetKaraokeButtons() {
|
|||
|
||||
JoinButton->Enable(join);
|
||||
SplitButton->Enable(split);
|
||||
if (audioKaraoke->splitting) {
|
||||
karaokeSizer->Show(CancelAcceptSizer);
|
||||
karaokeSizer->Hide(JoinSplitSizer);
|
||||
karaokeSizer->Layout();
|
||||
} else {
|
||||
karaokeSizer->Hide(CancelAcceptSizer);
|
||||
karaokeSizer->Show(JoinSplitSizer);
|
||||
karaokeSizer->Layout();
|
||||
}
|
||||
|
||||
karaokeSizer->Show(CancelAcceptSizer, audioKaraoke->splitting);
|
||||
karaokeSizer->Show(JoinSplitSizer, !audioKaraoke->splitting);
|
||||
}
|
||||
|
||||
/// @brief Join button in karaoke mode
|
||||
|
@ -671,7 +592,8 @@ void AudioBox::OnAccept(wxCommandEvent &event) {
|
|||
///
|
||||
void AudioBox::OnGoto(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->MakeDialogueVisible(true);
|
||||
if (controller->GetTimingController())
|
||||
audioDisplay->ScrollSampleRangeInView(controller->GetTimingController()->GetIdealVisibleSampleRange());
|
||||
}
|
||||
|
||||
|
||||
|
@ -706,26 +628,14 @@ void AudioBox::OnNextLineCommit(wxCommandEvent &event) {
|
|||
|
||||
|
||||
|
||||
/// @brief Medusa Mode
|
||||
/// @param event
|
||||
///
|
||||
/// @todo Put global audio hotkeys toggling into the menu bar
|
||||
/*
|
||||
void AudioBox::OnMedusaMode(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
OPT_SET("Audio/Medusa Timing Hotkeys")->SetBool(MedusaMode->GetValue());
|
||||
frameMain->SetAccelerators();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Spectrum Analyzer Mode
|
||||
/// @param event
|
||||
///
|
||||
void AudioBox::OnSpectrumMode(wxCommandEvent &event) {
|
||||
OPT_SET("Audio/Spectrum")->SetBool(SpectrumMode->GetValue());
|
||||
audioDisplay->UpdateImage(false);
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->Refresh(false);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
@ -734,7 +644,7 @@ void AudioBox::OnSpectrumMode(wxCommandEvent &event) {
|
|||
///
|
||||
void AudioBox::OnLeadIn(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->AddLead(true,false);
|
||||
//audioDisplay->AddLead(true,false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -743,21 +653,6 @@ void AudioBox::OnLeadIn(wxCommandEvent &event) {
|
|||
///
|
||||
void AudioBox::OnLeadOut(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->AddLead(false,true);
|
||||
//audioDisplay->AddLead(false,true);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Focus event handling for the scrollbar
|
||||
BEGIN_EVENT_TABLE(FocusEvent,wxEvtHandler)
|
||||
EVT_SET_FOCUS(FocusEvent::OnSetFocus)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
/// @brief DOCME
|
||||
/// @param event
|
||||
///
|
||||
void FocusEvent::OnSetFocus(wxFocusEvent &event) {
|
||||
wxWindow *previous = event.GetWindow();
|
||||
if (previous) previous->SetFocus();
|
||||
}
|
||||
|
|
|
@ -54,9 +54,14 @@
|
|||
#include <wx/tglbtn.h>
|
||||
#endif
|
||||
|
||||
#ifndef AGI_AUDIO_CONTROLLER_INCLUDED
|
||||
#error You must include "audio_controller.h" before "audio_box.h"
|
||||
#endif
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AssDialogue;
|
||||
class AudioDisplay;
|
||||
class AudioKaraoke;
|
||||
class FrameMain;
|
||||
|
@ -66,18 +71,21 @@ class ToggleBitmap;
|
|||
|
||||
|
||||
|
||||
/// DOCME
|
||||
/// @class AudioBox
|
||||
/// @brief DOCME
|
||||
///
|
||||
/// DOCME
|
||||
/// @brief Panel with audio playback and timing controls, also containing an AudioDisplay
|
||||
class AudioBox : public wxPanel {
|
||||
friend class AudioDisplay;
|
||||
/// @todo Get rid of this ASAP, currently required for FrameMain to be able to notify
|
||||
/// audio display about renderer having changed.
|
||||
friend class FrameMain;
|
||||
|
||||
private:
|
||||
/// The audio display in the box
|
||||
AudioDisplay *audioDisplay;
|
||||
|
||||
/// DOCME
|
||||
wxScrollBar *audioScroll;
|
||||
/// Selection controller used for timing controllers
|
||||
SelectionController<AssDialogue> *selection_controller;
|
||||
|
||||
/// The regular dalogue timing controller
|
||||
AudioTimingController *timing_controller_dialogue;
|
||||
|
||||
/// DOCME
|
||||
wxSlider *HorizontalZoom;
|
||||
|
@ -100,9 +108,6 @@ private:
|
|||
/// DOCME
|
||||
wxSizer *DisplaySizer;
|
||||
|
||||
/// DOCME
|
||||
wxSashWindow *Sash;
|
||||
|
||||
/// DOCME
|
||||
ToggleBitmap *VerticalLink;
|
||||
|
||||
|
@ -133,21 +138,13 @@ private:
|
|||
/// DOCME
|
||||
ToggleBitmap *NextCommit;
|
||||
|
||||
/// DOCME
|
||||
ToggleBitmap *MedusaMode;
|
||||
|
||||
/// DOCME
|
||||
ToggleBitmap *AutoCommit;
|
||||
|
||||
/// DOCME
|
||||
ToggleBitmap *SpectrumMode;
|
||||
|
||||
void OnScrollbar(wxScrollEvent &event);
|
||||
void OnHorizontalZoom(wxScrollEvent &event);
|
||||
void OnVerticalZoom(wxScrollEvent &event);
|
||||
void OnVolume(wxScrollEvent &event);
|
||||
void OnVerticalLink(wxCommandEvent &event);
|
||||
void OnSash(wxSashEvent &event);
|
||||
|
||||
void OnPlaySelection(wxCommandEvent &event);
|
||||
void OnPlayDialogue(wxCommandEvent &event);
|
||||
|
@ -171,14 +168,13 @@ private:
|
|||
|
||||
void OnAutoGoto(wxCommandEvent &event);
|
||||
void OnAutoCommit(wxCommandEvent &event);
|
||||
void OnMedusaMode(wxCommandEvent &event);
|
||||
void OnSpectrumMode(wxCommandEvent &event);
|
||||
void OnNextLineCommit(wxCommandEvent &event);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// DOCME
|
||||
AudioDisplay *audioDisplay;
|
||||
/// The controller controlling this audio box
|
||||
AudioController *controller;
|
||||
|
||||
/// DOCME
|
||||
AudioKaraoke *audioKaraoke;
|
||||
|
@ -189,124 +185,15 @@ public:
|
|||
/// DOCME
|
||||
FrameMain *frameMain;
|
||||
|
||||
/// DOCME
|
||||
wxString audioName;
|
||||
|
||||
/// DOCME
|
||||
bool loaded;
|
||||
|
||||
/// DOCME
|
||||
bool karaokeMode;
|
||||
|
||||
AudioBox(wxWindow *parent, SubtitlesGrid *grid);
|
||||
AudioBox(wxWindow *parent, AudioController *controller, SelectionController<AssDialogue> *selection_controller);
|
||||
~AudioBox();
|
||||
|
||||
void SetFile(wxString file,bool FromVideo);
|
||||
void SetKaraokeButtons();
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// DOCME
|
||||
/// @class FocusEvent
|
||||
/// @brief DOCME
|
||||
///
|
||||
/// DOCME
|
||||
class FocusEvent : public wxEvtHandler {
|
||||
|
||||
private:
|
||||
void OnSetFocus(wxFocusEvent &event);
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
||||
///////
|
||||
// IDs
|
||||
enum {
|
||||
|
||||
/// DOCME
|
||||
Audio_Scrollbar = 1600,
|
||||
|
||||
/// DOCME
|
||||
Audio_Horizontal_Zoom,
|
||||
|
||||
/// DOCME
|
||||
Audio_Vertical_Zoom,
|
||||
|
||||
/// DOCME
|
||||
Audio_Volume,
|
||||
|
||||
/// DOCME
|
||||
Audio_Sash,
|
||||
|
||||
/// DOCME
|
||||
Audio_Vertical_Link,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Play,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Stop,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Prev,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Next,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Play_500ms_Before,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Play_500ms_After,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Play_500ms_First,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Play_500ms_Last,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Play_Row,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Play_To_End,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Commit,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Karaoke,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Goto,
|
||||
|
||||
Audio_Button_Join, /// Karaoke -> Enter join mode.
|
||||
Audio_Button_Split, /// Karaoke -> Enter split mode.
|
||||
Audio_Button_Accept, /// Karaoke -> Split/Join mode -> Accept.
|
||||
Audio_Button_Cancel, /// KAraoke -> Split/Join mode -> Cancel.
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Leadin,
|
||||
|
||||
/// DOCME
|
||||
Audio_Button_Leadout,
|
||||
|
||||
|
||||
/// DOCME
|
||||
Audio_Check_AutoCommit,
|
||||
|
||||
/// DOCME
|
||||
Audio_Check_NextCommit,
|
||||
|
||||
/// DOCME
|
||||
Audio_Check_AutoGoto,
|
||||
|
||||
/// DOCME
|
||||
Audio_Check_Medusa,
|
||||
|
||||
/// DOCME
|
||||
Audio_Check_Spectrum
|
||||
};
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) 2009-2010, Niels Martin Hansen
|
||||
// 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 Project http://www.aegisub.org/
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/// @file audio_colorscheme.cpp
|
||||
/// @ingroup audio_ui
|
||||
///
|
||||
/// Manage colour schemes for the audio display
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
#include "audio_colorscheme.h"
|
||||
#include "colorspace.h"
|
||||
|
||||
// Something is defining "min" and "max" macros, and they interfere with using std::min and std::max
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
|
||||
|
||||
void AudioColorScheme::InitIcyBlue_Normal()
|
||||
{
|
||||
unsigned char *palptr = palette;
|
||||
for (size_t i = 0; i <= factor; ++i)
|
||||
{
|
||||
float t = (float)i / factor;
|
||||
int H = (int)(255 * (1.5 - t) / 2);
|
||||
int S = (int)(255 * (0.5 + t/2));
|
||||
int L = std::min(255, (int)(128 * 2 * t));
|
||||
hsl_to_rgb(H, S, L, palptr + 0, palptr + 1, palptr + 2);
|
||||
palptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioColorScheme::InitIcyBlue_Selected()
|
||||
{
|
||||
unsigned char *palptr = palette;
|
||||
for (size_t i = 0; i <= factor; ++i)
|
||||
{
|
||||
float t = (float)i / factor;
|
||||
int H = (int)(255 * (1.5 - t) / 2);
|
||||
int S = (int)(255 * (0.5 + t/2));
|
||||
int L = std::min(255, (int)(128 * (3 * t/2 + 0.5)));
|
||||
hsl_to_rgb(H, S, L, palptr + 0, palptr + 1, palptr + 2);
|
||||
palptr += 4;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) 2009-2010, Niels Martin Hansen
|
||||
// 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 Project http://www.aegisub.org/
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/// @file audio_colorscheme.h
|
||||
/// @see audio_colorscheme.cpp
|
||||
/// @ingroup audio_ui
|
||||
///
|
||||
/// Manage colour schemes for the audio display
|
||||
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <wx/colour.h>
|
||||
#endif
|
||||
|
||||
|
||||
/// @class AudioSpectrumColorMap
|
||||
/// @brief Provides colour maps for audio display rendering
|
||||
///
|
||||
/// Maps values from floats in range 0..1 into RGB colour values.
|
||||
///
|
||||
/// First create an instance of this class, then call an initialisation function
|
||||
/// in it to fill the palette with a colour map.
|
||||
///
|
||||
/// @todo Let consumers of this class specify their own palette generation function.
|
||||
class AudioColorScheme {
|
||||
/// The palette data for the map
|
||||
unsigned char *palette;
|
||||
|
||||
/// Factor to multiply 0..1 values by to map them into the palette range
|
||||
size_t factor;
|
||||
|
||||
public:
|
||||
/// @brief Constructor
|
||||
/// @param prec Bit precision to create the colour map with
|
||||
///
|
||||
/// Allocates the palette array to 2^prec entries
|
||||
AudioColorScheme(int prec)
|
||||
: palette(new unsigned char[(4<<prec) + 4])
|
||||
, factor(1<<prec)
|
||||
{
|
||||
}
|
||||
|
||||
/// @brief Destructor
|
||||
///
|
||||
/// De-allocates the palette array
|
||||
~AudioColorScheme()
|
||||
{
|
||||
delete[] palette;
|
||||
}
|
||||
|
||||
/// @brief Initialise the palette to the Aegisub 2.1 "Icy Blue" scheme (unselected)
|
||||
void InitIcyBlue_Normal();
|
||||
/// @brief Initialise the palette to the Aegisub 2.1 "Icy Blue" scheme (selected)
|
||||
void InitIcyBlue_Selected();
|
||||
|
||||
/// @brief Map a floating point value to RGB
|
||||
/// @param val [in] The value to map from
|
||||
/// @param pixel [out] First byte of the pixel to write
|
||||
///
|
||||
/// Writes into the XRGB pixel (assumed 32 bit without alpha) passed.
|
||||
/// The pixel format is assumed to be the same as that in the palette.
|
||||
inline void map(float val, unsigned char *pixel)
|
||||
{
|
||||
if (val < 0.0) val = 0.0;
|
||||
if (val > 1.0) val = 1.0;
|
||||
// Find the colour in the palette
|
||||
unsigned char *color = palette + ((int)(val*factor) * 4);
|
||||
// Copy to the destination.
|
||||
// Has to be done one byte at a time since we're writing RGB and not RGBX or RGBA
|
||||
// data, and we otherwise write past the end of the pixel we're writing, possibly
|
||||
// hitting adjacent memory blocks or just overwriting the start of the following
|
||||
// scanline in the image.
|
||||
// As the image is 24 bpp, 3 of every 4 uint32_t writes would be unaligned anyway.
|
||||
pixel[0] = color[0];
|
||||
pixel[1] = color[1];
|
||||
pixel[2] = color[2];
|
||||
}
|
||||
|
||||
/// @brief Get a floating point value's colour as a wxColour
|
||||
/// @param val The value to map from
|
||||
/// @return The corresponding wxColour
|
||||
wxColour get(float val)
|
||||
{
|
||||
if (val < 0.0) val = 0.0;
|
||||
if (val > 1.0) val = 1.0;
|
||||
unsigned char *color = palette + ((int)(val*factor) * 4);
|
||||
return wxColour(color[0], color[1], color[2]);
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,568 @@
|
|||
// Copyright (c) 2009-2010, Niels Martin Hansen
|
||||
// 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 Project http://www.aegisub.org/
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/// @file audio_controller.cpp
|
||||
/// @brief Manage open audio and abstract state away from display
|
||||
/// @ingroup audio_ui
|
||||
///
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <algorithm>
|
||||
|
||||
#include <wx/filename.h>
|
||||
#endif
|
||||
|
||||
#include "selection_controller.h"
|
||||
#include "audio_controller.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "include/aegisub/audio_player.h"
|
||||
#include "audio_provider_dummy.h"
|
||||
#include "audio_timing.h"
|
||||
#include "compat.h"
|
||||
#include "video_context.h"
|
||||
|
||||
class AudioMarkerKeyframe : public AudioMarker {
|
||||
int64_t position;
|
||||
static wxPen style;
|
||||
public:
|
||||
AudioMarkerKeyframe(int64_t position) : position(position) { }
|
||||
int64_t GetPosition() const { return position; }
|
||||
FeetStyle GetFeet() const { return Feet_None; }
|
||||
bool CanSnap() const { return true; }
|
||||
wxPen GetStyle() const
|
||||
{
|
||||
if (!style.IsOk())
|
||||
/// @todo Make this colour configurable
|
||||
style = wxPen(wxColour(255,0,255), 1);
|
||||
return style;
|
||||
}
|
||||
bool operator < (const AudioMarkerKeyframe &other) const { return position < other.position; }
|
||||
operator int64_t() const { return position; }
|
||||
};
|
||||
bool operator < (int64_t a, const AudioMarkerKeyframe &b) { return a < b.GetPosition(); }
|
||||
bool operator < (const AudioMarkerKeyframe &a, int64_t b) { return a.GetPosition() < b; }
|
||||
wxPen AudioMarkerKeyframe::style;
|
||||
|
||||
class AudioMarkerProviderKeyframes : public AudioMarkerProvider, private AudioControllerAudioEventListener {
|
||||
// GetMarkers needs to be const but still needs to modify this state, which is really
|
||||
// just a cache... use the mutable "hack".
|
||||
mutable int last_keyframes_revision;
|
||||
mutable std::vector<AudioMarkerKeyframe> keyframe_samples;
|
||||
AudioController *controller;
|
||||
int64_t samplerate;
|
||||
|
||||
void ReloadKeyframes() const
|
||||
{
|
||||
keyframe_samples.clear();
|
||||
|
||||
VideoContext *vc = VideoContext::Get();
|
||||
if (!vc) return;
|
||||
|
||||
last_keyframes_revision = vc->GetKeyframesRevision();
|
||||
const std::vector<int> &raw_keyframes = vc->GetKeyFrames();
|
||||
keyframe_samples.reserve(raw_keyframes.size());
|
||||
for (size_t i = 0; i < raw_keyframes.size(); ++i)
|
||||
{
|
||||
keyframe_samples.push_back(AudioMarkerKeyframe(
|
||||
vc->TimeAtFrame(raw_keyframes[i]) * samplerate / 1000));
|
||||
}
|
||||
std::sort(keyframe_samples.begin(), keyframe_samples.end());
|
||||
}
|
||||
|
||||
private:
|
||||
// AudioControllerAudioEventListener implementation
|
||||
virtual void OnAudioOpen(AudioProvider *provider)
|
||||
{
|
||||
samplerate = provider->GetSampleRate();
|
||||
ReloadKeyframes();
|
||||
}
|
||||
virtual void OnAudioClose() { }
|
||||
virtual void OnPlaybackPosition(int64_t sample_position) { }
|
||||
virtual void OnPlaybackStop() { }
|
||||
|
||||
public:
|
||||
AudioMarkerProviderKeyframes(AudioController *controller)
|
||||
: controller(controller)
|
||||
{
|
||||
// Assume that a video context with keyframes revision 0 never has keyframes loaded
|
||||
last_keyframes_revision = 0;
|
||||
samplerate = 44100;
|
||||
controller->AddAudioListener(this);
|
||||
}
|
||||
|
||||
virtual ~AudioMarkerProviderKeyframes()
|
||||
{
|
||||
controller->RemoveAudioListener(this);
|
||||
}
|
||||
|
||||
void GetMarkers(const AudioController::SampleRange &range, AudioMarkerVector &out) const
|
||||
{
|
||||
VideoContext *vc = VideoContext::Get();
|
||||
if (!vc) return;
|
||||
|
||||
// Re-read keyframe data if the revision number changed, the keyframe data probably did too
|
||||
if (vc->GetKeyframesRevision() != last_keyframes_revision)
|
||||
ReloadKeyframes();
|
||||
|
||||
// Find first and last keyframes inside the range
|
||||
std::vector<AudioMarkerKeyframe>::iterator a = std::lower_bound(
|
||||
keyframe_samples.begin(), keyframe_samples.end(), range.begin());
|
||||
std::vector<AudioMarkerKeyframe>::iterator b = std::upper_bound(
|
||||
keyframe_samples.begin(), keyframe_samples.end(), range.end());
|
||||
|
||||
// Place pointers to the markers in the output vector
|
||||
for (; a != b; ++a)
|
||||
out.push_back(&*a);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Type of the audio event listener container in AudioController
|
||||
typedef std::set<AudioControllerAudioEventListener *> AudioEventListenerSet;
|
||||
/// Type of the timing event listener container in AudioController
|
||||
typedef std::set<AudioControllerTimingEventListener *> TimingEventListenerSet;
|
||||
|
||||
/// Macro to iterate audio event listeners in AudioController implementation
|
||||
#define AUDIO_LISTENERS(listener) for (AudioEventListenerSet::iterator listener = audio_event_listeners.begin(); listener != audio_event_listeners.end(); ++listener)
|
||||
/// Macro to iterate audio event listeners in AudioController implementation
|
||||
#define TIMING_LISTENERS(listener) for (TimingEventListenerSet::iterator listener = timing_event_listeners.begin(); listener != timing_event_listeners.end(); ++listener)
|
||||
|
||||
|
||||
AudioController::AudioController()
|
||||
: player(0)
|
||||
, provider(0)
|
||||
, timing_controller(0)
|
||||
, keyframes_marker_provider(new AudioMarkerProviderKeyframes(this))
|
||||
, playback_mode(PM_NotPlaying)
|
||||
, playback_timer(this)
|
||||
{
|
||||
Connect(playback_timer.GetId(), wxEVT_TIMER, (wxObjectEventFunction)&AudioController::OnPlaybackTimer);
|
||||
|
||||
#ifdef wxHAS_POWER_EVENTS
|
||||
Connect(wxEVT_POWER_SUSPENDED, (wxObjectEventFunction)&AudioController::OnComputerSuspending);
|
||||
Connect(wxEVT_POWER_RESUME, (wxObjectEventFunction)&AudioController::OnComputerResuming);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
AudioController::~AudioController()
|
||||
{
|
||||
CloseAudio();
|
||||
}
|
||||
|
||||
|
||||
void AudioController::OnPlaybackTimer(wxTimerEvent &event)
|
||||
{
|
||||
int64_t pos = player->GetCurrentPosition();
|
||||
|
||||
if (!player->IsPlaying() ||
|
||||
(playback_mode != PM_ToEnd && pos >= player->GetEndPosition()+200))
|
||||
{
|
||||
// The +200 is to allow the player to end the sound output cleanly, otherwise a popping
|
||||
// artifact can sometimes be heard.
|
||||
Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
AUDIO_LISTENERS(l)
|
||||
{
|
||||
(*l)->OnPlaybackPosition(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef wxHAS_POWER_EVENTS
|
||||
void AudioController::OnComputerSuspending(wxPowerEvent &event)
|
||||
{
|
||||
Stop();
|
||||
player->CloseStream();
|
||||
}
|
||||
|
||||
|
||||
void AudioController::OnComputerResuming(wxPowerEvent &event)
|
||||
{
|
||||
if (provider)
|
||||
player->OpenStream();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void AudioController::OpenAudio(const wxString &url)
|
||||
{
|
||||
CloseAudio();
|
||||
|
||||
if (!url)
|
||||
throw agi::InternalError("AudioController::OpenAudio() was passed an empty string. This must not happen.", 0);
|
||||
|
||||
wxString path_part;
|
||||
|
||||
if (url.StartsWith(_T("dummy-audio:"), &path_part))
|
||||
{
|
||||
/*
|
||||
* scheme ::= "dummy-audio" ":" signal-specifier "?" signal-parameters
|
||||
* signal-specifier ::= "silence" | "noise" | "sine" "/" frequency
|
||||
* frequency ::= integer
|
||||
* signal-parameters ::= signal-parameter [ "&" signal-parameters ]
|
||||
* signal-parameter ::= signal-parameter-name "=" integer
|
||||
* signal-parameter-name ::= "sr" | "bd" | "ch" | "ln"
|
||||
*
|
||||
* Signal types:
|
||||
* "silence", a silent signal is generated.
|
||||
* "noise", a white noise signal is generated.
|
||||
* "sine", a sine wave is generated at the specified frequency.
|
||||
*
|
||||
* Signal parameters:
|
||||
* "sr", sample rate to generate signal at.
|
||||
* "bd", bit depth to generate signal at (usually 16).
|
||||
* "ch", number of channels to generate, usually 1 or 2. The same signal is generated
|
||||
* in every channel even if one would be LFE.
|
||||
* "ln", length of signal in samples. ln/sr gives signal length in seconds.
|
||||
*/
|
||||
provider = new DummyAudioProvider(5*30*60*1000, true);
|
||||
}
|
||||
else if (url.StartsWith(_T("video-audio:"), &path_part))
|
||||
{
|
||||
/*
|
||||
* scheme ::= "video-audio" ":" stream-type
|
||||
* stream-type ::= "stream" | "cache"
|
||||
*
|
||||
* Stream types:
|
||||
*
|
||||
* "stream", the audio is streamed as required directly from the video provider,
|
||||
* and cannot be used to drive an audio display. Seeking is unreliable.
|
||||
*
|
||||
* "cache", the entire audio is cached to memory or disk. Audio displays can be
|
||||
* driven and seeking is reliable. Opening takes longer because the entire audio
|
||||
* stream has to be decoded and stored.
|
||||
*/
|
||||
}
|
||||
else if (url.StartsWith(_T("file:"), &path_part))
|
||||
{
|
||||
/*
|
||||
* scheme ::= "file" ":" "//" file-system-path
|
||||
*
|
||||
* On Unix-like systems, the file system path is regular. On Windows-systems, the
|
||||
* path uses forward slashes instead of back-slashes and the drive letter is
|
||||
* preceded by a slash.
|
||||
*
|
||||
* URL-encoding??
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Assume it's not a URI but instead a filename in the platform's native format.
|
||||
*/
|
||||
wxFileName fn(url);
|
||||
if (!fn.FileExists())
|
||||
{
|
||||
agi::FileNotFoundError fnf(STD_STR(url));
|
||||
throw agi::AudioOpenError(
|
||||
"Failed opening audio file (parsing as plain filename)",
|
||||
&fnf);
|
||||
}
|
||||
provider = AudioProviderFactory::GetProvider(url);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
player = AudioPlayerFactory::GetAudioPlayer();
|
||||
player->SetProvider(provider);
|
||||
player->OpenStream();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
delete player;
|
||||
delete provider;
|
||||
player = 0;
|
||||
provider = 0;
|
||||
throw;
|
||||
}
|
||||
|
||||
// Tell listeners about this.
|
||||
AUDIO_LISTENERS(l)
|
||||
{
|
||||
(*l)->OnAudioOpen(provider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioController::CloseAudio()
|
||||
{
|
||||
Stop();
|
||||
|
||||
delete player;
|
||||
delete provider;
|
||||
player = 0;
|
||||
provider = 0;
|
||||
|
||||
AUDIO_LISTENERS(l)
|
||||
{
|
||||
(*l)->OnAudioClose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool AudioController::IsAudioOpen() const
|
||||
{
|
||||
return player && provider;
|
||||
}
|
||||
|
||||
|
||||
wxString AudioController::GetAudioURL() const
|
||||
{
|
||||
/// @todo figure out how to get the url
|
||||
return _T("");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioController::AddAudioListener(AudioControllerAudioEventListener *listener)
|
||||
{
|
||||
audio_event_listeners.insert(listener);
|
||||
}
|
||||
|
||||
|
||||
void AudioController::RemoveAudioListener(AudioControllerAudioEventListener *listener)
|
||||
{
|
||||
audio_event_listeners.erase(listener);
|
||||
}
|
||||
|
||||
|
||||
void AudioController::AddTimingListener(AudioControllerTimingEventListener *listener)
|
||||
{
|
||||
timing_event_listeners.insert(listener);
|
||||
}
|
||||
|
||||
|
||||
void AudioController::RemoveTimingListener(AudioControllerTimingEventListener *listener)
|
||||
{
|
||||
timing_event_listeners.erase(listener);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioController::SetTimingController(AudioTimingController *new_controller)
|
||||
{
|
||||
delete timing_controller;
|
||||
timing_controller = new_controller;
|
||||
|
||||
TIMING_LISTENERS(l)
|
||||
{
|
||||
(*l)->OnTimingControllerChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioController::OnTimingControllerUpdatedPrimaryRange(AudioTimingController *sending_controller)
|
||||
{
|
||||
assert(sending_controller != 0);
|
||||
if (sending_controller != timing_controller)
|
||||
return;
|
||||
|
||||
if (playback_mode == PM_PrimaryRange)
|
||||
{
|
||||
player->SetEndPosition(timing_controller->GetPrimaryPlaybackRange().end());
|
||||
}
|
||||
|
||||
TIMING_LISTENERS(l)
|
||||
{
|
||||
(*l)->OnSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioController::OnTimingControllerUpdatedStyleRanges(AudioTimingController *sending_controller)
|
||||
{
|
||||
assert(sending_controller != 0);
|
||||
if (sending_controller != timing_controller)
|
||||
return;
|
||||
|
||||
/// @todo redraw and stuff, probably
|
||||
}
|
||||
|
||||
|
||||
void AudioController::OnTimingControllerMarkerMoved(AudioTimingController *sending_controller, AudioMarker *marker)
|
||||
{
|
||||
assert(sending_controller != 0);
|
||||
if (sending_controller != timing_controller)
|
||||
return;
|
||||
|
||||
/// @todo shouldn't this be more detailed?
|
||||
TIMING_LISTENERS(l)
|
||||
{
|
||||
(*l)->OnMarkersMoved();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioController::PlayRange(const AudioController::SampleRange &range)
|
||||
{
|
||||
if (!IsAudioOpen()) return;
|
||||
|
||||
player->Play(range.begin(), range.length());
|
||||
playback_mode = PM_Range;
|
||||
playback_timer.Start(20);
|
||||
|
||||
AUDIO_LISTENERS(l)
|
||||
{
|
||||
(*l)->OnPlaybackPosition(range.begin());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioController::PlayPrimaryRange()
|
||||
{
|
||||
PlayRange(GetPrimaryPlaybackRange());
|
||||
if (playback_mode == PM_Range)
|
||||
playback_mode = PM_PrimaryRange;
|
||||
}
|
||||
|
||||
|
||||
void AudioController::PlayToEnd(int64_t start_sample)
|
||||
{
|
||||
if (!IsAudioOpen()) return;
|
||||
|
||||
player->Play(start_sample, provider->GetNumSamples()-start_sample);
|
||||
playback_mode = PM_ToEnd;
|
||||
playback_timer.Start(20);
|
||||
|
||||
AUDIO_LISTENERS(l)
|
||||
{
|
||||
(*l)->OnPlaybackPosition(start_sample);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioController::Stop()
|
||||
{
|
||||
if (!IsAudioOpen()) return;
|
||||
|
||||
player->Stop();
|
||||
playback_mode = PM_NotPlaying;
|
||||
playback_timer.Stop();
|
||||
|
||||
AUDIO_LISTENERS(l)
|
||||
{
|
||||
(*l)->OnPlaybackStop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool AudioController::IsPlaying()
|
||||
{
|
||||
return IsAudioOpen() && playback_mode != PM_NotPlaying;
|
||||
}
|
||||
|
||||
|
||||
int64_t AudioController::GetPlaybackPosition()
|
||||
{
|
||||
if (!IsPlaying()) return 0;
|
||||
|
||||
return player->GetCurrentPosition();
|
||||
}
|
||||
|
||||
|
||||
void AudioController::ResyncPlaybackPosition(int64_t new_position)
|
||||
{
|
||||
if (!IsPlaying()) return;
|
||||
|
||||
player->SetCurrentPosition(new_position);
|
||||
}
|
||||
|
||||
|
||||
AudioController::SampleRange AudioController::GetPrimaryPlaybackRange() const
|
||||
{
|
||||
if (timing_controller != 0)
|
||||
{
|
||||
return timing_controller->GetPrimaryPlaybackRange();
|
||||
}
|
||||
else
|
||||
{
|
||||
return SampleRange(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioController::GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const
|
||||
{
|
||||
/// @todo Find all sources of markers
|
||||
keyframes_marker_provider->GetMarkers(range, markers);
|
||||
}
|
||||
|
||||
|
||||
double AudioController::GetVolume() const
|
||||
{
|
||||
if (!IsAudioOpen()) return 1.0;
|
||||
return player->GetVolume();
|
||||
}
|
||||
|
||||
|
||||
void AudioController::SetVolume(double volume)
|
||||
{
|
||||
if (!IsAudioOpen()) return;
|
||||
player->SetVolume(volume);
|
||||
}
|
||||
|
||||
|
||||
int64_t AudioController::SamplesFromMilliseconds(int64_t ms) const
|
||||
{
|
||||
/// @todo There might be some subtle rounding errors here.
|
||||
|
||||
if (!provider) return 0;
|
||||
|
||||
int64_t sr = provider->GetSampleRate();
|
||||
|
||||
int64_t millisamples = ms * sr;
|
||||
|
||||
return (millisamples + 999) / 1000;
|
||||
}
|
||||
|
||||
|
||||
int64_t AudioController::MillisecondsFromSamples(int64_t samples) const
|
||||
{
|
||||
/// @todo There might be some subtle rounding errors here.
|
||||
|
||||
if (!provider) return 0;
|
||||
|
||||
int64_t sr = provider->GetSampleRate();
|
||||
|
||||
int64_t millisamples = samples * 1000;
|
||||
|
||||
return millisamples / sr;
|
||||
}
|
||||
|
|
@ -0,0 +1,439 @@
|
|||
// Copyright (c) 2009-2010, Niels Martin Hansen
|
||||
// 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 Project http://www.aegisub.org/
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/// @file audio_controller.h
|
||||
/// @see audio_controller.cpp
|
||||
/// @ingroup audio_ui
|
||||
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/pen.h>
|
||||
#include <wx/power.h>
|
||||
#endif
|
||||
|
||||
#include <libaegisub/exception.h>
|
||||
|
||||
#define AGI_AUDIO_CONTROLLER_INCLUDED 1
|
||||
|
||||
|
||||
class AudioPlayer;
|
||||
class AudioProvider;
|
||||
|
||||
// Declared below
|
||||
class AudioControllerAudioEventListener;
|
||||
class AudioControllerTimingEventListener;
|
||||
class AudioTimingController;
|
||||
class AudioMarker;
|
||||
class AudioMarkerProvider;
|
||||
|
||||
|
||||
typedef std::vector<const AudioMarker*> AudioMarkerVector;
|
||||
|
||||
|
||||
/// @class AudioController
|
||||
/// @brief Manage an open audio stream and UI state for it
|
||||
///
|
||||
/// Keeps track of the UI interaction state of the open audio for a project, ie. what the current
|
||||
/// selection is, what moveable markers are on the audio, and any secondary non-moveable markers
|
||||
/// that are present.
|
||||
///
|
||||
/// Changes in interaction are broadcast to all managed audio displays so they can redraw, and
|
||||
/// the audio displays report all interactions back to the controller. There is a one to many
|
||||
/// relationship between controller and audio displays. There is at most one audio controller
|
||||
/// for an open subtitling project.
|
||||
///
|
||||
/// Creates and destroys audio providers and players. This behaviour should at some point be moved
|
||||
/// to a separate class, as it adds too many responsibilities to this class, but at the time of
|
||||
/// writing, it would extend the scope of reworking components too much.
|
||||
///
|
||||
/// There is not supposed to be a way to get direct access to the audio providers or players owned
|
||||
/// by a controller. If some operation that isn't possible in the existing design is needed, the
|
||||
/// controller should be extended in some way to allow it.
|
||||
class AudioController : public wxEvtHandler {
|
||||
public:
|
||||
|
||||
/// @class SampleRange
|
||||
/// @brief Represents an immutable range of audio samples
|
||||
class SampleRange {
|
||||
int64_t _begin;
|
||||
int64_t _end;
|
||||
|
||||
public:
|
||||
/// @brief Constructor
|
||||
/// @param begin Index of the first sample to include in the range
|
||||
/// @param end Index of one past the last sample to include in the range
|
||||
SampleRange(int64_t begin, int64_t end)
|
||||
: _begin(begin)
|
||||
, _end(end)
|
||||
{
|
||||
assert(end >= begin);
|
||||
}
|
||||
|
||||
/// @brief Copy constructor, optionally adjusting the range
|
||||
/// @param src The range to duplicate
|
||||
/// @param begin_adjust Number of samples to add to the start of the range
|
||||
/// @param end_adjust Number of samples to add to the end of the range
|
||||
SampleRange(const SampleRange &src, int64_t begin_adjust = 0, int64_t end_adjust = 0)
|
||||
{
|
||||
_begin = src._begin + begin_adjust;
|
||||
_end = src._end + end_adjust;
|
||||
assert(_end >= _begin);
|
||||
}
|
||||
|
||||
/// Get the number of samples in the range
|
||||
int64_t length() const { return _end - _begin; }
|
||||
/// Get the index of the first sample in the range
|
||||
int64_t begin() const { return _begin; }
|
||||
/// Get the index of one past the last sample in the range
|
||||
int64_t end() const { return _end; }
|
||||
|
||||
/// Determine whether the range contains a given sample index
|
||||
bool contains(int64_t sample) const { return sample >= begin() && sample < end(); }
|
||||
|
||||
/// Determine whether there is an overlap between two ranges
|
||||
bool overlaps(const SampleRange &other) const
|
||||
{
|
||||
return other.contains(_begin)
|
||||
|| other.contains(_end)
|
||||
|| contains(other._begin)
|
||||
|| contains(other._end);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/// Listeners for audio-related events
|
||||
std::set<AudioControllerAudioEventListener *> audio_event_listeners;
|
||||
|
||||
/// Listeners for timing-related events
|
||||
std::set<AudioControllerTimingEventListener *> timing_event_listeners;
|
||||
|
||||
/// The audio output object
|
||||
AudioPlayer *player;
|
||||
|
||||
/// The audio provider
|
||||
AudioProvider *provider;
|
||||
|
||||
/// The current timing mode, if any; owned by the audio controller
|
||||
AudioTimingController *timing_controller;
|
||||
|
||||
/// Provide keyframe data for audio displays
|
||||
std::auto_ptr<AudioMarkerProvider> keyframes_marker_provider;
|
||||
|
||||
|
||||
enum PlaybackMode {
|
||||
PM_NotPlaying,
|
||||
PM_Range,
|
||||
PM_PrimaryRange,
|
||||
PM_ToEnd
|
||||
};
|
||||
/// The current playback mode
|
||||
PlaybackMode playback_mode;
|
||||
|
||||
|
||||
/// Timer used for playback position updates
|
||||
wxTimer playback_timer;
|
||||
|
||||
/// Event handler for the playback timer
|
||||
void OnPlaybackTimer(wxTimerEvent &event);
|
||||
|
||||
|
||||
#ifdef wxHAS_POWER_EVENTS
|
||||
/// Handle computer going into suspend mode by stopping audio and closing device
|
||||
void OnComputerSuspending(wxPowerEvent &event);
|
||||
/// Handle computer resuming from suspend by re-opening the audio device
|
||||
void OnComputerResuming(wxPowerEvent &event);
|
||||
#endif
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructor
|
||||
AudioController();
|
||||
|
||||
/// @brief Destructor
|
||||
~AudioController();
|
||||
|
||||
|
||||
/// @brief Open an audio stream
|
||||
/// @param url URL of the stream to open
|
||||
///
|
||||
/// The URL can either be a plain filename (with no qualifiers) or one
|
||||
/// recognised by various providers.
|
||||
void OpenAudio(const wxString &url);
|
||||
|
||||
/// @brief Closes the current audio stream
|
||||
void CloseAudio();
|
||||
|
||||
/// @brief Determine whether audio is currently open
|
||||
/// @return True if an audio stream is open and can be played back
|
||||
bool IsAudioOpen() const;
|
||||
|
||||
/// @brief Get the URL for the current open audio stream
|
||||
/// @return The URL for the audio stream
|
||||
///
|
||||
/// The returned URL can be passed into OpenAudio() later to open the same stream again.
|
||||
wxString GetAudioURL() const;
|
||||
|
||||
|
||||
/// @brief Add an audio event listener
|
||||
/// @param listener The listener to add
|
||||
void AddAudioListener(AudioControllerAudioEventListener *listener);
|
||||
|
||||
/// @brief Remove an audio event listener
|
||||
/// @param listener The listener to remove
|
||||
void RemoveAudioListener(AudioControllerAudioEventListener *listener);
|
||||
|
||||
/// @brief Add a timing event listener
|
||||
/// @param listener The listener to add
|
||||
void AddTimingListener(AudioControllerTimingEventListener *listener);
|
||||
|
||||
/// @brief Remove a timing event listener
|
||||
/// @param listener The listener to remove
|
||||
void RemoveTimingListener(AudioControllerTimingEventListener *listener);
|
||||
|
||||
|
||||
/// @brief Start or restart audio playback, playing a range
|
||||
/// @param range The range of audio to play back
|
||||
///
|
||||
/// The end of the played back range may be requested changed, but is not changed
|
||||
/// automatically from any other operations.
|
||||
void PlayRange(const SampleRange &range);
|
||||
|
||||
/// @brief Start or restart audio playback, playing the primary playback range
|
||||
///
|
||||
/// If the primary playback range is updated during playback, the end of the
|
||||
/// active playback range will be updated to match the new selection. The playback
|
||||
/// end can not be changed in any other way.
|
||||
void PlayPrimaryRange();
|
||||
|
||||
/// @brief Start or restart audio playback, playing from a point to the end of stream
|
||||
/// @param start_sample Index of the sample to start playback at
|
||||
///
|
||||
/// Playback to end cannot be converted to a range playback like range playback can,
|
||||
/// it will continue until the end is reached, it is stopped, or restarted.
|
||||
void PlayToEnd(int64_t start_sample);
|
||||
|
||||
/// @brief Stop all audio playback
|
||||
void Stop();
|
||||
|
||||
/// @brief Determine whether playback is ongoing
|
||||
/// @return True if audio is being played back
|
||||
bool IsPlaying();
|
||||
|
||||
/// @brief Get the current playback position
|
||||
/// @return Approximate current sample index being heard by the user
|
||||
///
|
||||
/// Returns 0 if playback is stopped. The return value is only approximate.
|
||||
int64_t GetPlaybackPosition();
|
||||
|
||||
/// @brief If playing, restart playback from the specified position
|
||||
/// @param new_position Sample index to restart playback from
|
||||
///
|
||||
/// This function can be used to re-synchronise audio playback to another source that
|
||||
/// might not be able to keep up with the full speed, such as video playback in high
|
||||
/// resolution or with complex subtitles.
|
||||
///
|
||||
/// This function only does something if audio is already playing.
|
||||
void ResyncPlaybackPosition(int64_t new_position);
|
||||
|
||||
|
||||
/// @brief Get the primary playback range
|
||||
/// @return An immutable SampleRange object
|
||||
SampleRange GetPrimaryPlaybackRange() const;
|
||||
|
||||
/// @brief Get all static markers inside a range
|
||||
/// @param range The sample range to retrieve markers for
|
||||
/// @param markers Vector to fill found markers into
|
||||
///
|
||||
/// The markers retrieved are static markers the user can't interact with.
|
||||
/// Markers for user interaction are obtained through the timing controller.
|
||||
void GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const;
|
||||
|
||||
|
||||
/// @brief Get the playback audio volume
|
||||
/// @return The amplification factor for the audio
|
||||
double GetVolume() const;
|
||||
|
||||
/// @brief Set the playback audio volume
|
||||
/// @param volume The new amplification factor for the audio
|
||||
void SetVolume(double volume);
|
||||
|
||||
|
||||
/// @brief Return the current audio provider
|
||||
/// @return A const pointer to the current audio provider
|
||||
const AudioProvider * GetAudioProvider() const { return provider; }
|
||||
|
||||
|
||||
/// @brief Return the current timing controller
|
||||
/// @return The current timing controller or 0
|
||||
AudioTimingController * GetTimingController() const { return timing_controller; }
|
||||
|
||||
/// @brief Change the current timing controller
|
||||
/// @param new_mode The new timing controller or 0. This may be the same object as
|
||||
/// the current timing controller, to signal that the timing controller has changed
|
||||
/// the object being timed, eg. changed to a new dialogue line.
|
||||
void SetTimingController(AudioTimingController *new_controller);
|
||||
|
||||
|
||||
/// @brief Timing controller signals primary playback range changed
|
||||
/// @param timing_controller The timing controller sending this notification
|
||||
///
|
||||
/// Only timing controllers should call this function. This function must be called
|
||||
/// when the primary playback range is changed in the timing controller, usually
|
||||
/// as a result of user interaction.
|
||||
void OnTimingControllerUpdatedPrimaryRange(AudioTimingController *timing_controller);
|
||||
|
||||
/// @brief Timing controller signals that the rendering style ranges have changed
|
||||
/// @param timing_controller The timing controller sending this notification
|
||||
///
|
||||
/// Only timing controllers should call this function. This function must be called
|
||||
/// when one or more rendering style ranges have changed in the timing controller.
|
||||
void OnTimingControllerUpdatedStyleRanges(AudioTimingController *timing_controller);
|
||||
|
||||
/// @brief Timing controller signals that an audio marker has moved
|
||||
/// @param timing_controller The timing controller sending this notification
|
||||
/// @param marker The marker that was moved
|
||||
///
|
||||
/// Only timing controllers should call this function. This function must be called
|
||||
/// when a marker owned by the timing controller has been updated in some way.
|
||||
void OnTimingControllerMarkerMoved(AudioTimingController *timing_controller, AudioMarker *marker);
|
||||
|
||||
|
||||
/// @brief Convert a count of audio samples to a time in milliseconds
|
||||
/// @param samples Sample count to convert
|
||||
/// @return The number of milliseconds equivalent to the sample-count, rounded down
|
||||
int64_t MillisecondsFromSamples(int64_t samples) const;
|
||||
|
||||
/// @brief Convert a time in milliseconds to a count of audio samples
|
||||
/// @param ms Time in milliseconds to convert
|
||||
/// @return The index of the first sample that is wholly inside the millisecond
|
||||
int64_t SamplesFromMilliseconds(int64_t ms) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @class AudioControllerAudioEventListener
|
||||
/// @brief Abstract interface for objects that want audio events
|
||||
class AudioControllerAudioEventListener {
|
||||
public:
|
||||
/// A new audio stream was opened (and any previously open was closed)
|
||||
virtual void OnAudioOpen(AudioProvider *) = 0;
|
||||
|
||||
/// The current audio stream was closed
|
||||
virtual void OnAudioClose() = 0;
|
||||
|
||||
/// Playback is in progress and ths current position was updated
|
||||
virtual void OnPlaybackPosition(int64_t sample_position) = 0;
|
||||
|
||||
/// Playback has stopped
|
||||
virtual void OnPlaybackStop() = 0;
|
||||
};
|
||||
|
||||
|
||||
/// @class AudioControllerTimingEventListener
|
||||
/// @brief Abstract interface for objects that want audio timing events
|
||||
class AudioControllerTimingEventListener {
|
||||
public:
|
||||
/// One or more moveable markers were moved
|
||||
virtual void OnMarkersMoved() = 0;
|
||||
|
||||
/// The selection was changed
|
||||
virtual void OnSelectionChanged() = 0;
|
||||
|
||||
/// The timing controller was replaced
|
||||
virtual void OnTimingControllerChanged() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @class AudioMarkerProvider
|
||||
/// @brief Abstract interface for audio marker providers
|
||||
class AudioMarkerProvider {
|
||||
public:
|
||||
/// Virtual destructor, does nothing
|
||||
virtual ~AudioMarkerProvider() { }
|
||||
|
||||
/// @brief Return markers in a sample range
|
||||
virtual void GetMarkers(const AudioController::SampleRange &range, AudioMarkerVector &out) const = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @class AudioMarker
|
||||
/// @brief A marker on the audio display
|
||||
class AudioMarker {
|
||||
public:
|
||||
|
||||
/// Describe which directions a marker has feet in
|
||||
enum FeetStyle {
|
||||
Feet_None = 0,
|
||||
Feet_Left,
|
||||
Feet_Right,
|
||||
Feet_Both // Conveniently Feet_Left|Feet_Right
|
||||
};
|
||||
|
||||
/// @brief Get the marker's position
|
||||
/// @return The marker's position in samples
|
||||
virtual int64_t GetPosition() const = 0;
|
||||
|
||||
/// @brief Get the marker's drawing style
|
||||
/// @return A pen object describing the marker's drawing style
|
||||
virtual wxPen GetStyle() const = 0;
|
||||
|
||||
/// @brief Get the marker's feet style
|
||||
/// @return The marker's feet style
|
||||
virtual FeetStyle GetFeet() const = 0;
|
||||
|
||||
/// @brief Retrieve whether this marker participates in snapping
|
||||
/// @return True if this marker may snap to other snappable markers
|
||||
///
|
||||
/// If a marker being dragged returns true from this method, and another marker which also
|
||||
/// returns true from this method is within range, the marker being dragged will be positioned
|
||||
/// at the position of the other marker if it is released while it is inside snapping range.
|
||||
virtual bool CanSnap() const = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace agi {
|
||||
DEFINE_BASE_EXCEPTION(AudioControllerError, Exception);
|
||||
DEFINE_SIMPLE_EXCEPTION(AudioOpenError, AudioControllerError, "audio_controller/open_failed");
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// Copyright (c) 2009-2010, Niels Martin Hansen
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -45,247 +46,285 @@
|
|||
#include <wx/window.h>
|
||||
#endif
|
||||
|
||||
#include <libaegisub/signals.h>
|
||||
|
||||
#include "audio_renderer_spectrum.h"
|
||||
#include "selection_controller.h"
|
||||
|
||||
class AudioBox;
|
||||
#ifndef AGI_AUDIO_CONTROLLER_INCLUDED
|
||||
#error You must include "audio_controller.h" before "audio_display.h"
|
||||
#endif
|
||||
|
||||
|
||||
class AudioRenderer;
|
||||
class AudioSpectrumRenderer;
|
||||
class AudioWaveformRenderer;
|
||||
class AudioKaraoke;
|
||||
class AudioPlayer;
|
||||
class AudioProvider;
|
||||
class AssDialogue;
|
||||
class FrameMain;
|
||||
class AudioPlayer;
|
||||
class SubtitlesGrid;
|
||||
class VideoProvider;
|
||||
|
||||
/// DOCME
|
||||
/// @class AudioDisplay
|
||||
/// @brief DOCME
|
||||
class AudioBox;
|
||||
class SubtitlesGrid;
|
||||
class AssDialogue;
|
||||
class wxScrollBar;
|
||||
|
||||
// Helper classes used in implementation of the audio display
|
||||
class AudioDisplayScrollbar;
|
||||
class AudioDisplayTimeline;
|
||||
class AudioDisplaySelection;
|
||||
|
||||
|
||||
|
||||
/// @class AudioDisplayInteractionObject
|
||||
/// @brief Interface for objects on the audio display that can respond to mouse events
|
||||
class AudioDisplayInteractionObject {
|
||||
public:
|
||||
/// @brief The user is interacting with the object using the mouse
|
||||
/// @param event Mouse event data
|
||||
/// @return True to take mouse capture, false to release mouse capture
|
||||
///
|
||||
/// DOCME
|
||||
class AudioDisplay: public wxWindow, private SelectionListener<AssDialogue> {
|
||||
friend class FrameMain;
|
||||
/// Assuming no object has the mouse capture, the audio display uses other methods
|
||||
/// in the object implementing this interface to deterine whether a mouse event
|
||||
/// should go to the object. If the mouse event goes to the object, this method
|
||||
/// is called.
|
||||
///
|
||||
/// If this method returns true, the audio display takes the mouse capture and
|
||||
/// stores a pointer to the AudioDisplayInteractionObject interface for the object
|
||||
/// and redirects the next mouse event to that object.
|
||||
///
|
||||
/// If the object that has the mouse capture returns false from this method, the
|
||||
/// capture is released and regular processing is done for the next event.
|
||||
///
|
||||
/// If the object does not have mouse capture and returns false from this method,
|
||||
/// no capture is taken or released and regular processing is done for the next
|
||||
/// mouse event.
|
||||
virtual bool OnMouseEvent(wxMouseEvent &event) = 0;
|
||||
|
||||
/// @brief Destructor
|
||||
///
|
||||
/// Empty virtual destructor for the cases that need it.
|
||||
virtual ~AudioDisplayInteractionObject() { }
|
||||
};
|
||||
|
||||
|
||||
/// @class AudioDisplay
|
||||
/// @brief Primary view/UI for interaction with audio timing
|
||||
///
|
||||
/// The audio display is the common view that allows the user to interact with the active
|
||||
/// timing controller. The audio display also renders audio according to the audio controller
|
||||
/// and the timing controller, using an audio renderer instance.
|
||||
class AudioDisplay: public wxWindow, private AudioControllerAudioEventListener, private AudioControllerTimingEventListener {
|
||||
private:
|
||||
|
||||
/// DOCME
|
||||
SubtitlesGrid *grid;
|
||||
/// The audio renderer manager
|
||||
AudioRenderer *audio_renderer;
|
||||
|
||||
/// DOCME
|
||||
int line_n;
|
||||
/// The renderer for audio spectrums
|
||||
AudioSpectrumRenderer *audio_spectrum_renderer;
|
||||
|
||||
/// DOCME
|
||||
AssDialogue *dialogue;
|
||||
/// The renderer for audio waveforms
|
||||
AudioWaveformRenderer *audio_waveform_renderer;
|
||||
|
||||
/// DOCME
|
||||
AudioSpectrum *spectrumRenderer;
|
||||
/// Our current audio provider
|
||||
AudioProvider *provider;
|
||||
|
||||
/// DOCME
|
||||
wxBitmap *origImage;
|
||||
/// The controller managing us
|
||||
AudioController *controller;
|
||||
|
||||
/// DOCME
|
||||
wxBitmap *spectrumDisplay;
|
||||
|
||||
/// DOCME
|
||||
wxBitmap *spectrumDisplaySelected;
|
||||
/// Scrollbar helper object
|
||||
AudioDisplayScrollbar *scrollbar;
|
||||
|
||||
/// DOCME
|
||||
int64_t PositionSample;
|
||||
/// Timeline helper object
|
||||
AudioDisplayTimeline *timeline;
|
||||
|
||||
/// DOCME
|
||||
float scale;
|
||||
|
||||
/// DOCME
|
||||
int samples;
|
||||
/// Current object on display being dragged, if any
|
||||
AudioDisplayInteractionObject *dragged_object;
|
||||
/// Change the dragged object and update mouse capture
|
||||
void SetDraggedObject(AudioDisplayInteractionObject *new_obj);
|
||||
|
||||
/// DOCME
|
||||
int64_t Position;
|
||||
|
||||
/// DOCME
|
||||
int samplesPercent;
|
||||
/// Leftmost pixel in the vitual audio image being displayed
|
||||
int scroll_left;
|
||||
|
||||
/// DOCME
|
||||
int oldCurPos;
|
||||
/// Total width of the audio in pixels
|
||||
int pixel_audio_width;
|
||||
|
||||
/// DOCME
|
||||
bool hasFocus;
|
||||
/// Horizontal zoom measured in audio samples per pixel
|
||||
int pixel_samples;
|
||||
|
||||
/// DOCME
|
||||
bool blockUpdate;
|
||||
/// Amplitude scaling ("vertical zoom") as a factor, 1.0 is neutral
|
||||
float scale_amplitude;
|
||||
|
||||
/// DOCME
|
||||
bool dontReadTimes;
|
||||
/// Top of the main audio area in pixels
|
||||
int audio_top;
|
||||
|
||||
/// DOCME
|
||||
bool playingToEnd;
|
||||
/// Height of main audio area in pixels
|
||||
int audio_height;
|
||||
|
||||
/// DOCME
|
||||
bool needImageUpdate;
|
||||
|
||||
/// DOCME
|
||||
bool needImageUpdateWeak;
|
||||
/// Zoom level given as a number, see SetZoomLevel for details
|
||||
int zoom_level;
|
||||
// Mouse wheel zoom accumulator
|
||||
int mouse_zoom_accum;
|
||||
|
||||
/// DOCME
|
||||
bool hasSel;
|
||||
|
||||
/// DOCME
|
||||
bool hasKaraoke;
|
||||
/// Absolute pixel position of the tracking cursor (mouse or playback)
|
||||
int track_cursor_pos;
|
||||
/// Label to show by track cursor
|
||||
wxString track_cursor_label;
|
||||
/// Bounding rectangle last drawn track cursor label
|
||||
wxRect track_cursor_label_rect;
|
||||
/// @brief Move the tracking cursor
|
||||
/// @param new_pos New absolute pixel position of the tracking cursor
|
||||
/// @param show_time Display timestamp by the tracking cursor?
|
||||
void SetTrackCursor(int new_pos, bool show_time);
|
||||
/// @brief Remove the tracking cursor from the display
|
||||
void RemoveTrackCursor();
|
||||
|
||||
/// DOCME
|
||||
bool diagUpdated;
|
||||
|
||||
/// DOCME
|
||||
bool holding;
|
||||
/// Previous audio selection for optimising redraw when selection changes
|
||||
AudioController::SampleRange old_selection;
|
||||
|
||||
/// DOCME
|
||||
bool draggingScale;
|
||||
|
||||
/// DOCME
|
||||
int64_t selStart;
|
||||
|
||||
/// DOCME
|
||||
int64_t selEnd;
|
||||
|
||||
/// DOCME
|
||||
int64_t lineStart;
|
||||
|
||||
/// DOCME
|
||||
int64_t lineEnd;
|
||||
|
||||
/// DOCME
|
||||
int64_t selStartCap;
|
||||
|
||||
/// DOCME
|
||||
int64_t selEndCap;
|
||||
|
||||
/// DOCME
|
||||
int hold;
|
||||
|
||||
/// DOCME
|
||||
int lastX;
|
||||
|
||||
/// DOCME
|
||||
int lastDragX;
|
||||
|
||||
/// DOCME
|
||||
int curStartMS;
|
||||
|
||||
/// DOCME
|
||||
int curEndMS;
|
||||
|
||||
/// DOCME
|
||||
int holdSyl;
|
||||
|
||||
/// DOCME
|
||||
int *peak;
|
||||
|
||||
/// DOCME
|
||||
int *min;
|
||||
|
||||
/// wxWidgets paint event
|
||||
void OnPaint(wxPaintEvent &event);
|
||||
/// wxWidgets mouse input event
|
||||
void OnMouseEvent(wxMouseEvent &event);
|
||||
/// wxWidgets control size changed event
|
||||
void OnSize(wxSizeEvent &event);
|
||||
void OnUpdateTimer(wxTimerEvent &event);
|
||||
void OnKeyDown(wxKeyEvent &event);
|
||||
void OnGetFocus(wxFocusEvent &event);
|
||||
void OnLoseFocus(wxFocusEvent &event);
|
||||
/// wxWidgets input focus changed event
|
||||
void OnFocus(wxFocusEvent &event);
|
||||
|
||||
void UpdateSamples();
|
||||
void Reset();
|
||||
void DrawTimescale(wxDC &dc);
|
||||
void DrawKeyframes(wxDC &dc);
|
||||
void DrawInactiveLines(wxDC &dc);
|
||||
void DrawWaveform(wxDC &dc,bool weak);
|
||||
void DrawSpectrum(wxDC &dc,bool weak);
|
||||
void GetDialoguePos(int64_t &start,int64_t &end,bool cap);
|
||||
void GetKaraokePos(int64_t &start,int64_t &end,bool cap);
|
||||
void UpdatePosition(int pos,bool IsSample=false);
|
||||
|
||||
int GetBoundarySnap(int x,int range,bool shiftHeld,bool start=true);
|
||||
void DoUpdateImage();
|
||||
private:
|
||||
// AudioControllerAudioEventListener implementation
|
||||
virtual void OnAudioOpen(AudioProvider *provider);
|
||||
virtual void OnAudioClose();
|
||||
virtual void OnPlaybackPosition(int64_t sample_position);
|
||||
virtual void OnPlaybackStop();
|
||||
|
||||
// AudioControllerTimingEventListener implementation
|
||||
virtual void OnMarkersMoved();
|
||||
virtual void OnSelectionChanged();
|
||||
virtual void OnTimingControllerChanged();
|
||||
|
||||
void OnActiveLineChanged(AssDialogue *new_line);
|
||||
void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed);
|
||||
void OnCommit(int);
|
||||
agi::signal::Connection commitListener;
|
||||
|
||||
public:
|
||||
|
||||
/// DOCME
|
||||
AudioProvider *provider;
|
||||
|
||||
/// DOCME
|
||||
AudioPlayer *player;
|
||||
|
||||
/// DOCME
|
||||
bool NeedCommit;
|
||||
|
||||
/// DOCME
|
||||
bool loaded;
|
||||
|
||||
/// DOCME
|
||||
bool temporary;
|
||||
|
||||
/// DOCME
|
||||
|
||||
/// DOCME
|
||||
int w,h;
|
||||
|
||||
/// DOCME
|
||||
AudioBox *box;
|
||||
|
||||
/// DOCME
|
||||
AudioKaraoke *karaoke;
|
||||
|
||||
/// DOCME
|
||||
wxScrollBar *ScrollBar;
|
||||
|
||||
/// DOCME
|
||||
wxTimer UpdateTimer;
|
||||
|
||||
AudioDisplay(wxWindow *parent, SubtitlesGrid *grid);
|
||||
AudioDisplay(wxWindow *parent, AudioController *controller);
|
||||
~AudioDisplay();
|
||||
|
||||
void UpdateImage(bool weak=false);
|
||||
void Update();
|
||||
void RecreateImage();
|
||||
void SetPosition(int pos);
|
||||
void SetSamplesPercent(int percent,bool update=true,float pivot=0.5);
|
||||
void SetScale(float scale);
|
||||
void UpdateScrollbar();
|
||||
void SetDialogue(SubtitlesGrid *_grid=NULL,AssDialogue *diag=NULL,int n=-1);
|
||||
void MakeDialogueVisible(bool force=false);
|
||||
void ChangeLine(int delta, bool block=false);
|
||||
void Next(bool play=true);
|
||||
void Prev(bool play=true);
|
||||
|
||||
void CommitChanges(bool nextLine=false);
|
||||
void AddLead(bool in,bool out);
|
||||
/// @brief Scroll the audio display
|
||||
/// @param pixel_amount Number of pixels to scroll the view
|
||||
///
|
||||
/// A positive amount moves the display to the right, making later parts of the audio visible.
|
||||
void ScrollBy(int pixel_amount);
|
||||
|
||||
void SetFile(wxString file);
|
||||
void SetFromVideo();
|
||||
void Reload();
|
||||
/// @brief Scroll the audio display
|
||||
/// @param pixel_position Absolute pixel to put at left edge of the audio display
|
||||
///
|
||||
/// This is the principal scrolling function. All other scrolling functions eventually
|
||||
/// call this function to perform the actual scrolling.
|
||||
void ScrollPixelToLeft(int pixel_position);
|
||||
|
||||
void Play(int start,int end);
|
||||
void Stop();
|
||||
/// @brief Scroll the audio display
|
||||
/// @param pixel_position Absolute pixel to put in center of the audio display
|
||||
void ScrollPixelToCenter(int pixel_position);
|
||||
|
||||
int64_t GetSampleAtX(int x);
|
||||
int GetXAtSample(int64_t n);
|
||||
int GetMSAtX(int64_t x);
|
||||
int GetXAtMS(int64_t ms);
|
||||
int GetMSAtSample(int64_t x);
|
||||
int64_t GetSampleAtMS(int64_t ms);
|
||||
int GetSyllableAtX(int x);
|
||||
/// @brief Scroll the audio display
|
||||
/// @param sample_position Audio sample to put at left edge of the audio display
|
||||
void ScrollSampleToLeft(int64_t sample_position);
|
||||
|
||||
/// @brief Scroll the audio display
|
||||
/// @param sample_position Audio sample to put in center of the audio display
|
||||
void ScrollSampleToCenter(int64_t sample_position);
|
||||
|
||||
/// @brief Scroll the audio display
|
||||
/// @param range Range of audio samples to ensure is in view
|
||||
///
|
||||
/// If the entire range is already visible inside the display, nothing is scrolled. If
|
||||
/// just one of the two endpoints is visible, the display is scrolled such that the
|
||||
/// visible endpoint stays in view but more of the rest of the range becomes visible.
|
||||
///
|
||||
/// If the entire range fits inside the display, the display is centered over the range.
|
||||
/// For this calculation, the display is considered smaller by some margins, see below.
|
||||
///
|
||||
/// If the range does not fit within the display with margins subtracted, the start of
|
||||
/// the range is ensured visible and as much of the rest of the range is brought into
|
||||
/// view.
|
||||
///
|
||||
/// For the purpose of this function, a 5 percent margin is assumed at each end of the
|
||||
/// audio display such that a range endpoint that is ensured to be in view never gets
|
||||
/// closer to the edge of the display than the margin. The edge that is not ensured to
|
||||
/// be in view might be outside of view or might be closer to the display edge than the
|
||||
/// margin.
|
||||
void ScrollSampleRangeInView(const AudioController::SampleRange &range);
|
||||
|
||||
|
||||
/// @brief Change the zoom level
|
||||
/// @param new_zoom_level The new zoom level to use
|
||||
///
|
||||
/// A zoom level of 0 is the default zoom level, all other levels are based on this.
|
||||
/// Negative zoom levels zoom out, positive zoom in.
|
||||
///
|
||||
/// The zoom levels generally go from +30 to -30. It is possible to zoom in more than
|
||||
/// +30
|
||||
void SetZoomLevel(int new_zoom_level);
|
||||
|
||||
/// @brief Get the zoom level
|
||||
/// @return The zoom level
|
||||
///
|
||||
/// See SetZoomLevel for a description of zoom levels.
|
||||
int GetZoomLevel() const;
|
||||
|
||||
/// @brief Get a textual description of a zoom level
|
||||
/// @param level The zoom level to describe
|
||||
/// @return A translated string describing a zoom level
|
||||
///
|
||||
/// The zoom level description can tell the user details about how much audio is
|
||||
/// actually displayed.
|
||||
wxString GetZoomLevelDescription(int level) const;
|
||||
|
||||
/// @brief Get the zoom factor in percent for a zoom level
|
||||
/// @param level The zoom level to get the factor of
|
||||
/// @return The zoom factor in percent
|
||||
///
|
||||
/// Positive: 125, 150, 175, 200, 225, ...
|
||||
///
|
||||
/// Negative: 90, 80, 70, 60, 50, 45, 40, 35, 30, 25, 20, 19, 18, 17, ..., 1
|
||||
///
|
||||
/// Too negative numbers get clamped.
|
||||
static int GetZoomLevelFactor(int level);
|
||||
|
||||
|
||||
/// @brief Set amplitude scale factor
|
||||
/// @param scale New amplitude scale factor, 1.0 is no scaling
|
||||
void SetAmplitudeScale(float scale);
|
||||
|
||||
/// @brief Get amplitude scale factor
|
||||
/// @return The amplitude scaling factor
|
||||
float GetAmplitudeScale() const;
|
||||
|
||||
|
||||
/// @brief Reload all rendering settings from Options and reset caches
|
||||
///
|
||||
/// This can be called if some rendering quality settings have been changed in Options
|
||||
/// and need to be reloaded to take effect.
|
||||
void ReloadRenderingSettings();
|
||||
|
||||
|
||||
/// @brief Get a sample index from an X coordinate relative to current scroll
|
||||
int64_t SamplesFromRelativeX(int x) const { return (scroll_left + x) * pixel_samples; }
|
||||
/// @brief Get a sample index from an absolute X coordinate
|
||||
int64_t SamplesFromAbsoluteX(int x) const { return x * pixel_samples; }
|
||||
/// @brief Get an X coordinate relative to the current scroll from a sample index
|
||||
int RelativeXFromSamples(int64_t samples) const { return samples/pixel_samples - scroll_left; }
|
||||
/// @brief Get an absolute X coordinate from a sample index
|
||||
int AbsoluteXFromSamples(int64_t samples) const { return samples/pixel_samples; }
|
||||
|
||||
void GetTimesDialogue(int &start,int &end);
|
||||
void GetTimesSelection(int &start,int &end);
|
||||
void SetSelection(int start, int end);
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
///////
|
||||
// IDs
|
||||
enum {
|
||||
Audio_Update_Timer = 1700
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2005, 2006, 2007, Rodrigo Braz Monteiro, Niels Martin Hansen
|
||||
// Copyright (c) 2005-2009, Rodrigo Braz Monteiro, Niels Martin Hansen
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -48,6 +48,8 @@
|
|||
#include <libaegisub/log.h>
|
||||
|
||||
#include "ass_override.h"
|
||||
#include "selection_controller.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_box.h"
|
||||
#include "audio_display.h"
|
||||
#include "audio_karaoke.h"
|
||||
|
@ -634,7 +636,7 @@ void AudioKaraoke::Join() {
|
|||
|
||||
// Update
|
||||
must_rebuild = true;
|
||||
display->NeedCommit = true;
|
||||
//display->NeedCommit = true;
|
||||
display->Update();
|
||||
Refresh(false);
|
||||
|
||||
|
@ -678,7 +680,7 @@ void AudioKaraoke::EndSplit(bool commit) {
|
|||
if (hasSplit) {
|
||||
LOG_D("karaoke/audio") << "hassplit";
|
||||
must_rebuild = true;
|
||||
display->NeedCommit = true;
|
||||
//display->NeedCommit = true;
|
||||
SetSelection(first_sel);
|
||||
display->Update();
|
||||
}
|
||||
|
@ -879,8 +881,9 @@ void AudioKaraokeTagMenu::OnSelectItem(wxCommandEvent &event) {
|
|||
// Update display
|
||||
kara->must_rebuild = true;
|
||||
//kara->Commit();
|
||||
kara->display->NeedCommit = true;
|
||||
kara->display->CommitChanges();
|
||||
//kara->display->NeedCommit = true;
|
||||
/// @todo Commit changes and stay on current line
|
||||
//kara->display->CommitChanges();
|
||||
//kara->display->Update();
|
||||
kara->SetSelection(firstsel, lastsel);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "audio_player_alsa.h"
|
||||
#include "main.h"
|
||||
#include "compat.h"
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "audio_player_dsound.h"
|
||||
#include "frame_main.h"
|
||||
#include "main.h"
|
||||
|
|
|
@ -49,9 +49,10 @@
|
|||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "audio_player_dsound2.h"
|
||||
#include "frame_main.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "frame_main.h"
|
||||
#include "main.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "audio_player_openal.h"
|
||||
#include "frame_main.h"
|
||||
#include "utils.h"
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "audio_player_oss.h"
|
||||
#include "frame_main.h"
|
||||
#include "compat.h"
|
||||
|
|
|
@ -65,77 +65,6 @@ AudioProvider::~AudioProvider() {
|
|||
delete[] raw;
|
||||
}
|
||||
|
||||
/// @brief Get waveform
|
||||
/// @param min
|
||||
/// @param peak
|
||||
/// @param start
|
||||
/// @param w
|
||||
/// @param h
|
||||
/// @param samples
|
||||
/// @param scale
|
||||
///
|
||||
void AudioProvider::GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int samples,float scale) {
|
||||
// Setup
|
||||
int channels = GetChannels();
|
||||
int n = w * samples;
|
||||
for (int i=0;i<w;i++) {
|
||||
peak[i] = 0;
|
||||
min[i] = h;
|
||||
}
|
||||
|
||||
// Prepare waveform
|
||||
int cur;
|
||||
int curvalue;
|
||||
|
||||
// Prepare buffers
|
||||
int needLen = n*channels*bytes_per_sample;
|
||||
if (raw) {
|
||||
if (raw_len < needLen) {
|
||||
delete[] raw;
|
||||
raw = NULL;
|
||||
}
|
||||
}
|
||||
if (!raw) {
|
||||
raw_len = needLen;
|
||||
raw = new char[raw_len];
|
||||
}
|
||||
|
||||
if (bytes_per_sample == 1) {
|
||||
// Read raw samples
|
||||
unsigned char *raw_char = (unsigned char*) raw;
|
||||
GetAudio(raw,start,n);
|
||||
int amplitude = int(h*scale);
|
||||
|
||||
// Calculate waveform
|
||||
for (int i=0;i<n;i++) {
|
||||
cur = i/samples;
|
||||
curvalue = h - (int(raw_char[i*channels])*amplitude)/0xFF;
|
||||
if (curvalue > h) curvalue = h;
|
||||
if (curvalue < 0) curvalue = 0;
|
||||
if (curvalue < min[cur]) min[cur] = curvalue;
|
||||
if (curvalue > peak[cur]) peak[cur] = curvalue;
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes_per_sample == 2) {
|
||||
// Read raw samples
|
||||
short *raw_short = (short*) raw;
|
||||
GetAudio(raw,start,n);
|
||||
int half_h = h/2;
|
||||
int half_amplitude = int(half_h * scale);
|
||||
|
||||
// Calculate waveform
|
||||
for (int i=0;i<n;i++) {
|
||||
cur = i/samples;
|
||||
curvalue = half_h - (int(raw_short[i*channels])*half_amplitude)/0x8000;
|
||||
if (curvalue > h) curvalue = h;
|
||||
if (curvalue < 0) curvalue = 0;
|
||||
if (curvalue < min[cur]) min[cur] = curvalue;
|
||||
if (curvalue > peak[cur]) peak[cur] = curvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Get audio with volume
|
||||
/// @param buf
|
||||
/// @param start
|
||||
|
@ -143,7 +72,7 @@ void AudioProvider::GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int
|
|||
/// @param volume
|
||||
/// @return
|
||||
///
|
||||
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) {
|
||||
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
|
||||
try {
|
||||
GetAudio(buf,start,count);
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ void AvisynthAudioProvider::LoadFromClip(AVSValue _clip) {
|
|||
/// @param start
|
||||
/// @param count
|
||||
///
|
||||
void AvisynthAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) {
|
||||
void AvisynthAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
||||
// Requested beyond the length of audio
|
||||
if (start+count > num_samples) {
|
||||
int64_t oldcount = count;
|
||||
|
|
|
@ -57,15 +57,12 @@ class AvisynthAudioProvider : public AudioProvider, public AviSynthWrapper {
|
|||
public:
|
||||
AvisynthAudioProvider(wxString _filename);
|
||||
|
||||
wxString GetFilename() { return filename; }
|
||||
wxString GetFilename() const { return filename; }
|
||||
|
||||
/// @brief Only exists for x86 Windows, always delivers machine (little) endian
|
||||
/// @return
|
||||
///
|
||||
bool AreSamplesNativeEndian() const { return true; }
|
||||
bool NeedsCache() const { return true; }
|
||||
|
||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
||||
void GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int samples,float scale);
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -62,7 +62,7 @@ ConvertAudioProvider::ConvertAudioProvider(AudioProvider *src) : source(src) {
|
|||
/// @param dst
|
||||
/// @param count
|
||||
///
|
||||
void ConvertAudioProvider::Make16Bit(const char *src, short *dst, int64_t count) {
|
||||
void ConvertAudioProvider::Make16Bit(const char *src, short *dst, int64_t count) const {
|
||||
for (int64_t i=0;i<count;i++) {
|
||||
dst[i] = (short(src[i])-128)*255;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ template<class SampleConverter>
|
|||
/// @param count
|
||||
/// @param converter
|
||||
///
|
||||
void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_t count, const SampleConverter &converter) {
|
||||
void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_t count, const SampleConverter &converter) const {
|
||||
// Upsample by 2
|
||||
if (sampleMult == 2) {
|
||||
int64_t size = count/2;
|
||||
|
@ -139,7 +139,7 @@ struct EndianSwapSampleConverter {
|
|||
/// @param start
|
||||
/// @param count
|
||||
///
|
||||
void ConvertAudioProvider::GetAudio(void *destination, int64_t start, int64_t count) {
|
||||
void ConvertAudioProvider::GetAudio(void *destination, int64_t start, int64_t count) const {
|
||||
// Bits per sample
|
||||
int srcBps = source->GetBytesPerSample();
|
||||
|
||||
|
|
|
@ -51,9 +51,9 @@ class ConvertAudioProvider : public AudioProvider {
|
|||
|
||||
/// DOCME
|
||||
std::tr1::shared_ptr<AudioProvider> source;
|
||||
void Make16Bit(const char *src, short *dst, int64_t count);
|
||||
void Make16Bit(const char *src, short *dst, int64_t count) const;
|
||||
template<class SampleConverter>
|
||||
void ChangeSampleRate(const short *src, short *dst, int64_t count, const SampleConverter &converter);
|
||||
void ChangeSampleRate(const short *src, short *dst, int64_t count, const SampleConverter &converter) const;
|
||||
|
||||
public:
|
||||
ConvertAudioProvider(AudioProvider *source);
|
||||
|
@ -62,9 +62,9 @@ public:
|
|||
/// That's one of the points of it!
|
||||
bool AreSamplesNativeEndian() const { return true; }
|
||||
|
||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
||||
|
||||
wxString GetFilename() { return source->GetFilename(); }
|
||||
wxString GetFilename() const { return source->GetFilename(); }
|
||||
};
|
||||
|
||||
AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider);
|
||||
|
|
|
@ -64,7 +64,7 @@ DownmixingAudioProvider::DownmixingAudioProvider(AudioProvider *source) : provid
|
|||
/// @param start
|
||||
/// @param count
|
||||
///
|
||||
void DownmixingAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) {
|
||||
void DownmixingAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
||||
if (count == 0) return;
|
||||
|
||||
// We can do this ourselves
|
||||
|
|
|
@ -57,5 +57,5 @@ public:
|
|||
///
|
||||
bool AreSamplesNativeEndian() const { return true; }
|
||||
|
||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
||||
};
|
||||
|
|
|
@ -62,7 +62,7 @@ DummyAudioProvider::~DummyAudioProvider() {
|
|||
/// @param start
|
||||
/// @param count
|
||||
///
|
||||
void DummyAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) {
|
||||
void DummyAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
||||
short *workbuf = (short*)buf;
|
||||
|
||||
if (noise) {
|
||||
|
|
|
@ -50,5 +50,5 @@ public:
|
|||
~DummyAudioProvider();
|
||||
|
||||
bool AreSamplesNativeEndian() const { return true; }
|
||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
||||
};
|
||||
|
|
|
@ -228,7 +228,7 @@ void FFmpegSourceAudioProvider::Close() {
|
|||
/// @param Start
|
||||
/// @param Count
|
||||
///
|
||||
void FFmpegSourceAudioProvider::GetAudio(void *Buf, int64_t Start, int64_t Count) {
|
||||
void FFmpegSourceAudioProvider::GetAudio(void *Buf, int64_t Start, int64_t Count) const {
|
||||
uint8_t *Buf2 = static_cast<uint8_t*>(Buf);
|
||||
Start -= delay;
|
||||
if (Start < 0) {
|
||||
|
|
|
@ -46,8 +46,8 @@ private:
|
|||
FFMS_AudioSource *AudioSource; ///< audio source object
|
||||
bool COMInited; ///< COM initialization state
|
||||
|
||||
char FFMSErrMsg[1024]; ///< FFMS error message
|
||||
FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
|
||||
mutable char FFMSErrMsg[1024]; ///< FFMS error message
|
||||
mutable FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
|
||||
|
||||
void Close();
|
||||
void LoadAudio(wxString filename);
|
||||
|
@ -65,6 +65,6 @@ public:
|
|||
bool AreSamplesNativeEndian() const { return true; }
|
||||
bool NeedsCache() const { return true; }
|
||||
|
||||
virtual void GetAudio(void *buf, int64_t start, int64_t count);
|
||||
virtual void GetAudio(void *buf, int64_t start, int64_t count) const;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <wx/filename.h>
|
||||
#endif
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "audio_provider_hd.h"
|
||||
#include "compat.h"
|
||||
#include "dialog_progress.h"
|
||||
|
@ -114,7 +115,7 @@ HDAudioProvider::~HDAudioProvider() {
|
|||
/// @param start
|
||||
/// @param count
|
||||
///
|
||||
void HDAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) {
|
||||
void HDAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
||||
// Requested beyond the length of audio
|
||||
if (start+count > num_samples) {
|
||||
int64_t oldcount = count;
|
||||
|
|
|
@ -48,10 +48,10 @@
|
|||
/// DOCME
|
||||
class HDAudioProvider : public AudioProvider {
|
||||
/// DOCME
|
||||
wxMutex diskmutex;
|
||||
mutable wxMutex diskmutex;
|
||||
|
||||
/// DOCME
|
||||
wxFile file_cache;
|
||||
mutable wxFile file_cache;
|
||||
|
||||
/// DOCME
|
||||
wxString diskCacheFilename;
|
||||
|
@ -71,5 +71,5 @@ public:
|
|||
|
||||
bool AreSamplesNativeEndian() const { return samples_native_endian; }
|
||||
|
||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
||||
};
|
||||
|
|
|
@ -144,7 +144,7 @@ PCMAudioProvider::~PCMAudioProvider()
|
|||
/// @param range_length
|
||||
/// @return
|
||||
///
|
||||
char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length)
|
||||
char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const
|
||||
{
|
||||
if (range_start + range_length > file_size) {
|
||||
throw AudioDecodeError("Attempted to map beyond end of file");
|
||||
|
@ -217,13 +217,13 @@ char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t rang
|
|||
/// @param start
|
||||
/// @param count
|
||||
///
|
||||
void PCMAudioProvider::GetAudio(void *buf, int64_t start, int64_t count)
|
||||
void PCMAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const
|
||||
{
|
||||
// Read blocks from the file
|
||||
size_t index = 0;
|
||||
while (count > 0 && index < index_points.size()) {
|
||||
// Check if this index contains the samples we're looking for
|
||||
IndexPoint &ip = index_points[index];
|
||||
const IndexPoint &ip = index_points[index];
|
||||
if (ip.start_sample <= start && ip.start_sample+ip.num_samples > start) {
|
||||
|
||||
// How many samples we can maximum take from this block
|
||||
|
|
|
@ -64,24 +64,24 @@ private:
|
|||
HANDLE file_mapping;
|
||||
|
||||
/// DOCME
|
||||
void *current_mapping;
|
||||
mutable void *current_mapping;
|
||||
|
||||
/// DOCME
|
||||
int64_t mapping_start;
|
||||
mutable int64_t mapping_start;
|
||||
|
||||
/// DOCME
|
||||
size_t mapping_length;
|
||||
mutable size_t mapping_length;
|
||||
#else
|
||||
int file_handle;
|
||||
void *current_mapping;
|
||||
off_t mapping_start;
|
||||
size_t mapping_length;
|
||||
mutable void *current_mapping;
|
||||
mutable off_t mapping_start;
|
||||
mutable size_t mapping_length;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
PCMAudioProvider(const wxString &filename); // Create base object and open the file mapping
|
||||
virtual ~PCMAudioProvider(); // Closes the file mapping
|
||||
char * EnsureRangeAccessible(int64_t range_start, int64_t range_length); // Ensure that the given range of bytes are accessible in the file mapping and return a pointer to the first byte of the requested range
|
||||
char * EnsureRangeAccessible(int64_t range_start, int64_t range_length) const; // Ensure that the given range of bytes are accessible in the file mapping and return a pointer to the first byte of the requested range
|
||||
|
||||
|
||||
/// DOCME
|
||||
|
@ -108,7 +108,7 @@ protected:
|
|||
IndexVector index_points;
|
||||
|
||||
public:
|
||||
virtual void GetAudio(void *buf, int64_t start, int64_t count);
|
||||
virtual void GetAudio(void *buf, int64_t start, int64_t count) const;
|
||||
};
|
||||
|
||||
// Construct the right PCM audio provider (if any) for the file
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "audio_provider_ram.h"
|
||||
#include "dialog_progress.h"
|
||||
#include "frame_main.h"
|
||||
|
@ -131,7 +132,7 @@ void RAMAudioProvider::Clear() {
|
|||
/// @param start
|
||||
/// @param count
|
||||
///
|
||||
void RAMAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) {
|
||||
void RAMAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
||||
// Requested beyond the length of audio
|
||||
if (start+count > num_samples) {
|
||||
int64_t oldcount = count;
|
||||
|
|
|
@ -58,5 +58,5 @@ public:
|
|||
~RAMAudioProvider();
|
||||
|
||||
bool AreSamplesNativeEndian() const { return samples_native_endian; }
|
||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Niels Martin Hansen
|
||||
// Copyright (c) 2009-2010, Niels Martin Hansen
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -42,9 +42,13 @@
|
|||
#include <wx/dcmemory.h>
|
||||
#endif
|
||||
|
||||
#include "block_cache.h"
|
||||
#include "audio_renderer.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
|
||||
AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(AudioRenderer *_renderer)
|
||||
{
|
||||
|
@ -56,7 +60,7 @@ AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(Aud
|
|||
wxBitmap *AudioRendererBitmapCacheBitmapFactory::ProduceBlock(int i)
|
||||
{
|
||||
(void)i;
|
||||
return new wxBitmap(renderer->cache_bitmap_width, renderer->pixel_height, 32);
|
||||
return new wxBitmap(renderer->cache_bitmap_width, renderer->pixel_height, 24);
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,7 +72,7 @@ void AudioRendererBitmapCacheBitmapFactory::DisposeBlock(wxBitmap *bmp)
|
|||
|
||||
size_t AudioRendererBitmapCacheBitmapFactory::GetBlockSize() const
|
||||
{
|
||||
return sizeof(wxBitmap) + renderer->cache_bitmap_width * renderer->pixel_height * 4;
|
||||
return sizeof(wxBitmap) + renderer->cache_bitmap_width * renderer->pixel_height * 3;
|
||||
}
|
||||
|
||||
|
||||
|
@ -78,7 +82,8 @@ AudioRenderer::AudioRenderer()
|
|||
: cache_bitmap_width(32) // arbitrary value for now
|
||||
, bitmaps_normal(256, AudioRendererBitmapCacheBitmapFactory(this))
|
||||
, bitmaps_selected(256, AudioRendererBitmapCacheBitmapFactory(this))
|
||||
, cache_maxsize(0)
|
||||
, cache_bitmap_maxsize(0)
|
||||
, cache_renderer_maxsize(0)
|
||||
, renderer(0)
|
||||
, provider(0)
|
||||
{
|
||||
|
@ -166,7 +171,12 @@ void AudioRenderer::SetAudioProvider(AudioProvider *_provider)
|
|||
|
||||
void AudioRenderer::SetCacheMaxSize(size_t max_size)
|
||||
{
|
||||
cache_maxsize = max_size;
|
||||
// Limit the bitmap cache sizes to 16 MB hard, to avoid the risk of exhausting
|
||||
// system bitmap object resources and similar. Experimenting shows that 16 MB
|
||||
// bitmap cache should be plenty even if working with a one hour audio clip.
|
||||
cache_bitmap_maxsize = std::min(max_size/8, (size_t)0x1000000);
|
||||
// The renderer gets whatever is left.
|
||||
cache_renderer_maxsize = max_size - 2*cache_bitmap_maxsize;
|
||||
}
|
||||
|
||||
|
||||
|
@ -174,9 +184,10 @@ void AudioRenderer::ResetBlockCount()
|
|||
{
|
||||
if (provider)
|
||||
{
|
||||
size_t num_bitmaps = (size_t)((provider->GetNumSamples() + pixel_samples - 1) / pixel_samples);
|
||||
bitmaps_normal.SetBlockCount(num_bitmaps);
|
||||
bitmaps_selected.SetBlockCount(num_bitmaps);
|
||||
size_t rendered_width = (size_t)((provider->GetNumSamples() + pixel_samples - 1) / pixel_samples);
|
||||
cache_numblocks = rendered_width / cache_bitmap_width;
|
||||
bitmaps_normal.SetBlockCount(cache_numblocks);
|
||||
bitmaps_selected.SetBlockCount(cache_numblocks);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,16 +215,16 @@ wxBitmap AudioRenderer::GetCachedBitmap(int i, bool selected)
|
|||
|
||||
void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool selected)
|
||||
{
|
||||
assert(start >= 0);
|
||||
assert(length >= 0);
|
||||
|
||||
assert(start >= 0);
|
||||
|
||||
if (!provider) return;
|
||||
if (!renderer) return;
|
||||
if (length <= 0) return;
|
||||
|
||||
// Last absolute pixel strip to render
|
||||
int end = start + length - 1;
|
||||
// One past last absolute pixel strip to render
|
||||
int end = start + length;
|
||||
// One past last X coordinate to render on
|
||||
int lastx = origin.x + length;
|
||||
// Figure out which range of bitmaps are required
|
||||
int firstbitmap = start / cache_bitmap_width;
|
||||
// And the offset in it to start its use at
|
||||
|
@ -223,20 +234,36 @@ void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool
|
|||
// How many columns of the last bitmap to use
|
||||
int lastbitmapoffset = end % cache_bitmap_width;
|
||||
|
||||
// Two basic cases now: Either firstbitmap is the same as lastbitmap, or they're different.
|
||||
// Check if we need to render any blank audio past the last bitmap from cache,
|
||||
// this happens if we're asked to render more audio than the provider has.
|
||||
if (lastbitmap >= (int)cache_numblocks)
|
||||
{
|
||||
lastbitmap = cache_numblocks - 1;
|
||||
lastbitmapoffset = cache_bitmap_width;
|
||||
|
||||
if (firstbitmap > lastbitmap)
|
||||
firstbitmap = lastbitmap;
|
||||
}
|
||||
|
||||
// Three basic cases now:
|
||||
// * Either we're just rendering blank audio,
|
||||
// * Or there is exactly one bitmap to render,
|
||||
// * Or there is more than one bitmap to render.
|
||||
|
||||
// origin is passed by value because we'll be using it as a local var to keep track
|
||||
// of rendering progress!
|
||||
|
||||
if (firstbitmap == lastbitmap)
|
||||
if (start / cache_bitmap_width >= (int)cache_numblocks)
|
||||
{
|
||||
// These better be the same: The first to the last column of the single bitmap
|
||||
// to use should equal the length of the area to render.
|
||||
assert(lastbitmapoffset - firstbitmapoffset == length);
|
||||
|
||||
// Do nothing, the blank audio rendering will happen later
|
||||
}
|
||||
else if (firstbitmap == lastbitmap)
|
||||
{
|
||||
const int renderwidth = lastbitmapoffset - firstbitmapoffset;
|
||||
wxBitmap bmp = GetCachedBitmap(firstbitmap, selected);
|
||||
wxMemoryDC bmpdc(bmp);
|
||||
dc.Blit(origin, wxSize(length, pixel_height), &bmpdc, wxPoint(firstbitmapoffset, 0));
|
||||
dc.Blit(origin, wxSize(renderwidth, pixel_height), &bmpdc, wxPoint(firstbitmapoffset, 0));
|
||||
origin.x += renderwidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -244,32 +271,40 @@ void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool
|
|||
|
||||
{
|
||||
bmp = GetCachedBitmap(firstbitmap, selected);
|
||||
// Can't use dc.DrawBitmap here because we need to clip the bitmap
|
||||
wxMemoryDC bmpdc(bmp);
|
||||
dc.Blit(origin, wxSize(cache_bitmap_width-firstbitmapoffset, pixel_height),
|
||||
&bmpdc, wxPoint(firstbitmapoffset, 0));
|
||||
origin.x += cache_bitmap_width-firstbitmapoffset;
|
||||
}
|
||||
|
||||
for (int i = 1; i < lastbitmap; ++i)
|
||||
for (int i = firstbitmap+1; i < lastbitmap; ++i)
|
||||
{
|
||||
bmp = GetCachedBitmap(i, selected);
|
||||
wxMemoryDC bmpdc(bmp);
|
||||
dc.Blit(origin, wxSize(cache_bitmap_width, pixel_height), &bmpdc, wxPoint(0, 0));
|
||||
dc.DrawBitmap(bmp, origin);
|
||||
origin.x += cache_bitmap_width;
|
||||
}
|
||||
|
||||
{
|
||||
bmp = GetCachedBitmap(lastbitmap, selected);
|
||||
// We also need clipping here
|
||||
wxMemoryDC bmpdc(bmp);
|
||||
dc.Blit(origin, wxSize(lastbitmapoffset, pixel_height), &bmpdc, wxPoint(0, 0));
|
||||
dc.Blit(origin, wxSize(lastbitmapoffset+1, pixel_height), &bmpdc, wxPoint(0, 0));
|
||||
origin.x += lastbitmapoffset+1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now render blank audio from origin to end
|
||||
if (origin.x < lastx)
|
||||
{
|
||||
renderer->RenderBlank(dc, wxRect(origin.x-1, origin.y, lastx-origin.x+1, pixel_height), selected);
|
||||
}
|
||||
|
||||
if (selected)
|
||||
bitmaps_selected.Age(cache_maxsize / 8);
|
||||
bitmaps_selected.Age(cache_bitmap_maxsize);
|
||||
else
|
||||
bitmaps_normal.Age(cache_maxsize / 8);
|
||||
renderer->AgeCache(3 * cache_maxsize / 4);
|
||||
bitmaps_normal.Age(cache_bitmap_maxsize);
|
||||
renderer->AgeCache(cache_renderer_maxsize);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Niels Martin Hansen
|
||||
// Copyright (c) 2009-2010, Niels Martin Hansen
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -43,8 +43,6 @@
|
|||
#include <wx/gdicmn.h>
|
||||
#endif
|
||||
|
||||
#include "block_cache.h"
|
||||
|
||||
|
||||
// Some forward declarations for outside stuff
|
||||
class AudioProvider;
|
||||
|
@ -54,6 +52,11 @@ class AudioRendererBitmapProvider;
|
|||
class AudioRenderer;
|
||||
|
||||
|
||||
#ifndef AGI_BLOCK_CACHE_INCLUDED
|
||||
#error You much include "block_cache.h" before "audio_renderer.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/// @class AudioRendererBitmapCacheBitmapFactory
|
||||
/// @brief Produces wxBitmap objects for DataBlockCache storage for the audio renderer
|
||||
|
@ -111,8 +114,12 @@ class AudioRenderer {
|
|||
AudioRendererBitmapCache bitmaps_normal;
|
||||
/// Cached bitmaps for marked (selected) audio ranges
|
||||
AudioRendererBitmapCache bitmaps_selected;
|
||||
/// The maximum allowed size of the cache, in bytes
|
||||
size_t cache_maxsize;
|
||||
/// Number of blocks in the bitmap caches
|
||||
size_t cache_numblocks;
|
||||
/// The maximum allowed size of each bitmap cache, in bytes
|
||||
size_t cache_bitmap_maxsize;
|
||||
/// The maximum allowed size of the renderer's cache, in bytes
|
||||
size_t cache_renderer_maxsize;
|
||||
|
||||
/// Actual renderer for bitmaps
|
||||
AudioRendererBitmapProvider *renderer;
|
||||
|
@ -284,7 +291,7 @@ public:
|
|||
AudioRendererBitmapProvider() : provider(0), pixel_samples(0) { };
|
||||
|
||||
/// @brief Destructor
|
||||
~AudioRendererBitmapProvider() { }
|
||||
virtual ~AudioRendererBitmapProvider() { }
|
||||
|
||||
/// @brief Rendering function
|
||||
/// @param bmp Bitmap to render to
|
||||
|
@ -295,6 +302,15 @@ public:
|
|||
/// the width and height to render.
|
||||
virtual void Render(wxBitmap &bmp, int start, bool selected) = 0;
|
||||
|
||||
/// @brief Blank audio rendering function
|
||||
/// @param dc The device context to render to
|
||||
/// @param rect The rectangle to fill with the image of blank audio
|
||||
/// @param selected Whether to render as being selected or not
|
||||
///
|
||||
/// Deriving classes must implement this method. The rectangle has the height
|
||||
/// of the entire canvas the audio is being rendered in.
|
||||
virtual void RenderBlank(wxDC &dc, const wxRect &rect, bool selected) = 0;
|
||||
|
||||
/// @brief Change audio provider
|
||||
/// @param provider Audio provider to change to
|
||||
void SetProvider(AudioProvider *provider);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,4 @@
|
|||
// Copyright (c) 2005, 2006, Rodrigo Braz Monteiro
|
||||
// Copyright (c) 2006, 2007, Niels Martin Hansen
|
||||
// Copyright (c) 2009, Niels Martin Hansen
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -40,65 +39,102 @@
|
|||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
class AudioProvider;
|
||||
|
||||
// Specified and implemented in cpp file, interface is private to spectrum code
|
||||
class AudioSpectrumCacheManager;
|
||||
#ifdef WITH_FFTW
|
||||
#include <fftw3.h>
|
||||
#endif
|
||||
|
||||
|
||||
/// @class AudioSpectrum
|
||||
|
||||
// Specified and implemented in cpp file, to avoid pulling in too much
|
||||
// complex template code in this header.
|
||||
class AudioSpectrumCache;
|
||||
struct AudioSpectrumCacheBlockFactory;
|
||||
|
||||
|
||||
|
||||
/// @class AudioSpectrumRenderer
|
||||
/// @brief Render frequency-power spectrum graphs for audio data.
|
||||
///
|
||||
/// Renders frequency-power spectrum graphs of PCM audio data using a fast fourier transform
|
||||
/// to derive the data. The frequency-power data are cached to avoid re-computing them
|
||||
/// frequently, and the cache size is limited by a configuration setting.
|
||||
///
|
||||
/// The spectrum image is rendered to a 32 bit RGB bitmap. Power data is scaled linearly
|
||||
/// and not logarithmically, since the rendering is done with limited precision, but
|
||||
/// an amplification factor can be specified to see different ranges.
|
||||
class AudioSpectrum {
|
||||
private:
|
||||
/// Renders frequency-power spectrum graphs of PCM audio data using a derivation function
|
||||
/// such as the fast fourier transform.
|
||||
class AudioSpectrumRenderer : public AudioRendererBitmapProvider {
|
||||
friend struct AudioSpectrumCacheBlockFactory;
|
||||
|
||||
/// Internal cache management for the spectrum
|
||||
AudioSpectrumCacheManager *cache;
|
||||
AudioSpectrumCache *cache;
|
||||
|
||||
/// Colour table used for regular rendering
|
||||
unsigned char colours_normal[256*3];
|
||||
AudioColorScheme colors_normal;
|
||||
|
||||
/// Colour table used for rendering the audio selection
|
||||
unsigned char colours_selected[256*3];
|
||||
AudioColorScheme colors_selected;
|
||||
|
||||
/// The audio provider to use as source
|
||||
AudioProvider *provider;
|
||||
/// Binary logarithm of number of samples to use in deriving frequency-power data
|
||||
size_t derivation_size;
|
||||
|
||||
unsigned long line_length; ///< Number of frequency components per line (half of number of samples)
|
||||
unsigned long num_lines; ///< Number of lines needed for the audio
|
||||
unsigned int fft_overlaps; ///< Number of overlaps used in FFT
|
||||
float power_scale; ///< Amplification of displayed power
|
||||
int minband; ///< Smallest frequency band displayed
|
||||
int maxband; ///< Largest frequency band displayed
|
||||
/// Binary logarithm of number of samples between the start of derivations
|
||||
size_t derivation_dist;
|
||||
|
||||
/// @brief Reset in response to changing audio provider
|
||||
///
|
||||
/// Overrides the OnSetProvider event handler in the base class, to reset things
|
||||
/// when the audio provider is changed.
|
||||
void OnSetProvider();
|
||||
|
||||
/// @brief Recreates the cache
|
||||
///
|
||||
/// To be called when the number of blocks in cache might have changed,
|
||||
// eg. new audio provider or new resolution.
|
||||
void RecreateCache();
|
||||
|
||||
/// @brief Fill a block with frequency-power data for a time range
|
||||
/// @param block_index Index of the block to fill data for
|
||||
/// @param[out] block Address to write the data to
|
||||
void FillBlock(size_t block_index, float *block);
|
||||
|
||||
#ifdef WITH_FFTW
|
||||
/// FFTW plan data
|
||||
fftw_plan dft_plan;
|
||||
/// Pre-allocated input array for FFTW
|
||||
double *dft_input;
|
||||
/// Pre-allocated output array for FFTW
|
||||
fftw_complex *dft_output;
|
||||
#else
|
||||
/// Pre-allocated scratch area for doing FFT derivations
|
||||
float *fft_scratch;
|
||||
#endif
|
||||
|
||||
/// Pre-allocated scratch area for storing raw audio data
|
||||
int16_t *audio_scratch;
|
||||
|
||||
public:
|
||||
/// @brief Constructor
|
||||
/// @param _provider Audio provider to render spectrum data for.
|
||||
///
|
||||
/// Reads configuration data for the spectrum display and initialises itself following that.
|
||||
AudioSpectrum(AudioProvider *_provider);
|
||||
AudioSpectrumRenderer();
|
||||
|
||||
/// @brief Destructor
|
||||
~AudioSpectrum();
|
||||
virtual ~AudioSpectrumRenderer();
|
||||
|
||||
/// @brief Render a range of audio spectrum to a bitmap buffer.
|
||||
/// @param range_start First audio sample in the range to render.
|
||||
/// @param range_end Last audio sample in the range to render.
|
||||
/// @param selected Use the alternate colour palette?
|
||||
/// @param img Pointer to 32 bit RGBX data
|
||||
/// @param imgleft Offset from left edge of bitmap to render to, in pixels
|
||||
/// @param imgwidth Width of bitmap to render, in pixels
|
||||
/// @param imgpitch Offset from one scanline to the next in the bitmap, in bytes
|
||||
/// @param imgheight Number of lines in the bitmap
|
||||
void RenderRange(int64_t range_start, int64_t range_end, bool selected, unsigned char *img, int imgleft, int imgwidth, int imgpitch, int imgheight);
|
||||
/// @brief Render a range of audio spectrum
|
||||
/// @param bmp [in,out] Bitmap to render into, also carries lenght information
|
||||
/// @param start First column of pixel data in display to render
|
||||
/// @param selected Whether to use the alternate colour scheme
|
||||
void Render(wxBitmap &bmp, int start, bool selected);
|
||||
|
||||
/// @brief Set the amplification to use when rendering.
|
||||
/// @param _power_scale Amplification factor to use.
|
||||
void SetScaling(float _power_scale);
|
||||
/// @brief Render blank area
|
||||
void RenderBlank(wxDC &dc, const wxRect &rect, bool selected);
|
||||
|
||||
/// @brief Set the derivation resolution
|
||||
/// @param derivation_size Binary logarithm of number of samples to use in deriving frequency-power data
|
||||
/// @param derivation_dist Binary logarithm of number of samples between the start of derivations
|
||||
///
|
||||
/// The derivations done will each use 2^derivation_size audio samples and at a distance
|
||||
/// of 2^derivation_dist samples.
|
||||
///
|
||||
/// The derivation distance must be smaller than or equal to the size. If the distance
|
||||
/// is specified too large, it will be clamped to the size.
|
||||
void SetResolution(size_t derivation_size, size_t derivation_dist);
|
||||
|
||||
/// @brief Cleans up the cache
|
||||
/// @param max_size Maximum size in bytes for the cache
|
||||
void AgeCache(size_t max_size);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
// Copyright (c) 2010, Niels Martin Hansen
|
||||
// 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 Project http://www.aegisub.org/
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/// @file audio_renderer_waveform.cpp
|
||||
/// @ingroup audio_ui
|
||||
///
|
||||
/// Render a waveform display of PCM audio data
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <algorithm>
|
||||
|
||||
#include <wx/dcmemory.h>
|
||||
#endif
|
||||
|
||||
#include "block_cache.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "audio_colorscheme.h"
|
||||
#include "audio_renderer.h"
|
||||
#include "audio_renderer_waveform.h"
|
||||
#include "colorspace.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
|
||||
|
||||
AudioWaveformRenderer::AudioWaveformRenderer()
|
||||
: AudioRendererBitmapProvider()
|
||||
, colors_normal(6)
|
||||
, colors_selected(6)
|
||||
, audio_buffer(0)
|
||||
{
|
||||
colors_normal.InitIcyBlue_Normal();
|
||||
colors_selected.InitIcyBlue_Selected();
|
||||
}
|
||||
|
||||
|
||||
AudioWaveformRenderer::~AudioWaveformRenderer()
|
||||
{
|
||||
delete[] audio_buffer;
|
||||
}
|
||||
|
||||
|
||||
void AudioWaveformRenderer::Render(wxBitmap &bmp, int start, bool selected)
|
||||
{
|
||||
wxMemoryDC dc(bmp);
|
||||
wxRect rect(wxPoint(0, 0), bmp.GetSize());
|
||||
int midpoint = rect.height / 2;
|
||||
|
||||
AudioColorScheme *pal = selected ? &colors_selected : &colors_normal;
|
||||
|
||||
// Fill the background
|
||||
dc.SetBrush(wxBrush(pal->get(0.0f)));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(rect);
|
||||
|
||||
// Make sure we've got a buffer to fill with audio data
|
||||
if (!audio_buffer)
|
||||
{
|
||||
// Buffer for one pixel strip of audio
|
||||
size_t buffer_needed = pixel_samples * provider->GetChannels() * provider->GetSampleRate() * provider->GetBytesPerSample();
|
||||
audio_buffer = new char[buffer_needed];
|
||||
}
|
||||
|
||||
int64_t cur_sample = start * pixel_samples;
|
||||
|
||||
assert(provider->GetBytesPerSample() == 2);
|
||||
assert(provider->GetChannels() == 1);
|
||||
|
||||
wxPen pen_peaks(wxPen(pal->get(0.4f)));
|
||||
wxPen pen_avgs(wxPen(pal->get(0.7f)));
|
||||
|
||||
for (int x = 0; x < rect.width; ++x)
|
||||
{
|
||||
provider->GetAudio(audio_buffer, cur_sample, pixel_samples);
|
||||
cur_sample += pixel_samples;
|
||||
|
||||
int peak_min = 0, peak_max = 0;
|
||||
int64_t avg_min_accum = 0, avg_max_accum = 0;
|
||||
const int16_t *aud = (const int16_t *)audio_buffer;
|
||||
for (int si = pixel_samples; si > 0; --si, ++aud)
|
||||
{
|
||||
if (*aud > 0)
|
||||
{
|
||||
peak_max = std::max(peak_max, (int)*aud);
|
||||
avg_max_accum += *aud;
|
||||
}
|
||||
else
|
||||
{
|
||||
peak_min = std::min(peak_min, (int)*aud);
|
||||
avg_min_accum += *aud;
|
||||
}
|
||||
}
|
||||
|
||||
// midpoint is half height
|
||||
peak_min = std::max((int)(peak_min * amplitude_scale * midpoint) / 0x8000, -midpoint);
|
||||
peak_max = std::min((int)(peak_max * amplitude_scale * midpoint) / 0x8000, midpoint);
|
||||
int avg_min = std::max((int)(avg_min_accum * amplitude_scale * midpoint / pixel_samples) / 0x8000, -midpoint);
|
||||
int avg_max = std::min((int)(avg_max_accum * amplitude_scale * midpoint / pixel_samples) / 0x8000, midpoint);
|
||||
|
||||
dc.SetPen(pen_peaks);
|
||||
dc.DrawLine(x, midpoint - peak_max, x, midpoint - peak_min);
|
||||
dc.SetPen(pen_avgs);
|
||||
dc.DrawLine(x, midpoint - avg_max, x, midpoint - avg_min);
|
||||
}
|
||||
|
||||
// Horizontal zero-point line
|
||||
dc.SetPen(wxPen(pal->get(1.0f)));
|
||||
dc.DrawLine(0, midpoint, rect.width, midpoint);
|
||||
}
|
||||
|
||||
|
||||
void AudioWaveformRenderer::RenderBlank(wxDC &dc, const wxRect &rect, bool selected)
|
||||
{
|
||||
AudioColorScheme *pal = selected ? &colors_selected : &colors_normal;
|
||||
|
||||
wxColor line(pal->get(1.0));
|
||||
wxColor bg(pal->get(0.0));
|
||||
|
||||
// Draw the line as background above and below, and line in the middle, to avoid
|
||||
// overdraw flicker (the common theme in all of audio display direct drawing).
|
||||
int halfheight = rect.height / 2;
|
||||
|
||||
dc.SetBrush(wxBrush(bg));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(rect.x, rect.y, rect.width, halfheight);
|
||||
dc.DrawRectangle(rect.x, rect.y + halfheight + 1, rect.width, rect.height - halfheight - 1);
|
||||
|
||||
dc.SetPen(wxPen(line));
|
||||
dc.DrawLine(rect.x, rect.y+halfheight, rect.x+rect.width, rect.y+halfheight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioWaveformRenderer::OnSetProvider()
|
||||
{
|
||||
delete[] audio_buffer;
|
||||
audio_buffer = 0;
|
||||
}
|
||||
|
||||
|
||||
void AudioWaveformRenderer::OnSetSamplesPerPixel()
|
||||
{
|
||||
delete[] audio_buffer;
|
||||
audio_buffer = 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2010, Niels Martin Hansen
|
||||
// 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 Project http://www.aegisub.org/
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/// @file audio_renderer_waveform.h
|
||||
/// @see audio_renderer_waveform.cpp
|
||||
/// @ingroup audio_ui
|
||||
///
|
||||
/// Render a waveform display of PCM audio data
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
class AudioWaveformRenderer : public AudioRendererBitmapProvider {
|
||||
/// Colour table used for regular rendering
|
||||
AudioColorScheme colors_normal;
|
||||
|
||||
/// Colour table used for rendering the audio selection
|
||||
AudioColorScheme colors_selected;
|
||||
|
||||
/// Pre-allocated buffer for audio fetched from provider
|
||||
char *audio_buffer;
|
||||
|
||||
protected:
|
||||
virtual void OnSetProvider();
|
||||
virtual void OnSetSamplesPerPixel();
|
||||
|
||||
public:
|
||||
/// @brief Constructor
|
||||
AudioWaveformRenderer();
|
||||
|
||||
/// @brief Destructor
|
||||
virtual ~AudioWaveformRenderer();
|
||||
|
||||
/// @brief Render a range of audio waveform
|
||||
/// @param bmp [in,out] Bitmap to render into, also carries lenght information
|
||||
/// @param start First column of pixel data in display to render
|
||||
/// @param selected Whether to use the alternate colour scheme
|
||||
void Render(wxBitmap &bmp, int start, bool selected);
|
||||
|
||||
/// @brief Render blank area
|
||||
void RenderBlank(wxDC &dc, const wxRect &rect, bool selected);
|
||||
|
||||
/// @brief Cleans up the cache
|
||||
/// @param max_size Maximum size in bytes for the cache
|
||||
///
|
||||
/// Does nothing for waveform renderer, since it does not have a backend cache
|
||||
void AgeCache(size_t max_size) { }
|
||||
};
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) 2010, Niels Martin Hansen
|
||||
// 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 Project http://www.aegisub.org/
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/// @file audio_timing.h
|
||||
/// @brief Construction-functions for timing controller objects
|
||||
/// @ingroup audio_ui
|
||||
|
||||
|
||||
|
||||
class AssDialogue;
|
||||
class AudioController;
|
||||
|
||||
|
||||
/// @class AudioTimingController
|
||||
/// @brief Base class for objects controlling audio timing
|
||||
///
|
||||
/// There is just one active audio timing controller at a time per audio controller.
|
||||
/// The timing controller manages the timing mode and supplies markers that can be
|
||||
/// manupulated to the audio display, as well as the current selection.
|
||||
///
|
||||
/// The timing controller must then be sent the marker drag events as well as clicks
|
||||
/// in empty areas of the audio display.
|
||||
class AudioTimingController : public AudioMarkerProvider {
|
||||
public:
|
||||
/// @brief Get any warning message to show in the audio display
|
||||
/// @return The warning message to show, may be empty if there is none
|
||||
virtual wxString GetWarningMessage() const = 0;
|
||||
|
||||
/// @brief Get the sample range the user is most likely to want to see for the current state
|
||||
/// @return A sample range
|
||||
///
|
||||
/// This is used for "bring working area into view" operations.
|
||||
virtual AudioController::SampleRange GetIdealVisibleSampleRange() const = 0;
|
||||
|
||||
/// @brief Get the primary playback range
|
||||
/// @return A sample range
|
||||
///
|
||||
/// Get the sample range the user is most likely to want to play back currently.
|
||||
virtual AudioController::SampleRange GetPrimaryPlaybackRange() const = 0;
|
||||
|
||||
/// @brief Does this timing mode have labels on the audio display?
|
||||
/// @return True if this timing mode needs labels on the audio display.
|
||||
///
|
||||
/// This is labels for things such as karaoke syllables. When labels are required, some vertical
|
||||
/// space is set off for them in the drawing of the audio display.
|
||||
virtual bool HasLabels() const = 0;
|
||||
|
||||
/// @brief Go to next timing unit
|
||||
///
|
||||
/// Advances the timing controller cursor to the next timing unit, for example the next dialogue
|
||||
/// line or the next karaoke syllable.
|
||||
virtual void Next() = 0;
|
||||
|
||||
/// @brief Go to the previous timing unit
|
||||
///
|
||||
/// Rewinds the timing controller to the previous timing unit.
|
||||
virtual void Prev() = 0;
|
||||
|
||||
/// @brief Commit all changes
|
||||
///
|
||||
/// Stores all changes permanently.
|
||||
virtual void Commit() = 0;
|
||||
|
||||
/// @brief Revert all changes
|
||||
///
|
||||
/// Revert all changes to the last committed state.
|
||||
virtual void Revert() = 0;
|
||||
|
||||
/// @brief Determine if a position is close to a draggable marker
|
||||
/// @param sample The audio sample index to test
|
||||
/// @param sensitivity Distance in samples to consider markers as nearby
|
||||
/// @return True if a marker is close by the given sample, as defined by sensitivity
|
||||
///
|
||||
/// This is solely for hit-testing against draggable markers, for controlling the mouse cursor.
|
||||
virtual bool IsNearbyMarker(int64_t sample, int sensitivity) const = 0;
|
||||
|
||||
/// @brief The user pressed the left button down at an empty place in the audio
|
||||
/// @param sample The audio sample index the user clicked
|
||||
/// @param sensitivity Distance in samples to consider existing markers
|
||||
/// @return An audio marker or 0. If a marker is returned and the user starts dragging
|
||||
/// the mouse after pressing down the button, the returned marker is being dragged.
|
||||
virtual AudioMarker * OnLeftClick(int64_t sample, int sensitivity) = 0;
|
||||
|
||||
/// @brief The user pressed the right button down at an empty place in the audio
|
||||
/// @param sample The audio sample index the user clicked
|
||||
/// @param sensitivity Distance in samples to consider existing markers
|
||||
/// @return An audio marker or 0. If a marker is returned and the user starts dragging
|
||||
/// the mouse after pressing down the button, the returned marker is being dragged.
|
||||
virtual AudioMarker * OnRightClick(int64_t sample, int sensitivity) = 0;
|
||||
|
||||
/// @brief The user dragged a timing marker
|
||||
/// @param marker The marker being dragged
|
||||
/// @param new_position Sample position the marker was dragged to
|
||||
virtual void OnMarkerDrag(AudioMarker *marker, int64_t new_position) = 0;
|
||||
|
||||
/// @brief Destructor
|
||||
///
|
||||
/// Does nothing in the base class, only present for virtual destruction.
|
||||
virtual ~AudioTimingController() { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @brief Create a standard dialogue audio timing controller
|
||||
/// @param audio_controller The audio controller to own the timing controller
|
||||
/// @param selection_controller The selection controller to manage the set of lines being timed
|
||||
AudioTimingController *CreateDialogueTimingController(AudioController *audio_controller, SelectionController<AssDialogue> *selection_controller);
|
||||
|
|
@ -0,0 +1,478 @@
|
|||
// Copyright (c) 2010, Niels Martin Hansen
|
||||
// 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 Project http://www.aegisub.org/
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/// @file audio_timing_dialogue.cpp
|
||||
/// @brief Default timing mode for dialogue subtitles
|
||||
/// @ingroup audio_ui
|
||||
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <stdint.h>
|
||||
#include <wx/pen.h>
|
||||
#endif
|
||||
|
||||
#include "ass_time.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "selection_controller.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_timing.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
|
||||
/// @class AudioMarkerDialogueTiming
|
||||
/// @brief AudioMarker implementation for AudioTimingControllerDialogue
|
||||
///
|
||||
/// Audio marker intended to live in pairs of two, taking styles depending
|
||||
/// on which marker in the pair is to the left and which is to the right.
|
||||
class AudioMarkerDialogueTiming : public AudioMarker {
|
||||
/// The other marker for the dialogue line's pair
|
||||
AudioMarkerDialogueTiming *other;
|
||||
|
||||
/// Current sample position of this marker
|
||||
int64_t position;
|
||||
|
||||
/// Draw style for the marker
|
||||
wxPen style;
|
||||
/// Foot style for the marker
|
||||
FeetStyle feet;
|
||||
|
||||
public:
|
||||
// AudioMarker interface
|
||||
virtual int64_t GetPosition() const { return position; }
|
||||
virtual wxPen GetStyle() const { return style; }
|
||||
virtual FeetStyle GetFeet() const { return feet; }
|
||||
virtual bool CanSnap() const { return true; }
|
||||
|
||||
public:
|
||||
// Specific interface
|
||||
|
||||
/// @brief Move the marker to a new position
|
||||
/// @param new_position The position to move the marker to, in audio samples
|
||||
///
|
||||
/// If the marker moves to the opposite side of the ohter marker in the pair,
|
||||
/// the styles of the two markers will be changed to match the new start/end
|
||||
/// relationship of them.
|
||||
void SetPosition(int64_t new_position);
|
||||
|
||||
|
||||
/// @brief Constructor
|
||||
///
|
||||
/// Initialises the fields to default values.
|
||||
AudioMarkerDialogueTiming();
|
||||
|
||||
|
||||
/// @brief Initialise a pair of dialogue markers to be a pair
|
||||
/// @param marker1 The first marker in the pair to make
|
||||
/// @param marker2 The second marker in the pair to make
|
||||
///
|
||||
/// This checks that the markers aren't already part of a pair, and then sets their
|
||||
/// "other" field. Positions and styles aren't affected.
|
||||
static void InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @class AudioTimingControllerDialogue
|
||||
/// @brief Default timing mode for dialogue subtitles
|
||||
///
|
||||
/// Displays a start and end marker for an active subtitle line, and allows
|
||||
/// for those markers to be dragged. Dragging the start/end markers changes
|
||||
/// the audio selection.
|
||||
///
|
||||
/// When the audio rendering code is expanded to support it, inactive lines
|
||||
/// will also be shown as shaded lines that cannot be changed.
|
||||
///
|
||||
/// Another later expansion will be to affect the timing of multiple selected
|
||||
/// lines at the same time, if they e.g. have end1==start2.
|
||||
class AudioTimingControllerDialogue : public AudioTimingController, private SelectionListener<AssDialogue> {
|
||||
/// Start and end markers for the active line
|
||||
AudioMarkerDialogueTiming markers[2];
|
||||
|
||||
/// Has the timing been modified by the user?
|
||||
bool timing_modified;
|
||||
|
||||
/// Get the leftmost of the markers
|
||||
AudioMarkerDialogueTiming *GetLeftMarker();
|
||||
const AudioMarkerDialogueTiming *GetLeftMarker() const;
|
||||
/// Get the rightmost of the markers
|
||||
AudioMarkerDialogueTiming *GetRightMarker();
|
||||
const AudioMarkerDialogueTiming *GetRightMarker() const;
|
||||
|
||||
/// The owning audio controller
|
||||
AudioController *audio_controller;
|
||||
|
||||
/// Update the audio controller's selection
|
||||
void UpdateSelection();
|
||||
|
||||
/// Selection controller managing the set of lines currently being timed
|
||||
SelectionController<AssDialogue> *selection_controller;
|
||||
|
||||
private:
|
||||
// SubtitleSelectionListener interface
|
||||
virtual void OnActiveLineChanged(AssDialogue *new_line);
|
||||
virtual void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed);
|
||||
|
||||
public:
|
||||
// AudioMarkerProvider interface
|
||||
virtual void GetMarkers(const AudioController::SampleRange &range, AudioMarkerVector &out_markers) const;
|
||||
|
||||
// AudioTimingController interface
|
||||
virtual wxString GetWarningMessage() const;
|
||||
virtual AudioController::SampleRange GetIdealVisibleSampleRange() const;
|
||||
virtual AudioController::SampleRange GetPrimaryPlaybackRange() const;
|
||||
virtual bool HasLabels() const;
|
||||
virtual void Next();
|
||||
virtual void Prev();
|
||||
virtual void Commit();
|
||||
virtual void Revert();
|
||||
virtual bool IsNearbyMarker(int64_t sample, int sensitivity) const;
|
||||
virtual AudioMarker * OnLeftClick(int64_t sample, int sensitivity);
|
||||
virtual AudioMarker * OnRightClick(int64_t sample, int sensitivity);
|
||||
virtual void OnMarkerDrag(AudioMarker *marker, int64_t new_position);
|
||||
|
||||
public:
|
||||
// Specific interface
|
||||
|
||||
/// @brief Constructor
|
||||
AudioTimingControllerDialogue(AudioController *audio_controller, SelectionController<AssDialogue> *selection_controller);
|
||||
virtual ~AudioTimingControllerDialogue();
|
||||
};
|
||||
|
||||
|
||||
|
||||
AudioTimingController *CreateDialogueTimingController(AudioController *audio_controller, SelectionController<AssDialogue> *selection_controller)
|
||||
{
|
||||
return new AudioTimingControllerDialogue(audio_controller, selection_controller);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// AudioMarkerDialogueTiming
|
||||
|
||||
void AudioMarkerDialogueTiming::SetPosition(int64_t new_position)
|
||||
{
|
||||
position = new_position;
|
||||
|
||||
if (other)
|
||||
{
|
||||
/// @todo Make this depend on configuration
|
||||
wxPen style_left = wxPen(*wxRED, 2);
|
||||
wxPen style_right = wxPen(*wxBLUE, 2);
|
||||
if (position < other->position)
|
||||
{
|
||||
feet = Feet_Right;
|
||||
other->feet = Feet_Left;
|
||||
style = style_left;
|
||||
other->style = style_right;
|
||||
}
|
||||
else if (position > other->position)
|
||||
{
|
||||
feet = Feet_Left;
|
||||
other->feet = Feet_Right;
|
||||
style = style_right;
|
||||
other->style = style_left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AudioMarkerDialogueTiming::AudioMarkerDialogueTiming()
|
||||
: other(0)
|
||||
, position(0)
|
||||
, style(*wxTRANSPARENT_PEN)
|
||||
, feet(Feet_None)
|
||||
{
|
||||
// Nothing more to do
|
||||
}
|
||||
|
||||
|
||||
void AudioMarkerDialogueTiming::InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2)
|
||||
{
|
||||
assert(marker1->other == 0);
|
||||
assert(marker2->other == 0);
|
||||
|
||||
marker1->other = marker2;
|
||||
marker2->other = marker1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// AudioTimingControllerDialogue
|
||||
|
||||
AudioTimingControllerDialogue::AudioTimingControllerDialogue(AudioController *audio_controller, SelectionController<AssDialogue> *selection_controller)
|
||||
: timing_modified(false)
|
||||
, audio_controller(audio_controller)
|
||||
, selection_controller(selection_controller)
|
||||
{
|
||||
assert(audio_controller != 0);
|
||||
|
||||
AudioMarkerDialogueTiming::InitPair(&markers[0], &markers[1]);
|
||||
|
||||
selection_controller->AddSelectionListener(this);
|
||||
}
|
||||
|
||||
|
||||
AudioTimingControllerDialogue::~AudioTimingControllerDialogue()
|
||||
{
|
||||
selection_controller->RemoveSelectionListener(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker()
|
||||
{
|
||||
return markers[0].GetPosition() < markers[1].GetPosition() ? &markers[0] : &markers[1];
|
||||
}
|
||||
|
||||
const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker() const
|
||||
{
|
||||
return markers[0].GetPosition() < markers[1].GetPosition() ? &markers[0] : &markers[1];
|
||||
}
|
||||
|
||||
AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker()
|
||||
{
|
||||
return markers[0].GetPosition() < markers[1].GetPosition() ? &markers[1] : &markers[0];
|
||||
}
|
||||
|
||||
const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker() const
|
||||
{
|
||||
return markers[0].GetPosition() < markers[1].GetPosition() ? &markers[1] : &markers[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::GetMarkers(const AudioController::SampleRange &range, AudioMarkerVector &out_markers) const
|
||||
{
|
||||
if (range.contains(markers[0].GetPosition()))
|
||||
out_markers.push_back(&markers[0]);
|
||||
if (range.contains(markers[1].GetPosition()))
|
||||
out_markers.push_back(&markers[1]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::OnActiveLineChanged(AssDialogue *new_line)
|
||||
{
|
||||
/// @todo Need to change policy to default commit at some point
|
||||
Revert(); // revert will read and reset the selection/markers
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed)
|
||||
{
|
||||
/// @todo Create new passive markers, perhaps
|
||||
}
|
||||
|
||||
|
||||
|
||||
wxString AudioTimingControllerDialogue::GetWarningMessage() const
|
||||
{
|
||||
// We have no warning messages currently, maybe add the old "Modified" message back later?
|
||||
return wxString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
AudioController::SampleRange AudioTimingControllerDialogue::GetIdealVisibleSampleRange() const
|
||||
{
|
||||
return GetPrimaryPlaybackRange();
|
||||
}
|
||||
|
||||
|
||||
|
||||
AudioController::SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
|
||||
{
|
||||
return AudioController::SampleRange(
|
||||
GetLeftMarker()->GetPosition(),
|
||||
GetRightMarker()->GetPosition());
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool AudioTimingControllerDialogue::HasLabels() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::Next()
|
||||
{
|
||||
selection_controller->NextLine();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::Prev()
|
||||
{
|
||||
selection_controller->PrevLine();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::Commit()
|
||||
{
|
||||
/// @todo Make these depend on actual configuration
|
||||
const bool next_line_on_commit = true;
|
||||
const int default_duration = 5000; // milliseconds
|
||||
|
||||
int new_start_ms = audio_controller->MillisecondsFromSamples(GetLeftMarker()->GetPosition());
|
||||
int new_end_ms = audio_controller->MillisecondsFromSamples(GetRightMarker()->GetPosition());
|
||||
|
||||
// Store back new times
|
||||
if (timing_modified)
|
||||
{
|
||||
Selection sel;
|
||||
selection_controller->GetSelectedSet(sel);
|
||||
for (Selection::iterator sub = sel.begin(); sub != sel.end(); ++sub)
|
||||
{
|
||||
(*sub)->Start.SetMS(new_start_ms);
|
||||
(*sub)->End.SetMS(new_end_ms);
|
||||
}
|
||||
/// @todo Set an undo point
|
||||
timing_modified = false;
|
||||
}
|
||||
|
||||
// Assume that the next line might be zero-timed and should thus get a default timing
|
||||
if (next_line_on_commit)
|
||||
{
|
||||
markers[0].SetPosition(audio_controller->SamplesFromMilliseconds(new_end_ms));
|
||||
markers[1].SetPosition(audio_controller->SamplesFromMilliseconds(new_end_ms + default_duration));
|
||||
UpdateSelection();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::Revert()
|
||||
{
|
||||
AssDialogue *line = selection_controller->GetActiveLine();
|
||||
if (line)
|
||||
{
|
||||
AssTime new_start = line->Start;
|
||||
AssTime new_end = line->End;
|
||||
|
||||
if (new_start.GetMS() != 0 || new_end.GetMS() != 0)
|
||||
{
|
||||
markers[0].SetPosition(audio_controller->SamplesFromMilliseconds(new_start.GetMS()));
|
||||
markers[1].SetPosition(audio_controller->SamplesFromMilliseconds(new_end.GetMS()));
|
||||
timing_modified = false;
|
||||
UpdateSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool AudioTimingControllerDialogue::IsNearbyMarker(int64_t sample, int sensitivity) const
|
||||
{
|
||||
AudioController::SampleRange range(sample-sensitivity, sample+sensitivity);
|
||||
|
||||
return range.contains(markers[0].GetPosition()) || range.contains(markers[1].GetPosition());
|
||||
}
|
||||
|
||||
|
||||
AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sensitivity)
|
||||
{
|
||||
assert(sensitivity >= 0);
|
||||
|
||||
int64_t dist_l, dist_r;
|
||||
|
||||
AudioMarkerDialogueTiming *left = GetLeftMarker();
|
||||
AudioMarkerDialogueTiming *right = GetRightMarker();
|
||||
|
||||
dist_l = tabs(left->GetPosition() - sample);
|
||||
dist_r = tabs(right->GetPosition() - sample);
|
||||
|
||||
if (dist_l < dist_r && dist_l <= sensitivity)
|
||||
{
|
||||
// Clicked near the left marker:
|
||||
// Insta-move it and start dragging it
|
||||
left->SetPosition(sample);
|
||||
audio_controller->OnTimingControllerMarkerMoved(this, left);
|
||||
timing_modified = true;
|
||||
UpdateSelection();
|
||||
return left;
|
||||
}
|
||||
|
||||
if (dist_r < dist_l && dist_r <= sensitivity)
|
||||
{
|
||||
// Clicked near the right marker:
|
||||
// Only drag it. For insta-move, the user must right-click.
|
||||
return right;
|
||||
}
|
||||
|
||||
// Clicked far from either marker:
|
||||
// Insta-set the left marker to the clicked position and return the right as the dragged one,
|
||||
// such that if the user does start dragging, he will create a new selection from scratch
|
||||
left->SetPosition(sample);
|
||||
audio_controller->OnTimingControllerMarkerMoved(this, left);
|
||||
timing_modified = true;
|
||||
UpdateSelection();
|
||||
return right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AudioMarker * AudioTimingControllerDialogue::OnRightClick(int64_t sample, int sensitivity)
|
||||
{
|
||||
AudioMarkerDialogueTiming *right = GetRightMarker();
|
||||
|
||||
right->SetPosition(sample);
|
||||
audio_controller->OnTimingControllerMarkerMoved(this, right);
|
||||
timing_modified = true;
|
||||
UpdateSelection();
|
||||
return right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::OnMarkerDrag(AudioMarker *marker, int64_t new_position)
|
||||
{
|
||||
assert(marker == &markers[0] || marker == &markers[1]);
|
||||
|
||||
static_cast<AudioMarkerDialogueTiming*>(marker)->SetPosition(new_position);
|
||||
audio_controller->OnTimingControllerMarkerMoved(this, marker);
|
||||
timing_modified = true;
|
||||
|
||||
UpdateSelection();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::UpdateSelection()
|
||||
{
|
||||
audio_controller->OnTimingControllerUpdatedPrimaryRange(this);
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +50,8 @@
|
|||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_style.h"
|
||||
#include "audio_display.h"
|
||||
#include "audio_controller.h"
|
||||
#include "selection_controller.h"
|
||||
#include "compat.h"
|
||||
#include "frame_main.h"
|
||||
#include "main.h"
|
||||
|
@ -1251,9 +1252,12 @@ void BaseGrid::OnKeyPress(wxKeyEvent &event) {
|
|||
}
|
||||
|
||||
// Other events, send to audio display
|
||||
/// @todo Reinstate this, or make a better solution, when audio is getting stabler again
|
||||
/*
|
||||
if (context->audio->loaded) {
|
||||
context->audio->GetEventHandler()->ProcessEvent(event);
|
||||
}
|
||||
*/
|
||||
else event.Skip();
|
||||
}
|
||||
|
||||
|
|
|
@ -149,7 +149,6 @@ public:
|
|||
/// DOCME
|
||||
SubsEditBox *editBox;
|
||||
|
||||
|
||||
/// DOCME
|
||||
bool byFrame;
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include <vector>
|
||||
#endif
|
||||
|
||||
#define AGI_BLOCK_CACHE_INCLUDED 1
|
||||
|
||||
|
||||
/// @class BasicDataBlockFactory
|
||||
/// @brief Simple factory for allocating blocks for DataBlockCache
|
||||
|
@ -143,7 +145,7 @@ class DataBlockCache {
|
|||
for (size_t bi = 0; bi < mb.blocks.size(); ++bi)
|
||||
{
|
||||
BlockT *b = mb.blocks[bi];
|
||||
if (!b)
|
||||
if (b)
|
||||
factory.DisposeBlock(b);
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,12 @@
|
|||
//#define FINAL_RELEASE
|
||||
|
||||
|
||||
// Use FFTW instead of shipped FFT code
|
||||
// FFTW <http://fftw.org/> is a very fast library for computing the discrete fourier transform, but is a bit
|
||||
// tricky to get working on Windows, and has the additional problem of being GPL licensed.
|
||||
// Enable this option to use FFTW to get faster rendering of the audio spectrogram
|
||||
//#define WITH_FFTW
|
||||
//#pragma comment(lib,libfftw.lib)
|
||||
// Specify tags the update checker accepts
|
||||
// See <http://devel.aegisub.org/wiki/Technical/UpdateChecker> for details on tags.
|
||||
// Depending on who will be using your build, you may or may not want to have the
|
||||
|
|
|
@ -46,8 +46,11 @@
|
|||
#endif
|
||||
|
||||
|
||||
/// DOCME
|
||||
namespace Automation4 { class ScriptManager; class Script; class AutoloadScriptManager; };
|
||||
namespace Automation4 {
|
||||
class ScriptManager;
|
||||
class AutoloadScriptManager;
|
||||
class Script;
|
||||
};
|
||||
|
||||
|
||||
/// DOCME
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <wx/display.h> /// Must be included last.
|
||||
#endif
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "dialog_detached_video.h"
|
||||
#include "frame_main.h"
|
||||
#include "main.h"
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "ass_file.h"
|
||||
#include "ass_override.h"
|
||||
#include "ass_style.h"
|
||||
#include "audio_controller.h"
|
||||
#include "compat.h"
|
||||
#include "dialog_fonts_collector.h"
|
||||
#include "font_file_lister.h"
|
||||
|
@ -58,6 +59,7 @@
|
|||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "scintilla_text_ctrl.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_grid.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "help_button.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_grid.h"
|
||||
#include "utils.h"
|
||||
#include "validators.h"
|
||||
|
|
|
@ -47,9 +47,11 @@
|
|||
#include "compat.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "audio_controller.h"
|
||||
#include "dialog_search_replace.h"
|
||||
#include "frame_main.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_edit_ctrl.h"
|
||||
#include "subs_grid.h"
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "dialog_selection.h"
|
||||
#include "help_button.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_grid.h"
|
||||
#include "subs_edit_box.h"
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "audio_controller.h"
|
||||
#include "compat.h"
|
||||
#include "dialog_spellchecker.h"
|
||||
#include "frame_main.h"
|
||||
|
@ -51,6 +52,7 @@
|
|||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "include/aegisub/spellchecker.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_edit_ctrl.h"
|
||||
#include "subs_grid.h"
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "help_button.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_grid.h"
|
||||
#include "subs_preview.h"
|
||||
#include "include/aegisub/subtitles_provider.h"
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "help_button.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "standard_paths.h"
|
||||
#include "subs_grid.h"
|
||||
#include "utils.h"
|
||||
|
|
|
@ -46,8 +46,9 @@
|
|||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_style.h"
|
||||
#include "selection_controller.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_box.h"
|
||||
#include "audio_display.h"
|
||||
#include "dialog_styling_assistant.h"
|
||||
#include "frame_main.h"
|
||||
#include "help_button.h"
|
||||
|
@ -72,8 +73,8 @@ wxDialog (parent, -1, _("Styling assistant"), wxDefaultPosition, wxDefaultSize,
|
|||
|
||||
// Variables
|
||||
grid = _grid;
|
||||
audio = VideoContext::Get()->audio->box->audioDisplay;
|
||||
video = video->Get();
|
||||
audio = VideoContext::Get()->audio;
|
||||
video = VideoContext::Get();
|
||||
needCommit = false;
|
||||
linen = -1;
|
||||
|
||||
|
@ -269,7 +270,8 @@ void DialogStyling::OnActivate(wxActivateEvent &event) {
|
|||
}
|
||||
// Enable/disable play video/audio buttons
|
||||
PlayVideoButton->Enable(video->IsLoaded());
|
||||
PlayAudioButton->Enable(audio->loaded);
|
||||
/// @todo Reinstate this when the audio controller is made reachable from here
|
||||
//PlayAudioButton->Enable(audio->loaded);
|
||||
// Fix style list
|
||||
Styles->Set(grid->ass->GetStyles());
|
||||
// Fix line selection
|
||||
|
@ -377,7 +379,9 @@ void DialogStyling::OnPlayVideoButton(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void DialogStyling::OnPlayAudioButton(wxCommandEvent &event) {
|
||||
audio->Play(line->Start.GetMS(),line->End.GetMS());
|
||||
audio->PlayRange(AudioController::SampleRange(
|
||||
audio->SamplesFromMilliseconds(line->Start.GetMS()),
|
||||
audio->SamplesFromMilliseconds(line->End.GetMS())));
|
||||
TypeBox->SetFocus();
|
||||
}
|
||||
|
||||
|
@ -446,9 +450,12 @@ void StyleEditBox::OnKeyDown(wxKeyEvent &event) {
|
|||
|
||||
// Play audio
|
||||
if (Hotkeys.IsPressed(_T("Styling Assistant Play Audio"))) {
|
||||
/// @todo Reinstate this when the audio controller is made reachable from here
|
||||
/*
|
||||
if (diag->audio->loaded) {
|
||||
diag->audio->Play(diag->line->Start.GetMS(),diag->line->End.GetMS());
|
||||
}
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ class SubtitlesGrid;
|
|||
class DialogStyling;
|
||||
class AudioDisplay;
|
||||
class VideoContext;
|
||||
class AudioController;
|
||||
|
||||
|
||||
|
||||
|
@ -142,7 +143,7 @@ public:
|
|||
AssDialogue *line;
|
||||
|
||||
/// DOCME
|
||||
AudioDisplay *audio;
|
||||
AudioController *audio;
|
||||
|
||||
/// DOCME
|
||||
VideoContext *video;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "help_button.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_grid.h"
|
||||
#include "utils.h"
|
||||
#include "validators.h"
|
||||
|
|
|
@ -45,12 +45,13 @@
|
|||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "audio_display.h"
|
||||
#include "audio_controller.h"
|
||||
#include "dialog_translation.h"
|
||||
#include "frame_main.h"
|
||||
#include "help_button.h"
|
||||
#include "hotkeys.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_edit_ctrl.h"
|
||||
#include "subs_grid.h"
|
||||
|
@ -78,7 +79,7 @@ DialogTranslation::DialogTranslation (wxWindow *parent,AssFile *_subs,SubtitlesG
|
|||
subs = _subs;
|
||||
grid = _grid;
|
||||
audio = VideoContext::Get()->audio;
|
||||
video = video->Get();
|
||||
video = VideoContext::Get();
|
||||
|
||||
// Translation controls
|
||||
OrigText = new ScintillaTextCtrl(this,TEXT_ORIGINAL,_T(""),wxDefaultPosition,wxSize(320,80));
|
||||
|
@ -132,7 +133,8 @@ DialogTranslation::DialogTranslation (wxWindow *parent,AssFile *_subs,SubtitlesG
|
|||
wxButton *PlayVideoButton = new wxButton(this,BUTTON_TRANS_PLAY_VIDEO,_("Play Video"));
|
||||
wxButton *PlayAudioButton = new wxButton(this,BUTTON_TRANS_PLAY_AUDIO,_("Play Audio"));
|
||||
PlayVideoButton->Enable(video->IsLoaded());
|
||||
PlayAudioButton->Enable(audio->loaded);
|
||||
/// @todo Reinstate this when the audio context is made reachable from here
|
||||
//PlayAudioButton->Enable(audio->loaded);
|
||||
ToolSizer->Add(PlayAudioButton,0,wxALL,5);
|
||||
ToolSizer->Add(PlayVideoButton,0,wxLEFT | wxRIGHT | wxBOTTOM,5);
|
||||
|
||||
|
@ -406,10 +408,13 @@ void DialogTranslation::OnTransBoxKey(wxKeyEvent &event) {
|
|||
|
||||
// Play audio
|
||||
if (Hotkeys.IsPressed(_T("Translation Assistant Play Audio"))) {
|
||||
/// @todo Reinstate this when the audio controller is made reachable from here
|
||||
/*
|
||||
if (audio->loaded) {
|
||||
audio->Play(current->Start.GetMS(),current->End.GetMS());
|
||||
TransText->SetFocus();
|
||||
}
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -439,7 +444,9 @@ void DialogTranslation::OnPlayVideoButton(wxCommandEvent &event) {
|
|||
/// @param event
|
||||
///
|
||||
void DialogTranslation::OnPlayAudioButton(wxCommandEvent &event) {
|
||||
audio->Play(current->Start.GetMS(),current->End.GetMS());
|
||||
audio->PlayRange(AudioController::SampleRange(
|
||||
audio->SamplesFromMilliseconds(current->Start.GetMS()),
|
||||
audio->SamplesFromMilliseconds(current->End.GetMS())));
|
||||
TransText->SetFocus();
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ class AudioDisplay;
|
|||
class ScintillaTextCtrl;
|
||||
class SubtitlesGrid;
|
||||
class VideoContext;
|
||||
class AudioController;
|
||||
|
||||
|
||||
/// DOCME
|
||||
|
@ -56,7 +57,7 @@ class DialogTranslation : public wxDialog {
|
|||
private:
|
||||
|
||||
/// DOCME
|
||||
AudioDisplay *audio;
|
||||
AudioController *audio;
|
||||
|
||||
/// DOCME
|
||||
VideoContext *video;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <wx/filename.h>
|
||||
#endif
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "drop.h"
|
||||
#include "frame_main.h"
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "compat.h"
|
||||
#include "ffmpegsource_common.h"
|
||||
#include "frame_main.h"
|
||||
|
|
|
@ -46,8 +46,9 @@
|
|||
|
||||
|
||||
#include "ass_file.h"
|
||||
#include "selection_controller.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_box.h"
|
||||
#include "audio_display.h"
|
||||
#ifdef WITH_AUTOMATION
|
||||
#include "auto4_base.h"
|
||||
#endif
|
||||
|
@ -114,8 +115,7 @@ FrameMain::FrameMain (wxArrayString args)
|
|||
// Initialize flags
|
||||
HasSelection = false;
|
||||
menuCreated = false;
|
||||
blockAudioLoad = false;
|
||||
blockAudioLoad = false;
|
||||
blockVideoLoad = false;
|
||||
|
||||
StartupLog(_T("Install PNG handler"));
|
||||
// Create PNG handler
|
||||
|
@ -130,6 +130,10 @@ FrameMain::FrameMain (wxArrayString args)
|
|||
local_scripts = new Automation4::ScriptManager();
|
||||
#endif
|
||||
|
||||
// Contexts and controllers
|
||||
audioController = new AudioController;
|
||||
audioController->AddAudioListener(this);
|
||||
|
||||
// Create menu and tool bars
|
||||
StartupLog(_T("Apply saved Maximized state"));
|
||||
if (OPT_GET("App/Maximized")->GetBool()) Maximize(true);
|
||||
|
@ -206,7 +210,10 @@ FrameMain::FrameMain (wxArrayString args)
|
|||
|
||||
/// @brief FrameMain destructor
|
||||
FrameMain::~FrameMain () {
|
||||
VideoContext::Get()->SetVideo(_T(""));
|
||||
audioController->CloseAudio();
|
||||
DeInitContents();
|
||||
delete audioController;
|
||||
#ifdef WITH_AUTOMATION
|
||||
delete local_scripts;
|
||||
#endif
|
||||
|
@ -492,6 +499,9 @@ void FrameMain::InitMenu() {
|
|||
AppendBitmapMenuItem(audioMenu, Menu_Audio_Close, _("&Close Audio"), _("Closes the currently open audio file"), GETIMAGE(close_audio_menu_16));
|
||||
wxMenuItem *RecentAudParent = new wxMenuItem(audioMenu, Menu_File_Recent_Auds_Parent, _("Recent"), _T(""), wxITEM_NORMAL, RecentAuds);
|
||||
audioMenu->Append(RecentAudParent);
|
||||
audioMenu->AppendSeparator();
|
||||
audioMenu->Append(Menu_Audio_Spectrum, _("Spectrum display"), _("Display audio as a frequency-power spectrogrph"), wxITEM_RADIO);
|
||||
audioMenu->Append(Menu_Audio_Waveform, _("Waveform display"), _("Display audio as a linear amplitude graph"), wxITEM_RADIO);
|
||||
#ifdef _DEBUG
|
||||
audioMenu->AppendSeparator();
|
||||
audioMenu->Append(Menu_Audio_Open_Dummy, _T("Open 2h30 Blank Audio"), _T("Open a 150 minutes blank audio clip, for debugging"));
|
||||
|
@ -557,45 +567,52 @@ void FrameMain::InitContents() {
|
|||
StartupLog(_T("Create background panel"));
|
||||
Panel = new wxPanel(this,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL | wxCLIP_CHILDREN);
|
||||
|
||||
// Initialize sizers
|
||||
StartupLog(_T("Create main sizers"));
|
||||
MainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
TopSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
BottomSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Video area;
|
||||
StartupLog(_T("Create video box"));
|
||||
videoBox = new VideoBox(Panel, false, ZoomBox, ass);
|
||||
TopSizer->Add(videoBox,0,wxEXPAND,0);
|
||||
VideoContext::Get()->audio = audioController;
|
||||
wxBoxSizer *videoSizer = new wxBoxSizer(wxVERTICAL);
|
||||
videoSizer->Add(videoBox, 0, wxEXPAND);
|
||||
videoSizer->AddStretchSpacer(1);
|
||||
|
||||
// Subtitles area
|
||||
StartupLog(_T("Create subtitles grid"));
|
||||
SubsGrid = new SubtitlesGrid(this,Panel,-1,ass,wxDefaultPosition,wxSize(600,100),wxWANTS_CHARS | wxSUNKEN_BORDER,_T("Subs grid"));
|
||||
BottomSizer->Add(SubsGrid,1,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,0);
|
||||
videoBox->videoSlider->grid = SubsGrid;
|
||||
VideoContext::Get()->grid = SubsGrid;
|
||||
Search.grid = SubsGrid;
|
||||
|
||||
// Tools area
|
||||
StartupLog(_T("Create tool area splitter window"));
|
||||
audioSash = new wxSashWindow(Panel, Main_AudioSash, wxDefaultPosition, wxDefaultSize, wxSW_3D|wxCLIP_CHILDREN);
|
||||
wxBoxSizer *audioSashSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
audioSash->SetSashVisible(wxSASH_BOTTOM, true);
|
||||
|
||||
// Audio area
|
||||
StartupLog(_T("Create audio box"));
|
||||
audioBox = new AudioBox(Panel, SubsGrid);
|
||||
audioBox = new AudioBox(audioSash, audioController, SubsGrid);
|
||||
audioBox->frameMain = this;
|
||||
VideoContext::Get()->audio = audioBox->audioDisplay;
|
||||
audioSashSizer->Add(audioBox, 1, wxEXPAND);
|
||||
audioSash->SetSizer(audioSashSizer);
|
||||
audioBox->Fit();
|
||||
audioSash->SetMinimumSizeY(audioBox->GetSize().GetHeight());
|
||||
|
||||
// Top sizer
|
||||
// Editing area
|
||||
StartupLog(_T("Create subtitle editing box"));
|
||||
EditBox = new SubsEditBox(Panel,SubsGrid);
|
||||
StartupLog(_T("Arrange controls in sizers"));
|
||||
ToolSizer = new wxBoxSizer(wxVERTICAL);
|
||||
ToolSizer->Add(audioBox,0,wxEXPAND | wxBOTTOM,5);
|
||||
ToolSizer->Add(EditBox,1,wxEXPAND,5);
|
||||
TopSizer->Add(ToolSizer,1,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,5);
|
||||
|
||||
// Set sizers/hints
|
||||
StartupLog(_T("Arrange main sizers"));
|
||||
ToolsSizer = new wxBoxSizer(wxVERTICAL);
|
||||
ToolsSizer->Add(audioSash, 0, wxEXPAND);
|
||||
ToolsSizer->Add(EditBox, 1, wxEXPAND);
|
||||
TopSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
TopSizer->Add(videoSizer, 0, wxEXPAND, 0);
|
||||
TopSizer->Add(ToolsSizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
|
||||
MainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
MainSizer->Add(new wxStaticLine(Panel),0,wxEXPAND | wxALL,0);
|
||||
MainSizer->Add(TopSizer,0,wxEXPAND | wxALL,0);
|
||||
MainSizer->Add(BottomSizer,1,wxEXPAND | wxALL,0);
|
||||
MainSizer->Add(SubsGrid,1,wxEXPAND | wxALL,0);
|
||||
Panel->SetSizer(MainSizer);
|
||||
//MainSizer->SetSizeHints(Panel);
|
||||
//SetSizer(MainSizer);
|
||||
|
@ -828,7 +845,7 @@ void FrameMain::SetDisplayMode(int video, int audio) {
|
|||
else if (video) sv = VideoContext::Get()->IsLoaded() && !detachedVideo;
|
||||
|
||||
if (audio == -1) sa = showAudio;
|
||||
else if (audio) sa = audioBox->loaded;
|
||||
else if (audio) sa = audioController->IsAudioOpen();
|
||||
|
||||
// See if anything changed
|
||||
if (sv == showVideo && sa == showAudio) return;
|
||||
|
@ -843,7 +860,7 @@ void FrameMain::SetDisplayMode(int video, int audio) {
|
|||
|
||||
// Set display
|
||||
TopSizer->Show(videoBox, showVideo, true);
|
||||
ToolSizer->Show(audioBox,showAudio,true);
|
||||
ToolsSizer->Show(audioSash, showAudio, true);
|
||||
|
||||
// Update
|
||||
UpdateToolbar();
|
||||
|
@ -881,7 +898,7 @@ void FrameMain::UpdateTitle() {
|
|||
else newTitle << _("untitled");
|
||||
#endif
|
||||
|
||||
#ifdef __WXMAC__
|
||||
#if defined(__WXMAC__) && !defined(__LP64__)
|
||||
// On Mac, set the mark in the close button
|
||||
OSXSetModified(subsMod);
|
||||
#endif
|
||||
|
@ -914,19 +931,19 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
|
|||
// Get new state info
|
||||
ass->GetScriptInfo(_T("Video Position")).ToLong(&videoPos);
|
||||
ass->GetScriptInfo(_T("Video Zoom Percent")).ToDouble(&videoZoom);
|
||||
wxString curassVideo = DecodeRelativePath(ass->GetScriptInfo(_T("Video File")),ass->filename);
|
||||
wxString curassVFR = DecodeRelativePath(ass->GetScriptInfo(_T("VFR File")),ass->filename);
|
||||
wxString curassKeyframes = DecodeRelativePath(ass->GetScriptInfo(_T("Keyframes File")),ass->filename);
|
||||
wxString curassAudio = DecodeRelativePath(ass->GetScriptInfo(_T("Audio File")),ass->filename);
|
||||
wxString curSubsVideo = DecodeRelativePath(ass->GetScriptInfo(_T("Video File")),ass->filename);
|
||||
wxString curSubsVFR = DecodeRelativePath(ass->GetScriptInfo(_T("VFR File")),ass->filename);
|
||||
wxString curSubsKeyframes = DecodeRelativePath(ass->GetScriptInfo(_T("Keyframes File")),ass->filename);
|
||||
wxString curSubsAudio = DecodeRelativePath(ass->GetScriptInfo(_T("Audio URI")),ass->filename);
|
||||
wxString AutoScriptString = ass->GetScriptInfo(_T("Automation Scripts"));
|
||||
|
||||
// Check if there is anything to change
|
||||
int autoLoadMode = OPT_GET("App/Auto/Load Linked Files")->GetInt();
|
||||
bool hasToLoad = false;
|
||||
if (curassAudio != audioBox->audioName ||
|
||||
curassVFR != VideoContext::Get()->GetTimecodesName() ||
|
||||
curassVideo != VideoContext::Get()->videoName ||
|
||||
curassKeyframes != VideoContext::Get()->GetKeyFramesName()
|
||||
if (curSubsAudio !=audioController->GetAudioURL() ||
|
||||
curSubsVFR != VideoContext::Get()->GetTimecodesName() ||
|
||||
curSubsVideo != VideoContext::Get()->videoName ||
|
||||
curSubsKeyframes != VideoContext::Get()->GetKeyFramesName()
|
||||
#ifdef WITH_AUTOMATION
|
||||
|| !AutoScriptString.IsEmpty() || local_scripts->GetScripts().size() > 0
|
||||
#endif
|
||||
|
@ -946,8 +963,8 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
|
|||
|
||||
if (doLoad) {
|
||||
// Video
|
||||
if (curassVideo != VideoContext::Get()->videoName) {
|
||||
LoadVideo(curassVideo);
|
||||
if (curSubsVideo != VideoContext::Get()->videoName) {
|
||||
LoadVideo(curSubsVideo);
|
||||
if (VideoContext::Get()->IsLoaded()) {
|
||||
VideoContext::Get()->SetAspectRatio(videoAr,videoArValue);
|
||||
videoBox->videoDisplay->SetZoom(videoZoom);
|
||||
|
@ -955,13 +972,12 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
|
|||
}
|
||||
}
|
||||
|
||||
VideoContext::Get()->LoadTimecodes(curassVFR);
|
||||
VideoContext::Get()->LoadKeyframes(curassKeyframes);
|
||||
VideoContext::Get()->LoadTimecodes(curSubsVFR);
|
||||
VideoContext::Get()->LoadKeyframes(curSubsKeyframes);
|
||||
|
||||
// Audio
|
||||
if (curassAudio != audioBox->audioName) {
|
||||
if (curassAudio == _T("?video")) LoadAudio(_T(""),true);
|
||||
else LoadAudio(curassAudio);
|
||||
if (curSubsAudio != audioController->GetAudioURL()) {
|
||||
audioController->OpenAudio(curSubsAudio);
|
||||
}
|
||||
|
||||
// Automation scripts
|
||||
|
@ -1019,7 +1035,7 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
|
|||
}
|
||||
|
||||
// Store audio data
|
||||
ass->SetScriptInfo(_T("Audio File"),MakeRelativePath(audioBox->audioName,ass->filename));
|
||||
ass->SetScriptInfo(_T("Audio URI"),MakeRelativePath(audioController->GetAudioURL(),ass->filename));
|
||||
|
||||
// Store video data
|
||||
ass->SetScriptInfo(_T("Video File"),MakeRelativePath(VideoContext::Get()->videoName,ass->filename));
|
||||
|
@ -1124,31 +1140,6 @@ void FrameMain::LoadVideo(wxString file,bool autoload) {
|
|||
Thaw();
|
||||
}
|
||||
|
||||
/// @brief Loads audio
|
||||
/// @param filename
|
||||
/// @param FromVideo
|
||||
void FrameMain::LoadAudio(wxString filename,bool FromVideo) {
|
||||
if (blockAudioLoad) return;
|
||||
VideoContext::Get()->Stop();
|
||||
try {
|
||||
audioBox->SetFile(filename,FromVideo);
|
||||
SetDisplayMode(-1,1);
|
||||
}
|
||||
catch (const wchar_t *error) {
|
||||
wxString err(error);
|
||||
wxMessageBox(err, _T("Error opening audio file"), wxOK | wxICON_ERROR, this);
|
||||
}
|
||||
#ifdef WITH_AVISYNTH
|
||||
catch (AvisynthError err) {
|
||||
wxMessageBox (wxString(_T("AviSynth error: ")) + wxString(err.msg,wxConvUTF8), _T("Error loading audio"), wxOK | wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
catch (...) {
|
||||
wxMessageBox(_T("Unknown error"), _T("Error opening audio file"), wxOK | wxICON_ERROR, this);
|
||||
}
|
||||
}
|
||||
|
||||
void FrameMain::LoadVFR(wxString filename) {
|
||||
if (filename.empty()) {
|
||||
VideoContext::Get()->CloseTimecodes();
|
||||
|
@ -1208,7 +1199,7 @@ void FrameMain::SetAccelerators() {
|
|||
|
||||
// Medusa
|
||||
bool medusaPlay = OPT_GET("Audio/Medusa Timing Hotkeys")->GetBool();
|
||||
if (medusaPlay && audioBox->audioDisplay->loaded) {
|
||||
if (medusaPlay && audioController->IsAudioOpen()) {
|
||||
entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Play"),Medusa_Play));
|
||||
entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Stop"),Medusa_Stop));
|
||||
entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Play Before"),Medusa_Play_Before));
|
||||
|
@ -1294,7 +1285,6 @@ bool FrameMain::LoadList(wxArrayString list) {
|
|||
}
|
||||
|
||||
// Set blocking
|
||||
blockAudioLoad = (audio != _T(""));
|
||||
blockVideoLoad = (video != _T(""));
|
||||
|
||||
// Load files
|
||||
|
@ -1305,10 +1295,8 @@ bool FrameMain::LoadList(wxArrayString list) {
|
|||
blockVideoLoad = false;
|
||||
LoadVideo(video);
|
||||
}
|
||||
if (blockAudioLoad) {
|
||||
blockAudioLoad = false;
|
||||
LoadAudio(audio);
|
||||
}
|
||||
if (!audio.IsEmpty())
|
||||
audioController->OpenAudio(audio);
|
||||
|
||||
// Result
|
||||
return ((subs != _T("")) || (audio != _T("")) || (video != _T("")));
|
||||
|
|
|
@ -43,9 +43,14 @@
|
|||
#include <wx/menu.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/sashwin.h>
|
||||
#include <wx/timer.h>
|
||||
#endif
|
||||
|
||||
#ifndef AGI_AUDIO_CONTROLLER_INCLUDED
|
||||
#error You must include "audio_controller.h" before "frame_main.h"
|
||||
#endif
|
||||
|
||||
class AssFile;
|
||||
class VideoDisplay;
|
||||
class VideoSlider;
|
||||
|
@ -57,6 +62,7 @@ class VideoBox;
|
|||
class DialogDetachedVideo;
|
||||
class DialogStyling;
|
||||
class AegisubFileDropTarget;
|
||||
class AudioController;
|
||||
|
||||
namespace Automation4 { class FeatureMacro; class ScriptManager; }
|
||||
|
||||
|
@ -67,7 +73,7 @@ namespace Automation4 { class FeatureMacro; class ScriptManager; }
|
|||
/// @brief DOCME
|
||||
///
|
||||
/// DOCME
|
||||
class FrameMain: public wxFrame {
|
||||
class FrameMain: public wxFrame, private AudioControllerAudioEventListener {
|
||||
friend class AegisubFileDropTarget;
|
||||
friend class AegisubApp;
|
||||
friend class SubtitlesGrid;
|
||||
|
@ -93,9 +99,6 @@ private:
|
|||
wxTimer StatusClear;
|
||||
|
||||
|
||||
/// DOCME
|
||||
bool blockAudioLoad;
|
||||
|
||||
/// DOCME
|
||||
bool blockVideoLoad;
|
||||
|
||||
|
@ -184,6 +187,8 @@ private:
|
|||
|
||||
void OnVideoPlay(wxCommandEvent &event);
|
||||
|
||||
void OnAudioBoxResize(wxSashEvent &event);
|
||||
|
||||
void OnOpenRecentSubs (wxCommandEvent &event);
|
||||
void OnOpenRecentVideo (wxCommandEvent &event);
|
||||
void OnOpenRecentAudio (wxCommandEvent &event);
|
||||
|
@ -239,6 +244,7 @@ private:
|
|||
void OnOpenAudio (wxCommandEvent &event);
|
||||
void OnOpenAudioFromVideo (wxCommandEvent &event);
|
||||
void OnCloseAudio (wxCommandEvent &event);
|
||||
void OnAudioDisplayMode (wxCommandEvent &event);
|
||||
#ifdef _DEBUG
|
||||
void OnOpenDummyAudio(wxCommandEvent &event);
|
||||
void OnOpenDummyNoiseAudio(wxCommandEvent &event);
|
||||
|
@ -313,7 +319,6 @@ private:
|
|||
void OnMedusaPrev(wxCommandEvent &event);
|
||||
|
||||
void LoadVideo(wxString filename,bool autoload=false);
|
||||
void LoadAudio(wxString filename,bool FromVideo=false);
|
||||
void LoadVFR(wxString filename);
|
||||
void LoadSubtitles(wxString filename,wxString charset=_T(""));
|
||||
bool SaveSubtitles(bool saveas=false,bool withCharset=false);
|
||||
|
@ -322,21 +327,33 @@ private:
|
|||
void RebuildRecentList(wxString listName,wxMenu *menu,int startID);
|
||||
void SynchronizeProject(bool FromSubs=false);
|
||||
|
||||
|
||||
private:
|
||||
// AudioControllerAudioEventListener implementation
|
||||
virtual void OnAudioOpen(AudioProvider *provider);
|
||||
virtual void OnAudioClose();
|
||||
virtual void OnPlaybackPosition(int64_t sample_position);
|
||||
virtual void OnPlaybackStop();
|
||||
|
||||
|
||||
void OnSubtitlesFileChanged();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// DOCME
|
||||
/// The subtitle editing area
|
||||
SubtitlesGrid *SubsGrid;
|
||||
|
||||
/// DOCME
|
||||
/// The subtitle editing textbox
|
||||
SubsEditBox *EditBox;
|
||||
|
||||
/// DOCME
|
||||
/// Sash for resizing the audio area
|
||||
wxSashWindow *audioSash;
|
||||
|
||||
/// The audio area
|
||||
AudioBox *audioBox;
|
||||
|
||||
/// DOCME
|
||||
/// The video area
|
||||
VideoBox *videoBox;
|
||||
|
||||
/// DOCME
|
||||
|
@ -345,18 +362,19 @@ public:
|
|||
/// DOCME
|
||||
DialogStyling *stylingAssistant;
|
||||
|
||||
/// The audio controller for the open project
|
||||
AudioController *audioController;
|
||||
|
||||
/// DOCME
|
||||
|
||||
/// Arranges things from top to bottom in the window
|
||||
wxBoxSizer *MainSizer;
|
||||
|
||||
/// DOCME
|
||||
/// Arranges video box and tool box from left to right
|
||||
wxBoxSizer *TopSizer;
|
||||
|
||||
/// DOCME
|
||||
wxBoxSizer *BottomSizer;
|
||||
/// Arranges audio and editing areas top to bottom
|
||||
wxBoxSizer *ToolsSizer;
|
||||
|
||||
/// DOCME
|
||||
wxBoxSizer *ToolSizer;
|
||||
|
||||
FrameMain (wxArrayString args);
|
||||
~FrameMain ();
|
||||
|
@ -435,6 +453,9 @@ enum {
|
|||
Menu_Audio_Open_File,
|
||||
Menu_Audio_Open_From_Video,
|
||||
Menu_Audio_Close,
|
||||
|
||||
Menu_Audio_Spectrum,
|
||||
Menu_Audio_Waveform,
|
||||
#ifdef _DEBUG
|
||||
Menu_Audio_Open_Dummy,
|
||||
Menu_Audio_Open_Dummy_Noise,
|
||||
|
@ -503,6 +524,12 @@ enum {
|
|||
AutoSave_Timer,
|
||||
StatusClear_Timer,
|
||||
|
||||
|
||||
/// Id for the audio box resizing sash
|
||||
Main_AudioSash,
|
||||
|
||||
|
||||
/// DOCME
|
||||
Video_Next_Frame,
|
||||
Video_Prev_Frame,
|
||||
Video_Focus_Seek,
|
||||
|
|
|
@ -48,6 +48,8 @@
|
|||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "selection_controller.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_box.h"
|
||||
#include "audio_display.h"
|
||||
#ifdef WITH_AUTOMATION
|
||||
|
@ -84,6 +86,7 @@
|
|||
#include "main.h"
|
||||
#include "preferences.h"
|
||||
#include "standard_paths.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_edit_ctrl.h"
|
||||
#include "subs_grid.h"
|
||||
|
@ -104,6 +107,8 @@ BEGIN_EVENT_TABLE(FrameMain, wxFrame)
|
|||
|
||||
EVT_CLOSE(FrameMain::OnCloseWindow)
|
||||
|
||||
EVT_SASH_DRAGGED(Main_AudioSash, FrameMain::OnAudioBoxResize)
|
||||
|
||||
EVT_MENU_OPEN(FrameMain::OnMenuOpen)
|
||||
EVT_MENU_RANGE(Menu_File_Recent,Menu_File_Recent+99, FrameMain::OnOpenRecentSubs)
|
||||
EVT_MENU_RANGE(Menu_Video_Recent,Menu_Video_Recent+99, FrameMain::OnOpenRecentVideo)
|
||||
|
@ -155,6 +160,8 @@ BEGIN_EVENT_TABLE(FrameMain, wxFrame)
|
|||
EVT_MENU(Menu_Audio_Open_File, FrameMain::OnOpenAudio)
|
||||
EVT_MENU(Menu_Audio_Open_From_Video, FrameMain::OnOpenAudioFromVideo)
|
||||
EVT_MENU(Menu_Audio_Close, FrameMain::OnCloseAudio)
|
||||
EVT_MENU(Menu_Audio_Spectrum, FrameMain::OnAudioDisplayMode)
|
||||
EVT_MENU(Menu_Audio_Waveform, FrameMain::OnAudioDisplayMode)
|
||||
#ifdef _DEBUG
|
||||
EVT_MENU(Menu_Audio_Open_Dummy, FrameMain::OnOpenDummyAudio)
|
||||
EVT_MENU(Menu_Audio_Open_Dummy_Noise, FrameMain::OnOpenDummyNoiseAudio)
|
||||
|
@ -294,7 +301,7 @@ void FrameMain::OnMenuOpen (wxMenuEvent &event) {
|
|||
// View menu
|
||||
else if (curMenu == viewMenu) {
|
||||
// Flags
|
||||
bool aud = audioBox->audioDisplay->loaded;
|
||||
bool aud = audioController->IsAudioOpen();
|
||||
bool vid = VideoContext::Get()->IsLoaded() && !detachedVideo;
|
||||
|
||||
// Set states
|
||||
|
@ -365,12 +372,16 @@ void FrameMain::OnMenuOpen (wxMenuEvent &event) {
|
|||
|
||||
// Audio menu
|
||||
else if (curMenu == audioMenu) {
|
||||
bool state = audioBox->loaded;
|
||||
bool state = audioController->IsAudioOpen();
|
||||
bool vidstate = VideoContext::Get()->IsLoaded();
|
||||
|
||||
MenuBar->Enable(Menu_Audio_Open_From_Video,vidstate);
|
||||
MenuBar->Enable(Menu_Audio_Close,state);
|
||||
|
||||
bool spectrum_enabled = OPT_GET("Audio/Spectrum")->GetBool();
|
||||
MenuBar->Check(Menu_Audio_Spectrum, spectrum_enabled);
|
||||
MenuBar->Check(Menu_Audio_Waveform, !spectrum_enabled);
|
||||
|
||||
// Rebuild recent
|
||||
RebuildRecentList(_T("Audio"),RecentAuds,Menu_Audio_Recent);
|
||||
}
|
||||
|
@ -541,7 +552,7 @@ void FrameMain::OnOpenRecentKeyframes(wxCommandEvent &event) {
|
|||
/// @brief Open recent audio menu entry
|
||||
/// @param event
|
||||
void FrameMain::OnOpenRecentAudio(wxCommandEvent &event) {
|
||||
LoadAudio(lagi_wxString(config::mru->GetEntry("Audio", event.GetId()-Menu_Audio_Recent)));
|
||||
audioController->OpenAudio(lagi_wxString(config::mru->GetEntry("Audio", event.GetId()-Menu_Audio_Recent)));
|
||||
}
|
||||
|
||||
/// @brief Open new Window
|
||||
|
@ -650,31 +661,40 @@ void FrameMain::OnOpenAudio (wxCommandEvent&) {
|
|||
+ _("All files") + _T(" (*.*)|*.*");
|
||||
wxString filename = wxFileSelector(_("Open audio file"),path,_T(""),_T(""),str,wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (!filename.empty()) {
|
||||
LoadAudio(filename);
|
||||
audioController->OpenAudio(filename);
|
||||
OPT_SET("Path/Last/Audio")->SetString(STD_STR(filename));
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnOpenAudioFromVideo (wxCommandEvent&) {
|
||||
LoadAudio(_T(""),true);
|
||||
audioController->OpenAudio(_T("audio-video:cache"));
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnCloseAudio (wxCommandEvent&) {
|
||||
LoadAudio(_T(""));
|
||||
audioController->CloseAudio();
|
||||
}
|
||||
|
||||
|
||||
/// @brief Event handler for audio display renderer selection menu options
|
||||
/// @param event wxWidgets event object
|
||||
void FrameMain::OnAudioDisplayMode (wxCommandEvent &event) {
|
||||
OPT_SET("Audio/Spectrum")->SetBool(event.GetId() == Menu_Audio_Spectrum);
|
||||
/// @todo Remove this reload call when the audio display starts listening for option changes
|
||||
audioBox->audioDisplay->ReloadRenderingSettings();
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnOpenDummyAudio (wxCommandEvent&) {
|
||||
LoadAudio(_T("?dummy"));
|
||||
audioController->OpenAudio(_T("dummy-audio:silence?sr=44100&bd=16&ch=1&ln=396900000"));
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnOpenDummyNoiseAudio (wxCommandEvent&) {
|
||||
LoadAudio(_T("?noise"));
|
||||
audioController->OpenAudio(_T("dummy-audio:noise?sr=44100&bd=16&ch=1&ln=396900000"));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1245,7 +1265,7 @@ void FrameMain::OnSetARCustom (wxCommandEvent &) {
|
|||
void FrameMain::OnCloseWindow (wxCloseEvent &event) {
|
||||
// Stop audio and video
|
||||
VideoContext::Get()->Stop();
|
||||
audioBox->audioDisplay->Stop();
|
||||
audioController->Stop();
|
||||
|
||||
// Ask user if he wants to save first
|
||||
bool canVeto = event.CanVeto();
|
||||
|
@ -1465,6 +1485,7 @@ void FrameMain::OnChooseLanguage (wxCommandEvent &) {
|
|||
|
||||
/// @brief View standard
|
||||
void FrameMain::OnViewStandard (wxCommandEvent &) {
|
||||
if (!audioController->IsAudioOpen() || !VideoContext::Get()->IsLoaded()) return;
|
||||
SetDisplayMode(1,1);
|
||||
}
|
||||
|
||||
|
@ -1475,6 +1496,7 @@ void FrameMain::OnViewVideo (wxCommandEvent &) {
|
|||
|
||||
/// @brief View audio
|
||||
void FrameMain::OnViewAudio (wxCommandEvent &) {
|
||||
if (!audioController->IsAudioOpen()) return;
|
||||
SetDisplayMode(0,1);
|
||||
}
|
||||
|
||||
|
@ -1485,82 +1507,132 @@ void FrameMain::OnViewSubs (wxCommandEvent &) {
|
|||
|
||||
/// @brief Medusa shortcuts
|
||||
void FrameMain::OnMedusaPlay(wxCommandEvent &) {
|
||||
int start=0,end=0;
|
||||
audioBox->audioDisplay->GetTimesSelection(start,end);
|
||||
audioBox->audioDisplay->Play(start,end);
|
||||
audioController->PlayPrimaryRange();
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaStop(wxCommandEvent &) {
|
||||
// Playing, stop
|
||||
if (audioBox->audioDisplay->player->IsPlaying()) {
|
||||
audioBox->audioDisplay->Stop();
|
||||
audioBox->audioDisplay->Refresh();
|
||||
if (audioController->IsPlaying()) {
|
||||
audioController->Stop();
|
||||
}
|
||||
|
||||
// Otherwise, play the last 500 ms
|
||||
else {
|
||||
int start=0,end=0;
|
||||
audioBox->audioDisplay->GetTimesSelection(start,end);
|
||||
audioBox->audioDisplay->Play(end-500,end);
|
||||
AudioController::SampleRange sel(audioController->GetPrimaryPlaybackRange());
|
||||
audioController->PlayRange(AudioController::SampleRange(
|
||||
sel.end() - audioController->SamplesFromMilliseconds(500),
|
||||
sel.end()));;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaShiftStartForward(wxCommandEvent &) {
|
||||
audioBox->audioDisplay->curStartMS += 10;
|
||||
audioBox->audioDisplay->Update();
|
||||
audioBox->audioDisplay->wxWindow::Update();
|
||||
AudioController::SampleRange newsel(
|
||||
audioController->GetPrimaryPlaybackRange(),
|
||||
audioController->SamplesFromMilliseconds(10),
|
||||
0);
|
||||
/// @todo Make this use the timing controller instead
|
||||
//audioController->SetSelection(newsel);
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaShiftStartBack(wxCommandEvent &) {
|
||||
audioBox->audioDisplay->curStartMS -= 10;
|
||||
audioBox->audioDisplay->Update();
|
||||
audioBox->audioDisplay->wxWindow::Update();
|
||||
AudioController::SampleRange newsel(
|
||||
audioController->GetPrimaryPlaybackRange(),
|
||||
-audioController->SamplesFromMilliseconds(10),
|
||||
0);
|
||||
/// @todo Make this use the timing controller instead
|
||||
//audioController->SetSelection(newsel);
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaShiftEndForward(wxCommandEvent &) {
|
||||
audioBox->audioDisplay->curEndMS += 10;
|
||||
audioBox->audioDisplay->Update();
|
||||
audioBox->audioDisplay->wxWindow::Update();
|
||||
AudioController::SampleRange newsel(
|
||||
audioController->GetPrimaryPlaybackRange(),
|
||||
0,
|
||||
audioController->SamplesFromMilliseconds(10));
|
||||
/// @todo Make this use the timing controller instead
|
||||
//audioController->SetSelection(newsel);
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaShiftEndBack(wxCommandEvent &) {
|
||||
audioBox->audioDisplay->curEndMS -= 10;
|
||||
audioBox->audioDisplay->Update();
|
||||
audioBox->audioDisplay->wxWindow::Update();
|
||||
AudioController::SampleRange newsel(
|
||||
audioController->GetPrimaryPlaybackRange(),
|
||||
0,
|
||||
-audioController->SamplesFromMilliseconds(10));
|
||||
/// @todo Make this use the timing controller instead
|
||||
//audioController->SetSelection(newsel);
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaPlayBefore(wxCommandEvent &) {
|
||||
int start=0,end=0;
|
||||
audioBox->audioDisplay->GetTimesSelection(start,end);
|
||||
audioBox->audioDisplay->Play(start-500,start);
|
||||
AudioController::SampleRange sel(audioController->GetPrimaryPlaybackRange());
|
||||
audioController->PlayRange(AudioController::SampleRange(
|
||||
sel.begin() - audioController->SamplesFromMilliseconds(500),
|
||||
sel.begin()));;
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaPlayAfter(wxCommandEvent &) {
|
||||
int start=0,end=0;
|
||||
audioBox->audioDisplay->GetTimesSelection(start,end);
|
||||
audioBox->audioDisplay->Play(end,end+500);
|
||||
AudioController::SampleRange sel(audioController->GetPrimaryPlaybackRange());
|
||||
audioController->PlayRange(AudioController::SampleRange(
|
||||
sel.end(),
|
||||
sel.end() + audioController->SamplesFromMilliseconds(500)));;
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaNext(wxCommandEvent &) {
|
||||
audioBox->audioDisplay->Next(false);
|
||||
/// @todo Figure out how to handle this in the audio controller
|
||||
//audioBox->audioDisplay->Next(false);
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaPrev(wxCommandEvent &) {
|
||||
audioBox->audioDisplay->Prev(false);
|
||||
/// @todo Figure out how to handle this in the audio controller
|
||||
//audioBox->audioDisplay->Prev(false);
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
void FrameMain::OnMedusaEnter(wxCommandEvent &) {
|
||||
audioBox->audioDisplay->CommitChanges(true);
|
||||
/// @todo Figure out how to handle this in the audio controller
|
||||
//audioBox->audioDisplay->CommitChanges(true);
|
||||
}
|
||||
|
||||
void FrameMain::OnAudioBoxResize(wxSashEvent &event)
|
||||
{
|
||||
if (event.GetDragStatus() == wxSASH_STATUS_OUT_OF_RANGE)
|
||||
return;
|
||||
|
||||
wxRect rect = event.GetDragRect();
|
||||
|
||||
if (rect.GetHeight() < audioSash->GetMinimumSizeY())
|
||||
rect.SetHeight(audioSash->GetMinimumSizeY());
|
||||
|
||||
audioBox->SetMinSize(wxSize(-1, rect.GetHeight()));
|
||||
Panel->Layout();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void FrameMain::OnAudioOpen(AudioProvider *provider)
|
||||
{
|
||||
SetDisplayMode(-1, 1);
|
||||
}
|
||||
|
||||
void FrameMain::OnAudioClose()
|
||||
{
|
||||
SetDisplayMode(-1, 0);
|
||||
}
|
||||
|
||||
void FrameMain::OnPlaybackPosition(int64_t sample_position)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void FrameMain::OnPlaybackStop()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void FrameMain::OnSubtitlesFileChanged() {
|
||||
|
@ -1570,3 +1642,4 @@ void FrameMain::OnSubtitlesFileChanged() {
|
|||
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
|
|
|
@ -79,8 +79,8 @@ public:
|
|||
virtual ~AudioProvider();
|
||||
|
||||
virtual wxString GetFilename() const { return filename; };
|
||||
virtual void GetAudio(void *buf, int64_t start, int64_t count)=0;
|
||||
void GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume);
|
||||
virtual void GetAudio(void *buf, int64_t start, int64_t count) const = 0;
|
||||
void GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const;
|
||||
|
||||
int64_t GetNumSamples() const { return num_samples; }
|
||||
int GetSampleRate() const { return sample_rate; }
|
||||
|
@ -88,8 +88,6 @@ public:
|
|||
int GetChannels() const { return channels; }
|
||||
virtual bool AreSamplesNativeEndian() const = 0;
|
||||
|
||||
void GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int samples,float scale);
|
||||
|
||||
/// @brief Does this provider benefit from external caching?
|
||||
virtual bool NeedsCache() const { return false; }
|
||||
};
|
||||
|
|
|
@ -54,8 +54,9 @@
|
|||
#include "ass_export_filter.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_time.h"
|
||||
#include "selection_controller.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_box.h"
|
||||
#include "audio_display.h"
|
||||
#ifdef WITH_AUTOMATION
|
||||
#include "auto4_base.h"
|
||||
#endif
|
||||
|
@ -68,7 +69,6 @@
|
|||
#include "libresrc/libresrc.h"
|
||||
#include "plugin_manager.h"
|
||||
#include "standard_paths.h"
|
||||
#include "subs_grid.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "version.h"
|
||||
#include "video_context.h"
|
||||
|
@ -551,13 +551,14 @@ END_EVENT_TABLE()
|
|||
/// @param event
|
||||
///
|
||||
void AegisubApp::OnMouseWheel(wxMouseEvent &event) {
|
||||
if (event.WasProcessed()) return;
|
||||
wxPoint pt;
|
||||
wxWindow *target = wxFindWindowAtPointer(pt);
|
||||
if (frame && (target == frame->audioBox->audioDisplay || target == frame->SubsGrid)) {
|
||||
/*if (frame && (target == frame->audioBox->audioDisplay || target == frame->SubsGrid)) {
|
||||
if (target->IsShownOnScreen()) target->GetEventHandler()->ProcessEvent(event);
|
||||
else event.Skip();
|
||||
}
|
||||
else event.Skip();
|
||||
else event.Skip();*/
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -140,6 +140,4 @@
|
|||
#pragma comment(lib, "libass.lib")
|
||||
#endif
|
||||
|
||||
|
||||
#endif // VisualC
|
||||
|
||||
|
|
|
@ -58,12 +58,13 @@
|
|||
#include "ass_file.h"
|
||||
#include "ass_override.h"
|
||||
#include "ass_style.h"
|
||||
#include "audio_display.h"
|
||||
#include "audio_controller.h"
|
||||
#include "dialog_colorpicker.h"
|
||||
#include "dialog_search_replace.h"
|
||||
#include "frame_main.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_edit_ctrl.h"
|
||||
#include "subs_grid.h"
|
||||
|
|
|
@ -55,6 +55,7 @@ class wxSpinCtrl;
|
|||
class wxStyledTextCtrl;
|
||||
class wxStyledTextEvent;
|
||||
class wxTextCtrl;
|
||||
class AudioController;
|
||||
|
||||
/// DOCME
|
||||
/// @class SubsEditBox
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "compat.h"
|
||||
#include "main.h"
|
||||
#include "include/aegisub/spellchecker.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_edit_ctrl.h"
|
||||
#include "subs_grid.h"
|
||||
|
|
|
@ -51,8 +51,10 @@
|
|||
#include "ass_karaoke.h"
|
||||
#include "ass_override.h"
|
||||
#include "ass_style.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "selection_controller.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_box.h"
|
||||
#include "audio_display.h"
|
||||
#include "charset_conv.h"
|
||||
#include "dialog_paste_over.h"
|
||||
#include "frame_main.h"
|
||||
|
@ -204,7 +206,7 @@ void SubtitlesGrid::OnPopupMenu(bool alternate) {
|
|||
menu.AppendSeparator();
|
||||
|
||||
//Make audio clip
|
||||
state = parentFrame->audioBox->audioDisplay->loaded==true;
|
||||
state = parentFrame->audioController->IsAudioOpen()==true;
|
||||
menu.Append(MENU_AUDIOCLIP,_("Create audio clip"),_("Create an audio clip of the selected line"))->Enable(state);
|
||||
menu.AppendSeparator();
|
||||
|
||||
|
@ -646,8 +648,8 @@ void SubtitlesGrid::OnRecombine(wxCommandEvent &) {
|
|||
/// @brief Export audio clip of line
|
||||
void SubtitlesGrid::OnAudioClip(wxCommandEvent &) {
|
||||
int64_t num_samples,start=0,end=0,temp;
|
||||
AudioDisplay *audioDisplay = parentFrame->audioBox->audioDisplay;
|
||||
AudioProvider *provider = audioDisplay->provider;
|
||||
AudioController *audioController = parentFrame->audioController;
|
||||
const AudioProvider *provider = audioController->GetAudioProvider();
|
||||
AssDialogue *cur;
|
||||
wxArrayInt sel = GetSelection();
|
||||
|
||||
|
@ -656,9 +658,9 @@ void SubtitlesGrid::OnAudioClip(wxCommandEvent &) {
|
|||
for(unsigned int i=0;i!=sel.GetCount();i++) {
|
||||
cur = GetDialogue(sel[i]);
|
||||
|
||||
temp = audioDisplay->GetSampleAtMS(cur->Start.GetMS());
|
||||
temp = audioController->SamplesFromMilliseconds(cur->Start.GetMS());
|
||||
start = (i==0||temp<start)?temp:start;
|
||||
temp = audioDisplay->GetSampleAtMS(cur->End.GetMS());
|
||||
temp = audioController->SamplesFromMilliseconds(cur->End.GetMS());
|
||||
end = (i==0||temp>end)?temp:end;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "ass_file.h"
|
||||
#include "dialog_progress.h"
|
||||
#include "frame_main.h"
|
||||
|
|
|
@ -70,6 +70,10 @@ int AegiStringToFix(const wxString &str,size_t decimalPlaces,int start=0,int end
|
|||
wxIcon BitmapToIcon(wxBitmap bmp);
|
||||
void RestartAegisub();
|
||||
|
||||
|
||||
/// @brief Templated abs() function
|
||||
template <typename T> T tabs(T x) { return x < 0 ? -x : x; }
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a)<(b))?(a):(b)
|
||||
#endif
|
||||
|
|
|
@ -45,10 +45,12 @@
|
|||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "audio_controller.h"
|
||||
#include "frame_main.h"
|
||||
#include "help_button.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_grid.h"
|
||||
#include "toggle_bitmap.h"
|
||||
|
|
|
@ -53,11 +53,13 @@
|
|||
#include <GL/glu.h>
|
||||
#endif
|
||||
|
||||
#include "include/aegisub/audio_player.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_style.h"
|
||||
#include "ass_time.h"
|
||||
#include "audio_display.h"
|
||||
#include "audio_controller.h"
|
||||
#include "compat.h"
|
||||
#include "include/aegisub/audio_player.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
|
@ -67,6 +69,7 @@
|
|||
#include "main.h"
|
||||
#include "mkv_wrap.h"
|
||||
#include "standard_paths.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_grid.h"
|
||||
#include "threaded_frame_source.h"
|
||||
|
@ -119,10 +122,6 @@ VideoContext::VideoContext()
|
|||
}
|
||||
|
||||
VideoContext::~VideoContext () {
|
||||
if (audio && audio->temporary) {
|
||||
delete audio->provider;
|
||||
delete audio->player;
|
||||
}
|
||||
}
|
||||
|
||||
VideoContext *VideoContext::Get() {
|
||||
|
@ -135,15 +134,7 @@ void VideoContext::Reset() {
|
|||
|
||||
keyFrames.clear();
|
||||
videoFPS = agi::vfr::Framerate();
|
||||
|
||||
// Remove temporary audio provider
|
||||
if (audio && audio->temporary) {
|
||||
delete audio->provider;
|
||||
audio->provider = NULL;
|
||||
delete audio->player;
|
||||
audio->player = NULL;
|
||||
audio->temporary = false;
|
||||
}
|
||||
keyframesRevision++;
|
||||
|
||||
// Remove video data
|
||||
frame_n = 0;
|
||||
|
@ -316,8 +307,11 @@ void VideoContext::PlayNextFrame() {
|
|||
int thisFrame = frame_n;
|
||||
JumpToFrame(frame_n + 1);
|
||||
// Start playing audio
|
||||
if (playAudioOnStep->GetBool())
|
||||
audio->Play(TimeAtFrame(thisFrame),TimeAtFrame(thisFrame + 1));
|
||||
if (playAudioOnStep->GetBool()) {
|
||||
audio->PlayRange(AudioController::SampleRange(
|
||||
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame)),
|
||||
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame + 1))));
|
||||
}
|
||||
}
|
||||
|
||||
void VideoContext::PlayPrevFrame() {
|
||||
|
@ -327,8 +321,11 @@ void VideoContext::PlayPrevFrame() {
|
|||
int thisFrame = frame_n;
|
||||
JumpToFrame(frame_n -1);
|
||||
// Start playing audio
|
||||
if (playAudioOnStep->GetBool())
|
||||
audio->Play(TimeAtFrame(thisFrame - 1),TimeAtFrame(thisFrame));
|
||||
if (playAudioOnStep->GetBool()) {
|
||||
audio->PlayRange(AudioController::SampleRange(
|
||||
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame - 1)),
|
||||
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame))));
|
||||
}
|
||||
}
|
||||
|
||||
void VideoContext::Play() {
|
||||
|
@ -342,7 +339,7 @@ void VideoContext::Play() {
|
|||
endFrame = -1;
|
||||
|
||||
// Start playing audio
|
||||
audio->Play(TimeAtFrame(startFrame),-1);
|
||||
audio->PlayToEnd(audio->SamplesFromMilliseconds(TimeAtFrame(startFrame)));
|
||||
|
||||
//audio->Play will override this if we put it before, so put it after.
|
||||
isPlaying = true;
|
||||
|
@ -358,7 +355,9 @@ void VideoContext::PlayLine() {
|
|||
if (!curline) return;
|
||||
|
||||
// Start playing audio
|
||||
audio->Play(curline->Start.GetMS(),curline->End.GetMS());
|
||||
audio->PlayRange(AudioController::SampleRange(
|
||||
audio->SamplesFromMilliseconds(curline->Start.GetMS()),
|
||||
audio->SamplesFromMilliseconds(curline->End.GetMS())));
|
||||
|
||||
// Set variables
|
||||
isPlaying = true;
|
||||
|
@ -417,7 +416,9 @@ void VideoContext::OnPlayTimer(wxTimerEvent &event) {
|
|||
if (nextFrame == frame_n) return;
|
||||
|
||||
// Next frame is before or over 2 frames ahead, so force audio resync
|
||||
if (audio->player && keepAudioSync && (nextFrame < frame_n || nextFrame > frame_n + 2)) audio->player->SetCurrentPosition(audio->GetSampleAtMS(TimeAtFrame(nextFrame)));
|
||||
if (audio->IsPlaying() && keepAudioSync && (nextFrame < frame_n || nextFrame > frame_n + 2)) {
|
||||
audio->ResyncPlaybackPosition(audio->SamplesFromMilliseconds(TimeAtFrame(nextFrame)));
|
||||
}
|
||||
|
||||
// Jump to next frame
|
||||
playNextFrame = nextFrame;
|
||||
|
@ -425,13 +426,13 @@ void VideoContext::OnPlayTimer(wxTimerEvent &event) {
|
|||
JumpToFrame(nextFrame);
|
||||
|
||||
// Sync audio
|
||||
if (keepAudioSync && nextFrame % 10 == 0 && audio && audio->provider && audio->player) {
|
||||
int64_t audPos = audio->GetSampleAtMS(TimeAtFrame(nextFrame));
|
||||
int64_t curPos = audio->player->GetCurrentPosition();
|
||||
if (keepAudioSync && nextFrame % 10 == 0 && audio->IsPlaying()) {
|
||||
int64_t audPos = audio->SamplesFromMilliseconds(TimeAtFrame(nextFrame));
|
||||
int64_t curPos = audio->GetPlaybackPosition();
|
||||
int delta = int(audPos-curPos);
|
||||
if (delta < 0) delta = -delta;
|
||||
int maxDelta = audio->provider->GetSampleRate();
|
||||
if (delta > maxDelta) audio->player->SetCurrentPosition(audPos);
|
||||
int maxDelta = audio->SamplesFromMilliseconds(1000);
|
||||
if (delta > maxDelta) audio->ResyncPlaybackPosition(audPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -468,10 +469,12 @@ void VideoContext::LoadKeyframes(wxString filename) {
|
|||
catch (...) {
|
||||
wxMessageBox(_T("Unknown error"), _T("Error opening keyframes file"), wxOK | wxICON_ERROR, NULL);
|
||||
}
|
||||
keyframesRevision++;
|
||||
}
|
||||
|
||||
void VideoContext::SaveKeyframes(wxString filename) {
|
||||
KeyFrameFile::Save(filename, GetKeyFrames());
|
||||
keyframesRevision++;
|
||||
}
|
||||
|
||||
void VideoContext::CloseKeyframes() {
|
||||
|
@ -483,6 +486,7 @@ void VideoContext::CloseKeyframes() {
|
|||
keyFrames.clear();
|
||||
}
|
||||
KeyframesOpen(keyFrames);
|
||||
keyframesRevision++;
|
||||
}
|
||||
|
||||
void VideoContext::LoadTimecodes(wxString filename) {
|
||||
|
|
|
@ -62,6 +62,7 @@ class SubtitlesProviderErrorEvent;
|
|||
class ThreadedFrameSource;
|
||||
class VideoProvider;
|
||||
class VideoProviderErrorEvent;
|
||||
class AudioController;
|
||||
|
||||
namespace agi {
|
||||
class OptionValue;
|
||||
|
@ -101,6 +102,10 @@ private:
|
|||
/// DOCME
|
||||
wxString keyFramesFilename;
|
||||
|
||||
/// Revision counter for keyframes, when the set of keyframes is changed this number changes
|
||||
int keyframesRevision;
|
||||
|
||||
|
||||
/// DOCME
|
||||
wxMutex playMutex;
|
||||
|
||||
|
@ -161,8 +166,8 @@ public:
|
|||
/// File name of currently open video, if any
|
||||
wxString videoName;
|
||||
|
||||
/// DOCME
|
||||
AudioDisplay *audio;
|
||||
/// The audio controller for this video context
|
||||
AudioController *audio;
|
||||
|
||||
const agi::vfr::Framerate &VFR_Input;
|
||||
const agi::vfr::Framerate &VFR_Output;
|
||||
|
@ -265,6 +270,7 @@ public:
|
|||
void CloseKeyframes();
|
||||
bool OverKeyFramesLoaded() const { return !keyFramesFilename.empty(); }
|
||||
bool KeyFramesLoaded() const { return !keyFrames.empty(); }
|
||||
int GetKeyframesRevision() const { return keyframesRevision; }
|
||||
|
||||
wxString GetTimecodesName() const { return ovrTimecodeFile; }
|
||||
void LoadTimecodes(wxString filename);
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#endif
|
||||
|
||||
#include "video_display.h"
|
||||
#include "selection_controller.h"
|
||||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "ass_dialogue.h"
|
||||
#include "main.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_grid.h"
|
||||
#include "utils.h"
|
||||
#include "video_context.h"
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "ass_style.h"
|
||||
#include "ass_time.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_grid.h"
|
||||
#include "utils.h"
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "selection_controller.h"
|
||||
|
||||
#include "ass_dialogue.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
|
|
Loading…
Reference in New Issue