1
0
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:
rbdannenberg 2010-11-01 03:15:39 +00:00
parent 38caac8648
commit f2e6d8ec0f
4 changed files with 136 additions and 17 deletions

View File

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

View File

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

View File

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

View File

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