Here are some approaches to consider:
Transmit a "chirp" signal where the frequency transmitted is slowly ramped from a low frequency to a high frequency (repeatably as a sawtooth or ramp pattern).
Multiply the received chirp with a replica of the transmitted chirp. The frequency of the resulting signal (after low pass filtering) will be proportional to the delay. An FFT is often used at this point to measure the frequency, but other frequency estimation techniques can also be used.
Review more information on "FMCW" radar for more details on this approach. Such as at this link: https://en.wikipedia.org/wiki/Continuous-wave_radar
Ranging with PRN sequences
Transmit a pseudo-random noise sequence that can be generated with linear feedback shift registers as a modulated signal in your audio band (FSK or BPSK are good choices).
Cross correlate the received sequence with the transmitted sequence efficiently with FFT's using xcorr = ifft(fft(rx)*fft(tx)), here assuming both tx and rx are real.
Note if resources are severely constrained but you have time on your side, you can perform the cross correlation over time: To demonstrate this I will give an example:
Given you want to measure 1 cm to 1 m, means you want to measure delays from 300 us to 3ms, this means we would want to use an LFSR that does not repeat from 3ms. Further for an accuracy of 1cm, a chip rate of 4KHz should be adequate. This means an 11 chip barker code such as the one I list below would work but you could also explore longer LFSR sequences.
11 bit barker code: 1 1 -1 1 -1 -1 -1 1 1 1
Note, the unique property of this code like other LFSR maximum length sequences, the cross correlation is maximum when the code is aligned (in this case = 11) and -1 every where else.
Using the above barker code with a 8Khz carrier, When the code is 1 transmit the 8KHz carrier in-phase and when it is -1 transmit it 180 degrees out of phase, thus you are mulitplying your carrier by plus or minus 1. (Note that an XOR gate is such a multiplier if you want to make this "ultra-low" resources). Because of the relationship I used between carrier and code, there will only be two cycles of the carrier for every code chip transmitted. The sequence is transmitted continuously.
At the receiver multiply in quadrature the received sequence with the same 8KHz carrier you already generated- meaning two multipliers where one of the received sequence is multiplied by the carrier directly and the other is multiplied by the carrier shifted 90 degrees at 8KHz, a quarter cycle. (again you could do this most simply with square waves and XOR gates by hard limiting the received signal and applying a DC offset to be midrange in the XOR gate, which would really be an "analog" solution as we would be using the XOR gate as an analog multiplier similar to a mixer- given you are using a sound card which will essentially be your A/D converter a digital multiplier will be more likely). The reason for the two multipliers is due to various phase delays, one path will be at a minimum when the other is maximum but with the two you will always have a signal to detect.
The multiplier output is followed with a simple moving average low pass filter, averaging over 1/16KHz = 62.5 us would be a good choice as it will have a null at 16KHz where we expect content of twice the carrier after the multiplication.
After the low pass filter, pass the signal through an FIR filter with the coefficients of the filter equal to your barker code sequence in reverse order. I recommend decimating the received sequence down to twice the chip rate (8 KHz) and then therefore using 2 samples/chip as the coefficients:
1 1 1 1 -1 -1 1 1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1
The output of the filter will be similar to my plot below that is specific to GPS (1023 length sequence), but to show that you will get spikes that repeat at the symbol rate:
If you compare the spike locations with a reference pulse created by your start of your sequence, you have a measure of delay. To do this, start a counter at the start of your sequence, and have a threshold detector on the filter output which stops the counter. Note as I described earlier that two multipliers and thus two filters are used and one output is "I" and the other filter output is "Q". A single estimate of magnitude can be determined using the following simple magnitude estimator:
$$|I+jQ| \approx Max(I,Q)+ 1/2 Min(I,Q)$$