Support for Nyquist effects in Chains
Forum rules
If you require help using Audacity, please post on the forum board relevant to your operating system:
Windows
Mac OS X
GNU/Linux and Unix-like
If you require help using Audacity, please post on the forum board relevant to your operating system:
Windows
Mac OS X
GNU/Linux and Unix-like
Support for Nyquist effects in Chains
How feasible would it be to add one Nyquist effect to the Chain menu?
What I'm thinking of is for there to be a "dummy" Nyquist effect (possibly called "chain.ny") that can be added as an effect in a batch processing chain. If this could be done (preferably with the facility to pass parameters to the effect, though that would be an optional bonus feature), then "chain.ny" could be manually edited to include arbitrary Nyquist code for use in a processing chain.
An alternative approach might be to add the "Nyquist Prompt" to Chains, in which the Nyquist Prompt text can be added as the "Parameters".
What I'm thinking of is for there to be a "dummy" Nyquist effect (possibly called "chain.ny") that can be added as an effect in a batch processing chain. If this could be done (preferably with the facility to pass parameters to the effect, though that would be an optional bonus feature), then "chain.ny" could be manually edited to include arbitrary Nyquist code for use in a processing chain.
An alternative approach might be to add the "Nyquist Prompt" to Chains, in which the Nyquist Prompt text can be added as the "Parameters".
Last edited by steve on Fri Apr 24, 2015 3:42 pm, edited 3 times in total.
Reason: update title
Reason: update title
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
Re: Question re. adding effect to Chain
Sorry Steve, this is way out of my league! I will take a quick look at the code but don't have much time now that summer gig season is in full swing.
Re: Question re. adding effect to Chain
You mean something like this:
I can get this far with a few lines of code but do not really know where to go from here! Can you write me a tiny Nyquist effect which has no GUI and no params --it could just add 5 secs of silence to the start of the track; then a different effect which takes a param named "amount" which has no GUI but needs a single param (number of seconds) "amount" which adds that to the front of the track? This would give me something with which to test.
here is the patch so far...
here is the patch so far...
Code: Select all
Index: src/BatchCommands.cpp
===================================================================
--- src/BatchCommands.cpp (revision 11173)
+++ src/BatchCommands.cpp (working copy)
@@ -60,6 +60,10 @@
wxT("ExportMp3"),
wxT("ExportOgg"),
wxT("ExportWav")
+# if defined(USE_NYQUIST)
+ ,
+ wxT("Nyquist")//efm5
+# endif
};
static const wxString CleanSpeech = wxT("CleanSpeech");
@@ -238,6 +242,9 @@
AddToChain( wxT("Leveller") );
AddToChain( wxT("Normalize") );
AddToChain( wxT("ExportMp3") );
+# if defined(USE_NYQUIST)
+ AddToChain( wxT("Nyquist") );//efm5
+# endif
}
void BatchCommands::SetWavToMp3Chain()
@@ -488,6 +495,11 @@
return false;
#endif
}
+# if defined(USE_NYQUIST)
+ else if (command == wxT("Nyquist")) {//efm5 start
+ wxMessageBox(wxT("nyquist msg"));//debug msg
+ }//efm5 end
+# endif
wxMessageBox(wxString::Format(_("Command %s not implemented yet"),command.c_str()));
return false;
}
Re: Question re. adding effect to Chain
Yes, that's exactly the sort of thing.Edgar wrote:You mean something like this:
This is the most simple full plug-in that I could think of. It scales the amplitude of the selection by 0.5 (half amplitude).Edgar wrote:Can you write me a tiny Nyquist effect which has no GUI and no params
Save this as a text file with a file extension .ny
Code: Select all
;nyquist plug-in
;version 1
;type process
;name "Half Amplitude"
;action "Processing..."
(mult s 0.5)
This might be a tricky bit.Edgar wrote:then a different effect which takes a param named "amount" which has no GUI but needs a single param...
Nyquist code does not have its own GUI. Nyquist plug-ins begin with a few header lines that are read by Audacity (see here: http://www.audacity-forum.de/download/e ... htm#header ). If the header includes one or more lines:
Code: Select all
;control parametersTo create a global variable in Nyquist and give it a value, the Nyquist code is:
Code: Select all
(setq variable value)If the track is a mono track, then the value of "s" is of data type "sound".
If the track is a stereo track, then the value of "s" is an array with two elements. Each element is a "sound" (one for each channel).
A "sound" is a primitive data type in Nyquist. http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#79
Ways that it may be possible to pass parameters to Nyquist:
1) To send values for the control widgets in a similar way to how parameters are sent to other effects (I don't know how that is done, or how the effect GUI is suppressed).
2) Pass a named variable with a value to Nyquist in a similar way to how "S" is passed to Nyquist.
3) Pass code to the Nyquist plug-in. This may work most easily if using the Nyquist Prompt as the Nyquist Chain effect.
Example of Nyquist code that includes a variable:
Code: Select all
(setq amount 0.5)
(mult s amount)Code: Select all
;nyquist plug-in
;version 1
;type process
;name "Change Amplitude"
;action "Processing..."
;control amount "Amount to amplify" real "Linear scale" 0.5 0.0 10.0
(mult s amount)
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
Re: Question re. adding effect to Chain
OK, will play some more after doing the gardening...
Re: Question re. adding effect to Chain
OK, gardening done for the day--the joy of multi-tasking is I got to think about this feature while potting, weeding and putting in a new section of lawn!
First thoughts:
1) User Interface:
A) Is it OK to interrupt the chain with a typical Nyquist dialog asking for user input?
i) If "No" how do we pass parameters into the effect?
ii) If "Yes" do we want to be able to access all (Nyquist??) effects?
B) If we go with a one-off effect that the user writes/edits where do we store the file?
i) If it is a typical xxx.ny file stored in plug-ins it will be picked up automatically for inclusion in the general menu.
a) Do we want it in the Audacity menu?
b) If we do not want it in the menu how do we deal with this?
ii) We could invent a custom extension-- .nc maybe, and keep it in the plug-ins folder.
2) Code:
A) Can we find a member of the Development Team to mentor the project?
i) If not, can we find someone on Mac who can compile Audacity so we may pre-alpha test on all 3 platforms?
B) Should we expect to get this committed pre-2.0?
C) Can this be written as a module?
i) If so, should it be a module?
ii) Can we find a programmer who knows how to write modules who might help?
I'll play with a proof-of-concept based on "no user input dialog", one-off, using .ny (thus it will appear in the menu) and not-a-module (I do not know how to write modules). I think the ultimate goal could be "allow user dialog", any Nyquist effect including a "hidden" chainEffect.nc which will be in plug-ins, user-editable and NOT in the menu, and it would be nice if we could make it a module so it would not be dependent on a Development Team programmer/mentor.
First thoughts:
1) User Interface:
A) Is it OK to interrupt the chain with a typical Nyquist dialog asking for user input?
i) If "No" how do we pass parameters into the effect?
ii) If "Yes" do we want to be able to access all (Nyquist??) effects?
B) If we go with a one-off effect that the user writes/edits where do we store the file?
i) If it is a typical xxx.ny file stored in plug-ins it will be picked up automatically for inclusion in the general menu.
a) Do we want it in the Audacity menu?
b) If we do not want it in the menu how do we deal with this?
ii) We could invent a custom extension-- .nc maybe, and keep it in the plug-ins folder.
2) Code:
A) Can we find a member of the Development Team to mentor the project?
i) If not, can we find someone on Mac who can compile Audacity so we may pre-alpha test on all 3 platforms?
B) Should we expect to get this committed pre-2.0?
C) Can this be written as a module?
i) If so, should it be a module?
ii) Can we find a programmer who knows how to write modules who might help?
I'll play with a proof-of-concept based on "no user input dialog", one-off, using .ny (thus it will appear in the menu) and not-a-module (I do not know how to write modules). I think the ultimate goal could be "allow user dialog", any Nyquist effect including a "hidden" chainEffect.nc which will be in plug-ins, user-editable and NOT in the menu, and it would be nice if we could make it a module so it would not be dependent on a Development Team programmer/mentor.
Re: Question re. adding effect to Chain
It is whipping me! When called via a chain the nyquist code fails to return audio and I cannot figure out why. Here is the code so far:
I am very close but am having trouble debugging the "C" code as I may not set breakpoints in it because it does not compile into Debug code. Will play with it some more later.
Code: Select all
Index: src/BatchCommands.cpp
===================================================================
--- src/BatchCommands.cpp (revision 11173)
+++ src/BatchCommands.cpp (working copy)
@@ -60,6 +60,10 @@
wxT("ExportMp3"),
wxT("ExportOgg"),
wxT("ExportWav")
+# if defined(USE_NYQUIST)
+ ,
+ wxT("Nyquist")//efm5
+# endif
};
static const wxString CleanSpeech = wxT("CleanSpeech");
@@ -410,7 +414,7 @@
// If you find yourself adding lots of existing commands from the menus here, STOP
// and think again.
// ======= IMPORTANT ========
-bool BatchCommands::ApplySpecialCommand(int iCommand, const wxString command,const wxString params)
+bool BatchCommands::ApplySpecialCommand(int iCommand, const wxString command, const wxString params)
{
if (ReportAndSkip(command, params))
return true;
@@ -425,12 +429,14 @@
}
wxString filename;
- if (mFileName.IsEmpty()) {
- filename = project->BuildCleanFileName(project->GetFileName());
+ if (command != wxT("Nyquist")) {//efm5 start
+ if (mFileName.IsEmpty()) {
+ filename = project->BuildCleanFileName(project->GetFileName());
+ }
+ else {
+ filename = project->BuildCleanFileName(mFileName);
+ }
}
- else {
- filename = project->BuildCleanFileName(mFileName);
- }
// We have a command index, but we don't use it!
// TODO: Make this special-batch-command code use the menu item code....
@@ -488,6 +494,14 @@
return false;
#endif
}
+# if defined(USE_NYQUIST)//efm5 start
+ else if (command == wxT("Nyquist")) {
+ Effect * f = EffectManager::Get().GetEffectByIdentifier(wxT("Batch"));
+ if (f != NULL) {
+ return ApplyEffectCommand(f, command, params);
+ }
+ }
+# endif//efm5 end
wxMessageBox(wxString::Format(_("Command %s not implemented yet"),command.c_str()));
return false;
}
Re: Question re. adding effect to Chain
Got it!
chain.ny:
To make debugging a bit easier I changed the text on the Nyquist code you supplied. I am going to try something a bit more challenging next...
Code: Select all
Index: src/BatchCommands.cpp
===================================================================
--- src/BatchCommands.cpp (revision 11173)
+++ src/BatchCommands.cpp (working copy)
@@ -60,6 +60,10 @@
wxT("ExportMp3"),
wxT("ExportOgg"),
wxT("ExportWav")
+# if defined(USE_NYQUIST)
+ ,
+ wxT("Nyquist")//efm5
+# endif
};
static const wxString CleanSpeech = wxT("CleanSpeech");
@@ -410,7 +414,7 @@
// If you find yourself adding lots of existing commands from the menus here, STOP
// and think again.
// ======= IMPORTANT ========
-bool BatchCommands::ApplySpecialCommand(int iCommand, const wxString command,const wxString params)
+bool BatchCommands::ApplySpecialCommand(int iCommand, const wxString command, const wxString params)
{
if (ReportAndSkip(command, params))
return true;
@@ -425,12 +429,14 @@
}
wxString filename;
- if (mFileName.IsEmpty()) {
- filename = project->BuildCleanFileName(project->GetFileName());
- }
- else {
- filename = project->BuildCleanFileName(mFileName);
- }
+ if (command != wxT("Nyquist")) {//efm5 start
+ if (mFileName.IsEmpty()) {
+ filename = project->BuildCleanFileName(project->GetFileName());
+ }
+ else {
+ filename = project->BuildCleanFileName(mFileName);
+ }
+ }//efm5 end
// We have a command index, but we don't use it!
// TODO: Make this special-batch-command code use the menu item code....
@@ -488,6 +494,14 @@
return false;
#endif
}
+# if defined(USE_NYQUIST)//efm5 start
+ else if (command == wxT("Nyquist")) {
+ Effect * f = EffectManager::Get().GetEffectByIdentifier(wxT("Batch"));
+ if (f != NULL) {
+ return ApplyEffectCommand(f, command, params, true);
+ }
+ }
+# endif//efm5 end
wxMessageBox(wxString::Format(_("Command %s not implemented yet"),command.c_str()));
return false;
}
@@ -511,7 +525,7 @@
return true;
}
-bool BatchCommands::ApplyEffectCommand( Effect * f, const wxString command, const wxString params)
+bool BatchCommands::ApplyEffectCommand( Effect * f, const wxString command, const wxString params, bool pbNyquistChain)
{
//Possibly end processing here, if in batch-debug
if( ReportAndSkip(command, params))
@@ -525,7 +539,11 @@
project->SelectAllIfNone();
// NOW actually apply the effect.
- return project->OnEffect(ALL_EFFECTS | CONFIGURED_EFFECT , f, params, false);
+ //efm5 start
+ int type = ALL_EFFECTS | CONFIGURED_EFFECT;
+ if (pbNyquistChain) type = PLUGIN_EFFECT | PROCESS_EFFECT;
+ return project->OnEffect(type, f, params, false);
+ //efm5 end
}
bool BatchCommands::ApplyMenuCommand(const wxString command, const wxString params)
Index: src/BatchCommands.h
===================================================================
--- src/BatchCommands.h (revision 11173)
+++ src/BatchCommands.h (working copy)
@@ -28,7 +28,7 @@
bool ApplyCommand( const wxString command, const wxString params );
bool ApplyCommandInBatchMode(const wxString & command, const wxString ¶ms);
bool ApplySpecialCommand(int iCommand, const wxString command,const wxString params);
- bool ApplyEffectCommand(Effect * f, const wxString command, const wxString params);
+ bool ApplyEffectCommand(Effect * f, const wxString command, const wxString params, bool pbNyquistChain = false);
bool ApplyMenuCommand(const wxString command, const wxString params);
bool ReportAndSkip( const wxString command, const wxString params );
void AbortBatch();
Code: Select all
;nyquist plug-in
;version 1
;type process
;name "Batch"
;action "Processing custom chain effect"
(mult s 0.5)
Re: Question re. adding effect to Chain
more challenging...
I kept the first few lines of the half amp code and inserted the code from SilenceFinder:
worked a charm! Can I figure out how to pass a parameter?
I kept the first few lines of the half amp code and inserted the code from SilenceFinder:
Code: Select all
;nyquist plug-in
;version 1
;type process
;name "Batch"
;action "Processing custom chain effect"
;info "Written by Alex S. Brown, PMP (http://www.alexsbrown.com) nReleased under terms of the GNU General Public License version 2nAdds point labels in areas of silence according to the specifiednlevel and duration of silence. If too many silences are detected,nincrease the silence level and duration; if too few are detected,nreduce the level and duration."
;control sil-lev "Treat audio below this level as silence [ -dB]" real "" 26 0 100
;control sil-dur "Minimum duration of silence [seconds]" real "" 1.0 0.1 5.0
;control labelbeforedur "Label placement [seconds before silence ends]" real "" 0.3 0.0 1.0
;Create a function to make the sum the two channels if they are stereo
(defun mono-s (s-in) (if (arrayp s-in) (snd-add (aref s-in 0) (aref s-in 1))
s-in))
;Create a function to reduce the sample rate and prepare the signal for
;analysis. RMS is good to monitor volume the way humans hear it, but is not
;available in Audacity. Used a peak-calculating function instead.
;NOTE: this is the place to add any processing to improve the quality of the
;signal. Noise filters could improve the quality of matches for noisy signals.
;PERFORMANCE vs. ACCURACY
;Reducing the samples per second should improve the performance and decrease
;the accuracy of the labels. Increasing the samples per second will do the
;opposite. The more samples checked, the longer it takes. The more samples
;checked, the more precisely the program can place the silence labels.
;my-srate-ratio determines the number of samples in my-s. Set the number after (snd-srate s)
;higher to increase the number of samples.
(defun my-s (s-in)
(setq my-srate-ratio (truncate (/ (snd-srate (mono-s s-in)) 100)))
(snd-avg (mono-s s-in) my-srate-ratio my-srate-ratio OP-PEAK)
)
;Set the silence threshold level (convert it to a linear form)
(setq thres (db-to-linear (* -1 sil-lev)))
;Store the sample rate of the sound
(setq s1-srate (snd-srate (my-s s)))
;Initialize the variable that will hold the length of the sound.
;Do not calculate it now with snd-length, because it would waste memory.
;We will calculate it later.
(setq s1-length 0)
;Initialize the silence counter and the labels variable
(setq sil-c 0)
(setq l NIL)
;Convert the silence duration in seconds to a length in samples
(setq sil-length (* sil-dur s1-srate))
;Define a function to add new items to the list of labels
(defun add-label (l-time l-text)
(setq l (cons (list l-time l-text) l))
)
;The main working part of the program, it counts
;the number of sequential samples with volume under
;the threshold. It adds to a list of markers ever time
;there is a longer period of silence than the silence
;duration amount.
;It runs through a loop, adding to the list of markers (l)
;each time it finds silence.
(let (s1) ;Define s1 as a local variable to allow efficient memory use
; Get the sample into s1, then free s to save memory
(setq s1 (my-s s))
(setq s nil)
;Capture the result of this "do" loop, because we need the sountd's legnth
;in samples.
(setq s1-length
;Keep repeating, incrementing the counter and getting another sample
;each time through the loop.
(do ((n 1 (+ n 1)) (v (snd-fetch s1) (setq v (snd-fetch s1))))
;Exit when we run out of samples (v is nil) and return the number of
;samples processed (n)
((not v) n)
;Start the execution part of the do loop
;if found silence, increment the silence counter
(if (< v thres) (setq sil-c (+ sil-c 1)))
;If this sample is NOT silent and the previous samples were silent
;then mark the passage.
(if (and (> v thres) (> sil-c sil-length))
;Mark the user-set number of seconds BEFORE this point to avoid clipping the start
;of the material.
(add-label (- (/ n s1-srate) labelbeforedur) "S")
)
;If this sample is NOT silent, then reset the silence counter
(if (> v thres)
(setq sil-c 0)
)
)
)
)
;Check for a long period of silence at the end
;of the sample. If so, then mark it.
(if (> sil-c sil-length)
;If found, add a label
;Label time is the time the silence began plus the silence duration target
;amount. We calculate the time the silence began as the end-time minus the
;final value of the silence counter
(add-label (+ (/ (- s1-length sil-c) s1-srate) sil-dur) "S")
)
;If no silence markers were found, return a message
(if (null l)
(setq l "No silences found. Try reducing the silencenlevel and minimum silence duration.")
)
l
Re: Question re. adding effect to Chain
I see now that without major surgery to the Nyquist concept I would not be able to pass generic parameters.Edgar wrote:Can I figure out how to pass a parameter?
I see how to hide the Batch menu item:
Code: Select all
Index: src/BatchCommands.cpp
===================================================================
--- src/BatchCommands.cpp (revision 11173)
+++ src/BatchCommands.cpp (working copy)
@@ -60,6 +60,10 @@
wxT("ExportMp3"),
wxT("ExportOgg"),
wxT("ExportWav")
+# if defined(USE_NYQUIST)
+ ,
+ wxT("Nyquist")//efm5
+# endif
};
static const wxString CleanSpeech = wxT("CleanSpeech");
@@ -410,7 +414,7 @@
// If you find yourself adding lots of existing commands from the menus here, STOP
// and think again.
// ======= IMPORTANT ========
-bool BatchCommands::ApplySpecialCommand(int iCommand, const wxString command,const wxString params)
+bool BatchCommands::ApplySpecialCommand(int iCommand, const wxString command, const wxString params)
{
if (ReportAndSkip(command, params))
return true;
@@ -425,12 +429,14 @@
}
wxString filename;
- if (mFileName.IsEmpty()) {
- filename = project->BuildCleanFileName(project->GetFileName());
- }
- else {
- filename = project->BuildCleanFileName(mFileName);
- }
+ if (command != wxT("Nyquist")) {//efm5 start
+ if (mFileName.IsEmpty()) {
+ filename = project->BuildCleanFileName(project->GetFileName());
+ }
+ else {
+ filename = project->BuildCleanFileName(mFileName);
+ }
+ }//efm5 end
// We have a command index, but we don't use it!
// TODO: Make this special-batch-command code use the menu item code....
@@ -488,6 +494,14 @@
return false;
#endif
}
+# if defined(USE_NYQUIST)//efm5 start
+ else if (command == wxT("Nyquist")) {
+ Effect * f = EffectManager::Get().GetEffectByIdentifier(wxT("Batch"));
+ if (f != NULL) {
+ return ApplyEffectCommand(f, command, params, true);
+ }
+ }
+# endif//efm5 end
wxMessageBox(wxString::Format(_("Command %s not implemented yet"),command.c_str()));
return false;
}
@@ -511,7 +525,7 @@
return true;
}
-bool BatchCommands::ApplyEffectCommand( Effect * f, const wxString command, const wxString params)
+bool BatchCommands::ApplyEffectCommand( Effect * f, const wxString command, const wxString params, bool pbNyquistChain)
{
//Possibly end processing here, if in batch-debug
if( ReportAndSkip(command, params))
@@ -525,7 +539,11 @@
project->SelectAllIfNone();
// NOW actually apply the effect.
- return project->OnEffect(ALL_EFFECTS | CONFIGURED_EFFECT , f, params, false);
+ //efm5 start
+ int type = ALL_EFFECTS | CONFIGURED_EFFECT;
+ if (pbNyquistChain) type = PLUGIN_EFFECT | PROCESS_EFFECT;
+ return project->OnEffect(type, f, params, false);
+ //efm5 end
}
bool BatchCommands::ApplyMenuCommand(const wxString command, const wxString params)
Index: src/BatchCommands.h
===================================================================
--- src/BatchCommands.h (revision 11173)
+++ src/BatchCommands.h (working copy)
@@ -28,7 +28,7 @@
bool ApplyCommand( const wxString command, const wxString params );
bool ApplyCommandInBatchMode(const wxString & command, const wxString ¶ms);
bool ApplySpecialCommand(int iCommand, const wxString command,const wxString params);
- bool ApplyEffectCommand(Effect * f, const wxString command, const wxString params);
+ bool ApplyEffectCommand(Effect * f, const wxString command, const wxString params, bool pbNyquistChain = false);
bool ApplyMenuCommand(const wxString command, const wxString params);
bool ReportAndSkip( const wxString command, const wxString params );
void AbortBatch();
Index: src/Menus.cpp
===================================================================
--- src/Menus.cpp (revision 11173)
+++ src/Menus.cpp (working copy)
@@ -890,7 +890,8 @@
c->AddSeparator();
names.Clear();
for (size_t i = 0; i < effects->GetCount(); i++) {
- names.Add((*effects)[i]->GetEffectName());
+ if ((*effects)[i]->GetEffectName() != wxT("Batch"))
+ names.Add((*effects)[i]->GetEffectName());
}
c->AddItemList(wxT("EffectPlugin"), names, FN(OnProcessPlugin), true);
}
This does not solve the situation of wanting to apply two or more different Nyquist plug-ins during a batch command chain (one could add Nyquist2, Nyquist3 etc. batch commands--each using a different user-edited chain2.ny & chain3.ny etc. plug-in; this would require re-compiling Audacity). There are LOTS of TODOs & FIX-MEs in the batch command code especially in relation to effects. It may not make much sense to go further than this until these are resolved (and that will require major changes to the Effects code!)