ruffle-rs / ruffle

A Flash Player emulator written in Rust
https://ruffle.rs
Other
15.76k stars 820 forks source link

miscalculated `Sound.duration()` (non-standard sample rate) #6673

Open ousia opened 2 years ago

ousia commented 2 years ago

The attached image shows how Flash Player and Ruffle display the Sound.duration() of the audio contained in https://www.free-culture.tk/lfc-presentation.swf:

dm-rfflvfp

As @Herschel has explained many times, the problem here is that the sound file has a non-standard sample rate 16kHz (although it is tagged in the SWF animation as being 11kHz).

For both Sound.position() and Sound.duration(), sample rate should be read from theMP3 file itself (not from SWF tagging).

BTW, I think there is no need to attach a sample file (it’s over 5MB). Let me know, if you need it.

Many thanks for your excellent work.

ousia commented 2 years ago

Even after #4273, miscalculated Sound.duration() is still there.

ousia commented 2 years ago

Although tagged as MP3 11Khz 16Bit mono in the SWF file, the actual sample rate is 16kHz.

n0samu commented 2 years ago

As a workaround, why not modify the SWF file so the sound sample rate is tagged correctly?

ousia commented 2 years ago

As a workaround, why not modify the SWF file so the sound sample rate is tagged correctly?

Many thanks for your reply, @n0samu.

Well, in that case I’d have to modify the MP3 file itself. According to the SWF File Format Specification:

The sampling rate. This is ignored for Nellymoser and Speex codecs. 5.5kHz is not allowed for MP3. 0 = 5.5 kHz; 1 = 11 kHz; 2 = 22 kHz; 3 = 44 kHz.

In theory, 16kHz would be an invalid value. But in practice, the official player from Adobe had no problem with it.

If Sound.position() works right with this value for sampling rate, I think it would be consistent to make it work right with Sound.duration().

Many thanks for your help.

n0samu commented 2 years ago

Ah that makes sense, I agree with what you're saying then

ousia commented 2 years ago

@n0samu,

I think this may be an easy fix, but I’m afraid I cannot code (in almost any programing language known to humankind 😅).

So, I will have to wait for the fix.

ousia commented 1 year ago

BTW, this prevents having a timer with total time in sound.

ousia commented 1 year ago

This is what I meant in my previous comment:

wrong-timer-fs8

Total playing time is wrong, see bellow:

right-timer-fs8

Buttons are also missing in Ruffle, because of indirect reference (#5492).

Many thanks for your help and happy holidays.

ousia commented 1 year ago

Just in case this might help to fix this issue.

In 3dfa5ade1852f264b6a15fb274feb50a11f90ede, @Herschel fixed distorted playing in MP3 with non-standard sample rates.

The sample rate is read from MP3 itself for Sound.position(), but from the tag in SWF for Sound.duration().

This might be where it is defined https://github.com/ruffle-rs/ruffle/blob/eb35d6217b90be9c40bf7fa6d91a516c5c8c1157/core/src/backend/audio.rs#L177

I wonder whether it should be something similar to:

let sample_rate: f64 = sound.decoder.sample_rate.into();

Of course, this is probably wrong, since all of this is totally Greek to me.

But I wonder whether this might help to advance a PR to fix Sound.duration() for non-standard frame rates.

Many thanks for your help.

n0samu commented 1 year ago

As far as I can tell, there is no change that simple that could fix this problem. audio.rs is dealing only with reading the SWF tags and is treating the actual MP3 data as opaque. Meanwhile the MP3 decoder is feeding the MP3 data into Symphonia frame by frame. I think Ruffle throws away all of the MP3 metadata and it would be a fair bit of work to get it reading that metadata just to correctly handle this type of corner case.

Oh, and the reason why Sound.position() is working correctly is because keeping track of that is necessary to correctly play the sound at all. Keeping track of this value does not require reading the MP3 metadata.

And regarding the commit from Herschel that you linked, it's true that Ruffle's audio decoder now uses the correct sample rate, but reporting it to ActionScript is a different matter entirely. The ActionScript values are set when the SWF tag is read, not when the sound is streamed.

None of this means that the task is impossible or even very difficult, but it's not as simple as it sounds.

ousia commented 1 year ago

@n0samu, many thanks for your explanation.

Oh, and the reason why Sound.position() is working correctly is because keeping track of that is necessary to correctly play the sound at all. Keeping track of this value does not require reading the MP3 metadata.

I thought the same logic could be applied to Sound.duration() (not reading the MP3 metadata in SWF to provide the value).

Many thanks for your help again.

Klymins commented 1 month ago

Out of topic but I can't use a 16kHz MP3 stream in a Flash file (it simply doesn't play on the original Flash Player), can you tell me how can you do that while keeping the original Flash Player able to play it @ousia ?

ousia commented 1 month ago

@Klymins,

swfc from SWFTools does that. Attached animation (remove the extra .txt extension) plays fine with Adobe Flash Player.

Be warned that sound quality is not great. I only reported this issue to be able to handle already generated animations.

Are you interested in fixing the issue? Many thanks for your help.

Klymins commented 1 month ago

Thanks @ousia , I don't have enough knowledge to solve this problem.

I could not run SWFC but I detected that the original Flash Player can't play 16kbps 16kHz MP3's those encoded by MP3Enc. Do you know why the original Flash Player is unable to play 16kbps 16kHz MP3's those encoded by MP3Enc (it's the MP3 encoder that is included in the original SWF creating programs)?

By the way, I did not detect any degrade on the sound quality, what do you mean by "sound quality is not great"?

ousia commented 1 month ago

@Klymins,

I use lame -m m -b 16 sound.wav to generate sound.mp3.

A binary for Win64 may be obtained from https://www.rarewares.org/mp3-lame-bundle.php.

Depending in your actual recorded sound, 16kbps may be too low quality.

I hope it helps.

Klymins commented 1 month ago

I thought you were talking about the quality of playing 16kHz content at 11025Hz, I understand now. But 16kbps is actually pretty standard in Flash (many Flash movies use it) and I think it's pretty good when it's encoded by MP3Enc at 11025Hz, thanks.