forked from ppy/osu-framework
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
279 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. | ||
// See the LICENCE file in the repository root for full licence text. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
using ManagedBass; | ||
using ManagedBass.Mix; | ||
|
||
namespace osu.Framework.Audio.Mixing.Bass | ||
{ | ||
internal interface IBassAudioMixer | ||
{ | ||
BassFlags SampleFlags { get; } | ||
BassFlags TrackFlags { get; } | ||
|
||
void Add(IAudioChannel channel); | ||
|
||
/// <summary> | ||
/// Plays a channel. | ||
/// </summary> | ||
/// <remarks>See: <see cref="ManagedBass.Bass.ChannelPlay"/>.</remarks> | ||
/// <param name="channel">The channel to play.</param> | ||
/// <param name="restart">Restart playback from the beginning?</param> | ||
/// <returns> | ||
/// If successful, <see langword="true"/> is returned, else <see langword="false"/> is returned. | ||
/// Use <see cref="ManagedBass.Bass.LastError"/> to get the error code. | ||
/// </returns> | ||
bool ChannelPlay(IBassAudioChannel channel, bool restart = false); | ||
|
||
/// <summary> | ||
/// Pauses a channel. | ||
/// </summary> | ||
/// <remarks>See: <see cref="ManagedBass.Bass.ChannelPause"/>.</remarks> | ||
/// <param name="channel">The channel to pause.</param> | ||
/// <param name="flushMixer">Set to <c>true</c> to make the pause take effect immediately. | ||
/// <para> | ||
/// This will change the timing of <see cref="BassAudioMixer.ChannelGetPosition"/>, so should be used sparingly. | ||
/// </para> | ||
/// </param> | ||
/// <returns> | ||
/// If successful, <see langword="true"/> is returned, else <see langword="false"/> is returned. | ||
/// Use <see cref="ManagedBass.Bass.LastError"/> to get the error code. | ||
/// </returns> | ||
bool ChannelPause(IBassAudioChannel channel, bool flushMixer = false); | ||
|
||
/// <summary> | ||
/// Checks if a channel is active (playing) or stalled. | ||
/// </summary> | ||
/// <remarks>See: <see cref="ManagedBass.Bass.ChannelIsActive"/>.</remarks> | ||
/// <param name="channel">The channel to get the state of.</param> | ||
/// <returns><see cref="PlaybackState"/> indicating the state of the channel.</returns> | ||
PlaybackState ChannelIsActive(IBassAudioChannel channel); | ||
|
||
/// <summary> | ||
/// Retrieves the playback position of a channel. | ||
/// </summary> | ||
/// <remarks>See: <see cref="ManagedBass.Bass.ChannelGetPosition"/>.</remarks> | ||
/// <param name="channel">The channel to retrieve the position of.</param> | ||
/// <param name="mode">How to retrieve the position.</param> | ||
/// <returns> | ||
/// If an error occurs, -1 is returned, use <see cref="ManagedBass.Bass.LastError"/> to get the error code. | ||
/// If successful, the position is returned. | ||
/// </returns> | ||
long ChannelGetPosition(IBassAudioChannel channel, PositionFlags mode = PositionFlags.Bytes); | ||
|
||
/// <summary> | ||
/// Sets the playback position of a channel. | ||
/// </summary> | ||
/// <remarks>See: <see cref="ManagedBass.Bass.ChannelSetPosition"/>.</remarks> | ||
/// <param name="channel">The <see cref="IBassAudioChannel"/> to set the position of.</param> | ||
/// <param name="position">The position, in units determined by the <paramref name="mode"/>.</param> | ||
/// <param name="mode">How to set the position.</param> | ||
/// <returns> | ||
/// If successful, then <see langword="true"/> is returned, else <see langword="false"/> is returned. | ||
/// Use <see cref="P:ManagedBass.Bass.LastError"/> to get the error code. | ||
/// </returns> | ||
bool ChannelSetPosition(IBassAudioChannel channel, long position, PositionFlags mode = PositionFlags.Bytes); | ||
|
||
/// <summary> | ||
/// Retrieves the level (peak amplitude) of a channel. | ||
/// </summary> | ||
/// <remarks>See: <see cref="ManagedBass.Bass.ChannelGetLevel(int, float[], float, LevelRetrievalFlags)"/>.</remarks> | ||
/// <param name="channel">The <see cref="IBassAudioChannel"/> to get the levels of.</param> | ||
/// <param name="levels">The array in which the levels are to be returned.</param> | ||
/// <param name="length">How much data (in seconds) to look at to get the level (limited to 1 second).</param> | ||
/// <param name="flags">What levels to retrieve.</param> | ||
/// <returns><c>true</c> if successful, false otherwise.</returns> | ||
bool ChannelGetLevel(IBassAudioChannel channel, [In, Out] float[] levels, float length, LevelRetrievalFlags flags); | ||
|
||
/// <summary> | ||
/// Retrieves the immediate sample data (or an FFT representation of it) of a channel. | ||
/// </summary> | ||
/// <remarks>See: <see cref="ManagedBass.Bass.ChannelGetData(int, float[], int)"/>.</remarks> | ||
/// <param name="channel">The <see cref="IBassAudioChannel"/> to retrieve the data of.</param> | ||
/// <param name="buffer">float[] to write the data to.</param> | ||
/// <param name="length">Number of bytes wanted, and/or <see cref="T:ManagedBass.DataFlags"/>.</param> | ||
/// <returns>If an error occurs, -1 is returned, use <see cref="P:ManagedBass.Bass.LastError"/> to get the error code. | ||
/// <para>When requesting FFT data, the number of bytes read from the channel (to perform the FFT) is returned.</para> | ||
/// <para>When requesting sample data, the number of bytes written to buffer will be returned (not necessarily the same as the number of bytes read when using the <see cref="F:ManagedBass.DataFlags.Float"/> or DataFlags.Fixed flag).</para> | ||
/// <para>When using the <see cref="F:ManagedBass.DataFlags.Available"/> flag, the number of bytes in the channel's buffer is returned.</para> | ||
/// </returns> | ||
int ChannelGetData(IBassAudioChannel channel, float[] buffer, int length); | ||
|
||
/// <summary> | ||
/// Sets up a synchroniser on a mixer source channel. | ||
/// </summary> | ||
/// <remarks>See: <see cref="BassMix.ChannelSetSync(int, SyncFlags, long, SyncProcedure, IntPtr)"/>.</remarks> | ||
/// <param name="channel">The <see cref="IBassAudioChannel"/> to set up the synchroniser for.</param> | ||
/// <param name="type">The type of sync.</param> | ||
/// <param name="parameter">The sync parameters, depending on the sync type.</param> | ||
/// <param name="procedure">The callback function which should be invoked with the sync.</param> | ||
/// <param name="user">User instance data to pass to the callback function.</param> | ||
/// <returns>If successful, then the new synchroniser's handle is returned, else 0 is returned. Use <see cref="P:ManagedBass.Bass.LastError" /> to get the error code.</returns> | ||
int ChannelSetSync(IBassAudioChannel channel, SyncFlags type, long parameter, SyncProcedure procedure, IntPtr user = default); | ||
|
||
/// <summary> | ||
/// Removes a synchroniser from a mixer source channel. | ||
/// </summary> | ||
/// <param name="channel">The <see cref="IBassAudioChannel"/> to remove the synchroniser for.</param> | ||
/// <param name="sync">Handle of the synchroniser to remove (return value of a previous <see cref="M:ManagedBass.Mix.BassMix.ChannelSetSync(System.Int32,ManagedBass.SyncFlags,System.Int64,ManagedBass.SyncProcedure,System.IntPtr)" /> call).</param> | ||
/// <returns>If successful, <see langword="true" /> is returned, else <see langword="false" /> is returned. Use <see cref="P:ManagedBass.Bass.LastError" /> to get the error code.</returns> | ||
bool ChannelRemoveSync(IBassAudioChannel channel, int sync); | ||
|
||
/// <summary> | ||
/// Frees a channel's resources. | ||
/// </summary> | ||
/// <param name="channel">The <see cref="IBassAudioChannel"/> to free.</param> | ||
/// <returns>If successful, <see langword="true" /> is returned, else <see langword="false" /> is returned. Use <see cref="P:ManagedBass.Bass.LastError" /> to get the error code.</returns> | ||
bool StreamFree(IBassAudioChannel channel); | ||
|
||
/// <summary> | ||
/// Adds a channel to the native BASS mix. | ||
/// </summary> | ||
void AddChannelToBassMix(IBassAudioChannel channel); | ||
} | ||
} |
115 changes: 115 additions & 0 deletions
115
osu.Framework/Audio/Mixing/Bass/PassthroughBassAudioMixer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. | ||
// See the LICENCE file in the repository root for full licence text. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using ManagedBass; | ||
|
||
namespace osu.Framework.Audio.Mixing.Bass | ||
{ | ||
internal class PassthroughBassAudioMixer : AudioMixer, IBassAudio, IBassAudioMixer | ||
{ | ||
private readonly List<IBassAudioChannel> activeChannels = new List<IBassAudioChannel>(); | ||
|
||
public PassthroughBassAudioMixer(AudioManager manager, AudioMixer? fallbackMixer, string identifier) | ||
: base(fallbackMixer, identifier) | ||
{ | ||
} | ||
|
||
public override void AddEffect(IEffectParameter effect, int priority = 0) | ||
{ | ||
} | ||
|
||
public override void RemoveEffect(IEffectParameter effect) | ||
{ | ||
} | ||
|
||
public override void UpdateEffect(IEffectParameter effect) | ||
{ | ||
} | ||
|
||
protected override void AddInternal(IAudioChannel channel) | ||
{ | ||
if (!(channel is IBassAudioChannel bassChannel)) | ||
return; | ||
|
||
if (bassChannel.Handle == 0) | ||
return; | ||
|
||
activeChannels.Add(bassChannel); | ||
|
||
if (ManagedBass.Bass.ChannelIsActive(bassChannel.Handle) != PlaybackState.Stopped) | ||
ManagedBass.Bass.ChannelPlay(bassChannel.Handle); | ||
} | ||
|
||
protected override void RemoveInternal(IAudioChannel channel) | ||
{ | ||
if (!(channel is IBassAudioChannel bassChannel)) | ||
return; | ||
|
||
if (bassChannel.Handle == 0) | ||
return; | ||
|
||
activeChannels.Remove(bassChannel); | ||
ManagedBass.Bass.ChannelPause(bassChannel.Handle); | ||
} | ||
|
||
public void UpdateDevice(int deviceIndex) | ||
{ | ||
} | ||
|
||
public BassFlags SampleFlags => BassFlags.Default; | ||
public BassFlags TrackFlags => BassFlags.Default; | ||
|
||
public bool ChannelPlay(IBassAudioChannel channel, bool restart = false) | ||
{ | ||
if (channel.Handle == 0) | ||
return false; | ||
|
||
return ManagedBass.Bass.ChannelPlay(channel.Handle, restart); | ||
} | ||
|
||
public bool ChannelPause(IBassAudioChannel channel, bool flushMixer = false) | ||
=> ManagedBass.Bass.ChannelPause(channel.Handle); | ||
|
||
public PlaybackState ChannelIsActive(IBassAudioChannel channel) | ||
=> ManagedBass.Bass.ChannelIsActive(channel.Handle); | ||
|
||
public long ChannelGetPosition(IBassAudioChannel channel, PositionFlags mode = PositionFlags.Bytes) | ||
=> ManagedBass.Bass.ChannelGetPosition(channel.Handle, mode); | ||
|
||
public bool ChannelSetPosition(IBassAudioChannel channel, long position, PositionFlags mode = PositionFlags.Bytes) | ||
=> ManagedBass.Bass.ChannelSetPosition(channel.Handle, position, mode); | ||
|
||
public bool ChannelGetLevel(IBassAudioChannel channel, float[] levels, float length, LevelRetrievalFlags flags) | ||
=> ManagedBass.Bass.ChannelGetLevel(channel.Handle, levels, length, flags); | ||
|
||
public int ChannelGetData(IBassAudioChannel channel, float[] buffer, int length) | ||
=> ManagedBass.Bass.ChannelGetData(channel.Handle, buffer, length); | ||
|
||
public int ChannelSetSync(IBassAudioChannel channel, SyncFlags type, long parameter, SyncProcedure procedure, IntPtr user = default) | ||
=> ManagedBass.Bass.ChannelSetSync(channel.Handle, type, parameter, procedure, user); | ||
|
||
public bool ChannelRemoveSync(IBassAudioChannel channel, int sync) | ||
=> ManagedBass.Bass.ChannelRemoveSync(channel.Handle, sync); | ||
|
||
public bool StreamFree(IBassAudioChannel channel) | ||
{ | ||
ManagedBass.Bass.ChannelStop(channel.Handle); | ||
return ManagedBass.Bass.StreamFree(channel.Handle); | ||
} | ||
|
||
public void AddChannelToBassMix(IBassAudioChannel channel) | ||
{ | ||
} | ||
|
||
protected override void Dispose(bool disposing) | ||
{ | ||
base.Dispose(disposing); | ||
|
||
foreach (var channel in activeChannels.ToArray()) | ||
ManagedBass.Bass.ChannelPause(channel.Handle); | ||
activeChannels.Clear(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.