(Non)concurrency-related problem

I followed the official concurrecy guide, and I wanted to play a ogg file with SFML lib.

Here is my code:

require "crsfml/audio"

channel = Channel(Nil).new

spawn do
  sound = SF::Sound.new SF::SoundBuffer.from_file "/home/freeze-dolphin/Documents/arcaea_4.0.255c_assets/app-data/story/vn/res/glassRain.ogg"
  sound.play
  channel.send(nil)
end

channel.receive
puts "done"

This won’t play the sound, and prints done immediately and then exit the program.

freeze-dolphin@nebula:~/Documents/vscode-workspace/arcaea-inspector$ crystal playground/fiber_test.cr 
done
AL lib: (EE) alc_cleanup: 1 device not closed
freeze-dolphin@nebula:~/Documents/vscode-workspace/arcaea-inspector$ 

I’ve also tried removing the channel.send(nil), and in that case the sound was played. However, the whole program will stuck at the fiber and done will never be printed.

Try changing Nil to Int32 and sending 1 instead? I can’t remember where, but I remember reading somewhere that a channel with nil is indistinguishable from a closed channel, which might be why receive immediately passes.

I might be wrong though, on my phone and can’t verify :P

Unfortunately, that’s the same case. xd

I can reproduce this issue.

I guess it caused by misusage of crsfml library?

because if not use channel and fiber, the ogg file won’t got play too.

sound = SF::Sound.new SF::SoundBuffer.from_file "/home/zw963/Dropbox/common/sonar.ogg"
sound.play
puts "done"

 ╰─ $ cr src/crsfml.cr 
done

In my opinion, the sound is played ‘separately’ from the main thread.
But, when the done is printed, the program exits, and sound playing is ‘interupted’.
So there is a message saying AL lib: (EE) alc_cleanup: 1 device not closed

If nothing (the nil) is not sent into the channel like this:

require "crsfml/audio"

channel = Channel(Nil).new

spawn do
  sound = SF::Sound.new SF::SoundBuffer.from_file "/home/freeze-dolphin/Documents/arcaea_4.0.255c_assets/app-data/story/vn/res/glassRain.ogg"
  sound.play
  # channel.send(nil)
end

channel.receive
puts "done"

The channel will never receive and the sound is played correctly. And the program will stuck at channel.receive, done won’t be printed.

When the music is over, done is not printed as well. But this time if you type ^C to end the program, no AL lib: (EE) alc_cleanup: 1 device not closed will be printed.

That’s not problem with Crystal concurrency, but what you are doing is invoking async operation which runs its own thread (external to crystal), so using a spawn doesn’t make any difference.

you should be invoking Sound#status in a loop with some delay and when you library is done with playing, you can continue with your current logic of sending some value to Channel.

so pseudo code like below

  ......

  loop do
   break if sound.status == SoundSource::Status::Stopped
   sleep some_duration
 end
  ........

HIH

4 Likes

Solved, thanks very much! :)

Maybe should change title, because this is not a concurrency related issue.