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:
Amar Takhar 2010-12-08 03:36:10 +00:00
parent 0e6d8631fd
commit c15777f844
84 changed files with 4483 additions and 3724 deletions

View file

@ -47,6 +47,7 @@
MinimalRebuild="true" MinimalRebuild="true"
UsePrecompiledHeader="2" UsePrecompiledHeader="2"
PrecompiledHeaderThrough="agi_pre.h" PrecompiledHeaderThrough="agi_pre.h"
DebugInformationFormat="3"
DisableSpecificWarnings="4267" DisableSpecificWarnings="4267"
ForcedIncludeFiles="agi_pre.h" ForcedIncludeFiles="agi_pre.h"
/> />
@ -113,6 +114,7 @@
MinimalRebuild="true" MinimalRebuild="true"
UsePrecompiledHeader="2" UsePrecompiledHeader="2"
PrecompiledHeaderThrough="agi_pre.h" PrecompiledHeaderThrough="agi_pre.h"
DebugInformationFormat="3"
DisableSpecificWarnings="4267" DisableSpecificWarnings="4267"
ForcedIncludeFiles="agi_pre.h" ForcedIncludeFiles="agi_pre.h"
/> />
@ -715,6 +717,14 @@
RelativePath="..\..\src\aegisublocale.h" RelativePath="..\..\src\aegisublocale.h"
> >
</File> </File>
<File
RelativePath="..\..\src\block_cache.h"
>
</File>
<File
RelativePath="..\..\src\block_cache.h"
>
</File>
<File <File
RelativePath="..\..\src\charset_conv.cpp" RelativePath="..\..\src\charset_conv.cpp"
> >
@ -1699,6 +1709,22 @@
RelativePath="..\..\src\audio_box.h" RelativePath="..\..\src\audio_box.h"
> >
</File> </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 <File
RelativePath="..\..\src\audio_display.cpp" RelativePath="..\..\src\audio_display.cpp"
> >
@ -1731,10 +1757,34 @@
RelativePath="..\..\src\audio_renderer_spectrum.h" RelativePath="..\..\src\audio_renderer_spectrum.h"
> >
</File> </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>
<Filter <Filter
Name="Video UI" 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 <File
RelativePath="..\..\src\video_box.cpp" RelativePath="..\..\src\video_box.cpp"
> >
@ -1799,6 +1849,18 @@
<Filter <Filter
Name="Controllers" Name="Controllers"
> >
<File
RelativePath="..\..\src\audio_controller.cpp"
>
</File>
<File
RelativePath="..\..\src\audio_controller.h"
>
</File>
<File
RelativePath="..\..\src\audio_timing.h"
>
</File>
<File <File
RelativePath="..\..\src\selection_controller.h" RelativePath="..\..\src\selection_controller.h"
> >

View file

@ -6,6 +6,7 @@
> >
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalOptions="/Zm120"
EnableIntrinsicFunctions="true" EnableIntrinsicFunctions="true"
RuntimeLibrary="2" RuntimeLibrary="2"
OpenMP="true" OpenMP="true"

View file

@ -6,6 +6,10 @@
OutputDirectory="$(SolutionDir)build/$(ProjectName)/$(PlatformName)/$(ConfigurationName)/" OutputDirectory="$(SolutionDir)build/$(ProjectName)/$(PlatformName)/$(ConfigurationName)/"
IntermediateDirectory="$(OutDir)" IntermediateDirectory="$(OutDir)"
> >
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="&quot;$(SolutionDir)include/&quot;;&quot;$(SolutionDir)include/$(PlatformName)/&quot;;&quot;$(SolutionDir)include/$(ConfigurationName)/&quot;;&quot;$(SolutionDir)include/$(PlatformName)/$(ConfigurationName)/&quot;"
/>
<Tool <Tool
Name="VCLibrarianTool" Name="VCLibrarianTool"
OutputFile="$(LibraryOutDir)/$(ProjectName).lib" OutputFile="$(LibraryOutDir)/$(ProjectName).lib"

View file

@ -86,7 +86,6 @@
/> />
<Tool <Tool
Name="VCPostBuildEventTool" Name="VCPostBuildEventTool"
CommandLine="cd &quot;$(ExecutableOutDir)&quot;&#x0D;&#x0A;&quot;$(ProjectDir)\..\..\tests\setup.bat&quot; &quot;$(ProjectDir)\..\..\tests&quot;&#x0D;&#x0A;"
/> />
</Configuration> </Configuration>
<Configuration <Configuration

View file

@ -157,6 +157,7 @@
#include <wx/dataobj.h> #include <wx/dataobj.h>
#include <wx/datetime.h> #include <wx/datetime.h>
#include <wx/dc.h> #include <wx/dc.h>
#include <wx/dcbuffer.h>
#include <wx/dcclient.h> #include <wx/dcclient.h>
#include <wx/dcmemory.h> #include <wx/dcmemory.h>
#include <wx/dcscreen.h> #include <wx/dcscreen.h>
@ -199,6 +200,7 @@
#include <wx/mstream.h> #include <wx/mstream.h>
#include <wx/notebook.h> #include <wx/notebook.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/power.h>
#include <wx/protocol/http.h> #include <wx/protocol/http.h>
#include <wx/radiobox.h> #include <wx/radiobox.h>
#include <wx/radiobut.h> #include <wx/radiobut.h>

View file

@ -39,6 +39,7 @@
#include "ass_export_filter.h" #include "ass_export_filter.h"
#include "ass_exporter.h" #include "ass_exporter.h"
#include "ass_file.h" #include "ass_file.h"
#include "audio_controller.h"
#include "frame_main.h" #include "frame_main.h"
/// @brief Constructor /// @brief Constructor

View file

@ -47,9 +47,13 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "include/aegisub/audio_player.h"
#include "selection_controller.h"
#include "audio_controller.h"
#include "audio_box.h" #include "audio_box.h"
#include "audio_display.h" #include "audio_display.h"
#include "audio_karaoke.h" #include "audio_karaoke.h"
#include "audio_timing.h"
#include "frame_main.h" #include "frame_main.h"
#include "hotkeys.h" #include "hotkeys.h"
#include "include/aegisub/audio_player.h" #include "include/aegisub/audio_player.h"
@ -58,60 +62,79 @@
#include "toggle_bitmap.h" #include "toggle_bitmap.h"
#include "tooltip_manager.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 /// @brief Constructor
/// @param parent /// @param parent
/// ///
AudioBox::AudioBox(wxWindow *parent, SubtitlesGrid *grid) : AudioBox::AudioBox(wxWindow *parent, AudioController *_controller, SelectionController<AssDialogue> *selection_controller)
wxPanel(parent,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_RAISED) : wxPanel(parent,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_RAISED)
, selection_controller(selection_controller)
, controller(_controller)
{ {
// Setup // Setup
loaded = false;
karaokeMode = false; karaokeMode = false;
// Sash and Display // Sash and Display
audioScroll = new wxScrollBar(this,Audio_Scrollbar); audioDisplay = new AudioDisplay(this, controller);
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);
// Zoom // Zoom
HorizontalZoom = new wxSlider(this,Audio_Horizontal_Zoom,50,0,100,wxDefaultPosition,wxSize(-1,20),wxSL_VERTICAL|wxSL_BOTH); HorizontalZoom = new wxSlider(this,Audio_Horizontal_Zoom,0,-50,30,wxDefaultPosition,wxSize(-1,20),wxSL_VERTICAL|wxSL_BOTH);
HorizontalZoom->PushEventHandler(new FocusEvent());
HorizontalZoom->SetToolTip(_("Horizontal zoom")); 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 = 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")); VerticalZoom->SetToolTip(_("Vertical zoom"));
VolumeBar = new wxSlider(this,Audio_Volume,50,0,100,wxDefaultPosition,wxSize(-1,20),wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE); 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")); VolumeBar->SetToolTip(_("Audio Volume"));
bool link = OPT_GET("Audio/Link")->GetBool(); bool link = OPT_GET("Audio/Link")->GetBool();
if (link) { if (link) {
VolumeBar->SetValue(VerticalZoom->GetValue()); VolumeBar->SetValue(VerticalZoom->GetValue());
VolumeBar->Enable(false); 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->SetToolTip(_("Link vertical zoom and volume sliders"));
VerticalLink->SetValue(link); 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 // VertVol sider
wxSizer *VertVol = new wxBoxSizer(wxHORIZONTAL); wxSizer *VertVol = new wxBoxSizer(wxHORIZONTAL);
VertVol->Add(VerticalZoom,1,wxEXPAND,0); VertVol->Add(VerticalZoom,1,wxEXPAND,0);
@ -122,99 +145,93 @@ wxPanel(parent,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_RAISE
// Top sizer // Top sizer
TopSizer = new wxBoxSizer(wxHORIZONTAL); TopSizer = new wxBoxSizer(wxHORIZONTAL);
TopSizer->Add(DisplaySizer,1,wxEXPAND,0); TopSizer->Add(audioDisplay,1,wxEXPAND,0);
TopSizer->Add(HorizontalZoom,0,wxEXPAND,0); TopSizer->Add(HorizontalZoom,0,wxEXPAND,0);
TopSizer->Add(VertVolArea,0,wxEXPAND,0); TopSizer->Add(VertVolArea,0,wxEXPAND,0);
// Buttons sizer // Buttons sizer
wxSizer *ButtonSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer *ButtonSizer = new wxBoxSizer(wxHORIZONTAL);
wxButton *temp; 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")); ToolTipManager::Bind(temp,_("Previous line or syllable (%KEY%/%KEY%)"),_T("Audio Prev Line"),_T("Audio Prev Line Alt"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); ToolTipManager::Bind(temp,_("Next line/syllable (%KEY%/%KEY%)"),_T("Audio Next Line"),_T("Audio Next Line Alt"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); ToolTipManager::Bind(temp,_("Play selection (%KEY%/%KEY%)"),_T("Audio Play"),_T("Audio Play Alt"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); ToolTipManager::Bind(temp,_("Play current line (%KEY%)"),_T("Audio Play Original Line"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); ToolTipManager::Bind(temp,_("Stop (%KEY%)"),_T("Audio Stop"));
ButtonSizer->Add(temp,0,wxRIGHT,10); 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")); ToolTipManager::Bind(temp,_("Play 500 ms before selection (%KEY%)"),_T("Audio Play 500ms Before"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); ToolTipManager::Bind(temp,_("Play 500 ms after selection (%KEY%)"),_T("Audio Play 500ms after"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); ToolTipManager::Bind(temp,_("Play first 500ms of selection (%KEY%)"),_T("Audio Play First 500ms"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); ToolTipManager::Bind(temp,_("Play last 500ms of selection (%KEY%)"),_T("Audio Play Last 500ms"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); ToolTipManager::Bind(temp,_("Play from selection start to end of file (%KEY%)"),_T("Audio Play To End"));
ButtonSizer->Add(temp,0,wxRIGHT,10); 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")); ToolTipManager::Bind(temp,_("Add lead in (%KEY%)"),_T("Audio Add Lead In"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); ToolTipManager::Bind(temp,_("Add lead out (%KEY%)"),_T("Audio Add Lead Out"));
ButtonSizer->Add(temp,0,wxRIGHT,10); 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")); ToolTipManager::Bind(temp,_("Commit changes (%KEY%/%KEY%)"),_T("Audio Commit (Stay)"),_T("Audio Commit Alt"));
ButtonSizer->Add(temp,0,wxRIGHT,0); 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")); temp->SetToolTip(_("Go to selection"));
ButtonSizer->Add(temp,0,wxRIGHT,10); 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->SetToolTip(_("Automatically commit all changes"));
AutoCommit->SetValue(OPT_GET("Audio/Auto/Commit")->GetBool()); AutoCommit->SetValue(OPT_GET("Audio/Auto/Commit")->GetBool());
ButtonSizer->Add(AutoCommit,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0); 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->SetToolTip(_("Auto goes to next line on commit"));
NextCommit->SetValue(OPT_GET("Audio/Next Line on Commit")->GetBool()); NextCommit->SetValue(OPT_GET("Audio/Next Line on Commit")->GetBool());
ButtonSizer->Add(NextCommit,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0); 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->SetToolTip(_("Auto scrolls audio display to selected line"));
AutoScroll->SetValue(OPT_GET("Audio/Auto/Scroll")->GetBool()); AutoScroll->SetValue(OPT_GET("Audio/Auto/Scroll")->GetBool());
ButtonSizer->Add(AutoScroll,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0); ButtonSizer->Add(AutoScroll,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,10);
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->AddStretchSpacer(1); 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 // Karaoke sizer
karaokeSizer = new wxBoxSizer(wxHORIZONTAL); 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); 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")); 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")); SplitButton->SetToolTip(_("Enter split-mode"));
JoinSplitSizer->Add(JoinButton,0,wxRIGHT|wxEXPAND,0); JoinSplitSizer->Add(JoinButton,0,wxRIGHT|wxEXPAND,0);
JoinSplitSizer->Add(SplitButton,0,wxRIGHT|wxEXPAND,0); JoinSplitSizer->Add(SplitButton,0,wxRIGHT|wxEXPAND,0);
CancelAcceptSizer = new wxBoxSizer(wxHORIZONTAL); 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")); 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")); AcceptButton->SetToolTip(_("Discard all splits and leave split-mode"));
CancelAcceptSizer->Add(CancelButton,0,wxRIGHT|wxEXPAND,0); CancelAcceptSizer->Add(CancelButton,0,wxRIGHT|wxEXPAND,0);
CancelAcceptSizer->Add(AcceptButton,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 = new AudioKaraoke(this);
audioKaraoke->box = this; audioKaraoke->box = this;
audioKaraoke->display = audioDisplay; audioKaraoke->display = audioDisplay;
audioDisplay->karaoke = audioKaraoke;
karaokeSizer->Add(audioKaraoke,1,wxEXPAND,0); karaokeSizer->Add(audioKaraoke,1,wxEXPAND,0);
SetKaraokeButtons(); // Decide which one to show or hide.
// Main sizer // Main sizer
MainSizer = new wxBoxSizer(wxVERTICAL); MainSizer = new wxBoxSizer(wxVERTICAL);
MainSizer->Add(TopSizer,0,wxEXPAND,0); MainSizer->Add(TopSizer,1,wxEXPAND|wxALL,3);
MainSizer->Add(ButtonSizer,0,wxEXPAND,0); MainSizer->Add(ButtonSizer,0,wxEXPAND|wxBOTTOM|wxLEFT|wxRIGHT,3);
MainSizer->Add(new wxStaticLine(this),0,wxEXPAND|wxTOP|wxBOTTOM,2); //MainSizer->Add(new wxStaticLine(this),0,wxEXPAND|wxTOP|wxBOTTOM,2);
MainSizer->Add(karaokeSizer,0,wxEXPAND,0); MainSizer->Add(karaokeSizer,0,wxEXPAND|wxBOTTOM|wxLEFT|wxRIGHT,3);
MainSizer->AddSpacer(3);
//MainSizer->SetSizeHints(this); //MainSizer->SetSizeHints(this);
SetSizer(MainSizer); SetSizer(MainSizer);
SetKaraokeButtons(); // Decide which one to show or hide.
timing_controller_dialogue = CreateDialogueTimingController(controller, selection_controller);
controller->SetTimingController(timing_controller_dialogue);
} }
/// @brief Destructor /// @brief Destructor
/// ///
AudioBox::~AudioBox() { AudioBox::~AudioBox()
audioScroll->PopEventHandler(true); {
HorizontalZoom->PopEventHandler(true);
VerticalZoom->PopEventHandler(true);
VolumeBar->PopEventHandler(true);
} }
/// @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 // Event table
BEGIN_EVENT_TABLE(AudioBox,wxPanel) BEGIN_EVENT_TABLE(AudioBox,wxPanel)
EVT_COMMAND_SCROLL(Audio_Scrollbar, AudioBox::OnScrollbar)
EVT_COMMAND_SCROLL(Audio_Horizontal_Zoom, AudioBox::OnHorizontalZoom) EVT_COMMAND_SCROLL(Audio_Horizontal_Zoom, AudioBox::OnHorizontalZoom)
EVT_COMMAND_SCROLL(Audio_Vertical_Zoom, AudioBox::OnVerticalZoom) EVT_COMMAND_SCROLL(Audio_Vertical_Zoom, AudioBox::OnVerticalZoom)
EVT_COMMAND_SCROLL(Audio_Volume, AudioBox::OnVolume) 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, AudioBox::OnPlaySelection)
EVT_BUTTON(Audio_Button_Play_Row, AudioBox::OnPlayDialogue) 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_Vertical_Link, AudioBox::OnVerticalLink)
EVT_TOGGLEBUTTON(Audio_Button_Karaoke, AudioBox::OnKaraoke) EVT_TOGGLEBUTTON(Audio_Button_Karaoke, AudioBox::OnKaraoke)
EVT_TOGGLEBUTTON(Audio_Check_AutoGoto,AudioBox::OnAutoGoto) 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_AutoCommit,AudioBox::OnAutoCommit)
EVT_TOGGLEBUTTON(Audio_Check_NextCommit,AudioBox::OnNextLineCommit) EVT_TOGGLEBUTTON(Audio_Check_NextCommit,AudioBox::OnNextLineCommit)
END_EVENT_TABLE() END_EVENT_TABLE()
/// @brief Scrollbar changed
/// @param event
///
void AudioBox::OnScrollbar(wxScrollEvent &event) {
audioDisplay->SetPosition(event.GetPosition()*12);
}
/// @brief Horizontal zoom bar changed /// @brief Horizontal zoom bar changed
/// @param event /// @param event
/// ///
void AudioBox::OnHorizontalZoom(wxScrollEvent &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 < 1) pos = 1;
if (pos > 100) pos = 100; if (pos > 100) pos = 100;
float value = pow(float(pos)/50.0f,3); float value = pow(float(pos)/50.0f,3);
audioDisplay->SetScale(value); audioDisplay->SetAmplitudeScale(value);
if (VerticalLink->GetValue()) { if (VerticalLink->GetValue()) {
audioDisplay->player->SetVolume(value); controller->SetVolume(value);
VolumeBar->SetValue(pos); VolumeBar->SetValue(pos);
} }
} }
@ -363,7 +341,7 @@ void AudioBox::OnVolume(wxScrollEvent &event) {
int pos = event.GetPosition(); int pos = event.GetPosition();
if (pos < 1) pos = 1; if (pos < 1) pos = 1;
if (pos > 100) pos = 100; 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; if (pos > 100) pos = 100;
float value = pow(float(pos)/50.0f,3); float value = pow(float(pos)/50.0f,3);
if (VerticalLink->GetValue()) { if (VerticalLink->GetValue()) {
audioDisplay->player->SetVolume(value); controller->SetVolume(value);
VolumeBar->SetValue(pos); VolumeBar->SetValue(pos);
} }
VolumeBar->Enable(!VerticalLink->GetValue()); 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 /// @brief Play selection
/// @param event /// @param event
/// ///
void AudioBox::OnPlaySelection(wxCommandEvent &event) { void AudioBox::OnPlaySelection(wxCommandEvent &event) {
int start=0,end=0; controller->PlayPrimaryRange();
audioDisplay->SetFocus();
audioDisplay->GetTimesSelection(start,end);
audioDisplay->Play(start,end);
} }
@ -453,11 +379,9 @@ void AudioBox::OnPlaySelection(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void AudioBox::OnPlayDialogue(wxCommandEvent &event) { void AudioBox::OnPlayDialogue(wxCommandEvent &event) {
int start=0,end=0; if (controller->GetTimingController())
audioDisplay->SetFocus(); controller->GetTimingController()->Revert();
audioDisplay->GetTimesDialogue(start,end); controller->PlayPrimaryRange();
audioDisplay->SetSelection(start, end);
audioDisplay->Play(start,end);
} }
@ -466,8 +390,7 @@ void AudioBox::OnPlayDialogue(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void AudioBox::OnStop(wxCommandEvent &event) { void AudioBox::OnStop(wxCommandEvent &event) {
audioDisplay->SetFocus(); controller->Stop();
audioDisplay->Stop();
} }
@ -476,9 +399,11 @@ void AudioBox::OnStop(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void AudioBox::OnNext(wxCommandEvent &event) { void AudioBox::OnNext(wxCommandEvent &event) {
audioDisplay->SetFocus(); //audioDisplay->SetFocus();
audioDisplay->Stop(); controller->Stop();
audioDisplay->Next(); if (controller->GetTimingController())
controller->GetTimingController()->Next();
controller->PlayPrimaryRange();
} }
@ -487,9 +412,11 @@ void AudioBox::OnNext(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void AudioBox::OnPrev(wxCommandEvent &event) { void AudioBox::OnPrev(wxCommandEvent &event) {
audioDisplay->SetFocus(); //audioDisplay->SetFocus();
audioDisplay->Stop(); controller->Stop();
audioDisplay->Prev(); if (controller->GetTimingController())
controller->GetTimingController()->Prev();
controller->PlayPrimaryRange();
} }
@ -498,10 +425,10 @@ void AudioBox::OnPrev(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void AudioBox::OnPlay500Before(wxCommandEvent &event) { void AudioBox::OnPlay500Before(wxCommandEvent &event) {
int start=0,end=0; AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
audioDisplay->SetFocus(); controller->PlayRange(AudioController::SampleRange(
audioDisplay->GetTimesSelection(start,end); times.begin() - controller->SamplesFromMilliseconds(500),
audioDisplay->Play(start-500,start); times.begin()));
} }
@ -510,10 +437,10 @@ void AudioBox::OnPlay500Before(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void AudioBox::OnPlay500After(wxCommandEvent &event) { void AudioBox::OnPlay500After(wxCommandEvent &event) {
int start=0,end=0; AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
audioDisplay->SetFocus(); controller->PlayRange(AudioController::SampleRange(
audioDisplay->GetTimesSelection(start,end); times.end(),
audioDisplay->Play(end,end+500); times.end() + controller->SamplesFromMilliseconds(500)));
} }
@ -522,12 +449,12 @@ void AudioBox::OnPlay500After(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void AudioBox::OnPlay500First(wxCommandEvent &event) { void AudioBox::OnPlay500First(wxCommandEvent &event) {
int start=0,end=0; AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
audioDisplay->SetFocus(); controller->PlayRange(AudioController::SampleRange(
audioDisplay->GetTimesSelection(start,end); times.begin(),
int endp = start+500; times.begin() + std::min(
if (endp > end) endp = end; controller->SamplesFromMilliseconds(500),
audioDisplay->Play(start,endp); times.length())));
} }
@ -536,12 +463,12 @@ void AudioBox::OnPlay500First(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void AudioBox::OnPlay500Last(wxCommandEvent &event) { void AudioBox::OnPlay500Last(wxCommandEvent &event) {
int start=0,end=0; AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
audioDisplay->SetFocus(); controller->PlayRange(AudioController::SampleRange(
audioDisplay->GetTimesSelection(start,end); times.end() - std::min(
int startp = end-500; controller->SamplesFromMilliseconds(500),
if (startp < start) startp = start; times.length()),
audioDisplay->Play(startp,end); times.end()));
} }
@ -550,10 +477,7 @@ void AudioBox::OnPlay500Last(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void AudioBox::OnPlayToEnd(wxCommandEvent &event) { void AudioBox::OnPlayToEnd(wxCommandEvent &event) {
int start=0,end=0; controller->PlayToEnd(controller->GetPrimaryPlaybackRange().begin());
audioDisplay->SetFocus();
audioDisplay->GetTimesSelection(start,end);
audioDisplay->Play(start,-1);
} }
@ -566,7 +490,8 @@ void AudioBox::OnCommit(wxCommandEvent &event) {
LOG_D("audio/box") << "OnCommit"; LOG_D("audio/box") << "OnCommit";
audioDisplay->SetFocus(); audioDisplay->SetFocus();
LOG_D("audio/box") << "has set focus, now committing changes"; 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"; LOG_D("audio/box") << "returning";
} }
@ -586,7 +511,8 @@ void AudioBox::OnKaraoke(wxCommandEvent &event) {
} }
karaokeMode = false; karaokeMode = false;
audioKaraoke->enabled = false; audioKaraoke->enabled = false;
audioDisplay->SetDialogue(); /// @todo Replace this with changing timing controller
//audioDisplay->SetDialogue();
audioKaraoke->Refresh(false); audioKaraoke->Refresh(false);
} }
@ -594,7 +520,8 @@ void AudioBox::OnKaraoke(wxCommandEvent &event) {
LOG_D("audio/box") << "karaoke disabled, enabling"; LOG_D("audio/box") << "karaoke disabled, enabling";
karaokeMode = true; karaokeMode = true;
audioKaraoke->enabled = true; audioKaraoke->enabled = true;
audioDisplay->SetDialogue(); /// @todo Replace this with changing timing controller
//audioDisplay->SetDialogue();
} }
SetKaraokeButtons(); SetKaraokeButtons();
@ -618,15 +545,9 @@ void AudioBox::SetKaraokeButtons() {
JoinButton->Enable(join); JoinButton->Enable(join);
SplitButton->Enable(split); SplitButton->Enable(split);
if (audioKaraoke->splitting) {
karaokeSizer->Show(CancelAcceptSizer); karaokeSizer->Show(CancelAcceptSizer, audioKaraoke->splitting);
karaokeSizer->Hide(JoinSplitSizer); karaokeSizer->Show(JoinSplitSizer, !audioKaraoke->splitting);
karaokeSizer->Layout();
} else {
karaokeSizer->Hide(CancelAcceptSizer);
karaokeSizer->Show(JoinSplitSizer);
karaokeSizer->Layout();
}
} }
/// @brief Join button in karaoke mode /// @brief Join button in karaoke mode
@ -671,7 +592,8 @@ void AudioBox::OnAccept(wxCommandEvent &event) {
/// ///
void AudioBox::OnGoto(wxCommandEvent &event) { void AudioBox::OnGoto(wxCommandEvent &event) {
audioDisplay->SetFocus(); 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 /// @todo Put global audio hotkeys toggling into the menu bar
/// @param event /*
///
void AudioBox::OnMedusaMode(wxCommandEvent &event) { void AudioBox::OnMedusaMode(wxCommandEvent &event) {
audioDisplay->SetFocus(); audioDisplay->SetFocus();
OPT_SET("Audio/Medusa Timing Hotkeys")->SetBool(MedusaMode->GetValue()); OPT_SET("Audio/Medusa Timing Hotkeys")->SetBool(MedusaMode->GetValue());
frameMain->SetAccelerators(); 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) { void AudioBox::OnLeadIn(wxCommandEvent &event) {
audioDisplay->SetFocus(); audioDisplay->SetFocus();
audioDisplay->AddLead(true,false); //audioDisplay->AddLead(true,false);
} }
@ -743,21 +653,6 @@ void AudioBox::OnLeadIn(wxCommandEvent &event) {
/// ///
void AudioBox::OnLeadOut(wxCommandEvent &event) { void AudioBox::OnLeadOut(wxCommandEvent &event) {
audioDisplay->SetFocus(); 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();
}

View file

@ -54,9 +54,14 @@
#include <wx/tglbtn.h> #include <wx/tglbtn.h>
#endif #endif
#ifndef AGI_AUDIO_CONTROLLER_INCLUDED
#error You must include "audio_controller.h" before "audio_box.h"
#endif
////////////// //////////////
// Prototypes // Prototypes
class AssDialogue;
class AudioDisplay; class AudioDisplay;
class AudioKaraoke; class AudioKaraoke;
class FrameMain; class FrameMain;
@ -66,18 +71,21 @@ class ToggleBitmap;
/// DOCME
/// @class AudioBox /// @class AudioBox
/// @brief DOCME /// @brief Panel with audio playback and timing controls, also containing an AudioDisplay
///
/// DOCME
class AudioBox : public wxPanel { 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 /// DOCME
wxSlider *HorizontalZoom; wxSlider *HorizontalZoom;
@ -100,9 +108,6 @@ private:
/// DOCME /// DOCME
wxSizer *DisplaySizer; wxSizer *DisplaySizer;
/// DOCME
wxSashWindow *Sash;
/// DOCME /// DOCME
ToggleBitmap *VerticalLink; ToggleBitmap *VerticalLink;
@ -133,21 +138,13 @@ private:
/// DOCME /// DOCME
ToggleBitmap *NextCommit; ToggleBitmap *NextCommit;
/// DOCME
ToggleBitmap *MedusaMode;
/// DOCME /// DOCME
ToggleBitmap *AutoCommit; ToggleBitmap *AutoCommit;
/// DOCME
ToggleBitmap *SpectrumMode;
void OnScrollbar(wxScrollEvent &event);
void OnHorizontalZoom(wxScrollEvent &event); void OnHorizontalZoom(wxScrollEvent &event);
void OnVerticalZoom(wxScrollEvent &event); void OnVerticalZoom(wxScrollEvent &event);
void OnVolume(wxScrollEvent &event); void OnVolume(wxScrollEvent &event);
void OnVerticalLink(wxCommandEvent &event); void OnVerticalLink(wxCommandEvent &event);
void OnSash(wxSashEvent &event);
void OnPlaySelection(wxCommandEvent &event); void OnPlaySelection(wxCommandEvent &event);
void OnPlayDialogue(wxCommandEvent &event); void OnPlayDialogue(wxCommandEvent &event);
@ -171,14 +168,13 @@ private:
void OnAutoGoto(wxCommandEvent &event); void OnAutoGoto(wxCommandEvent &event);
void OnAutoCommit(wxCommandEvent &event); void OnAutoCommit(wxCommandEvent &event);
void OnMedusaMode(wxCommandEvent &event);
void OnSpectrumMode(wxCommandEvent &event);
void OnNextLineCommit(wxCommandEvent &event); void OnNextLineCommit(wxCommandEvent &event);
public: public:
/// DOCME /// The controller controlling this audio box
AudioDisplay *audioDisplay; AudioController *controller;
/// DOCME /// DOCME
AudioKaraoke *audioKaraoke; AudioKaraoke *audioKaraoke;
@ -189,124 +185,15 @@ public:
/// DOCME /// DOCME
FrameMain *frameMain; FrameMain *frameMain;
/// DOCME
wxString audioName;
/// DOCME
bool loaded;
/// DOCME /// DOCME
bool karaokeMode; bool karaokeMode;
AudioBox(wxWindow *parent, SubtitlesGrid *grid); AudioBox(wxWindow *parent, AudioController *controller, SelectionController<AssDialogue> *selection_controller);
~AudioBox(); ~AudioBox();
void SetFile(wxString file,bool FromVideo);
void SetKaraokeButtons(); void SetKaraokeButtons();
DECLARE_EVENT_TABLE() 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
};

View file

@ -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;
}
}

View file

@ -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]);
}
};

View file

@ -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;
}

View file

@ -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

View file

@ -1,4 +1,5 @@
// Copyright (c) 2005, Rodrigo Braz Monteiro // Copyright (c) 2005, Rodrigo Braz Monteiro
// Copyright (c) 2009-2010, Niels Martin Hansen
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@ -45,247 +46,285 @@
#include <wx/window.h> #include <wx/window.h>
#endif #endif
#include <libaegisub/signals.h>
#include "audio_renderer_spectrum.h"
#include "selection_controller.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 AudioKaraoke;
class AudioPlayer;
class AudioProvider; class AudioProvider;
class AssDialogue; class AudioPlayer;
class FrameMain;
class SubtitlesGrid; class SubtitlesGrid;
class VideoProvider; class VideoProvider;
/// 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
///
/// 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 /// @class AudioDisplay
/// @brief DOCME /// @brief Primary view/UI for interaction with audio timing
/// ///
/// DOCME /// The audio display is the common view that allows the user to interact with the active
class AudioDisplay: public wxWindow, private SelectionListener<AssDialogue> { /// timing controller. The audio display also renders audio according to the audio controller
friend class FrameMain; /// and the timing controller, using an audio renderer instance.
class AudioDisplay: public wxWindow, private AudioControllerAudioEventListener, private AudioControllerTimingEventListener {
private: private:
/// DOCME /// The audio renderer manager
SubtitlesGrid *grid; AudioRenderer *audio_renderer;
/// DOCME /// The renderer for audio spectrums
int line_n; AudioSpectrumRenderer *audio_spectrum_renderer;
/// DOCME /// The renderer for audio waveforms
AssDialogue *dialogue; AudioWaveformRenderer *audio_waveform_renderer;
/// DOCME /// Our current audio provider
AudioSpectrum *spectrumRenderer; AudioProvider *provider;
/// DOCME /// The controller managing us
wxBitmap *origImage; AudioController *controller;
/// DOCME
wxBitmap *spectrumDisplay;
/// DOCME /// Scrollbar helper object
wxBitmap *spectrumDisplaySelected; AudioDisplayScrollbar *scrollbar;
/// DOCME /// Timeline helper object
int64_t PositionSample; AudioDisplayTimeline *timeline;
/// DOCME
float scale;
/// DOCME /// Current object on display being dragged, if any
int samples; AudioDisplayInteractionObject *dragged_object;
/// Change the dragged object and update mouse capture
void SetDraggedObject(AudioDisplayInteractionObject *new_obj);
/// DOCME
int64_t Position;
/// DOCME /// Leftmost pixel in the vitual audio image being displayed
int samplesPercent; int scroll_left;
/// DOCME /// Total width of the audio in pixels
int oldCurPos; int pixel_audio_width;
/// DOCME /// Horizontal zoom measured in audio samples per pixel
bool hasFocus; int pixel_samples;
/// DOCME /// Amplitude scaling ("vertical zoom") as a factor, 1.0 is neutral
bool blockUpdate; float scale_amplitude;
/// DOCME /// Top of the main audio area in pixels
bool dontReadTimes; int audio_top;
/// DOCME /// Height of main audio area in pixels
bool playingToEnd; int audio_height;
/// DOCME
bool needImageUpdate;
/// DOCME /// Zoom level given as a number, see SetZoomLevel for details
bool needImageUpdateWeak; int zoom_level;
// Mouse wheel zoom accumulator
int mouse_zoom_accum;
/// DOCME
bool hasSel;
/// DOCME /// Absolute pixel position of the tracking cursor (mouse or playback)
bool hasKaraoke; 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 /// Previous audio selection for optimising redraw when selection changes
bool holding; 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); void OnPaint(wxPaintEvent &event);
/// wxWidgets mouse input event
void OnMouseEvent(wxMouseEvent &event); void OnMouseEvent(wxMouseEvent &event);
/// wxWidgets control size changed event
void OnSize(wxSizeEvent &event); void OnSize(wxSizeEvent &event);
void OnUpdateTimer(wxTimerEvent &event); /// wxWidgets input focus changed event
void OnKeyDown(wxKeyEvent &event); void OnFocus(wxFocusEvent &event);
void OnGetFocus(wxFocusEvent &event);
void OnLoseFocus(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); private:
void DoUpdateImage(); // 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: public:
/// DOCME AudioDisplay(wxWindow *parent, AudioController *controller);
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(); ~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); /// @brief Scroll the audio display
void AddLead(bool in,bool out); /// @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); /// @brief Scroll the audio display
void SetFromVideo(); /// @param pixel_position Absolute pixel to put at left edge of the audio display
void Reload(); ///
/// 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); /// @brief Scroll the audio display
void Stop(); /// @param pixel_position Absolute pixel to put in center of the audio display
void ScrollPixelToCenter(int pixel_position);
int64_t GetSampleAtX(int x); /// @brief Scroll the audio display
int GetXAtSample(int64_t n); /// @param sample_position Audio sample to put at left edge of the audio display
int GetMSAtX(int64_t x); void ScrollSampleToLeft(int64_t sample_position);
int GetXAtMS(int64_t ms);
int GetMSAtSample(int64_t x); /// @brief Scroll the audio display
int64_t GetSampleAtMS(int64_t ms); /// @param sample_position Audio sample to put in center of the audio display
int GetSyllableAtX(int x); 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() DECLARE_EVENT_TABLE()
}; };
///////
// IDs
enum {
Audio_Update_Timer = 1700
};

View file

@ -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. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@ -48,6 +48,8 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "ass_override.h" #include "ass_override.h"
#include "selection_controller.h"
#include "audio_controller.h"
#include "audio_box.h" #include "audio_box.h"
#include "audio_display.h" #include "audio_display.h"
#include "audio_karaoke.h" #include "audio_karaoke.h"
@ -634,7 +636,7 @@ void AudioKaraoke::Join() {
// Update // Update
must_rebuild = true; must_rebuild = true;
display->NeedCommit = true; //display->NeedCommit = true;
display->Update(); display->Update();
Refresh(false); Refresh(false);
@ -678,7 +680,7 @@ void AudioKaraoke::EndSplit(bool commit) {
if (hasSplit) { if (hasSplit) {
LOG_D("karaoke/audio") << "hassplit"; LOG_D("karaoke/audio") << "hassplit";
must_rebuild = true; must_rebuild = true;
display->NeedCommit = true; //display->NeedCommit = true;
SetSelection(first_sel); SetSelection(first_sel);
display->Update(); display->Update();
} }
@ -879,8 +881,9 @@ void AudioKaraokeTagMenu::OnSelectItem(wxCommandEvent &event) {
// Update display // Update display
kara->must_rebuild = true; kara->must_rebuild = true;
//kara->Commit(); //kara->Commit();
kara->display->NeedCommit = true; //kara->display->NeedCommit = true;
kara->display->CommitChanges(); /// @todo Commit changes and stay on current line
//kara->display->CommitChanges();
//kara->display->Update(); //kara->display->Update();
kara->SetSelection(firstsel, lastsel); kara->SetSelection(firstsel, lastsel);
} }

View file

@ -40,6 +40,7 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_alsa.h" #include "audio_player_alsa.h"
#include "main.h" #include "main.h"
#include "compat.h" #include "compat.h"

View file

@ -40,6 +40,7 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_dsound.h" #include "audio_player_dsound.h"
#include "frame_main.h" #include "frame_main.h"
#include "main.h" #include "main.h"

View file

@ -49,9 +49,10 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_dsound2.h" #include "audio_player_dsound2.h"
#include "frame_main.h"
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
#include "frame_main.h"
#include "main.h" #include "main.h"
#include "utils.h" #include "utils.h"

View file

@ -40,6 +40,7 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_openal.h" #include "audio_player_openal.h"
#include "frame_main.h" #include "frame_main.h"
#include "utils.h" #include "utils.h"

View file

@ -38,6 +38,7 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_oss.h" #include "audio_player_oss.h"
#include "frame_main.h" #include "frame_main.h"
#include "compat.h" #include "compat.h"

View file

@ -65,77 +65,6 @@ AudioProvider::~AudioProvider() {
delete[] raw; 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 /// @brief Get audio with volume
/// @param buf /// @param buf
/// @param start /// @param start
@ -143,7 +72,7 @@ void AudioProvider::GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int
/// @param volume /// @param volume
/// @return /// @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 { try {
GetAudio(buf,start,count); GetAudio(buf,start,count);
} }

View file

@ -141,7 +141,7 @@ void AvisynthAudioProvider::LoadFromClip(AVSValue _clip) {
/// @param start /// @param start
/// @param count /// @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 // Requested beyond the length of audio
if (start+count > num_samples) { if (start+count > num_samples) {
int64_t oldcount = count; int64_t oldcount = count;

View file

@ -57,15 +57,12 @@ class AvisynthAudioProvider : public AudioProvider, public AviSynthWrapper {
public: public:
AvisynthAudioProvider(wxString _filename); 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 AreSamplesNativeEndian() const { return true; }
bool NeedsCache() 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); void GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int samples,float scale);
}; };
#endif #endif

View file

@ -62,7 +62,7 @@ ConvertAudioProvider::ConvertAudioProvider(AudioProvider *src) : source(src) {
/// @param dst /// @param dst
/// @param count /// @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++) { for (int64_t i=0;i<count;i++) {
dst[i] = (short(src[i])-128)*255; dst[i] = (short(src[i])-128)*255;
} }
@ -80,7 +80,7 @@ template<class SampleConverter>
/// @param count /// @param count
/// @param converter /// @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 // Upsample by 2
if (sampleMult == 2) { if (sampleMult == 2) {
int64_t size = count/2; int64_t size = count/2;
@ -139,7 +139,7 @@ struct EndianSwapSampleConverter {
/// @param start /// @param start
/// @param count /// @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 // Bits per sample
int srcBps = source->GetBytesPerSample(); int srcBps = source->GetBytesPerSample();

View file

@ -51,9 +51,9 @@ class ConvertAudioProvider : public AudioProvider {
/// DOCME /// DOCME
std::tr1::shared_ptr<AudioProvider> source; 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> 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: public:
ConvertAudioProvider(AudioProvider *source); ConvertAudioProvider(AudioProvider *source);
@ -62,9 +62,9 @@ public:
/// That's one of the points of it! /// That's one of the points of it!
bool AreSamplesNativeEndian() const { return true; } 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); AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider);

View file

@ -64,7 +64,7 @@ DownmixingAudioProvider::DownmixingAudioProvider(AudioProvider *source) : provid
/// @param start /// @param start
/// @param count /// @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; if (count == 0) return;
// We can do this ourselves // We can do this ourselves

View file

@ -57,5 +57,5 @@ public:
/// ///
bool AreSamplesNativeEndian() const { return true; } 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;
}; };

View file

@ -62,7 +62,7 @@ DummyAudioProvider::~DummyAudioProvider() {
/// @param start /// @param start
/// @param count /// @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; short *workbuf = (short*)buf;
if (noise) { if (noise) {

View file

@ -50,5 +50,5 @@ public:
~DummyAudioProvider(); ~DummyAudioProvider();
bool AreSamplesNativeEndian() const { return true; } 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;
}; };

View file

@ -228,7 +228,7 @@ void FFmpegSourceAudioProvider::Close() {
/// @param Start /// @param Start
/// @param Count /// @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); uint8_t *Buf2 = static_cast<uint8_t*>(Buf);
Start -= delay; Start -= delay;
if (Start < 0) { if (Start < 0) {

View file

@ -46,8 +46,8 @@ private:
FFMS_AudioSource *AudioSource; ///< audio source object FFMS_AudioSource *AudioSource; ///< audio source object
bool COMInited; ///< COM initialization state bool COMInited; ///< COM initialization state
char FFMSErrMsg[1024]; ///< FFMS error message mutable char FFMSErrMsg[1024]; ///< FFMS error message
FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages mutable FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
void Close(); void Close();
void LoadAudio(wxString filename); void LoadAudio(wxString filename);
@ -65,6 +65,6 @@ public:
bool AreSamplesNativeEndian() const { return true; } bool AreSamplesNativeEndian() const { return true; }
bool NeedsCache() 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 #endif

View file

@ -41,6 +41,7 @@
#include <wx/filename.h> #include <wx/filename.h>
#endif #endif
#include "audio_controller.h"
#include "audio_provider_hd.h" #include "audio_provider_hd.h"
#include "compat.h" #include "compat.h"
#include "dialog_progress.h" #include "dialog_progress.h"
@ -114,7 +115,7 @@ HDAudioProvider::~HDAudioProvider() {
/// @param start /// @param start
/// @param count /// @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 // Requested beyond the length of audio
if (start+count > num_samples) { if (start+count > num_samples) {
int64_t oldcount = count; int64_t oldcount = count;

View file

@ -48,10 +48,10 @@
/// DOCME /// DOCME
class HDAudioProvider : public AudioProvider { class HDAudioProvider : public AudioProvider {
/// DOCME /// DOCME
wxMutex diskmutex; mutable wxMutex diskmutex;
/// DOCME /// DOCME
wxFile file_cache; mutable wxFile file_cache;
/// DOCME /// DOCME
wxString diskCacheFilename; wxString diskCacheFilename;
@ -71,5 +71,5 @@ public:
bool AreSamplesNativeEndian() const { return samples_native_endian; } 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;
}; };

View file

@ -144,7 +144,7 @@ PCMAudioProvider::~PCMAudioProvider()
/// @param range_length /// @param range_length
/// @return /// @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) { if (range_start + range_length > file_size) {
throw AudioDecodeError("Attempted to map beyond end of file"); 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 start
/// @param count /// @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 // Read blocks from the file
size_t index = 0; size_t index = 0;
while (count > 0 && index < index_points.size()) { while (count > 0 && index < index_points.size()) {
// Check if this index contains the samples we're looking for // 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) { if (ip.start_sample <= start && ip.start_sample+ip.num_samples > start) {
// How many samples we can maximum take from this block // How many samples we can maximum take from this block

View file

@ -64,24 +64,24 @@ private:
HANDLE file_mapping; HANDLE file_mapping;
/// DOCME /// DOCME
void *current_mapping; mutable void *current_mapping;
/// DOCME /// DOCME
int64_t mapping_start; mutable int64_t mapping_start;
/// DOCME /// DOCME
size_t mapping_length; mutable size_t mapping_length;
#else #else
int file_handle; int file_handle;
void *current_mapping; mutable void *current_mapping;
off_t mapping_start; mutable off_t mapping_start;
size_t mapping_length; mutable size_t mapping_length;
#endif #endif
protected: protected:
PCMAudioProvider(const wxString &filename); // Create base object and open the file mapping PCMAudioProvider(const wxString &filename); // Create base object and open the file mapping
virtual ~PCMAudioProvider(); // Closes 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 /// DOCME
@ -108,7 +108,7 @@ protected:
IndexVector index_points; IndexVector index_points;
public: 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 // Construct the right PCM audio provider (if any) for the file

View file

@ -36,6 +36,7 @@
#include "config.h" #include "config.h"
#include "audio_controller.h"
#include "audio_provider_ram.h" #include "audio_provider_ram.h"
#include "dialog_progress.h" #include "dialog_progress.h"
#include "frame_main.h" #include "frame_main.h"
@ -131,7 +132,7 @@ void RAMAudioProvider::Clear() {
/// @param start /// @param start
/// @param count /// @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 // Requested beyond the length of audio
if (start+count > num_samples) { if (start+count > num_samples) {
int64_t oldcount = count; int64_t oldcount = count;

View file

@ -58,5 +58,5 @@ public:
~RAMAudioProvider(); ~RAMAudioProvider();
bool AreSamplesNativeEndian() const { return samples_native_endian; } 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;
}; };

View file

@ -1,4 +1,4 @@
// Copyright (c) 2009, Niels Martin Hansen // Copyright (c) 2009-2010, Niels Martin Hansen
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@ -42,9 +42,13 @@
#include <wx/dcmemory.h> #include <wx/dcmemory.h>
#endif #endif
#include "block_cache.h"
#include "audio_renderer.h" #include "audio_renderer.h"
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
#undef min
#undef max
AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(AudioRenderer *_renderer) AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(AudioRenderer *_renderer)
{ {
@ -56,7 +60,7 @@ AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(Aud
wxBitmap *AudioRendererBitmapCacheBitmapFactory::ProduceBlock(int i) wxBitmap *AudioRendererBitmapCacheBitmapFactory::ProduceBlock(int i)
{ {
(void)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 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 : cache_bitmap_width(32) // arbitrary value for now
, bitmaps_normal(256, AudioRendererBitmapCacheBitmapFactory(this)) , bitmaps_normal(256, AudioRendererBitmapCacheBitmapFactory(this))
, bitmaps_selected(256, AudioRendererBitmapCacheBitmapFactory(this)) , bitmaps_selected(256, AudioRendererBitmapCacheBitmapFactory(this))
, cache_maxsize(0) , cache_bitmap_maxsize(0)
, cache_renderer_maxsize(0)
, renderer(0) , renderer(0)
, provider(0) , provider(0)
{ {
@ -166,7 +171,12 @@ void AudioRenderer::SetAudioProvider(AudioProvider *_provider)
void AudioRenderer::SetCacheMaxSize(size_t max_size) 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) if (provider)
{ {
size_t num_bitmaps = (size_t)((provider->GetNumSamples() + pixel_samples - 1) / pixel_samples); size_t rendered_width = (size_t)((provider->GetNumSamples() + pixel_samples - 1) / pixel_samples);
bitmaps_normal.SetBlockCount(num_bitmaps); cache_numblocks = rendered_width / cache_bitmap_width;
bitmaps_selected.SetBlockCount(num_bitmaps); 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) void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool selected)
{ {
assert(start >= 0);
assert(length >= 0);
assert(start >= 0); assert(start >= 0);
if (!provider) return; if (!provider) return;
if (!renderer) return; if (!renderer) return;
if (length <= 0) return;
// Last absolute pixel strip to render // One past last absolute pixel strip to render
int end = start + length - 1; 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 // Figure out which range of bitmaps are required
int firstbitmap = start / cache_bitmap_width; int firstbitmap = start / cache_bitmap_width;
// And the offset in it to start its use at // 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 // How many columns of the last bitmap to use
int lastbitmapoffset = end % cache_bitmap_width; 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 // origin is passed by value because we'll be using it as a local var to keep track
// of rendering progress! // 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 // Do nothing, the blank audio rendering will happen later
// to use should equal the length of the area to render. }
assert(lastbitmapoffset - firstbitmapoffset == length); else if (firstbitmap == lastbitmap)
{
const int renderwidth = lastbitmapoffset - firstbitmapoffset;
wxBitmap bmp = GetCachedBitmap(firstbitmap, selected); wxBitmap bmp = GetCachedBitmap(firstbitmap, selected);
wxMemoryDC bmpdc(bmp); 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 else
{ {
@ -244,32 +271,40 @@ void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool
{ {
bmp = GetCachedBitmap(firstbitmap, selected); bmp = GetCachedBitmap(firstbitmap, selected);
// Can't use dc.DrawBitmap here because we need to clip the bitmap
wxMemoryDC bmpdc(bmp); wxMemoryDC bmpdc(bmp);
dc.Blit(origin, wxSize(cache_bitmap_width-firstbitmapoffset, pixel_height), dc.Blit(origin, wxSize(cache_bitmap_width-firstbitmapoffset, pixel_height),
&bmpdc, wxPoint(firstbitmapoffset, 0)); &bmpdc, wxPoint(firstbitmapoffset, 0));
origin.x += cache_bitmap_width-firstbitmapoffset; 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); bmp = GetCachedBitmap(i, selected);
wxMemoryDC bmpdc(bmp); dc.DrawBitmap(bmp, origin);
dc.Blit(origin, wxSize(cache_bitmap_width, pixel_height), &bmpdc, wxPoint(0, 0));
origin.x += cache_bitmap_width; origin.x += cache_bitmap_width;
} }
{ {
bmp = GetCachedBitmap(lastbitmap, selected); bmp = GetCachedBitmap(lastbitmap, selected);
// We also need clipping here
wxMemoryDC bmpdc(bmp); 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) if (selected)
bitmaps_selected.Age(cache_maxsize / 8); bitmaps_selected.Age(cache_bitmap_maxsize);
else else
bitmaps_normal.Age(cache_maxsize / 8); bitmaps_normal.Age(cache_bitmap_maxsize);
renderer->AgeCache(3 * cache_maxsize / 4); renderer->AgeCache(cache_renderer_maxsize);
} }
@ -297,7 +332,7 @@ void AudioRendererBitmapProvider::SetSamplesPerPixel(int _pixel_samples)
if (pixel_samples == _pixel_samples) return; if (pixel_samples == _pixel_samples) return;
pixel_samples = _pixel_samples; pixel_samples = _pixel_samples;
OnSetSamplesPerPixel(); OnSetSamplesPerPixel();
} }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2009, Niels Martin Hansen // Copyright (c) 2009-2010, Niels Martin Hansen
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@ -43,8 +43,6 @@
#include <wx/gdicmn.h> #include <wx/gdicmn.h>
#endif #endif
#include "block_cache.h"
// Some forward declarations for outside stuff // Some forward declarations for outside stuff
class AudioProvider; class AudioProvider;
@ -54,6 +52,11 @@ class AudioRendererBitmapProvider;
class AudioRenderer; class AudioRenderer;
#ifndef AGI_BLOCK_CACHE_INCLUDED
#error You much include "block_cache.h" before "audio_renderer.h"
#endif
/// @class AudioRendererBitmapCacheBitmapFactory /// @class AudioRendererBitmapCacheBitmapFactory
/// @brief Produces wxBitmap objects for DataBlockCache storage for the audio renderer /// @brief Produces wxBitmap objects for DataBlockCache storage for the audio renderer
@ -111,8 +114,12 @@ class AudioRenderer {
AudioRendererBitmapCache bitmaps_normal; AudioRendererBitmapCache bitmaps_normal;
/// Cached bitmaps for marked (selected) audio ranges /// Cached bitmaps for marked (selected) audio ranges
AudioRendererBitmapCache bitmaps_selected; AudioRendererBitmapCache bitmaps_selected;
/// The maximum allowed size of the cache, in bytes /// Number of blocks in the bitmap caches
size_t cache_maxsize; 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 /// Actual renderer for bitmaps
AudioRendererBitmapProvider *renderer; AudioRendererBitmapProvider *renderer;
@ -284,7 +291,7 @@ public:
AudioRendererBitmapProvider() : provider(0), pixel_samples(0) { }; AudioRendererBitmapProvider() : provider(0), pixel_samples(0) { };
/// @brief Destructor /// @brief Destructor
~AudioRendererBitmapProvider() { } virtual ~AudioRendererBitmapProvider() { }
/// @brief Rendering function /// @brief Rendering function
/// @param bmp Bitmap to render to /// @param bmp Bitmap to render to
@ -295,6 +302,15 @@ public:
/// the width and height to render. /// the width and height to render.
virtual void Render(wxBitmap &bmp, int start, bool selected) = 0; 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 /// @brief Change audio provider
/// @param provider Audio provider to change to /// @param provider Audio provider to change to
void SetProvider(AudioProvider *provider); void SetProvider(AudioProvider *provider);

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,4 @@
// Copyright (c) 2005, 2006, Rodrigo Braz Monteiro // Copyright (c) 2009, Niels Martin Hansen
// Copyright (c) 2006, 2007, Niels Martin Hansen
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@ -40,65 +39,102 @@
#include <stdint.h> #include <stdint.h>
#endif #endif
class AudioProvider; #ifdef WITH_FFTW
#include <fftw3.h>
// Specified and implemented in cpp file, interface is private to spectrum code #endif
class AudioSpectrumCacheManager;
/// @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. /// @brief Render frequency-power spectrum graphs for audio data.
/// ///
/// Renders frequency-power spectrum graphs of PCM audio data using a fast fourier transform /// Renders frequency-power spectrum graphs of PCM audio data using a derivation function
/// to derive the data. The frequency-power data are cached to avoid re-computing them /// such as the fast fourier transform.
/// frequently, and the cache size is limited by a configuration setting. class AudioSpectrumRenderer : public AudioRendererBitmapProvider {
/// friend struct AudioSpectrumCacheBlockFactory;
/// 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:
/// Internal cache management for the spectrum /// Internal cache management for the spectrum
AudioSpectrumCacheManager *cache; AudioSpectrumCache *cache;
/// Colour table used for regular rendering /// Colour table used for regular rendering
unsigned char colours_normal[256*3]; AudioColorScheme colors_normal;
/// Colour table used for rendering the audio selection /// Colour table used for rendering the audio selection
unsigned char colours_selected[256*3]; AudioColorScheme colors_selected;
/// The audio provider to use as source /// Binary logarithm of number of samples to use in deriving frequency-power data
AudioProvider *provider; size_t derivation_size;
unsigned long line_length; ///< Number of frequency components per line (half of number of samples) /// Binary logarithm of number of samples between the start of derivations
unsigned long num_lines; ///< Number of lines needed for the audio size_t derivation_dist;
unsigned int fft_overlaps; ///< Number of overlaps used in FFT
float power_scale; ///< Amplification of displayed power /// @brief Reset in response to changing audio provider
int minband; ///< Smallest frequency band displayed ///
int maxband; ///< Largest frequency band displayed /// 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: public:
/// @brief Constructor /// @brief Constructor
/// @param _provider Audio provider to render spectrum data for. AudioSpectrumRenderer();
///
/// Reads configuration data for the spectrum display and initialises itself following that.
AudioSpectrum(AudioProvider *_provider);
/// @brief Destructor /// @brief Destructor
~AudioSpectrum(); virtual ~AudioSpectrumRenderer();
/// @brief Render a range of audio spectrum to a bitmap buffer. /// @brief Render a range of audio spectrum
/// @param range_start First audio sample in the range to render. /// @param bmp [in,out] Bitmap to render into, also carries lenght information
/// @param range_end Last audio sample in the range to render. /// @param start First column of pixel data in display to render
/// @param selected Use the alternate colour palette? /// @param selected Whether to use the alternate colour scheme
/// @param img Pointer to 32 bit RGBX data void Render(wxBitmap &bmp, int start, bool selected);
/// @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 Set the amplification to use when rendering. /// @brief Render blank area
/// @param _power_scale Amplification factor to use. void RenderBlank(wxDC &dc, const wxRect &rect, bool selected);
void SetScaling(float _power_scale);
/// @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);
}; };

View file

@ -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;
}

View file

@ -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) { }
};

136
aegisub/src/audio_timing.h Normal file
View file

@ -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);

View file

@ -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);
}

View file

@ -50,7 +50,8 @@
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "ass_style.h" #include "ass_style.h"
#include "audio_display.h" #include "audio_controller.h"
#include "selection_controller.h"
#include "compat.h" #include "compat.h"
#include "frame_main.h" #include "frame_main.h"
#include "main.h" #include "main.h"
@ -1251,9 +1252,12 @@ void BaseGrid::OnKeyPress(wxKeyEvent &event) {
} }
// Other events, send to audio display // Other events, send to audio display
/// @todo Reinstate this, or make a better solution, when audio is getting stabler again
/*
if (context->audio->loaded) { if (context->audio->loaded) {
context->audio->GetEventHandler()->ProcessEvent(event); context->audio->GetEventHandler()->ProcessEvent(event);
} }
*/
else event.Skip(); else event.Skip();
} }

View file

@ -149,7 +149,6 @@ public:
/// DOCME /// DOCME
SubsEditBox *editBox; SubsEditBox *editBox;
/// DOCME /// DOCME
bool byFrame; bool byFrame;

View file

@ -41,6 +41,8 @@
#include <vector> #include <vector>
#endif #endif
#define AGI_BLOCK_CACHE_INCLUDED 1
/// @class BasicDataBlockFactory /// @class BasicDataBlockFactory
/// @brief Simple factory for allocating blocks for DataBlockCache /// @brief Simple factory for allocating blocks for DataBlockCache
@ -143,7 +145,7 @@ class DataBlockCache {
for (size_t bi = 0; bi < mb.blocks.size(); ++bi) for (size_t bi = 0; bi < mb.blocks.size(); ++bi)
{ {
BlockT *b = mb.blocks[bi]; BlockT *b = mb.blocks[bi];
if (!b) if (b)
factory.DisposeBlock(b); factory.DisposeBlock(b);
} }

View file

@ -107,6 +107,12 @@
//#define FINAL_RELEASE //#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 // Specify tags the update checker accepts
// See <http://devel.aegisub.org/wiki/Technical/UpdateChecker> for details on tags. // 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 // Depending on who will be using your build, you may or may not want to have the

View file

@ -46,8 +46,11 @@
#endif #endif
/// DOCME namespace Automation4 {
namespace Automation4 { class ScriptManager; class Script; class AutoloadScriptManager; }; class ScriptManager;
class AutoloadScriptManager;
class Script;
};
/// DOCME /// DOCME

View file

@ -42,6 +42,7 @@
#include <wx/display.h> /// Must be included last. #include <wx/display.h> /// Must be included last.
#endif #endif
#include "audio_controller.h"
#include "dialog_detached_video.h" #include "dialog_detached_video.h"
#include "frame_main.h" #include "frame_main.h"
#include "main.h" #include "main.h"

View file

@ -51,6 +51,7 @@
#include "ass_file.h" #include "ass_file.h"
#include "ass_override.h" #include "ass_override.h"
#include "ass_style.h" #include "ass_style.h"
#include "audio_controller.h"
#include "compat.h" #include "compat.h"
#include "dialog_fonts_collector.h" #include "dialog_fonts_collector.h"
#include "font_file_lister.h" #include "font_file_lister.h"
@ -58,6 +59,7 @@
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "scintilla_text_ctrl.h" #include "scintilla_text_ctrl.h"
#include "selection_controller.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "utils.h" #include "utils.h"

View file

@ -55,6 +55,7 @@
#include "help_button.h" #include "help_button.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "utils.h" #include "utils.h"
#include "validators.h" #include "validators.h"

View file

@ -47,9 +47,11 @@
#include "compat.h" #include "compat.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "audio_controller.h"
#include "dialog_search_replace.h" #include "dialog_search_replace.h"
#include "frame_main.h" #include "frame_main.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "subs_edit_ctrl.h" #include "subs_edit_ctrl.h"
#include "subs_grid.h" #include "subs_grid.h"

View file

@ -49,6 +49,7 @@
#include "dialog_selection.h" #include "dialog_selection.h"
#include "help_button.h" #include "help_button.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"

View file

@ -44,6 +44,7 @@
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "audio_controller.h"
#include "compat.h" #include "compat.h"
#include "dialog_spellchecker.h" #include "dialog_spellchecker.h"
#include "frame_main.h" #include "frame_main.h"
@ -51,6 +52,7 @@
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "include/aegisub/spellchecker.h" #include "include/aegisub/spellchecker.h"
#include "selection_controller.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "subs_edit_ctrl.h" #include "subs_edit_ctrl.h"
#include "subs_grid.h" #include "subs_grid.h"

View file

@ -55,6 +55,7 @@
#include "help_button.h" #include "help_button.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "subs_preview.h" #include "subs_preview.h"
#include "include/aegisub/subtitles_provider.h" #include "include/aegisub/subtitles_provider.h"

View file

@ -54,6 +54,7 @@
#include "help_button.h" #include "help_button.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "standard_paths.h" #include "standard_paths.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "utils.h" #include "utils.h"

View file

@ -46,8 +46,9 @@
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "ass_style.h" #include "ass_style.h"
#include "selection_controller.h"
#include "audio_controller.h"
#include "audio_box.h" #include "audio_box.h"
#include "audio_display.h"
#include "dialog_styling_assistant.h" #include "dialog_styling_assistant.h"
#include "frame_main.h" #include "frame_main.h"
#include "help_button.h" #include "help_button.h"
@ -72,8 +73,8 @@ wxDialog (parent, -1, _("Styling assistant"), wxDefaultPosition, wxDefaultSize,
// Variables // Variables
grid = _grid; grid = _grid;
audio = VideoContext::Get()->audio->box->audioDisplay; audio = VideoContext::Get()->audio;
video = video->Get(); video = VideoContext::Get();
needCommit = false; needCommit = false;
linen = -1; linen = -1;
@ -269,7 +270,8 @@ void DialogStyling::OnActivate(wxActivateEvent &event) {
} }
// Enable/disable play video/audio buttons // Enable/disable play video/audio buttons
PlayVideoButton->Enable(video->IsLoaded()); 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 // Fix style list
Styles->Set(grid->ass->GetStyles()); Styles->Set(grid->ass->GetStyles());
// Fix line selection // Fix line selection
@ -377,7 +379,9 @@ void DialogStyling::OnPlayVideoButton(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void DialogStyling::OnPlayAudioButton(wxCommandEvent &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(); TypeBox->SetFocus();
} }
@ -446,9 +450,12 @@ void StyleEditBox::OnKeyDown(wxKeyEvent &event) {
// Play audio // Play audio
if (Hotkeys.IsPressed(_T("Styling Assistant 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) { if (diag->audio->loaded) {
diag->audio->Play(diag->line->Start.GetMS(),diag->line->End.GetMS()); diag->audio->Play(diag->line->Start.GetMS(),diag->line->End.GetMS());
} }
*/
return; return;
} }

View file

@ -56,6 +56,7 @@ class SubtitlesGrid;
class DialogStyling; class DialogStyling;
class AudioDisplay; class AudioDisplay;
class VideoContext; class VideoContext;
class AudioController;
@ -142,7 +143,7 @@ public:
AssDialogue *line; AssDialogue *line;
/// DOCME /// DOCME
AudioDisplay *audio; AudioController *audio;
/// DOCME /// DOCME
VideoContext *video; VideoContext *video;

View file

@ -43,6 +43,7 @@
#include "help_button.h" #include "help_button.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "utils.h" #include "utils.h"
#include "validators.h" #include "validators.h"

View file

@ -45,12 +45,13 @@
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "audio_display.h" #include "audio_controller.h"
#include "dialog_translation.h" #include "dialog_translation.h"
#include "frame_main.h" #include "frame_main.h"
#include "help_button.h" #include "help_button.h"
#include "hotkeys.h" #include "hotkeys.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "selection_controller.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "subs_edit_ctrl.h" #include "subs_edit_ctrl.h"
#include "subs_grid.h" #include "subs_grid.h"
@ -78,7 +79,7 @@ DialogTranslation::DialogTranslation (wxWindow *parent,AssFile *_subs,SubtitlesG
subs = _subs; subs = _subs;
grid = _grid; grid = _grid;
audio = VideoContext::Get()->audio; audio = VideoContext::Get()->audio;
video = video->Get(); video = VideoContext::Get();
// Translation controls // Translation controls
OrigText = new ScintillaTextCtrl(this,TEXT_ORIGINAL,_T(""),wxDefaultPosition,wxSize(320,80)); 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 *PlayVideoButton = new wxButton(this,BUTTON_TRANS_PLAY_VIDEO,_("Play Video"));
wxButton *PlayAudioButton = new wxButton(this,BUTTON_TRANS_PLAY_AUDIO,_("Play Audio")); wxButton *PlayAudioButton = new wxButton(this,BUTTON_TRANS_PLAY_AUDIO,_("Play Audio"));
PlayVideoButton->Enable(video->IsLoaded()); 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(PlayAudioButton,0,wxALL,5);
ToolSizer->Add(PlayVideoButton,0,wxLEFT | wxRIGHT | wxBOTTOM,5); ToolSizer->Add(PlayVideoButton,0,wxLEFT | wxRIGHT | wxBOTTOM,5);
@ -406,10 +408,13 @@ void DialogTranslation::OnTransBoxKey(wxKeyEvent &event) {
// Play audio // Play audio
if (Hotkeys.IsPressed(_T("Translation Assistant 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) { if (audio->loaded) {
audio->Play(current->Start.GetMS(),current->End.GetMS()); audio->Play(current->Start.GetMS(),current->End.GetMS());
TransText->SetFocus(); TransText->SetFocus();
} }
*/
return; return;
} }
@ -439,7 +444,9 @@ void DialogTranslation::OnPlayVideoButton(wxCommandEvent &event) {
/// @param event /// @param event
/// ///
void DialogTranslation::OnPlayAudioButton(wxCommandEvent &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(); TransText->SetFocus();
} }

View file

@ -45,6 +45,7 @@ class AudioDisplay;
class ScintillaTextCtrl; class ScintillaTextCtrl;
class SubtitlesGrid; class SubtitlesGrid;
class VideoContext; class VideoContext;
class AudioController;
/// DOCME /// DOCME
@ -56,7 +57,7 @@ class DialogTranslation : public wxDialog {
private: private:
/// DOCME /// DOCME
AudioDisplay *audio; AudioController *audio;
/// DOCME /// DOCME
VideoContext *video; VideoContext *video;

View file

@ -43,6 +43,7 @@
#include <wx/filename.h> #include <wx/filename.h>
#endif #endif
#include "audio_controller.h"
#include "drop.h" #include "drop.h"
#include "frame_main.h" #include "frame_main.h"

View file

@ -48,6 +48,7 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_controller.h"
#include "compat.h" #include "compat.h"
#include "ffmpegsource_common.h" #include "ffmpegsource_common.h"
#include "frame_main.h" #include "frame_main.h"

View file

@ -46,8 +46,9 @@
#include "ass_file.h" #include "ass_file.h"
#include "selection_controller.h"
#include "audio_controller.h"
#include "audio_box.h" #include "audio_box.h"
#include "audio_display.h"
#ifdef WITH_AUTOMATION #ifdef WITH_AUTOMATION
#include "auto4_base.h" #include "auto4_base.h"
#endif #endif
@ -114,8 +115,7 @@ FrameMain::FrameMain (wxArrayString args)
// Initialize flags // Initialize flags
HasSelection = false; HasSelection = false;
menuCreated = false; menuCreated = false;
blockAudioLoad = false; blockVideoLoad = false;
blockAudioLoad = false;
StartupLog(_T("Install PNG handler")); StartupLog(_T("Install PNG handler"));
// Create PNG handler // Create PNG handler
@ -130,6 +130,10 @@ FrameMain::FrameMain (wxArrayString args)
local_scripts = new Automation4::ScriptManager(); local_scripts = new Automation4::ScriptManager();
#endif #endif
// Contexts and controllers
audioController = new AudioController;
audioController->AddAudioListener(this);
// Create menu and tool bars // Create menu and tool bars
StartupLog(_T("Apply saved Maximized state")); StartupLog(_T("Apply saved Maximized state"));
if (OPT_GET("App/Maximized")->GetBool()) Maximize(true); if (OPT_GET("App/Maximized")->GetBool()) Maximize(true);
@ -206,7 +210,10 @@ FrameMain::FrameMain (wxArrayString args)
/// @brief FrameMain destructor /// @brief FrameMain destructor
FrameMain::~FrameMain () { FrameMain::~FrameMain () {
VideoContext::Get()->SetVideo(_T(""));
audioController->CloseAudio();
DeInitContents(); DeInitContents();
delete audioController;
#ifdef WITH_AUTOMATION #ifdef WITH_AUTOMATION
delete local_scripts; delete local_scripts;
#endif #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)); 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); wxMenuItem *RecentAudParent = new wxMenuItem(audioMenu, Menu_File_Recent_Auds_Parent, _("Recent"), _T(""), wxITEM_NORMAL, RecentAuds);
audioMenu->Append(RecentAudParent); 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 #ifdef _DEBUG
audioMenu->AppendSeparator(); audioMenu->AppendSeparator();
audioMenu->Append(Menu_Audio_Open_Dummy, _T("Open 2h30 Blank Audio"), _T("Open a 150 minutes blank audio clip, for debugging")); 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")); StartupLog(_T("Create background panel"));
Panel = new wxPanel(this,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL | wxCLIP_CHILDREN); 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; // Video area;
StartupLog(_T("Create video box")); StartupLog(_T("Create video box"));
videoBox = new VideoBox(Panel, false, ZoomBox, ass); 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 // Subtitles area
StartupLog(_T("Create subtitles grid")); StartupLog(_T("Create subtitles grid"));
SubsGrid = new SubtitlesGrid(this,Panel,-1,ass,wxDefaultPosition,wxSize(600,100),wxWANTS_CHARS | wxSUNKEN_BORDER,_T("Subs 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; videoBox->videoSlider->grid = SubsGrid;
VideoContext::Get()->grid = SubsGrid; VideoContext::Get()->grid = SubsGrid;
Search.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 // Audio area
StartupLog(_T("Create audio box")); StartupLog(_T("Create audio box"));
audioBox = new AudioBox(Panel, SubsGrid); audioBox = new AudioBox(audioSash, audioController, SubsGrid);
audioBox->frameMain = this; 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")); StartupLog(_T("Create subtitle editing box"));
EditBox = new SubsEditBox(Panel,SubsGrid); 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 // Set sizers/hints
StartupLog(_T("Arrange main sizers")); 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(new wxStaticLine(Panel),0,wxEXPAND | wxALL,0);
MainSizer->Add(TopSizer,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); Panel->SetSizer(MainSizer);
//MainSizer->SetSizeHints(Panel); //MainSizer->SetSizeHints(Panel);
//SetSizer(MainSizer); //SetSizer(MainSizer);
@ -828,7 +845,7 @@ void FrameMain::SetDisplayMode(int video, int audio) {
else if (video) sv = VideoContext::Get()->IsLoaded() && !detachedVideo; else if (video) sv = VideoContext::Get()->IsLoaded() && !detachedVideo;
if (audio == -1) sa = showAudio; if (audio == -1) sa = showAudio;
else if (audio) sa = audioBox->loaded; else if (audio) sa = audioController->IsAudioOpen();
// See if anything changed // See if anything changed
if (sv == showVideo && sa == showAudio) return; if (sv == showVideo && sa == showAudio) return;
@ -842,8 +859,8 @@ void FrameMain::SetDisplayMode(int video, int audio) {
VideoContext::Get()->Stop(); VideoContext::Get()->Stop();
// Set display // Set display
TopSizer->Show(videoBox,showVideo,true); TopSizer->Show(videoBox, showVideo, true);
ToolSizer->Show(audioBox,showAudio,true); ToolsSizer->Show(audioSash, showAudio, true);
// Update // Update
UpdateToolbar(); UpdateToolbar();
@ -881,7 +898,7 @@ void FrameMain::UpdateTitle() {
else newTitle << _("untitled"); else newTitle << _("untitled");
#endif #endif
#ifdef __WXMAC__ #if defined(__WXMAC__) && !defined(__LP64__)
// On Mac, set the mark in the close button // On Mac, set the mark in the close button
OSXSetModified(subsMod); OSXSetModified(subsMod);
#endif #endif
@ -914,19 +931,19 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
// Get new state info // Get new state info
ass->GetScriptInfo(_T("Video Position")).ToLong(&videoPos); ass->GetScriptInfo(_T("Video Position")).ToLong(&videoPos);
ass->GetScriptInfo(_T("Video Zoom Percent")).ToDouble(&videoZoom); ass->GetScriptInfo(_T("Video Zoom Percent")).ToDouble(&videoZoom);
wxString curassVideo = DecodeRelativePath(ass->GetScriptInfo(_T("Video File")),ass->filename); wxString curSubsVideo = DecodeRelativePath(ass->GetScriptInfo(_T("Video File")),ass->filename);
wxString curassVFR = DecodeRelativePath(ass->GetScriptInfo(_T("VFR File")),ass->filename); wxString curSubsVFR = DecodeRelativePath(ass->GetScriptInfo(_T("VFR File")),ass->filename);
wxString curassKeyframes = DecodeRelativePath(ass->GetScriptInfo(_T("Keyframes File")),ass->filename); wxString curSubsKeyframes = DecodeRelativePath(ass->GetScriptInfo(_T("Keyframes File")),ass->filename);
wxString curassAudio = DecodeRelativePath(ass->GetScriptInfo(_T("Audio File")),ass->filename); wxString curSubsAudio = DecodeRelativePath(ass->GetScriptInfo(_T("Audio URI")),ass->filename);
wxString AutoScriptString = ass->GetScriptInfo(_T("Automation Scripts")); wxString AutoScriptString = ass->GetScriptInfo(_T("Automation Scripts"));
// Check if there is anything to change // Check if there is anything to change
int autoLoadMode = OPT_GET("App/Auto/Load Linked Files")->GetInt(); int autoLoadMode = OPT_GET("App/Auto/Load Linked Files")->GetInt();
bool hasToLoad = false; bool hasToLoad = false;
if (curassAudio != audioBox->audioName || if (curSubsAudio !=audioController->GetAudioURL() ||
curassVFR != VideoContext::Get()->GetTimecodesName() || curSubsVFR != VideoContext::Get()->GetTimecodesName() ||
curassVideo != VideoContext::Get()->videoName || curSubsVideo != VideoContext::Get()->videoName ||
curassKeyframes != VideoContext::Get()->GetKeyFramesName() curSubsKeyframes != VideoContext::Get()->GetKeyFramesName()
#ifdef WITH_AUTOMATION #ifdef WITH_AUTOMATION
|| !AutoScriptString.IsEmpty() || local_scripts->GetScripts().size() > 0 || !AutoScriptString.IsEmpty() || local_scripts->GetScripts().size() > 0
#endif #endif
@ -946,8 +963,8 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
if (doLoad) { if (doLoad) {
// Video // Video
if (curassVideo != VideoContext::Get()->videoName) { if (curSubsVideo != VideoContext::Get()->videoName) {
LoadVideo(curassVideo); LoadVideo(curSubsVideo);
if (VideoContext::Get()->IsLoaded()) { if (VideoContext::Get()->IsLoaded()) {
VideoContext::Get()->SetAspectRatio(videoAr,videoArValue); VideoContext::Get()->SetAspectRatio(videoAr,videoArValue);
videoBox->videoDisplay->SetZoom(videoZoom); videoBox->videoDisplay->SetZoom(videoZoom);
@ -955,13 +972,12 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
} }
} }
VideoContext::Get()->LoadTimecodes(curassVFR); VideoContext::Get()->LoadTimecodes(curSubsVFR);
VideoContext::Get()->LoadKeyframes(curassKeyframes); VideoContext::Get()->LoadKeyframes(curSubsKeyframes);
// Audio // Audio
if (curassAudio != audioBox->audioName) { if (curSubsAudio != audioController->GetAudioURL()) {
if (curassAudio == _T("?video")) LoadAudio(_T(""),true); audioController->OpenAudio(curSubsAudio);
else LoadAudio(curassAudio);
} }
// Automation scripts // Automation scripts
@ -1019,7 +1035,7 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
} }
// Store audio data // 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 // Store video data
ass->SetScriptInfo(_T("Video File"),MakeRelativePath(VideoContext::Get()->videoName,ass->filename)); ass->SetScriptInfo(_T("Video File"),MakeRelativePath(VideoContext::Get()->videoName,ass->filename));
@ -1124,31 +1140,6 @@ void FrameMain::LoadVideo(wxString file,bool autoload) {
Thaw(); 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) { void FrameMain::LoadVFR(wxString filename) {
if (filename.empty()) { if (filename.empty()) {
VideoContext::Get()->CloseTimecodes(); VideoContext::Get()->CloseTimecodes();
@ -1208,7 +1199,7 @@ void FrameMain::SetAccelerators() {
// Medusa // Medusa
bool medusaPlay = OPT_GET("Audio/Medusa Timing Hotkeys")->GetBool(); 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 Play"),Medusa_Play));
entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Stop"),Medusa_Stop)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Stop"),Medusa_Stop));
entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Play Before"),Medusa_Play_Before)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Play Before"),Medusa_Play_Before));
@ -1294,7 +1285,6 @@ bool FrameMain::LoadList(wxArrayString list) {
} }
// Set blocking // Set blocking
blockAudioLoad = (audio != _T(""));
blockVideoLoad = (video != _T("")); blockVideoLoad = (video != _T(""));
// Load files // Load files
@ -1305,10 +1295,8 @@ bool FrameMain::LoadList(wxArrayString list) {
blockVideoLoad = false; blockVideoLoad = false;
LoadVideo(video); LoadVideo(video);
} }
if (blockAudioLoad) { if (!audio.IsEmpty())
blockAudioLoad = false; audioController->OpenAudio(audio);
LoadAudio(audio);
}
// Result // Result
return ((subs != _T("")) || (audio != _T("")) || (video != _T(""))); return ((subs != _T("")) || (audio != _T("")) || (video != _T("")));

View file

@ -43,9 +43,14 @@
#include <wx/menu.h> #include <wx/menu.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/sashwin.h>
#include <wx/timer.h> #include <wx/timer.h>
#endif #endif
#ifndef AGI_AUDIO_CONTROLLER_INCLUDED
#error You must include "audio_controller.h" before "frame_main.h"
#endif
class AssFile; class AssFile;
class VideoDisplay; class VideoDisplay;
class VideoSlider; class VideoSlider;
@ -57,6 +62,7 @@ class VideoBox;
class DialogDetachedVideo; class DialogDetachedVideo;
class DialogStyling; class DialogStyling;
class AegisubFileDropTarget; class AegisubFileDropTarget;
class AudioController;
namespace Automation4 { class FeatureMacro; class ScriptManager; } namespace Automation4 { class FeatureMacro; class ScriptManager; }
@ -67,7 +73,7 @@ namespace Automation4 { class FeatureMacro; class ScriptManager; }
/// @brief DOCME /// @brief DOCME
/// ///
/// DOCME /// DOCME
class FrameMain: public wxFrame { class FrameMain: public wxFrame, private AudioControllerAudioEventListener {
friend class AegisubFileDropTarget; friend class AegisubFileDropTarget;
friend class AegisubApp; friend class AegisubApp;
friend class SubtitlesGrid; friend class SubtitlesGrid;
@ -93,9 +99,6 @@ private:
wxTimer StatusClear; wxTimer StatusClear;
/// DOCME
bool blockAudioLoad;
/// DOCME /// DOCME
bool blockVideoLoad; bool blockVideoLoad;
@ -184,6 +187,8 @@ private:
void OnVideoPlay(wxCommandEvent &event); void OnVideoPlay(wxCommandEvent &event);
void OnAudioBoxResize(wxSashEvent &event);
void OnOpenRecentSubs (wxCommandEvent &event); void OnOpenRecentSubs (wxCommandEvent &event);
void OnOpenRecentVideo (wxCommandEvent &event); void OnOpenRecentVideo (wxCommandEvent &event);
void OnOpenRecentAudio (wxCommandEvent &event); void OnOpenRecentAudio (wxCommandEvent &event);
@ -239,6 +244,7 @@ private:
void OnOpenAudio (wxCommandEvent &event); void OnOpenAudio (wxCommandEvent &event);
void OnOpenAudioFromVideo (wxCommandEvent &event); void OnOpenAudioFromVideo (wxCommandEvent &event);
void OnCloseAudio (wxCommandEvent &event); void OnCloseAudio (wxCommandEvent &event);
void OnAudioDisplayMode (wxCommandEvent &event);
#ifdef _DEBUG #ifdef _DEBUG
void OnOpenDummyAudio(wxCommandEvent &event); void OnOpenDummyAudio(wxCommandEvent &event);
void OnOpenDummyNoiseAudio(wxCommandEvent &event); void OnOpenDummyNoiseAudio(wxCommandEvent &event);
@ -313,7 +319,6 @@ private:
void OnMedusaPrev(wxCommandEvent &event); void OnMedusaPrev(wxCommandEvent &event);
void LoadVideo(wxString filename,bool autoload=false); void LoadVideo(wxString filename,bool autoload=false);
void LoadAudio(wxString filename,bool FromVideo=false);
void LoadVFR(wxString filename); void LoadVFR(wxString filename);
void LoadSubtitles(wxString filename,wxString charset=_T("")); void LoadSubtitles(wxString filename,wxString charset=_T(""));
bool SaveSubtitles(bool saveas=false,bool withCharset=false); bool SaveSubtitles(bool saveas=false,bool withCharset=false);
@ -322,21 +327,33 @@ private:
void RebuildRecentList(wxString listName,wxMenu *menu,int startID); void RebuildRecentList(wxString listName,wxMenu *menu,int startID);
void SynchronizeProject(bool FromSubs=false); 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(); void OnSubtitlesFileChanged();
public: public:
/// DOCME /// The subtitle editing area
SubtitlesGrid *SubsGrid; SubtitlesGrid *SubsGrid;
/// DOCME /// The subtitle editing textbox
SubsEditBox *EditBox; SubsEditBox *EditBox;
/// DOCME /// Sash for resizing the audio area
wxSashWindow *audioSash;
/// The audio area
AudioBox *audioBox; AudioBox *audioBox;
/// DOCME /// The video area
VideoBox *videoBox; VideoBox *videoBox;
/// DOCME /// DOCME
@ -345,18 +362,19 @@ public:
/// DOCME /// DOCME
DialogStyling *stylingAssistant; DialogStyling *stylingAssistant;
/// The audio controller for the open project
AudioController *audioController;
/// DOCME
/// Arranges things from top to bottom in the window
wxBoxSizer *MainSizer; wxBoxSizer *MainSizer;
/// DOCME /// Arranges video box and tool box from left to right
wxBoxSizer *TopSizer; wxBoxSizer *TopSizer;
/// DOCME /// Arranges audio and editing areas top to bottom
wxBoxSizer *BottomSizer; wxBoxSizer *ToolsSizer;
/// DOCME
wxBoxSizer *ToolSizer;
FrameMain (wxArrayString args); FrameMain (wxArrayString args);
~FrameMain (); ~FrameMain ();
@ -435,6 +453,9 @@ enum {
Menu_Audio_Open_File, Menu_Audio_Open_File,
Menu_Audio_Open_From_Video, Menu_Audio_Open_From_Video,
Menu_Audio_Close, Menu_Audio_Close,
Menu_Audio_Spectrum,
Menu_Audio_Waveform,
#ifdef _DEBUG #ifdef _DEBUG
Menu_Audio_Open_Dummy, Menu_Audio_Open_Dummy,
Menu_Audio_Open_Dummy_Noise, Menu_Audio_Open_Dummy_Noise,
@ -503,6 +524,12 @@ enum {
AutoSave_Timer, AutoSave_Timer,
StatusClear_Timer, StatusClear_Timer,
/// Id for the audio box resizing sash
Main_AudioSash,
/// DOCME
Video_Next_Frame, Video_Next_Frame,
Video_Prev_Frame, Video_Prev_Frame,
Video_Focus_Seek, Video_Focus_Seek,

View file

@ -48,6 +48,8 @@
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "selection_controller.h"
#include "audio_controller.h"
#include "audio_box.h" #include "audio_box.h"
#include "audio_display.h" #include "audio_display.h"
#ifdef WITH_AUTOMATION #ifdef WITH_AUTOMATION
@ -84,6 +86,7 @@
#include "main.h" #include "main.h"
#include "preferences.h" #include "preferences.h"
#include "standard_paths.h" #include "standard_paths.h"
#include "selection_controller.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "subs_edit_ctrl.h" #include "subs_edit_ctrl.h"
#include "subs_grid.h" #include "subs_grid.h"
@ -104,6 +107,8 @@ BEGIN_EVENT_TABLE(FrameMain, wxFrame)
EVT_CLOSE(FrameMain::OnCloseWindow) EVT_CLOSE(FrameMain::OnCloseWindow)
EVT_SASH_DRAGGED(Main_AudioSash, FrameMain::OnAudioBoxResize)
EVT_MENU_OPEN(FrameMain::OnMenuOpen) EVT_MENU_OPEN(FrameMain::OnMenuOpen)
EVT_MENU_RANGE(Menu_File_Recent,Menu_File_Recent+99, FrameMain::OnOpenRecentSubs) EVT_MENU_RANGE(Menu_File_Recent,Menu_File_Recent+99, FrameMain::OnOpenRecentSubs)
EVT_MENU_RANGE(Menu_Video_Recent,Menu_Video_Recent+99, FrameMain::OnOpenRecentVideo) 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_File, FrameMain::OnOpenAudio)
EVT_MENU(Menu_Audio_Open_From_Video, FrameMain::OnOpenAudioFromVideo) EVT_MENU(Menu_Audio_Open_From_Video, FrameMain::OnOpenAudioFromVideo)
EVT_MENU(Menu_Audio_Close, FrameMain::OnCloseAudio) EVT_MENU(Menu_Audio_Close, FrameMain::OnCloseAudio)
EVT_MENU(Menu_Audio_Spectrum, FrameMain::OnAudioDisplayMode)
EVT_MENU(Menu_Audio_Waveform, FrameMain::OnAudioDisplayMode)
#ifdef _DEBUG #ifdef _DEBUG
EVT_MENU(Menu_Audio_Open_Dummy, FrameMain::OnOpenDummyAudio) EVT_MENU(Menu_Audio_Open_Dummy, FrameMain::OnOpenDummyAudio)
EVT_MENU(Menu_Audio_Open_Dummy_Noise, FrameMain::OnOpenDummyNoiseAudio) EVT_MENU(Menu_Audio_Open_Dummy_Noise, FrameMain::OnOpenDummyNoiseAudio)
@ -294,7 +301,7 @@ void FrameMain::OnMenuOpen (wxMenuEvent &event) {
// View menu // View menu
else if (curMenu == viewMenu) { else if (curMenu == viewMenu) {
// Flags // Flags
bool aud = audioBox->audioDisplay->loaded; bool aud = audioController->IsAudioOpen();
bool vid = VideoContext::Get()->IsLoaded() && !detachedVideo; bool vid = VideoContext::Get()->IsLoaded() && !detachedVideo;
// Set states // Set states
@ -365,12 +372,16 @@ void FrameMain::OnMenuOpen (wxMenuEvent &event) {
// Audio menu // Audio menu
else if (curMenu == audioMenu) { else if (curMenu == audioMenu) {
bool state = audioBox->loaded; bool state = audioController->IsAudioOpen();
bool vidstate = VideoContext::Get()->IsLoaded(); bool vidstate = VideoContext::Get()->IsLoaded();
MenuBar->Enable(Menu_Audio_Open_From_Video,vidstate); MenuBar->Enable(Menu_Audio_Open_From_Video,vidstate);
MenuBar->Enable(Menu_Audio_Close,state); 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 // Rebuild recent
RebuildRecentList(_T("Audio"),RecentAuds,Menu_Audio_Recent); RebuildRecentList(_T("Audio"),RecentAuds,Menu_Audio_Recent);
} }
@ -541,7 +552,7 @@ void FrameMain::OnOpenRecentKeyframes(wxCommandEvent &event) {
/// @brief Open recent audio menu entry /// @brief Open recent audio menu entry
/// @param event /// @param event
void FrameMain::OnOpenRecentAudio(wxCommandEvent &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 /// @brief Open new Window
@ -650,31 +661,40 @@ void FrameMain::OnOpenAudio (wxCommandEvent&) {
+ _("All files") + _T(" (*.*)|*.*"); + _("All files") + _T(" (*.*)|*.*");
wxString filename = wxFileSelector(_("Open audio file"),path,_T(""),_T(""),str,wxFD_OPEN | wxFD_FILE_MUST_EXIST); wxString filename = wxFileSelector(_("Open audio file"),path,_T(""),_T(""),str,wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (!filename.empty()) { if (!filename.empty()) {
LoadAudio(filename); audioController->OpenAudio(filename);
OPT_SET("Path/Last/Audio")->SetString(STD_STR(filename)); OPT_SET("Path/Last/Audio")->SetString(STD_STR(filename));
} }
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnOpenAudioFromVideo (wxCommandEvent&) { void FrameMain::OnOpenAudioFromVideo (wxCommandEvent&) {
LoadAudio(_T(""),true); audioController->OpenAudio(_T("audio-video:cache"));
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnCloseAudio (wxCommandEvent&) { 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 #ifdef _DEBUG
/// @brief DOCME /// @brief DOCME
void FrameMain::OnOpenDummyAudio (wxCommandEvent&) { void FrameMain::OnOpenDummyAudio (wxCommandEvent&) {
LoadAudio(_T("?dummy")); audioController->OpenAudio(_T("dummy-audio:silence?sr=44100&bd=16&ch=1&ln=396900000"));
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnOpenDummyNoiseAudio (wxCommandEvent&) { void FrameMain::OnOpenDummyNoiseAudio (wxCommandEvent&) {
LoadAudio(_T("?noise")); audioController->OpenAudio(_T("dummy-audio:noise?sr=44100&bd=16&ch=1&ln=396900000"));
} }
#endif #endif
@ -1245,7 +1265,7 @@ void FrameMain::OnSetARCustom (wxCommandEvent &) {
void FrameMain::OnCloseWindow (wxCloseEvent &event) { void FrameMain::OnCloseWindow (wxCloseEvent &event) {
// Stop audio and video // Stop audio and video
VideoContext::Get()->Stop(); VideoContext::Get()->Stop();
audioBox->audioDisplay->Stop(); audioController->Stop();
// Ask user if he wants to save first // Ask user if he wants to save first
bool canVeto = event.CanVeto(); bool canVeto = event.CanVeto();
@ -1465,6 +1485,7 @@ void FrameMain::OnChooseLanguage (wxCommandEvent &) {
/// @brief View standard /// @brief View standard
void FrameMain::OnViewStandard (wxCommandEvent &) { void FrameMain::OnViewStandard (wxCommandEvent &) {
if (!audioController->IsAudioOpen() || !VideoContext::Get()->IsLoaded()) return;
SetDisplayMode(1,1); SetDisplayMode(1,1);
} }
@ -1475,6 +1496,7 @@ void FrameMain::OnViewVideo (wxCommandEvent &) {
/// @brief View audio /// @brief View audio
void FrameMain::OnViewAudio (wxCommandEvent &) { void FrameMain::OnViewAudio (wxCommandEvent &) {
if (!audioController->IsAudioOpen()) return;
SetDisplayMode(0,1); SetDisplayMode(0,1);
} }
@ -1485,82 +1507,132 @@ void FrameMain::OnViewSubs (wxCommandEvent &) {
/// @brief Medusa shortcuts /// @brief Medusa shortcuts
void FrameMain::OnMedusaPlay(wxCommandEvent &) { void FrameMain::OnMedusaPlay(wxCommandEvent &) {
int start=0,end=0; audioController->PlayPrimaryRange();
audioBox->audioDisplay->GetTimesSelection(start,end);
audioBox->audioDisplay->Play(start,end);
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnMedusaStop(wxCommandEvent &) { void FrameMain::OnMedusaStop(wxCommandEvent &) {
// Playing, stop // Playing, stop
if (audioBox->audioDisplay->player->IsPlaying()) { if (audioController->IsPlaying()) {
audioBox->audioDisplay->Stop(); audioController->Stop();
audioBox->audioDisplay->Refresh();
} }
// Otherwise, play the last 500 ms // Otherwise, play the last 500 ms
else { else {
int start=0,end=0; AudioController::SampleRange sel(audioController->GetPrimaryPlaybackRange());
audioBox->audioDisplay->GetTimesSelection(start,end); audioController->PlayRange(AudioController::SampleRange(
audioBox->audioDisplay->Play(end-500,end); sel.end() - audioController->SamplesFromMilliseconds(500),
sel.end()));;
} }
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnMedusaShiftStartForward(wxCommandEvent &) { void FrameMain::OnMedusaShiftStartForward(wxCommandEvent &) {
audioBox->audioDisplay->curStartMS += 10; AudioController::SampleRange newsel(
audioBox->audioDisplay->Update(); audioController->GetPrimaryPlaybackRange(),
audioBox->audioDisplay->wxWindow::Update(); audioController->SamplesFromMilliseconds(10),
0);
/// @todo Make this use the timing controller instead
//audioController->SetSelection(newsel);
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnMedusaShiftStartBack(wxCommandEvent &) { void FrameMain::OnMedusaShiftStartBack(wxCommandEvent &) {
audioBox->audioDisplay->curStartMS -= 10; AudioController::SampleRange newsel(
audioBox->audioDisplay->Update(); audioController->GetPrimaryPlaybackRange(),
audioBox->audioDisplay->wxWindow::Update(); -audioController->SamplesFromMilliseconds(10),
0);
/// @todo Make this use the timing controller instead
//audioController->SetSelection(newsel);
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnMedusaShiftEndForward(wxCommandEvent &) { void FrameMain::OnMedusaShiftEndForward(wxCommandEvent &) {
audioBox->audioDisplay->curEndMS += 10; AudioController::SampleRange newsel(
audioBox->audioDisplay->Update(); audioController->GetPrimaryPlaybackRange(),
audioBox->audioDisplay->wxWindow::Update(); 0,
audioController->SamplesFromMilliseconds(10));
/// @todo Make this use the timing controller instead
//audioController->SetSelection(newsel);
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnMedusaShiftEndBack(wxCommandEvent &) { void FrameMain::OnMedusaShiftEndBack(wxCommandEvent &) {
audioBox->audioDisplay->curEndMS -= 10; AudioController::SampleRange newsel(
audioBox->audioDisplay->Update(); audioController->GetPrimaryPlaybackRange(),
audioBox->audioDisplay->wxWindow::Update(); 0,
-audioController->SamplesFromMilliseconds(10));
/// @todo Make this use the timing controller instead
//audioController->SetSelection(newsel);
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnMedusaPlayBefore(wxCommandEvent &) { void FrameMain::OnMedusaPlayBefore(wxCommandEvent &) {
int start=0,end=0; AudioController::SampleRange sel(audioController->GetPrimaryPlaybackRange());
audioBox->audioDisplay->GetTimesSelection(start,end); audioController->PlayRange(AudioController::SampleRange(
audioBox->audioDisplay->Play(start-500,start); sel.begin() - audioController->SamplesFromMilliseconds(500),
sel.begin()));;
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnMedusaPlayAfter(wxCommandEvent &) { void FrameMain::OnMedusaPlayAfter(wxCommandEvent &) {
int start=0,end=0; AudioController::SampleRange sel(audioController->GetPrimaryPlaybackRange());
audioBox->audioDisplay->GetTimesSelection(start,end); audioController->PlayRange(AudioController::SampleRange(
audioBox->audioDisplay->Play(end,end+500); sel.end(),
sel.end() + audioController->SamplesFromMilliseconds(500)));;
} }
/// @brief DOCME /// @brief DOCME
void FrameMain::OnMedusaNext(wxCommandEvent &) { 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 /// @brief DOCME
void FrameMain::OnMedusaPrev(wxCommandEvent &) { 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 /// @brief DOCME
void FrameMain::OnMedusaEnter(wxCommandEvent &) { 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() { void FrameMain::OnSubtitlesFileChanged() {
@ -1570,3 +1642,4 @@ void FrameMain::OnSubtitlesFileChanged() {
UpdateTitle(); UpdateTitle();
} }

View file

@ -79,17 +79,15 @@ public:
virtual ~AudioProvider(); virtual ~AudioProvider();
virtual wxString GetFilename() const { return filename; }; virtual wxString GetFilename() const { return filename; };
virtual void GetAudio(void *buf, int64_t start, int64_t count)=0; 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); void GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const;
int64_t GetNumSamples() const { return num_samples; } int64_t GetNumSamples() const { return num_samples; }
int GetSampleRate() const { return sample_rate; } int GetSampleRate() const { return sample_rate; }
int GetBytesPerSample() const { return bytes_per_sample; } int GetBytesPerSample() const { return bytes_per_sample; }
int GetChannels() const { return channels; } int GetChannels() const { return channels; }
virtual bool AreSamplesNativeEndian() const = 0; 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? /// @brief Does this provider benefit from external caching?
virtual bool NeedsCache() const { return false; } virtual bool NeedsCache() const { return false; }
}; };

View file

@ -54,8 +54,9 @@
#include "ass_export_filter.h" #include "ass_export_filter.h"
#include "ass_file.h" #include "ass_file.h"
#include "ass_time.h" #include "ass_time.h"
#include "selection_controller.h"
#include "audio_controller.h"
#include "audio_box.h" #include "audio_box.h"
#include "audio_display.h"
#ifdef WITH_AUTOMATION #ifdef WITH_AUTOMATION
#include "auto4_base.h" #include "auto4_base.h"
#endif #endif
@ -68,7 +69,6 @@
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "plugin_manager.h" #include "plugin_manager.h"
#include "standard_paths.h" #include "standard_paths.h"
#include "subs_grid.h"
#include "subtitle_format.h" #include "subtitle_format.h"
#include "version.h" #include "version.h"
#include "video_context.h" #include "video_context.h"
@ -551,13 +551,14 @@ END_EVENT_TABLE()
/// @param event /// @param event
/// ///
void AegisubApp::OnMouseWheel(wxMouseEvent &event) { void AegisubApp::OnMouseWheel(wxMouseEvent &event) {
if (event.WasProcessed()) return;
wxPoint pt; wxPoint pt;
wxWindow *target = wxFindWindowAtPointer(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); if (target->IsShownOnScreen()) target->GetEventHandler()->ProcessEvent(event);
else event.Skip(); else event.Skip();
} }
else event.Skip(); else event.Skip();*/
} }

View file

@ -140,6 +140,4 @@
#pragma comment(lib, "libass.lib") #pragma comment(lib, "libass.lib")
#endif #endif
#endif // VisualC #endif // VisualC

View file

@ -58,12 +58,13 @@
#include "ass_file.h" #include "ass_file.h"
#include "ass_override.h" #include "ass_override.h"
#include "ass_style.h" #include "ass_style.h"
#include "audio_display.h" #include "audio_controller.h"
#include "dialog_colorpicker.h" #include "dialog_colorpicker.h"
#include "dialog_search_replace.h" #include "dialog_search_replace.h"
#include "frame_main.h" #include "frame_main.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "subs_edit_ctrl.h" #include "subs_edit_ctrl.h"
#include "subs_grid.h" #include "subs_grid.h"

View file

@ -55,6 +55,7 @@ class wxSpinCtrl;
class wxStyledTextCtrl; class wxStyledTextCtrl;
class wxStyledTextEvent; class wxStyledTextEvent;
class wxTextCtrl; class wxTextCtrl;
class AudioController;
/// DOCME /// DOCME
/// @class SubsEditBox /// @class SubsEditBox

View file

@ -49,6 +49,7 @@
#include "compat.h" #include "compat.h"
#include "main.h" #include "main.h"
#include "include/aegisub/spellchecker.h" #include "include/aegisub/spellchecker.h"
#include "selection_controller.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "subs_edit_ctrl.h" #include "subs_edit_ctrl.h"
#include "subs_grid.h" #include "subs_grid.h"

View file

@ -51,8 +51,10 @@
#include "ass_karaoke.h" #include "ass_karaoke.h"
#include "ass_override.h" #include "ass_override.h"
#include "ass_style.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_box.h"
#include "audio_display.h"
#include "charset_conv.h" #include "charset_conv.h"
#include "dialog_paste_over.h" #include "dialog_paste_over.h"
#include "frame_main.h" #include "frame_main.h"
@ -204,7 +206,7 @@ void SubtitlesGrid::OnPopupMenu(bool alternate) {
menu.AppendSeparator(); menu.AppendSeparator();
//Make audio clip //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.Append(MENU_AUDIOCLIP,_("Create audio clip"),_("Create an audio clip of the selected line"))->Enable(state);
menu.AppendSeparator(); menu.AppendSeparator();
@ -646,8 +648,8 @@ void SubtitlesGrid::OnRecombine(wxCommandEvent &) {
/// @brief Export audio clip of line /// @brief Export audio clip of line
void SubtitlesGrid::OnAudioClip(wxCommandEvent &) { void SubtitlesGrid::OnAudioClip(wxCommandEvent &) {
int64_t num_samples,start=0,end=0,temp; int64_t num_samples,start=0,end=0,temp;
AudioDisplay *audioDisplay = parentFrame->audioBox->audioDisplay; AudioController *audioController = parentFrame->audioController;
AudioProvider *provider = audioDisplay->provider; const AudioProvider *provider = audioController->GetAudioProvider();
AssDialogue *cur; AssDialogue *cur;
wxArrayInt sel = GetSelection(); wxArrayInt sel = GetSelection();
@ -656,9 +658,9 @@ void SubtitlesGrid::OnAudioClip(wxCommandEvent &) {
for(unsigned int i=0;i!=sel.GetCount();i++) { for(unsigned int i=0;i!=sel.GetCount();i++) {
cur = GetDialogue(sel[i]); cur = GetDialogue(sel[i]);
temp = audioDisplay->GetSampleAtMS(cur->Start.GetMS()); temp = audioController->SamplesFromMilliseconds(cur->Start.GetMS());
start = (i==0||temp<start)?temp:start; 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; end = (i==0||temp>end)?temp:end;
} }

View file

@ -50,6 +50,7 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_controller.h"
#include "ass_file.h" #include "ass_file.h"
#include "dialog_progress.h" #include "dialog_progress.h"
#include "frame_main.h" #include "frame_main.h"

View file

@ -70,6 +70,10 @@ int AegiStringToFix(const wxString &str,size_t decimalPlaces,int start=0,int end
wxIcon BitmapToIcon(wxBitmap bmp); wxIcon BitmapToIcon(wxBitmap bmp);
void RestartAegisub(); void RestartAegisub();
/// @brief Templated abs() function
template <typename T> T tabs(T x) { return x < 0 ? -x : x; }
#ifndef MIN #ifndef MIN
#define MIN(a,b) ((a)<(b))?(a):(b) #define MIN(a,b) ((a)<(b))?(a):(b)
#endif #endif

View file

@ -45,10 +45,12 @@
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "audio_controller.h"
#include "frame_main.h" #include "frame_main.h"
#include "help_button.h" #include "help_button.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "toggle_bitmap.h" #include "toggle_bitmap.h"

View file

@ -53,11 +53,13 @@
#include <GL/glu.h> #include <GL/glu.h>
#endif #endif
#include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "ass_style.h" #include "ass_style.h"
#include "ass_time.h" #include "ass_time.h"
#include "audio_display.h" #include "audio_controller.h"
#include "compat.h" #include "compat.h"
#include "include/aegisub/audio_player.h" #include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
@ -67,6 +69,7 @@
#include "main.h" #include "main.h"
#include "mkv_wrap.h" #include "mkv_wrap.h"
#include "standard_paths.h" #include "standard_paths.h"
#include "selection_controller.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "threaded_frame_source.h" #include "threaded_frame_source.h"
@ -119,10 +122,6 @@ VideoContext::VideoContext()
} }
VideoContext::~VideoContext () { VideoContext::~VideoContext () {
if (audio && audio->temporary) {
delete audio->provider;
delete audio->player;
}
} }
VideoContext *VideoContext::Get() { VideoContext *VideoContext::Get() {
@ -135,15 +134,7 @@ void VideoContext::Reset() {
keyFrames.clear(); keyFrames.clear();
videoFPS = agi::vfr::Framerate(); videoFPS = agi::vfr::Framerate();
keyframesRevision++;
// Remove temporary audio provider
if (audio && audio->temporary) {
delete audio->provider;
audio->provider = NULL;
delete audio->player;
audio->player = NULL;
audio->temporary = false;
}
// Remove video data // Remove video data
frame_n = 0; frame_n = 0;
@ -316,8 +307,11 @@ void VideoContext::PlayNextFrame() {
int thisFrame = frame_n; int thisFrame = frame_n;
JumpToFrame(frame_n + 1); JumpToFrame(frame_n + 1);
// Start playing audio // Start playing audio
if (playAudioOnStep->GetBool()) if (playAudioOnStep->GetBool()) {
audio->Play(TimeAtFrame(thisFrame),TimeAtFrame(thisFrame + 1)); audio->PlayRange(AudioController::SampleRange(
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame)),
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame + 1))));
}
} }
void VideoContext::PlayPrevFrame() { void VideoContext::PlayPrevFrame() {
@ -327,8 +321,11 @@ void VideoContext::PlayPrevFrame() {
int thisFrame = frame_n; int thisFrame = frame_n;
JumpToFrame(frame_n -1); JumpToFrame(frame_n -1);
// Start playing audio // Start playing audio
if (playAudioOnStep->GetBool()) if (playAudioOnStep->GetBool()) {
audio->Play(TimeAtFrame(thisFrame - 1),TimeAtFrame(thisFrame)); audio->PlayRange(AudioController::SampleRange(
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame - 1)),
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame))));
}
} }
void VideoContext::Play() { void VideoContext::Play() {
@ -342,7 +339,7 @@ void VideoContext::Play() {
endFrame = -1; endFrame = -1;
// Start playing audio // 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. //audio->Play will override this if we put it before, so put it after.
isPlaying = true; isPlaying = true;
@ -358,7 +355,9 @@ void VideoContext::PlayLine() {
if (!curline) return; if (!curline) return;
// Start playing audio // 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 // Set variables
isPlaying = true; isPlaying = true;
@ -417,7 +416,9 @@ void VideoContext::OnPlayTimer(wxTimerEvent &event) {
if (nextFrame == frame_n) return; if (nextFrame == frame_n) return;
// Next frame is before or over 2 frames ahead, so force audio resync // 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 // Jump to next frame
playNextFrame = nextFrame; playNextFrame = nextFrame;
@ -425,13 +426,13 @@ void VideoContext::OnPlayTimer(wxTimerEvent &event) {
JumpToFrame(nextFrame); JumpToFrame(nextFrame);
// Sync audio // Sync audio
if (keepAudioSync && nextFrame % 10 == 0 && audio && audio->provider && audio->player) { if (keepAudioSync && nextFrame % 10 == 0 && audio->IsPlaying()) {
int64_t audPos = audio->GetSampleAtMS(TimeAtFrame(nextFrame)); int64_t audPos = audio->SamplesFromMilliseconds(TimeAtFrame(nextFrame));
int64_t curPos = audio->player->GetCurrentPosition(); int64_t curPos = audio->GetPlaybackPosition();
int delta = int(audPos-curPos); int delta = int(audPos-curPos);
if (delta < 0) delta = -delta; if (delta < 0) delta = -delta;
int maxDelta = audio->provider->GetSampleRate(); int maxDelta = audio->SamplesFromMilliseconds(1000);
if (delta > maxDelta) audio->player->SetCurrentPosition(audPos); if (delta > maxDelta) audio->ResyncPlaybackPosition(audPos);
} }
} }
@ -468,10 +469,12 @@ void VideoContext::LoadKeyframes(wxString filename) {
catch (...) { catch (...) {
wxMessageBox(_T("Unknown error"), _T("Error opening keyframes file"), wxOK | wxICON_ERROR, NULL); wxMessageBox(_T("Unknown error"), _T("Error opening keyframes file"), wxOK | wxICON_ERROR, NULL);
} }
keyframesRevision++;
} }
void VideoContext::SaveKeyframes(wxString filename) { void VideoContext::SaveKeyframes(wxString filename) {
KeyFrameFile::Save(filename, GetKeyFrames()); KeyFrameFile::Save(filename, GetKeyFrames());
keyframesRevision++;
} }
void VideoContext::CloseKeyframes() { void VideoContext::CloseKeyframes() {
@ -483,6 +486,7 @@ void VideoContext::CloseKeyframes() {
keyFrames.clear(); keyFrames.clear();
} }
KeyframesOpen(keyFrames); KeyframesOpen(keyFrames);
keyframesRevision++;
} }
void VideoContext::LoadTimecodes(wxString filename) { void VideoContext::LoadTimecodes(wxString filename) {

View file

@ -62,6 +62,7 @@ class SubtitlesProviderErrorEvent;
class ThreadedFrameSource; class ThreadedFrameSource;
class VideoProvider; class VideoProvider;
class VideoProviderErrorEvent; class VideoProviderErrorEvent;
class AudioController;
namespace agi { namespace agi {
class OptionValue; class OptionValue;
@ -101,6 +102,10 @@ private:
/// DOCME /// DOCME
wxString keyFramesFilename; wxString keyFramesFilename;
/// Revision counter for keyframes, when the set of keyframes is changed this number changes
int keyframesRevision;
/// DOCME /// DOCME
wxMutex playMutex; wxMutex playMutex;
@ -161,8 +166,8 @@ public:
/// File name of currently open video, if any /// File name of currently open video, if any
wxString videoName; wxString videoName;
/// DOCME /// The audio controller for this video context
AudioDisplay *audio; AudioController *audio;
const agi::vfr::Framerate &VFR_Input; const agi::vfr::Framerate &VFR_Input;
const agi::vfr::Framerate &VFR_Output; const agi::vfr::Framerate &VFR_Output;
@ -265,6 +270,7 @@ public:
void CloseKeyframes(); void CloseKeyframes();
bool OverKeyFramesLoaded() const { return !keyFramesFilename.empty(); } bool OverKeyFramesLoaded() const { return !keyFramesFilename.empty(); }
bool KeyFramesLoaded() const { return !keyFrames.empty(); } bool KeyFramesLoaded() const { return !keyFrames.empty(); }
int GetKeyframesRevision() const { return keyframesRevision; }
wxString GetTimecodesName() const { return ovrTimecodeFile; } wxString GetTimecodesName() const { return ovrTimecodeFile; }
void LoadTimecodes(wxString filename); void LoadTimecodes(wxString filename);

View file

@ -57,6 +57,7 @@
#endif #endif
#include "video_display.h" #include "video_display.h"
#include "selection_controller.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"

View file

@ -43,6 +43,7 @@
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "main.h" #include "main.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "selection_controller.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "utils.h" #include "utils.h"
#include "video_context.h" #include "video_context.h"

View file

@ -46,6 +46,7 @@
#include "ass_style.h" #include "ass_style.h"
#include "ass_time.h" #include "ass_time.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "subs_edit_box.h" #include "subs_edit_box.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "utils.h" #include "utils.h"

View file

@ -53,6 +53,7 @@
#endif #endif
#include "config.h" #include "config.h"
#include "selection_controller.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"