SFML community forums

Help => Audio => Topic started by: leiradel on November 17, 2015, 10:03:33 pm

Title: Custom SoundStream
Post by: leiradel on November 17, 2015, 10:03:33 pm
Hi All,

I'm trying to write my own sf::SoundStream but the application crashes with a floating point exception after onGetData is called 3 times.

I'm initializing the sound stream to 2 chanels at 22050 Hz, and filling the data chunk with 735 samples of silence. The rationale is:

22050 / 60 = 367.5 frames
367.5 * 2 channels = 735 samples

However, I'm receiving warnings and then the application crashes:

An internal OpenAL call failed in SoundStream.cpp (327) : AL_INVALID_VALUE, a numeric argument is out of range
An internal OpenAL call failed in SoundStream.cpp (327) : AL_INVALID_VALUE, a numeric argument is out of range
An internal OpenAL call failed in SoundStream.cpp (327) : AL_INVALID_VALUE, a numeric argument is out of range
Floating point exception

My methods are:

void initialize( unsigned sample_rate )
{
  SoundStream::initialize( 2, sample_rate );
}

bool onGetData( Chunk& data )
{
  static int16_t buffer[ 735 ] = { 0 };
  data.samples = buffer;
  data.sampleCount = sizeof( buffer ) / sizeof( buffer[ 0 ] );
  return true;
}

I'm not sure my numbers are correct. In fact, I find it odd that there are 735 samples in a chunk since the number of channels is 2, so I think the number of samples should be even...

Any thoughts? What size the buffer array should be? In SFML terminology, on a stereo stream, a sample is really just one sample, or it's two samples?

My SFML version is 2.1.

Thanks,

Andre
Title: Re: Custom SoundStream
Post by: Jesper Juhl on November 17, 2015, 10:41:00 pm
Code: [Select]
  static int16_t buffer[ 735 ] = { 0 };
unless I really need to get more morning coffee to be able to read code, this is initializing the first element of the array to zero - not the entire array. IIRC {0} does not equal memset of the entire array to 0.
Someone correct me if I got this wrong...
Title: Re: Custom SoundStream
Post by: leiradel on November 17, 2015, 11:19:25 pm
Quote
unless I really need to get more morning coffee to be able to read code, this is initializing the first element of the array to zero - not the entire array. IIRC {0} does not equal memset of the entire array to 0.
Someone correct me if I got this wrong...

Hm, I always thought it did. If I change the code to create the buffer on the stack and compile with -O0, the generated code is:

0000000000000000 <_ZN2SS9onGetDataERN2sf11SoundStream5ChunkE>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 81 ec 58 05 00 00    sub    $0x558,%rsp
   b:   48 89 bd 38 fa ff ff    mov    %rdi,-0x5c8(%rbp)
  12:   48 89 b5 30 fa ff ff    mov    %rsi,-0x5d0(%rbp)
  19:   48 8d 95 40 fa ff ff    lea    -0x5c0(%rbp),%rdx
  20:   b8 00 00 00 00          mov    $0x0,%eax
  25:   b9 b7 00 00 00          mov    $0xb7,%ecx
  2a:   48 89 d7                mov    %rdx,%rdi
  2d:   f3 48 ab                rep stos %rax,%es:(%rdi)

I looked for a formal specification on variable initialization and the C specification corroborates the above code: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf), section 6.7.8 :)
Title: Re: Custom SoundStream
Post by: leiradel on November 17, 2015, 11:20:54 pm
I looked for a formal specification on variable initialization and the C specification corroborates the above code: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf), section 6.7.8 :)

Ah, the original code initializes the entire array to zero according to the specification, so = { 0 } isn't needed.
Title: Re: Custom SoundStream
Post by: Hapax on November 17, 2015, 11:29:53 pm
= { 0 } isn't needed.
According to this (http://www.cplusplus.com/doc/tutorial/arrays/), all the remaining elements after the one you gave the zero for, "are set to their default values (which for fundamental types, means they are filled with zeroes)". If you used just "= { }", it does so for all elements. However, if you leave this section off, they "are left uninitialized".

As for "735", you were correct to realise that this isn't acceptable as stereo samples are stored in pairs.
"22050 / 60 = 367.5 frames"
22050 is how many samples per second. What is 60? Your frame rate? 22050/60 would then be samples per frame, not "frames". Obviously half of a sample isn't much use.
Title: Re: Custom SoundStream
Post by: leiradel on November 17, 2015, 11:44:19 pm
As for "735", you were correct to realise that this isn't acceptable as stereo samples are stored in pairs.
"22050 / 60 = 367.5 frames"
22050 is how many samples per second. What is 60? Your frame rate? 22050/60 would then be samples per frame, not "frames". Obviously half of a sample isn't much use.

Yes, 60 frames/second.

Do you know how much it should be for one video frame? 735 (since a stereo sound frame is 2 samples) causes the crash.
Title: Re: Custom SoundStream
Post by: eXpl0it3r on November 18, 2015, 12:10:52 am
Take a look at the example (http://www.sfml-dev.org/tutorials/2.3/audio-streams.php#a-simple-example) in the official tutorial, it uses class member std::vector, so there's no need for scary sizeof() calls and static buffers.
Title: Re: Custom SoundStream
Post by: Hapax on November 18, 2015, 12:11:07 am
60 frames/second.
Do you know how much it should be for one video frame? 735 (since a stereo sound frame is 2 samples) causes the crash.
At sample rate of 22050, you would need 368 samples (per channel) for one frame. Since 22050 is not divisible by 60 exactly, this may be not be exact but a 368th of a frame should not be too noticable.
You could alternate between using 368 and 367 samples for a frame, or you could consider using higher sample rate (44100 is divisible by 60).

It might also be worth noting that it's highly unlikely that your frame rate is ever exactly 60 frames per second.
Title: Re: Custom SoundStream
Post by: Hapax on November 18, 2015, 12:13:05 am
...it uses...std::vector...
This solves a lot of problems including the initialisation of all of the elements discussed above  ;)
Title: Re: Custom SoundStream
Post by: leiradel on November 18, 2015, 12:22:25 am
Take a look at the example (http://www.sfml-dev.org/tutorials/2.3/audio-streams.php#a-simple-example) in the official tutorial, it uses class member std::vector, so there's no need for scary sizeof() calls and static buffers.

I'm more scared of the standard library than of those things ;)
Title: Re: Custom SoundStream
Post by: leiradel on November 18, 2015, 12:24:06 am
This solves a lot of problems including the initialisation of all of the elements discussed above  ;)

Actually my buffer is filled on every onGetData call, that code was just the bare minimum that reproduces the problem.
Title: Re: Custom SoundStream
Post by: Hapax on November 18, 2015, 12:33:32 am
Take a look at the example (http://www.sfml-dev.org/tutorials/2.3/audio-streams.php#a-simple-example) in the official tutorial, it uses class member std::vector, so there's no need for scary sizeof() calls and static buffers.

I'm more scared of the standard library than of those things ;)
Don't be scared. std::vector (http://www.cplusplus.com/reference/vector/vector/) is very simple to use, generally a lot safer than standard arrays, and probably the standard go-to container for most cases.
It was just the "class member" bit that removes the need for the static buffer, by the way.
Title: Re: Custom SoundStream
Post by: leiradel on November 18, 2015, 12:52:48 am
At sample rate of 22050, you would need 368 samples (per channel) for one frame. Since 22050 is not divisible by 60 exactly, this may be not be exact but a 368th of a frame should not be too noticable.

Hm, it could work but my actual sound source is more complicated than that...

You could alternate between using 368 and 367 samples for a frame, or you could consider using higher sample rate (44100 is divisible by 60).

I was already trying with 44100 too, but the sound is not right.

It might also be worth noting that it's highly unlikely that your frame rate is ever exactly 60 frames per second.

Some more details: my sound source is a libretro (http://libretro.com) core. I'm actually testing with the snes9x-next (https://github.com/libretro/snes9x-next) core and Super Mario Kart.

I can render the video just fine, and I can see the game demo going on at an accelerated rate. I then went on to render the audio. I initialize the sound stream to 44100 Hz, and in onGetData, I render one SNES frame. I've turned the video off to make it easier to get the audio right.

Each SNES frame renders 1024 or 1026 samples per video frame at 32040,5 Hz (the video refresh rate is 60.098812.) I'm using an audio resampler to go from 32040,5 to 44100 (I'm actually varying the source frequency a bit to get a constant number of 1470 samples at 44100), and then I return the converted buffer in the sf::SoundStream::Chunk structure.

But the audio sounds wrong, it seems to go slower or faster at times, and it stutters a lot. I'm not sure what I'm doing wrong yet.

Don't be scared. std::vector (http://www.cplusplus.com/reference/vector/vector/) is very simple to use, generally a lot safer than standard arrays, and probably the standard go-to container for most cases.
It was just the "class member" bit that removes the need for the static buffer, by the way.

I know it and its interface is easy to use. I'm scared of what you can't see, like how it manages memory. The thing I dislike about SFML is its use of the standard library. I tried to use SDL2 before going to SFML because of that, but SDL2 don't seem to allow me to render the audio (it imposes a power-of-two audio buffer and libretro cores can send audio frames of any length.)
Title: Re: Custom SoundStream
Post by: leiradel on November 18, 2015, 01:24:53 am
I have to apologize... I was developing on a Linux virtual machine, and even RetroArch had a terrible sound there. So I moved to the host Windows OS and my custom SoundStream is working just fine now without any changes.

Now on to render the video together with the audio. I'd love if SFML had more thread primitives, conditions and semaphores are really *not* advanced features, and SFML should really provide them in a cross-platform way.
Title: Re: Custom SoundStream
Post by: Laurent on November 18, 2015, 07:46:26 am
Quote
I'd love if SFML had more thread primitives, conditions and semaphores are really *not* advanced features, and SFML should really provide them in a cross-platform way.
What's wrong with using the C++ standard library for that?
Title: Re: Custom SoundStream
Post by: leiradel on November 18, 2015, 02:18:28 pm
Quote
I'd love if SFML had more thread primitives, conditions and semaphores are really *not* advanced features, and SFML should really provide them in a cross-platform way.
What's wrong with using the C++ standard library for that?

Compiler support for them is still a problem, and I don't want to bring in boost as a dependency. But then again, it's hard to find a thread library that goes beyond the *nix/Windows world, so I might just unroll my own code for conditions and semaphores.
Title: Re: Custom SoundStream
Post by: Laurent on November 18, 2015, 02:23:53 pm
Quote
Compiler support for them is still a problem
Really? What compiler(s) do you use? And what problems do you have?
Title: Re: Custom SoundStream
Post by: leiradel on November 18, 2015, 04:21:59 pm
Quote
Compiler support for them is still a problem
Really? What compiler(s) do you use? And what problems do you have?

Many compilers for old consoles don't have good c/c++ stdlib support, and most don't even know what C++11 is. I don't think I'll be targeting them for this particular project though.

Even then I think I'll write some wrappers over pthreads and the Windows API. What do current Macs use? pthreads?
Title: Custom SoundStream
Post by: eXpl0it3r on November 18, 2015, 06:01:04 pm
You are aware that std::thread is juat doing that as well?

Anyways, this is getting offtopic. You might want to ask such questions on StackOverflow or similar.
Title: Re: Custom SoundStream
Post by: Hapax on November 19, 2015, 05:58:02 pm
The thing I dislike about SFML is its use of the standard library. I tried to use SDL2 before going to SFML because of that, but SDL2 don't seem to allow me to render the audio (it imposes a power-of-two audio buffer and libretro cores can send audio frames of any length.)
C++ > C
therefore:
SFML > SDL
 ;D

I'm scared of what you can't see, like how it manages memory.
You might want to start with writing your own OS then.
Title: Re: Custom SoundStream
Post by: Jesper Juhl on November 19, 2015, 10:03:35 pm
Compiler support for them is still a problem, and I don't want to bring in boost as a dependency. But then again, it's hard to find a thread library that goes beyond the *nix/Windows world, so I might just unroll my own code for conditions and semaphores.
Gcc has had reasonable C++11 support since ~4.7, clang since ~3.4 and visual studio since v2013 and even Intels compiler and IBMs are not too far behind the game. Good C++11 support has been available on most platforms with multiple compilers for years. There's no excuse for not moving to it (IMHO) - especially since it's such a huge improvement over C++98.
And your comments about being scared about the mem allocations etc that you cannot see; in my experience you should be more scared about the manual memory management code you write yourself. Chances are good that the C++ standard library got it right and you'll get it wrong.
Title: Re: Custom SoundStream
Post by: leiradel on November 20, 2015, 03:04:54 pm
You might want to start with writing your own OS then.

Nah, VirtualAlloc and mmap are your friends, it's possible to write/use nice memory allocators on top of them.
Title: Re: Custom SoundStream
Post by: Nexus on November 29, 2015, 08:34:22 pm
Nah, VirtualAlloc and mmap are your friends, it's possible to write/use nice memory allocators on top of them.
I don't think it makes sense to reinvent wheels here, especially because a self-written allocator will almost definitely be less efficient than existing solutions. There are heavily optimized allocators for all kinds of scenarios, I would go with the "use" option rather than "write" :)