mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-17 00:20:06 +02:00
Nyquist Effects now treat Nyquist code and output as UTF-8. Nyquist Prompt effect accepts both LISP and SAL (in SAL, either define function main or write return <expression> at the top level).
This commit is contained in:
parent
38caac8648
commit
f2e6d8ec0f
@ -1026,7 +1026,9 @@
|
||||
(if (token-is '(:define :load :chdir :variable :function
|
||||
; :system
|
||||
:play :print :display))
|
||||
(parse-command)))
|
||||
(parse-command)
|
||||
(if (and (token-is '(:return)) *audacity-top-level-return-flag*)
|
||||
(parse-command))))
|
||||
|
||||
|
||||
(defun parse-command ()
|
||||
@ -1044,6 +1046,8 @@
|
||||
(parse-print-display :print 'sal-print))
|
||||
((token-is :display)
|
||||
(parse-print-display :display 'display))
|
||||
((and *audacity-top-level-return-flag* (token-is :return))
|
||||
(parse-return))
|
||||
; ((token-is :output)
|
||||
; (parse-output))
|
||||
(t
|
||||
@ -1251,12 +1255,16 @@
|
||||
|
||||
(defun parse-return ()
|
||||
(or (token-is :return) (error "parse-return internal error"))
|
||||
(let (loc)
|
||||
(if (null *sal-fn-name*)
|
||||
(let (loc expr)
|
||||
;; this seems to be a redundant test
|
||||
(if (and (null *sal-fn-name*)
|
||||
(not *audacity-top-level-return-flag*))
|
||||
(errexit "Return must be inside a function body"))
|
||||
(setf loc (parse-token))
|
||||
(add-line-info-to-stmt (list 'sal-return-from *sal-fn-name*
|
||||
(parse-sexpr)) loc)))
|
||||
(setf expr (parse-sexpr))
|
||||
(if *sal-fn-name*
|
||||
(add-line-info-to-stmt (list 'sal-return-from *sal-fn-name* expr) loc)
|
||||
(list 'defun 'main '() (add-line-info-to-stmt expr loc)))))
|
||||
|
||||
|
||||
(defun parse-load ()
|
||||
|
@ -487,6 +487,21 @@
|
||||
(if *sal-traceback* (sal-traceback))
|
||||
(setf *sal-call-stack* stack)) ;; clear the stack
|
||||
|
||||
|
||||
;; when true, top-level return statement is legal and compiled into MAIN
|
||||
(setf *audacity-top-level-return-flag* nil)
|
||||
|
||||
;; SAL-COMPILE-AUDACITY -- special treatment of RETURN
|
||||
;;
|
||||
;; This works like SAL-COMPILE, but if there is a top-level
|
||||
;; return statement (not normally legal), it is compiled into
|
||||
;; a function named MAIN. This is a shorthand for Audacity plug-ins
|
||||
;;
|
||||
(defun sal-compile-audacity (input eval-flag multiple-statements filename)
|
||||
(progv '(*audacity-top-level-return-flag*) '(t)
|
||||
(sal-compile input eval-flag multiple-statements filename)))
|
||||
|
||||
|
||||
;; SAL-COMPILE -- translate string or token list to lisp and eval
|
||||
;;
|
||||
;; input is either a string or a token list
|
||||
|
@ -71,6 +71,7 @@ WX_DEFINE_OBJARRAY(NyqControlArray);
|
||||
EffectNyquist::EffectNyquist(wxString fName)
|
||||
{
|
||||
mAction = _("Applying Nyquist Effect...");
|
||||
mInputCmd = wxEmptyString;
|
||||
mCmd = wxEmptyString;
|
||||
SetEffectFlags(HIDDEN_EFFECT);
|
||||
mInteractive = false;
|
||||
@ -105,6 +106,18 @@ EffectNyquist::~EffectNyquist()
|
||||
{
|
||||
}
|
||||
|
||||
wxString EffectNyquist::NyquistToWxString(const char *nyqString)
|
||||
{
|
||||
wxString str(nyqString, wxConvUTF8);
|
||||
if (nyqString != NULL && nyqString[0] && str.IsEmpty()) {
|
||||
// invalid UTF-8 string, convert as Latin-1
|
||||
str = _("[Warning: Nyquist returned invalid UTF-8 string, converted here as Latin-1]");
|
||||
str += LAT1CTOWX(nyqString);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
void EffectNyquist::Break()
|
||||
{
|
||||
mBreak = true;
|
||||
@ -416,7 +429,7 @@ bool EffectNyquist::PromptUser()
|
||||
NyquistInputDialog dlog(wxGetTopLevelParent(NULL), -1,
|
||||
_("Nyquist Prompt"),
|
||||
_("Enter Nyquist Command: "),
|
||||
mCmd);
|
||||
mInputCmd);
|
||||
dlog.CentreOnParent();
|
||||
int result = dlog.ShowModal();
|
||||
|
||||
@ -429,7 +442,83 @@ bool EffectNyquist::PromptUser()
|
||||
}*/
|
||||
mDebug = (result == eDebugID);
|
||||
|
||||
mCmd = dlog.GetCommand();
|
||||
// remember exact input in mInputCmd which will appear in the next
|
||||
// NyquistInputDialog. Copy to mCmd for possible embedding in
|
||||
// "function main() begin ... end":
|
||||
mCmd = mInputCmd = dlog.GetCommand();
|
||||
|
||||
// Is this LISP or SAL? Both allow comments. After comments, LISP
|
||||
// must begin with "(". Technically, a LISP expression could be a
|
||||
// symbol or number or string, etc., but these are not really
|
||||
// useful expressions. If the input begins with a symbol, number,
|
||||
// or string, etc., it is more likely an erroneous attempt to type
|
||||
// a SAL expression (which should probably begin with "return"),
|
||||
// so we will treat it as SAL.
|
||||
|
||||
// this is a state machine to scan past LISP comments and white
|
||||
// space to find the first real character of LISP or SAL. Note
|
||||
// that #| ... |# style comments are not valid in SAL, so we do
|
||||
// not skip these. Instead, "#|" indicates LISP if found.
|
||||
//
|
||||
int i = 0;
|
||||
bool inComment = false; // handle "; ... \n" comments
|
||||
while (i < mCmd.Len()) {
|
||||
if (inComment) {
|
||||
inComment = (mCmd[i] != wxT('\n'));
|
||||
} else if (mCmd[i] == wxT(';')) {
|
||||
inComment = true;
|
||||
} else if (!wxIsspace(mCmd[i])) {
|
||||
break; // found the first non-comment, non-space character
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// invariant: i == mCmd.Len() |
|
||||
// mCmd[i] is first non-comment, non-space character
|
||||
|
||||
mIsSal = false;
|
||||
mCmd = mCmd.Mid(i); // remove initial comments
|
||||
if (mCmd.Len() > 0 && mCmd[0] != wxT('(') && mCmd[0] != wxT('#')) {
|
||||
mIsSal = true;
|
||||
wxString cmdUp = mCmd.Upper();
|
||||
int returnLoc = cmdUp.Find(wxT("RETURN"));
|
||||
if (returnLoc == wxNOT_FOUND) {
|
||||
wxMessageBox(_("Your code looks like SAL syntax, but there is no return statement. Either use a return statement such as\n\treturn s * 0.1\nfor SAL, or begin with an open parenthesis such as\n\t(mult s 0.1)\n for LISP."), _("Error in Nyquist code"), wxOK | wxCENTRE);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
// Allow two forms of SAL "expressions":
|
||||
// 1) a bunch of statements that do not define "main" followed by
|
||||
// "return ...": wrap the return statement in main
|
||||
// 2) a bunch of statements that include a definition of "main":
|
||||
// return the code as is
|
||||
// This allows a simple input of the form "return <expression>"
|
||||
// but since this does not match the syntax of a real SAL plug-in,
|
||||
// we want to allow user to define "main"
|
||||
|
||||
// Search for "function main". This might be fooled if user puts
|
||||
// "function main" inside a string or comment
|
||||
bool definesMain = false;
|
||||
int loc = cmdUp.Find(wxT("FUNCTION"));
|
||||
while (loc != wxNOT_FOUND) {
|
||||
// remove everything up to FUNCTION and additional white space
|
||||
cmdUp = cmdUp.Mid(loc + 8).Trim(false);
|
||||
// see if the function name is MAIN
|
||||
if (cmdUp.StartsWith(wxT("MAIN"))) {
|
||||
definesMain = true;
|
||||
break;
|
||||
}
|
||||
loc = cmdUp.Find(wxT("FUNCTION")); // look for next definition
|
||||
}
|
||||
if (loc == wxNOT_FOUND) {
|
||||
// replace the LAST return
|
||||
returnLoc = FindFromEnd(mCmd, wxT("RETURN"));
|
||||
|
||||
// wrap Sal statements in a function (main)
|
||||
mCmd = mCmd.Prepend(wxT("function main() begin\n"));
|
||||
mCmd += wxT("\nend\n");
|
||||
*/
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -601,7 +690,7 @@ bool EffectNyquist::Process()
|
||||
NyquistOutputDialog dlog(mParent, -1,
|
||||
_("Nyquist"),
|
||||
_("Nyquist Output: "),
|
||||
wxString(mDebugOutput.c_str(), wxConvISO8859_1));
|
||||
NyquistToWxString(mDebugOutput.c_str()));
|
||||
dlog.CentreOnParent();
|
||||
dlog.ShowModal();
|
||||
}
|
||||
@ -657,9 +746,12 @@ bool EffectNyquist::ProcessOne()
|
||||
wxString str = mControls[j].valStr;
|
||||
str.Replace(wxT("\\"), wxT("\\\\"));
|
||||
str.Replace(wxT("\""), wxT("\\\""));
|
||||
cmd += wxString::Format(wxT("(setf %s \"%s\")\n"),
|
||||
mControls[j].var.c_str(),
|
||||
str.c_str());
|
||||
cmd += wxT("(setf ");
|
||||
// restrict variable names to 7-bit ASCII:
|
||||
cmd += mControls[j].var.c_str();
|
||||
cmd += wxT(" \"");
|
||||
cmd += str; // unrestricted value will become quoted UTF-8
|
||||
cmd += wxT("\")\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -672,7 +764,7 @@ bool EffectNyquist::ProcessOne()
|
||||
}
|
||||
|
||||
cmd += wxT("(setf *sal-call-stack* nil)\n");
|
||||
cmd += wxT("(sal-compile \"\n") + str + wxT("\n\" t t nil)\n");
|
||||
cmd += wxT("(sal-compile-audacity \"\n") + str + wxT("\n\" t t nil)\n");
|
||||
cmd += wxT("(main)\n");
|
||||
}
|
||||
else {
|
||||
@ -684,11 +776,12 @@ bool EffectNyquist::ProcessOne()
|
||||
mCurBuffer[i] = NULL;
|
||||
}
|
||||
|
||||
rval = nyx_eval_expression(cmd.mb_str());
|
||||
rval = nyx_eval_expression(cmd.mb_str(wxConvUTF8));
|
||||
|
||||
if (rval == nyx_string) {
|
||||
wxMessageBox(wxString(nyx_get_string(), wxConvISO8859_1), wxT("Nyquist"),
|
||||
wxOK | wxCENTRE, mParent);
|
||||
wxMessageBox(NyquistToWxString(nyx_get_string()),
|
||||
wxT("Nyquist"),
|
||||
wxOK | wxCENTRE, mParent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -734,7 +827,7 @@ bool EffectNyquist::ProcessOne()
|
||||
|
||||
nyx_get_label(l, &t0, &t1, &str);
|
||||
|
||||
ltrack->AddLabel(t0 + mT0, t1 + mT0, LAT1CTOWX(str));
|
||||
ltrack->AddLabel(t0 + mT0, t1 + mT0, UTF8CTOWX(str));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -118,6 +118,8 @@ class AUDACITY_DLL_API EffectNyquist:public Effect
|
||||
|
||||
private:
|
||||
|
||||
static wxString NyquistToWxString(const char *nyqString);
|
||||
|
||||
bool ProcessOne();
|
||||
|
||||
static int StaticGetCallback(float *buffer, int channel,
|
||||
@ -161,7 +163,8 @@ class AUDACITY_DLL_API EffectNyquist:public Effect
|
||||
*/
|
||||
bool mInteractive;
|
||||
bool mOK;
|
||||
wxString mCmd;
|
||||
wxString mInputCmd; // history: exactly what the user typed
|
||||
wxString mCmd; // the command to be processed
|
||||
wxString mName; ///< Name of the Effect
|
||||
wxString mAction;
|
||||
wxString mInfo;
|
||||
|
Loading…
x
Reference in New Issue
Block a user