switch to Spectogram display by code

Hi,

The whole message cracking of Audacity is quite complex and I haven’t found a layout which explains the headlines (if there is one). Therefor this question:

I am looking for a message or a software command or something alike with which I can write C++ code (Windows) to switch between the Waveform and Spectogram display like when you use the mouse to hit those menu entries in the Trackinfo screen of the GuiStereoTrack.
Until now I arrived at the code (TrackPanel::HandleLabelClick(wxMouseEvent & event) ) which causes the popup menu to popup. But in debug mode that popup screen is already gone before I can hit the menu item ‘Spectogram’ to find out what happens next, or what message is constructed for doing the switching job.

Any help is awfully welcome!
regards,
jerome42

Just another reminder that no Audacity developers use the Forum.

I would guess Ed (user Edgar) may be able to help you.

Anyhow, does setting a breakpoint after the menu disappears help?

I have not looked, but is there an entry in GetAllMenuCommands in scripting:
http://manual.audacityteam.org/o/man/scripting.html ?


Gale

The track drop down menu uses index numbers for each menu option.
Line 7170 of TrackPanel.cpp sets the display mode after an option has been selected in the menu:
Waveform: id=1
Waveform (dB): id=2
Spectrogram: id=3
and so on.

Does that help?

Yes, I remember you warned me earlier, but I didn’t find how to access the developers group.

No, setting a breakpoint after the menu disappears, doesn’t help. As far as my understanding goes for the moment, the popup menu is constructed on the fly by wxWitgets, and I guess, it’s wxWitgets which generates the menu driven message that finally activates the switching done by TrackPanel::OnSetDisplay(wxCommandEvent & event). So the whole thing is done and cleared already when the debug execution is stopped at the breakpoint you suggested.

Scripting (from an external source) is also no option, as this switching to Spectrum mode is just one task of several others in my planned C++ code.

Anyhow, thanks for responding,
jerome42

Hello Steve,

Well, you guided me anyhow some steps in the right direction.
Strange is that we do not seem to have exactly the same sources.
My line 7170 of trackPanel.cpp shows: TrackPanel::OnFormatChange(wxCommandEvent & event) and this code is not touched when switching to Spectrum view.
A few lines earlier on 7125, I found TrackPanel::OnSetDisplay(wxCommandEvent & event), which indeed does the switching.
Invoking this subroutine directly is problematic, because it is a protected routine, not accessible from outside and it also requires a correctly constructed event, although that could be circumvented by a dummy construct.
Both approaches require some changes in the TrackPanel code and that will not match the quality requirements(?) of the Audacity team, I guess.
The best solution remains a message, but how should that look like.
So I am afraid I have to do some diving into the wxWidhets code.

thanks and kind regards,
jerome42

Yes, that’s what I was looking at.
In the current svn sourcecode that is line 7160 and line 7170 is wt->SetDisplay(id);
Development work is usually done against svn head so that it’s not out of date before you finish :wink:

You would have to subscribe to the audacity-devel mailing list:
https://lists.sourceforge.net/lists/listinfo/audacity-devel

however you are very unlikely to get help unless you give them some idea of the purpose of your code changes and how this might be useful to Audacity.

Steve is gradually getting into Widgets and C++ I think (thanks, Steve) but generally there may be many questions you have that you won’t find an answer to here.


Gale

“Gradually” being the important word :slight_smile: Although I’ve made quite a lot of Nyquist plug-ins I’m a noob with C++ and WxWidgets.

Hi Jerome42!

I got your PM and apologize for not chiming in the earlier on this thread; the subject matter is way out of my area of familiarity - I’ve never actually used the Spectrogram display. A quick read through of your PM and this thread leaves me a little bit in the dark (not an uncommon occurrence - I often have a hard time understanding other folk’s descriptions when Gale and Steve seem to find them obvious ); I have some spare time for the next couple days so will try to get up to speed and see if I can help you out. More later…

Hi Edgar,
You made my day already now, but I think you know best how to keep enough spare time for yourself!
regards, jerome42

I just grabbed SVN HEAD (commit 12244) and had a look at:
virtual void OnSetDisplay (wxCommandEvent &event);
note that this is Protected not Private.

Depending on where you need to call this function from there are three routes to do so:

  1. If it will only be called from another method of “class AUDACITY_DLL_API TrackPanel:public wxPanel” just do so.
  2. Since it is Protected you could make the calling class a “Friend”.
  3. The preferred C++ method would be to make A Public accessor method. This accessor would build a wxWidgets Event with the appropriate value(s) - this is very easy to do.

Now all you need to do is figure out how to “trigger” this action from the GUI. I will need a lot more specifics - what has the user done (exact steps) to get to the point where this switch over from Waveform to Spectrogram occurs; if there is an automatic process in your code which decides to switch over, I will need to see that code (a patch would help).

I am on my way to the gym… Back in a few hours…

Hi Edgar,
There is little specifics to know about the foregoing user actions. Just imaging that you have loaded a wave file and you then activate the Click Removal Effect of Craig DeForest (see Effect menu) and the first thing you want Craig’s code to do is switch to the Spectogram display. The history of user actions isn’t relevant then for that display switching. And there is no harm in case the Spectogram display was already selected.
Of couse you might try to effectuate that switching by direct software calls without the event mechanism, but I think that events may more easily circumvent all those typical restrictions arising from the C++ structure like protected, AUDACITY_DLL_API etc.
Anyhow that is my experience with windows SendEvent() code.
If a menu event can be created to switch the display, it must also be possible to create a custom event for the same purpose.
We have a custom event example code available in wxWidgets. As always with examples, the main problems arise in details not covered by the example and in this example the problem is: what should be used in stead of ‘this’ in codeline 379 of event.cpp (wxWidgets) to get that event arrive at the BEGIN_EVENT_TABLE(TrackPanel, wxWindow) in TrackPanel to get OnSetDisplay() activated.
More I can’t say about it for the moment.
Does this clarify my point a bit?

I don’t work with and have by consequence no experience with SVN HEAD. I downloaded the sources some time ago and work only with them.
The modification I have in mind will be made in a new class, which can be developed independantly and apart from SVN.
At the moment there is little code to show you. Once finished I will send the whole thing to the Audacity Team.
If you like I will try to write (tomorrow!) some code you might insert in Craig’s ClickRemoval.cpp to show you the problem.

Well, when you return from gym, I am about to go to bed, so don’t hurry!
Till next contact,
jerome42

Okay, wake up ! Now I have a better idea of what you are trying to accomplish and can recommend some steps and give you some pointers to the appropriate code.

At some point it’s going to be mandatory for you to install an SVN client; TortoiseSVN is your best bet as it is known to work extremely well with Windows and Audacity, for some handholding details check this out:
http://www.hometownband.org/Audacity/CompilingAudacity.html#TortoiseSVN
this will allow you to check out the current SVN HEAD and create patches against it. This is the only way that Gale, Steve or I will be able to work with you at a code level.

For now I can give you some pointers so that you may get started using the source code which you currently have. The first thing you want to do is create a trivial, do-nothing Effect; this Effect will do nothing more than open an “Hello, world” dialog for the user to dismiss. The best way to do this will be to copy one of the other C++ Effects and strip out all the audio processing code. You may either add a new source and header file to the current project or (simpler) add your code to existing source and header files; since you are working with a Click Removal concept let’s assume that you are going to add your code to the two existing files:
srceffectsClickRemoval.h and srceffectsClickRemoval.cpp

In the header file define a new class (name it something appropriate):

class EffectJeromeClickRemoval: public Effect {
public:
   EffectClickRemoval();
   virtual ~EffectClickRemoval();
[…]
}

just to make sure that everything is going well make no other change other than to copy the entire class and change its name.

in the source file you will need to copy all of the original class’s methods and then change the class name in the copies:

EffectClickRemoval::EffectClickRemoval()
{
   windowSize = 8192;
//   mThresholdLevel = 200;
//   mClickWidth = 20;
   sep=2049;

   Init();
}

EffectClickRemoval::~EffectClickRemoval()
{
}

becomes:

EffectJeromeClickRemoval::EffectJeromeClickRemoval()
{
   windowSize = 8192;
//   mThresholdLevel = 200;
//   mClickWidth = 20;
   sep=2049;

   Init();
}

EffectJeromeClickRemoval::~EffectJeromeClickRemoval()
{
}

similarly for all the other methods of the original class. Now, compile this code but do not bother to run it, you are just making sure that it will compile your copied class. Next, let’s make a tiny change to your new code (small steps at this stage of the game are critical, otherwise, when something goes wrong, and it will, it will be much more difficult to pinpoint problem). From here on out, if I give you a line number you will need to take it with a grain of salt because I am going to be working from a totally different code base; I will try to give you enough information that you can use Search to find the appropriate section of the code!

If you had run the previous compiled code it should have ended up putting 2 identical entries in the Effects menu. Let’s make a tiny change so that your entry on the menu has an unique and distinct name; look in your new class’s CTOR method:

JeromeClickRemovalDialog::JeromeClickRemovalDialog(EffectClickRemoval *effect,
                                       wxWindow *parent)
:  EffectDialog(parent, _("Jerome Click Removal"), PROCESS_EFFECT),
   mEffect(effect)
{
   Init();
}

and make some change to the string:
_(“Jerome Click Removal”)
to make it unique (will worry about descriptively accurate later). Compile the code again and run it this time, look at the Effects menu to ensure that your version of the Click Removal Effect shows up.

This feels like a good place for me to stop; the next step will be stripping out the current audio processing code (unless you expect to be using it mainly intact) and adding the “Hello, world” dialog so that we may know that we have functionally interrupted the code in the proper place and that everything is going smoothly. Pardon me if I am being obsessively detailed here, I have no idea of your experience level!

I was just notified that I may need to go out of town on business leaving about 12 hours from now and being gone at least one day. If you reply here and don’t hear anything more from me right away don’t be surprised.

That CompilingAudacity.html business I did already in last january and I got it compiling.
On jan 20 I send in the question "are there others developing the same software patches?"with a summary of my plans.
On jan 24th you answered my first question: Where in code the length of current track can be found? Remember?
On jan 27 your answer resulted in two extra buttons in the Selection Panel and I send in a screenprint. But Gale wasn’t very happy with it.
Some more on my level of experience:

  • started C programming in the late 80’s with the famous Borland Dos compiler just for hobby.
  • learned myself C++ in the 90’s, used all microsoft compilers form VC4 to VC10 nowadays.
  • never used microsoft MFC for GUI programming as it was not real OO.
  • worked as a professional programmer from '95 on, mostly for insurance companies to create their backend caLculation modules.
  • Created my own GUI development kit for windows, by wrapping the win32 api’s.
    Got retired and continued programming for fun and got interested in repairing my damaged vinyl recordings.
    Created a clickremoving program with my own GUI kit.
    Stopped that development when I found the ClickRepair program of Brian Davies, the retired math professor from Australia(?).
    Discovered soon that his ClickRepair, although the best available at the moment, had some drawbacks.
    Found Audacity and thought it would be easier for me to use Audacity as basis for further clickremoving experiments, especially because of the excellent large wav file management, which I was too lazy to develop for my own GUI kit.

So: yes, I have quite some experience, but have too little knowledge of the C++ theory and the fine details of the language compared with those who learned C++ programming at university age and level. Moreover: after reading the relevant topics (e.g. Bjarne Stroustrup) today, they quickly slip out of my retired memory.

I never liked working under sourcecontrol systems, always problems. Yes did it a few times in the late 90’s.
So: yes, already 2 month ago, I followed all steps in http://www.hometownband.org/Audacity/CompilingAudacity.htm except for the installation of TortoiseSVN, which I installed only this morning at your request.

You gave me quite a bunch of homework and I respond with quite a lot of text :wink: .
Like you I have some other things to do next days.
Playing around with TortoiseSVN and responding to your other remarks and advises will have to wait, although: the new class you proposed has been created already some days ago and has indeed a (working) entry in the Effect menu.

regards, j

and then there is wxWidgets which is a whole new adventure :wink:
I’ve only just started with C++ and wxWidgets so you have many years head-start on me, but I have a lot of sound engineering experience and I’m very familiar with Audacity (and other audio software) so I’ll chip in if I have anything to contribute :wink:

You are very welcome!

It is a pain to keep your code in step with SVN HEAD and only really necessary if you hope to have it accepted into Audacity’s project by the Developers or if you need to do any shared development as all developers must be working from the same source code. My understanding was that you were hoping to present something for consideration by the Developers - that’s why I suggested that TortoiseSVN would eventually be required.

It seems like you have gotten past all the early steps you now need to decide where the switch (from Waveform to Spectrogram) is going to be placed you could place it in the dialog code (look in the header file for the class ClickRemovalDialog) so that it would switch behind the dialog - this makes the most sense to me. You are going to have to somehow make this dialog aware of the TrackPanel from which it originates. Probably the only away to do this is via the pointer to the Effect (EffectClickRemoval *mEffect;) which seems to be the only external reference, an Effect has a pointer (mParent) to its parent window which you will need to cast back to a Audacity Project (window)

class AUDACITY_DLL_API AudacityProject:
[…]
TrackPanel * GetTrackPanel(){return mTrackPanel;};

so, it would look something like this:

void ClickRemovalDialog::PopulateOrExchange(ShuttleGui & S)
{
   Effect * baseEffect = (Effect *)mEffect;
   wxWindow * baseWindow = baseEffect->GetParent();//new public method of Effect class
   AudacityProject * currentProject = (AudacityProject *)baseWindow;
   //or  to save a few steps but be less general...
   //AudacityProject * currentProject = baseEffect->GetProject();//new public method of Effect class
   //Which has the casting built-in
   TrackPanel * trackPanel = currentProject->GetTrackPanel();//will need to add #include "../TrackPanel.h" above
   wxCommandEvent anEvent(2);
   trackPanel->OnSetDisplay(anEvent);

Warning: I have not tried to compile or run the above!

@Jerome, although it’s not very mentally stimulating, one way to do what you want without programming would be to duplicate the track then have one view as waveform and one view as spectrogram. This was in fact similar to what Vaughan did in his Thinklabs modification of Audacity 1.2.6, where clicking a “Display” button created an additional spectrogram view of the track, albeit within the same track container. See http://www.thinklabsmedical.com/software-download.html .


Gale

Hello Gale, very interesting what some people do with Audacity and indeed it is a proof of succes of the A-team.
I love me also recommended once you might have mentally more stimulating mails :wink:.
regards, jerome42

Code: Select all
void ClickRemovalDialog::PopulateOrExchange(ShuttleGui & S)
{
Effect * baseEffect = (Effect *)mEffect;
wxWindow * baseWindow = baseEffect->GetParent();//new public method of Effect class
AudacityProject * currentProject = (AudacityProject *)baseWindow;
//or to save a few steps but be less general…
//AudacityProject * currentProject = baseEffect->GetProject();//new public method of Effect class
//Which has the casting built-in
TrackPanel * trackPanel = currentProject->GetTrackPanel();//will need to add #include “…/TrackPanel.h” above
wxCommandEvent anEvent(2);
trackPanel->OnSetDisplay(anEvent);

Hi Edgar,
We were quite on the same track with the above codelines, you by hart and I by cumbersome try and error.
As I saw the code

AudacityProject *p = GetActiveProject();

being used at many places in Audacity to get acces to many different properties, I decided to use it also and found that it produces the same pointer as your code:

AudacityProject * currentProject = baseEffect->GetProject();

It comes however with a small but nasty problem: baseEffect->GetProject() gives a zero pointer error and I still couldn’t find out why. The pointer to be returned, is initialised in Effect::DoEffect() line 99: mParent = parent; There you can check that is is the same pointer as the one GetActiveProject() returns. But once baseEffect->GetProject() wants to grab that pointer, it’s gone. Propably out of scope in the meantime.

Anyhow we can simplify your code to

AudacityProject *p = GetActiveProject();
TrackPanel * trackPanel = p->GetTrackPanel();
 wxCommandEvent anEvent(2);
 trackPanel->OnSetDisplay(anEvent);

Running this code is jammed by the wxASSERT’s in OnSetDisplay() and the fact that ‘mPopupMenuTarget’ there is not correctly initialized by our code (apart from the fact that I think that anEvent(2) should be written as anEvent.SetId(2023)).

For the moment I have no inspiration on how to go on and I am even doubting if this is the right approach because Audacity requires every property to be 100% right and it looks quite a job to get that done now Audacity itself is not very helpful with that.
Maybe I should concentrate on the wxWidgets menu system.
Gale draw my attention to a Thinklabs modification, but if Thinklabs did write code we could use here, it’s not available as their modification is not open source as far as I could see.
Regards, Jerome42