Play~ distorted sounds

Carlo's icon

Hi all,
I was simply using line~/play~ into a patch to read a buffer (some minutes long) at normal speed, when I heard that some strange kind of subtle distortion (with high partials) was produced after few seconds.
I made a test with a sine wave (440 Hz, 40 seconds) and found out that a process of growing distortion, with more and more partials appearing every some seconds, occurs during the playback.
(I've attached a snapshot of the sonogram)
I tested the same sine wave and sound files with sfplay~ and they played back fine. I also tried rebooting and using an old version of play~ from 2009, without any success.
How can I do to avoid this distortion with play~?
Am I missing something?
Thank you very much!

P.S.
I am running Max Msp 5.1.4 on a 2007 2,33 Ghz MacBookPro, Hitachi 500Gb 7200rpm HD, with Osx Snow Leopard 10.6.4

987.Sine_wave_440Hz.png
png
AlexHarker's icon

This is a well known floating point precision issue with play~ and longer files. If you search the forums you will find all kinds of info on it.

For longer files do not use play~ - use groove~, or if you are unable to get the functionality you need with groove~ then download jkc's high res objects (can be found on the share pages) and use the hi res version of play~ (hr.play~).

Anyway, [play~] will always have this problem, unless max signals go 64bit at some point, so better use another object...

Alex

Carlo's icon

Got it, thanks!
Do you know what would be the file duration limit not to be excedeed to avoid the floating point precision issue with play~?

Noob4Life's icon

depends on the sampling rate. at 44.1k:
"...about 6 minutes... if I remember correctly 32bit floats can only represent every integer up to 2^24...."
see username 'Roth' post from this thread:
https://cycling74.com/forums/buffer-play-record-buffer

(search 'play' on these forums, you'll find more(searching for a tilde(~) messes up the search results, so you just have to search for 'play' to get results for 'play~')

AlexHarker's icon

Yes - *theoretically* you can be sample accurate with a 32 bit float for around 380 secs with 44.1kHz audio.

However, the line is driven in milliseconds, then this value must be converted back to samples inside the play~ object. This is another potentially inaccurate conversion. Also, as we approach this limit, the subsample accuracy approaches zero, which means that any inaccuracy places us a whole sample (or close to that amount) out, which is a long way, so realistically it's less than 6 minutes, but it's hard to say by how much.

All the above accounts only for playback at original speed, or any integer multiple. For, any non whole number the situation is a *whole* lot worse. Likewie for higher sample rates.

Personally, due to the above I avoid play~ entirely nowadays. Groove~ i almost always more convenient, as well as being more accurate if I really need to drive the sample directly I use hr.play~ and associated hr objects. There's no point in dropping accuracy needlessly...

A.

Carlo's icon

Well, it's strange, but in the test I made with the sine wave (440 Hz, only 40 seconds long), the distortion started just after 2 seconds and grew from then on. If you have a look at the picture I posted above, you can see it clearly.
It seems, if it's not my computer's fault, that the object play~ can't be used at all, apart from sounds shorter than 2 seconds.

I will use groove~ or JKC's HR objects from now on, but.. is Cycling74 doing something for this issue?
At least the timing issue should be mentioned in some way in the help, I would say.. maybe is for that reason that the example in the help is just 1 second long?..

AlexHarker's icon

Post the sound and the patch and other people can verify if they experience the problem or not.

It does sound bad if you are having issues after only 2 seconds - if you post an example and this is the case then we can at least see a worst case scenario for play~ (pure tones are likely to be the worst case) - if others do not see the same problem then maybe it is something else...

Carlo's icon
Max Patch
Copy patch and select New From Clipboard in Max.

Hi all,
here's the example patch which produces the spectrum I've posted above.
Can someone give it a try?

Thank you!

1082.sinewave40secs.zip
zip
Tim Lloyd's icon

I've just tried your patch, and the distortion becomes visible in spectroscope~ around 1 second after starting playback. I think this is simply the nature of play~, like AH says. It would be great if Max went 64-bit, but this isn't likely to happen any time soon.

AlexHarker's icon

Yep - I see the same (and hear it) here as well. Yes - play~ is not good for this. I suspect you simply don't hear the errors anywhere near as early in more complex sounds. A quick check with groove~ shows that this doesn't happen (or at least not within the first 30 seconds or more...

Roth's icon

Yes - *theoretically* you can be sample accurate with a 32 bit float for around 380 secs with 44.1kHz audio.

However, the line is driven in milliseconds, then this value must be converted back to samples inside the play~ object. This is another potentially inaccurate conversion. Also, as we approach this limit, the subsample accuracy approaches zero, which means that any inaccuracy places us a whole sample (or close to that amount) out, which is a long way, so realistically it's less than 6 minutes, but it's hard to say by how much.

Max Patch
Copy patch and select New From Clipboard in Max.

The ms to samples conversion is another potential source of error that as AlexHarker points out doesn't allow us to get to the theoretical limit of sample accuracty with 32-bit floats. I don't think this is the source of the problem with distortions appearing almost immediately as any error from this converion would take at least a minute (maybe more, 360 seconds we are only off by one sample) because everything rounds correctly. This patch demonstrates that:

I have a feeling it is [play~]'s sample interpolation that is causing the distortion. While I don't know for sure, I'm guessing that [groove~] doesn't have this problem because (perhaps to save CPU) it is smart enough to not interpolate when playback speed is ±1.0.

AlexHarker's icon

@Roth

"While I don't know for sure, I'm guessing that [groove~] doesn't have this problem because (perhaps to save CPU) it is smart enough to not interpolate when playback speed is ±1.0."

98% sure it isn't - you won't see a difference in cpu usage even with 100s of objects (unless things have changed recently). Trust me - I've looked into this a lot.

Also, correct rounding is all well and good, but your patch isn't going to reflect what happens inside play~ where the value in samples will most likely be truncated and then the fractional part taken and used used for interpolation.

It is possible that the interpolation is faulty. I am certainly surprised at the poor performance. I have an alternative object here that I coded myself and believe to be accurate. It's not quite like play~ in design, but I should be able to use it to test against and see how results vary. I'll post back my findings when I'm done.

AlexHarker's icon

OK, so I checked this out. I have two very similar objects. One has internal double precision drive (like groove), the other is driven with a float signal in the range zero to one (somewhat like play~ but scaled differently).

Both externals are capable of cubic interpolation (as play~ and groove~).

With float drive I get the same level of artefacts as early on as with play~. With the internal drive objects I don't see artefacts. Therefore, I think it's fair to conclude that this is most probably an issue due to the inaccuracy of float precision drive being highlighted by particularly pure sound.

We don't need to be off by a sample to get artefacts, we only need to have some jitter in the error a a high enough level to create harmonics that aren't there....

Anyway, I'll never use play~ for audio again....

Roth's icon

@Roth

"While I don't know for sure, I'm guessing that [groove~] doesn't have this problem because (perhaps to save CPU) it is smart enough to not interpolate when playback speed is ±1.0."

98% sure it isn't - you won't see a difference in cpu usage even with 100s of objects (unless things have changed recently). Trust me - I've looked into this a lot.

Glad you corrected me there: that was some blind speculation without any real testing.

Also, correct rounding is all well and good, but your patch isn't going to reflect what happens inside play~ where the value in samples will most likely be truncated and then the fractional part taken and used used for interpolation.

True. I tried to do some simple testing in MSP (didn't save a patch to post) and got the impression that truncation would cause even worse distortion--but that testing was not thorough, so I am not convinced of that point either I suppose.

We don't need to be off by a sample to get artefacts, we only need to have some jitter in the error a a high enough level to create harmonics that aren't there....

I am not sure I follow your meaning (maybe I'm too tired to think about this now). Am I right that what you are talking about is an issue brought on by interpolation? Clearly I'm too tired to even type out what I think you mean so I'm going to leave it here, but you are right, I was being hasty earlier and did not think this all the way through.

Anyway, I'll never use play~ for audio again....

Yeah, same here, although this has made me curious to think about exactly what is going on. Hopefully I will have time this weekend to write my own version of [play~] (indexed by ms) to experiment with what is going on.

AlexHarker's icon

" got the impression that truncation would cause even worse distortion"

yes - it would, but in reality we read 4 samples around the truncated one, and use the fractional part to interpolate between them - ie. estimate a value that lies between samples. Otherwise, varispeed playback wouldn't work.

"I am not sure I follow your meaning (maybe I'm too tired to think about this now). Am I right that what you are talking about is an issue brought on by interpolation? "

What I'm saying is this. The interpolation of both externals is I believe the same. However, you first have to determine which samples to interpolate between, and at what point you want to take a value. You can think about it on the time axis as being a series of points that ideally for a playback speed of 1 hits every sample exactly. In reality what happens is that rather than an equal grid spacing of 1 sample, there is an unevenness to the grid (some points are a little wider than one sample apart, some a little less) due to calculation error. If that unevenness is large enough it will create aliasing, or other distortion (visible here as frequency components that are not in the original sample). That unevenness does not need to be anywhere near one sample out, half a sample out would be pretty terrible.

What this thread shows is that the error is actually fairly bad for even very short periods of time. I had previously thought play~ to be reasonable for samples sub one minute in length, although I still avoided using it. I've now revised that opinion.

Roth's icon

Ah, thanks Alex, that was along the lines of what I was thinking, just couldn't explain it back for clarification.