Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow PlayMusic() to work from Sound Library / move sound code to SDL? #759

Open
droscoe opened this issue Sep 7, 2023 · 27 comments
Open
Labels
BASS sound issue related to BASS sound feature About a new feature.

Comments

@droscoe
Copy link
Contributor

droscoe commented Sep 7, 2023

Is your feature request related to a problem? Please describe.
The behavior of PlayMusic() is ideal for adding features like an in-game jukebox. It is mutually exclusive in that playing another song automatically ends the previous and, it has a handy callback in the table script to process the event when a song ends. Unfortunately, it only works for external music files.

Describe the solution you'd like
I am suggesting adding the ability of using PlayMusic() for songs stored in the Sound Library. This gives more flexibility. For example, if I want to include some default songs without having the user download and install a supplemental music file to store in VPinball/music. Not supplying a path defaults to using the root of vpinball/music, so simply omitting the path is not an option. It would likely require a flag indicating internal or external storage in the command

Describe alternatives you've considered
I've tried PlaySound, but it does not automatically stop previous sounds, nor has the option to. It also does not have a way to determine when a song has finished playing, which is a necessary feature for a jukebox type application

Additional context
Add any other context or screenshots about the feature request here.

@droscoe droscoe added the feature About a new feature. label Sep 7, 2023
@droscoe
Copy link
Contributor Author

droscoe commented Sep 8, 2023

Looking at the code, it appears that PlayMusic() uses BASS Audio Library and PlaySound() uses Windows PlaySoundA/W. I'm guessing that this has access to the table container whereas PlayMusic() does not? I imagine then, that this request is not possible to implement?

@toxieainc
Copy link
Member

toxieainc commented Sep 8, 2023

It would also be possible, by passing in the in-mem data instead of the file (at the cost of having to have all music in-mem instead of streaming from disc).

But lets step back a bit, cause in general there is (at least) one thing that should be done before all of that:

There is the ONLY_USE_BASS define that can be used to get rid of the old windows API based sound playing, that i started years ago.
Unfortunately its not fully compatible yet, see e.g. #224 and #572, #564 and #566 .

One could even be more radical and consider a fully OSS based (and still well maintained and multi-platform!) sound lib instead of BASS (i guess SDL already has plenty of that ;)), too, while at it, and base the whole of the sound code on that.

Would be great if you could pick this up.. :)

@toxieainc toxieainc added the BASS sound issue related to BASS sound label Sep 8, 2023
@jsm174
Copy link
Contributor

jsm174 commented Sep 8, 2023

FWIW, I think #572 might be fixed in standalone (but not backported to master), so it would be interesting to try ONLY_USE_BASS again on windows.

I mentioned this on discord, but Blood Machines uses the WMP ocx which we were able to recreate in standalone. I've added all the methods in AudioPlayer to accomplish it. Also, I added methods for audio streams. I needed that to get libpinmame's audio buffer playing in standalone.

vpinball/audioplayer.cpp

Lines 198 to 299 in cb17c1d

#ifdef __STANDALONE__
bool AudioPlayer::SetMusicFile(const string& szFileName)
{
if (m_stream)
MusicClose();
m_stream = BASS_StreamCreateFile(FALSE, szFileName.c_str(), 0, 0, 0);
if (m_stream == 0) {
const int code = BASS_ErrorGetCode();
string message;
BASS_ErrorMapCode(code, message);
g_pvp->MessageBox(("BASS music/sound library cannot load \"" + szFileName + "\" (error " + std::to_string(code) + ": " + message + ')').c_str(), "Error", MB_ICONERROR);
return false;
}
return true;
}
void AudioPlayer::MusicPlay()
{
if (m_stream) {
if(bass_BG_idx != -1 && bass_STD_idx != bass_BG_idx) BASS_SetDevice(bass_BG_idx);
BASS_ChannelPlay(m_stream, 0);
}
}
void AudioPlayer::MusicStop()
{
if (m_stream) {
if(bass_BG_idx != -1 && bass_STD_idx != bass_BG_idx) BASS_SetDevice(bass_BG_idx);
BASS_ChannelStop(m_stream);
}
}
void AudioPlayer::MusicClose()
{
if (m_stream) {
if (bass_BG_idx != -1 && bass_STD_idx != bass_BG_idx) BASS_SetDevice(bass_BG_idx);
BASS_ChannelStop(m_stream);
BASS_StreamFree(m_stream);
m_stream = 0;
}
}
double AudioPlayer::GetMusicPosition()
{
if (m_stream) {
if(bass_BG_idx != -1 && bass_STD_idx != bass_BG_idx) BASS_SetDevice(bass_BG_idx);
return BASS_ChannelBytes2Seconds(m_stream, BASS_ChannelGetPosition(m_stream, BASS_POS_BYTE));
}
return -1;
}
void AudioPlayer::SetMusicPosition(double seconds)
{
if (m_stream) {
if(bass_BG_idx != -1 && bass_STD_idx != bass_BG_idx) BASS_SetDevice(bass_BG_idx);
BASS_ChannelSetPosition(m_stream, BASS_ChannelSeconds2Bytes(m_stream, seconds), BASS_POS_BYTE);
}
}
bool AudioPlayer::StreamInit(DWORD frequency, int channels, const float volume)
{
if (bass_BG_idx != -1 && bass_STD_idx != bass_BG_idx) BASS_SetDevice(bass_BG_idx);
m_stream = BASS_StreamCreate( frequency, channels, 0, STREAMPROC_PUSH, 0 );
if (m_stream == 0) {
const int code = BASS_ErrorGetCode();
string message;
BASS_ErrorMapCode(code, message);
g_pvp->MessageBox(("BASS music/sound library cannot play stream (error " + std::to_string(code) + ": " + message + ')').c_str(), "Error", MB_ICONERROR);
return false;
}
BASS_ChannelSetAttribute(m_stream, BASS_ATTRIB_VOL, volume);
BASS_ChannelPlay(m_stream, 0);
return true;
}
void AudioPlayer::StreamUpdate(void* buffer, DWORD length)
{
if (m_stream)
BASS_StreamPutData(m_stream, buffer, length);
}
void AudioPlayer::StreamVolume(const float volume)
{
MusicVolume(volume);
}
#endif

STDMETHODIMP WMPControls::play()
{
PLOGI.printf("player=%p, play", m_pCore);
m_pCore->m_pAudioPlayer->MusicPlay();
return S_OK;
}
STDMETHODIMP WMPControls::stop()
{
PLOGI.printf("player=%p, stop", m_pCore);
m_pCore->m_pAudioPlayer->MusicStop();
return S_OK;
}
STDMETHODIMP WMPControls::pause()
{
PLOGI.printf("player=%p, pause", m_pCore);
m_pCore->m_pAudioPlayer->MusicPause();
return S_OK;
}
STDMETHODIMP WMPControls::fastForward() { return E_NOTIMPL; }
STDMETHODIMP WMPControls::fastReverse() { return E_NOTIMPL; }
STDMETHODIMP WMPControls::get_currentPosition(double *pdCurrentPosition)
{
*pdCurrentPosition = m_pCore->m_pAudioPlayer->GetMusicPosition();
return S_OK;
}
STDMETHODIMP WMPControls::put_currentPosition(double pdCurrentPosition)
{
PLOGI.printf("player=%p, position=%f", this, pdCurrentPosition);
m_pCore->m_pAudioPlayer->SetMusicPosition(pdCurrentPosition);
return S_OK;
}

@vbousquet
Copy link
Collaborator

If we go the SDL way, we could choose to use with https://github.com/libsdl-org/SDL_mixer which seems to offer everything we need. It would be really nice to drop the legacy windows only stuff.

@jsm174
Copy link
Contributor

jsm174 commented Sep 8, 2023

I would be all for that. I have to do a bunch some trickery (approved) on tvOS with BASS due to licensing issues.

@toxieainc
Copy link
Member

FWIW, I think #572 might be fixed in standalone (but not backported to master), so it would be interesting to try ONLY_USE_BASS again on windows.

I couldn't find any real difference, or do you mean

if (pps->IsWav2())
?

@jsm174
Copy link
Contributor

jsm174 commented Sep 8, 2023

yes, instead of :

pps->Play(volume, randompitch, pitch, pan, front_rear_fade, flags, restart);

try:

			if (pps->IsWav2())
				pps->Play(volume, randompitch, pitch, pan, front_rear_fade, flags, (!usesame) ? true : restart);
			else
				pps->Play(volume, randompitch, pitch, pan, front_rear_fade, flags, restart); 

@droscoe
Copy link
Contributor Author

droscoe commented Sep 8, 2023

Is there a preference of SDL or BASS?

@jsm174
Copy link
Contributor

jsm174 commented Sep 8, 2023

see above: SDL_mixer

@vbousquet
Copy link
Collaborator

BASS is not open source licensed, and we are already using SDL, so I would go for SDL.
It has to be revieewed before diving too much in it to be sure it will fit all the needed features.
I'm confident for the base features, the thing to be more carefully looked at are for SSF: 3D sound is always difficult and not always well supported (ideally we should not rely on it since it is not really designed for the SSF use case but do our own mixing and output per channel streams but this would imply quite some advanced work).

@toxieainc
Copy link
Member

Second all of the concerns above.. But at least something open source and well maintained, please..

Back then i picked BASS because i knew it well, and there were not that many options that worked as good in practice.

@toxieainc
Copy link
Member

@jsm174 i just backported your audio changes, thanks!!

@droscoe
Copy link
Contributor Author

droscoe commented Sep 11, 2023

I'd be willing to look into this. It won't be a fast process. There's a lot I don't know about the sound system of VPX

@toxieainc
Copy link
Member

Nice! Take your time, this should become a post-10.8 thing anyway.

Also take the freedom to restructure what you need, its super convoluted at the moment anyway (as it evolved/mutated over 2 decades ;)). And as vbousquet pointed out, the major hurdle will be 3D sound support (along with multi-soundboard).

@toxieainc
Copy link
Member

One more thing that just came to mind: If you will pick SDL, please consider going directly to SDL3, as it apparently revamps the audio system, compared to SDL2.

@toxieainc toxieainc changed the title Allow PlayMusic() to work from Sound Library Allow PlayMusic() to work from Sound Library / move sound code to SDL? Sep 18, 2023
@francisdb
Copy link
Contributor

Just went through SDL2 mixer docs and it mentions this:

You may only have one audio device open at a time;

https://wiki.libsdl.org/SDL2_mixer/Mix_OpenAudioDevice

SDL3 looks similar, static audio_device

https://github.com/libsdl-org/SDL_mixer/blob/2bf94b9a29abe0b6146cfa606b078d044683577f/src/mixer.c#L489C5-L489C18

If I understand it correctly this would no longer allow having a separate device for the backglass

@francisdb
Copy link
Contributor

Further SDL2/3 mixer does not support 7.1 sound.
see libsdl-org/SDL_mixer#573

@vbousquet
Copy link
Collaborator

I understand it does not support 7.1 positional sound but does it support 7.1 simple direct channel (I mean, doing the positional stuff on VPX end, which seems more logical since we have a very specific use case) ?

@francisdb
Copy link
Contributor

Indeed, we could roll our own specific mixer. And if we don't use the mixer we can use multiple audio devices.
Not sure if that's something I could come up with though 😅

@vbousquet
Copy link
Collaborator

vbousquet commented Nov 2, 2023

I don't know if this is that complicated. In the end, I would hope that it resolves to compute 4 attenuations (based on the player position, which is already defined in real world unit in latest builds, allowing to apply traditional physics) and sending the same streams but with the attenuations to the 4 exciter channels. It could even prove to be easier to do than spoofing the native 3D positional sounds of Windows like we do now (which expects the listener to be inside the speakers and not outside and above, and could be difficult to make portable).

edit: after some search, there are papers that describe solutions for our use case like this one http://decoy.iki.fi/dsound/ambisonic/motherlode/source/2001_Spatial%20Sound%20Generation%20And%20Perception_Ville%20Pulkki.pdf

edit2: after some more search, it happens that VBAP seems to be quite admitted as a 'good' solution for this problem and there are a few OSS library available to be used as-is like this one: https://github.com/pierreguillot/vbap

@toxieainc
Copy link
Member

Something else i just stumbled over by accident, maybe also sufficient for our needs?

https://miniaud.io/

@jsm174
Copy link
Contributor

jsm174 commented Dec 5, 2023

Wow! Very interesting!

@droscoe
Copy link
Contributor Author

droscoe commented Dec 5, 2023

Whoa!

@toxieainc
Copy link
Member

On top, this could be used for .ogg support: https://github.com/edubart/minivorbis

@francisdb
Copy link
Contributor

Something else i just stumbled over by accident, maybe also sufficient for our needs?

https://miniaud.io/

Any open source lib would be an improvement as long as the new situation is not stuck with ALSA only for linux. So preferably pipewire support with a fallback to pulseaudio/jack and as last resort alsa.

@toxieainc
Copy link
Member

So you volunteer? ;)

@francisdb
Copy link
Contributor

francisdb commented Jan 20, 2024

So you volunteer? ;)

Nope, found out I'm allergic to C/C++

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BASS sound issue related to BASS sound feature About a new feature.
Projects
None yet
Development

No branches or pull requests

5 participants