I made my own voice training app
I made my own open source voice training app with PureData.

I've been doing voice training for some time now. Though others have said that they think my voice is fine, I don't, and I realistically still have quite a way to go.
One thing I don't have much difficulty with is pitch, and a lot of this is because of this app I built early in the process. It looks like a bit of a dog's breakfast – it's not really intended to be pretty in any meaningful way, but it does do the job, and was very quick to build and is easy to tweak.
The app is built using PureData, an open source music and audio processing system that's intended for experimentation. It started as basically a nonstandard sequencer that let (mostly avant garde and experimental) musicians and composers do things that ordinary software couldn't. Later it expanded to include audio processing. To use the patch, you need to download and install PureData from here. I use the 'vanilla' version by Miller S. Puckette – if you use a different version my patch file may or may not work, but feel free to experiment.
The app is intended to work with a headset, and does do so very well. It may or may not work with other configurations, but again, feel free to experiment. I happen to use a fairly high end headset that connects via an audio interface, but a simple/cheap headset should work fine.
Start by installing Puredata (also known as Pd). You'll then need to do some minor setup. Start Pd and go to Preferences | Edit Preferences in the menu.

You should be able to keep most of the defaults, but select your headset in the Input Devices and Output Devices sections. Then you can click OK.
Next, download the patch file from the link above (voice-traning-2.pd) and save it somewhere convenient. Load it into Pd from the menu via File | Open. The window should appear. It won't work yet until you turn audio on inside Pd – to do that click the check box next to DSP in the top right. It should look like this:

If all goes well, the app should start working:

It's fair to explain what you can see. The plot on the right shows frequency. The centre line is set at the frequency in Hz in the Low Freq box – here I have it set to 165Hz, which is my personal preference, but most people will probably want to start with 150Hz. As you speak (or make silly noises, like I just did!), the frequency tracks very smoothly. Unvoiced sounds (sibilants, coughs, breath noises, etc.) can sometimes be misinterpreted as high or low pitches – just ignore that. Your current frequency appears in the Freq (Hz) box.
Typically, your voice teacher will give you a pitch you should try to avoid going below. This is the 'Low Freq' setting, and the main idea behind the app. If you go below this frequency, a big X appears in the yellow box, and you should hear a beep in your headset. The idea is to keep practicing, without requiring you to be constantly watching the plot or the frequency readout, knowing that if your pitch drifts a bit low you'll hear the beep and can adjust accordingly. This will all work happily in the background while you chat to people via Zoom, Google Meet, etc., so you can get practice in without having to devote time specifically for the purpose. This was an absolute gamechanger for me personally, given that I have a very busy schedule and tend to have lots of online meetings every working day.
The next trick the app can do is something I built for myself, because I find working on resonance very difficult when I can't really hear myself. The green Enable Delay and Delay Time settings at the bottom left are the second game changer here. When you click the Enable Delay box, you basically hear an echo of yourself anything up to 10 seconds later. This mutes itself automatically while you are talking or making sounds. If I am practicing on my own (doing voice practice exclusively), I'll usually set the delay to about 2000 - 3000 (the units are milliseconds, so this equates to 2 to 3 seconds). If I am using this during a session with my voice teacher, I set it at about 650, which means I get a brief confirmation of the way I was just sounding as soon as I stop. It takes some getting used to, but it does really help.
At some point, when I have the spare time (yes, laughable concept that this is), I intend to code a much better version directly in C++ (or something) that is much cleaner and easier to use, but for now this thing really works and is completely free, so please have at and let me know how you get along with it if you decide to try it.