Originally committed to SVN as r2.
548
automation/automation-lua.txt
Normal file
|
@ -0,0 +1,548 @@
|
|||
Aegisub Automation documentation
|
||||
Version 3
|
||||
Copyright 2005 Niels Martin Hansen.
|
||||
|
||||
---
|
||||
|
||||
This document describes version 3 of the automation system used in Aegisub.
|
||||
The automation system uses the Lua language for scripting engine.
|
||||
See <http://www.lua.org/> for more information.
|
||||
|
||||
---
|
||||
|
||||
What is Automation?
|
||||
|
||||
Aegisub Automation is a scripting system designed to automate many processing
|
||||
tasks of ASS subtitles, instead of using tedious, error-prone manual
|
||||
processing. The primary purpose is creating karaoke effects for anime fansubs.
|
||||
|
||||
The Automation script is given the complete subtitle data from a subtitle
|
||||
file, in a format suited for creating special effects.
|
||||
The script will return a complete substiture for the original subtitle
|
||||
data, allowing full freedom of processing.
|
||||
|
||||
A number of helper functions are provided, to aid in finding errors in
|
||||
scripts, as well as retrieve further data about the subtitles, needed to
|
||||
created advanced effects.
|
||||
|
||||
---
|
||||
|
||||
Scripts, files, functions:
|
||||
|
||||
A script is a file containing Lua code. One file can define just one script,
|
||||
but several scripts can share code by the help of including other files.
|
||||
|
||||
All scripts are run in a separate interpreter, and as such don't have any way
|
||||
of interacting with other loaded scripts.
|
||||
|
||||
All strings in a script should be in UTF-8 encoding, without byte-order mark.
|
||||
All strings input to a script are encoded as UTF-8.
|
||||
Script files may start with an UTF-8 BOM (byte-order mark) or not, but this
|
||||
is currently not well tested.
|
||||
|
||||
A script must define certain global variables:
|
||||
|
||||
version
|
||||
Number. Version of the scripting interface used.
|
||||
The version described in this file is 3.
|
||||
To comply with version 3, version must be: 3 <= version < 4
|
||||
kind
|
||||
String. Not used, but mandatory. Set it to "basic_ass" for now.
|
||||
name
|
||||
String. Displayed name of the script.
|
||||
description
|
||||
String. Optional. Long description of the script.
|
||||
process_lines
|
||||
Function. The main script function.
|
||||
configuration
|
||||
Table. Optional. Configuration options for the script.
|
||||
|
||||
The functions are described in detail in the following.
|
||||
|
||||
The script may define further global variables, but they do not have any
|
||||
special meaning. Be aware, however, that later versions of the scripting
|
||||
system might define further global variables with special meanings, so be
|
||||
careful choosing names for private use globals.
|
||||
It's recommended to prefix private global variables with "p_"; the scripting
|
||||
system will never assign special meanings to global variables with that
|
||||
prefix.
|
||||
|
||||
The scripting system defines a global variable with name "aegisub", which
|
||||
contains important values. You should not hide the "aegisub" variable.
|
||||
|
||||
---
|
||||
|
||||
The processing function:
|
||||
|
||||
The processing function is the heart of the script.
|
||||
|
||||
It takes as input some meta-information about the subtitles, the styles
|
||||
used in the subtitles, as well as the actual subtitle data to process.
|
||||
|
||||
The output is a set of subtitle data in the same format as the input.
|
||||
The output subtitle data will be used as a complete replacement of the
|
||||
input data.
|
||||
Future versions might allow modifying style data and meta data as well.
|
||||
|
||||
|
||||
The processing function is defined as follows:
|
||||
|
||||
function process_lines(meta, styles, lines, config)
|
||||
|
||||
The arguments are:
|
||||
|
||||
@meta
|
||||
Table. Meta information about the script. (Script Info section.)
|
||||
@styles
|
||||
Table. Style definitions. (V4+ Styles section.)
|
||||
@lines
|
||||
Table. Subtitle events. (Events section.)
|
||||
@config
|
||||
Table. Values set for the configuration options provided. If no
|
||||
configuration options were provided, this will be an empty table.
|
||||
|
||||
Returns: One value.
|
||||
This value must be a table, using the same format as @lines.
|
||||
Note that the indexes in the return value may be either zero-based or
|
||||
one-based, to allow for greater compatibility. You are encouraged to
|
||||
use one-based indexes.
|
||||
|
||||
|
||||
Description of @meta:
|
||||
|
||||
This is a table with the following keys:
|
||||
|
||||
res_x
|
||||
Horizontal resolution of the script.
|
||||
res_y
|
||||
Vertical resolution of the script.
|
||||
|
||||
|
||||
Description of @styles:
|
||||
|
||||
This is a table with the following keys:
|
||||
|
||||
-1
|
||||
Number. The amount of styles defined, called "n".
|
||||
0 -> n-1
|
||||
Table. The actual style definitions.
|
||||
<string "name">
|
||||
Table. The style definition with the specified name.
|
||||
|
||||
The key -1 is used for count rather than "n", since one might have a style
|
||||
definition with the name "n".
|
||||
|
||||
A style definition is a table with the following keys:
|
||||
|
||||
name
|
||||
String. Name of the style.
|
||||
fontname
|
||||
String. Name of the font used.
|
||||
fontsize
|
||||
Number. Size of the font used.
|
||||
color1
|
||||
String. Primary color.
|
||||
All color fields use raw hexadecimal format, that is, no special characters
|
||||
before or after the hex string.
|
||||
color2
|
||||
String. Secondary color.
|
||||
color3
|
||||
String. Outline color.
|
||||
color4
|
||||
String. Shadow color.
|
||||
bold
|
||||
Boolean. Bold text or not.
|
||||
italic
|
||||
Boolean. Italic text or not.
|
||||
underline
|
||||
Boolean. Underlined text or not.
|
||||
strikeout
|
||||
Boolean. Striked-out text or not.
|
||||
scale_x
|
||||
Number. Horizontal scale.
|
||||
scale_y
|
||||
Number. Vertical scale.
|
||||
spacing
|
||||
Number. Spacing between characters.
|
||||
angle
|
||||
Number. Rotation angle in degrees.
|
||||
borderstyle
|
||||
Number. 1=Outline + drop shadow, 3=Opaque box (not really used???)
|
||||
outline
|
||||
Number. Thickness of outline.
|
||||
shadow
|
||||
Number. Distance of shadow from text.
|
||||
align
|
||||
Number. Numpad style alignment.
|
||||
margin_l
|
||||
Number. Left margin in pixels.
|
||||
margin_r
|
||||
Number. Right margin in pixels.
|
||||
margin_v
|
||||
Number. Vertical margin in pixels.
|
||||
encoding
|
||||
Number. Font encoding used.
|
||||
|
||||
|
||||
Description of @lines:
|
||||
|
||||
This is a table with the following keys:
|
||||
|
||||
n
|
||||
Number. The amount of lines.
|
||||
0 -> n-1
|
||||
Table. The actual lines.
|
||||
|
||||
A line is a table with the following key:
|
||||
|
||||
kind
|
||||
String. Can be "blank", "scomment", "comment" or "dialogue".
|
||||
|
||||
The keys otherwise defined depends on the kind of the line.
|
||||
|
||||
If the kind if "blank", no further fields are defined.
|
||||
|
||||
If the kind is "scomment", the line is a "semicolon comment", and the
|
||||
following key is defined:
|
||||
|
||||
text
|
||||
String. Text following the semicolon until end of line. EOL not included.
|
||||
|
||||
If the kind is "comment" or "dialogue", the line is either a Comment: or
|
||||
a Dialogue: line. In both cases, the following keys are defined:
|
||||
|
||||
layer
|
||||
Number.
|
||||
start_time
|
||||
Number. Start time of line in centiseconds.
|
||||
(Might change to userdata later.)
|
||||
end_time
|
||||
Number. End time of line in centiseconds.
|
||||
(Might change to userdata later.)
|
||||
style
|
||||
String. Style used for this line.
|
||||
name
|
||||
String. Character name speaking this line.
|
||||
margin_l
|
||||
Number. Left margin override, in pixels. (0=no override)
|
||||
margin_r
|
||||
Number. Right margin override, in pixels. (0=no override)
|
||||
margin_v
|
||||
Number. Right margin override, in pixels. (0=no override)
|
||||
effect
|
||||
String. Effect to apply to the line. (No error checking done.)
|
||||
text
|
||||
String. Text to display.
|
||||
text_stripped
|
||||
String. Same as text, but stripped for all tags, and newline/hardspace
|
||||
tags are converted to real newlines/spaces. Non-hard spaces at the start/
|
||||
end of lines are stripped.
|
||||
karaoke
|
||||
Table. Line split into karaoke syllables. See below for more information.
|
||||
|
||||
Note about output:
|
||||
Neither text_stripped nor karaoke are used when the results are parsed, they
|
||||
are only passed to simplify processing. You should set text to the final text
|
||||
of the line, you want in the output.
|
||||
It is encouraged to entirely leave text_stripped and karaoke out of the
|
||||
tables in the result.
|
||||
|
||||
|
||||
Karaoke tables:
|
||||
|
||||
A karaoke table has a number of values indexed by numbers. Each value
|
||||
represents a karaoke syllable.
|
||||
Key "n" holds the number of syllables. The syllables can be accessed from
|
||||
index 0 and up. The syllables are indexed chronologically.
|
||||
A karaoke table always has at least one syllable. The first syllable (index
|
||||
0) contains all data before the first timed syllable.
|
||||
Each syllable is a table containing the following keys:
|
||||
|
||||
duration
|
||||
Number. Duration of the syllable in centiseconds. Always 0 for first
|
||||
syllable.
|
||||
kind
|
||||
String. "Kind" of the karaoke, the name of the tag. For a \k type syllable,
|
||||
kind is "k", for a \kf syllable kind is "kf". Freeform tags can be used, as
|
||||
long as they start with the letter "k" or "K".
|
||||
Always the empty string ("") for the first syllable.
|
||||
text
|
||||
String. Text of the syllable. This includes formatting tags.
|
||||
For the first syllable, this contains everything before the first karaoke
|
||||
timing tag.
|
||||
text_stripped
|
||||
String. Same as text, but with all formatting tags stripped.
|
||||
|
||||
|
||||
Description of @config:
|
||||
|
||||
This is a table. The keys are the names for the options defined in the global
|
||||
"configuration" table. The values are the values provided by the user.
|
||||
|
||||
---
|
||||
|
||||
Script configuration:
|
||||
|
||||
An automation script can provide a configuration set, allowing the user to
|
||||
set certain options before the script is called.
|
||||
|
||||
This is performed through the "configuration" value.
|
||||
|
||||
Scripts can define configuration options of the following types:
|
||||
|
||||
label
|
||||
A static, non-editable text displayed to the user. (Useful for adding
|
||||
additional explanations for some options.)
|
||||
text
|
||||
Freeform text entry.
|
||||
int
|
||||
Integer numbers. A range of valid values can be specified.
|
||||
float
|
||||
Any kind of number. A range of valid values can be specified.
|
||||
bool
|
||||
A boolean on/off value.
|
||||
colour
|
||||
An RGB colour value.
|
||||
style
|
||||
The name of a style defined in the subtitles.
|
||||
|
||||
|
||||
The "configuration" table:
|
||||
|
||||
The "configuration" table contains a number of values indexed by numbers.
|
||||
Each value defines a configuration option.
|
||||
The configuration options must be in keys numbered from 1 to n, where n
|
||||
is the number of options. No "n" key is required.
|
||||
The configuration options will be presented to the user in the order defined.
|
||||
Each configuration option is a table containing the following keys:
|
||||
|
||||
name
|
||||
String. The internal name used to refer to the configuration option.
|
||||
Must not contain the colon or pipe characters. (ASCII 58 and 124.)
|
||||
kind
|
||||
String. One of "label", "text", "int", "float", "bool", "colour" and
|
||||
"style". Defines what kind of option this is.
|
||||
label
|
||||
String. Name of the option, presented to the user. Should be very short.
|
||||
hint
|
||||
String. Longer description of the option, presented to the user as a
|
||||
tooltip. Ignored for "label" kind options.
|
||||
min
|
||||
Number. Optional. Lowest value allowed. Only used for "int" and "float" kinds.
|
||||
max
|
||||
Number. Optional. Highest value allowed. Only used for "int" and "float" kinds.
|
||||
default.
|
||||
Type depends on "kind". The value given to this configuration option before
|
||||
the user has entered another value. Ignored for "label" kind options.
|
||||
|
||||
Data types for the different kinds:
|
||||
|
||||
label
|
||||
None. A label doesn't have a value, and won't be present in the @config
|
||||
table in the process_lines function.
|
||||
text
|
||||
String. You might want to do some kind of extra validation on text input, as
|
||||
it might be anything.
|
||||
int
|
||||
Number. Guaranteed to always be integer.
|
||||
float
|
||||
Number. Can be integer or not.
|
||||
bool
|
||||
Boolean.
|
||||
colour
|
||||
String. An ASS hex colourcode in "&HBBGGRR&" format.
|
||||
style
|
||||
String. The name of the style. The style can't be guaranteed to exist, as
|
||||
another export filter in Aegisub might have removed it before your script
|
||||
gets to run.
|
||||
|
||||
---
|
||||
|
||||
Script environment and registration:
|
||||
|
||||
A script is assigned to a subtitle file by adding it to the
|
||||
"Automation Scripts" extra header in the [Script Info] section. This header
|
||||
contains a list of script filenames, separated by pipe characters. Example:
|
||||
|
||||
Automation Scripts: test1.lua|test2.lua
|
||||
|
||||
All scripts run in their own separate interpreter. This means there is no
|
||||
risk of name collisions, though also that scripts can't easily share code.
|
||||
|
||||
If you need to share code between several scripts, you should create a
|
||||
subdirectory to the script directory, and place include files there.
|
||||
|
||||
|
||||
The settings for the configuration options for a script are stored in the ASS
|
||||
file in the following way:
|
||||
|
||||
Each script gets one line for configuration, named "Automation Settings" plus
|
||||
a space plus the filename of the script. The filename used is stripped of all
|
||||
path specifiers. (Use unique filenames for your scripts!)
|
||||
|
||||
The value of the line is a pipe-separated list of "name:value" pairs. The name
|
||||
is the internal name given by the "name" key. It is not mangled in any way.
|
||||
|
||||
The way the value is stored depends on the kind of the option.
|
||||
|
||||
label
|
||||
Not stored.
|
||||
text
|
||||
The string is stored in an URL-encoding like manner. Some unsafe characters
|
||||
are replaced with escape-sequences of the form #xx, where xx is a two-digit
|
||||
hexadecimal number for the ASCII code of the escaped character. Only ASCII-
|
||||
characters can be escaped this way, Unicode characters aren't supported.
|
||||
int
|
||||
Stored in ASCII base 10 without any group separators.
|
||||
float
|
||||
Stored in exponential notation, using ASCII base 10. (As the %e sprintf()
|
||||
argument.)
|
||||
bool
|
||||
True is stored as "1", false as "0".
|
||||
colour
|
||||
Stored as in ASS hex format without any mangling.
|
||||
style
|
||||
Stored in the same manner as "text" kind options.
|
||||
|
||||
---
|
||||
|
||||
Helper functions:
|
||||
|
||||
There is a gloabl variable names "aegisub". This is a table containing
|
||||
various helper functions.
|
||||
|
||||
The following helper functions are defined:
|
||||
|
||||
|
||||
function aegisub.set_status(text)
|
||||
|
||||
Sets the current status-message. (Used for progress-reporting.)
|
||||
|
||||
@text
|
||||
String. The status message.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
|
||||
function aegisub.output_debug(text)
|
||||
|
||||
Output text to a debug console.
|
||||
|
||||
@text
|
||||
String. The text to output.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
|
||||
function aegisub.colorstring_to_rgb(colorstring)
|
||||
|
||||
Convert an ASS color-string to a set of RGB values.
|
||||
|
||||
@colorstring
|
||||
String. The color-string to convert.
|
||||
|
||||
Returns: Four values, all numbers, being the color components in this
|
||||
order: Red, Green, Blue, Alpha-channel
|
||||
|
||||
|
||||
function aegisub.report_progress(percent)
|
||||
|
||||
Report the progress of the processing.
|
||||
|
||||
@percent
|
||||
Number. How much of the data have been processed so far. (Percent)
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
|
||||
function aegisub.text_extents(style, text)
|
||||
|
||||
Calculate the on-screen pixel size of the given text using the given style.
|
||||
|
||||
@style
|
||||
Table. A single style definition like those passed to process_lines.
|
||||
@text
|
||||
String. The text to calculate the extents for. This should not contain
|
||||
formatting codes, as they will be treated as part of the text.
|
||||
|
||||
Returns 4 values:
|
||||
1: Number. Width of the text, in pixels.
|
||||
2: Number. Height of the text, in pixels.
|
||||
3: Number. Descent of the text, in pixels.
|
||||
4: Number. External leading for the text, in pixels.
|
||||
|
||||
Short description of the values returned:
|
||||
Width: The X advance of the text, how much the "cursor" moves forward when
|
||||
this text is rendered.
|
||||
Height: The total height of the text, including internal leading.
|
||||
Descent: How far below the baseline a character can extend. The ascent of
|
||||
the text can be calculated as (height - descent).
|
||||
External leading: How much vertical spacing will be added between the lines
|
||||
of text rendered with this font. The total height of a line is
|
||||
(height + external_leading).
|
||||
|
||||
|
||||
function aegisub.frame_from_ms(ms)
|
||||
|
||||
Return the video frame-number for the given time.
|
||||
|
||||
@ms
|
||||
Number. Time in miliseconds to get the frame number for.
|
||||
|
||||
Returns: A number, the frame numer. If there is no framerate data, returns
|
||||
nil.
|
||||
|
||||
|
||||
function aegisub.ms_from_frame(frame)
|
||||
|
||||
Returns the start-time for the given video frame-number.
|
||||
|
||||
@frame
|
||||
Number. Frame-number to get start-time from.
|
||||
|
||||
Returns: A number, the start-time of the frame. If there is no framerate
|
||||
data, returns nil.
|
||||
|
||||
|
||||
function include(filename)
|
||||
|
||||
Include the named script. The script search-path defined in Aegisub will be
|
||||
used, searching for the script.
|
||||
If the filename is relative, the regular search path will not be used, but
|
||||
instead the filename will be taken as relative to the directory the current
|
||||
script is located in.
|
||||
Note that if you use include() inside an included script, relative paths
|
||||
will still be taken relative to the original script, and not relative to the
|
||||
current included script. This is a design limitation.
|
||||
The included script is loaded as an anonymous function, which is executed in
|
||||
the current environment. This has two implications: You can include files
|
||||
based on conditional statements, and even in loops, and included files can
|
||||
return values using the "return" statement.
|
||||
|
||||
@filename
|
||||
String. Name of the file to include.
|
||||
|
||||
Returns: Depends on the script included.
|
||||
|
||||
Note that if the file couldn't be found, the script will be terminated
|
||||
(or fail to load.)
|
||||
|
||||
---
|
||||
|
||||
Versions of the scripting interface:
|
||||
|
||||
Here's a quick history of the scripting interface:
|
||||
|
||||
Version 1
|
||||
Using Lua as engine.
|
||||
The scripts used in the Karaoke Effector application, avaible at:
|
||||
<http://www.jiifurusu.dk/files/programming/effector/>
|
||||
|
||||
Version 2
|
||||
Using Python as engine.
|
||||
The first draft for an Aegisub automation engine.
|
||||
Never implemented.
|
||||
|
||||
Version 3
|
||||
Using Lua as engine.
|
||||
The current version.
|
16
automation/demos/1-minimal.lua
Normal file
|
@ -0,0 +1,16 @@
|
|||
-- Aegisub Automation demonstration script
|
||||
-- Original written by Niels Martin Hansen
|
||||
-- Given into the public domain
|
||||
|
||||
version = 3
|
||||
kind = "basic_ass"
|
||||
name = "Minimal demonstration"
|
||||
description = "A very minimal demonstration of the strucrure of an Automation script."
|
||||
configuration = {}
|
||||
|
||||
function process_lines(meta, styles, lines, config)
|
||||
aegisub.report_progress(50)
|
||||
aegisub.output_debug("Test script 1 running")
|
||||
aegisub.report_progress(100)
|
||||
return lines
|
||||
end
|
43
automation/demos/2-dump.lua
Normal file
|
@ -0,0 +1,43 @@
|
|||
-- Aegisub Automation demonstration script
|
||||
-- Original written by Niels Martin Hansen
|
||||
-- Given into the public domain
|
||||
|
||||
version = 3
|
||||
kind = "basic_ass"
|
||||
name = "Reading data demonstration"
|
||||
description = "This is a demonstration of how to access the various data passed to an Automation script. It loops over the data structures provided, and dumps them to the debug console."
|
||||
configuration = {}
|
||||
|
||||
function process_lines(meta, styles, lines, config)
|
||||
out = aegisub.output_debug
|
||||
|
||||
out(string.format("Metadata: res_x=%d res_s=%d", meta.res_x, meta.res_y))
|
||||
|
||||
numstyles = styles[-1]
|
||||
out("Number of styles: " .. numstyles)
|
||||
for i = 0, numstyles-1 do
|
||||
out(string.format("Style %d: name='%s' fontname='%s'", i, styles[i].name, styles[i].fontname))
|
||||
end
|
||||
|
||||
out("Number of subtitle lines: " .. lines.n)
|
||||
for i = 0, lines.n-1 do
|
||||
aegisub.report_progress(i/lines.n*100)
|
||||
if lines[i].kind == "dialogue" then
|
||||
out(string.format("Line %d: dialogue start=%d end=%d style=%s", i, lines[i].start_time, lines[i].end_time, lines[i].style))
|
||||
out(" Text: " .. lines[i].text)
|
||||
out(" Stripped text: " .. lines[i].text_stripped)
|
||||
out(" Number of karaoke syllables: " .. lines[i].karaoke.n)
|
||||
for j = 0, lines[i].karaoke.n-1 do
|
||||
syl = lines[i].karaoke[j]
|
||||
extx, exty, extd, extl = aegisub.text_extents(styles[lines[i].style], syl.text_stripped)
|
||||
out(string.format(" Syllable %d: dur=%d kind=%s text='%s' text_stripped='%s' extx=%d exty=%d extd=%d extl=%d", j, syl.duration, syl.kind, syl.text, syl.text_stripped, extx, exty, extd, extl))
|
||||
--out(string.format(" Syllable %d: kind=%s", j, syl.kind))
|
||||
end
|
||||
else
|
||||
out(string.format("Line %d: %s", i, lines[i].kind))
|
||||
end
|
||||
end
|
||||
|
||||
-- but really just do nothing
|
||||
return lines
|
||||
end
|
16
automation/demos/3-include.lua
Normal file
|
@ -0,0 +1,16 @@
|
|||
-- Aegisub Automation demonstration script
|
||||
-- Original written by Niels Martin Hansen
|
||||
-- Given into the public domain
|
||||
|
||||
include("utils.lua")
|
||||
|
||||
name = "Include demo"
|
||||
description = "Simple demonstration of the include function."
|
||||
|
||||
version, kind, configuration = 3, 'basic_ass', {}
|
||||
|
||||
function process_lines(meta, styles, lines, config)
|
||||
lines[lines.n] = copy_line(lines[0])
|
||||
lines.n = lines.n + 1
|
||||
return lines
|
||||
end
|
55
automation/demos/4-text_extents.lua
Normal file
|
@ -0,0 +1,55 @@
|
|||
-- Aegisub Automation demonstration script
|
||||
-- Original written by Niels Martin Hansen
|
||||
-- Given into the public domain
|
||||
|
||||
include("utils.lua")
|
||||
|
||||
name = "Text placement demo"
|
||||
description = "Demonstration of the text_extents function, to do per-syllable placement of text."
|
||||
|
||||
version, kind, configuration = 3, 'basic_ass', {}
|
||||
|
||||
function process_lines(meta, styles, lines, config)
|
||||
-- Prepare local variables
|
||||
local output = { n=0 }
|
||||
-- Loop through every line
|
||||
for i = 0, lines.n-1 do
|
||||
aegisub.report_progress(i/lines.n*100)
|
||||
-- Only process dialogue lines
|
||||
if lines[i].kind ~= "dialogue" then
|
||||
table.insert(output, lines[i])
|
||||
else
|
||||
-- This is just for making the code a bit easier to read
|
||||
local line = lines[i]
|
||||
-- Get the rendered size of the entire line. (Won't work if there's line breaks in it.)
|
||||
local totalx, totaly = aegisub.text_extents(styles[line.style], line.text_stripped)
|
||||
-- Calculate where the first syllable should be positioned, if the line is to appear centered on screen
|
||||
local curx, cury = (meta.res_x - totalx) / 2, meta.res_y / 2
|
||||
-- And more preparations for per-syllable placement
|
||||
local startx = curx
|
||||
local tstart, tend = 0, 0
|
||||
-- Now process each stllable
|
||||
for j = 1, line.karaoke.n-1 do
|
||||
-- A shortcut variable, and, most important: a copy of the original line
|
||||
local syl, syllin = line.karaoke[j], copy_line(line)
|
||||
-- Calculate the ending time of this syllable
|
||||
tend = tstart + syl.duration*10
|
||||
-- Get the rendered size of this syllable
|
||||
local extx, exty, extd, extl = aegisub.text_extents(styles[line.style], syl.text_stripped)
|
||||
-- Some debug stuff...
|
||||
aegisub.output_debug(string.format("text_extents returned: %d, %d, %d, %d", extx, exty, extd, extl));
|
||||
-- Replace the text of the copy of the line with this syllable, moving around
|
||||
syllin.text = string.format("{\\an4\\move(%d,%d,%d,%d,%d,%d)\\kf%d\\kf%d}%s", curx, cury, curx, cury-exty, tstart, tend, tstart/10, syl.duration, syl.text)
|
||||
-- Add the line to the output
|
||||
table.insert(output, syllin)
|
||||
-- And prepare for next iteration
|
||||
curx = curx + extx
|
||||
tstart = tend
|
||||
end
|
||||
-- More debug stuff
|
||||
aegisub.output_debug(string.format("after syllable loop: totalx=%d curx-startx=%d", totalx, curx-startx))
|
||||
end
|
||||
end
|
||||
-- And remember to return something :)
|
||||
return output
|
||||
end
|
72
automation/demos/5-configuration.lua
Normal file
|
@ -0,0 +1,72 @@
|
|||
-- Aegisub Automation demonstration script
|
||||
-- Original written by Niels Martin Hansen
|
||||
-- Given into the public domain
|
||||
|
||||
name = "Configuration demo"
|
||||
description = "This script allows the user to input some data in the Aegisub Export window. Some of these data are used during the processing, to change the subtitles. (The strings is prepended every dialogue line.)"
|
||||
configuration = {
|
||||
[1] = {
|
||||
name = "thelabel";
|
||||
kind = "label";
|
||||
label = "This is a label control. Just for shows.";
|
||||
hint = "Tooltip for label?!?";
|
||||
};
|
||||
[2] = {
|
||||
name = "thestring";
|
||||
kind = "text";
|
||||
label = "String:";
|
||||
hint = "The string to insert at the beginning of each line";
|
||||
default = "foobar "
|
||||
};
|
||||
[3] = {
|
||||
name = "theint";
|
||||
kind = "int";
|
||||
label = "Integer:";
|
||||
hint = "An integer number to display in debug output";
|
||||
default = 50;
|
||||
min = 0;
|
||||
max = 100;
|
||||
};
|
||||
[4] = {
|
||||
name = "thefloat";
|
||||
kind = "float";
|
||||
label = "Float number:";
|
||||
hint = "Just a random float number";
|
||||
default = 3.1415927;
|
||||
};
|
||||
[5] = {
|
||||
name = "thebool";
|
||||
kind = "bool";
|
||||
label = "I accept";
|
||||
hint = "Check if you accept the terms of the license agreement";
|
||||
default = false;
|
||||
};
|
||||
[6] = {
|
||||
name = "thecolour";
|
||||
kind = "colour";
|
||||
label = "Favourite color:";
|
||||
hint = "What color do you want your pantsu?";
|
||||
default = "&H8080FF";
|
||||
};
|
||||
[7] = {
|
||||
name = "thestyle";
|
||||
kind = "style";
|
||||
label = "Style:";
|
||||
hint = "Pick a style the effects will apply to, or none to apply to everything";
|
||||
default = "";
|
||||
}
|
||||
}
|
||||
|
||||
version, kind = 3, 'basic_ass'
|
||||
|
||||
function process_lines(meta, styles, lines, config)
|
||||
aegisub.output_debug("The string entered is: " .. config.thestring)
|
||||
for i = 0, lines.n-1 do
|
||||
aegisub.report_progress(i/lines.n*100)
|
||||
if lines[i].kind == "dialogue" then
|
||||
lines[i].text = config.thestring .. lines[i].text
|
||||
end
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
87
automation/demos/6-simple-effect.lua
Normal file
|
@ -0,0 +1,87 @@
|
|||
-- Aegisub Automation demonstration script
|
||||
-- Original written by Niels Martin Hansen
|
||||
-- Given into the public domain
|
||||
|
||||
-- Define some required constants
|
||||
-- version and kind are required to have the given values for the script to work.
|
||||
-- configuration is not needed in this script, so it's just left as an empty table
|
||||
version, kind, configuration = 3, "basic_ass", {}
|
||||
|
||||
-- Define the displayed name of the script
|
||||
name = "Simple karaoke effect"
|
||||
-- A longer description of the script
|
||||
description = "A simple karaoke effect, with the source code heavily commented. Provided as a starting point for a useful effect."
|
||||
|
||||
-- The actual script function
|
||||
function process_lines(meta, styles, lines, config)
|
||||
-- Create a local variable to store the output subtitles in
|
||||
local output = { n=0 }
|
||||
|
||||
-- Start to loop over the lines, one by one
|
||||
-- The lines are numbered 0..n-1
|
||||
for i = 0, lines.n-1 do
|
||||
-- Show the user how far the script has got
|
||||
aegisub.report_progress(i/lines.n*100)
|
||||
-- First check if the line is even a dialogue line. If it's not, no need to process it.
|
||||
if lines[i].kind ~= "dialogue" then
|
||||
table.insert(output, lines[i])
|
||||
else
|
||||
-- This is a dialogue line, so process is
|
||||
-- Make a nicer name for the line we're processing
|
||||
newline = lines[i]
|
||||
-- Also show the line to the user
|
||||
aegisub.set_status(newline.text_stripped)
|
||||
|
||||
-- The text of the new line will be build little by little
|
||||
-- Each line has 700 ms fadein, 300 ms fadeout,
|
||||
-- is positioned at the center of the screen (\an8)
|
||||
-- and the highlighting should be delayed by 1000 ms (100 cs)
|
||||
newline.text = string.format("{\\fad(700,300)\\pos(%d,30)\\k100}", meta.res_x/2)
|
||||
-- Make the line start 1000 ms (100 cs) earlier than original
|
||||
newline.start_time = newline.start_time - 100
|
||||
|
||||
-- Now it's time to loop through the syllables one by one, processing them
|
||||
-- The first syllable is usually a "null" syllable, not containing real data, so that one should be skipped.
|
||||
-- This variable is used to keep track of when the last syllable ended
|
||||
-- It's initialised to 1000, since the start of the line was pushed 1000 ms back
|
||||
local cursylpos = 1000
|
||||
for j = 1, lines[i].karaoke.n-1 do
|
||||
local syl = lines[i].karaoke[j]
|
||||
-- Call another function to process the syllable
|
||||
newline.text = newline.text .. doSyllable(syl.text, cursylpos, cursylpos+syl.duration*10, syl.duration, syl.kind)
|
||||
-- Calculate the start time of the next syllable
|
||||
cursylpos = cursylpos + syl.duration*10
|
||||
end
|
||||
|
||||
-- The entire line has been calculated
|
||||
-- Add it to the output
|
||||
table.insert(output, newline)
|
||||
end
|
||||
end
|
||||
|
||||
-- All lines processed, and output filled
|
||||
-- Just return it
|
||||
-- (This is important! If you don't return anything, the output file will be empty!)
|
||||
return output
|
||||
end
|
||||
|
||||
-- This effect was originally written in the "Effector" program, which can be considered the first version of Automation.
|
||||
-- This following function is almost verbatimly copied from that original script.
|
||||
-- This is done in order to show how you can make sub-functions to make your script more readable.
|
||||
-- The contents of this function could also just be pasted into the middle of the main loop in process_lines,
|
||||
-- but that generally makes scripts harder to read.
|
||||
function doSyllable(text, t_start, t_end, t_dur, ktype)
|
||||
-- Declare two local variables needed here
|
||||
-- (If they're not declared local, they will be global.)
|
||||
local a, b
|
||||
-- If it's a "long" syllable, let the effect be different
|
||||
if t_dur > 75 then
|
||||
a = t_start + 500
|
||||
b = t_end
|
||||
else
|
||||
a = t_start + 100
|
||||
b = t_start + 500
|
||||
end
|
||||
-- Return the replacement for the syllable, including some ASS tags for format it
|
||||
return string.format("{\\r\\t(%d,%d,\\1c&H808080&\\2c&H808080&)\\kf%d}%s", a, b, t_dur, text)
|
||||
end
|
189
automation/demos/7-advanced-effect.lua
Normal file
|
@ -0,0 +1,189 @@
|
|||
-- Aegisub Automation demonstration script
|
||||
-- Original written by Niels Martin Hansen
|
||||
-- Given into the public domain
|
||||
-- But still, please don't use the effect generated by this script
|
||||
-- (unchanged) for your own works.
|
||||
|
||||
include("utils.lua")
|
||||
|
||||
name = "Advanced karaoke effect"
|
||||
description = "An advanced karaoke effect, making heavy use of both line-copying and per-syllable text placement. Also demonstrates how to treat lines differently, based on their style, and syllables differently based on the timing tag used."
|
||||
version, kind, configuration = 3, 'basic_ass', {}
|
||||
|
||||
function process_lines(meta, styles, lines, config)
|
||||
local output = { n=0 }
|
||||
math.randomseed(5922) -- just to make sure it's initialised the same every time
|
||||
for curline = 0, lines.n-1 do
|
||||
aegisub.report_progress(curline/lines.n*100)
|
||||
local lin = lines[curline]
|
||||
if lin.kind == "dialogue" and lin.style == "op romaji" then
|
||||
doromaji(lin, output, styles["op romaji"], 30)
|
||||
elseif lin.kind == "dialogue" and lin.style == "op kanji" then
|
||||
-- Just one of these lines should be uncommented.
|
||||
-- This script was written before configuration worked, otherwise it would use configuration
|
||||
-- to select which of the two effects to use for "op kanji" style lines.
|
||||
--doromaji(lin, output, styles["op kanji"], 20)
|
||||
dokanji(lin, output, styles["op kanji"])
|
||||
else
|
||||
-- Unknown lines are copied verbatim
|
||||
table.insert(output, lin)
|
||||
end
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
function doromaji(lin, output, sty, linetop)
|
||||
aegisub.set_status(lin.text_stripped)
|
||||
|
||||
--local linetop = 50
|
||||
|
||||
-- prepare syllable data
|
||||
local linewidth = 0
|
||||
local syltime = 0
|
||||
local syls = {n=0}
|
||||
for i = 1, lin.karaoke.n-1 do
|
||||
local syl = lin.karaoke[i]
|
||||
syl.width, syl.height, syl.descent, syl.extlead = aegisub.text_extents(sty, syl.text_stripped)
|
||||
syl.left = linewidth
|
||||
syl.start_time = syltime
|
||||
syl.end_time = syltime + syl.duration
|
||||
syltime = syltime + syl.duration
|
||||
if syl.kind == "kf" and (syl.text == " " or syl.text == " ") then
|
||||
-- The font this effect was made to work with has too wide spaces, so make those half width
|
||||
syl.width = math.floor(syl.width / 2)
|
||||
syl.kind = "space"
|
||||
elseif syl.kind == "kf" then
|
||||
syl.kind = "deco"
|
||||
elseif syl.kind == "k" then
|
||||
syl.kind = "reg"
|
||||
elseif syl.kind == "ko" then
|
||||
syl.kind = "dance"
|
||||
else
|
||||
syl.kind = "ignore"
|
||||
end
|
||||
if syl.text == "#" then
|
||||
syls[syls.n-1].duration = syls[syls.n-1].duration + syl.duration
|
||||
syls[syls.n-1].end_time = syls[syls.n-1].end_time + syl.duration
|
||||
else
|
||||
linewidth = linewidth + syl.width
|
||||
syls[syls.n] = syl
|
||||
syls.n = syls.n + 1
|
||||
end
|
||||
end
|
||||
local lineofs = math.floor((640 - linewidth) / 2)
|
||||
|
||||
for i = 0, syls.n-1 do
|
||||
local syl = syls[i], copy_line(lin)
|
||||
if syl.kind == "space" then
|
||||
-- Spaces are skipped. Since the width of them is already incorporated in the position calculations
|
||||
-- for the following syllables, they can safely be stripped from output, without losing the spaces.
|
||||
elseif syl.kind == "ignore" then
|
||||
-- These are actually syllable kinds we don't know what to do about. They are not included in the output.
|
||||
else
|
||||
local startx, starty, shakex, shakey, enterangle
|
||||
-- angle to enter from
|
||||
enterangle = math.rad((180 / lin.karaoke.n) * i - 90)
|
||||
-- position to enter from (350 pixels from (320, 50))
|
||||
startx = math.sin(enterangle)*350 + 320
|
||||
starty = linetop - math.cos(enterangle)*350
|
||||
if syl.kind == "reg" then
|
||||
shakex = (syl.left+lineofs) + math.random(-5, 5)
|
||||
shakey = linetop + math.random(-5, 5)
|
||||
elseif syl.kind == "deco" then
|
||||
shakex, shakey = syl.left+lineofs, linetop
|
||||
elseif syl.kind == "dance" then
|
||||
shakex = (syl.left+lineofs) + math.random(-5, 5)
|
||||
shakey = linetop + math.random(-5, 5)
|
||||
end
|
||||
-- origin for rotation
|
||||
local orgx = syl.left + lineofs + syl.width/2
|
||||
local orgy = linetop + syl.height/2
|
||||
-- entry effect
|
||||
local enterlin = copy_line(lin)
|
||||
enterlin.start_time = lin.start_time - 40
|
||||
enterlin.end_time = lin.start_time
|
||||
enterlin.text = string.format("{\\move(%d,%d,%d,%d)\\fr%d\\t(\\fr0)\\an7}%s", startx, starty, shakex, shakey, -math.deg(enterangle), syl.text)
|
||||
table.insert(output, enterlin)
|
||||
-- main highlight effect
|
||||
local newlin = copy_line(lin)
|
||||
local hilistart, hilimid, hiliend = syl.start_time*10, (syl.start_time+syl.duration/2)*10, (syl.start_time+syl.duration)*10
|
||||
newlin.text = string.format("\\move(%d,%d,%d,%d,%d,%d)\\an7}%s", shakex, shakey, syl.left+lineofs, linetop, hilistart, hiliend, syl.text)
|
||||
newlin.layer = 1
|
||||
if syl.kind == "dance" then
|
||||
local fx = string.format("\\org(%d,%d)\\t(%d,%d,\\fr30)\\t(%d,%d,\\fr-30)\\t(%d,%d,\\3c&H0000ff&)\\t(%d,%d,\\3c&H000080&)\\t(%d,%d,\\fr0)", orgx, orgy, hilistart, hilistart+20, hilistart+20, hiliend, hilistart, hilimid, hilimid, hiliend, hiliend, hiliend+syl.duration*10)
|
||||
newlin.text = fx .. newlin.text
|
||||
else
|
||||
local fx = string.format("\\t(%d,%d,\\3c&H0000ff&)\\t(%d,%d,\\3c&H000080&)", hilistart, hilimid, hilimid, hiliend)
|
||||
newlin.text = fx .. newlin.text
|
||||
end
|
||||
local bord = copy_line(newlin)
|
||||
bord.layer = 0
|
||||
bord.text = "{" .. bord.text
|
||||
newlin.text = "{\\bord0" .. newlin.text
|
||||
table.insert(output, bord)
|
||||
table.insert(output, newlin)
|
||||
-- leave effect
|
||||
-- cut the line over in two, lower half "drops down", upper just fades away
|
||||
local tophalf = copy_line(lin)
|
||||
tophalf.start_time = lin.end_time
|
||||
tophalf.end_time = lin.end_time + 100
|
||||
tophalf.text = string.format("\\3c&H000080&\\fad(0,700)\\pos(%d,%d)\\an7}%s", syl.left+lineofs, linetop, syl.text_stripped)
|
||||
local bottomhalf = copy_line(tophalf)
|
||||
tophalf.text = string.format("{\\t(0,200,\\1c&H000080&)\\clip(0,0,640,%d)%s", linetop+syl.height/2, tophalf.text)
|
||||
bottomhalf.text = string.format("{\\org(%d,%d)\\clip(0,%d,640,480)\\t(0,200,\\1c&H000080&)\\t(200,1000,1.2,\\frx90\\clip(0,%d,640,480)%s", 320, linetop+syl.height, linetop+syl.height/2, linetop+syl.height, bottomhalf.text)
|
||||
table.insert(output, tophalf)
|
||||
table.insert(output, bottomhalf)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function dokanji(lin, output, sty)
|
||||
aegisub.set_status(lin.text_stripped)
|
||||
|
||||
local fontname = "@HGSHigemoji"
|
||||
|
||||
local lineheight = 0
|
||||
local syltime = 0
|
||||
local syls = {n=0}
|
||||
for i = 1, lin.karaoke.n-1 do
|
||||
local syl = lin.karaoke[i]
|
||||
syl.height, syl.width, syl.descent, syl.extlead = aegisub.text_extents(sty, syl.text_stripped)
|
||||
syl.start_time = syltime
|
||||
syl.end_time = syltime + syl.duration
|
||||
syltime = syltime + syl.duration
|
||||
if syl.text == "#" then
|
||||
syls[syls.n-1].duration = syls[syls.n-1].duration + syl.duration
|
||||
syls[syls.n-1].end_time = syls[syls.n-1].end_time + syl.duration
|
||||
--elseif syl.text == "" then
|
||||
-- skip
|
||||
else
|
||||
lineheight = lineheight + syl.height
|
||||
syls[syls.n] = syl
|
||||
syls.n = syls.n + 1
|
||||
end
|
||||
end
|
||||
|
||||
for i = 0, syls.n-1 do
|
||||
local syl = syls[i]
|
||||
local top = 170
|
||||
for j = 0, 8 do
|
||||
if i+j >= syls.n then
|
||||
break
|
||||
end
|
||||
local newlin = copy_line(lin)
|
||||
newlin.start_time = lin.start_time + syl.start_time
|
||||
newlin.end_time = newlin.start_time + syl.duration
|
||||
local startalpha, targetalpha
|
||||
if j == 0 then
|
||||
startalpha = 0
|
||||
targetalpha = 255
|
||||
else
|
||||
startalpha = j * 255 / 8
|
||||
targetalpha = (j-1) * 255 / 8
|
||||
end
|
||||
newlin.text = string.format("{\\fn%s\\an7\\fr-90\\move(%d,%d,%d,%d)\\1a&H%2x&\\3a&H%2x&\\t(\\1a&H%2x&\\3a&H%2x&)}%s", fontname, 620, top, 620, top-syl.height, startalpha, startalpha, targetalpha, targetalpha, syls[i+j].text)
|
||||
top = top + syls[i+j].height
|
||||
table.insert(output, newlin)
|
||||
end
|
||||
end
|
||||
end
|
18
automation/demos/8-skeleton.lua
Normal file
|
@ -0,0 +1,18 @@
|
|||
-- Aegisub Automation demonstration script
|
||||
-- Original written by Niels Martin Hansen
|
||||
-- Given into the public domain
|
||||
|
||||
name = "Karaoke skeleton demo"
|
||||
description = "This script demonstrates the use of the karaskel.lua include file, to avoid writing almost identical code for every karaoke effect script."
|
||||
|
||||
version, kind, configuration = 3, 'basic_ass', {}
|
||||
|
||||
include("karaskel.lua")
|
||||
|
||||
function do_syllable(meta, styles, config, line, syl)
|
||||
if syl.i == 0 then
|
||||
return syl.text
|
||||
else
|
||||
return string.format("{\\r\\k%d\\t(%d,%d,\\1c&H%s&)}%s", syl.duration, syl.start_time, syl.end_time, line.styleref.color2, syl.text)
|
||||
end
|
||||
end
|
49
automation/demos/9-skeleton-advanced.lua
Normal file
|
@ -0,0 +1,49 @@
|
|||
-- Aegisub Automation demonstration script
|
||||
-- Original written by Niels Martin Hansen
|
||||
-- Given into the public domain
|
||||
|
||||
name = "Advanced skeleton demo"
|
||||
description = "This script demonstrates using the karaskel-adv.lua include file to make rceation of per-syllable positioning effects easier."
|
||||
|
||||
version, kind, configuration = 3, 'basic_ass', {}
|
||||
|
||||
include("karaskel-adv.lua")
|
||||
|
||||
-- What kind of effect this makes:
|
||||
-- Each syllable "jumps" up and down once during its duration
|
||||
-- This is achieved using two \move operations, and as known, gabest's TextSub can only handle one \move per line
|
||||
-- So we need two lines per syllable, split exactly in the middle of the duration of the syllable
|
||||
|
||||
function do_syllable(meta, styles, config, line, syl)
|
||||
-- Make two copies of the original line (having the right timings etc)
|
||||
local half1, half2 = copy_line(line), copy_line(line)
|
||||
-- Make the first half line end halfway into the duration of the syllable
|
||||
half1.end_time = half1.start_time + syl.start_time/10 + syl.duration/2
|
||||
-- And make the second half line start where the first one ends
|
||||
half2.start_time = half1.end_time
|
||||
-- Where to move the syllable to/from
|
||||
local fromx, fromy = line.centerleft+syl.center, line.height*2 + 20
|
||||
local tox, toy = fromx, fromy - 10
|
||||
-- Generate some text for the syllable
|
||||
half1.text = string.format("{\\an8\\move(%d,%d,%d,%d,%d,%d)}%s", fromx, fromy, tox, toy, syl.start_time, syl.start_time+syl.duration*5, syl.text_stripped)
|
||||
half2.text = string.format("{\\an8\\move(%d,%d,%d,%d,%d,%d)}%s", tox, toy, fromx, fromy, 0, syl.duration*5, syl.text_stripped)
|
||||
-- Things will look bad with overlapping borders and stuff unless
|
||||
-- we manually layer borders lower than text,
|
||||
-- and shadows lower than borders, so let's do that
|
||||
local half1b, half1s = copy_line(half1), copy_line(half1)
|
||||
half1b.text = "{\\1a&HFF&\\shad0}" .. half1b.text
|
||||
half1s.text = "{\\1a&HFF&\\bord0}" .. half1s.text
|
||||
half1.text = "{\\bord0\\shad0}" .. half1.text
|
||||
half1.layer = 2
|
||||
half1b.layer = 1
|
||||
half1s.layer = 0
|
||||
local half2b, half2s = copy_line(half2), copy_line(half2)
|
||||
half2b.text = "{\\1a&HFF&\\shad0}" .. half2b.text
|
||||
half2s.text = "{\\1a&HFF&\\bord0\\shad2}" .. half2s.text
|
||||
half2.text = "{\\bord0\\shad0}" .. half2.text
|
||||
half2.layer = 2
|
||||
half2b.layer = 1
|
||||
half2s.layer = 0
|
||||
-- Done, return the two new lines
|
||||
return {n=6, [1]=half1, [2]=half2b, [3]=half1s, [4]=half2, [5]=half2b, [6]=half2s}
|
||||
end
|
18
automation/demos/readme.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
This directory contains various Aegisub Automation scripts provided for demonstration
|
||||
purposes. Most of these were originally written for testing the various functions
|
||||
in Automation durings its development, but they hopefully can also serve as good
|
||||
places to learn how to do things.
|
||||
All of these scripts are written by Niels Martin Hansen.
|
||||
|
||||
They are given into the public domain, but if you use a substantial part of any of
|
||||
the more advanced ones, I'd really like a bit of credit where it's due.
|
||||
|
||||
And remember, it's cheap to use someone else's advanced karaoke effect, especially
|
||||
without any changes at all. It also gives bad karma. Like, *really* bad :)
|
||||
|
||||
|
||||
WARNING!
|
||||
DO NOT ADD YOUR OWN FILES TO THIS DIRECTORY,
|
||||
they will be deleted when you uninstall/upgrade Aegisub.
|
||||
This also means it's unwise to modify these files, if you want to keep your
|
||||
changes to them.
|
158
automation/factorybrew/line-per-syllable.lua
Normal file
|
@ -0,0 +1,158 @@
|
|||
--[[
|
||||
Copyright (c) 2005, 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 Automation factory-brewed script
|
||||
-- "Line-per-syllable effects"
|
||||
|
||||
-- Use karaskel.lua for skeleton code
|
||||
include("karaskel-adv.lua")
|
||||
|
||||
-- Define the name of the script
|
||||
name = "Line-per-syllable effects"
|
||||
-- Define a description of the script
|
||||
description = "Makes line-per-syllable effects, that is, each syllable in the script gets its own line. This allows for more advanced effects, such as syllables moving about separately, or overlapping each other."
|
||||
-- Define the script variables that can be configured graphically
|
||||
-- This is the primary point of the script, being able to configure it graphically
|
||||
configuration = {
|
||||
-- First a label to descript what special variables can be used
|
||||
[1] = {
|
||||
name = "label1";
|
||||
kind = "label";
|
||||
label = [[Variable-names are prefixed with $,
|
||||
expressions are enclosed in % pairs.
|
||||
Variables:
|
||||
$START = Start-time of syllable (ms)
|
||||
$END = End-time of syllable (ms)
|
||||
$MID = Time midways through the syllable (ms)
|
||||
$DUR = Duration of syllable (cs)
|
||||
$X = X-position of syllable (center)
|
||||
$WIDTH = width of syllable
|
||||
$HEIGHT = height of syllable
|
||||
(There is no $Y)
|
||||
Calculation example:
|
||||
\t($start,%$start+10,\fscx110\fscy110)
|
||||
\move($x,40,%$x-$width+10%,40)
|
||||
Remember you should always have a \pos or \move in your effect.]];
|
||||
hint = ""
|
||||
-- No "default", since a label doesn't have a value
|
||||
},
|
||||
-- Then a text field to input the string to replace \k's with
|
||||
-- Make the default a "NOP" string
|
||||
[2] = {
|
||||
name = "k_repstr";
|
||||
kind = "text";
|
||||
label = "Effect";
|
||||
hint = "The string to place at the start of every syllable line.";
|
||||
default = "{\\an5\\pos($X,40)\\t($START,$END,\\fry360)}"
|
||||
},
|
||||
-- Allow the user to specify whether to strip tags or not
|
||||
[3] = {
|
||||
name = "striptags";
|
||||
kind = "bool";
|
||||
label = "Strip all tags";
|
||||
hint = "Strip all formatting tags apart from the processed karaoke tags?";
|
||||
default = false
|
||||
},
|
||||
[4] = {
|
||||
name = "workstyle";
|
||||
kind = "style";
|
||||
label = "Line style";
|
||||
hint = "Only apply the effect to lines with this style. Empty means apply to all lines.";
|
||||
default = ""
|
||||
}
|
||||
}
|
||||
-- Mandatory values
|
||||
version, kind= 3, 'basic_ass'
|
||||
|
||||
function do_syllable(meta, styles, config, line, syl)
|
||||
-- text is the replacement text for the syllable
|
||||
-- ktext is the karaoke effect string
|
||||
local text, ktext
|
||||
|
||||
-- Prepare the stripped or unstripped main text
|
||||
if config.striptags then
|
||||
text = syl.text_stripped
|
||||
else
|
||||
text = syl.text
|
||||
end
|
||||
|
||||
-- Don't bother with empty syllables
|
||||
if syl.text == "" then
|
||||
return { n=0 }
|
||||
end
|
||||
|
||||
-- Add the variable names to the syllable data
|
||||
syl["dur"] = syl.duration
|
||||
syl["start"] = syl.start_time
|
||||
syl["end"] = syl.end_time
|
||||
syl["mid"] = syl.start_time + syl.duration*5
|
||||
syl["x"] = syl.center + line.centerleft
|
||||
|
||||
-- Prepare the karaoke effect string
|
||||
ktext = config.k_repstr
|
||||
|
||||
-- Function for replacing the variables
|
||||
local function var_replacer(varname)
|
||||
varname = string.lower(varname)
|
||||
if syl[varname] ~= nil then
|
||||
return syl[varname]
|
||||
else
|
||||
aegisub.output_debug(string.format("Unknown variable name: %s", varname))
|
||||
return "$" .. varname
|
||||
end
|
||||
end
|
||||
-- Replace the variables in the ktext
|
||||
ktext = string.gsub(ktext, "$(%a+)", var_replacer)
|
||||
|
||||
-- Function for evaluating expressions
|
||||
local function expression_evaluator(expression)
|
||||
chunk, err = loadstring(string.format("return (%s)", expression))
|
||||
if (err) ~= nil then
|
||||
aegisub.output_debug(string.format("Error parsing expression:\n%s", expression, err))
|
||||
return "%" .. expression .. "%"
|
||||
else
|
||||
return chunk()
|
||||
end
|
||||
end
|
||||
-- Find and evaluate expressions
|
||||
ktext = string.gsub(ktext, "%%([^%%]*)%%", expression_evaluator)
|
||||
|
||||
local newline = copy_line(line)
|
||||
newline.text = ktext .. text
|
||||
|
||||
return { n=1, [1]=newline }
|
||||
end
|
||||
|
||||
function do_line(meta, styles, config, line)
|
||||
if config.workstyle == "" or config.workstyle == line.style then
|
||||
return adv_do_line(meta, styles, config, line)
|
||||
else
|
||||
return { n=1, [1]=line }
|
||||
end
|
||||
end
|
12
automation/factorybrew/readme.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
This directory contains flexible, configurable Automation scripts to do common jobs,
|
||||
without having to do programming.
|
||||
|
||||
You can load these scripts into an ASS file in Aegisub, and apply an effect without
|
||||
doing more than writing ASS override tags.
|
||||
|
||||
|
||||
WARNING!
|
||||
DO NOT ADD YOUR OWN FILES TO THIS DIRECTORY,
|
||||
they will be deleted when you uninstall/upgrade Aegisub.
|
||||
This also means it's unwise to modify these files, if you want to keep your
|
||||
changes to them.
|
143
automation/factorybrew/simple-k-replacer.lua
Normal file
|
@ -0,0 +1,143 @@
|
|||
--[[
|
||||
Copyright (c) 2005, 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 Automation factory-brewed script
|
||||
-- "Basic \k replacer"
|
||||
|
||||
-- Use karaskel.lua for skeleton code
|
||||
include("karaskel.lua")
|
||||
|
||||
-- Define the name of the script
|
||||
name = "Basic \\k replacer"
|
||||
-- Define a description of the script
|
||||
description = "Makes basic karaoke effects. Replace all \\k tags (and variations) with a custom string, where some variables can be substituted in."
|
||||
-- Define the script variables that can be configured graphically
|
||||
-- This is the primary point of the script, being able to configure it graphically
|
||||
configuration = {
|
||||
-- First a label to descript what special variables can be used
|
||||
[1] = {
|
||||
name = "label1";
|
||||
kind = "label";
|
||||
label = [[Variable-names are prefixed with $,
|
||||
expressions are enclosed in % pairs.
|
||||
Variables:
|
||||
$START = Start-time of syllable (ms)
|
||||
$END = End-time of syllable (ms)
|
||||
$MID = Time midways through the syllable (ms)
|
||||
$DUR = Duration of syllable (cs)
|
||||
Calculation example:
|
||||
\t($start,%$start+$dur*2%,\fscx110)
|
||||
\t(%$start+$dur*2%,$end,\fscx90)]];
|
||||
hint = ""
|
||||
-- No "default", since a label doesn't have a value
|
||||
},
|
||||
-- Then a text field to input the string to replace \k's with
|
||||
-- Make the default a "NOP" string
|
||||
[2] = {
|
||||
name = "k_repstr";
|
||||
kind = "text";
|
||||
label = "\\k replacement";
|
||||
hint = "The string to replace \\k tags with. Should start and end with { } characters.";
|
||||
default = "{\\k$DUR}"
|
||||
},
|
||||
-- Allow the user to specify whether to strip tags or not
|
||||
[3] = {
|
||||
name = "striptags";
|
||||
kind = "bool";
|
||||
label = "Strip all tags";
|
||||
hint = "Strip all formatting tags apart from the processed karaoke tags?";
|
||||
default = false
|
||||
},
|
||||
[4] = {
|
||||
name = "workstyle";
|
||||
kind = "style";
|
||||
label = "Line style";
|
||||
hint = "Only apply the effect to lines with this style. Empty means apply to all lines.";
|
||||
default = ""
|
||||
}
|
||||
}
|
||||
-- Mandatory values
|
||||
version, kind= 3, 'basic_ass'
|
||||
|
||||
function do_syllable(meta, styles, config, line, syl)
|
||||
-- text is the replacement text for the syllable
|
||||
-- ktext is the karaoke effect string
|
||||
local text, ktext
|
||||
|
||||
-- Prepare the stripped or unstripped main text
|
||||
if config.striptags then
|
||||
text = syl.text_stripped
|
||||
else
|
||||
text = syl.text
|
||||
end
|
||||
|
||||
-- Add the variable names to the syllable data
|
||||
syl["dur"] = syl.duration
|
||||
syl["start"] = syl.start_time
|
||||
syl["end"] = syl.end_time
|
||||
syl["mid"] = syl.start_time + syl.duration*5
|
||||
|
||||
ktext = config.k_repstr
|
||||
|
||||
-- Function for replacing the variables
|
||||
local function var_replacer(varname)
|
||||
varname = string.lower(varname)
|
||||
if syl[varname] ~= nil then
|
||||
return syl[varname]
|
||||
else
|
||||
aegisub.output_debug(string.format("Unknown variable name: %s", varname))
|
||||
return "$" .. varname
|
||||
end
|
||||
end
|
||||
-- Replace the variables in the ktext
|
||||
ktext = string.gsub(ktext, "$(%a+)", var_replacer)
|
||||
|
||||
-- Function for evaluating expressions
|
||||
local function expression_evaluator(expression)
|
||||
chunk, err = loadstring(string.format("return (%s)", expression))
|
||||
if (err) ~= nil then
|
||||
aegisub.output_debug(string.format("Error parsing expression:\n%s", expression, err))
|
||||
return "%" .. expression .. "%"
|
||||
else
|
||||
return chunk()
|
||||
end
|
||||
end
|
||||
-- Find and evaluate expressions
|
||||
ktext = string.gsub(ktext, "%%([^%%]*)%%", expression_evaluator)
|
||||
|
||||
return ktext .. text
|
||||
end
|
||||
|
||||
function do_line(meta, styles, config, line)
|
||||
if config.workstyle == "" or config.workstyle == line.style then
|
||||
return default_do_line(meta, styles, config, line)
|
||||
else
|
||||
return { n=1, [1]=line }
|
||||
end
|
||||
end
|
64
automation/include/karaskel-adv.lua
Normal file
|
@ -0,0 +1,64 @@
|
|||
--[[
|
||||
Copyright (c) 2005, 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 Automation include file
|
||||
-- This include file is an extension of karaskel.lua providing out-of-the-box support for per-syllable
|
||||
-- positioning of karaoke.
|
||||
|
||||
-- It automatically includes and re-setups karaskel.lua, so you should not include that yourself!
|
||||
include("karaskel.lua")
|
||||
-- Also include utils.lua, since you'll most likely need to use the copy_line function
|
||||
include("utils.lua")
|
||||
|
||||
-- The interface here has been greatly simplified, there is only one function to override, do_syllable
|
||||
-- The format for that one has changed.
|
||||
-- The rest of the functions can be "emulated" through the do_syllable function.
|
||||
-- Though you can still also override do_line and get an effect our of it, it's not advisable, since that
|
||||
-- defeats the entire purpose of using this include file.
|
||||
|
||||
-- The arguments here mean the same as in karaskel.lua, and all tables have the same members
|
||||
-- The return value is different now, though.
|
||||
-- It is required to be in the same format as the do_line function:
|
||||
-- A table with an "n" key, and keys 0..n-1 with line structures.
|
||||
function default_do_syllable(meta, styles, config, line, syl)
|
||||
return {n=0}
|
||||
end
|
||||
do_syllable = default_do_syllable
|
||||
|
||||
function adv_do_line(meta, styles, config, line)
|
||||
local result = {n=0}
|
||||
for i = 0, line.karaoke.n-1 do
|
||||
local out = do_syllable(meta, styles, config, line, line.karaoke[i])
|
||||
for j = 1, out.n do
|
||||
table.insert(result, out[j])
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
do_line = adv_do_line
|
183
automation/include/karaskel.lua
Normal file
|
@ -0,0 +1,183 @@
|
|||
--[[
|
||||
Copyright (c) 2005, 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 Automation include file
|
||||
-- The purpose of this include file is to provide a default skeleton for Automation scripts,
|
||||
-- where you, as a scripter, can just override the functions you need to override.
|
||||
|
||||
-- The following functions can be overridden:
|
||||
-- do_syllable - called for each syllable in a line
|
||||
-- do_line_decide - called to decide whether to process a line or not
|
||||
-- do_line_start - called at the beginning of each line
|
||||
-- do_line_end - called at the end of each line
|
||||
-- do_line - Process an entire line
|
||||
-- By default, these functions are all assigned to the following default functions
|
||||
-- Your functions should obviously use the same parameter lists etc.
|
||||
-- Note that if you override do_line, none of the other functions will be called!
|
||||
|
||||
|
||||
-- All the functions mentioned here are, however, wrapped in some code that pre-calculates some
|
||||
-- extra data for the lines and syllables.
|
||||
|
||||
-- The following fields are added to lines:
|
||||
-- i - The index of the line in the file, 0 being the first
|
||||
-- width - Rendered width of the line in pixels (using the line style, overrides are not taken into account)
|
||||
-- height - Rendered height of the line in pixels (note that rotation isn't taken into account either)
|
||||
-- ascent - Height of the ascenders, in pixels (notice that this looks like the output of text_extents?)
|
||||
-- extlead - External leading of the font, in pixels (well, it is!)
|
||||
-- centerleft - Pixel position of the left edge of the line, when horisontally centered on screen
|
||||
-- centerright - Pixel pisition of the right edge of the line, when horisontally centered on screen
|
||||
-- duration - Duration of the line in miliseconds
|
||||
-- styleref - The actual style table for the style this line has
|
||||
|
||||
-- The following fields are added to syllables:
|
||||
-- i - The index of the syllable in the line, 0 being the first (and usually empty) one
|
||||
-- width, height, ascent, extlead - Same as for lines
|
||||
-- left - Left pixel position of the syllable, relative to the start of the line
|
||||
-- center - Center pixel position of the syllable, also relative to the start of the line
|
||||
-- right - Right pixel position of the syllable, relative again
|
||||
-- start_time - Start time of the syllable, in miliseconds, relative to the start of the line
|
||||
-- end_time - End time of the syllable, similar to start_time
|
||||
|
||||
|
||||
-- Return a replacement text for a syllable
|
||||
function default_do_syllable(meta, styles, config, line, syl)
|
||||
return syl.text
|
||||
end
|
||||
|
||||
-- Decide whether or not to process a line
|
||||
function default_do_line_decide(meta, styles, config, line)
|
||||
return line.kind == "dialogue"
|
||||
end
|
||||
|
||||
-- Return a text to prefix the line
|
||||
function default_do_line_start(meta, styles, config, line)
|
||||
return ""
|
||||
end
|
||||
|
||||
-- Return a text to suffix the line
|
||||
function default_do_line_end(meta, styles, config, line)
|
||||
return ""
|
||||
end
|
||||
|
||||
-- Process an entire line (which has pre-calculated extra data in it already)
|
||||
-- Return a table of replacement lines
|
||||
function default_do_line(meta, styles, config, line)
|
||||
-- Check if the line should be processed at all
|
||||
if not do_line_decide(meta, styles, config, line) then
|
||||
return {n=0}
|
||||
end
|
||||
-- Create a new local var for the line replacement text, set it to line prefix
|
||||
-- This is to make sure the actual line text isn't replaced before the line has been completely processed
|
||||
local newtext = do_line_start(meta, styles, config, line)
|
||||
-- Loop over the syllables
|
||||
for i = 0, line.karaoke.n-1 do
|
||||
-- Append the replacement for each syllable onto the line
|
||||
newtext = newtext .. do_syllable(meta, styles, config, line, line.karaoke[i])
|
||||
end
|
||||
-- Append line suffix
|
||||
newtext = newtext .. do_line_end(meta, styles, config, line)
|
||||
-- Now replace the line text
|
||||
line.text = newtext
|
||||
-- And return a table with one entry
|
||||
return {n=1; [1]=line}
|
||||
end
|
||||
|
||||
-- Now assign all the default functions to the names that are actually called
|
||||
do_syllable = default_do_syllable
|
||||
do_line_decide = default_do_line_decide
|
||||
do_line_start = default_do_line_start
|
||||
do_line_end = default_do_line_end
|
||||
do_line = default_do_line
|
||||
|
||||
precalc_start_progress = 0
|
||||
precalc_end_progress = 50
|
||||
function precalc_syllable_data(meta, styles, lines)
|
||||
aegisub.set_status("Preparing syllable-data")
|
||||
for i = 0, lines.n-1 do
|
||||
aegisub.report_progress(precalc_start_progress + i/lines.n*(precalc_end_progress-precalc_start_progress))
|
||||
local line, style = lines[i]
|
||||
-- Index number of the line
|
||||
line.i = i
|
||||
if line.kind == "dialogue" or line.kind == "comment" then
|
||||
local style = styles[line.style]
|
||||
-- Line dimensions
|
||||
line.width, line.height, line.ascent, line.extlead = aegisub.text_extents(style, line.text_stripped)
|
||||
-- Line position
|
||||
line.centerleft = math.floor((meta.res_x - line.width) / 2)
|
||||
line.centerright = meta.res_x - line.centerleft
|
||||
-- Line duration, in miliseconds
|
||||
line.duration = (line.end_time - line.start_time) * 10
|
||||
-- Style reference
|
||||
line.styleref = style
|
||||
-- Process the syllables
|
||||
local curx, curtime = 0, 0
|
||||
for j = 0, line.karaoke.n-1 do
|
||||
local syl = line.karaoke[j]
|
||||
-- Syllable index
|
||||
syl.i = j
|
||||
-- Syllable dimensions
|
||||
syl.width, syl.height, syl.ascent, syl.extlead = aegisub.text_extents(style, syl.text_stripped)
|
||||
-- Syllable positioning
|
||||
syl.left = curx
|
||||
syl.center = math.floor(curx + syl.width/2)
|
||||
syl.right = curx + syl.width
|
||||
curx = syl.right
|
||||
-- Start and end times in miliseconds
|
||||
syl.start_time = curtime
|
||||
syl.end_time = curtime + syl.duration*10
|
||||
curtime = syl.end_time
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Everything else is done in the process_lines function
|
||||
function skel_process_lines(meta, styles, lines, config)
|
||||
-- Do a little pre-calculation for each line and syllable
|
||||
precalc_syllable_data(meta, styles, lines)
|
||||
-- A var for the new output
|
||||
local result = {n=0}
|
||||
aegisub.set_status("Running main-processing")
|
||||
-- Now do the usual processing
|
||||
for i = 0, lines.n-1 do
|
||||
aegisub.report_progress(50+i/lines.n*50)
|
||||
if do_line_decide(meta, styles, config, lines[i]) then
|
||||
-- Get replacement lines
|
||||
repl = do_line(meta, styles, config, lines[i])
|
||||
-- Append to result table
|
||||
for j = 1, repl.n do
|
||||
table.insert(result, repl[j])
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Done, return the stuff
|
||||
return result
|
||||
end
|
||||
process_lines = skel_process_lines
|
10
automation/include/readme.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
This directory contains standard include files for Automation.
|
||||
By default, this directory is included in the Automation Include Path, which
|
||||
means the files here are always available for use with the include() function.
|
||||
|
||||
See the Aegisub help and the individual files in this directory for more
|
||||
information on their purpose and contents.
|
||||
|
||||
You may add your own files to this directory, they will not be touched upon
|
||||
uninstalling/upgrading Aegisub. The standard files here will, however, be
|
||||
removed/replaced on uninstall/upgrade, so you are advised not to edit those!
|
167
automation/include/utils.lua
Normal file
|
@ -0,0 +1,167 @@
|
|||
--[[
|
||||
Copyright (c) 2005, Niels Martin Hansen, Rodrigo Braz Monteiro
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of the Aegisub Group nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
]]
|
||||
|
||||
-- Variables with tables only hold references to the actual tables
|
||||
-- Since a line is a table, a line needs to be copied, otherwise things break in bad ways
|
||||
function copy_line(input)
|
||||
output = {}
|
||||
output.kind = input.kind
|
||||
if input.kind == "scomment" then
|
||||
output.text = input.text
|
||||
elseif input.kind == "comment" or input.kind == "dialogue" then
|
||||
output.layer = input.layer
|
||||
output.start_time = input.start_time
|
||||
output.end_time = input.end_time
|
||||
output.style = input.style
|
||||
output.name = input.name
|
||||
output.margin_l = input.margin_l
|
||||
output.margin_r = input.margin_r
|
||||
output.margin_v = input.margin_v
|
||||
output.effect = input.effect
|
||||
output.text = input.text
|
||||
output.text_stripped = input.text_stripped
|
||||
output.karaoke = input.karaoke -- Don't bother copying the karaoke table, it shouldn't be changed anyway
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
|
||||
-- Generates ASS hexadecimal string from R,G,B integer components, in &HBBGGRR& format
|
||||
function ass_color(r,g,b)
|
||||
return string.format("&H%02X%02X%02X&",b,g,r)
|
||||
end
|
||||
|
||||
|
||||
-- Converts HSV (Hue, Saturation, Value) to RGB
|
||||
function HSV_to_RGB(H,S,V)
|
||||
local r,g,b;
|
||||
|
||||
-- Saturation is zero, make grey
|
||||
if S == 0 then
|
||||
r = V*255
|
||||
if r < 0 then
|
||||
r = 0
|
||||
end
|
||||
if r > 255 then
|
||||
r = 255
|
||||
end
|
||||
g = r
|
||||
b = r
|
||||
|
||||
-- Else, calculate color
|
||||
else
|
||||
-- Calculate subvalues
|
||||
local Hi = math.floor(H/60)
|
||||
local f = H/60.0 - Hi
|
||||
local p = V*(1-S)
|
||||
local q = V*(1-f*S)
|
||||
local t = V*(1-(1-f)*S)
|
||||
|
||||
-- Do math based on hue index
|
||||
if Hi == 0 then
|
||||
r = V*255.0
|
||||
g = t*255.0
|
||||
b = p*255.0
|
||||
elseif Hi == 1 then
|
||||
r = q*255.0
|
||||
g = V*255.0
|
||||
b = p*255.0
|
||||
elseif Hi == 2 then
|
||||
r = p*255.0
|
||||
g = V*255.0
|
||||
b = t*255.0
|
||||
elseif Hi == 3 then
|
||||
r = p*255.0
|
||||
g = q*255.0
|
||||
b = V*255.0
|
||||
elseif Hi == 4 then
|
||||
r = t*255.0
|
||||
g = p*255.0
|
||||
b = V*255.0
|
||||
elseif Hi == 5 then
|
||||
r = V*255.0
|
||||
g = p*255.0
|
||||
b = q*255.0
|
||||
end
|
||||
end
|
||||
|
||||
r = math.floor(r)
|
||||
g = math.floor(g)
|
||||
b = math.floor(b)
|
||||
return r,g,b
|
||||
end
|
||||
|
||||
|
||||
-- Removes spaces at the start and end of string
|
||||
function trim (s)
|
||||
return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
|
||||
-- UTF-8 string handling functions
|
||||
-- Contributed by roxfan
|
||||
|
||||
-- Get the offset for the next character in the string, given the current offset
|
||||
function next_utf_char(str, off)
|
||||
local leadb = string.byte(str, off)
|
||||
if leadb < 128 then
|
||||
return off+1
|
||||
elseif leadb < 224 then
|
||||
return off+2
|
||||
elseif leadb < 240 then
|
||||
return off+3
|
||||
elseif leadb < 248 then
|
||||
return off+4
|
||||
end
|
||||
aegisub.output_debug(string.format("bad utf-8 in %q at %d",str,off))
|
||||
return -1
|
||||
end
|
||||
|
||||
-- Get the number of characters in the UTF-8 string (not the number of bytes)
|
||||
function utf_len(str)
|
||||
local i = 1
|
||||
local len = 0
|
||||
while i<=string.len(str) do
|
||||
i = next_utf_char(str, i)
|
||||
len = len + 1
|
||||
end
|
||||
-- aegisub.output_debug(string.format("utf_len(%q)=%d",str,len))
|
||||
return len
|
||||
end
|
||||
|
||||
|
||||
-- Get the "head" and "tail" of a string, treating it as a sequence of words separated by one or more space-characters
|
||||
function string.headtail(s)
|
||||
local a, b, head, tail = string.find(s, "(.-)%s+(.*)")
|
||||
if a then
|
||||
return head, tail
|
||||
else
|
||||
return s, ""
|
||||
end
|
||||
end
|
BIN
code-docs/export-system-new-idea.vsd
Normal file
BIN
code-docs/export-system-new.vsd
Normal file
BIN
code-docs/export-system-original.vsd
Normal file
92
core/about.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include "about.h"
|
||||
#include "version.h"
|
||||
#include "options.h"
|
||||
#include <wx/statline.h>
|
||||
|
||||
|
||||
///////////////
|
||||
// Constructor
|
||||
AboutScreen::AboutScreen(wxWindow *parent,bool easter)
|
||||
: wxDialog (parent, -1, _("About Aegisub"), wxDefaultPosition, wxSize(300,240), wxSTAY_ON_TOP | wxCAPTION | wxCLOSE_BOX , _("About Aegisub"))
|
||||
{
|
||||
// Get splash
|
||||
wxBitmap splash;
|
||||
int splash_n = Options.AsInt(_T("Splash number"));
|
||||
if (splash_n < 1 || splash_n > 5) splash_n = (rand()%5)+1;
|
||||
if (splash_n == 1) splash = wxBITMAP(splash_01);
|
||||
if (splash_n == 2) splash = wxBITMAP(splash_02);
|
||||
if (splash_n == 3) splash = wxBITMAP(splash_03);
|
||||
if (splash_n == 4) splash = wxBITMAP(splash_04);
|
||||
if (splash_n == 5) splash = wxBITMAP(splash_05);
|
||||
|
||||
// Picture
|
||||
wxSizer *PicSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
PicSizer->Add(new BitmapControl(this,splash));
|
||||
|
||||
// Text sizer
|
||||
wxSizer *TextSizer = new wxBoxSizer(wxVERTICAL);
|
||||
TextSizer->Add(new wxStaticText(this,-1,wxString(_T("Aegisub ")) + VERSION_STRING + _(" by ArchMage ZeratuL.\n\nCopyright (c) 2005 - Rodrigo Braz Monteiro. All rights reserved.\nAutomation module and is Copyright (c) 2005 Niels Martin Hansen (aka jfs).\nAll rights reserved.\nCoding by ArchMageZeratuL, jfs, Myrsloik and nmap.\nManual by ArchMage ZeratuL, jfs, movax, Kobi, TheFluff and Jcubed.\nForum hosting by Malakith.\nSee the help file for full credits.")),1);
|
||||
|
||||
// Button sizer
|
||||
wxSizer *ButtonSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
ButtonSizer->AddStretchSpacer(1);
|
||||
ButtonSizer->Add(new wxButton(this,wxID_OK),0,0,0);
|
||||
|
||||
// Main sizer
|
||||
wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
MainSizer->Add(PicSizer,0,wxBOTTOM,5);
|
||||
MainSizer->Add(TextSizer,0,wxEXPAND | wxBOTTOM | wxRIGHT | wxLEFT,5);
|
||||
MainSizer->Add(new wxStaticLine(this,wxID_ANY),0,wxEXPAND | wxALL,5);
|
||||
MainSizer->Add(ButtonSizer,0,wxEXPAND | wxBOTTOM | wxRIGHT | wxLEFT,5);
|
||||
|
||||
// Set sizer
|
||||
MainSizer->SetSizeHints(this);
|
||||
SetSizer(MainSizer);
|
||||
|
||||
// Draw logo
|
||||
Centre();
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AboutScreen::~AboutScreen () {
|
||||
}
|
52
core/about.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include <wx/wxprec.h>
|
||||
#include "static_bmp.h"
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Splash screen class
|
||||
class AboutScreen: public wxDialog {
|
||||
public:
|
||||
AboutScreen(wxWindow *parent,bool easter=false);
|
||||
~AboutScreen();
|
||||
};
|
128
core/aegisublocale.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
#include <wx/intl.h>
|
||||
#include <locale.h>
|
||||
#include <wx/dir.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/choicdlg.h>
|
||||
#include "aegisublocale.h"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
///////////////
|
||||
// Constructor
|
||||
AegisubLocale::AegisubLocale () {
|
||||
locale = NULL;
|
||||
curCode = -1;
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Initialize
|
||||
void AegisubLocale::Init(int language) {
|
||||
if (language == -1) language = wxLANGUAGE_ENGLISH;
|
||||
if (locale) delete locale;
|
||||
curCode = language;
|
||||
locale = new wxLocale(language);
|
||||
locale->AddCatalogLookupPathPrefix(AegisubApp::folderName + _T("locale/"));
|
||||
locale->AddCatalog(_T("aegisub"));
|
||||
locale->AddCatalog(_T("wxstd"));
|
||||
setlocale(LC_NUMERIC, "English");
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// Pick a language
|
||||
int AegisubLocale::PickLanguage() {
|
||||
// Get list
|
||||
wxArrayInt langs = GetAvailableLanguages();
|
||||
|
||||
// Check if english is in it, else add it
|
||||
if (langs.Index(wxLANGUAGE_ENGLISH) == wxNOT_FOUND) {
|
||||
langs.Insert(wxLANGUAGE_ENGLISH,0);
|
||||
}
|
||||
|
||||
// Check if user local language is available, if so, make it first
|
||||
int user = wxLocale::GetSystemLanguage();
|
||||
if (langs.Index(user) != wxNOT_FOUND) {
|
||||
langs.Remove(user);
|
||||
langs.Insert(user,0);
|
||||
}
|
||||
|
||||
// Generate names
|
||||
wxArrayString langNames;
|
||||
for (size_t i=0;i<langs.Count();i++) {
|
||||
langNames.Add(wxLocale::GetLanguageName(langs[i]));
|
||||
}
|
||||
|
||||
// Nothing to pick
|
||||
if (langs.Count() == 0) return -1;
|
||||
|
||||
// Popup
|
||||
int picked = wxGetSingleChoiceIndex(_T("Please choose a language:"),_T("Language"),langNames,NULL,-1,-1,true,300,400);
|
||||
if (picked == -1) return -1;
|
||||
return langs[picked];
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////
|
||||
// Get list of available languages
|
||||
wxArrayInt AegisubLocale::GetAvailableLanguages() {
|
||||
wxArrayInt final;
|
||||
wxString temp1;
|
||||
|
||||
// Open directory
|
||||
wxString folder = AegisubApp::folderName + _T("/locale/");
|
||||
wxDir dir(folder);
|
||||
if (!dir.IsOpened()) return final;
|
||||
|
||||
// Enumerate folders
|
||||
for (bool cont = dir.GetFirst(&temp1,_T(""),wxDIR_DIRS);cont;cont = dir.GetNext(&temp1)) {
|
||||
// Check if .so exists inside folder
|
||||
wxFileName file(folder + temp1 + _T("/aegisub.mo"));
|
||||
if (file.FileExists()) {
|
||||
const wxLanguageInfo *lang = wxLocale::FindLanguageInfo(temp1);
|
||||
if (lang) {
|
||||
final.Add(lang->Language);
|
||||
}
|
||||
}
|
||||
}
|
||||
return final;
|
||||
}
|
61
core/aegisublocale.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
#ifndef LOCALE_H
|
||||
#define LOCALE_H
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class wxLocale;
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Aegisub locale class
|
||||
class AegisubLocale {
|
||||
private:
|
||||
wxLocale *locale;
|
||||
wxArrayInt GetAvailableLanguages();
|
||||
|
||||
public:
|
||||
int curCode;
|
||||
|
||||
AegisubLocale();
|
||||
void Init(int language);
|
||||
int PickLanguage();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
94
core/aspell_wrap.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) 2005, Ghassan Nassar
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
#ifndef NO_SPELLCHECKER
|
||||
|
||||
//
|
||||
//Includes
|
||||
//
|
||||
#include ".\aspell_wrap.h"
|
||||
|
||||
|
||||
//
|
||||
// Aspell Constructor
|
||||
//
|
||||
AspellWrapper::AspellWrapper(void)
|
||||
{
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
//
|
||||
// AviSynth destructor
|
||||
//
|
||||
AspellWrapper::~AspellWrapper(void)
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
//
|
||||
// Load Aspell-15.dll
|
||||
//
|
||||
void AspellWrapper::Load() {
|
||||
if (!loaded) {
|
||||
hLib=LoadLibrary(L"aspell-15.dll");
|
||||
if (hLib == NULL) {
|
||||
throw L"Could not load aspell.dll";
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Unloads Aspell-15.dll
|
||||
//
|
||||
void AspellWrapper::Unload() {
|
||||
if (loaded) {
|
||||
//delete_aspell_config();
|
||||
FreeLibrary(hLib);
|
||||
loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Declare global
|
||||
//
|
||||
AspellWrapper Aspell;
|
||||
|
||||
#endif
|
68
core/aspell_wrap.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) 2005, Ghassan Nassar
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
#ifndef NO_SPELLCHECKER
|
||||
|
||||
#ifndef ASPELL_WRAP_H_RKEY9023809128309182
|
||||
#define ASPELL_WRAP_H_RKEY9023809128309182
|
||||
|
||||
//////////
|
||||
// Headers
|
||||
extern "C" {
|
||||
#include <aspell.h>
|
||||
}
|
||||
#include <windows.h>
|
||||
#include <wx/wxprec.h>
|
||||
|
||||
class AspellWrapper
|
||||
{
|
||||
public:
|
||||
AspellWrapper(void);
|
||||
~AspellWrapper(void);
|
||||
void Load();
|
||||
void Unload();
|
||||
private:
|
||||
void Initiate();
|
||||
bool loaded;
|
||||
HINSTANCE hLib;
|
||||
//wxMutex AviSynthMutex; //ehhhh? Sempahores?
|
||||
|
||||
};
|
||||
|
||||
extern AspellWrapper Aspell;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
819
core/ass_dialogue.cpp
Normal file
|
@ -0,0 +1,819 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_override.h"
|
||||
#include "vfr.h"
|
||||
#include "utils.h"
|
||||
#include <fstream>
|
||||
#include <wx/tokenzr.h>
|
||||
|
||||
|
||||
////////////////////// AssEntry //////////////////////
|
||||
///////////////////////
|
||||
// Constructs AssEntry
|
||||
AssEntry::AssEntry() {
|
||||
Type = ENTRY_BASE;
|
||||
Valid = true;
|
||||
}
|
||||
|
||||
AssEntry::AssEntry(wxString _data) {
|
||||
data = _data;
|
||||
Type = ENTRY_BASE;
|
||||
Valid = true;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// Destructor for AssEntry
|
||||
AssEntry::~AssEntry() {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// Comparison for STL Sort
|
||||
bool operator < (const AssEntry &t1, const AssEntry &t2) {
|
||||
return (t1.StartMS < t2.StartMS);
|
||||
}
|
||||
|
||||
|
||||
////////////////////// AssDialogue //////////////////////
|
||||
// Constructs AssDialogue
|
||||
AssDialogue::AssDialogue() {
|
||||
Type = ENTRY_DIALOGUE;
|
||||
group = _T("[Events]");
|
||||
|
||||
Valid = true;
|
||||
Start.SetMS(0);
|
||||
End.SetMS(5000);
|
||||
StartMS = 0;
|
||||
Layer = 0;
|
||||
MarginR = MarginL = MarginV = 0;
|
||||
Text = _T("");
|
||||
Style = _T("Default");
|
||||
Actor = _T("");
|
||||
Effect = _T("");
|
||||
Comment = false;
|
||||
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
|
||||
AssDialogue::AssDialogue(wxString _data,bool IsSSA) {
|
||||
Type = ENTRY_DIALOGUE;
|
||||
group = _T("[Events]");
|
||||
data = _data;
|
||||
Valid = Parse(IsSSA);
|
||||
if (!Valid) {
|
||||
throw _T("Failed parsing line.");
|
||||
}
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssDialogue::~AssDialogue () {
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
/////////
|
||||
// Clear
|
||||
void AssDialogue::Clear () {
|
||||
using std::vector;
|
||||
for (vector<AssDialogueBlock*>::iterator cur=Blocks.begin();cur!=Blocks.end();cur++) {
|
||||
delete *cur;
|
||||
}
|
||||
Blocks.clear();
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Parse ASS Data
|
||||
bool AssDialogue::Parse(bool IsSSA) {
|
||||
size_t pos = 0;
|
||||
wxString temp;
|
||||
|
||||
// Get type
|
||||
if (data.substr(pos,9) == _T("Dialogue:")) {
|
||||
Comment = false;
|
||||
pos = 10;
|
||||
}
|
||||
else if (data.substr(pos,8) == _T("Comment:")) {
|
||||
Comment = true;
|
||||
pos = 9;
|
||||
}
|
||||
else return false;
|
||||
wxStringTokenizer tkn(data.Mid(pos),_T(","),wxTOKEN_RET_EMPTY_ALL);
|
||||
|
||||
// Get layer number
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken().Trim(false).Trim(true);
|
||||
if (IsSSA) Layer = 0;
|
||||
else {
|
||||
long templ;
|
||||
temp.ToLong(&templ);
|
||||
Layer = templ;
|
||||
}
|
||||
|
||||
// Get start time
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
Start.ParseASS(tkn.GetNextToken());
|
||||
StartMS = Start.GetMS();
|
||||
|
||||
// Get end time
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
End.ParseASS(tkn.GetNextToken());
|
||||
|
||||
// Get style
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
Style = tkn.GetNextToken();
|
||||
Style.Trim(true);
|
||||
Style.Trim(false);
|
||||
|
||||
// Get actor
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
Actor = tkn.GetNextToken();
|
||||
Actor.Trim(true);
|
||||
Actor.Trim(false);
|
||||
|
||||
// Get left margin
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
SetMarginString(tkn.GetNextToken().Trim(false).Trim(true),1);
|
||||
|
||||
// Get right margin
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
SetMarginString(tkn.GetNextToken().Trim(false).Trim(true),2);
|
||||
|
||||
// Get vertical margin
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
SetMarginString(tkn.GetNextToken().Trim(false).Trim(true),3);
|
||||
|
||||
// Get effect
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
Effect = tkn.GetNextToken();
|
||||
Effect.Trim(true);
|
||||
Effect.Trim(false);
|
||||
|
||||
// Get text
|
||||
Text = tkn.GetNextToken();
|
||||
while (tkn.HasMoreTokens()) {
|
||||
Text += _T(",");
|
||||
Text += tkn.GetNextToken();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////
|
||||
// Update AssDialogue's data line
|
||||
void AssDialogue::UpdateData () {
|
||||
// Prepare
|
||||
data = _T("");
|
||||
|
||||
// Write all data
|
||||
if (Comment) data += _T("Comment: ");
|
||||
else data += _T("Dialogue: ");
|
||||
|
||||
data += wxString::Format(_T("%01i"),Layer);
|
||||
data += _T(",");
|
||||
|
||||
data += Start.GetASSFormated() + _T(",");
|
||||
data += End.GetASSFormated() + _T(",");
|
||||
|
||||
Style.Replace(_T(","),_T(";"));
|
||||
Actor.Replace(_T(","),_T(";"));
|
||||
data += Style + _T(",");
|
||||
data += Actor + _T(",");
|
||||
|
||||
data += GetMarginString(1);
|
||||
data += _T(",");
|
||||
data += GetMarginString(2);
|
||||
data += _T(",");
|
||||
data += GetMarginString(3);
|
||||
data += _T(",");
|
||||
|
||||
Effect.Replace(_T(","),_T(";"));
|
||||
data += Effect + _T(",");
|
||||
data += Text;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// Get SSA version of Dialogue
|
||||
wxString AssDialogue::GetSSAText () {
|
||||
// Prepare
|
||||
wxString work = _T("");
|
||||
|
||||
// Write all work
|
||||
if (Comment) work += _T("Comment: ");
|
||||
else work += _T("Dialogue: ");
|
||||
|
||||
work += _T("Marked=0,");
|
||||
|
||||
work += Start.GetASSFormated() + _T(",");
|
||||
work += End.GetASSFormated() + _T(",");
|
||||
|
||||
Style.Replace(_T(","),_T(";"));
|
||||
Actor.Replace(_T(","),_T(";"));
|
||||
work += Style + _T(",");
|
||||
work += Actor + _T(",");
|
||||
|
||||
work += GetMarginString(1);
|
||||
work += _T(",");
|
||||
work += GetMarginString(2);
|
||||
work += _T(",");
|
||||
work += GetMarginString(3);
|
||||
work += _T(",");
|
||||
|
||||
Effect.Replace(_T(","),_T(";"));
|
||||
work += Effect + _T(",");
|
||||
work += Text;
|
||||
|
||||
return work;
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Parse SRT tags
|
||||
// --------------
|
||||
// Yea, I convert to ASS tags, then parse that. So sue me.
|
||||
void AssDialogue::ParseSRTTags () {
|
||||
// Search and replace
|
||||
size_t total = 0;
|
||||
total += Text.Replace(_T("<i>"),_T("{\\i1}"));
|
||||
total += Text.Replace(_T("</i>"),_T("{\\i0}"));
|
||||
total += Text.Replace(_T("<b>"),_T("{\\b1}"));
|
||||
total += Text.Replace(_T("</b>"),_T("{\\b0}"));
|
||||
total += Text.Replace(_T("<u>"),_T("{\\u1}"));
|
||||
total += Text.Replace(_T("</u>"),_T("{\\u0}"));
|
||||
total += Text.Replace(_T("<s>"),_T("{\\s1}"));
|
||||
total += Text.Replace(_T("</s>"),_T("{\\s0}"));
|
||||
|
||||
// Process <font> tag
|
||||
wxString work = Text;
|
||||
work.UpperCase();
|
||||
size_t pos_open = 0;
|
||||
size_t pos_close = 0;
|
||||
size_t pos = 0;
|
||||
size_t end = 0;
|
||||
size_t start = 0;
|
||||
bool isOpen;
|
||||
|
||||
// Iterate
|
||||
pos_open = work.find(_T("<FONT"),0);
|
||||
pos_close = work.find(_T("</FONT"),0);
|
||||
while (pos_open != wxString::npos || pos_close != wxString::npos) {
|
||||
// Determine if it's an open or close tag
|
||||
if (pos_open < pos_close) {
|
||||
start = pos_open;
|
||||
isOpen = true;
|
||||
}
|
||||
else {
|
||||
start = pos_close;
|
||||
isOpen = false;
|
||||
}
|
||||
end = work.find(_T(">"),start)+1;
|
||||
//if (end == wxString::npos) continue;
|
||||
|
||||
// Open tag
|
||||
if (isOpen) {
|
||||
wxString replaced = _T("");
|
||||
|
||||
// Color tag
|
||||
if ((pos = work.find(_T("COLOR=\""),start)) != wxString::npos) {
|
||||
if (pos < end) {
|
||||
pos += 7;
|
||||
size_t end_tag = Text.find(_T("\""),pos);
|
||||
if (end_tag != wxString::npos) {
|
||||
if (end_tag-pos == 7) {
|
||||
replaced += _T("{\\c&H");
|
||||
replaced += work.substr(pos+5,2);
|
||||
replaced += work.substr(pos+3,2);
|
||||
replaced += work.substr(pos+1,2);
|
||||
replaced += _T("&}");
|
||||
total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Face tag
|
||||
if ((pos = work.find(_T("FACE=\""),start)) != wxString::npos) {
|
||||
if (pos < end) {
|
||||
pos += 6;
|
||||
size_t end_tag = work.find(_T("\""),pos);
|
||||
if (end_tag != wxString::npos) {
|
||||
replaced += _T("{\\fn");
|
||||
replaced += work.substr(pos,end_tag-pos);
|
||||
replaced += _T("}");
|
||||
total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Size tag
|
||||
if ((pos = work.find(_T("SIZE=\""),start)) != wxString::npos) {
|
||||
if (pos < end) {
|
||||
pos += 6;
|
||||
size_t end_tag = Text.find(_T("\""),pos);
|
||||
if (end_tag != wxString::npos) {
|
||||
replaced += _T("{\\fs");
|
||||
replaced += work.substr(pos,end_tag-pos);
|
||||
replaced += _T("}");
|
||||
total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace whole tag
|
||||
Text = Text.substr(0,start) + replaced + Text.substr(end);
|
||||
total++;
|
||||
}
|
||||
|
||||
// Close tag
|
||||
else {
|
||||
// Find if it's italic, bold, underline, and strikeout
|
||||
wxString prev = Text.Left(start);
|
||||
bool isItalic=false,isBold=false,isUnder=false,isStrike=false;
|
||||
if (CountMatches(prev,_T("{\\i1}")) > CountMatches(prev,_T("{\\i0}"))) isItalic = true;
|
||||
if (CountMatches(prev,_T("{\\b1}")) > CountMatches(prev,_T("{\\b0}"))) isBold = true;
|
||||
if (CountMatches(prev,_T("{\\u1}")) > CountMatches(prev,_T("{\\u0}"))) isUnder = true;
|
||||
if (CountMatches(prev,_T("{\\s1}")) > CountMatches(prev,_T("{\\s0}"))) isStrike = true;
|
||||
|
||||
// Generate new tag, by reseting and then restoring flags
|
||||
wxString replaced = _T("{\\r");
|
||||
if (isItalic) replaced += _T("\\i1");
|
||||
if (isBold) replaced += _T("\\b1");
|
||||
if (isUnder) replaced += _T("\\u1");
|
||||
if (isStrike) replaced += _T("\\s1");
|
||||
replaced += _T("}");
|
||||
|
||||
// Replace
|
||||
Text = Text.substr(0,start) + replaced + Text.substr(end);
|
||||
total++;
|
||||
}
|
||||
|
||||
// Get next
|
||||
work = Text;
|
||||
work.UpperCase();
|
||||
pos_open = work.find(_T("<FONT"),0);
|
||||
pos_close = work.find(_T("</FONT"),0);
|
||||
}
|
||||
|
||||
// Remove double tagging
|
||||
Text.Replace(_T("}{"),_T(""));
|
||||
|
||||
// Update all stuff
|
||||
ParseASSTags();
|
||||
if (total > 0) UpdateText();
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Parse ASS tags
|
||||
void AssDialogue::ParseASSTags () {
|
||||
// Clear blocks
|
||||
for (size_t i=0;i<Blocks.size();i++) {
|
||||
delete Blocks.at(i);
|
||||
}
|
||||
Blocks.clear();
|
||||
|
||||
// Is drawing?
|
||||
int drawingLevel = 0;
|
||||
|
||||
// Loop through
|
||||
const size_t len = Text.size();
|
||||
size_t cur = 0;
|
||||
size_t end = 0;
|
||||
while (cur < len) {
|
||||
// Overrides block
|
||||
if (Text[cur] == '{') {
|
||||
// Get contents of block
|
||||
wxString work;
|
||||
end = Text.find(_T("}"),cur);
|
||||
if (end == wxString::npos) {
|
||||
work = Text.substr(cur);
|
||||
end = len;
|
||||
}
|
||||
else work = Text.substr(cur,end-cur);
|
||||
work = Text.substr(cur+1,end-cur-1);
|
||||
|
||||
// Create block
|
||||
AssDialogueBlockOverride *block = new AssDialogueBlockOverride;
|
||||
block->parent = this;
|
||||
block->text = work;
|
||||
block->ParseTags();
|
||||
Blocks.push_back(block);
|
||||
|
||||
// Look for \p in block
|
||||
std::vector<AssOverrideTag*>::iterator curTag;
|
||||
for (curTag = block->Tags.begin();curTag != block->Tags.end();curTag++) {
|
||||
AssOverrideTag *tag = *curTag;
|
||||
if (tag->Name == _T("\\p")) {
|
||||
drawingLevel = tag->Params.at(0)->AsInt();
|
||||
}
|
||||
}
|
||||
|
||||
// Increase
|
||||
cur = end+1;
|
||||
}
|
||||
|
||||
// Plain-text/drawing block
|
||||
else {
|
||||
wxString work;
|
||||
end = Text.find(_T("{"),cur);
|
||||
if (end == wxString::npos) {
|
||||
work = Text.substr(cur);
|
||||
end = len;
|
||||
}
|
||||
else work = Text.substr(cur,end-cur);
|
||||
|
||||
// Plain-text
|
||||
if (drawingLevel == 0) {
|
||||
AssDialogueBlockPlain *block = new AssDialogueBlockPlain;
|
||||
block->text = work;
|
||||
Blocks.push_back(block);
|
||||
}
|
||||
|
||||
// Drawing
|
||||
else {
|
||||
AssDialogueBlockDrawing *block = new AssDialogueBlockDrawing;
|
||||
block->text = work;
|
||||
block->Scale = drawingLevel;
|
||||
Blocks.push_back(block);
|
||||
}
|
||||
|
||||
cur = end;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty line, make an empty block
|
||||
if (len == 0) {
|
||||
AssDialogueBlockPlain *block = new AssDialogueBlockPlain;
|
||||
block->text = _T("");
|
||||
Blocks.push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Strip tags
|
||||
void AssDialogue::StripTags () {
|
||||
using std::list;
|
||||
using std::vector;
|
||||
vector<AssDialogueBlock*>::iterator next;
|
||||
for (vector<AssDialogueBlock*>::iterator cur=Blocks.begin();cur!=Blocks.end();cur=next) {
|
||||
next = cur;
|
||||
next++;
|
||||
if ((*cur)->type == BLOCK_OVERRIDE) {
|
||||
delete *cur;
|
||||
Blocks.erase(cur);
|
||||
}
|
||||
}
|
||||
UpdateText();
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Convert tags to SRT
|
||||
// -------------------
|
||||
// TODO: Improve this code
|
||||
//
|
||||
void AssDialogue::ConvertTagsToSRT () {
|
||||
// Setup
|
||||
using std::list;
|
||||
using std::vector;
|
||||
AssDialogueBlockOverride* curBlock;
|
||||
AssDialogueBlockPlain *curPlain;
|
||||
AssOverrideTag* curTag;
|
||||
wxString final = _T("");
|
||||
bool isItalic=false,isBold=false,isUnder=false,isStrike=false;
|
||||
bool temp;
|
||||
|
||||
// Iterate through blocks
|
||||
for (size_t i=0;i<Blocks.size();i++) {
|
||||
curBlock = AssDialogueBlock::GetAsOverride(Blocks.at(i));
|
||||
if (curBlock) {
|
||||
// Iterate through overrides
|
||||
for (size_t j=0;j<curBlock->Tags.size();j++) {
|
||||
curTag = curBlock->Tags.at(j);
|
||||
if (curTag->IsValid()) {
|
||||
// Italics
|
||||
if (curTag->Name == _T("\\i")) {
|
||||
temp = curTag->Params.at(0)->AsBool();
|
||||
if (temp && !isItalic) {
|
||||
isItalic = true;
|
||||
final += _T("<i>");
|
||||
}
|
||||
if (!temp && isItalic) {
|
||||
isItalic = false;
|
||||
final += _T("</i>");
|
||||
}
|
||||
}
|
||||
|
||||
// Underline
|
||||
if (curTag->Name == _T("\\u")) {
|
||||
temp = curTag->Params.at(0)->AsBool();
|
||||
if (temp && !isUnder) {
|
||||
isUnder = true;
|
||||
final += _T("<u>");
|
||||
}
|
||||
if (!temp && isUnder) {
|
||||
isUnder = false;
|
||||
final += _T("</u>");
|
||||
}
|
||||
}
|
||||
|
||||
// Strikeout
|
||||
if (curTag->Name == _T("\\s")) {
|
||||
temp = curTag->Params.at(0)->AsBool();
|
||||
if (temp && !isStrike) {
|
||||
isStrike = true;
|
||||
final += _T("<s>");
|
||||
}
|
||||
if (!temp && isStrike) {
|
||||
isStrike = false;
|
||||
final += _T("</s>");
|
||||
}
|
||||
}
|
||||
|
||||
// Bold
|
||||
if (curTag->Name == _T("\\b")) {
|
||||
temp = curTag->Params.at(0)->AsBool();
|
||||
if (temp && !isBold) {
|
||||
isBold = true;
|
||||
final += _T("<b>");
|
||||
}
|
||||
if (!temp && isBold) {
|
||||
isBold = false;
|
||||
final += _T("</b>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Plain text
|
||||
else {
|
||||
curPlain = AssDialogueBlock::GetAsPlain(Blocks.at(i));
|
||||
if (curPlain) {
|
||||
final += curPlain->GetText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text = final;
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
// Updates text from tags
|
||||
void AssDialogue::UpdateText () {
|
||||
using std::vector;
|
||||
Text = _T("");
|
||||
for (vector<AssDialogueBlock*>::iterator cur=Blocks.begin();cur!=Blocks.end();cur++) {
|
||||
if ((*cur)->type == BLOCK_OVERRIDE) {
|
||||
Text += _T("{");
|
||||
Text += (*cur)->GetText();
|
||||
Text += _T("}");
|
||||
}
|
||||
else Text += (*cur)->GetText();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Sets margin from a string
|
||||
void AssDialogue::SetMarginString(const wxString origvalue,int which) {
|
||||
// Make it numeric
|
||||
wxString strvalue = origvalue;
|
||||
if (!strvalue.IsNumber()) {
|
||||
strvalue = _T("");
|
||||
for (size_t i=0;i<origvalue.Length();i++) {
|
||||
if (origvalue.Mid(i,1).IsNumber()) {
|
||||
strvalue += origvalue.Mid(i,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get value
|
||||
long value;
|
||||
strvalue.ToLong(&value);
|
||||
|
||||
// Cap it
|
||||
if (value < 0) value = 0;
|
||||
if (value > 9999) value = 9999;
|
||||
|
||||
// Assign
|
||||
switch (which) {
|
||||
case 1: MarginL = value; break;
|
||||
case 2: MarginR = value; break;
|
||||
case 3: MarginV = value; break;
|
||||
default: throw _T("Invalid margin");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
// Gets string for margin
|
||||
wxString AssDialogue::GetMarginString(int which) {
|
||||
int value;
|
||||
switch (which) {
|
||||
case 1: value = MarginL; break;
|
||||
case 2: value = MarginR; break;
|
||||
case 3: value = MarginV; break;
|
||||
default: throw _T("Invalid margin");
|
||||
}
|
||||
wxString result = wxString::Format(_T("%04i"),value);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////
|
||||
// Process parameters via callback
|
||||
void AssDialogue::ProcessParameters(void (*callback)(wxString tagName,int par_n,AssOverrideParameter *param,void *userData),void *userData) {
|
||||
// Apply for all override blocks
|
||||
AssDialogueBlockOverride *curBlock;
|
||||
for (std::vector<AssDialogueBlock*>::iterator cur=Blocks.begin();cur!=Blocks.end();cur++) {
|
||||
if ((*cur)->type == BLOCK_OVERRIDE) {
|
||||
curBlock = static_cast<AssDialogueBlockOverride*> (*cur);
|
||||
curBlock->ProcessParameters(callback,userData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// Checks if two lines collide
|
||||
bool AssDialogue::CollidesWith(AssDialogue *target) {
|
||||
if (!target) return false;
|
||||
int a = Start.GetMS();
|
||||
int b = End.GetMS();
|
||||
int c = target->Start.GetMS();
|
||||
int d = target->End.GetMS();
|
||||
return ((a < c) ? (c < b) : (a < d));
|
||||
}
|
||||
|
||||
|
||||
////////////////////// AssDialogueBlock //////////////////////
|
||||
///////////////
|
||||
// Constructor
|
||||
AssDialogueBlock::AssDialogueBlock () {
|
||||
type = BLOCK_BASE;
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssDialogueBlock::~AssDialogueBlock () {
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// Returns as a plain block
|
||||
// ----------------------
|
||||
// If it isn't a plain block, returns NULL
|
||||
AssDialogueBlockPlain *AssDialogueBlock::GetAsPlain(AssDialogueBlock *base) {
|
||||
if (!base) return NULL;
|
||||
if (base->type == BLOCK_PLAIN) {
|
||||
return static_cast<AssDialogueBlockPlain*> (base);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////
|
||||
// Returns as an override block
|
||||
// ----------------------------
|
||||
// If it isn't an override block, returns NULL
|
||||
AssDialogueBlockOverride *AssDialogueBlock::GetAsOverride(AssDialogueBlock *base) {
|
||||
if (!base) return NULL;
|
||||
if (base->type == BLOCK_OVERRIDE) {
|
||||
return static_cast<AssDialogueBlockOverride*> (base);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// Returns as a drawing block
|
||||
// ----------------------------
|
||||
// If it isn't an drawing block, returns NULL
|
||||
AssDialogueBlockDrawing *AssDialogueBlock::GetAsDrawing(AssDialogueBlock *base) {
|
||||
if (!base) return NULL;
|
||||
if (base->type == BLOCK_DRAWING) {
|
||||
return static_cast<AssDialogueBlockDrawing*> (base);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
////////////////////// AssDialogueBlockPlain //////////////////////
|
||||
///////////////
|
||||
// Constructor
|
||||
AssDialogueBlockPlain::AssDialogueBlockPlain () {
|
||||
type = BLOCK_PLAIN;
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// Return the text
|
||||
wxString AssDialogueBlockPlain::GetText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
////////////////////// AssDialogueBlockDrawing //////////////////////
|
||||
///////////////
|
||||
// Constructor
|
||||
AssDialogueBlockDrawing::AssDialogueBlockDrawing () {
|
||||
type = BLOCK_DRAWING;
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// Return the text
|
||||
wxString AssDialogueBlockDrawing::GetText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Multiply coordinates
|
||||
void AssDialogueBlockDrawing::MultiplyCoords(double x,double y) {
|
||||
// HACK: Implement a proper parser ffs!!
|
||||
wxStringTokenizer tkn(GetText(),_T(" "),wxTOKEN_DEFAULT);
|
||||
wxString cur;
|
||||
wxString final;
|
||||
bool isX = true;
|
||||
long temp;
|
||||
|
||||
// Process tokens
|
||||
while (tkn.HasMoreTokens()) {
|
||||
cur = tkn.GetNextToken().Lower();
|
||||
|
||||
// Number, process it
|
||||
if (cur.IsNumber()) {
|
||||
// Multiply it
|
||||
cur.ToLong(&temp);
|
||||
if (isX) temp = temp*x + 0.5;
|
||||
else temp = temp*y + 0.5;
|
||||
|
||||
// Write back to list
|
||||
final += wxString::Format(_T("%i "),temp);
|
||||
|
||||
// Toggle X/Y
|
||||
isX = !isX;
|
||||
}
|
||||
|
||||
// Text
|
||||
else {
|
||||
if (cur == _T("m") || cur == _T("n") || cur == _T("l") || cur == _T("b") || cur == _T("s") || cur == _T("p") || cur == _T("c")) isX = true;
|
||||
final += cur + _T(" ");
|
||||
}
|
||||
}
|
||||
|
||||
// Write back final
|
||||
final = final.Left(final.Length()-1);
|
||||
text = final;
|
||||
}
|
186
core/ass_dialogue.h
Normal file
|
@ -0,0 +1,186 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include <vector>
|
||||
#include "ass_entry.h"
|
||||
#include "ass_time.h"
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AssOverrideParameter;
|
||||
class AssOverrideTag;
|
||||
class AssDialogueBlockPlain;
|
||||
class AssDialogueBlockOverride;
|
||||
class AssDialogueBlockDrawing;
|
||||
|
||||
|
||||
///////////////////
|
||||
// Block type enum
|
||||
enum ASS_BlockType {
|
||||
BLOCK_BASE,
|
||||
BLOCK_PLAIN,
|
||||
BLOCK_OVERRIDE,
|
||||
BLOCK_DRAWING
|
||||
};
|
||||
|
||||
|
||||
//////////////////////
|
||||
// AssDialogue Blocks
|
||||
// ------------------
|
||||
// A block is each group in the text field of an AssDialogue
|
||||
// For example:
|
||||
// Yes, I {\i1}am{\i0} here.
|
||||
//
|
||||
// Gets split in five blocks:
|
||||
// "Yes, I " (Plain)
|
||||
// "\i1" (Override)
|
||||
// "am" (Plain)
|
||||
// "\i0" (Override)
|
||||
// " here." (Plain)
|
||||
//
|
||||
// Also note how {}s are discarded.
|
||||
// Override blocks are further divided in AssOverrideTag's.
|
||||
//
|
||||
// The GetText() method generates a new value for the "text" field from
|
||||
// the other fields in the specific class, and returns the new value.
|
||||
//
|
||||
// TODO: Support for {\p#}...{\p0}
|
||||
//
|
||||
class AssDialogueBlock {
|
||||
public:
|
||||
wxString text;
|
||||
ASS_BlockType type;
|
||||
AssDialogue *parent;
|
||||
|
||||
AssDialogueBlock();
|
||||
virtual ~AssDialogueBlock();
|
||||
|
||||
virtual wxString GetText() = 0; // make the class abstract
|
||||
static AssDialogueBlockPlain *GetAsPlain(AssDialogueBlock *base); // Returns a block base as a plain block if it is valid, null otherwise
|
||||
static AssDialogueBlockOverride *GetAsOverride(AssDialogueBlock *base); // Returns a block base as an override block if it is valid, null otherwise
|
||||
static AssDialogueBlockDrawing *GetAsDrawing(AssDialogueBlock *base); // Returns a block base as a drawing block if it is valid, null otherwise
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Plain dialogue block text
|
||||
// -------------------------
|
||||
// This is used for standard text
|
||||
// type = BLOCK_PLAIN
|
||||
//
|
||||
class AssDialogueBlockPlain : public AssDialogueBlock {
|
||||
public:
|
||||
AssDialogueBlockPlain();
|
||||
wxString GetText();
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Plain dialogue block text
|
||||
// -------------------------
|
||||
// This is used for drawing commands
|
||||
// type = BLOCK_DRAWING
|
||||
//
|
||||
class AssDialogueBlockDrawing : public AssDialogueBlock {
|
||||
public:
|
||||
int Scale;
|
||||
|
||||
AssDialogueBlockDrawing();
|
||||
void MultiplyCoords(double x,double y);
|
||||
wxString GetText();
|
||||
};
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Override tag block
|
||||
// ------------------
|
||||
// Used to store ASS overrides
|
||||
// Type = BLOCK_OVERRIDE
|
||||
//
|
||||
class AssDialogueBlockOverride : public AssDialogueBlock {
|
||||
public:
|
||||
AssDialogueBlockOverride();
|
||||
~AssDialogueBlockOverride();
|
||||
std::vector<AssOverrideTag*> Tags;
|
||||
|
||||
wxString GetText();
|
||||
void ParseTags(); // Parses tags
|
||||
void ProcessParameters(void (*callback)(wxString,int,AssOverrideParameter*,void *),void *userData);
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class for Dialogue and Comment lines
|
||||
class AssDialogue : public AssEntry {
|
||||
public:
|
||||
std::vector<AssDialogueBlock*> Blocks; // Contains information about each block of text
|
||||
|
||||
bool Comment; // Is this a comment line?
|
||||
int Layer; // Layer number
|
||||
int MarginR; // Right margin
|
||||
int MarginL; // Left margin
|
||||
int MarginV; // Vertical margin
|
||||
AssTime Start; // Starting time
|
||||
AssTime End; // Ending time
|
||||
wxString Style; // Style name
|
||||
wxString Actor; // Actor name
|
||||
wxString Effect; // Effect name
|
||||
wxString Text; // Raw text data
|
||||
|
||||
bool Parse(bool IsSSA=false); // Parses raw ASS data into everything else
|
||||
void ParseASSTags(); // Parses text to generate block information (doesn't update data)
|
||||
void ParseSRTTags(); // Converts tags to ass format and calls ParseASSTags+UpdateData
|
||||
void UpdateData(); // Updates raw data from current values + text
|
||||
void UpdateText(); // Generates text from the override tags
|
||||
void ConvertTagsToSRT(); // Converts tags to SRT format
|
||||
void StripTags(); // Strips all tags from the text
|
||||
void Clear(); // Wipes all data
|
||||
void SetMarginString(const wxString value,int which); // Set string to a margin value (1 = left, 2 = right, 3 = vertical)
|
||||
wxString GetMarginString(int which); // Returns the string of a margin value (1 = left, 2 = right, 3 = vertical)
|
||||
void ProcessParameters(void (*callback)(wxString,int,AssOverrideParameter*,void *userData),void *userData=NULL); // Callback to process parameters
|
||||
wxString GetSSAText();
|
||||
bool CollidesWith(AssDialogue *target); // Checks if two lines collide
|
||||
|
||||
AssDialogue();
|
||||
AssDialogue(wxString data,bool IsSSA=false);
|
||||
~AssDialogue();
|
||||
};
|
76
core/ass_entry.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_style.h"
|
||||
#include "ass_entry.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Returns an entry as dialogue if possible, else, returns NULL
|
||||
AssDialogue *AssEntry::GetAsDialogue(AssEntry *base) {
|
||||
if (!base) return NULL;
|
||||
if (base->Type == ENTRY_DIALOGUE) {
|
||||
return static_cast<AssDialogue*> (base);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Returns an entry as style if possible, else, returns NULL
|
||||
AssStyle *AssEntry::GetAsStyle(AssEntry *base) {
|
||||
if (!base) return NULL;
|
||||
if (base->Type == ENTRY_STYLE) {
|
||||
return static_cast<AssStyle*> (base);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Get SSA conversion
|
||||
wxString AssEntry::GetSSAText() {
|
||||
if (data.Lower() == _T("[v4+ styles]")) return wxString(_T("[V4 Styles]"));
|
||||
if (data.Lower() == _T("scripttype: v4.00+")) return wxString(_T("ScriptType: v4.00"));
|
||||
if (data.Lower().Left(7) == _T("format:")) {
|
||||
if (group.Lower() == _T("[events]")) return wxString(_T("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"));
|
||||
if (group.Lower() == _T("[v4+ styles]")) return wxString(_T("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"));
|
||||
}
|
||||
return data;
|
||||
}
|
80
core/ass_entry.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AssDialogue;
|
||||
class AssStyle;
|
||||
|
||||
|
||||
///////////////////
|
||||
// Entry type enum
|
||||
enum ASS_EntryType {
|
||||
ENTRY_BASE,
|
||||
ENTRY_DIALOGUE,
|
||||
ENTRY_STYLE
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// Base class for each line in file
|
||||
class AssEntry {
|
||||
public:
|
||||
int StartMS; // This is only stored for sorting issues, in order to keep non-dialogue lines aligned
|
||||
bool Valid; // Flags as valid or not
|
||||
wxString data; // Raw data, exactly the same line that appears on the .ass (note that this will be in ass even if source wasn't)
|
||||
wxString group; // Group it belongs to, e.g. "[Events]"
|
||||
ASS_EntryType Type; // Defines if this is a dialogue, style, or unknown line
|
||||
|
||||
AssEntry();
|
||||
AssEntry(wxString data);
|
||||
virtual ~AssEntry();
|
||||
|
||||
virtual wxString GetSSAText();
|
||||
static AssDialogue *GetAsDialogue(AssEntry *base); // Returns an entry base as a dialogue if it is valid, null otherwise
|
||||
static AssStyle *GetAsStyle(AssEntry *base); // Returns an entry base as a style if it is valid, null otherwise
|
||||
};
|
||||
|
||||
// This operator is for sorting
|
||||
bool operator < (const AssEntry &t1, const AssEntry &t2);
|
177
core/ass_export_filter.cpp
Normal file
|
@ -0,0 +1,177 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include "ass_export_filter.h"
|
||||
#include "ass_file.h"
|
||||
|
||||
|
||||
///////////////
|
||||
// Constructor
|
||||
AssExportFilter::AssExportFilter() {
|
||||
autoExporter = false;
|
||||
initialized = false;
|
||||
FilterList *fil = AssExportFilterChain::GetUnpreparedFilterList();
|
||||
fil->push_back(this);
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssExportFilter::~AssExportFilter() {
|
||||
try {
|
||||
Unregister();
|
||||
}
|
||||
catch (...) {
|
||||
// Ignore error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////
|
||||
// Register
|
||||
void AssExportFilter::Register (wxString name,int priority) {
|
||||
// Check if it's registered
|
||||
if (RegisterName != _T("")) {
|
||||
throw _T("Register export filter: filter with name \"") + name + _T("\" is already registered.");
|
||||
}
|
||||
|
||||
// Remove pipes from name
|
||||
name.Replace(_T("|"),_T(""));
|
||||
|
||||
// Check if name exists
|
||||
FilterList::iterator begin = AssExportFilterChain::GetFilterList()->begin();
|
||||
FilterList::iterator end = AssExportFilterChain::GetFilterList()->end();
|
||||
for (FilterList::iterator cur=begin;cur!=end;cur++) {
|
||||
if ((*cur)->RegisterName == name) {
|
||||
throw _T("Register export filter: name \"") + name + _T("\" already exists.");
|
||||
}
|
||||
}
|
||||
|
||||
// Set name
|
||||
RegisterName = name;
|
||||
Priority = priority;
|
||||
|
||||
// Look for place to insert
|
||||
bool inserted = false;
|
||||
for (FilterList::iterator cur=begin;cur!=end;cur++) {
|
||||
if ((*cur)->Priority < Priority) {
|
||||
AssExportFilterChain::GetFilterList()->insert(cur,this);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inserted) AssExportFilterChain::GetFilterList()->push_back(this);
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Unregister
|
||||
void AssExportFilter::Unregister () {
|
||||
// Check if it's registered
|
||||
if (!IsRegistered()) throw _T("Unregister export filter: name \"") + RegisterName + _T("\" is not registered.");
|
||||
|
||||
// Unregister
|
||||
RegisterName = _T("");
|
||||
AssExportFilterChain::GetFilterList()->remove(this);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Checks if it's registered
|
||||
bool AssExportFilter::IsRegistered() {
|
||||
// Check name
|
||||
if (RegisterName == _T("")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check list
|
||||
bool found = false;
|
||||
FilterList::iterator begin = AssExportFilterChain::GetFilterList()->begin();
|
||||
FilterList::iterator end = AssExportFilterChain::GetFilterList()->end();
|
||||
for (FilterList::iterator cur=begin;cur!=end;cur++) {
|
||||
if ((*cur) == this) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
/////////////
|
||||
// Get sizer
|
||||
wxWindow *AssExportFilter::GetConfigDialogWindow(wxWindow *parent) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// Config dialog OK
|
||||
void AssExportFilter::LoadSettings(bool IsDefault) {
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Static list
|
||||
AssExportFilterChain *AssExportFilterChain::instance=NULL;
|
||||
|
||||
|
||||
////////////
|
||||
// Get list
|
||||
FilterList *AssExportFilterChain::GetFilterList() {
|
||||
if (instance == NULL) instance = new AssExportFilterChain();
|
||||
return &(instance->Filters);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Get unprepared list
|
||||
FilterList *AssExportFilterChain::GetUnpreparedFilterList() {
|
||||
if (instance == NULL) instance = new AssExportFilterChain();
|
||||
return &(instance->Unprepared);
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// Prepare filters
|
||||
void AssExportFilterChain::PrepareFilters() {
|
||||
for (FilterList::iterator cur=instance->Unprepared.begin();cur!=instance->Unprepared.end();cur++) {
|
||||
(*cur)->Init();
|
||||
}
|
||||
instance->Unprepared.clear();
|
||||
}
|
105
core/ass_export_filter.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
#include <list>
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AssFile;
|
||||
class AssExportFilter;
|
||||
class DialogExport;
|
||||
class AssExporter;
|
||||
|
||||
|
||||
////////////
|
||||
// Typedefs
|
||||
typedef std::list<AssExportFilter*> FilterList;
|
||||
|
||||
|
||||
//////////////////////////////////////
|
||||
// Singleton for storing filter chain
|
||||
class AssExportFilterChain {
|
||||
friend class AssExportFilter;
|
||||
friend class AssExporter;
|
||||
|
||||
private:
|
||||
FilterList Filters;
|
||||
FilterList Unprepared;
|
||||
|
||||
static AssExportFilterChain *instance;
|
||||
static FilterList *GetFilterList();
|
||||
static FilterList *GetUnpreparedFilterList();
|
||||
|
||||
public:
|
||||
static void PrepareFilters();
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// Base export filter class
|
||||
class AssExportFilter {
|
||||
friend class AssExporter;
|
||||
friend class AssExportFilterChain;
|
||||
|
||||
private:
|
||||
wxString RegisterName;
|
||||
int Priority;
|
||||
|
||||
protected:
|
||||
bool autoExporter;
|
||||
bool initialized;
|
||||
wxString Description;
|
||||
|
||||
void Register(wxString name,int priority=0); // Register the filter with specific name. Higher priority filters get the file to process first.
|
||||
void Unregister(); // Unregister the filter instance
|
||||
bool IsRegistered(); // Is this instance registered as a filter?
|
||||
virtual void Init()=0; // Tell it to initialize itself
|
||||
|
||||
public:
|
||||
AssExportFilter();
|
||||
virtual ~AssExportFilter();
|
||||
|
||||
virtual void ProcessSubs(AssFile *subs)=0; // Process subtitles - this function must be overriden.
|
||||
virtual wxWindow *GetConfigDialogWindow(wxWindow *parent); // Draw setup controls - this function may optionally be overridden.
|
||||
virtual void LoadSettings(bool IsDefault); // Config dialog is done - extract data now.
|
||||
};
|
180
core/ass_exporter.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include "ass_exporter.h"
|
||||
#include "ass_export_filter.h"
|
||||
#include "ass_file.h"
|
||||
#include "frame_main.h"
|
||||
|
||||
|
||||
///////////////
|
||||
// Constructor
|
||||
AssExporter::AssExporter (AssFile *subs) {
|
||||
OriginalSubs = subs;
|
||||
IsDefault = true;
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssExporter::~AssExporter () {
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// Draw control settings
|
||||
void AssExporter::DrawSettings(wxWindow *parent,wxSizer *AddTo) {
|
||||
IsDefault = false;
|
||||
wxWindow *window;
|
||||
wxSizer *box;
|
||||
FilterList::iterator begin = AssExportFilterChain::GetFilterList()->begin();
|
||||
FilterList::iterator end = AssExportFilterChain::GetFilterList()->end();
|
||||
for (FilterList::iterator cur=begin;cur!=end;cur++) {
|
||||
window = (*cur)->GetConfigDialogWindow(parent);
|
||||
if (window) {
|
||||
box = new wxStaticBoxSizer(wxVERTICAL,parent,(*cur)->RegisterName);
|
||||
box->Add(window,0,wxEXPAND,0);
|
||||
AddTo->Add(box,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,5);
|
||||
AddTo->Show(box,false);
|
||||
Sizers[(*cur)->RegisterName] = box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Add filter to chain
|
||||
void AssExporter::AddFilter(wxString name) {
|
||||
// Get filter
|
||||
AssExportFilter *filter = NULL;
|
||||
FilterList::iterator begin = AssExportFilterChain::GetFilterList()->begin();
|
||||
FilterList::iterator end = AssExportFilterChain::GetFilterList()->end();
|
||||
for (FilterList::iterator cur=begin;cur!=end;cur++) {
|
||||
if ((*cur)->RegisterName == name) {
|
||||
filter = *cur;
|
||||
}
|
||||
}
|
||||
|
||||
// Check
|
||||
if (!filter) throw _T("Filter not found");
|
||||
|
||||
// Add to list
|
||||
Filters.push_back(filter);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Adds all autoexporting filters to chain
|
||||
void AssExporter::AddAutoFilters() {
|
||||
FilterList::iterator begin = AssExportFilterChain::GetFilterList()->begin();
|
||||
FilterList::iterator end = AssExportFilterChain::GetFilterList()->end();
|
||||
for (FilterList::iterator cur=begin;cur!=end;cur++) {
|
||||
if ((*cur)->autoExporter) {
|
||||
Filters.push_back(*cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// Get name of all filters
|
||||
wxArrayString AssExporter::GetAllFilterNames() {
|
||||
wxArrayString names;
|
||||
FilterList::iterator begin = AssExportFilterChain::GetFilterList()->begin();
|
||||
FilterList::iterator end = AssExportFilterChain::GetFilterList()->end();
|
||||
for (FilterList::iterator cur=begin;cur!=end;cur++) {
|
||||
names.Add((*cur)->RegisterName);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
//////////
|
||||
// Export
|
||||
void AssExporter::Export(wxString filename, wxString charset) {
|
||||
// Copy
|
||||
AssFile *Subs = new AssFile(*OriginalSubs);
|
||||
|
||||
// Run filters
|
||||
for (FilterList::iterator cur=Filters.begin();cur!=Filters.end();cur++) {
|
||||
(*cur)->LoadSettings(IsDefault);
|
||||
(*cur)->ProcessSubs(Subs);
|
||||
}
|
||||
|
||||
/*
|
||||
// Set charset
|
||||
bool withCharset = !IsDefault;
|
||||
wxString charset = _T("");
|
||||
if (withCharset) {
|
||||
wxArrayString choices = FrameMain::GetEncodings();
|
||||
charset = wxGetSingleChoice(_T("Choose charset code:"), _T("Charset"),choices,NULL,-1, -1,true,250,200);
|
||||
if (charset == _T("")) {
|
||||
delete Subs;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// *FIXME* (or is it?) We assume charset argument is valid here
|
||||
|
||||
// Save
|
||||
Subs->Save(filename,false,false,charset);
|
||||
delete Subs;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////
|
||||
// Get window associated with name
|
||||
wxSizer *AssExporter::GetSettingsSizer(wxString name) {
|
||||
SizerMap::iterator pos = Sizers.find(name);
|
||||
if (pos == Sizers.end()) return NULL;
|
||||
else return pos->second;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Get description of filter
|
||||
wxString AssExporter::GetDescription(wxString name) {
|
||||
FilterList::iterator begin = AssExportFilterChain::GetFilterList()->begin();
|
||||
FilterList::iterator end = AssExportFilterChain::GetFilterList()->end();
|
||||
for (FilterList::iterator cur=begin;cur!=end;cur++) {
|
||||
if ((*cur)->RegisterName == name) {
|
||||
return (*cur)->Description;
|
||||
}
|
||||
}
|
||||
throw _T("Filter not found.");
|
||||
}
|
80
core/ass_exporter.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AssExportFilter;
|
||||
class AssFile;
|
||||
|
||||
|
||||
////////////
|
||||
// Typedefs
|
||||
typedef std::list<AssExportFilter*> FilterList;
|
||||
typedef std::map<wxString,wxSizer*> SizerMap;
|
||||
|
||||
|
||||
//////////////////
|
||||
// Exporter class
|
||||
class AssExporter {
|
||||
private:
|
||||
SizerMap Sizers;
|
||||
FilterList Filters;
|
||||
AssFile *OriginalSubs;
|
||||
bool IsDefault;
|
||||
|
||||
public:
|
||||
AssExporter(AssFile *subs);
|
||||
~AssExporter();
|
||||
|
||||
wxArrayString GetAllFilterNames();
|
||||
void AddFilter(wxString name);
|
||||
void AddAutoFilters();
|
||||
void DrawSettings(wxWindow *parent,wxSizer *AddTo);
|
||||
wxSizer *GetSettingsSizer(wxString name);
|
||||
void Export(wxString file, wxString charset = _T(""));
|
||||
AssFile *GetOriginalSubs() { return OriginalSubs; }
|
||||
wxString GetDescription(wxString name);
|
||||
};
|
1021
core/ass_file.cpp
Normal file
136
core/ass_file.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class FrameRate;
|
||||
class AssDialogue;
|
||||
class AssStyle;
|
||||
class AssDialogueBlock;
|
||||
class AssDialogueBlockOverride;
|
||||
class AssDialogueBlockPlain;
|
||||
class AssEntry;
|
||||
|
||||
|
||||
//////////////////////////////////////
|
||||
// Class to store the actual ass file
|
||||
class AssFile {
|
||||
private:
|
||||
bool Modified;
|
||||
|
||||
// Stack operations
|
||||
static std::list<AssFile*> SubsStack;
|
||||
static bool StackModified;
|
||||
static void StackClear();
|
||||
|
||||
// I/O operations
|
||||
void LoadASS(const wxString file,const wxString encoding,bool IsSSA=false);
|
||||
void LoadSRT(const wxString file,const wxString encoding);
|
||||
void LoadTXT(const wxString file,const wxString encoding);
|
||||
void SaveASS(const wxString file,bool setfilename,const wxString encoding=_T(""));
|
||||
void SaveSSA(const wxString file,const wxString encoding=_T(""));
|
||||
void SaveSRT(const wxString file,const wxString encoding=_T(""));
|
||||
|
||||
// Manipulation operations
|
||||
int AddLine(wxString data,wxString group,int lasttime,bool &IsSSA);
|
||||
void DialogueToSRT(AssDialogue *current,std::list<AssEntry*>::iterator prev);
|
||||
|
||||
// Format conversion operations
|
||||
void ConvertToSRT();
|
||||
void ConvertToSSA();
|
||||
|
||||
public:
|
||||
std::list<AssEntry*> Line;
|
||||
|
||||
wxString filename;
|
||||
bool loaded;
|
||||
bool IsASS;
|
||||
|
||||
AssFile();
|
||||
AssFile(AssFile &from);
|
||||
~AssFile();
|
||||
|
||||
bool IsModified(); // Returns if file has unmodified changes
|
||||
void FlagAsModified(); // Flag file as being modified, will automatically put a copy on stack
|
||||
void Clear(); // Wipes file
|
||||
void LoadDefault(bool noline=true); // Loads default file. Pass true to prevent it from adding a default line too
|
||||
void InsertStyle(AssStyle *style); // Inserts a style to file
|
||||
wxArrayString GetStyles(); // Gets a list of all styles available
|
||||
AssStyle *GetStyle(wxString name); // Gets style by its name
|
||||
|
||||
void Load(wxString file,wxString charset=_T("")); // Load from a file
|
||||
void Save(wxString file,bool setfilename=false,bool addToRecent=true,const wxString encoding=_T("")); // Save to a file. Pass true to second argument if this isn't a copy
|
||||
void Export(wxString file); // Saves exported copy, with effects applied
|
||||
void AddToRecent(wxString file); // Adds file name to list of recently opened files
|
||||
|
||||
int GetScriptInfoAsInt(const wxString key);
|
||||
wxString GetScriptInfo(const wxString key); // Returns the value in a [Script Info] key.
|
||||
void SetScriptInfo(const wxString key,const wxString value); // Sets the value of a [Script Info] key. Adds it if it doesn't exist.
|
||||
void AddComment(const wxString comment); // Adds a ";" comment under [Script Info].
|
||||
|
||||
static void StackPop(); // Pop subs from stack and sets 'top' to it
|
||||
static void StackPush(); // Puts a copy of 'top' on the stack
|
||||
static void StackReset(); // Resets stack. Do this before loading new subtitles.
|
||||
static bool StackEmpty(); // Checks if stack is empty
|
||||
static bool Popping; // Flags the stack as popping. You must unset this after popping
|
||||
static AssFile *top; // Current script file. It is "above" the stack.
|
||||
};
|
||||
|
||||
|
||||
////////////
|
||||
// Typedefs
|
||||
typedef std::list<AssEntry*>::iterator entryIter;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Hack to get STL sort to work on a list of pointers
|
||||
template <typename T>
|
||||
class LessByPointedToValue : std::binary_function<T const *, T const *, bool> {
|
||||
public:
|
||||
bool operator()(T const * x, T const * y) const {
|
||||
return std::less<T>()(*x, *y);
|
||||
}
|
||||
};
|
691
core/ass_override.cpp
Normal file
|
@ -0,0 +1,691 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_override.h"
|
||||
#include <wx/tokenzr.h>
|
||||
|
||||
|
||||
////////////////////// AssOverrideParameter //////////////////////
|
||||
///////////////
|
||||
// Constructor
|
||||
AssOverrideParameter::AssOverrideParameter () {
|
||||
classification = PARCLASS_NORMAL;
|
||||
ommited = false;
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssOverrideParameter::~AssOverrideParameter () {
|
||||
}
|
||||
|
||||
|
||||
////////
|
||||
// Copy
|
||||
void AssOverrideParameter::CopyFrom (const AssOverrideParameter ¶m) {
|
||||
switch(param.GetType()) {
|
||||
case VARDATA_INT: SetInt(param.AsInt()); break;
|
||||
case VARDATA_FLOAT: SetFloat(param.AsFloat()); break;
|
||||
case VARDATA_TEXT: SetText(param.AsText()); break;
|
||||
case VARDATA_BOOL: SetBool(param.AsBool()); break;
|
||||
case VARDATA_COLOUR: SetColour(param.AsColour()); break;
|
||||
case VARDATA_BLOCK: SetBlock(param.AsBlock()); break;
|
||||
default: DeleteValue();
|
||||
}
|
||||
classification = param.classification;
|
||||
ommited = param.ommited;
|
||||
}
|
||||
|
||||
void AssOverrideParameter::operator= (const AssOverrideParameter ¶m) {
|
||||
CopyFrom(param);
|
||||
}
|
||||
|
||||
|
||||
////////////////////// AssDialogueBlockOverride //////////////////////
|
||||
///////////////
|
||||
// Constructor
|
||||
AssDialogueBlockOverride::AssDialogueBlockOverride () {
|
||||
type = BLOCK_OVERRIDE;
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssDialogueBlockOverride::~AssDialogueBlockOverride () {
|
||||
for (std::vector<AssOverrideTag*>::iterator cur=Tags.begin();cur!=Tags.end();cur++) {
|
||||
delete *cur;
|
||||
}
|
||||
Tags.clear();
|
||||
}
|
||||
|
||||
|
||||
/////////////
|
||||
// Read tags
|
||||
void AssDialogueBlockOverride::ParseTags () {
|
||||
// Clear current vector
|
||||
Tags.clear();
|
||||
|
||||
// Fix parenthesis matching
|
||||
while (text.Freq(_T('(')) > text.Freq(_T(')'))) {
|
||||
text += _T(")");
|
||||
}
|
||||
|
||||
// Initialize tokenizer
|
||||
wxStringTokenizer tkn(text,_T("\\"),wxTOKEN_RET_EMPTY_ALL);
|
||||
|
||||
while (tkn.HasMoreTokens()) {
|
||||
wxString curTag = _T("\\");
|
||||
curTag += tkn.GetNextToken();
|
||||
if (curTag == _T("\\")) continue;
|
||||
|
||||
// Check for parenthesis matching
|
||||
while (curTag.Freq(_T('(')) > curTag.Freq(_T(')'))) {
|
||||
if (!tkn.HasMoreTokens()) {
|
||||
wxLogWarning(_T("Unmatched parenthesis! Line contents: ") + parent->Text);
|
||||
break;
|
||||
}
|
||||
curTag << _T("\\") << tkn.GetNextToken();
|
||||
}
|
||||
|
||||
AssOverrideTag *newTag = new AssOverrideTag;
|
||||
newTag->SetText(curTag);
|
||||
Tags.push_back(newTag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// Get Text representation
|
||||
wxString AssDialogueBlockOverride::GetText () {
|
||||
text = _T("");
|
||||
for (std::vector<AssOverrideTag*>::iterator cur=Tags.begin();cur!=Tags.end();cur++) {
|
||||
text += (*cur)->ToString();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////
|
||||
// Process parameters via callback
|
||||
void AssDialogueBlockOverride::ProcessParameters(void (*callback)(wxString,int,AssOverrideParameter *,void *),void *userData) {
|
||||
AssOverrideTag *curTag;
|
||||
AssOverrideParameter *curPar;
|
||||
|
||||
// Find tags
|
||||
for (std::vector<AssOverrideTag*>::iterator cur=Tags.begin();cur!=Tags.end();cur++) {
|
||||
int n = 0;
|
||||
curTag = *cur;
|
||||
|
||||
// Find parameters
|
||||
for (std::vector<AssOverrideParameter*>::iterator curParam=curTag->Params.begin();curParam!=curTag->Params.end();curParam++) {
|
||||
curPar = *curParam;
|
||||
|
||||
if (curPar->GetType() != VARDATA_NONE && curPar->ommited == false) {
|
||||
// Do callback
|
||||
(*callback)(curTag->Name,n,curPar,userData);
|
||||
|
||||
// Go recursive if it's a block parameter
|
||||
//if (curPar->classification == VARDATA_BLOCK) {
|
||||
if (curPar->GetType() == VARDATA_BLOCK) {
|
||||
curPar->AsBlock()->ProcessParameters(callback,userData);
|
||||
}
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////// AssOverrideParamProto //////////////////////////
|
||||
///////////////
|
||||
// Constructor
|
||||
AssOverrideParamProto::AssOverrideParamProto (VariableDataType _type,int opt,ASS_ParameterClass classi) {
|
||||
type = _type;
|
||||
optional = opt;
|
||||
classification = classi;
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssOverrideParamProto::~AssOverrideParamProto() {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////// AssOverrideTagProto //////////////////////////
|
||||
///////////////
|
||||
// Static vars
|
||||
std::list<AssOverrideTagProto> AssOverrideTagProto::proto;
|
||||
bool AssOverrideTagProto::loaded = false;
|
||||
|
||||
|
||||
///////////////
|
||||
// Constructor
|
||||
AssOverrideTagProto::AssOverrideTagProto() {
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssOverrideTagProto::~AssOverrideTagProto() {
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// Load prototypes
|
||||
void AssOverrideTagProto::LoadProtos () {
|
||||
if (loaded) return;
|
||||
loaded = true;
|
||||
|
||||
// \alpha
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\alpha");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT));
|
||||
|
||||
// \bord<depth>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\bord");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_SIZE));
|
||||
|
||||
// \shad<depth>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\shad");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_SIZE));
|
||||
|
||||
// \fade(<a1>,<a2>,<a3>,<t1>,<t2>,<t3>,<t4>)
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fade");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_RELATIVE_TIME_START));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_RELATIVE_TIME_START));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_RELATIVE_TIME_START));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_RELATIVE_TIME_START));
|
||||
|
||||
// \move(<x1>,<y1>,<x2>,<y2>[,<t1>,<t2>])
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\move");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_X));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_Y));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_X));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_Y));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,OPTIONAL_6,PARCLASS_RELATIVE_TIME_START));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,OPTIONAL_6,PARCLASS_RELATIVE_TIME_START));
|
||||
|
||||
// \clip(<x1>,<y1>,<x2>,<y2>)
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\clip");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_X));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_Y));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_X));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_Y));
|
||||
|
||||
// \clip([<scale>,]<some drawings>)
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\clip");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,OPTIONAL_2,PARCLASS_NORMAL));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_DRAWING));
|
||||
|
||||
// \fscx<percent>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fscx");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,NOT_OPTIONAL,PARCLASS_RELATIVE_SIZE_X));
|
||||
|
||||
// \fscy<percent>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fscy");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,NOT_OPTIONAL,PARCLASS_RELATIVE_SIZE_Y));
|
||||
|
||||
// \pos(<x>,<y>)
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\pos");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_X));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_Y));
|
||||
|
||||
// \org(<x>,<y>)
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\org");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_X));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_Y));
|
||||
|
||||
// \pbo<y>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\pbo");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_POS_Y));
|
||||
|
||||
// \fad(<t1>,<t2>)
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fad");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_RELATIVE_TIME_START));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_RELATIVE_TIME_END));
|
||||
|
||||
// \fsp<pixels>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fsp");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_SIZE));
|
||||
|
||||
// \frx<degrees>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\frx");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \fry<degrees>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fry");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \frz<degrees>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\frz");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \fr<degrees>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fr");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \1c&H<bbggrr>&
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\1c");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \2c&H<bbggrr>&
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\2c");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \3c&H<bbggrr>&
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\3c");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \4c&H<bbggrr>&
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\4c");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \1a&H<aa>&
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\1a");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \2a&H<aa>&
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\2a");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \3a&H<aa>&
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\3a");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \4a&H<aa>&
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\4a");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \fe<charset>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fe");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \ko<duration>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\ko");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_KARAOKE));
|
||||
|
||||
// \kf<duration>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\kf");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_KARAOKE));
|
||||
|
||||
// \be<0/1>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\be");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_BOOL,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \fn<name>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fn");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \fs<size>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\fs");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_ABSOLUTE_SIZE));
|
||||
|
||||
// \an<alignment>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\an");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \c&H<bbggrr>&
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\c");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \b<0/1/weight>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\b");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,OPTIONAL_1,PARCLASS_NORMAL));
|
||||
proto.back().params.back().defaultValue.SetBool(false);
|
||||
|
||||
// \i<0/1>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\i");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_BOOL,OPTIONAL_1,PARCLASS_NORMAL));
|
||||
proto.back().params.back().defaultValue.SetBool(false);
|
||||
|
||||
// \u<0/1>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\u");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_BOOL,OPTIONAL_1,PARCLASS_NORMAL));
|
||||
proto.back().params.back().defaultValue.SetBool(false);
|
||||
|
||||
// \s<0/1>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\s");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_BOOL,OPTIONAL_1,PARCLASS_NORMAL));
|
||||
proto.back().params.back().defaultValue.SetBool(false);
|
||||
|
||||
// \a<alignment>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\a");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \k<duration>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\k");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_KARAOKE));
|
||||
|
||||
// \K<duration>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\K");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_KARAOKE));
|
||||
|
||||
// \q<0-3>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\q");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \p<n>
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\p");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
|
||||
// \r[<name>]
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\r");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_TEXT,OPTIONAL_1,PARCLASS_NORMAL));
|
||||
|
||||
// \t([<t1>,<t2>,][<accel>,]<style modifiers>)
|
||||
proto.push_back(AssOverrideTagProto());
|
||||
proto.back().name = _T("\\t");
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,OPTIONAL_3 | OPTIONAL_4,PARCLASS_RELATIVE_TIME_START));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_INT,OPTIONAL_3 | OPTIONAL_4,PARCLASS_RELATIVE_TIME_START));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_FLOAT,OPTIONAL_2 | OPTIONAL_4,PARCLASS_NORMAL));
|
||||
proto.back().params.push_back(AssOverrideParamProto(VARDATA_BLOCK,NOT_OPTIONAL,PARCLASS_NORMAL));
|
||||
}
|
||||
|
||||
|
||||
//////////////////////// AssOverrideTag //////////////////////////
|
||||
///////////////
|
||||
// Constructor
|
||||
AssOverrideTag::AssOverrideTag () {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssOverrideTag::~AssOverrideTag () {
|
||||
for (std::vector<AssOverrideParameter*>::iterator cur=Params.begin();cur!=Params.end();cur++) {
|
||||
delete *cur;
|
||||
}
|
||||
Params.clear();
|
||||
valid = false;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// Parses text and sets tag
|
||||
void AssOverrideTag::SetText (wxString text) {
|
||||
// Determine name
|
||||
Name = _T("");
|
||||
AssOverrideTagProto *proto;
|
||||
for (std::list<AssOverrideTagProto>::iterator cur=AssOverrideTagProto::proto.begin();cur!=AssOverrideTagProto::proto.end();cur++) {
|
||||
proto = &(*cur);
|
||||
if (text.Left(proto->name.length()) == proto->name) {
|
||||
Name = proto->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set tag name
|
||||
if (!Name.empty()) {
|
||||
ParseParameters(text.Mid(Name.length()));
|
||||
valid = true;
|
||||
}
|
||||
|
||||
// Junk tag
|
||||
else {
|
||||
Name = text;
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// Checks if it is valid
|
||||
bool AssOverrideTag::IsValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Parses parameters
|
||||
void AssOverrideTag::ParseParameters(wxString text) {
|
||||
// Split parameters
|
||||
wxArrayString paramList;
|
||||
size_t start = 0;
|
||||
wxString work;
|
||||
int parDepth = 0;
|
||||
for (size_t i=0;i<text.Length();i++) {
|
||||
// Started an overrides block
|
||||
if (text[i] == _T('\\')) {
|
||||
work = text.Mid(start);
|
||||
while (parDepth > 0 && work.Right(1) == _T(")")) {
|
||||
work = work.Left(work.Length()-1);
|
||||
work.Trim(true);
|
||||
work.Trim(false);
|
||||
parDepth--;
|
||||
}
|
||||
paramList.Add(work);
|
||||
start = text.Length();
|
||||
break;
|
||||
}
|
||||
else if (text[i] == _T(',') || text[i] == _T('(') || text[i] == _T(')')) {
|
||||
if (text[i] == _T('(')) parDepth++;
|
||||
work = text.SubString(start,i-1);
|
||||
work.Trim(true);
|
||||
work.Trim(false);
|
||||
if (work != _T("")) paramList.Add(work);
|
||||
start = i+1;
|
||||
}
|
||||
else if (text[i] == _T(')')) parDepth--;
|
||||
}
|
||||
work = text.Mid(start);
|
||||
work.Trim(true);
|
||||
work.Trim(false);
|
||||
if (work != _T(""))
|
||||
paramList.Add(work);
|
||||
int curPar = 0;
|
||||
int totalPars = paramList.GetCount();
|
||||
|
||||
// Get optional parameters flag
|
||||
//wxStringTokenizer tkn(text,_T(",()"),wxTOKEN_STRTOK);
|
||||
//int totalPars = tkn.CountTokens();
|
||||
ASS_ParameterOptional parsFlag = OPTIONAL_0;
|
||||
switch (totalPars) {
|
||||
case 1: parsFlag = OPTIONAL_1; break;
|
||||
case 2: parsFlag = OPTIONAL_2; break;
|
||||
case 3: parsFlag = OPTIONAL_3; break;
|
||||
case 4: parsFlag = OPTIONAL_4; break;
|
||||
case 5: parsFlag = OPTIONAL_5; break;
|
||||
case 6: parsFlag = OPTIONAL_6; break;
|
||||
case 7: parsFlag = OPTIONAL_7; break;
|
||||
}
|
||||
|
||||
// Find prototype
|
||||
bool clipOnce = true;
|
||||
AssOverrideTagProto *proto = NULL;
|
||||
for (std::list<AssOverrideTagProto>::iterator cur=AssOverrideTagProto::proto.begin();cur!=AssOverrideTagProto::proto.end();cur++) {
|
||||
if (Name == (*cur).name) {
|
||||
if (Name == _T("\\clip") && totalPars != 4 && clipOnce) {
|
||||
clipOnce = false;
|
||||
continue;
|
||||
}
|
||||
proto = &(*cur);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (proto == NULL) {
|
||||
throw _T("Couldn't find tag prototype while parsing.");
|
||||
}
|
||||
|
||||
// Get parameters
|
||||
size_t n=0;
|
||||
wxString curtok = _T("");
|
||||
if (curPar < totalPars) {
|
||||
curtok = paramList[curPar];
|
||||
curPar++;
|
||||
}
|
||||
//while (curtok != _T("")) {
|
||||
while (n < proto->params.size()) {
|
||||
AssOverrideParamProto *curproto = &proto->params[n];
|
||||
bool isDefault = false;
|
||||
n++;
|
||||
|
||||
// Create parameter
|
||||
AssOverrideParameter *newparam = new AssOverrideParameter;
|
||||
|
||||
// Check if it's optional and not set (set to default)
|
||||
if (!(curproto->optional & parsFlag)) {
|
||||
isDefault = true;
|
||||
newparam->CopyFrom(curproto->defaultValue);
|
||||
newparam->ommited = true;
|
||||
}
|
||||
|
||||
else {
|
||||
// Determine parameter type and set value
|
||||
switch (curproto->type) {
|
||||
case VARDATA_INT: {
|
||||
long temp;
|
||||
curtok.ToLong(&temp);
|
||||
newparam->SetInt(temp);
|
||||
break;
|
||||
}
|
||||
case VARDATA_FLOAT: {
|
||||
double temp;
|
||||
curtok.ToDouble(&temp);
|
||||
newparam->SetFloat(temp);
|
||||
break;
|
||||
}
|
||||
case VARDATA_TEXT: {
|
||||
newparam->SetText(curtok);
|
||||
break;
|
||||
}
|
||||
case VARDATA_BOOL: {
|
||||
long temp;
|
||||
curtok.ToLong(&temp);
|
||||
newparam->SetBool(temp != 0);
|
||||
break;
|
||||
}
|
||||
case VARDATA_BLOCK: {
|
||||
AssDialogueBlockOverride *temp = new AssDialogueBlockOverride;
|
||||
temp->text = curtok;
|
||||
temp->ParseTags();
|
||||
newparam->SetBlock(temp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get next actual parameter
|
||||
if (curPar < totalPars) {
|
||||
curtok = paramList[curPar];
|
||||
curPar++;
|
||||
}
|
||||
else curtok = _T("");
|
||||
}
|
||||
|
||||
// Add to list
|
||||
newparam->classification = curproto->classification;
|
||||
Params.push_back(newparam);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Get string
|
||||
wxString AssOverrideTag::ToString() {
|
||||
// Start with name
|
||||
wxString result = Name;
|
||||
|
||||
// Determine if it needs parenthesis
|
||||
bool parenthesis = false;
|
||||
if (Name == _T("\\t") || Name == _T("\\pos") || Name == _T("\\fad") || Name == _T("\\org") || Name == _T("\\clip") || Name == _T("\\move") || Name == _T("\\fade")) parenthesis = true;
|
||||
if (parenthesis) result += _T("(");
|
||||
|
||||
// Add parameters
|
||||
int n = 0;
|
||||
for (std::vector<AssOverrideParameter*>::iterator cur=Params.begin();cur!=Params.end();cur++) {
|
||||
if ((*cur)->GetType() != VARDATA_NONE && (*cur)->ommited == false) {
|
||||
result += (*cur)->AsText();
|
||||
result += _T(",");
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (n > 0) result = result.Left(result.Length()-1);
|
||||
|
||||
// Finish
|
||||
if (parenthesis) result += _T(")");
|
||||
return result;
|
||||
}
|
142
core/ass_override.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include "variable_data.h"
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AssDialogueBlockOverride;
|
||||
|
||||
|
||||
// Enum of parameter classification
|
||||
// This is used for things like checking which tags VFR needs to update
|
||||
enum ASS_ParameterClass {
|
||||
PARCLASS_NORMAL,
|
||||
PARCLASS_ABSOLUTE_SIZE,
|
||||
PARCLASS_ABSOLUTE_POS_X,
|
||||
PARCLASS_ABSOLUTE_POS_Y,
|
||||
PARCLASS_RELATIVE_SIZE_X,
|
||||
PARCLASS_RELATIVE_SIZE_Y,
|
||||
PARCLASS_RELATIVE_TIME_START,
|
||||
PARCLASS_RELATIVE_TIME_END,
|
||||
//PARCLASS_RELATIVE_TIME_START_CENTI,
|
||||
//PARCLASS_RELATIVE_TIME_END_CENTI,
|
||||
PARCLASS_KARAOKE,
|
||||
PARCLASS_DRAWING
|
||||
};
|
||||
|
||||
// Actual class
|
||||
class AssOverrideParameter : public VariableData {
|
||||
public:
|
||||
ASS_ParameterClass classification;
|
||||
bool ommited;
|
||||
|
||||
AssOverrideParameter();
|
||||
~AssOverrideParameter();
|
||||
|
||||
void operator= (const AssOverrideParameter ¶m);
|
||||
void CopyFrom (const AssOverrideParameter ¶m);
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// Class for override tags
|
||||
// -----------------------
|
||||
//
|
||||
// GetText() returns the text representation of the tag
|
||||
//
|
||||
class AssOverrideTag {
|
||||
private:
|
||||
bool valid;
|
||||
|
||||
public:
|
||||
wxString Name;
|
||||
std::vector <AssOverrideParameter*> Params;
|
||||
|
||||
AssOverrideTag();
|
||||
~AssOverrideTag();
|
||||
|
||||
bool IsValid();
|
||||
void ParseParameters(wxString text);
|
||||
void SetText(wxString text);
|
||||
wxString ToString();
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// Override tags prototype
|
||||
enum ASS_ParameterOptional {
|
||||
NOT_OPTIONAL = 0xFF,
|
||||
OPTIONAL_0 = 0x00,
|
||||
OPTIONAL_1 = 0x01,
|
||||
OPTIONAL_2 = 0x02,
|
||||
OPTIONAL_3 = 0x04,
|
||||
OPTIONAL_4 = 0x08,
|
||||
OPTIONAL_5 = 0x10,
|
||||
OPTIONAL_6 = 0x20,
|
||||
OPTIONAL_7 = 0x40
|
||||
};
|
||||
|
||||
class AssOverrideParamProto {
|
||||
public:
|
||||
int optional;
|
||||
VariableDataType type;
|
||||
AssOverrideParameter defaultValue;
|
||||
ASS_ParameterClass classification;
|
||||
|
||||
AssOverrideParamProto (VariableDataType _type,int opt=NOT_OPTIONAL,ASS_ParameterClass classi=PARCLASS_NORMAL);
|
||||
~AssOverrideParamProto();
|
||||
};
|
||||
|
||||
class AssOverrideTagProto {
|
||||
public:
|
||||
wxString name;
|
||||
std::vector<AssOverrideParamProto> params;
|
||||
static std::list<AssOverrideTagProto> proto;
|
||||
static bool loaded;
|
||||
static void LoadProtos();
|
||||
|
||||
AssOverrideTagProto();
|
||||
~AssOverrideTagProto();
|
||||
};
|
528
core/ass_style.cpp
Normal file
|
@ -0,0 +1,528 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include "ass_style.h"
|
||||
#include <wx/tokenzr.h>
|
||||
|
||||
|
||||
///////////////////////// AssColor //////////////////////////
|
||||
////////////////
|
||||
// Constructors
|
||||
AssColor::AssColor () {
|
||||
r=g=b=a=0;
|
||||
}
|
||||
|
||||
AssColor::AssColor (wxColour &color) {
|
||||
SetWXColor(color);
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Parse from ASS
|
||||
void AssColor::ParseASS (const wxString _value) {
|
||||
// Prepare
|
||||
wxString value = _value;
|
||||
value.Trim(false);
|
||||
value.Trim(true);
|
||||
value.UpperCase();
|
||||
|
||||
// Remove leading and ending crap
|
||||
if (value.Left(1) == _T("&")) value = value.Mid(1);
|
||||
if (value.Left(1) == _T("H")) value = value.Mid(1);
|
||||
if (value.Right(1) == _T("&")) value = value.Left(value.Length()-1);
|
||||
|
||||
// Read colours
|
||||
long temp[4] = { 0, 0, 0, 0 };
|
||||
bool ok;
|
||||
for (int i=0;i<4;i++) {
|
||||
if (value.Length() > 0) {
|
||||
ok = value.Right(2).ToLong(&temp[i],16);
|
||||
if (!ok) temp[i] = 0;
|
||||
value.Truncate(value.Length()-2);
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
// Copy
|
||||
r = temp[0];
|
||||
g = temp[1];
|
||||
b = temp[2];
|
||||
a = temp[3];
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Parse from SSA
|
||||
void AssColor::ParseSSA (wxString value) {
|
||||
value.Trim(true);
|
||||
value.Trim(false);
|
||||
|
||||
// Check if the moron who wrote it used ASS style in SSA
|
||||
if (value.Left(2) == _T("&H")) {
|
||||
ParseASS(value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse SSA
|
||||
long val;
|
||||
value.ToLong(&val);
|
||||
b = (val >> 16) & 0xFF;
|
||||
g = (val >> 8) & 0xFF;
|
||||
r = val & 0xFF;
|
||||
a = 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// Gets a wxColour
|
||||
wxColour AssColor::GetWXColor() {
|
||||
return wxColour(r,g,b);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Sets color from wx
|
||||
void AssColor::SetWXColor(wxColor &color) {
|
||||
r = color.Red();
|
||||
g = color.Green();
|
||||
b = color.Blue();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// Get formatted in ASS format
|
||||
wxString AssColor::GetASSFormatted (bool alpha,bool stripped,bool isStyle) {
|
||||
wxString work;
|
||||
if (!stripped) work += _T("&H");
|
||||
if (alpha) work += wxString::Format(_T("%02X"),a);
|
||||
work += wxString::Format(_T("%02X%02X%02X"),b,g,r);
|
||||
if (!stripped && !isStyle) work += _T("&");
|
||||
return work;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// Get decimal formatted
|
||||
wxString AssColor::GetSSAFormatted () {
|
||||
return wxString::Format(_T("%i"),(b<<16)+(g<<8)+r);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////// AssStyle /////////////////////////
|
||||
///////////////////////
|
||||
// Default Constructor
|
||||
AssStyle::AssStyle() {
|
||||
Type = ENTRY_STYLE;
|
||||
group = _T("[V4+ Styles]");
|
||||
|
||||
name = _T("Default");
|
||||
font = _T("Arial");
|
||||
fontsize = 20;
|
||||
|
||||
primary.r = 255;
|
||||
primary.g = 255;
|
||||
primary.b = 255;
|
||||
primary.a = 0;
|
||||
secondary.r = 255;
|
||||
secondary.g = 255;
|
||||
secondary.b = 0;
|
||||
secondary.a = 0;
|
||||
outline.r = 0;
|
||||
outline.g = 0;
|
||||
outline.b = 0;
|
||||
outline.a = 0;
|
||||
shadow.r = 0;
|
||||
shadow.g = 0;
|
||||
shadow.b = 0;
|
||||
shadow.a = 0;
|
||||
|
||||
bold = false;
|
||||
italic = false;
|
||||
underline = false;
|
||||
strikeout = false;
|
||||
|
||||
scalex = 100;
|
||||
scaley = 100;
|
||||
spacing = 0;
|
||||
angle = 0.0;
|
||||
borderstyle = 1;
|
||||
outline_w = 2.0;
|
||||
shadow_w = 2.0;
|
||||
alignment = 2;
|
||||
MarginL = 10;
|
||||
MarginR = 10;
|
||||
MarginV = 10;
|
||||
encoding = 0;
|
||||
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Constructor
|
||||
AssStyle::AssStyle(wxString _data,bool IsSSA) {
|
||||
Type = ENTRY_STYLE;
|
||||
data = _data;
|
||||
Valid = Parse(IsSSA);
|
||||
if (!Valid) {
|
||||
throw _T("[Error] Failed parsing line.");
|
||||
}
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AssStyle::~AssStyle() {
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// Parses value from ASS data
|
||||
bool AssStyle::Parse(bool IsSSA) {
|
||||
wxString temp;
|
||||
long templ;
|
||||
wxStringTokenizer tkn(data.Mid(6),_T(","),wxTOKEN_RET_EMPTY_ALL);
|
||||
|
||||
// Read name
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
name = tkn.GetNextToken();
|
||||
name.Trim(true);
|
||||
name.Trim(false);
|
||||
|
||||
// Read font name
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
font = tkn.GetNextToken();
|
||||
font.Trim(true);
|
||||
font.Trim(false);
|
||||
|
||||
// Read font size
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToLong(&templ);
|
||||
fontsize = templ;
|
||||
|
||||
if (!IsSSA) {
|
||||
// Read primary color
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
primary.ParseASS(tkn.GetNextToken());
|
||||
|
||||
// Read secondary color
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
secondary.ParseASS(tkn.GetNextToken());
|
||||
|
||||
// Read outline color
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
outline.ParseASS(tkn.GetNextToken());
|
||||
|
||||
// Read shadow color
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
shadow.ParseASS(tkn.GetNextToken());
|
||||
}
|
||||
|
||||
else {
|
||||
// Read primary color
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
primary.ParseSSA(tkn.GetNextToken());
|
||||
|
||||
// Read secondary color
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
secondary.ParseSSA(tkn.GetNextToken());
|
||||
|
||||
// Read and discard tertiary color
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
tkn.GetNextToken();
|
||||
|
||||
// Read shadow/outline color
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
outline.ParseSSA(tkn.GetNextToken());
|
||||
shadow = outline;
|
||||
}
|
||||
|
||||
// Read bold
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToLong(&templ);
|
||||
bold = true;
|
||||
if (templ == 0) bold = false;
|
||||
|
||||
// Read italics
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken(); temp.ToLong(&templ);
|
||||
italic = true;
|
||||
if (templ == 0) italic = false;
|
||||
|
||||
if (!IsSSA) {
|
||||
// Read underline
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToLong(&templ);
|
||||
underline = true;
|
||||
if (templ == 0) underline = false;
|
||||
|
||||
// Read strikeout
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToLong(&templ);
|
||||
strikeout = true;
|
||||
if (templ == 0) strikeout = false;
|
||||
|
||||
// Read scale x
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToLong(&templ);
|
||||
scalex = templ;
|
||||
|
||||
// Read scale y
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToLong(&templ);
|
||||
scaley = templ;
|
||||
|
||||
// Read spacing
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToDouble(&spacing);
|
||||
//spacing = templ;
|
||||
|
||||
// Read angle
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToDouble(&angle);
|
||||
}
|
||||
|
||||
else {
|
||||
// SSA defaults
|
||||
shadow.a = 128;
|
||||
underline = false;
|
||||
strikeout = false;
|
||||
|
||||
scalex = 100;
|
||||
scaley = 100;
|
||||
spacing = 0;
|
||||
angle = 0.0;
|
||||
}
|
||||
|
||||
// Read border style
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToLong(&templ);
|
||||
borderstyle = templ;
|
||||
|
||||
// Read outline width
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToDouble(&outline_w);
|
||||
|
||||
// Read shadow width
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToDouble(&shadow_w);
|
||||
|
||||
// Read alignment
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToLong(&templ);
|
||||
if (IsSSA) {
|
||||
switch(templ) {
|
||||
case 1: alignment = 1; break;
|
||||
case 2: alignment = 2; break;
|
||||
case 3: alignment = 3; break;
|
||||
case 5: alignment = 7; break;
|
||||
case 6: alignment = 8; break;
|
||||
case 7: alignment = 9; break;
|
||||
case 9: alignment = 4; break;
|
||||
case 10: alignment = 5; break;
|
||||
case 11: alignment = 6; break;
|
||||
default: alignment = 2; break;
|
||||
}
|
||||
}
|
||||
else alignment = templ;
|
||||
|
||||
// Read left margin
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
SetMarginString(tkn.GetNextToken(),1);
|
||||
|
||||
// Read right margin
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
SetMarginString(tkn.GetNextToken(),2);
|
||||
|
||||
// Read vertical margin
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
SetMarginString(tkn.GetNextToken(),3);
|
||||
|
||||
if (IsSSA) {
|
||||
// Read alpha level
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
}
|
||||
|
||||
// Read encoding
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
temp = tkn.GetNextToken();
|
||||
temp.ToLong(&templ);
|
||||
encoding = templ;
|
||||
|
||||
// End
|
||||
if (tkn.HasMoreTokens()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////
|
||||
// Writes data back to ASS format
|
||||
void AssStyle::UpdateData() {
|
||||
// Prepare
|
||||
data = _T("Style: ");
|
||||
|
||||
// Write all data
|
||||
name.Replace(_T(","),_T(";"));
|
||||
font.Replace(_T(","),_T(";"));
|
||||
data += name + _T(",");
|
||||
data += font + _T(",");
|
||||
data += wxString::Format(_T("%i"),fontsize) + _T(",");
|
||||
|
||||
data += primary.GetASSFormatted(true,false,true) + _T(",");
|
||||
data += secondary.GetASSFormatted(true,false,true) + _T(",");
|
||||
data += outline.GetASSFormatted(true,false,true) + _T(",");
|
||||
data += shadow.GetASSFormatted(true,false,true) + _T(",");
|
||||
|
||||
data += wxString::Format(_T("%i"),bold?-1:0) + _T(",");
|
||||
data += wxString::Format(_T("%i"),italic?-1:0) + _T(",");
|
||||
data += wxString::Format(_T("%i"),underline?-1:0) + _T(",");
|
||||
data += wxString::Format(_T("%i"),strikeout?-1:0) + _T(",");
|
||||
|
||||
data += wxString::Format(_T("%i"),scalex) + _T(",");
|
||||
data += wxString::Format(_T("%i"),scaley) + _T(",");
|
||||
data += wxString::Format(_T("%.2f"),spacing) + _T(",");
|
||||
|
||||
data += wxString::Format(_T("%.2f"),angle) + _T(",");
|
||||
data += wxString::Format(_T("%i"),borderstyle) + _T(",");
|
||||
data += wxString::Format(_T("%.2f"),outline_w) + _T(",");
|
||||
data += wxString::Format(_T("%.2f"),shadow_w) + _T(",");
|
||||
|
||||
data += wxString::Format(_T("%i"),alignment) + _T(",");
|
||||
data += wxString::Format(_T("%i"),MarginL) + _T(",");
|
||||
data += wxString::Format(_T("%i"),MarginR) + _T(",");
|
||||
data += wxString::Format(_T("%i"),MarginV) + _T(",");
|
||||
data += wxString::Format(_T("%i"),encoding);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Sets margin from a string
|
||||
void AssStyle::SetMarginString(const wxString str,int which) {
|
||||
wxString work = str;
|
||||
work.Trim(false);
|
||||
work.Trim(true);
|
||||
if (!work.IsNumber()) throw _T("Invalid margin value");
|
||||
long value;
|
||||
work.ToLong(&value);
|
||||
if (value < 0) value = 0;
|
||||
if (value > 9999) value = 9999;
|
||||
switch (which) {
|
||||
case 1: MarginL = value; break;
|
||||
case 2: MarginR = value; break;
|
||||
case 3: MarginV = value; break;
|
||||
default: throw _T("Invalid margin");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
// Gets string for margin
|
||||
wxString AssStyle::GetMarginString(int which) {
|
||||
int value;
|
||||
switch (which) {
|
||||
case 1: value = MarginL; break;
|
||||
case 2: value = MarginR; break;
|
||||
case 3: value = MarginV; break;
|
||||
default: throw _T("Invalid margin");
|
||||
}
|
||||
wxString result = wxString::Format(_T("%04i"),value);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// Convert style to SSA string
|
||||
wxString AssStyle::GetSSAText() {
|
||||
// Prepare
|
||||
wxString output = _T("Style: ");
|
||||
|
||||
// Write all data
|
||||
name.Replace(_T(","),_T(";"));
|
||||
font.Replace(_T(","),_T(";"));
|
||||
output += name + _T(",");
|
||||
output += font + _T(",");
|
||||
output += wxString::Format(_T("%i"),fontsize) + _T(",");
|
||||
|
||||
output += primary.GetSSAFormatted() + _T(",");
|
||||
output += secondary.GetSSAFormatted() + _T(",");
|
||||
output += _T("0,");
|
||||
output += shadow.GetSSAFormatted() + _T(",");
|
||||
|
||||
output += wxString::Format(_T("%i"),bold?-1:0) + _T(",");
|
||||
output += wxString::Format(_T("%i"),italic?-1:0) + _T(",");
|
||||
|
||||
output += wxString::Format(_T("%i"),borderstyle) + _T(",");
|
||||
output += wxString::Format(_T("%.2f"),outline_w) + _T(",");
|
||||
output += wxString::Format(_T("%.2f"),shadow_w) + _T(",");
|
||||
|
||||
int align = 0;
|
||||
switch (alignment) {
|
||||
case 1: align = 1; break;
|
||||
case 2: align = 2; break;
|
||||
case 3: align = 3; break;
|
||||
case 4: align = 9; break;
|
||||
case 5: align = 10; break;
|
||||
case 6: align = 11; break;
|
||||
case 7: align = 5; break;
|
||||
case 8: align = 6; break;
|
||||
case 9: align = 7; break;
|
||||
}
|
||||
output += wxString::Format(_T("%i"),align) + _T(",");
|
||||
|
||||
output += wxString::Format(_T("%i"),MarginL) + _T(",");
|
||||
output += wxString::Format(_T("%i"),MarginR) + _T(",");
|
||||
output += wxString::Format(_T("%i"),MarginV) + _T(",");
|
||||
output += _T("0,");
|
||||
output += wxString::Format(_T("%i"),encoding);
|
||||
|
||||
return output;
|
||||
}
|
106
core/ass_style.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include "ass_entry.h"
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// Class to store colors
|
||||
class AssColor {
|
||||
public:
|
||||
int r; // Red component
|
||||
int g; // Green component
|
||||
int b; // Blue component
|
||||
int a; // Alpha component
|
||||
|
||||
AssColor();
|
||||
AssColor(wxColour &color);
|
||||
|
||||
wxColor GetWXColor(); // Return as a wxColor
|
||||
void SetWXColor(wxColor &color); // Sets from a wxColor
|
||||
void ParseASS(const wxString value); // Parse ASS-style color
|
||||
void ParseSSA(const wxString value); // Parse SSA-style color
|
||||
wxString GetASSFormatted(bool alpha,bool stripped=false,bool isStyle=false); // Gets color formated in ASS format
|
||||
wxString GetSSAFormatted();
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// Class to store styles
|
||||
class AssStyle : public AssEntry {
|
||||
public:
|
||||
wxString name;
|
||||
wxString font;
|
||||
int fontsize;
|
||||
|
||||
AssColor primary;
|
||||
AssColor secondary;
|
||||
AssColor outline;
|
||||
AssColor shadow;
|
||||
|
||||
bool bold;
|
||||
bool italic;
|
||||
bool underline;
|
||||
bool strikeout;
|
||||
|
||||
int scalex;
|
||||
int scaley;
|
||||
double spacing;
|
||||
double angle;
|
||||
int borderstyle;
|
||||
double outline_w;
|
||||
double shadow_w;
|
||||
int alignment;
|
||||
int MarginL;
|
||||
int MarginR;
|
||||
int MarginV;
|
||||
int encoding;
|
||||
|
||||
bool Parse(bool IsSSA=false); // Parses raw ASS/SSA data into everything else
|
||||
void UpdateData(); // Updates raw data
|
||||
wxString GetSSAText(); // Retrieves SSA-formatted style
|
||||
wxString GetMarginString(int which); // Returns the margin value as a string (1 = left, 2 = right, 3 = vertical)
|
||||
void SetMarginString(const wxString value,int which); // Sets margin value from a string (1 = left, 2 = right, 3 = vertical)
|
||||
|
||||
AssStyle();
|
||||
AssStyle(wxString data,bool IsSSA=false);
|
||||
~AssStyle();
|
||||
};
|
111
core/ass_style_storage.cpp
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include "ass_style_storage.h"
|
||||
#include "ass_style.h"
|
||||
#include "ass_file.h"
|
||||
#include "main.h"
|
||||
#include <fstream>
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Save styles to disk
|
||||
void AssStyleStorage::Save(wxString name) {
|
||||
if (name == _T("")) return;
|
||||
|
||||
using namespace std;
|
||||
ofstream file;
|
||||
|
||||
wxString filename = AegisubApp::folderName;
|
||||
filename += _T("/catalog/");
|
||||
filename += name;
|
||||
filename += _T(".sty");
|
||||
|
||||
file.open(filename.mb_str(wxConvLocal));
|
||||
for (list<AssStyle*>::iterator cur=style.begin();cur!=style.end();cur++) {
|
||||
file << (*cur)->data.mb_str(wxConvUTF8) << endl;
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// Load styles from disk
|
||||
void AssStyleStorage::Load(wxString name) {
|
||||
if (name == _T("")) return;
|
||||
|
||||
using namespace std;
|
||||
char buffer[65536];
|
||||
ifstream file;
|
||||
|
||||
wxString filename = AegisubApp::folderName;
|
||||
filename += _T("/catalog/");
|
||||
filename += name;
|
||||
filename += _T(".sty");
|
||||
|
||||
Clear();
|
||||
file.open(filename.mb_str(wxConvLocal));
|
||||
if (!file.is_open()) {
|
||||
throw _T("Failed opening file.");
|
||||
}
|
||||
|
||||
AssStyle *curStyle;
|
||||
while (!file.eof()) {
|
||||
file.getline(buffer,65536);
|
||||
wxString data(buffer,wxConvUTF8);
|
||||
data.Trim();
|
||||
if (data.substr(0,6) == _T("Style:")) {
|
||||
curStyle = new AssStyle(data);
|
||||
style.push_back(curStyle);
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
||||
/////////
|
||||
// Clear
|
||||
void AssStyleStorage::Clear () {
|
||||
using std::list;
|
||||
for (list<AssStyle*>::iterator cur=style.begin();cur!=style.end();cur++) {
|
||||
delete *cur;
|
||||
}
|
||||
style.clear();
|
||||
}
|
64
core/ass_style_storage.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#ifndef ASS_STYLE_STORAGE_H
|
||||
#define ASS_STYLE_STORAGE_H
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include <wx/wxprec.h>
|
||||
#include <list>
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AssStyle;
|
||||
|
||||
|
||||
/////////////////
|
||||
// Storage class
|
||||
class AssStyleStorage {
|
||||
public:
|
||||
std::list<AssStyle*> style;
|
||||
|
||||
void Clear();
|
||||
void Save(wxString name);
|
||||
void Load(wxString name);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
261
core/ass_time.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include "ass_time.h"
|
||||
#include "vfr.h"
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
|
||||
////////////////////// AssTime //////////////////////
|
||||
// AssTime constructors
|
||||
AssTime::AssTime () {
|
||||
h = m = s = ms = 0;
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// Parses from ASS
|
||||
// ---------------
|
||||
// Note that this function is atomic, it won't touch the values if it's invalid.
|
||||
void AssTime::ParseASS (const wxString _text) {
|
||||
// Prepare
|
||||
wxString text = _text;
|
||||
text.Trim(true);
|
||||
text.Trim(false);
|
||||
wxString temp;
|
||||
size_t pos = 0;
|
||||
size_t end = 0;
|
||||
long th,tm,ts,tms;
|
||||
double ts_raw;
|
||||
|
||||
try {
|
||||
// Hours
|
||||
end = text.find(_T(":"),pos);
|
||||
temp = text.SubString(pos,end-1);
|
||||
if (!temp.ToLong(&th)) throw 0;
|
||||
pos = end+1;
|
||||
|
||||
// Minutes
|
||||
end = text.find(_T(":"),pos);
|
||||
temp = text.SubString(pos,end-1);
|
||||
if (!temp.ToLong(&tm)) throw 0;
|
||||
pos = end+1;
|
||||
|
||||
// Seconds
|
||||
end = text.length();
|
||||
temp = text.Mid(pos);
|
||||
if (!temp.ToDouble(&ts_raw)) throw 0;
|
||||
|
||||
// Split into seconds and fraction
|
||||
ts = ts_raw;
|
||||
tms = (ts_raw-ts)*1000+0.5;
|
||||
}
|
||||
|
||||
// Something went wrong, don't change anything
|
||||
catch (...) {
|
||||
return;
|
||||
}
|
||||
|
||||
// OK, set values
|
||||
h = th;
|
||||
m = tm;
|
||||
s = ts;
|
||||
ms = tms;
|
||||
|
||||
// This should clamp values properly
|
||||
SetMS(GetMS());
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// Parses from SRT
|
||||
void AssTime::ParseSRT (const wxString _text) {
|
||||
// Prepare
|
||||
wxString text = _text;
|
||||
text.Trim(false);
|
||||
text.Trim(true);
|
||||
long tempv;
|
||||
wxString temp;
|
||||
|
||||
// Parse
|
||||
temp = text.Mid(0,2);
|
||||
temp.ToLong(&tempv);
|
||||
h = tempv;
|
||||
temp = text.Mid(3,2);
|
||||
temp.ToLong(&tempv);
|
||||
m = tempv;
|
||||
temp = text.Mid(6,2);
|
||||
temp.ToLong(&tempv);
|
||||
s = tempv;
|
||||
temp = text.Mid(9,3);
|
||||
temp.ToLong(&tempv);
|
||||
ms = tempv;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
// AssTime conversion to/from miliseconds
|
||||
int AssTime::GetMS () {
|
||||
if (!UseMSPrecision) return ms/10*10 + s*1000 + m*60000 + h*3600000;
|
||||
else return ms + s*1000 + m*60000 + h*3600000;
|
||||
}
|
||||
|
||||
void AssTime::SetMS (int _ms) {
|
||||
// Centisecond precision
|
||||
if (!UseMSPrecision) _ms = _ms/10*10;
|
||||
|
||||
// Reset
|
||||
h = m = s = ms = 0;
|
||||
if (_ms < 0) _ms = 0;
|
||||
|
||||
// Hours
|
||||
while (_ms >= 3600000) {
|
||||
_ms -= 3600000;
|
||||
h++;
|
||||
}
|
||||
|
||||
// Ass overflow
|
||||
if (h > 9) {
|
||||
h = 9;
|
||||
m = 59;
|
||||
s = 59;
|
||||
ms = 999;
|
||||
return;
|
||||
}
|
||||
|
||||
// Minutes
|
||||
while (_ms >= 60000) {
|
||||
_ms -= 60000;
|
||||
m++;
|
||||
}
|
||||
|
||||
// Seconds
|
||||
while (_ms >= 1000) {
|
||||
_ms -= 1000;
|
||||
s++;
|
||||
}
|
||||
|
||||
// Miliiseconds
|
||||
ms = _ms;
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// ASS Formated
|
||||
wxString AssTime::GetASSFormated () {
|
||||
if (UseMSPrecision) return wxString::Format(_T("%01i:%02i:%02i.%03i"),h,m,s,ms);
|
||||
else return wxString::Format(_T("%01i:%02i:%02i.%02i"),h,m,s,ms/10);
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// SRT Formated
|
||||
wxString AssTime::GetSRTFormated () {
|
||||
wxString result = wxString::Format(_T("%02i:%02i:%02i,%03i"),h,m,s,ms);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Reads value from a text control and update it
|
||||
void AssTime::UpdateFromTextCtrl(wxTextCtrl *ctrl) {
|
||||
long start,end;
|
||||
wxString text = ctrl->GetValue();
|
||||
ctrl->GetSelection(&start,&end);
|
||||
if (start == end) {
|
||||
wxString nextChar = text.Mid(start,1);
|
||||
if (nextChar == _T(":") || nextChar == _T(".")) {
|
||||
wxString temp = text;
|
||||
text = temp.Left(start-1);
|
||||
text += nextChar;
|
||||
text += temp.Mid(start-1,1);
|
||||
text += temp.Mid(start+2);
|
||||
start++;
|
||||
end++;
|
||||
}
|
||||
else if (nextChar == _T("")) text.Remove(start-1,1);
|
||||
else text.Remove(start,1);
|
||||
}
|
||||
|
||||
// Update time
|
||||
ParseASS(text);
|
||||
ctrl->SetValue(GetASSFormated());
|
||||
ctrl->SetSelection(start,end);
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// AssTime ostream
|
||||
std::ostream& operator << (std::ostream &os,AssTime &time) {
|
||||
os << time.GetASSFormated();
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// AssTime comparison
|
||||
bool operator < (AssTime &t1, AssTime &t2) {
|
||||
return (t1.GetMS() < t2.GetMS());
|
||||
}
|
||||
|
||||
bool operator > (AssTime &t1, AssTime &t2) {
|
||||
return (t1.GetMS() > t2.GetMS());
|
||||
}
|
||||
|
||||
bool operator <= (AssTime &t1, AssTime &t2) {
|
||||
return (t1.GetMS() <= t2.GetMS());
|
||||
}
|
||||
|
||||
bool operator >= (AssTime &t1, AssTime &t2) {
|
||||
return (t1.GetMS() >= t2.GetMS());
|
||||
}
|
||||
|
||||
bool operator == (AssTime &t1, AssTime &t2) {
|
||||
return (t1.GetMS() == t2.GetMS());
|
||||
}
|
||||
|
||||
bool operator != (AssTime &t1, AssTime &t2) {
|
||||
return (t1.GetMS() != t2.GetMS());
|
||||
}
|
||||
|
||||
|
||||
/////////////////
|
||||
// Static option
|
||||
bool AssTime::UseMSPrecision = false;
|
74
core/ass_time.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Class for Ass format time
|
||||
class AssTime {
|
||||
private:
|
||||
int h; // Hours
|
||||
int m; // Minutes
|
||||
int s; // Seconds
|
||||
int ms; // Miliseconds
|
||||
|
||||
public:
|
||||
static bool UseMSPrecision;
|
||||
|
||||
AssTime();
|
||||
|
||||
int GetMS(); // Returns miliseconds
|
||||
void SetMS(int ms); // Sets values to miliseconds
|
||||
void ParseASS(const wxString text); // Sets value to text-form time, in ASS format
|
||||
void ParseSRT(const wxString text); // Sets value to text-form time, in SRT format
|
||||
wxString GetASSFormated(); // Returns the ASS representation of time
|
||||
wxString GetSRTFormated(); // Returns the SRT representation of time
|
||||
void UpdateFromTextCtrl(wxTextCtrl *ctrl); // Reads value from text control and updates both
|
||||
};
|
||||
|
||||
// Comparison operators
|
||||
bool operator == (AssTime &t1, AssTime &t2);
|
||||
bool operator != (AssTime &t1, AssTime &t2);
|
||||
bool operator < (AssTime &t1, AssTime &t2);
|
||||
bool operator > (AssTime &t1, AssTime &t2);
|
||||
bool operator <= (AssTime &t1, AssTime &t2);
|
||||
bool operator >= (AssTime &t1, AssTime &t2);
|
527
core/audio_box.cpp
Normal file
|
@ -0,0 +1,527 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <math.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/statline.h>
|
||||
#include <wx/laywin.h>
|
||||
#include <wx/recguard.h>
|
||||
#include "audio_box.h"
|
||||
#include "audio_display.h"
|
||||
#include "audio_karaoke.h"
|
||||
#include "frame_main.h"
|
||||
#include "options.h"
|
||||
#include "toggle_bitmap.h"
|
||||
#include "hotkeys.h"
|
||||
|
||||
|
||||
///////////////
|
||||
// Constructor
|
||||
AudioBox::AudioBox(wxWindow *parent,VideoDisplay *display) :
|
||||
wxPanel(parent,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL|wxBORDER_RAISED)
|
||||
{
|
||||
// Setup
|
||||
loaded = false;
|
||||
karaokeMode = false;
|
||||
|
||||
// Controls
|
||||
audioScroll = new wxScrollBar(this,Audio_Scrollbar);
|
||||
audioScroll->SetToolTip(_("Seek bar"));
|
||||
Sash = new wxSashWindow(this,Audio_Sash,wxDefaultPosition,wxDefaultSize,wxCLIP_CHILDREN | wxSW_3DBORDER);
|
||||
//Sash = new wxSashLayoutWindow(this,Audio_Sash,wxDefaultPosition,wxDefaultSize);
|
||||
sashSizer = new wxBoxSizer(wxVERTICAL);
|
||||
audioDisplay = new AudioDisplay(Sash,display);
|
||||
sashSizer->Add(audioDisplay,1,wxEXPAND,0);
|
||||
//sashSizer->SetSizeHints(Sash);
|
||||
Sash->SetSizer(sashSizer);
|
||||
Sash->SetSashVisible(wxSASH_BOTTOM,true);
|
||||
Sash->SetSashBorder(wxSASH_BOTTOM,true);
|
||||
Sash->SetMinimumSizeY(50);
|
||||
audioDisplay->ScrollBar = audioScroll;
|
||||
audioDisplay->box = this;
|
||||
HorizontalZoom = new wxSlider(this,Audio_Horizontal_Zoom,50,0,100,wxDefaultPosition,wxSize(-1,20),wxSL_VERTICAL);
|
||||
HorizontalZoom->SetToolTip(_("Horizontal zoom"));
|
||||
VerticalZoom = new wxSlider(this,Audio_Vertical_Zoom,50,0,100,wxDefaultPosition,wxSize(-1,20),wxSL_VERTICAL|wxSL_INVERSE);
|
||||
VerticalZoom->SetToolTip(_("Vertical zoom"));
|
||||
|
||||
// Display sizer
|
||||
DisplaySizer = new wxBoxSizer(wxVERTICAL);
|
||||
//DisplaySizer->Add(audioDisplay,1,wxEXPAND,0);
|
||||
DisplaySizer->Add(Sash,0,wxEXPAND,0);
|
||||
DisplaySizer->Add(audioScroll,0,wxEXPAND,0);
|
||||
|
||||
// Top sizer
|
||||
TopSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
TopSizer->Add(DisplaySizer,1,wxEXPAND,0);
|
||||
TopSizer->Add(HorizontalZoom,0,wxEXPAND,0);
|
||||
TopSizer->Add(VerticalZoom,0,wxEXPAND,0);
|
||||
|
||||
// Buttons sizer
|
||||
wxSizer *ButtonSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxButton *temp;
|
||||
temp = new wxBitmapButton(this,Audio_Button_Prev,wxBITMAP(button_prev),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Previous line/syllable (") + Hotkeys.GetText(_T("Audio Prev Line")) + _T("/") + Hotkeys.GetText(_T("Audio Prev Line Alt")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Next,wxBITMAP(button_next),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Next line/syllable (") + Hotkeys.GetText(_T("Audio Next Line")) + _T("/") + Hotkeys.GetText(_T("Audio Next Line Alt")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play,wxBITMAP(button_playsel),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Play selection (") + Hotkeys.GetText(_T("Audio Play")) + _T("/") + Hotkeys.GetText(_T("Audio Play Alt")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_Row,wxBITMAP(button_playline),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Play current line (") + Hotkeys.GetText(_T("Audio Play Original Line")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Stop,wxBITMAP(button_stop),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Stop"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,10);
|
||||
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_Before,wxBITMAP(button_play500before),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Play 500 ms before selection (") + Hotkeys.GetText(_T("Audio Play 500ms Before")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_After,wxBITMAP(button_play500after),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Play 500 ms after selection (") + Hotkeys.GetText(_T("Audio Play 500ms after")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_First,wxBITMAP(button_playfirst500),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Play first 500ms of selection (") + Hotkeys.GetText(_T("Audio Play First 500ms")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_500ms_Last,wxBITMAP(button_playlast500),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Play last 500ms of selection (") + Hotkeys.GetText(_T("Audio Play Last 500ms")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Play_To_End,wxBITMAP(button_playtoend),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Play from selection start to end of file"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,10);
|
||||
|
||||
temp = new wxBitmapButton(this,Audio_Button_Leadin,wxBITMAP(button_leadin),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Add lead in (") + Hotkeys.GetText(_T("Audio Add Lead In")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Leadout,wxBITMAP(button_leadout),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Add lead out (") + Hotkeys.GetText(_T("Audio Add Lead Out")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,10);
|
||||
|
||||
temp = new wxBitmapButton(this,Audio_Button_Commit,wxBITMAP(button_audio_commit),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Commit changes (") + Hotkeys.GetText(_T("Audio Commit (Stay)")) + _T("/") + Hotkeys.GetText(_T("Audio Commit Alt")) + _T(")"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,0);
|
||||
temp = new wxBitmapButton(this,Audio_Button_Goto,wxBITMAP(button_audio_goto),wxDefaultPosition,wxSize(30,-1));
|
||||
temp->SetToolTip(_("Go to selection"));
|
||||
ButtonSizer->Add(temp,0,wxRIGHT,10);
|
||||
|
||||
AutoCommit = new ToggleBitmap(this,Audio_Check_AutoCommit,wxBITMAP(toggle_audio_autocommit),wxSize(30,-1));
|
||||
AutoCommit->SetToolTip(_("Automatically commit all changes"));
|
||||
AutoCommit->SetValue(Options.AsBool(_T("Audio Autocommit")));
|
||||
ButtonSizer->Add(AutoCommit,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0);
|
||||
AutoScroll = new ToggleBitmap(this,Audio_Check_AutoGoto,wxBITMAP(toggle_audio_autoscroll),wxSize(30,-1));
|
||||
AutoScroll->SetToolTip(_("Auto scrolls audio display to selected line"));
|
||||
AutoScroll->SetValue(Options.AsBool(_T("Audio Autoscroll")));
|
||||
ButtonSizer->Add(AutoScroll,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0);
|
||||
SSAMode = new ToggleBitmap(this,Audio_Check_SSA,wxBITMAP(toggle_audio_ssa),wxSize(30,-1));
|
||||
SSAMode->SetToolTip(_("Substation Alpha Mode - Left click sets start and right click sets end"));
|
||||
SSAMode->SetValue(Options.AsBool(_T("Audio SSA Mode")));
|
||||
ButtonSizer->Add(SSAMode,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0);
|
||||
SpectrumMode = new ToggleBitmap(this,Audio_Check_Spectrum,wxBITMAP(toggle_audio_spectrum),wxSize(30,-1));
|
||||
SpectrumMode->SetToolTip(_("Spectrum analyzer mode"));
|
||||
SpectrumMode->SetValue(Options.AsBool(_T("Audio Spectrum")));
|
||||
ButtonSizer->Add(SpectrumMode,0,wxRIGHT | wxALIGN_CENTER | wxEXPAND,0);
|
||||
ButtonSizer->AddStretchSpacer(1);
|
||||
|
||||
// Karaoke sizer
|
||||
wxSizer *karaokeSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
audioKaraoke = new AudioKaraoke(this);
|
||||
audioKaraoke->box = this;
|
||||
audioKaraoke->display = audioDisplay;
|
||||
audioDisplay->karaoke = audioKaraoke;
|
||||
KaraokeButton = new wxToggleButton(this,Audio_Button_Karaoke,_("Karaoke"),wxDefaultPosition,wxSize(-1,-1));
|
||||
KaraokeButton->SetToolTip(_("Toggle karaoke mode"));
|
||||
karaokeSizer->Add(KaraokeButton,0,wxRIGHT,0);
|
||||
JoinButton = new wxButton(this,Audio_Button_Join,_("Join"),wxDefaultPosition,wxSize(-1,-1));
|
||||
JoinButton->SetToolTip(_("Join selected syllables"));
|
||||
JoinButton->Enable(false);
|
||||
karaokeSizer->Add(JoinButton,0,wxRIGHT,0);
|
||||
SplitButton = new wxButton(this,Audio_Button_Split,_("Split"),wxDefaultPosition,wxSize(-1,-1));
|
||||
SplitButton->SetToolTip(_("Split selected syllables"));
|
||||
SplitButton->Enable(false);
|
||||
karaokeSizer->Add(SplitButton,0,wxRIGHT,5);
|
||||
karaokeSizer->Add(audioKaraoke,1,wxEXPAND,0);
|
||||
|
||||
// Main sizer
|
||||
MainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
MainSizer->Add(TopSizer,0,wxEXPAND,0);
|
||||
MainSizer->Add(ButtonSizer,0,wxEXPAND,0);
|
||||
MainSizer->Add(new wxStaticLine(this),0,wxEXPAND|wxTOP|wxBOTTOM,2);
|
||||
MainSizer->Add(karaokeSizer,0,wxEXPAND,0);
|
||||
//MainSizer->SetSizeHints(this);
|
||||
SetSizer(MainSizer);
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AudioBox::~AudioBox() {
|
||||
}
|
||||
|
||||
|
||||
////////////
|
||||
// Set file
|
||||
void AudioBox::SetFile(wxString file,bool 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Event table
|
||||
BEGIN_EVENT_TABLE(AudioBox,wxPanel)
|
||||
EVT_COMMAND_SCROLL(Audio_Scrollbar, AudioBox::OnScrollbar)
|
||||
EVT_COMMAND_SCROLL(Audio_Horizontal_Zoom, AudioBox::OnHorizontalZoom)
|
||||
EVT_COMMAND_SCROLL(Audio_Vertical_Zoom, AudioBox::OnVerticalZoom)
|
||||
EVT_SASH_DRAGGED(Audio_Sash,AudioBox::OnSash)
|
||||
|
||||
EVT_BUTTON(Audio_Button_Play, AudioBox::OnPlaySelection)
|
||||
EVT_BUTTON(Audio_Button_Play_Row, AudioBox::OnPlayDialogue)
|
||||
EVT_BUTTON(Audio_Button_Stop, AudioBox::OnStop)
|
||||
EVT_BUTTON(Audio_Button_Next, AudioBox::OnNext)
|
||||
EVT_BUTTON(Audio_Button_Prev, AudioBox::OnPrev)
|
||||
EVT_BUTTON(Audio_Button_Play_500ms_Before, AudioBox::OnPlay500Before)
|
||||
EVT_BUTTON(Audio_Button_Play_500ms_After, AudioBox::OnPlay500After)
|
||||
EVT_BUTTON(Audio_Button_Play_500ms_First, AudioBox::OnPlay500First)
|
||||
EVT_BUTTON(Audio_Button_Play_500ms_Last, AudioBox::OnPlay500Last)
|
||||
EVT_BUTTON(Audio_Button_Play_To_End, AudioBox::OnPlayToEnd)
|
||||
EVT_BUTTON(Audio_Button_Commit, AudioBox::OnCommit)
|
||||
EVT_BUTTON(Audio_Button_Goto, AudioBox::OnGoto)
|
||||
EVT_BUTTON(Audio_Button_Join,AudioBox::OnJoin)
|
||||
EVT_BUTTON(Audio_Button_Split,AudioBox::OnSplit)
|
||||
EVT_BUTTON(Audio_Button_Leadin,AudioBox::OnLeadIn)
|
||||
EVT_BUTTON(Audio_Button_Leadout,AudioBox::OnLeadOut)
|
||||
|
||||
EVT_TOGGLEBUTTON(Audio_Button_Karaoke, AudioBox::OnKaraoke)
|
||||
EVT_TOGGLEBUTTON(Audio_Check_AutoGoto,AudioBox::OnAutoGoto)
|
||||
EVT_TOGGLEBUTTON(Audio_Check_SSA,AudioBox::OnSSAMode)
|
||||
EVT_TOGGLEBUTTON(Audio_Check_Spectrum,AudioBox::OnSpectrumMode)
|
||||
EVT_TOGGLEBUTTON(Audio_Check_AutoCommit,AudioBox::OnAutoCommit)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Scrollbar changed
|
||||
void AudioBox::OnScrollbar(wxScrollEvent &event) {
|
||||
audioDisplay->SetPosition(event.GetPosition()*12);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// Horizontal zoom bar changed
|
||||
void AudioBox::OnHorizontalZoom(wxScrollEvent &event) {
|
||||
audioDisplay->SetSamplesPercent(event.GetPosition());
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Vertical zoom bar changed
|
||||
void AudioBox::OnVerticalZoom(wxScrollEvent &event) {
|
||||
int pos = event.GetPosition();
|
||||
if (pos < 1) pos = 1;
|
||||
if (pos > 100) pos = 100;
|
||||
audioDisplay->SetScale(pow(float(pos)/50.0f,3));
|
||||
}
|
||||
|
||||
|
||||
////////
|
||||
// Sash
|
||||
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->SetSize(w,h);
|
||||
audioDisplay->SetSizeHints(w,h,w,h);
|
||||
|
||||
// Store new size
|
||||
Options.SetInt(_T("Audio Display Height"),h);
|
||||
Options.Save();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Play selection
|
||||
void AudioBox::OnPlaySelection(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
audioDisplay->Play(start,end);
|
||||
}
|
||||
|
||||
|
||||
/////////////////
|
||||
// Play dialogue
|
||||
void AudioBox::OnPlayDialogue(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesDialogue(start,end);
|
||||
audioDisplay->SetSelection(start, end);
|
||||
audioDisplay->Play(start,end);
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// Stop Playing
|
||||
void AudioBox::OnStop(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->Stop();
|
||||
}
|
||||
|
||||
|
||||
////////
|
||||
// Next
|
||||
void AudioBox::OnNext(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->Next();
|
||||
}
|
||||
|
||||
|
||||
////////////
|
||||
// Previous
|
||||
void AudioBox::OnPrev(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->Prev();
|
||||
}
|
||||
|
||||
|
||||
/////////////////
|
||||
// 500 ms before
|
||||
void AudioBox::OnPlay500Before(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
audioDisplay->Play(start-500,start);
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// 500 ms after
|
||||
void AudioBox::OnPlay500After(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
audioDisplay->Play(end,end+500);
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// First 500 ms
|
||||
void AudioBox::OnPlay500First(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
int endp = start+500;
|
||||
if (endp > end) endp = end;
|
||||
audioDisplay->Play(start,endp);
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Last 500 ms
|
||||
void AudioBox::OnPlay500Last(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
int startp = end-500;
|
||||
if (startp < start) startp = start;
|
||||
audioDisplay->Play(startp,end);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Start to end of file
|
||||
void AudioBox::OnPlayToEnd(wxCommandEvent &event) {
|
||||
int start=0,end=0;
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->GetTimesSelection(start,end);
|
||||
audioDisplay->Play(start,-1);
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Commit changes
|
||||
void AudioBox::OnCommit(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->CommitChanges();
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Toggle karaoke
|
||||
void AudioBox::OnKaraoke(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
if (karaokeMode) {
|
||||
karaokeMode = false;
|
||||
audioKaraoke->enabled = false;
|
||||
SetKaraokeButtons(false,false);
|
||||
audioDisplay->SetDialogue();
|
||||
audioKaraoke->Refresh(false);
|
||||
}
|
||||
|
||||
else {
|
||||
karaokeMode = true;
|
||||
audioKaraoke->enabled = true;
|
||||
SetKaraokeButtons(true,true);
|
||||
audioDisplay->SetDialogue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Sets karaoke buttons
|
||||
void AudioBox::SetKaraokeButtons(bool join,bool split) {
|
||||
audioDisplay->SetFocus();
|
||||
JoinButton->Enable(join);
|
||||
SplitButton->Enable(split);
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Join button
|
||||
void AudioBox::OnJoin(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioKaraoke->Join();
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// Split button
|
||||
void AudioBox::OnSplit(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioKaraoke->Split();
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Goto button
|
||||
void AudioBox::OnGoto(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->MakeDialogueVisible(true);
|
||||
}
|
||||
|
||||
|
||||
/////////////
|
||||
// Auto Goto
|
||||
void AudioBox::OnAutoGoto(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
Options.SetBool(_T("Audio Autoscroll"),AutoScroll->GetValue());
|
||||
Options.Save();
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Auto Commit
|
||||
void AudioBox::OnAutoCommit(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
Options.SetBool(_T("Audio Autocommit"),AutoCommit->GetValue());
|
||||
Options.Save();
|
||||
}
|
||||
|
||||
|
||||
////////////
|
||||
// SSA Mode
|
||||
void AudioBox::OnSSAMode(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
Options.SetBool(_T("Audio SSA Mode"),SSAMode->GetValue());
|
||||
Options.Save();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
// Spectrum Analyzer Mode
|
||||
void AudioBox::OnSpectrumMode(wxCommandEvent &event) {
|
||||
Options.SetBool(_T("Audio Spectrum"),SpectrumMode->GetValue());
|
||||
Options.Save();
|
||||
audioDisplay->UpdateImage(false);
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->Refresh(false);
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Lead in/out
|
||||
void AudioBox::OnLeadIn(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->AddLead(true,false);
|
||||
}
|
||||
|
||||
void AudioBox::OnLeadOut(wxCommandEvent &event) {
|
||||
audioDisplay->SetFocus();
|
||||
audioDisplay->AddLead(false,true);
|
||||
}
|
159
core/audio_box.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#ifndef AUDIO_BOX_H
|
||||
#define AUDIO_BOX_H
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
#include <wx/sashwin.h>
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AudioDisplay;
|
||||
class VideoDisplay;
|
||||
class AudioKaraoke;
|
||||
class FrameMain;
|
||||
class wxToggleButton;
|
||||
class ToggleBitmap;
|
||||
|
||||
|
||||
///////////////////
|
||||
// Audio box class
|
||||
class AudioBox : public wxPanel {
|
||||
friend class AudioDisplay;
|
||||
|
||||
private:
|
||||
wxScrollBar *audioScroll;
|
||||
wxSlider *HorizontalZoom;
|
||||
wxSlider *VerticalZoom;
|
||||
wxSizer *MainSizer;
|
||||
wxSizer *TopSizer;
|
||||
wxSizer *sashSizer;
|
||||
wxSizer *DisplaySizer;
|
||||
wxSashWindow *Sash;
|
||||
|
||||
wxButton *SplitButton;
|
||||
wxButton *JoinButton;
|
||||
ToggleBitmap *AutoScroll;
|
||||
ToggleBitmap *SSAMode;
|
||||
ToggleBitmap *AutoCommit;
|
||||
ToggleBitmap *SpectrumMode;
|
||||
|
||||
void OnScrollbar(wxScrollEvent &event);
|
||||
void OnHorizontalZoom(wxScrollEvent &event);
|
||||
void OnVerticalZoom(wxScrollEvent &event);
|
||||
void OnSash(wxSashEvent &event);
|
||||
|
||||
void OnPlaySelection(wxCommandEvent &event);
|
||||
void OnPlayDialogue(wxCommandEvent &event);
|
||||
void OnStop(wxCommandEvent &event);
|
||||
void OnNext(wxCommandEvent &event);
|
||||
void OnPrev(wxCommandEvent &event);
|
||||
void OnPlay500Before(wxCommandEvent &event);
|
||||
void OnPlay500After(wxCommandEvent &event);
|
||||
void OnPlay500First(wxCommandEvent &event);
|
||||
void OnPlay500Last(wxCommandEvent &event);
|
||||
void OnPlayToEnd(wxCommandEvent &event);
|
||||
void OnCommit(wxCommandEvent &event);
|
||||
void OnKaraoke(wxCommandEvent &event);
|
||||
void OnJoin(wxCommandEvent &event);
|
||||
void OnSplit(wxCommandEvent &event);
|
||||
void OnGoto(wxCommandEvent &event);
|
||||
void OnLeadIn(wxCommandEvent &event);
|
||||
void OnLeadOut(wxCommandEvent &event);
|
||||
|
||||
void OnAutoGoto(wxCommandEvent &event);
|
||||
void OnAutoCommit(wxCommandEvent &event);
|
||||
void OnSSAMode(wxCommandEvent &event);
|
||||
void OnSpectrumMode(wxCommandEvent &event);
|
||||
|
||||
public:
|
||||
AudioDisplay *audioDisplay;
|
||||
AudioKaraoke *audioKaraoke;
|
||||
wxToggleButton *KaraokeButton;
|
||||
FrameMain *frameMain;
|
||||
wxString audioName;
|
||||
bool loaded;
|
||||
bool karaokeMode;
|
||||
|
||||
AudioBox(wxWindow *parent,VideoDisplay *display);
|
||||
~AudioBox();
|
||||
|
||||
void SetFile(wxString file,bool FromVideo);
|
||||
void SetKaraokeButtons(bool join,bool split);
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
|
||||
///////
|
||||
// IDs
|
||||
enum {
|
||||
Audio_Scrollbar = 1600,
|
||||
Audio_Horizontal_Zoom,
|
||||
Audio_Vertical_Zoom,
|
||||
Audio_Sash,
|
||||
|
||||
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,
|
||||
Audio_Button_Split,
|
||||
Audio_Button_Leadin,
|
||||
Audio_Button_Leadout,
|
||||
|
||||
Audio_Check_AutoCommit,
|
||||
Audio_Check_AutoGoto,
|
||||
Audio_Check_SSA,
|
||||
Audio_Check_Spectrum
|
||||
};
|
||||
|
||||
|
||||
#endif
|
1762
core/audio_display.cpp
Normal file
165
core/audio_display.h
Normal file
|
@ -0,0 +1,165 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#ifndef AUDIO_DISPLAY_H
|
||||
#define AUDIO_DISPLAY_H
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
#include "audio_provider.h"
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AssDialogue;
|
||||
class SubtitlesGrid;
|
||||
class AudioBox;
|
||||
class AudioKaraoke;
|
||||
class VideoDisplay;
|
||||
|
||||
/////////////////
|
||||
// Display class
|
||||
class AudioDisplay: public wxWindow {
|
||||
private:
|
||||
SubtitlesGrid *grid;
|
||||
int line_n;
|
||||
AssDialogue *dialogue;
|
||||
VideoDisplay *video;
|
||||
|
||||
wxBitmap *origImage;
|
||||
wxBitmap *spectrumDisplay;
|
||||
__int64 PositionSample;
|
||||
float scale;
|
||||
int samples;
|
||||
int Position;
|
||||
int samplesPercent;
|
||||
int oldCurPos;
|
||||
bool hasFocus;
|
||||
bool blockUpdate;
|
||||
bool dontReadTimes;
|
||||
|
||||
bool hasSel;
|
||||
bool hasKaraoke;
|
||||
bool diagUpdated;
|
||||
__int64 selStart;
|
||||
__int64 selEnd;
|
||||
__int64 lineStart;
|
||||
__int64 lineEnd;
|
||||
__int64 selStartCap;
|
||||
__int64 selEndCap;
|
||||
int hold;
|
||||
int lastX;
|
||||
int curStartMS;
|
||||
int curEndMS;
|
||||
int holdSyl;
|
||||
|
||||
int *peak;
|
||||
int *min;
|
||||
|
||||
void OnPaint(wxPaintEvent &event);
|
||||
void OnMouseEvent(wxMouseEvent &event);
|
||||
void OnSize(wxSizeEvent &event);
|
||||
void OnUpdateTimer(wxTimerEvent &event);
|
||||
void OnKeyDown(wxKeyEvent &event);
|
||||
void OnGetFocus(wxFocusEvent &event);
|
||||
void OnLoseFocus(wxFocusEvent &event);
|
||||
|
||||
void UpdateSamples();
|
||||
void Reset();
|
||||
void DrawWaveform(wxDC &dc,bool weak);
|
||||
void DrawSpectrum(wxDC &dc,bool weak);
|
||||
void GetDialoguePos(__int64 &start,__int64 &end,bool cap);
|
||||
void GetKaraokePos(__int64 &start,__int64 &end,bool cap);
|
||||
void UpdatePosition(int pos,bool IsSample=false);
|
||||
|
||||
int GetMSAtX(__int64 x);
|
||||
int GetXAtMS(__int64 ms);
|
||||
int GetMSAtSample(__int64 x);
|
||||
__int64 GetSampleAtMS(__int64 ms);
|
||||
int GetSyllableAtX(int x);
|
||||
|
||||
public:
|
||||
bool NeedCommit;
|
||||
bool loaded;
|
||||
int w,h;
|
||||
AudioProvider *provider;
|
||||
AudioBox *box;
|
||||
AudioKaraoke *karaoke;
|
||||
wxScrollBar *ScrollBar;
|
||||
wxTimer UpdateTimer;
|
||||
|
||||
AudioDisplay(wxWindow *parent,VideoDisplay *display);
|
||||
~AudioDisplay();
|
||||
|
||||
void AddLead(bool in,bool out);
|
||||
void UpdateImage(bool weak=false);
|
||||
void Update();
|
||||
void SetPosition(int pos);
|
||||
void SetSamplesPercent(int percent,bool update=true,float pivot=0.5);
|
||||
void SetScale(float scale);
|
||||
void SetFile(wxString file);
|
||||
void SetFromVideo();
|
||||
void UpdateScrollbar();
|
||||
__int64 GetSampleAtX(int x);
|
||||
int GetXAtSample(__int64 n);
|
||||
void SetDialogue(SubtitlesGrid *_grid=NULL,AssDialogue *diag=NULL,int n=-1);
|
||||
|
||||
void MakeDialogueVisible(bool force=false);
|
||||
void CommitChanges();
|
||||
void ChangeLine(int delta);
|
||||
void Next();
|
||||
void Prev();
|
||||
|
||||
void Play(int start,int end);
|
||||
void Stop();
|
||||
void GetTimesDialogue(int &start,int &end);
|
||||
void GetTimesSelection(int &start,int &end);
|
||||
void SetSelection(int start, int end);
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
|
||||
///////
|
||||
// IDs
|
||||
enum {
|
||||
Audio_Update_Timer = 1700
|
||||
};
|
||||
|
||||
|
||||
#endif
|
548
core/audio_karaoke.cpp
Normal file
|
@ -0,0 +1,548 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/tokenzr.h>
|
||||
#include "audio_karaoke.h"
|
||||
#include "audio_display.h"
|
||||
#include "audio_box.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_override.h"
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Syllable constructor
|
||||
KaraokeSyllable::KaraokeSyllable() {
|
||||
length = 0;
|
||||
position = 0;
|
||||
display_w = 0;
|
||||
display_x = 0;
|
||||
selected = false;
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Constructor
|
||||
AudioKaraoke::AudioKaraoke(wxWindow *parent)
|
||||
: wxWindow (parent,-1,wxDefaultPosition,wxSize(10,5),wxTAB_TRAVERSAL|wxBORDER_SUNKEN)
|
||||
{
|
||||
enabled = false;
|
||||
curSyllable = 0;
|
||||
diag = NULL;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Load from dialogue
|
||||
bool AudioKaraoke::LoadFromDialogue(AssDialogue *_diag) {
|
||||
// Set dialogue
|
||||
diag = _diag;
|
||||
if (!diag) {
|
||||
Refresh(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Split
|
||||
bool hasKar = ParseDialogue(diag);
|
||||
|
||||
// No karaoke, autosplit
|
||||
if (!hasKar) {
|
||||
AutoSplit();
|
||||
}
|
||||
|
||||
// Done
|
||||
SetSelection(curSyllable);
|
||||
Refresh(false);
|
||||
return !hasKar;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// Calculate length of karaoke
|
||||
int AudioKaraoke::GetKaraokeLength(AssDialogueBlockOverride *block) {
|
||||
AssOverrideTag *tag;
|
||||
size_t n = block->Tags.size();
|
||||
int len = -1;
|
||||
for (size_t i=0;i<n;i++) {
|
||||
tag = block->Tags.at(i);
|
||||
if (tag->Name == _T("\\k") || tag->Name == _T("\\K") || tag->Name == _T("\\kf") || tag->Name == _T("\\ko")) {
|
||||
len = tag->Params.at(0)->AsInt();
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// Gets tag of nth syllable
|
||||
wxString AudioKaraoke::GetSyllableTag(AssDialogueBlockOverride *block,int n) {
|
||||
return block->Tags.at(n)->Name;
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// Writes line back
|
||||
void AudioKaraoke::Commit() {
|
||||
wxString finalText = _T("");
|
||||
KaraokeSyllable *syl;
|
||||
size_t n = syllables.size();
|
||||
for (size_t i=0;i<n;i++) {
|
||||
syl = &syllables.at(i);
|
||||
finalText += wxString::Format(_T("{\\k%i}"),syl->length) + syl->contents;
|
||||
}
|
||||
diag->Text = finalText;
|
||||
diag->ParseASSTags();
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Autosplit line
|
||||
void AudioKaraoke::AutoSplit() {
|
||||
// Get lengths
|
||||
int timelen = (diag->End.GetMS() - diag->Start.GetMS())/10;
|
||||
int letterlen = diag->Text.Length();
|
||||
int round = letterlen / 2;
|
||||
int curlen;
|
||||
int acumLen = 0;
|
||||
wxString newText;
|
||||
|
||||
// Parse words
|
||||
wxStringTokenizer tkz(diag->Text,_T(" "),wxTOKEN_RET_DELIMS);
|
||||
while (tkz.HasMoreTokens()) {
|
||||
wxString token = tkz.GetNextToken();
|
||||
curlen = (token.Length() * timelen + round) / letterlen;
|
||||
acumLen += curlen;
|
||||
if (acumLen > timelen) {
|
||||
curlen -= acumLen - timelen;
|
||||
acumLen = timelen;
|
||||
}
|
||||
newText += wxString::Format(_T("{\\k%i}"),curlen) + token;
|
||||
}
|
||||
|
||||
// Load
|
||||
AssDialogue newDiag(diag->data);
|
||||
newDiag.Text = newText;
|
||||
newDiag.ParseASSTags();
|
||||
ParseDialogue(&newDiag);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////
|
||||
// Parses text to extract karaoke
|
||||
bool AudioKaraoke::ParseDialogue(AssDialogue *curDiag) {
|
||||
// Wipe
|
||||
syllables.clear();
|
||||
|
||||
// Prepare syllable data
|
||||
AssDialogueBlock *block;
|
||||
AssDialogueBlockOverride *override;
|
||||
AssDialogueBlockPlain *plain;
|
||||
KaraokeSyllable temp;
|
||||
temp.contents = _T("");
|
||||
int pos = 0;
|
||||
temp.length = 0;
|
||||
temp.position = 0;
|
||||
size_t n = curDiag->Blocks.size();
|
||||
bool foundOne = false;
|
||||
bool foundBlock = false;
|
||||
|
||||
// Load syllable data
|
||||
for (size_t i=0;i<n;i++) {
|
||||
block = curDiag->Blocks.at(i);
|
||||
override = AssDialogueBlock::GetAsOverride(block);
|
||||
if (override) {
|
||||
int len = GetKaraokeLength(override);
|
||||
if (len != -1) {
|
||||
if (foundOne) syllables.push_back(temp);
|
||||
foundOne = true;
|
||||
foundBlock = true;
|
||||
pos += temp.length;
|
||||
temp.length = len;
|
||||
temp.position = pos;
|
||||
temp.contents = _T("");
|
||||
}
|
||||
}
|
||||
else {
|
||||
plain = AssDialogueBlock::GetAsPlain(block);
|
||||
temp.contents += plain->text;
|
||||
if (plain->text != _T("")) foundOne = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty?
|
||||
if (curDiag->Text.IsEmpty()) {
|
||||
temp.length = (curDiag->End.GetMS() - curDiag->Start.GetMS())/10;
|
||||
temp.contents = curDiag->Text;
|
||||
temp.position = 0;
|
||||
foundBlock = true;
|
||||
}
|
||||
|
||||
// Last syllable
|
||||
if (foundBlock) syllables.push_back(temp);
|
||||
return foundBlock;
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// Set syllable
|
||||
void AudioKaraoke::SetSyllable(int n) {
|
||||
curSyllable = n;
|
||||
startClickSyl = n;
|
||||
SetSelection(n);
|
||||
Refresh(false);
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Event table
|
||||
BEGIN_EVENT_TABLE(AudioKaraoke,wxWindow)
|
||||
EVT_PAINT(AudioKaraoke::OnPaint)
|
||||
EVT_SIZE(AudioKaraoke::OnSize)
|
||||
EVT_MOUSE_EVENTS(AudioKaraoke::OnMouse)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
///////////////
|
||||
// Paint event
|
||||
void AudioKaraoke::OnPaint(wxPaintEvent &event) {
|
||||
// Get dimensions
|
||||
int w,h;
|
||||
GetClientSize(&w,&h);
|
||||
|
||||
// Start Paint
|
||||
wxPaintDC dc(this);
|
||||
dc.BeginDrawing();
|
||||
|
||||
// Draw background
|
||||
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(0,0,w,h);
|
||||
|
||||
// Set syllable font
|
||||
wxFont curFont(9,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_NORMAL,false,_T("Verdana"),wxFONTENCODING_SYSTEM);
|
||||
dc.SetFont(curFont);
|
||||
dc.SetPen(wxPen(wxColour(0,0,0)));
|
||||
|
||||
// Draw syllables
|
||||
if (enabled) {
|
||||
wxString temptext;
|
||||
size_t syln = syllables.size();
|
||||
int dx = 0;
|
||||
int tw,th;
|
||||
int delta;
|
||||
int dlen;
|
||||
for (size_t i=0;i<syln;i++) {
|
||||
// Calculate text length
|
||||
temptext = syllables.at(i).contents;
|
||||
temptext.Trim(true);
|
||||
temptext.Trim(false);
|
||||
GetTextExtent(temptext,&tw,&th,NULL,NULL,&curFont);
|
||||
delta = 0;
|
||||
if (tw < 10) delta = 10 - tw;
|
||||
dlen = tw + 8 + delta;
|
||||
|
||||
// Draw border
|
||||
if (syllables.at(i).selected) {
|
||||
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
|
||||
}
|
||||
else {
|
||||
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT)));
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
||||
}
|
||||
dc.DrawRectangle(dx,0,dlen,h);
|
||||
|
||||
// Draw text
|
||||
dc.DrawText(temptext,dx+(delta/2)+4,(h-th)/2);
|
||||
|
||||
// Set syllable data
|
||||
syllables.at(i).display_x = dx;
|
||||
syllables.at(i).display_w = dlen;
|
||||
|
||||
// Increment dx
|
||||
dx += dlen;
|
||||
}
|
||||
}
|
||||
|
||||
// End paint
|
||||
dc.EndDrawing();
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Size event
|
||||
void AudioKaraoke::OnSize(wxSizeEvent &event) {
|
||||
Refresh(false);
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Mouse event
|
||||
void AudioKaraoke::OnMouse(wxMouseEvent &event) {
|
||||
// Get coordinates
|
||||
int x = event.GetX();
|
||||
int y = event.GetY();
|
||||
bool shift = event.m_shiftDown;
|
||||
|
||||
// Left button down
|
||||
if (event.LeftDown()) {
|
||||
int syl = GetSylAtX(x);
|
||||
|
||||
if (syl != -1) {
|
||||
if (shift) {
|
||||
SetSelection(syl,startClickSyl);
|
||||
Refresh(false);
|
||||
}
|
||||
|
||||
else {
|
||||
SetSelection(syl);
|
||||
startClickSyl = syl;
|
||||
curSyllable = syl;
|
||||
Refresh(false);
|
||||
display->Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// Get Syllable at position X
|
||||
int AudioKaraoke::GetSylAtX(int x) {
|
||||
int dx,dw;
|
||||
size_t syln = syllables.size();
|
||||
for (size_t i=0;i<syln;i++) {
|
||||
dx = syllables.at(i).display_x;
|
||||
dw = syllables.at(i).display_w;
|
||||
if (x >= dx && x < dx+dw) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/////////////////
|
||||
// Set selection
|
||||
void AudioKaraoke::SetSelection(int start,int end) {
|
||||
// Default end
|
||||
if (end == -1) end = start;
|
||||
|
||||
// Get min/max range
|
||||
size_t min = start;
|
||||
size_t max = end;
|
||||
if (max < min) {
|
||||
size_t temp = max;
|
||||
max = min;
|
||||
min = temp;
|
||||
}
|
||||
|
||||
// Set values
|
||||
bool state;
|
||||
size_t syls = syllables.size();
|
||||
int sels = 0;
|
||||
for (size_t i=0;i<syls;i++) {
|
||||
state = (i >= min && i <= max);
|
||||
syllables.at(i).selected = state;
|
||||
if (state) sels++;
|
||||
}
|
||||
|
||||
// Set box buttons
|
||||
box->SetKaraokeButtons(sels > 1,sels > 0);
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
// Join syllables
|
||||
void AudioKaraoke::Join() {
|
||||
// Variables
|
||||
bool gotOne = false;
|
||||
size_t syls = syllables.size();
|
||||
KaraokeSyllable *curSyl;
|
||||
int first = 0;
|
||||
|
||||
// Loop
|
||||
for (size_t i=0;i<syls;i++) {
|
||||
curSyl = &syllables.at(i);
|
||||
if (curSyl->selected) {
|
||||
if (!gotOne) {
|
||||
gotOne = true;
|
||||
first = i;
|
||||
}
|
||||
else {
|
||||
syllables.at(i-1).length += curSyl->length;
|
||||
syllables.at(i-1).contents += curSyl->contents;
|
||||
syllables.erase(syllables.begin()+i);
|
||||
i--;
|
||||
syls--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set selection
|
||||
curSyllable = first;
|
||||
|
||||
// Update
|
||||
display->NeedCommit = true;
|
||||
display->Update();
|
||||
Refresh(false);
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// Split syllables
|
||||
void AudioKaraoke::Split() {
|
||||
// Variables
|
||||
bool hasSplit = false;
|
||||
|
||||
// Loop
|
||||
size_t syls = syllables.size();
|
||||
for (size_t i=0;i<syls;i++) {
|
||||
if (syllables.at(i).selected) {
|
||||
int split = SplitSyl(i);
|
||||
if (split > 0) {
|
||||
syls += split;
|
||||
i += split;
|
||||
hasSplit = true;
|
||||
}
|
||||
if (split == -1) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update
|
||||
if (hasSplit) {
|
||||
display->NeedCommit = true;
|
||||
display->Update();
|
||||
Refresh(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// Split a syllable
|
||||
int AudioKaraoke::SplitSyl (int n) {
|
||||
// Get split text
|
||||
KaraokeSyllable *curSyl = &syllables.at(n);
|
||||
wxString result = wxGetTextFromUser(_("Enter pipes (\"|\") to split:"), _("Split syllable"), curSyl->contents);
|
||||
if (result.IsEmpty()) return -1;
|
||||
|
||||
// Prepare parsing
|
||||
const int splits = result.Freq(_T('|'));
|
||||
const int totalCharLen = curSyl->contents.Length() + splits + 1;
|
||||
const int charRound = totalCharLen / 2;
|
||||
const int totalTimeLen = curSyl->length;
|
||||
int curpos = curSyl->position;
|
||||
int curlen = 0;
|
||||
wxStringTokenizer tkn(result,_T("|"),wxTOKEN_RET_EMPTY_ALL);
|
||||
bool isFirst = true;
|
||||
|
||||
// Parse
|
||||
for (int curn=n;tkn.HasMoreTokens();curn++) {
|
||||
// Prepare syllable
|
||||
if (!isFirst) {
|
||||
KaraokeSyllable temp;
|
||||
syllables.insert(syllables.begin()+curn,temp);
|
||||
temp.selected = true;
|
||||
}
|
||||
curSyl = &syllables.at(curn);
|
||||
|
||||
// Set text
|
||||
wxString token = tkn.GetNextToken();
|
||||
curSyl->contents = token;
|
||||
|
||||
// Set position
|
||||
int len = (totalTimeLen * (token.Length() + 1) + charRound) / totalCharLen;
|
||||
curlen += len;
|
||||
if (curlen > totalTimeLen) {
|
||||
len -= totalTimeLen - curlen;
|
||||
curlen = totalTimeLen;
|
||||
}
|
||||
curSyl->length = len;
|
||||
curSyl->position = curpos;
|
||||
curpos += len;
|
||||
|
||||
// Done
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
// Return splits
|
||||
return splits;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////
|
||||
// Apply delta length to syllable
|
||||
bool AudioKaraoke::SyllableDelta(int n,int delta,int mode) {
|
||||
// Get syllable and next
|
||||
KaraokeSyllable *curSyl=NULL,*nextSyl=NULL;
|
||||
curSyl = &syllables.at(n);
|
||||
int nkar = syllables.size();
|
||||
if (n < nkar-1) {
|
||||
nextSyl = &syllables.at(n+1);
|
||||
}
|
||||
|
||||
// Get variables
|
||||
int len = curSyl->length;
|
||||
|
||||
// Cap delta
|
||||
int minLen = 0;
|
||||
if (len + delta < minLen) delta = minLen-len;
|
||||
if (mode == 0 && nextSyl && (nextSyl->length - delta) < minLen) delta = nextSyl->length - minLen;
|
||||
|
||||
// Apply
|
||||
if (delta != 0) {
|
||||
curSyl->length += delta;
|
||||
|
||||
// Normal mode
|
||||
if (mode == 0 && nextSyl) {
|
||||
nextSyl->length -= delta;
|
||||
nextSyl->position += delta;
|
||||
}
|
||||
|
||||
// Shift mode
|
||||
if (mode == 1) {
|
||||
for (int i=n+1;i<nkar;i++) {
|
||||
syllables.at(i).position += delta;
|
||||
}
|
||||
}
|
||||
|
||||
// Flag update
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
118
core/audio_karaoke.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#ifndef AUDIO_KARAOKE_H
|
||||
#define AUDIO_KARAOKE_H
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AssDialogue;
|
||||
class AssDialogueBlockOverride;
|
||||
class AudioDisplay;
|
||||
class AudioBox;
|
||||
|
||||
|
||||
//////////////////
|
||||
// Syllable class
|
||||
class KaraokeSyllable {
|
||||
public:
|
||||
int length;
|
||||
int position;
|
||||
int display_w;
|
||||
int display_x;
|
||||
wxString contents;
|
||||
wxString tag;
|
||||
bool selected;
|
||||
|
||||
KaraokeSyllable();
|
||||
};
|
||||
|
||||
|
||||
////////////
|
||||
// Typedefs
|
||||
typedef std::vector<KaraokeSyllable> SylVector;
|
||||
|
||||
|
||||
/////////
|
||||
// Class
|
||||
class AudioKaraoke : public wxWindow {
|
||||
private:
|
||||
AssDialogue *diag;
|
||||
int startClickSyl;
|
||||
|
||||
int GetKaraokeLength(AssDialogueBlockOverride *block);
|
||||
wxString GetSyllableTag(AssDialogueBlockOverride *block,int n);
|
||||
void AutoSplit();
|
||||
bool ParseDialogue(AssDialogue *diag);
|
||||
|
||||
int GetSylAtX(int x);
|
||||
int SplitSyl(int n);
|
||||
|
||||
void OnPaint(wxPaintEvent &event);
|
||||
void OnSize(wxSizeEvent &event);
|
||||
void OnMouse(wxMouseEvent &event);
|
||||
|
||||
public:
|
||||
AudioDisplay *display;
|
||||
AudioBox *box;
|
||||
|
||||
int curSyllable;
|
||||
bool enabled;
|
||||
SylVector syllables;
|
||||
SylVector origSyl;
|
||||
|
||||
AudioKaraoke(wxWindow *parent);
|
||||
bool LoadFromDialogue(AssDialogue *diag);
|
||||
void Commit();
|
||||
void SetSyllable(int n);
|
||||
void SetSelection(int start,int end=-1);
|
||||
bool SyllableDelta(int n,int delta,int mode);
|
||||
|
||||
void Join();
|
||||
void Split();
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
605
core/audio_provider.cpp
Normal file
|
@ -0,0 +1,605 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
#include <wx/filename.h>
|
||||
#include <Mmreg.h>
|
||||
#include "avisynth_wrap.h"
|
||||
#include "audio_provider.h"
|
||||
#include "video_display.h"
|
||||
#include "options.h"
|
||||
#include "audio_display.h"
|
||||
#include "main.h"
|
||||
#include "dialog_progress.h"
|
||||
|
||||
extern "C" {
|
||||
#include <portaudio.h>
|
||||
}
|
||||
|
||||
//stupid msvc
|
||||
#ifndef max
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
int AudioProvider::pa_refcount = 0;
|
||||
|
||||
//////////////
|
||||
// Constructor
|
||||
AudioProvider::AudioProvider(wxString _filename, AudioDisplay *_display) {
|
||||
type = AUDIO_PROVIDER_NONE;
|
||||
playing = false;
|
||||
stopping = false;
|
||||
blockcache = NULL;
|
||||
blockcount = 0;
|
||||
raw = NULL;
|
||||
display = _display;
|
||||
blockcount = 0;
|
||||
|
||||
filename = _filename;
|
||||
|
||||
// Initialize portaudio
|
||||
if (!pa_refcount) {
|
||||
PaError err = Pa_Initialize();
|
||||
if (err != paNoError)
|
||||
throw _T("Failed opening PortAudio with error: ") + wxString(Pa_GetErrorText(err),wxConvLocal);
|
||||
pa_refcount++;
|
||||
}
|
||||
|
||||
try {
|
||||
OpenAVSAudio();
|
||||
OpenStream();
|
||||
} catch (...) {
|
||||
Unload();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Destructor
|
||||
AudioProvider::~AudioProvider() {
|
||||
CloseStream();
|
||||
Unload();
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// Unload audio
|
||||
void AudioProvider::Unload() {
|
||||
// Clean up avisynth
|
||||
clip = NULL;
|
||||
|
||||
// Close file
|
||||
if (type == AUDIO_PROVIDER_DISK_CACHE) {
|
||||
file_cache.close();
|
||||
wxRemoveFile(DiskCacheName());
|
||||
}
|
||||
|
||||
// Free ram cache
|
||||
if (blockcache) {
|
||||
for (int i = 0; i < blockcount; i++)
|
||||
if (blockcache[i])
|
||||
delete blockcache[i];
|
||||
delete blockcache;
|
||||
}
|
||||
|
||||
// Clear buffers
|
||||
delete raw;
|
||||
|
||||
if (!--pa_refcount)
|
||||
Pa_Terminate();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// Load audio from avisynth
|
||||
void AudioProvider::OpenAVSAudio() {
|
||||
// Set variables
|
||||
type = AUDIO_PROVIDER_AVS;
|
||||
AVSValue script;
|
||||
|
||||
// Prepare avisynth
|
||||
wxMutexLocker lock(AviSynthMutex);
|
||||
|
||||
try {
|
||||
const char * argnames[3] = { 0, "video", "audio" };
|
||||
AVSValue args[3] = { env->SaveString(filename.mb_str(wxConvLocal)), false, true };
|
||||
script = env->Invoke("DirectShowSource", AVSValue(args,3),argnames);
|
||||
|
||||
LoadFromClip(script);
|
||||
|
||||
} catch (AvisynthError &err) {
|
||||
throw _T("AviSynth error: ") + wxString(err.msg,wxConvLocal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// Read from environment
|
||||
void AudioProvider::LoadFromClip(AVSValue _clip) {
|
||||
// Prepare avisynth
|
||||
AVSValue script;
|
||||
|
||||
// Check if it has audio
|
||||
VideoInfo vi = _clip.AsClip()->GetVideoInfo();
|
||||
if (!vi.HasAudio()) throw wxString(_T("No audio found."));
|
||||
|
||||
// Convert to one channel
|
||||
char buffer[1024];
|
||||
strcpy(buffer,Options.AsText(_T("Audio Downmixer")).mb_str(wxConvLocal));
|
||||
script = env->Invoke(buffer, _clip);
|
||||
|
||||
// Convert to 16 bits per sample
|
||||
script = env->Invoke("ConvertAudioTo16bit", script);
|
||||
|
||||
// Convert sample rate
|
||||
int setsample = Options.AsInt(_T("Audio Sample Rate"));
|
||||
if (setsample != 0) {
|
||||
AVSValue args[2] = { script, setsample };
|
||||
script = env->Invoke("ResampleAudio", AVSValue(args,2));
|
||||
}
|
||||
|
||||
// Set clip
|
||||
PClip tempclip = script.AsClip();
|
||||
vi = tempclip->GetVideoInfo();
|
||||
|
||||
// Read properties
|
||||
channels = vi.AudioChannels();
|
||||
num_samples = vi.num_audio_samples;
|
||||
sample_rate = vi.SamplesPerSecond();
|
||||
bytes_per_sample = vi.BytesPerAudioSample();
|
||||
|
||||
// Read whole thing into ram cache
|
||||
if (Options.AsInt(_T("Audio Cache")) == 1) {
|
||||
ConvertToRAMCache(tempclip);
|
||||
clip = NULL;
|
||||
}
|
||||
|
||||
// Disk cache
|
||||
else if (Options.AsInt(_T("Audio Cache")) == 2) {
|
||||
ConvertToDiskCache(tempclip);
|
||||
clip = NULL;
|
||||
}
|
||||
|
||||
// Assign to avisynth
|
||||
else {
|
||||
clip = tempclip;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////
|
||||
// RAM Cache
|
||||
void AudioProvider::ConvertToRAMCache(PClip &tempclip) {
|
||||
#define CacheBits ((24))
|
||||
#define CacheBlockSize ((1 << CacheBits))
|
||||
|
||||
// Allocate cache
|
||||
__int64 ssize = num_samples * bytes_per_sample;
|
||||
blockcount = (ssize + CacheBlockSize - 1) >> CacheBits;
|
||||
|
||||
blockcache = new char*[blockcount];
|
||||
for (int i = 0; i < blockcount; i++)
|
||||
blockcache[i] = NULL;
|
||||
|
||||
try {
|
||||
for (int i = 0; i < blockcount - 1; i++)
|
||||
blockcache[i] = new char[CacheBlockSize];
|
||||
blockcache[blockcount-1] = new char[ssize-(blockcount-1)*CacheBlockSize];
|
||||
} catch (...) {
|
||||
for (int i = 0; i < blockcount; i++)
|
||||
if (blockcache[i])
|
||||
delete blockcache[i];
|
||||
delete blockcache;
|
||||
|
||||
blockcache = NULL;
|
||||
blockcount = 0;
|
||||
|
||||
if (wxMessageBox(_("Not enough ram available. Use disk cache instead?"),_("Audio Information"),wxICON_INFORMATION | wxYES_NO) == wxYES) {
|
||||
ConvertToDiskCache(tempclip);
|
||||
return;
|
||||
} else
|
||||
throw wxString(_T("Couldn't open audio, not enough ram available."));
|
||||
}
|
||||
|
||||
// Start progress
|
||||
volatile bool canceled = false;
|
||||
DialogProgress *progress = new DialogProgress(NULL,_("Load audio"),&canceled,_("Reading into RAM"),0,num_samples);
|
||||
progress->Show();
|
||||
progress->SetProgress(0,blockcount-1);
|
||||
|
||||
// Read cache
|
||||
int readsize = CacheBlockSize / bytes_per_sample;
|
||||
|
||||
for (int i=0;i<blockcount && !canceled; i++) {
|
||||
tempclip->GetAudio((char*)blockcache[i],i*readsize, i == blockcount-1 ? (num_samples - i*readsize) : readsize,env);
|
||||
progress->SetProgress(i,blockcount-1);
|
||||
}
|
||||
|
||||
type = AUDIO_PROVIDER_CACHE;
|
||||
|
||||
// Clean up progress
|
||||
if (!canceled)
|
||||
progress->Destroy();
|
||||
else
|
||||
throw wxString(_T("Audio loading cancelled by user"));
|
||||
}
|
||||
|
||||
|
||||
//////////////
|
||||
// Disk Cache
|
||||
void AudioProvider::ConvertToDiskCache(PClip &tempclip) {
|
||||
// Check free space
|
||||
wxLongLong freespace;
|
||||
if (wxGetDiskSpace(DiskCachePath(), NULL, &freespace))
|
||||
if (num_samples * channels * bytes_per_sample > freespace)
|
||||
throw wxString(_T("Not enough free diskspace in "))+DiskCachePath()+wxString(_T(" to cache the audio"));
|
||||
|
||||
// Open output file
|
||||
std::ofstream file;
|
||||
char filename[512];
|
||||
strcpy(filename,DiskCacheName().mb_str(wxConvLocal));
|
||||
file.open(filename,std::ios::binary | std::ios::out | std::ios::trunc);
|
||||
|
||||
// Start progress
|
||||
volatile bool canceled = false;
|
||||
DialogProgress *progress = new DialogProgress(NULL,_T("Load audio"),&canceled,_T("Reading to Hard Disk cache"),0,num_samples);
|
||||
progress->Show();
|
||||
|
||||
// Write to disk
|
||||
int block = 4096;
|
||||
char *temp = new char[block * channels * bytes_per_sample];
|
||||
for (__int64 i=0;i<num_samples && !canceled; i+=block) {
|
||||
if (block+i > num_samples) block = num_samples - i;
|
||||
tempclip->GetAudio(temp,i,block,env);
|
||||
file.write(temp,block * channels * bytes_per_sample);
|
||||
progress->SetProgress(i,num_samples);
|
||||
}
|
||||
file.close();
|
||||
type = AUDIO_PROVIDER_DISK_CACHE;
|
||||
|
||||
// Finish
|
||||
if (!canceled) {
|
||||
progress->Destroy();
|
||||
file_cache.open(filename,std::ios::binary | std::ios::in);
|
||||
}
|
||||
else
|
||||
throw wxString(_T("Audio loading cancelled by user"));
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Get filename
|
||||
wxString AudioProvider::GetFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
int aaa = 0;
|
||||
|
||||
/////////////
|
||||
// Get audio
|
||||
void AudioProvider::GetAudio(void *buf, __int64 start, __int64 count) {
|
||||
|
||||
// Requested beyond the length of audio
|
||||
if (start+count > num_samples) {
|
||||
__int64 oldcount = count;
|
||||
count = num_samples-start;
|
||||
if (count < 0) count = 0;
|
||||
|
||||
// Fill beyond with zero
|
||||
if (bytes_per_sample == 1) {
|
||||
char *temp = (char *) buf;
|
||||
for (int i=count;i<oldcount;i++) {
|
||||
temp[i] = 0;
|
||||
}
|
||||
}
|
||||
if (bytes_per_sample == 2) {
|
||||
short *temp = (short *) buf;
|
||||
for (int i=count;i<oldcount;i++) {
|
||||
temp[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
char *charbuf = (char *)buf;
|
||||
|
||||
// RAM Cache
|
||||
if (type == AUDIO_PROVIDER_CACHE) {
|
||||
int startblock = (start*bytes_per_sample) >> CacheBits;
|
||||
int endblock = (min(start+count, num_samples)*bytes_per_sample) >> CacheBits;
|
||||
__int64 start_offset = (start*bytes_per_sample) & (CacheBlockSize-1);
|
||||
|
||||
for (int i = startblock; i <= endblock; i++) {
|
||||
int readsize = min((i+1)*CacheBlockSize,(start+count)*bytes_per_sample) - max(i*CacheBlockSize,start*bytes_per_sample);
|
||||
memcpy(charbuf,(char *)blockcache[i]+start_offset,readsize);
|
||||
charbuf+=readsize;
|
||||
start_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Disk cache
|
||||
else if (type == AUDIO_PROVIDER_DISK_CACHE) {
|
||||
wxMutexLocker disklock(diskmutex);
|
||||
file_cache.seekg(start*bytes_per_sample);
|
||||
file_cache.read((char*)buf,count*bytes_per_sample*channels);
|
||||
}
|
||||
|
||||
// Avisynth
|
||||
else {
|
||||
wxMutexLocker disklock(diskmutex);
|
||||
clip->GetAudio(buf,start,count,env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
// Get number of channels
|
||||
int AudioProvider::GetChannels() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
// Get number of samples
|
||||
__int64 AudioProvider::GetNumSamples() {
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// Get sample rate
|
||||
int AudioProvider::GetSampleRate() {
|
||||
return sample_rate;
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// Get waveform
|
||||
void AudioProvider::GetWaveForm(int *min,int *peak,__int64 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 = (void*) 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 = 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 = 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// Get disk cache path
|
||||
wxString AudioProvider::DiskCachePath() {
|
||||
return AegisubApp::folderName;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// Get disk cache filename
|
||||
wxString AudioProvider::DiskCacheName() {
|
||||
return DiskCachePath() + _T("audio.tmp");
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// PortAudio callback
|
||||
int paCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData) {
|
||||
// Get provider
|
||||
AudioProvider *provider = (AudioProvider *) userData;
|
||||
int end = 0;
|
||||
|
||||
// Calculate how much left
|
||||
__int64 lenAvailable = provider->endPos - provider->playPos;
|
||||
unsigned __int64 avail = 0;
|
||||
if (lenAvailable > 0) {
|
||||
avail = lenAvailable;
|
||||
if (avail > framesPerBuffer) {
|
||||
lenAvailable = framesPerBuffer;
|
||||
avail = lenAvailable;
|
||||
}
|
||||
}
|
||||
else {
|
||||
lenAvailable = 0;
|
||||
avail = 0;
|
||||
}
|
||||
|
||||
// Play something
|
||||
if (lenAvailable > 0) {
|
||||
provider->GetAudio(outputBuffer,provider->playPos,lenAvailable);
|
||||
}
|
||||
|
||||
// Pad end with blank
|
||||
if (avail < (unsigned __int64) framesPerBuffer) {
|
||||
provider->softStop = true;
|
||||
}
|
||||
|
||||
// Fill rest with blank
|
||||
short *output = (short*) outputBuffer;
|
||||
for (unsigned int i=avail;i<framesPerBuffer;i++) output[i]=0;
|
||||
|
||||
// Set play position (and real one)
|
||||
provider->playPos += framesPerBuffer;
|
||||
provider->realPlayPos = provider->playPos - (outTime - Pa_StreamTime(provider->stream));
|
||||
|
||||
// Cap to start if lower
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
////////
|
||||
// Play
|
||||
void AudioProvider::Play(__int64 start,__int64 count) {
|
||||
// Stop if it's already playing
|
||||
wxMutexLocker locker(PAMutex);
|
||||
|
||||
// Set values
|
||||
endPos = start + count;
|
||||
realPlayPos = start;
|
||||
playPos = start;
|
||||
startPos = start;
|
||||
startMS = startPos * 1000 / GetSampleRate();
|
||||
|
||||
// Start playing
|
||||
if (!playing) {
|
||||
PaError err = Pa_StartStream(stream);
|
||||
if (err != paNoError) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
playing = true;
|
||||
|
||||
if (!display->UpdateTimer.IsRunning()) display->UpdateTimer.Start(15);
|
||||
}
|
||||
|
||||
|
||||
////////
|
||||
// Stop
|
||||
void AudioProvider::Stop(bool timerToo) {
|
||||
//wxMutexLocker locker(PAMutex);
|
||||
softStop = false;
|
||||
|
||||
// Stop stream
|
||||
playing = false;
|
||||
Pa_StopStream (stream);
|
||||
realPlayPos = 0;
|
||||
|
||||
// Stop timer
|
||||
if (timerToo) {
|
||||
display->UpdateTimer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Open stream
|
||||
void AudioProvider::OpenStream() {
|
||||
// Open stream
|
||||
PaError err = Pa_OpenDefaultStream(&stream,0,GetChannels(),paInt16,GetSampleRate(),256,16,paCallback,this);
|
||||
if (err != paNoError)
|
||||
throw wxString(_T("Failed initializing PortAudio stream with error: ") + wxString(Pa_GetErrorText(err),wxConvLocal));
|
||||
}
|
||||
|
||||
|
||||
///////////////
|
||||
// Close stream
|
||||
void AudioProvider::CloseStream() {
|
||||
try {
|
||||
Stop(false);
|
||||
Pa_CloseStream(stream);
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Ask to stop later
|
||||
void AudioProvider::RequestStop() {
|
||||
wxCommandEvent event(wxEVT_STOP_AUDIO, 1000);
|
||||
event.SetEventObject(this);
|
||||
wxMutexGuiEnter();
|
||||
AddPendingEvent(event);
|
||||
wxMutexGuiLeave();
|
||||
}
|
||||
|
||||
|
||||
/////////
|
||||
// Event
|
||||
DEFINE_EVENT_TYPE(wxEVT_STOP_AUDIO)
|
||||
|
||||
BEGIN_EVENT_TABLE(AudioProvider, wxEvtHandler)
|
||||
EVT_COMMAND (1000, wxEVT_STOP_AUDIO, AudioProvider::OnStopAudio)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
void AudioProvider::OnStopAudio(wxCommandEvent &event) {
|
||||
Stop(false);
|
||||
}
|
143
core/audio_provider.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#ifndef AUDIO_PROVIDER_H
|
||||
#define AUDIO_PROVIDER_H
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <wx/wxprec.h>
|
||||
#include "avisynth_wrap.h"
|
||||
#include <fstream>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
//////////////
|
||||
// Prototypes
|
||||
class AudioDisplay;
|
||||
|
||||
|
||||
//////////////
|
||||
// Types enum
|
||||
enum AudioProviderType {
|
||||
AUDIO_PROVIDER_NONE,
|
||||
AUDIO_PROVIDER_AVS,
|
||||
AUDIO_PROVIDER_VIDEO,
|
||||
AUDIO_PROVIDER_CACHE,
|
||||
AUDIO_PROVIDER_DISK_CACHE
|
||||
};
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Audio provider class
|
||||
class AudioProvider : public wxEvtHandler, public AviSynthWrapper {
|
||||
private:
|
||||
static int pa_refcount;
|
||||
|
||||
wxMutex diskmutex;
|
||||
|
||||
AudioProviderType type;
|
||||
|
||||
char** blockcache;
|
||||
int blockcount;
|
||||
|
||||
void *raw;
|
||||
int raw_len;
|
||||
|
||||
AudioDisplay *display;
|
||||
|
||||
std::ifstream file_cache;
|
||||
|
||||
wxString filename;
|
||||
PClip clip;
|
||||
int channels;
|
||||
__int64 num_samples;
|
||||
int sample_rate;
|
||||
int bytes_per_sample;
|
||||
|
||||
void OpenStream();
|
||||
void CloseStream();
|
||||
void ConvertToRAMCache(PClip &tempclip);
|
||||
void ConvertToDiskCache(PClip &tempclip);
|
||||
void LoadFromClip(AVSValue clip);
|
||||
void OpenAVSAudio();
|
||||
void OnStopAudio(wxCommandEvent &event);
|
||||
static wxString DiskCachePath();
|
||||
static wxString DiskCacheName();
|
||||
void SetFile();
|
||||
void Unload();
|
||||
public:
|
||||
wxMutex PAMutex;
|
||||
volatile bool stopping;
|
||||
bool softStop;
|
||||
bool playing;
|
||||
bool spectrum;
|
||||
|
||||
volatile __int64 playPos;
|
||||
volatile __int64 startPos;
|
||||
volatile __int64 endPos;
|
||||
volatile __int64 realPlayPos;
|
||||
volatile __int64 startMS;
|
||||
void *stream;
|
||||
clock_t span;
|
||||
|
||||
AudioProvider(wxString _filename, AudioDisplay *_display);
|
||||
~AudioProvider();
|
||||
|
||||
wxString GetFilename();
|
||||
|
||||
void GetAudio(void *buf, __int64 start, __int64 count);
|
||||
void GetWaveForm(int *min,int *peak,__int64 start,int w,int h,int samples,float scale);
|
||||
|
||||
void Play(__int64 start,__int64 count);
|
||||
void Stop(bool timerToo=true);
|
||||
void RequestStop();
|
||||
|
||||
int GetChannels();
|
||||
__int64 GetNumSamples();
|
||||
int GetSampleRate();
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
|
||||
/////////
|
||||
// Event
|
||||
DECLARE_EVENT_TYPE(wxEVT_STOP_AUDIO, -1)
|
||||
|
||||
|
||||
#endif
|
1595
core/automation.cpp
Normal file
155
core/automation.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
// Copyright (c) 2005, 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
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
extern "C" {
|
||||
#include <lua.h>
|
||||
}
|
||||
|
||||
#include <wx/wxprec.h>
|
||||
#include <wx/filefn.h>
|
||||
#include <vector>
|
||||
#include "ass_file.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_override.h"
|
||||
#include "ass_style.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
class AutomationError {
|
||||
public:
|
||||
wxString message;
|
||||
AutomationError(wxString msg);
|
||||
};
|
||||
|
||||
|
||||
enum AutomationScriptConfigurationOptionKind {
|
||||
COK_INVALID = 0,
|
||||
COK_LABEL,
|
||||
COK_TEXT,
|
||||
COK_INT,
|
||||
COK_FLOAT,
|
||||
COK_BOOL,
|
||||
COK_COLOUR,
|
||||
COK_STYLE
|
||||
};
|
||||
|
||||
struct AutomationScriptConfigurationOption {
|
||||
wxString name;
|
||||
AutomationScriptConfigurationOptionKind kind;
|
||||
wxString label;
|
||||
wxString hint;
|
||||
union {
|
||||
bool isset;
|
||||
int intval;
|
||||
double floatval;
|
||||
} min, max;
|
||||
struct {
|
||||
wxString stringval;
|
||||
int intval;
|
||||
double floatval;
|
||||
bool boolval;
|
||||
AssColor colourval;
|
||||
} default_val, value;
|
||||
};
|
||||
|
||||
class AutomationScriptConfiguration {
|
||||
public:
|
||||
|
||||
bool present; // is there any configuration option set at all?
|
||||
|
||||
std::vector<AutomationScriptConfigurationOption> options;
|
||||
|
||||
wxString serialize(); // make a string from the option name+value pairs
|
||||
void unserialize(wxString &settings); // set the option values from a serialized string
|
||||
|
||||
void load_from_lua(lua_State *L); // top of the stack must be a table in the format of the global "configuration" table, or nil; fill the options vector from that table. (the current options vector will be cleared. all values will be set to the defaults.)
|
||||
void store_to_lua(lua_State *L); // create a process_lines@config style table from the options name:value pairs. after the call, the top of the stack will be such a table.
|
||||
};
|
||||
|
||||
|
||||
struct AutomationScriptFile {
|
||||
bool utf8bom;
|
||||
char *scriptdata;
|
||||
size_t scriptlen;
|
||||
wxString filename;
|
||||
|
||||
~AutomationScriptFile();
|
||||
static AutomationScriptFile *CreateFromString(wxString &script);
|
||||
static AutomationScriptFile *CreateFromFile(wxString filename);
|
||||
};
|
||||
|
||||
|
||||
class AutomationScript {
|
||||
protected:
|
||||
lua_State *L;
|
||||
jmp_buf panicjmp;
|
||||
|
||||
static int L_panicfunc(lua_State *L);
|
||||
|
||||
public:
|
||||
AutomationScript(AutomationScriptFile *script);
|
||||
virtual ~AutomationScript();
|
||||
|
||||
// Reporting functions. These do nothing in the base class.
|
||||
// They should be overridden in a derived class.
|
||||
virtual void OutputDebugString(wxString str, bool isdebug = false);
|
||||
virtual void ReportProgress(float progress);
|
||||
|
||||
// stuff corresponding to globals in the script
|
||||
float version;
|
||||
wxString kind;
|
||||
wxString name;
|
||||
wxString description;
|
||||
AutomationScriptConfiguration configuration;
|
||||
|
||||
// filename the script was loaded from
|
||||
wxString filename;
|
||||
// include path for scripts
|
||||
wxPathList include_path;
|
||||
|
||||
volatile bool force_cancel;
|
||||
void *progress_target;
|
||||
void *debug_target;
|
||||
typedef void (*progress_reporter_t)(float progress, AutomationScript *script, void *target);
|
||||
typedef void (*debug_reporter_t)(wxString &str, bool isdebug, AutomationScript *script, void *target);
|
||||
progress_reporter_t progress_reporter;
|
||||
debug_reporter_t debug_reporter;
|
||||
|
||||
virtual void process_lines(AssFile *input);
|
||||
};
|
||||
|
293
core/automation_filter.cpp
Normal file
|
@ -0,0 +1,293 @@
|
|||
// Copyright (c) 2005, 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
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
#include "automation_filter.h"
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/filename.h>
|
||||
#include "dialog_progress.h"
|
||||
|
||||
|
||||
AutomationScriptThread::AutomationScriptThread(AutomationScript *a_script, AssFile *a_subs)
|
||||
: wxThread(wxTHREAD_JOINABLE), subs(a_subs), script(a_script)
|
||||
{
|
||||
Create();
|
||||
}
|
||||
|
||||
|
||||
wxThread::ExitCode AutomationScriptThread::Entry()
|
||||
{
|
||||
try {
|
||||
script->process_lines(subs);
|
||||
}
|
||||
catch (AutomationError &e) {
|
||||
script->OutputDebugString(wxString(_T("Script wrapper: Script produced an exception.")), true);
|
||||
script->OutputDebugString(wxString::Format(_T("Message was: %s"), e.message.c_str()), true);
|
||||
script->OutputDebugString(wxString(_T("Script wrapper: Output data are probably unchanged or corrupted.")), true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AutomationFilterConfigDialog::AutomationFilterConfigDialog(wxWindow *parent, AutomationScriptConfiguration &config)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
{
|
||||
wxFlexGridSizer *sizer = new wxFlexGridSizer(2, 5, 5);
|
||||
|
||||
//wxLogMessage(_T("Now going to create %d controls for automation script"), config.options.size());
|
||||
|
||||
for (std::vector<AutomationScriptConfigurationOption>::iterator opt = config.options.begin(); opt != config.options.end(); opt++) {
|
||||
//wxLogMessage(_T("Creating control for kind: %d"), opt->kind);
|
||||
if (opt->kind == COK_INVALID)
|
||||
continue;
|
||||
|
||||
Control control;
|
||||
control.option = &*opt;
|
||||
|
||||
switch (opt->kind) {
|
||||
case COK_LABEL:
|
||||
control.control = new wxStaticText(this, -1, opt->label);
|
||||
break;
|
||||
|
||||
case COK_TEXT:
|
||||
control.control = new wxTextCtrl(this, -1, opt->value.stringval);
|
||||
break;
|
||||
|
||||
case COK_INT:
|
||||
control.control = new wxSpinCtrl(this, -1);
|
||||
if (opt->min.isset && opt->max.isset) {
|
||||
((wxSpinCtrl*)control.control)->SetRange(opt->min.intval, opt->max.intval);
|
||||
} else if (opt->min.isset) {
|
||||
((wxSpinCtrl*)control.control)->SetRange(opt->min.intval, 0x7fff);
|
||||
} else if (opt->max.isset) {
|
||||
((wxSpinCtrl*)control.control)->SetRange(-0x7fff, opt->max.intval);
|
||||
} else {
|
||||
((wxSpinCtrl*)control.control)->SetRange(-0x7fff, 0x7fff);
|
||||
}
|
||||
((wxSpinCtrl*)control.control)->SetValue(opt->value.intval);
|
||||
break;
|
||||
|
||||
case COK_FLOAT:
|
||||
control.control = new wxTextCtrl(this, -1, wxString::Format(_T("%f"), opt->value.floatval));
|
||||
break;
|
||||
|
||||
case COK_BOOL:
|
||||
control.control = new wxCheckBox(this, -1, opt->label);
|
||||
((wxCheckBox*)control.control)->SetValue(opt->value.boolval);
|
||||
break;
|
||||
|
||||
case COK_COLOUR:
|
||||
// *FIXME* what to do here?
|
||||
// just put a stupid edit box for now
|
||||
control.control = new wxTextCtrl(this, -1, opt->value.colourval.GetASSFormatted(false));
|
||||
break;
|
||||
|
||||
case COK_STYLE:
|
||||
control.control = new wxChoice(this, -1, wxDefaultPosition, wxDefaultSize, AssFile::top->GetStyles());
|
||||
((wxChoice*)control.control)->Insert(_T(""), 0);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (opt->kind != COK_LABEL && opt->kind != COK_BOOL) {
|
||||
control.label = new wxStaticText(this, -1, opt->label);
|
||||
sizer->Add(control.label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
|
||||
} else {
|
||||
control.label = 0;
|
||||
sizer->AddSpacer(0);
|
||||
}
|
||||
control.control->SetToolTip(opt->hint);
|
||||
sizer->Add(control.control, 1, wxEXPAND);
|
||||
|
||||
controls.push_back(control);
|
||||
}
|
||||
|
||||
SetSizerAndFit(sizer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AssAutomationFilter::AssAutomationFilter(AutomationScript *a_script)
|
||||
: script(a_script), dialog(0)
|
||||
{
|
||||
Register(wxString::Format(_T("Automation: %s"), script->name.c_str()), 100);
|
||||
if (script->description.IsEmpty()) {
|
||||
Description = wxString::Format(_T("%s\r\n(This Automation script has not provided a description.)"), script->name.c_str());
|
||||
} else {
|
||||
Description = script->description;
|
||||
}
|
||||
automation_filter_list.push_back(this);
|
||||
}
|
||||
|
||||
|
||||
void AssAutomationFilter::Init() {
|
||||
}
|
||||
|
||||
|
||||
AssAutomationFilter::~AssAutomationFilter()
|
||||
{
|
||||
Unregister();
|
||||
automation_filter_list.remove(this);
|
||||
}
|
||||
|
||||
|
||||
static void progress_reporter(float progress, AutomationScript *script, void *dialog)
|
||||
{
|
||||
((DialogProgress*)dialog)->SetProgress((int)(progress*10), 1000);
|
||||
}
|
||||
|
||||
static void debug_reporter(wxString &str, bool isdebug, AutomationScript *script, void *dialog)
|
||||
{
|
||||
((DialogProgress*)dialog)->SetText(str);
|
||||
if (isdebug) {
|
||||
wxLogMessage(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AssAutomationFilter::ProcessSubs(AssFile *subs)
|
||||
{
|
||||
AutomationScriptThread thread(script, subs);
|
||||
|
||||
// prepare a progress dialog
|
||||
DialogProgress *dialog = new DialogProgress(0, script->name, &script->force_cancel, _T(""), 0, 1000);
|
||||
dialog->Show();
|
||||
|
||||
// make the script aware of it
|
||||
script->progress_reporter = progress_reporter;
|
||||
script->progress_target = dialog;
|
||||
script->debug_reporter = debug_reporter;
|
||||
script->debug_target = dialog;
|
||||
|
||||
// run the script
|
||||
thread.Run();
|
||||
thread.Wait();
|
||||
|
||||
// make sure the dialog won't be touched again
|
||||
script->progress_reporter = 0;
|
||||
script->debug_reporter = 0;
|
||||
delete dialog;
|
||||
}
|
||||
|
||||
|
||||
void AssAutomationFilter::LoadSettings(bool IsDefault)
|
||||
{
|
||||
wxString opthname = wxString::Format(_T("Automation Settings %s"), wxFileName(script->filename).GetFullName().c_str());
|
||||
|
||||
// if it's an auto export, just read the serialized settings from the ass file
|
||||
// (does nothing if no settings are serialized)
|
||||
if (IsDefault) {
|
||||
wxString serialized = AssFile::top->GetScriptInfo(opthname);
|
||||
script->configuration.unserialize(serialized);
|
||||
return;
|
||||
}
|
||||
|
||||
// if there's no dialog, we can't do anything
|
||||
if (!dialog) return;
|
||||
|
||||
for (std::vector<AutomationFilterConfigDialog::Control>::iterator ctl = dialog->controls.begin(); ctl != dialog->controls.end(); ctl++) {
|
||||
switch (ctl->option->kind) {
|
||||
case COK_TEXT:
|
||||
ctl->option->value.stringval = ((wxTextCtrl*)ctl->control)->GetValue();
|
||||
break;
|
||||
|
||||
case COK_INT:
|
||||
ctl->option->value.intval = ((wxSpinCtrl*)ctl->control)->GetValue();
|
||||
break;
|
||||
|
||||
case COK_FLOAT:
|
||||
if (!((wxTextCtrl*)ctl->control)->GetValue().ToDouble(&ctl->option->value.floatval)) {
|
||||
wxLogWarning(
|
||||
_T("The value entered for field '%s' (%s) could not be converted to a floating-point number. Default value (%f) substituted for the entered value."),
|
||||
ctl->option->label.c_str(),
|
||||
((wxTextCtrl*)ctl->control)->GetValue().c_str(),
|
||||
ctl->option->default_val.floatval);
|
||||
ctl->option->value.floatval = ctl->option->default_val.floatval;
|
||||
}
|
||||
break;
|
||||
|
||||
case COK_BOOL:
|
||||
ctl->option->value.boolval = ((wxCheckBox*)ctl->control)->GetValue();
|
||||
break;
|
||||
|
||||
case COK_COLOUR:
|
||||
// *FIXME* needs to be updated to use a proper color control
|
||||
ctl->option->value.colourval.ParseASS(((wxTextCtrl*)ctl->control)->GetValue());
|
||||
break;
|
||||
|
||||
case COK_STYLE:
|
||||
ctl->option->value.stringval = ((wxChoice*)ctl->control)->GetStringSelection();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// serialize the new settings and save them to the file
|
||||
AssFile::top->SetScriptInfo(opthname, script->configuration.serialize());
|
||||
}
|
||||
|
||||
|
||||
wxWindow *AssAutomationFilter::GetConfigDialogWindow(wxWindow *parent)
|
||||
{
|
||||
wxString opthname = wxString::Format(_T("Automation Settings %s"), wxFileName(script->filename).GetFullName().c_str());
|
||||
wxString serialized = AssFile::top->GetScriptInfo(opthname);
|
||||
script->configuration.unserialize(serialized);
|
||||
|
||||
if (script->configuration.present)
|
||||
return dialog = new AutomationFilterConfigDialog(parent, script->configuration);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
AutomationScript *AssAutomationFilter::GetScript()
|
||||
{
|
||||
return script;
|
||||
}
|
||||
|
||||
|
||||
const std::list<AssAutomationFilter*>& AssAutomationFilter::GetFilterList()
|
||||
{
|
||||
return AssAutomationFilter::automation_filter_list;
|
||||
}
|
||||
|
||||
|
||||
// static list of loaded automation filters
|
||||
std::list<AssAutomationFilter*> AssAutomationFilter::automation_filter_list;
|
||||
|
85
core/automation_filter.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2005, 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
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "automation.h"
|
||||
#include "ass_export_filter.h"
|
||||
|
||||
|
||||
class AutomationScriptThread : public wxThread {
|
||||
protected:
|
||||
AutomationScript *script;
|
||||
AssFile *subs;
|
||||
public:
|
||||
AutomationScriptThread(AutomationScript *a_script, AssFile *a_subs);
|
||||
wxThread::ExitCode Entry();
|
||||
};
|
||||
|
||||
|
||||
class AutomationFilterConfigDialog : public wxPanel {
|
||||
public:
|
||||
struct Control {
|
||||
wxStaticText *label;
|
||||
wxControl *control;
|
||||
AutomationScriptConfigurationOption *option;
|
||||
Control() : label(0), control(0), option(0) {}
|
||||
};
|
||||
std::vector<Control> controls;
|
||||
|
||||
AutomationFilterConfigDialog(wxWindow *parent, AutomationScriptConfiguration &config);
|
||||
};
|
||||
|
||||
|
||||
class AssAutomationFilter : public AssExportFilter {
|
||||
private:
|
||||
AutomationScript *script;
|
||||
AutomationFilterConfigDialog *dialog;
|
||||
|
||||
static std::list<AssAutomationFilter*> automation_filter_list;
|
||||
void Init();
|
||||
|
||||
public:
|
||||
AssAutomationFilter(AutomationScript *a_script);
|
||||
~AssAutomationFilter();
|
||||
|
||||
void ProcessSubs(AssFile *subs);
|
||||
wxWindow *GetConfigDialogWindow(wxWindow *parent);
|
||||
void LoadSettings(bool IsDefault);
|
||||
|
||||
AutomationScript *GetScript();
|
||||
|
||||
static const std::list<AssAutomationFilter*>& GetFilterList();
|
||||
};
|
376
core/automation_gui.cpp
Normal file
|
@ -0,0 +1,376 @@
|
|||
// Copyright (c) 2005, 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
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
#include <wx/filename.h>
|
||||
#include <wx/filedlg.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/textfile.h>
|
||||
#include <wx/utils.h>
|
||||
#include "automation_gui.h"
|
||||
#include "options.h"
|
||||
|
||||
|
||||
DialogAutomationManager::DialogAutomationManager(wxWindow *parent, SubtitlesGrid *grid)
|
||||
: wxDialog(parent, -1, _("Automation Manager"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE, _T("AutoMan"))
|
||||
{
|
||||
subgrid = grid;
|
||||
|
||||
// create main controls
|
||||
script_list = new wxListView(this, AUTOMAN_SCRIPTLIST, wxDefaultPosition, wxSize(600, 175), wxLC_REPORT|wxLC_SINGLE_SEL);
|
||||
create_button = new wxButton(this, AUTOMAN_CREATE, _("&Create..."));
|
||||
add_button = new wxButton(this, AUTOMAN_ADD, _("&Add..."));
|
||||
remove_button = new wxButton(this, AUTOMAN_REMOVE, _("&Remove"));
|
||||
test_button = new wxButton(this, AUTOMAN_APPLY, _("&Apply now"));
|
||||
edit_button = new wxButton(this, AUTOMAN_EDIT, _("&Edit..."));
|
||||
reload_button = new wxButton(this, AUTOMAN_RELOAD, _("Rel&oad"));
|
||||
close_button = new wxButton(this, wxID_CLOSE, _("C&lose"));
|
||||
|
||||
// add headers to list view
|
||||
script_list->InsertColumn(0, _("Script name"), wxLIST_FORMAT_LEFT, 140);
|
||||
script_list->InsertColumn(1, _("File"), wxLIST_FORMAT_LEFT, 100);
|
||||
//script_list->InsertColumn(2, _("Flags"), wxLIST_FORMAT_LEFT, 45);
|
||||
script_list->InsertColumn(2, _("Description"), wxLIST_FORMAT_LEFT, 350);
|
||||
|
||||
// button layout
|
||||
wxSizer *button_box = new wxBoxSizer(wxHORIZONTAL);
|
||||
button_box->AddStretchSpacer(2);
|
||||
button_box->Add(create_button, 0);
|
||||
button_box->Add(add_button, 0);
|
||||
button_box->Add(remove_button, 0);
|
||||
button_box->AddSpacer(10);
|
||||
button_box->Add(test_button, 0);
|
||||
button_box->AddSpacer(10);
|
||||
button_box->Add(edit_button, 0);
|
||||
button_box->Add(reload_button, 0);
|
||||
button_box->AddSpacer(10);
|
||||
button_box->Add(close_button, 0);
|
||||
button_box->AddStretchSpacer(2);
|
||||
|
||||
// main layout
|
||||
wxSizer *main_box = new wxBoxSizer(wxVERTICAL);
|
||||
main_box->Add(script_list, 1, wxEXPAND|wxALL, 5);
|
||||
main_box->Add(button_box, 0, wxEXPAND|wxALL&~wxTOP, 5);
|
||||
main_box->SetSizeHints(this);
|
||||
SetSizer(main_box);
|
||||
Center();
|
||||
|
||||
// fill the list view
|
||||
std::list<AssAutomationFilter*>::const_iterator f = AssAutomationFilter::GetFilterList().begin();
|
||||
for (;f != AssAutomationFilter::GetFilterList().end(); ++f) {
|
||||
AddScriptToList(*f);
|
||||
}
|
||||
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
|
||||
DialogAutomationManager::~DialogAutomationManager()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(DialogAutomationManager,wxDialog)
|
||||
EVT_BUTTON(AUTOMAN_CREATE,DialogAutomationManager::OnCreate)
|
||||
EVT_BUTTON(AUTOMAN_ADD,DialogAutomationManager::OnAdd)
|
||||
EVT_BUTTON(AUTOMAN_REMOVE,DialogAutomationManager::OnRemove)
|
||||
EVT_BUTTON(AUTOMAN_APPLY,DialogAutomationManager::OnApply)
|
||||
EVT_BUTTON(AUTOMAN_EDIT,DialogAutomationManager::OnEdit)
|
||||
EVT_BUTTON(AUTOMAN_RELOAD,DialogAutomationManager::OnReload)
|
||||
EVT_BUTTON(wxID_CLOSE,DialogAutomationManager::OnClose)
|
||||
EVT_LIST_ITEM_SELECTED(AUTOMAN_SCRIPTLIST,DialogAutomationManager::OnSelectionChange)
|
||||
EVT_LIST_ITEM_DESELECTED(AUTOMAN_SCRIPTLIST,DialogAutomationManager::OnSelectionChange)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
void DialogAutomationManager::OnCreate(wxCommandEvent &event)
|
||||
{
|
||||
wxString sfnames = wxFileSelector(_("Create Automation script"), _T(""), _T("*.lua"), _T("lua"), _T("Automation Lua scripts (*.lua)|*.lua|All files (*.*)|*.*"), wxSAVE|wxOVERWRITE_PROMPT, this);
|
||||
if (sfnames.empty()) return;
|
||||
wxFileName sfname(sfnames);
|
||||
|
||||
if (sfname.FileExists()) {
|
||||
if (!wxRemoveFile(sfnames)) {
|
||||
wxMessageBox(_T("The old file by this name could not be deleted."), _T("Error creating Automation script"), wxOK|wxICON_ERROR, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wxTextFile file;
|
||||
file.Create(sfnames);
|
||||
file.AddLine(_T("-- Aegisub Automation script"));
|
||||
file.AddLine(_T(""));
|
||||
file.AddLine(_T("-- You should change these two lines"));
|
||||
file.AddLine(wxString::Format(_T("name = \"%s\""), sfname.GetName().c_str()));
|
||||
file.AddLine(_T("description = \"New Automation script\""));
|
||||
file.AddLine(_T(""));
|
||||
file.AddLine(_T("-- Enter the configuration settings here, if needed. Refer to the manual for details"));
|
||||
file.AddLine(_T("configuration = {}"));
|
||||
file.AddLine(_T(""));
|
||||
file.AddLine(_T("-- You should NOT change this line!"));
|
||||
file.AddLine(_T("version, kind = 3, 'basic_ass'"));
|
||||
file.AddLine(_T(""));
|
||||
file.AddLine(_T("-- You should write your script in this function (don't change its name!)"));
|
||||
file.AddLine(_T("function process_lines(meta, styles, lines, config)"));
|
||||
file.AddLine(_T("\t-- For now, just return the subtitles as-is, no changes"));
|
||||
file.AddLine(_T("\treturn lines"));
|
||||
file.AddLine(_T("end"));
|
||||
file.AddLine(_T(""));
|
||||
file.Write(wxTextFileType_None, wxConvUTF8);
|
||||
file.Close();
|
||||
|
||||
AutomationScriptFile *sfile = AutomationScriptFile::CreateFromFile(sfnames);
|
||||
AutomationScript *script = new AutomationScript(sfile);
|
||||
AssAutomationFilter *filter = new AssAutomationFilter(script);
|
||||
AddScriptToList(filter);
|
||||
delete sfile;
|
||||
|
||||
EditScript(script);
|
||||
}
|
||||
|
||||
|
||||
void DialogAutomationManager::OnAdd(wxCommandEvent &event)
|
||||
{
|
||||
wxString sfnames = wxFileSelector(_("Load Automation script"), _T(""), _T("*.lua"), _T("lua"), _T("Automation Lua scripts (*.lua)|*.lua|All files (*.*)|*.*"), wxOPEN|wxFILE_MUST_EXIST, this);
|
||||
if (sfnames.empty()) return;
|
||||
|
||||
try {
|
||||
AutomationScriptFile *sfile = AutomationScriptFile::CreateFromFile(sfnames);
|
||||
AutomationScript *script = new AutomationScript(sfile);
|
||||
AssAutomationFilter *filter = new AssAutomationFilter(script);
|
||||
wxString script_settings = subgrid->ass->GetScriptInfo(wxString::Format(_T("Automation Settings %s"), wxFileName(sfnames).GetFullName().c_str()));
|
||||
script->configuration.unserialize(script_settings);
|
||||
AddScriptToList(filter);
|
||||
delete sfile;
|
||||
}
|
||||
catch (AutomationError &err) {
|
||||
wxMessageBox(wxString::Format(_T("Error loading Automation script '%s':\r\n\r\n%s"), sfnames.c_str(), err.message.c_str()), _T("Error loading Automation script"), wxOK | wxICON_ERROR, this);
|
||||
}
|
||||
catch (wchar_t *err) {
|
||||
wxMessageBox(wxString::Format(_T("Error loading Automation script %s:\r\n\r\n%s"), sfnames.c_str(), err), _T("Error loading Automation script"), wxOK|wxICON_ERROR, this);
|
||||
}
|
||||
catch (...) {
|
||||
wxMessageBox(_T("Unknown error loading Automation script."), _T("Error loading Automation script"), wxOK | wxICON_ERROR, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DialogAutomationManager::OnRemove(wxCommandEvent &event)
|
||||
{
|
||||
// assume only one item can be selected at a time...
|
||||
// removing multiple scripts might be disasterous (for the user) anyway
|
||||
// TODO: ask for confirmation if script supports configuration
|
||||
int selid = script_list->GetFirstSelected();
|
||||
AssAutomationFilter *filter = (AssAutomationFilter*)script_list->GetItemData(selid);
|
||||
script_list->DeleteItem(selid);
|
||||
AutomationScript *script = filter->GetScript();
|
||||
delete filter;
|
||||
delete script;
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
|
||||
void DialogAutomationManager::OnApply(wxCommandEvent &event)
|
||||
{
|
||||
int selid = script_list->GetFirstSelected();
|
||||
AssAutomationFilter *filter = (AssAutomationFilter*)script_list->GetItemData(selid);
|
||||
// Attempt to make a config window, if needed
|
||||
{
|
||||
wxDialog *dlg = new wxDialog(this, -1, filter->GetScript()->name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE, _T("AutomationScriptConfigDlg"));
|
||||
try {
|
||||
wxWindow *config_frame = filter->GetConfigDialogWindow(dlg);
|
||||
if (!config_frame) {
|
||||
delete dlg;
|
||||
goto skip_config;
|
||||
}
|
||||
wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(config_frame, 0, wxALL, 7);
|
||||
wxButton *ok = new wxButton(dlg, wxID_OK);
|
||||
wxButton *cancel = new wxButton(dlg, wxID_CANCEL);
|
||||
wxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
button_sizer->Add(ok, 0, wxALL, 7);
|
||||
button_sizer->Add(cancel, 0, wxALL&~wxLEFT, 7);
|
||||
main_sizer->Add(button_sizer, wxALIGN_CENTER);
|
||||
dlg->SetSizer(main_sizer);
|
||||
main_sizer->SetSizeHints(dlg);
|
||||
dlg->Layout();
|
||||
switch (dlg->ShowModal()) {
|
||||
case wxID_OK:
|
||||
filter->LoadSettings(false);
|
||||
delete dlg;
|
||||
break;
|
||||
case wxID_CANCEL:
|
||||
delete dlg;
|
||||
return;
|
||||
default:
|
||||
wxLogWarning(_T("Config dialog returned an unexpected value. This shouldn't happen. Please report to a dev."));
|
||||
delete dlg;
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
delete dlg;
|
||||
wxLogError(_T("Error while working on Automation script config dialog. This shouldn't happen. Please report to a dev."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
skip_config:
|
||||
// Now apply script
|
||||
filter->ProcessSubs(subgrid->ass);
|
||||
subgrid->LoadFromAss();
|
||||
script_list->Select(selid, false);
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
|
||||
void DialogAutomationManager::OnEdit(wxCommandEvent &event)
|
||||
{
|
||||
int selid = script_list->GetFirstSelected();
|
||||
AssAutomationFilter *filter = (AssAutomationFilter*)script_list->GetItemData(selid);
|
||||
AutomationScript *script = filter->GetScript();
|
||||
EditScript(script);
|
||||
}
|
||||
|
||||
|
||||
void DialogAutomationManager::OnReload(wxCommandEvent &event)
|
||||
{
|
||||
int selid = script_list->GetFirstSelected();
|
||||
AssAutomationFilter *filter = (AssAutomationFilter*)script_list->GetItemData(selid);
|
||||
AutomationScript *script = filter->GetScript();
|
||||
wxFileName sfname(script->filename);
|
||||
if (sfname.FileExists()) {
|
||||
AutomationScript *newscript;
|
||||
try {
|
||||
AutomationScriptFile *sfile = AutomationScriptFile::CreateFromFile(sfname.GetFullPath());
|
||||
newscript = new AutomationScript(sfile);
|
||||
delete sfile;
|
||||
}
|
||||
catch (AutomationError &err) {
|
||||
wxMessageBox(wxString::Format(_T("Error reloading Automation script '%s'.\r\nThe old version has been retained.\r\n\r\n%s"), sfname.GetFullPath().c_str(), err.message.c_str()), _T("Error reloading Automation script"), wxOK | wxICON_ERROR, this);
|
||||
return;
|
||||
}
|
||||
newscript->configuration = script->configuration;
|
||||
delete filter;
|
||||
delete script;
|
||||
AssAutomationFilter *newfilter = new AssAutomationFilter(newscript);
|
||||
script_list->DeleteItem(selid);
|
||||
AddScriptToList(newfilter);
|
||||
script_list->Select(0);
|
||||
} else {
|
||||
wxMessageBox(_T("The script file could not be found on disk. If you want to remove the script, please use the Remove button."), _T("Error reloading Automation script"), wxOK|wxICON_EXCLAMATION, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DialogAutomationManager::OnClose(wxCommandEvent &event)
|
||||
{
|
||||
EndModal(0);
|
||||
}
|
||||
|
||||
|
||||
void DialogAutomationManager::OnSelectionChange(wxListEvent &event)
|
||||
{
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
|
||||
void DialogAutomationManager::UpdateDisplay()
|
||||
{
|
||||
bool script_selected = script_list->GetSelectedItemCount() > 0;
|
||||
// enable/disable buttons
|
||||
create_button->Enable(true);
|
||||
add_button->Enable(true);
|
||||
remove_button->Enable(script_selected);
|
||||
test_button->Enable(script_selected);
|
||||
edit_button->Enable(script_selected);
|
||||
reload_button->Enable(script_selected);
|
||||
close_button->Enable(true);
|
||||
}
|
||||
|
||||
|
||||
void DialogAutomationManager::AddScriptToList(AssAutomationFilter *filter)
|
||||
{
|
||||
wxFileName fn(filter->GetScript()->filename);
|
||||
wxListItem item;
|
||||
item.SetText(filter->GetScript()->name);
|
||||
item.SetData(filter);
|
||||
int i = script_list->InsertItem(item);
|
||||
script_list->SetItem(i, 1, fn.GetFullName());
|
||||
//script_list->SetItem(i, 2, _T("")); // Flags - unused
|
||||
script_list->SetItem(i, 2, filter->GetScript()->description);
|
||||
}
|
||||
|
||||
void DialogAutomationManager::EditScript(AutomationScript *script)
|
||||
{
|
||||
if (!script) {
|
||||
wxMessageBox(_T("DialogAutomationManager::EditScript() called without a valid script object. Sloppy programming? You can probably blame jfs."), _T("Blame Canada!"), wxOK|wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
wxFileName sfname(script->filename);
|
||||
if (!sfname.FileExists()) {
|
||||
wxMessageBox(_T("The script file \"%s\" does not exist, and thus cannot be edited."), _T("Automation warning"), wxOK|wxICON_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
wxString editor;
|
||||
if (!Options.IsDefined(_T("automation script editor")) || wxGetKeyState(WXK_SHIFT)) {
|
||||
wxMessageBox(_T("You have not selected a script editor yet. Please select your script editor in the next window. It's recommended to use an editor with syntax highlighting for Lua scripts."), _T("Aegisub"), wxOK|wxICON_INFORMATION);
|
||||
#ifdef __WIN32__
|
||||
editor = wxFileSelector(_T("Select script editor"), _T(""), _T("C:\\Windows\\Notepad.exe"), _T("exe"), _T("Execatuables (*.exe)|*.exe|All files (*.*)|*.*"), wxOPEN|wxFILE_MUST_EXIST);
|
||||
#else
|
||||
char *env_editor = getenv("EDITOR");
|
||||
wxString editor(env_editor ? env_editor : "/usr/bin/gvim", wxConvLocal);
|
||||
editor = wxFileSelector(_T("Select script editor"), _T(""), editor, _T(""), _T("All files (*)|*"), wxOPEN|wxFILE_MUST_EXIST);
|
||||
#endif
|
||||
if (editor.empty()) return;
|
||||
Options.SetText(_T("automation script editor"), editor);
|
||||
Options.Save();
|
||||
} else {
|
||||
editor = Options.AsText(_T("automation script editor"));
|
||||
}
|
||||
|
||||
wxWCharBuffer editorbuf = editor.c_str(), sfnamebuf = sfname.GetFullPath().c_str();
|
||||
wchar_t **cmdline = new wchar_t*[3];
|
||||
cmdline[0] = editorbuf.data();
|
||||
cmdline[1] = sfnamebuf.data();
|
||||
cmdline[2] = 0;
|
||||
long res = wxExecute(cmdline);
|
||||
delete cmdline;
|
||||
|
||||
if (!res) {
|
||||
wxMessageBox(_T("Some error occurred trying to launch the external editor. Sorry!"), _T("Automation Error"), wxOK|wxICON_ERROR);
|
||||
}
|
||||
}
|
||||
|
87
core/automation_gui.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) 2005, 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
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wx/window.h>
|
||||
#include <wx/listctrl.h>
|
||||
#include "subs_grid.h"
|
||||
#include "automation_filter.h"
|
||||
|
||||
|
||||
class DialogAutomationManager : public wxDialog {
|
||||
private:
|
||||
wxListView *script_list;
|
||||
wxButton *create_button;
|
||||
wxButton *add_button;
|
||||
wxButton *remove_button;
|
||||
wxButton *test_button;
|
||||
wxButton *edit_button;
|
||||
wxButton *reload_button;
|
||||
wxButton *close_button;
|
||||
|
||||
SubtitlesGrid *subgrid;
|
||||
|
||||
void OnCreate(wxCommandEvent &event);
|
||||
void OnAdd(wxCommandEvent &event);
|
||||
void OnRemove(wxCommandEvent &event);
|
||||
void OnApply(wxCommandEvent &event);
|
||||
void OnEdit(wxCommandEvent &event);
|
||||
void OnReload(wxCommandEvent &event);
|
||||
void OnClose(wxCommandEvent &event);
|
||||
void OnSelectionChange(wxListEvent &event);
|
||||
|
||||
void UpdateDisplay();
|
||||
void AddScriptToList(AssAutomationFilter *filter);
|
||||
|
||||
public:
|
||||
DialogAutomationManager(wxWindow *parent, SubtitlesGrid *grid);
|
||||
~DialogAutomationManager();
|
||||
|
||||
static void EditScript(AutomationScript *script);
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
AUTOMAN_SCRIPTLIST = 3200,
|
||||
AUTOMAN_CREATE,
|
||||
AUTOMAN_ADD,
|
||||
AUTOMAN_REMOVE,
|
||||
AUTOMAN_APPLY,
|
||||
AUTOMAN_EDIT,
|
||||
AUTOMAN_RELOAD,
|
||||
AUTOMAN_CLOSE
|
||||
};
|
749
core/avisynth.h
Normal file
|
@ -0,0 +1,749 @@
|
|||
// Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al.
|
||||
// http://www.avisynth.org
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
|
||||
// http://www.gnu.org/copyleft/gpl.html .
|
||||
//
|
||||
// Linking Avisynth statically or dynamically with other modules is making a
|
||||
// combined work based on Avisynth. Thus, the terms and conditions of the GNU
|
||||
// General Public License cover the whole combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of Avisynth give you
|
||||
// permission to link Avisynth with independent modules that communicate with
|
||||
// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
|
||||
// terms of these independent modules, and to copy and distribute the
|
||||
// resulting combined work under terms of your choice, provided that
|
||||
// every copy of the combined work is accompanied by a complete copy of
|
||||
// the source code of Avisynth (the version of Avisynth used to produce the
|
||||
// combined work), being distributed under the terms of the GNU General
|
||||
// Public License plus this exception. An independent module is a module
|
||||
// which is not derived from or based on Avisynth, such as 3rd-party filters,
|
||||
// import and export plugins, or graphical user interfaces.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __AVISYNTH_H__
|
||||
#define __AVISYNTH_H__
|
||||
|
||||
enum { AVISYNTH_INTERFACE_VERSION = 2 };
|
||||
|
||||
|
||||
/* Define all types necessary for interfacing with avisynth.dll
|
||||
Moved from internal.h */
|
||||
|
||||
// Win32 API macros, notably the types BYTE, DWORD, ULONG, etc.
|
||||
#include <windef.h>
|
||||
|
||||
// COM interface macros
|
||||
#include <objbase.h>
|
||||
|
||||
|
||||
// Raster types used by VirtualDub & Avisynth
|
||||
#define in64 (__int64)(unsigned short)
|
||||
typedef unsigned long Pixel; // this will break on 64-bit machines!
|
||||
typedef unsigned long Pixel32;
|
||||
typedef unsigned char Pixel8;
|
||||
typedef long PixCoord;
|
||||
typedef long PixDim;
|
||||
typedef long PixOffset;
|
||||
|
||||
|
||||
/* Compiler-specific crap */
|
||||
|
||||
// Tell MSVC to stop precompiling here
|
||||
#ifdef _MSC_VER
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
// Set up debugging macros for MS compilers; for others, step down to the
|
||||
// standard <assert.h> interface
|
||||
#ifdef _MSC_VER
|
||||
#include <crtdbg.h>
|
||||
#else
|
||||
#define _RPT0(a,b) ((void)0)
|
||||
#define _RPT1(a,b,c) ((void)0)
|
||||
#define _RPT2(a,b,c,d) ((void)0)
|
||||
#define _RPT3(a,b,c,d,e) ((void)0)
|
||||
#define _RPT4(a,b,c,d,e,f) ((void)0)
|
||||
|
||||
#define _ASSERTE(x) assert(x)
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// I had problems with Premiere wanting 1-byte alignment for its structures,
|
||||
// so I now set the Avisynth struct alignment explicitly here.
|
||||
#pragma pack(push,8)
|
||||
|
||||
#define FRAME_ALIGN 16
|
||||
// Default frame alignment is 16 bytes, to help P4, when using SSE2
|
||||
|
||||
// The VideoInfo struct holds global information about a clip (i.e.
|
||||
// information that does not depend on the frame number). The GetVideoInfo
|
||||
// method in IClip returns this struct.
|
||||
|
||||
// Audio Sample information
|
||||
typedef float SFLOAT;
|
||||
|
||||
enum {SAMPLE_INT8 = 1<<0,
|
||||
SAMPLE_INT16 = 1<<1,
|
||||
SAMPLE_INT24 = 1<<2, // Int24 is a very stupid thing to code, but it's supported by some hardware.
|
||||
SAMPLE_INT32 = 1<<3,
|
||||
SAMPLE_FLOAT = 1<<4};
|
||||
|
||||
enum {
|
||||
PLANAR_Y=1<<0,
|
||||
PLANAR_U=1<<1,
|
||||
PLANAR_V=1<<2,
|
||||
PLANAR_ALIGNED=1<<3,
|
||||
PLANAR_Y_ALIGNED=PLANAR_Y|PLANAR_ALIGNED,
|
||||
PLANAR_U_ALIGNED=PLANAR_U|PLANAR_ALIGNED,
|
||||
PLANAR_V_ALIGNED=PLANAR_V|PLANAR_ALIGNED,
|
||||
};
|
||||
|
||||
struct VideoInfo {
|
||||
int width, height; // width=0 means no video
|
||||
unsigned fps_numerator, fps_denominator;
|
||||
int num_frames;
|
||||
// This is more extensible than previous versions. More properties can be added seeminglesly.
|
||||
|
||||
// Colorspace properties.
|
||||
enum {
|
||||
CS_BGR = 1<<28,
|
||||
CS_YUV = 1<<29,
|
||||
CS_INTERLEAVED = 1<<30,
|
||||
CS_PLANAR = 1<<31
|
||||
};
|
||||
|
||||
// Specific colorformats
|
||||
enum { CS_UNKNOWN = 0,
|
||||
CS_BGR24 = 1<<0 | CS_BGR | CS_INTERLEAVED,
|
||||
CS_BGR32 = 1<<1 | CS_BGR | CS_INTERLEAVED,
|
||||
CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED,
|
||||
CS_YV12 = 1<<3 | CS_YUV | CS_PLANAR, // y-v-u, planar
|
||||
CS_I420 = 1<<4 | CS_YUV | CS_PLANAR, // y-u-v, planar
|
||||
CS_IYUV = 1<<4 | CS_YUV | CS_PLANAR // same as above
|
||||
};
|
||||
int pixel_type; // changed to int as of 2.5
|
||||
|
||||
|
||||
int audio_samples_per_second; // 0 means no audio
|
||||
int sample_type; // as of 2.5
|
||||
__int64 num_audio_samples; // changed as of 2.5
|
||||
int nchannels; // as of 2.5
|
||||
|
||||
// Imagetype properties
|
||||
|
||||
int image_type;
|
||||
|
||||
enum {
|
||||
IT_BFF = 1<<0,
|
||||
IT_TFF = 1<<1,
|
||||
IT_FIELDBASED = 1<<2
|
||||
};
|
||||
|
||||
// useful functions of the above
|
||||
bool HasVideo() const { return (width!=0); }
|
||||
bool HasAudio() const { return (audio_samples_per_second!=0); }
|
||||
bool IsRGB() const { return !!(pixel_type&CS_BGR); }
|
||||
bool IsRGB24() const { return (pixel_type&CS_BGR24)==CS_BGR24; } // Clear out additional properties
|
||||
bool IsRGB32() const { return (pixel_type & CS_BGR32) == CS_BGR32 ; }
|
||||
bool IsYUV() const { return !!(pixel_type&CS_YUV ); }
|
||||
bool IsYUY2() const { return (pixel_type & CS_YUY2) == CS_YUY2; }
|
||||
bool IsYV12() const { return ((pixel_type & CS_YV12) == CS_YV12)||((pixel_type & CS_I420) == CS_I420); }
|
||||
bool IsColorSpace(int c_space) const { return ((pixel_type & c_space) == c_space); }
|
||||
bool Is(int property) const { return ((pixel_type & property)==property ); }
|
||||
bool IsPlanar() const { return !!(pixel_type & CS_PLANAR); }
|
||||
bool IsFieldBased() const { return !!(image_type & IT_FIELDBASED); }
|
||||
bool IsParityKnown() const { return ((image_type & IT_FIELDBASED)&&(image_type & (IT_BFF|IT_TFF))); }
|
||||
bool IsBFF() const { return !!(image_type & IT_BFF); }
|
||||
bool IsTFF() const { return !!(image_type & IT_TFF); }
|
||||
|
||||
bool IsVPlaneFirst() const {return ((pixel_type & CS_YV12) == CS_YV12); } // Don't use this
|
||||
int BytesFromPixels(int pixels) const { return pixels * (BitsPerPixel()>>3); } // Will not work on planar images, but will return only luma planes
|
||||
int RowSize() const { return BytesFromPixels(width); } // Also only returns first plane on planar images
|
||||
int BMPSize() const { if (IsPlanar()) {int p = height * ((RowSize()+3) & ~3); p+=p>>1; return p; } return height * ((RowSize()+3) & ~3); }
|
||||
__int64 AudioSamplesFromFrames(__int64 frames) const { return (fps_numerator && HasVideo()) ? ((__int64)(frames) * audio_samples_per_second * fps_denominator / fps_numerator) : 0; }
|
||||
int FramesFromAudioSamples(__int64 samples) const { return (fps_denominator && HasAudio()) ? (int)((samples * (__int64)fps_numerator)/((__int64)fps_denominator * (__int64)audio_samples_per_second)) : 0; }
|
||||
__int64 AudioSamplesFromBytes(__int64 bytes) const { return HasAudio() ? bytes / BytesPerAudioSample() : 0; }
|
||||
__int64 BytesFromAudioSamples(__int64 samples) const { return samples * BytesPerAudioSample(); }
|
||||
int AudioChannels() const { return nchannels; }
|
||||
int SampleType() const{ return sample_type;}
|
||||
bool IsSampleType(int testtype) const{ return !!(sample_type&testtype);}
|
||||
int SamplesPerSecond() const { return audio_samples_per_second; }
|
||||
int BytesPerAudioSample() const { return nchannels*BytesPerChannelSample();}
|
||||
void SetFieldBased(bool isfieldbased) { if (isfieldbased) image_type|=IT_FIELDBASED; else image_type&=~IT_FIELDBASED; }
|
||||
void Set(int property) { image_type|=property; }
|
||||
void Clear(int property) { image_type&=~property; }
|
||||
|
||||
int BitsPerPixel() const {
|
||||
switch (pixel_type) {
|
||||
case CS_BGR24:
|
||||
return 24;
|
||||
case CS_BGR32:
|
||||
return 32;
|
||||
case CS_YUY2:
|
||||
return 16;
|
||||
case CS_YV12:
|
||||
case CS_I420:
|
||||
return 12;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int BytesPerChannelSample() const {
|
||||
switch (sample_type) {
|
||||
case SAMPLE_INT8:
|
||||
return sizeof(signed char);
|
||||
case SAMPLE_INT16:
|
||||
return sizeof(signed short);
|
||||
case SAMPLE_INT24:
|
||||
return 3;
|
||||
case SAMPLE_INT32:
|
||||
return sizeof(signed int);
|
||||
case SAMPLE_FLOAT:
|
||||
return sizeof(SFLOAT);
|
||||
default:
|
||||
_ASSERTE("Sample type not recognized!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// useful mutator
|
||||
void SetFPS(unsigned numerator, unsigned denominator) {
|
||||
if ((numerator == 0) || (denominator == 0)) {
|
||||
fps_numerator = 0;
|
||||
fps_denominator = 1;
|
||||
}
|
||||
else {
|
||||
unsigned x=numerator, y=denominator;
|
||||
while (y) { // find gcd
|
||||
unsigned t = x%y; x = y; y = t;
|
||||
}
|
||||
fps_numerator = numerator/x;
|
||||
fps_denominator = denominator/x;
|
||||
}
|
||||
}
|
||||
|
||||
// Range protected multiply-divide of FPS
|
||||
void MulDivFPS(unsigned multiplier, unsigned divisor) {
|
||||
unsigned __int64 numerator = UInt32x32To64(fps_numerator, multiplier);
|
||||
unsigned __int64 denominator = UInt32x32To64(fps_denominator, divisor);
|
||||
|
||||
unsigned __int64 x=numerator, y=denominator;
|
||||
while (y) { // find gcd
|
||||
unsigned __int64 t = x%y; x = y; y = t;
|
||||
}
|
||||
numerator /= x; // normalize
|
||||
denominator /= x;
|
||||
|
||||
unsigned __int64 temp = numerator | denominator; // Just looking top bit
|
||||
unsigned u = 0;
|
||||
while (temp & 0xffffffff80000000) { // or perhaps > 16777216*2
|
||||
temp = Int64ShrlMod32(temp, 1);
|
||||
u++;
|
||||
}
|
||||
if (u) { // Scale to fit
|
||||
const unsigned round = 1 << (u-1);
|
||||
SetFPS( (unsigned)Int64ShrlMod32(numerator + round, u),
|
||||
(unsigned)Int64ShrlMod32(denominator + round, u) );
|
||||
}
|
||||
else {
|
||||
fps_numerator = (unsigned)numerator;
|
||||
fps_denominator = (unsigned)denominator;
|
||||
}
|
||||
}
|
||||
|
||||
// Test for same colorspace
|
||||
bool IsSameColorspace(const VideoInfo& vi) const {
|
||||
if (vi.pixel_type == pixel_type) return TRUE;
|
||||
if (IsYV12() && vi.IsYV12()) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// VideoFrameBuffer holds information about a memory block which is used
|
||||
// for video data. For efficiency, instances of this class are not deleted
|
||||
// when the refcount reaches zero; instead they're stored in a linked list
|
||||
// to be reused. The instances are deleted when the corresponding AVS
|
||||
// file is closed.
|
||||
|
||||
class VideoFrameBuffer {
|
||||
BYTE* const data;
|
||||
const int data_size;
|
||||
// sequence_number is incremented every time the buffer is changed, so
|
||||
// that stale views can tell they're no longer valid.
|
||||
long sequence_number;
|
||||
|
||||
friend class VideoFrame;
|
||||
friend class Cache;
|
||||
friend class ScriptEnvironment;
|
||||
long refcount;
|
||||
|
||||
public:
|
||||
VideoFrameBuffer(int size);
|
||||
VideoFrameBuffer();
|
||||
~VideoFrameBuffer();
|
||||
|
||||
const BYTE* GetReadPtr() const { return data; }
|
||||
BYTE* GetWritePtr() { ++sequence_number; return data; }
|
||||
int GetDataSize() { return data_size; }
|
||||
int GetSequenceNumber() { return sequence_number; }
|
||||
int GetRefcount() { return refcount; }
|
||||
};
|
||||
|
||||
|
||||
class IClip;
|
||||
class PClip;
|
||||
class PVideoFrame;
|
||||
class IScriptEnvironment;
|
||||
class AVSValue;
|
||||
|
||||
|
||||
// VideoFrame holds a "window" into a VideoFrameBuffer. Operator new
|
||||
// is overloaded to recycle class instances.
|
||||
|
||||
class VideoFrame {
|
||||
int refcount;
|
||||
VideoFrameBuffer* const vfb;
|
||||
const int offset, pitch, row_size, height, offsetU, offsetV, pitchUV; // U&V offsets are from top of picture.
|
||||
|
||||
friend class PVideoFrame;
|
||||
void AddRef() { InterlockedIncrement((long *)&refcount); }
|
||||
void Release() { if (refcount==1) InterlockedDecrement(&vfb->refcount); InterlockedDecrement((long *)&refcount); }
|
||||
|
||||
friend class ScriptEnvironment;
|
||||
friend class Cache;
|
||||
|
||||
VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height);
|
||||
VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV);
|
||||
|
||||
void* operator new(unsigned size);
|
||||
// TESTME: OFFSET U/V may be switched to what could be expected from AVI standard!
|
||||
public:
|
||||
int GetPitch() const { return pitch; }
|
||||
int GetPitch(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: return pitchUV;} return pitch; }
|
||||
int GetRowSize() const { return row_size; }
|
||||
int GetRowSize(int plane) const {
|
||||
switch (plane) {
|
||||
case PLANAR_U: case PLANAR_V: if (pitchUV) return row_size>>1; else return 0;
|
||||
case PLANAR_U_ALIGNED: case PLANAR_V_ALIGNED:
|
||||
if (pitchUV) {
|
||||
int r = ((row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)) )>>1; // Aligned rowsize
|
||||
if (r<=pitchUV)
|
||||
return r;
|
||||
return row_size>>1;
|
||||
} else return 0;
|
||||
case PLANAR_Y_ALIGNED:
|
||||
int r = (row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)); // Aligned rowsize
|
||||
if (r<=pitch)
|
||||
return r;
|
||||
return row_size;
|
||||
}
|
||||
return row_size; }
|
||||
int GetHeight() const { return height; }
|
||||
int GetHeight(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: if (pitchUV) return height>>1; return 0;} return height; }
|
||||
|
||||
// generally you shouldn't use these three
|
||||
VideoFrameBuffer* GetFrameBuffer() const { return vfb; }
|
||||
int GetOffset() const { return offset; }
|
||||
int GetOffset(int plane) const { switch (plane) {case PLANAR_U: return offsetU;case PLANAR_V: return offsetV;default: return offset;}; }
|
||||
|
||||
// in plugins use env->SubFrame()
|
||||
VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const;
|
||||
VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV) const;
|
||||
|
||||
|
||||
const BYTE* GetReadPtr() const { return vfb->GetReadPtr() + offset; }
|
||||
const BYTE* GetReadPtr(int plane) const { return vfb->GetReadPtr() + GetOffset(plane); }
|
||||
|
||||
bool IsWritable() const { return (refcount == 1 && vfb->refcount == 1); }
|
||||
|
||||
BYTE* GetWritePtr() const {
|
||||
if (vfb->GetRefcount()>1) {
|
||||
_ASSERT(FALSE);
|
||||
//throw AvisynthError("Internal Error - refcount was more than one!");
|
||||
}
|
||||
return IsWritable() ? (vfb->GetWritePtr() + offset) : 0;
|
||||
}
|
||||
|
||||
BYTE* GetWritePtr(int plane) const {
|
||||
if (plane==PLANAR_Y) {
|
||||
if (vfb->GetRefcount()>1) {
|
||||
_ASSERT(FALSE);
|
||||
// throw AvisynthError("Internal Error - refcount was more than one!");
|
||||
}
|
||||
return IsWritable() ? vfb->GetWritePtr() + GetOffset(plane) : 0;
|
||||
}
|
||||
return vfb->data + GetOffset(plane);
|
||||
}
|
||||
|
||||
~VideoFrame() { InterlockedDecrement(&vfb->refcount); }
|
||||
};
|
||||
|
||||
enum {
|
||||
CACHE_NOTHING=0,
|
||||
CACHE_RANGE=1,
|
||||
CACHE_ALL=2,
|
||||
CACHE_AUDIO=3,
|
||||
CACHE_AUDIO_NONE=4
|
||||
};
|
||||
|
||||
// Base class for all filters.
|
||||
class IClip {
|
||||
friend class PClip;
|
||||
friend class AVSValue;
|
||||
int refcnt;
|
||||
void AddRef() { InterlockedIncrement((long *)&refcnt); }
|
||||
void Release() { InterlockedDecrement((long *)&refcnt); if (!refcnt) delete this; }
|
||||
public:
|
||||
IClip() : refcnt(0) {}
|
||||
|
||||
virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; }
|
||||
|
||||
virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0;
|
||||
virtual bool __stdcall GetParity(int n) = 0; // return field parity if field_based, else parity of first field in frame
|
||||
virtual void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) = 0; // start and count are in samples
|
||||
virtual void __stdcall SetCacheHints(int cachehints,int frame_range) = 0 ; // We do not pass cache requests upwards, only to the next filter.
|
||||
virtual const VideoInfo& __stdcall GetVideoInfo() = 0;
|
||||
virtual __stdcall ~IClip() {}
|
||||
};
|
||||
|
||||
|
||||
// smart pointer to IClip
|
||||
class PClip {
|
||||
|
||||
IClip* p;
|
||||
|
||||
IClip* GetPointerWithAddRef() const { if (p) p->AddRef(); return p; }
|
||||
friend class AVSValue;
|
||||
friend class VideoFrame;
|
||||
|
||||
void Init(IClip* x) {
|
||||
if (x) x->AddRef();
|
||||
p=x;
|
||||
}
|
||||
void Set(IClip* x) {
|
||||
if (x) x->AddRef();
|
||||
if (p) p->Release();
|
||||
p=x;
|
||||
}
|
||||
|
||||
public:
|
||||
PClip() { p = 0; }
|
||||
PClip(const PClip& x) { Init(x.p); }
|
||||
PClip(IClip* x) { Init(x); }
|
||||
void operator=(IClip* x) { Set(x); }
|
||||
void operator=(const PClip& x) { Set(x.p); }
|
||||
|
||||
IClip* operator->() const { return p; }
|
||||
|
||||
// useful in conditional expressions
|
||||
operator void*() const { return p; }
|
||||
bool operator!() const { return !p; }
|
||||
|
||||
~PClip() { if (p) p->Release(); }
|
||||
};
|
||||
|
||||
|
||||
// smart pointer to VideoFrame
|
||||
class PVideoFrame {
|
||||
|
||||
VideoFrame* p;
|
||||
|
||||
void Init(VideoFrame* x) {
|
||||
if (x) x->AddRef();
|
||||
p=x;
|
||||
}
|
||||
void Set(VideoFrame* x) {
|
||||
if (x) x->AddRef();
|
||||
if (p) p->Release();
|
||||
p=x;
|
||||
}
|
||||
|
||||
public:
|
||||
PVideoFrame() { p = 0; }
|
||||
PVideoFrame(const PVideoFrame& x) { Init(x.p); }
|
||||
PVideoFrame(VideoFrame* x) { Init(x); }
|
||||
void operator=(VideoFrame* x) { Set(x); }
|
||||
void operator=(const PVideoFrame& x) { Set(x.p); }
|
||||
|
||||
VideoFrame* operator->() const { return p; }
|
||||
|
||||
// for conditional expressions
|
||||
operator void*() const { return p; }
|
||||
bool operator!() const { return !p; }
|
||||
|
||||
~PVideoFrame() { if (p) p->Release();}
|
||||
};
|
||||
|
||||
|
||||
class AVSValue {
|
||||
public:
|
||||
|
||||
AVSValue() { type = 'v'; }
|
||||
AVSValue(IClip* c) { type = 'c'; clip = c; if (c) c->AddRef(); }
|
||||
AVSValue(const PClip& c) { type = 'c'; clip = c.GetPointerWithAddRef(); }
|
||||
AVSValue(bool b) { type = 'b'; boolean = b; }
|
||||
AVSValue(int i) { type = 'i'; integer = i; }
|
||||
// AVSValue(__int64 l) { type = 'l'; longlong = l; }
|
||||
AVSValue(float f) { type = 'f'; floating_pt = f; }
|
||||
AVSValue(double f) { type = 'f'; floating_pt = float(f); }
|
||||
AVSValue(const char* s) { type = 's'; string = s; }
|
||||
AVSValue(const AVSValue* a, int size) { type = 'a'; array = a; array_size = size; }
|
||||
AVSValue(const AVSValue& v) { Assign(&v, true); }
|
||||
|
||||
~AVSValue() { if (IsClip() && clip) clip->Release(); }
|
||||
AVSValue& operator=(const AVSValue& v) { Assign(&v, false); return *this; }
|
||||
|
||||
// Note that we transparently allow 'int' to be treated as 'float'.
|
||||
// There are no int<->bool conversions, though.
|
||||
|
||||
bool Defined() const { return type != 'v'; }
|
||||
bool IsClip() const { return type == 'c'; }
|
||||
bool IsBool() const { return type == 'b'; }
|
||||
bool IsInt() const { return type == 'i'; }
|
||||
// bool IsLong() const { return (type == 'l'|| type == 'i'); }
|
||||
bool IsFloat() const { return type == 'f' || type == 'i'; }
|
||||
bool IsString() const { return type == 's'; }
|
||||
bool IsArray() const { return type == 'a'; }
|
||||
|
||||
PClip AsClip() const { _ASSERTE(IsClip()); return IsClip()?clip:0; }
|
||||
bool AsBool() const { _ASSERTE(IsBool()); return boolean; }
|
||||
int AsInt() const { _ASSERTE(IsInt()); return integer; }
|
||||
// int AsLong() const { _ASSERTE(IsLong()); return longlong; }
|
||||
const char* AsString() const { _ASSERTE(IsString()); return IsString()?string:0; }
|
||||
double AsFloat() const { _ASSERTE(IsFloat()); return IsInt()?integer:floating_pt; }
|
||||
|
||||
bool AsBool(bool def) const { _ASSERTE(IsBool()||!Defined()); return IsBool() ? boolean : def; }
|
||||
int AsInt(int def) const { _ASSERTE(IsInt()||!Defined()); return IsInt() ? integer : def; }
|
||||
double AsFloat(double def) const { _ASSERTE(IsFloat()||!Defined()); return IsInt() ? integer : type=='f' ? floating_pt : def; }
|
||||
const char* AsString(const char* def) const { _ASSERTE(IsString()||!Defined()); return IsString() ? string : def; }
|
||||
|
||||
int ArraySize() const { _ASSERTE(IsArray()); return IsArray()?array_size:1; }
|
||||
|
||||
const AVSValue& operator[](int index) const {
|
||||
_ASSERTE(IsArray() && index>=0 && index<array_size);
|
||||
return (IsArray() && index>=0 && index<array_size) ? array[index] : *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
short type; // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or 'l'ong
|
||||
short array_size;
|
||||
union {
|
||||
IClip* clip;
|
||||
bool boolean;
|
||||
int integer;
|
||||
float floating_pt;
|
||||
const char* string;
|
||||
const AVSValue* array;
|
||||
// __int64 longlong;
|
||||
};
|
||||
|
||||
void Assign(const AVSValue* src, bool init) {
|
||||
if (src->IsClip() && src->clip)
|
||||
src->clip->AddRef();
|
||||
if (!init && IsClip() && clip)
|
||||
clip->Release();
|
||||
// make sure this copies the whole struct!
|
||||
((__int32*)this)[0] = ((__int32*)src)[0];
|
||||
((__int32*)this)[1] = ((__int32*)src)[1];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// instantiable null filter
|
||||
class GenericVideoFilter : public IClip {
|
||||
protected:
|
||||
PClip child;
|
||||
VideoInfo vi;
|
||||
public:
|
||||
GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); }
|
||||
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); }
|
||||
void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); }
|
||||
const VideoInfo& __stdcall GetVideoInfo() { return vi; }
|
||||
bool __stdcall GetParity(int n) { return child->GetParity(n); }
|
||||
void __stdcall SetCacheHints(int cachehints,int frame_range) { } ; // We do not pass cache requests upwards, only to the next filter.
|
||||
};
|
||||
|
||||
|
||||
class AvisynthError /* exception */ {
|
||||
public:
|
||||
const char* const msg;
|
||||
AvisynthError(const char* _msg) : msg(_msg) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* Helper classes useful to plugin authors */
|
||||
|
||||
class AlignPlanar : public GenericVideoFilter
|
||||
{
|
||||
public:
|
||||
AlignPlanar(PClip _clip);
|
||||
static PClip Create(PClip clip);
|
||||
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class FillBorder : public GenericVideoFilter
|
||||
{
|
||||
public:
|
||||
FillBorder(PClip _clip);
|
||||
static PClip Create(PClip clip);
|
||||
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ConvertAudio : public GenericVideoFilter
|
||||
/**
|
||||
* Helper class to convert audio to any format
|
||||
**/
|
||||
{
|
||||
public:
|
||||
ConvertAudio(PClip _clip, int prefered_format);
|
||||
void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env);
|
||||
void __stdcall SetCacheHints(int cachehints,int frame_range); // We do pass cache requests upwards, to the cache!
|
||||
|
||||
static PClip Create(PClip clip, int sample_type, int prefered_type);
|
||||
static AVSValue __cdecl Create_float(AVSValue args, void*, IScriptEnvironment*);
|
||||
static AVSValue __cdecl Create_32bit(AVSValue args, void*, IScriptEnvironment*);
|
||||
static AVSValue __cdecl Create_24bit(AVSValue args, void*, IScriptEnvironment*);
|
||||
static AVSValue __cdecl Create_16bit(AVSValue args, void*, IScriptEnvironment*);
|
||||
static AVSValue __cdecl Create_8bit(AVSValue args, void*, IScriptEnvironment*);
|
||||
virtual ~ConvertAudio();
|
||||
|
||||
private:
|
||||
void convertToFloat(char* inbuf, float* outbuf, char sample_type, int count);
|
||||
void convertToFloat_3DN(char* inbuf, float* outbuf, char sample_type, int count);
|
||||
void convertToFloat_SSE(char* inbuf, float* outbuf, char sample_type, int count);
|
||||
void convertToFloat_SSE2(char* inbuf, float* outbuf, char sample_type, int count);
|
||||
void convertFromFloat(float* inbuf, void* outbuf, char sample_type, int count);
|
||||
void convertFromFloat_3DN(float* inbuf, void* outbuf, char sample_type, int count);
|
||||
void convertFromFloat_SSE(float* inbuf, void* outbuf, char sample_type, int count);
|
||||
void convertFromFloat_SSE2(float* inbuf, void* outbuf, char sample_type, int count);
|
||||
|
||||
__inline int Saturate_int8(float n);
|
||||
__inline short Saturate_int16(float n);
|
||||
__inline int Saturate_int24(float n);
|
||||
__inline int Saturate_int32(float n);
|
||||
|
||||
char src_format;
|
||||
char dst_format;
|
||||
int src_bps;
|
||||
char *tempbuffer;
|
||||
SFLOAT *floatbuffer;
|
||||
int tempbuffer_size;
|
||||
};
|
||||
|
||||
|
||||
// For GetCPUFlags. These are backwards-compatible with those in VirtualDub.
|
||||
enum {
|
||||
/* slowest CPU to support extension */
|
||||
CPUF_FORCE = 0x01, // N/A
|
||||
CPUF_FPU = 0x02, // 386/486DX
|
||||
CPUF_MMX = 0x04, // P55C, K6, PII
|
||||
CPUF_INTEGER_SSE = 0x08, // PIII, Athlon
|
||||
CPUF_SSE = 0x10, // PIII, Athlon XP/MP
|
||||
CPUF_SSE2 = 0x20, // PIV, Hammer
|
||||
CPUF_3DNOW = 0x40, // K6-2
|
||||
CPUF_3DNOW_EXT = 0x80, // Athlon
|
||||
CPUF_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, which only Hammer
|
||||
// will have anyway)
|
||||
CPUF_SSE3 = 0x100, // Some P4 & Athlon 64.
|
||||
};
|
||||
#define MAX_INT 0x7fffffff
|
||||
#define MIN_INT -0x7fffffff
|
||||
|
||||
|
||||
|
||||
class IScriptEnvironment {
|
||||
public:
|
||||
virtual __stdcall ~IScriptEnvironment() {}
|
||||
|
||||
virtual /*static*/ long __stdcall GetCPUFlags() = 0;
|
||||
|
||||
virtual char* __stdcall SaveString(const char* s, int length = -1) = 0;
|
||||
virtual char* __stdcall Sprintf(const char* fmt, ...) = 0;
|
||||
// note: val is really a va_list; I hope everyone typedefs va_list to a pointer
|
||||
virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0;
|
||||
|
||||
__declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0;
|
||||
|
||||
class NotFound /*exception*/ {}; // thrown by Invoke and GetVar
|
||||
|
||||
typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env);
|
||||
|
||||
virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0;
|
||||
virtual bool __stdcall FunctionExists(const char* name) = 0;
|
||||
virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char** arg_names=0) = 0;
|
||||
|
||||
virtual AVSValue __stdcall GetVar(const char* name) = 0;
|
||||
virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0;
|
||||
virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0;
|
||||
|
||||
virtual void __stdcall PushContext(int level=0) = 0;
|
||||
virtual void __stdcall PopContext() = 0;
|
||||
|
||||
// align should be 4 or 8
|
||||
virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=FRAME_ALIGN) = 0;
|
||||
|
||||
virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0;
|
||||
|
||||
virtual /*static*/ void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) = 0;
|
||||
|
||||
typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env);
|
||||
virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0;
|
||||
|
||||
virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0;
|
||||
|
||||
virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0;
|
||||
|
||||
virtual int __stdcall SetMemoryMax(int mem) = 0;
|
||||
|
||||
virtual int __stdcall SetWorkingDir(const char * newdir) = 0;
|
||||
|
||||
virtual void* __stdcall ManageCache(int key, void* data) = 0;
|
||||
|
||||
enum PlanarChromaAlignmentMode {
|
||||
PlanarChromaAlignmentOff,
|
||||
PlanarChromaAlignmentOn,
|
||||
PlanarChromaAlignmentTest };
|
||||
|
||||
virtual bool __stdcall PlanarChromaAlignment(PlanarChromaAlignmentMode key) = 0;
|
||||
|
||||
virtual PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) = 0;
|
||||
};
|
||||
|
||||
|
||||
// avisynth.dll exports this; it's a way to use it as a library, without
|
||||
// writing an AVS script or without going through AVIFile.
|
||||
IScriptEnvironment* __stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION);
|
||||
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif //__AVISYNTH_H__
|
87
core/avisynth_wrap.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
////////////
|
||||
// Includes
|
||||
#include "avisynth_wrap.h"
|
||||
#include "options.h"
|
||||
|
||||
///////////////////////////////
|
||||
// Static field initialization
|
||||
int AviSynthWrapper::avs_refcount = 0;
|
||||
HINSTANCE AviSynthWrapper::hLib = NULL;
|
||||
IScriptEnvironment *AviSynthWrapper::env = NULL;
|
||||
wxMutex AviSynthWrapper::AviSynthMutex;
|
||||
|
||||
////////////////////////
|
||||
// AviSynth constructor
|
||||
AviSynthWrapper::AviSynthWrapper() {
|
||||
if (!avs_refcount++) {
|
||||
hLib=LoadLibrary(_T("avisynth.dll"));
|
||||
|
||||
if (hLib == NULL)
|
||||
throw _T("Could not load avisynth.dll");
|
||||
|
||||
FUNC *CreateScriptEnv = (FUNC*)GetProcAddress(hLib, "CreateScriptEnvironment");
|
||||
|
||||
if (CreateScriptEnv == NULL)
|
||||
throw _T("Failed to get function from avisynth.dll");
|
||||
|
||||
env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION);
|
||||
|
||||
if (env == NULL)
|
||||
throw _T("Failed to create a new avisynth script environment. Avisynth is too old?");
|
||||
|
||||
// Check for a new enough avisynth version by looking for the most obscure function used
|
||||
if (!env->FunctionExists("InternalCache"))
|
||||
throw _T("Installed version of avisynth is too old");
|
||||
|
||||
// Set memory limit
|
||||
int memoryMax = Options.AsInt(_T("Avisynth MemoryMax"));
|
||||
if (memoryMax != 0)
|
||||
env->SetMemoryMax(memoryMax);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// AviSynth destructor
|
||||
AviSynthWrapper::~AviSynthWrapper() {
|
||||
if (!--avs_refcount) {
|
||||
delete env;
|
||||
FreeLibrary(hLib);
|
||||
}
|
||||
}
|
67
core/avisynth_wrap.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:zeratul@cellosoft.com
|
||||
//
|
||||
|
||||
|
||||
#ifndef AVISYNTH_WRAP_H
|
||||
#define AVISYNTH_WRAP_H
|
||||
|
||||
|
||||
///////////
|
||||
// Headers
|
||||
#include <windows.h>
|
||||
#include <wx/wxprec.h>
|
||||
#include "avisynth.h"
|
||||
|
||||
//////////////////////////////////
|
||||
// Typedef to make my life easier
|
||||
typedef IScriptEnvironment* __stdcall FUNC(int);
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// AviSynth wrapping class
|
||||
class AviSynthWrapper {
|
||||
private:
|
||||
static int avs_refcount;
|
||||
static HINSTANCE hLib;
|
||||
protected:
|
||||
static IScriptEnvironment *env;
|
||||
public:
|
||||
static wxMutex AviSynthMutex;
|
||||
|
||||
AviSynthWrapper();
|
||||
~AviSynthWrapper();
|
||||
};
|
||||
|
||||
#endif
|
BIN
core/bitmaps/automation.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_audio_commit.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_audio_go.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_bold.bmp
Normal file
After Width: | Height: | Size: 214 B |
BIN
core/bitmaps/button_color1.bmp
Normal file
After Width: | Height: | Size: 262 B |
BIN
core/bitmaps/button_color2.bmp
Normal file
After Width: | Height: | Size: 262 B |
BIN
core/bitmaps/button_color3.bmp
Normal file
After Width: | Height: | Size: 262 B |
BIN
core/bitmaps/button_color4.bmp
Normal file
After Width: | Height: | Size: 262 B |
BIN
core/bitmaps/button_fontname.bmp
Normal file
After Width: | Height: | Size: 262 B |
BIN
core/bitmaps/button_italics.bmp
Normal file
After Width: | Height: | Size: 214 B |
BIN
core/bitmaps/button_leadin.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_leadout.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_next.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_play.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_play500after.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_play500before.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_playfirst500.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_playlast500.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_playline.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_playsel.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_playtoend.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_prev.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_stop.bmp
Normal file
After Width: | Height: | Size: 238 B |
BIN
core/bitmaps/button_strikeout.bmp
Normal file
After Width: | Height: | Size: 214 B |
BIN
core/bitmaps/button_underline.bmp
Normal file
After Width: | Height: | Size: 214 B |
BIN
core/bitmaps/contents.bmp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
core/bitmaps/copy.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/copy_disable.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/cut.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/cut_disable.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/find.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/fontcollect.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/hotkeys.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/icon.ico
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
core/bitmaps/irc.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/jumpto.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/jumpto_disable.bmp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
core/bitmaps/new.bmp
Normal file
After Width: | Height: | Size: 238 B |