Skip to content

Commit

Permalink
chore: bump native and add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
lmichaelis committed May 1, 2024
1 parent e6109c8 commit e3b217c
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 15 deletions.
37 changes: 36 additions & 1 deletion DirectMusic/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ public enum LoaderOptions

public class Loader
{
/// <summary>
/// <b>A function used to look up and read in DirectMusic objects by file name.</b> When called, a function
/// implementing this interface should look up a DirectMusic data file corresponding to the given file
/// <paramref name="name"/> and return the data contained within as a memory buffer. When the function fails to
/// find an appropriate file, it should return <c>null</c>.
/// </summary>
/// <param name="name">The name of the file to look up.</param>
/// <returns>A memory buffer containing the file data or <c>null</c> if the lookup failed.</returns>
public delegate byte[]? ResolveCallback(string name);

private readonly List<GCHandle> _callbacks = new List<GCHandle>();
internal readonly IntPtr Handle;

public Loader(IntPtr handle)
private Loader(IntPtr handle)
{
Handle = handle;
}
Expand All @@ -30,19 +38,46 @@ public Loader(IntPtr handle)
Native.DmLoader_release(Handle);
}

/// <summary>
/// <b>Add a resolver to the loader.</b> Resolvers are used to locate stored DirectMusic object by file name.
/// Whenever the loader needs to look up an object, it calls all resolvers in sequential order until one
/// returns a match. If no match is found, an error is issued and the object is not loaded.
/// </summary>
/// <param name="resolve">The callback function used to resolve a file using the new resolver.</param>
/// <exception cref="NativeAccessError">If the operation fails in any way.</exception>
public void AddResolver(ResolveCallback resolve)
{
var cb = GCHandle.Alloc(resolve);
Native.DmLoader_addResolver(Handle, _nativeCallbackHandler, GCHandle.ToIntPtr(cb)).Check();
_callbacks.Add(cb);
}

/// <summary>
/// <b>Get a segment from the loader's cache or load it by file <paramref name="name"/></b> Gets a segment from
/// the loader's cache or loads the segment using the resolvers added to the loader. If the requested segment
/// is found in neither the loader, nor by any resolver, an <exception cref="NativeAccessError"></exception>
/// is thrown.
/// <p>
/// If the loader was created using the <see cref="LoaderOptions.Download"/> option, this function
/// automatically downloads the segment by calling <see cref="Segment.Download"/>.
/// </p>
/// </summary>
/// <param name="name">The file name of the segment to load.</param>
/// <returns>The loaded segment.</returns>
/// <exception cref="NativeAccessError">If the operation fails in any way.</exception>
public Segment GetSegment(string name)
{
Native.DmLoader_getSegment(Handle, name, out var segment).Check();
return new Segment(segment);
}

/// <summary>
/// <b>Create a new DirectMusic Loader object.</b> If the <see cref="LoaderOptions.Download"/> option is
/// specified, all references for objects retrieved for the loader are automatically resolved and downloaded.
/// </summary>
/// <param name="opt">A bitfield containing loader configuration flags.</param>
/// <returns>The newly created loader.</returns>
/// <exception cref="NativeAccessError">If the operation fails in any way.</exception>
public static Loader Create(LoaderOptions opt)
{
Native.DmLoader_create(out var handle, opt).Check();
Expand Down
45 changes: 44 additions & 1 deletion DirectMusic/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,34 @@ namespace DirectMusic
{
public enum LogLevel
{
/// <summary>
/// The log message indicates a fatal error.
/// </summary>
Fatal = 10,

/// <summary>
/// The log message indicates an error.
/// </summary>
Error = 20,

/// <summary>
/// The log message indicates a warning.
/// </summary>
Warning = 30,

/// <summary>
/// The log message is informational.
/// </summary>
Info = 40,

/// <summary>
/// The log message is a debug message.
/// </summary>
Debug = 50,

/// <summary>
/// The log message is a tracing message.
/// </summary>
Trace = 60
}

Expand All @@ -21,7 +44,16 @@ public static class Logger
private static GCHandle? _handler;
private static readonly Native.DmLogHandler NativeHandler = _nativeCallbackHandler;

public static void Set(LogLevel lvl, Callback callback)
/// <summary>
/// <b>Set a callback to send log messages to.</b> Registers the given <paramref name="callback"/> function to
/// be called whenever a log message is issued by the library. If <paramref name="callback"/> is set to <c>null</c>,
/// any existing log callback function is removed and logging is disabled.
/// </summary>
/// <param name="lvl">The log level to set for the library.</param>
/// <param name="callback">The callback function to invoke whenever log message is generated or <c>null</c></param>
/// <seealso cref="SetDefault"/>
/// <seealso cref="SetLevel"/>
public static void Set(LogLevel lvl, Callback? callback)
{
var handler = GCHandle.Alloc(callback);
Native.Dm_setLogger(lvl, NativeHandler, GCHandle.ToIntPtr(handler));
Expand All @@ -30,11 +62,22 @@ public static void Set(LogLevel lvl, Callback callback)
_handler = handler;
}

/// <summary>
/// <b>Set a default logging function.</b> Registers a default log handler which outputs all log messages at
/// or above the given level to the standard error stream (<c>stderr</c>).
/// </summary>
/// <param name="level">The log level to set for the library.</param>
/// <seealso cref="Set"/>
/// <seealso cref="SetLevel"/>
public static void SetDefault(LogLevel level)
{
Native.Dm_setLoggerDefault(level);
}

/// <summary>
/// <b>Set the log level of the library.</b>
/// </summary>
/// <param name="level">The log level to set.</param>
public static void SetLevel(LogLevel level)
{
Native.Dm_setLoggerLevel(level);
Expand Down
2 changes: 1 addition & 1 deletion DirectMusic/Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ internal static class Native
public static extern DmResult DmSegment_download(IntPtr slf, IntPtr loader);

[DllImport(DllName)]
public static extern DmResult DmPerformance_create(out IntPtr slf);
public static extern DmResult DmPerformance_create(out IntPtr slf, uint rate);

[DllImport(DllName)]
public static extern IntPtr DmPerformance_retain(IntPtr slf);
Expand Down
87 changes: 75 additions & 12 deletions DirectMusic/Performance.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
using System;
using DirectMusic.Util;

namespace DirectMusic
{
[Flags]
public enum Timing
{
/// <summary>
/// Timing flag indicating start at the next possible tick.
/// </summary>
Instant = 1,

/// <summary>
/// Timing flag indicating start at the next possible grid boundary.
/// </summary>
Grid = 2,

/// <summary>
/// Timing flag indicating start at the next possible beat boundary.
/// </summary>
Beat = 3,

/// <summary>
/// Timing flag indicating start at the next possible measure boundary.
/// </summary>
Measure = 4,
}

Expand Down Expand Up @@ -36,26 +51,59 @@ private Performance(IntPtr handle)
Native.DmPerformance_release(_handle);
}

public void PlaySegment(Segment segment, Timing timing)
{
Native.DmPerformance_playSegment(_handle, segment.Handle, timing).Check();
}

public void PlayTransition(Segment segment, Embellishment embellishment, Timing timing)
/// <summary>
/// <b>Schedule a new segment to be played by the performance.</b> The segment is played at the next
/// timing boundary provided with <paramref name="timing"/>. This function simply stops the currently playing
/// segment and starts playing the next one. To play a transition between the two segments, use
/// <see cref="PlayTransition"/>.
/// </summary>
/// <remarks>
/// The segment will always start playing strictly after the last call to <see cref="RenderPcm(short[],bool)"/>
/// since that function advances the internal clock. This means, if you have already rendered ten seconds
/// worth of PCM using <see cref="RenderPcm(short[],bool)"/>, the transition can only audibly be heard after
/// these ten seconds of PCM have been played.
/// </remarks>
/// <param name="segment">The segment to play or <c>null</c> to simply stop the playing segment.</param>
/// <param name="timing">The timing bounding to start playing the segment at.</param>
/// <exception cref="NativeAccessError">If the operation fails in any way.</exception>
public void PlaySegment(Segment? segment, Timing timing)
{
Native.DmPerformance_playTransition(_handle, segment.Handle, embellishment, timing).Check();
Native.DmPerformance_playSegment(_handle, segment?.Handle ?? IntPtr.Zero, timing).Check();
}

public void StopSegment(Timing timing)
/// <summary>
/// <b>Schedule a new segment to play by the performance with a transition.</b> Schedules a new
/// transitional segment to be played, which first plays a transitional pattern from the currently playing
/// segment's style and then starts playing the given segment. This can be used to smoothly transition from
/// one segment to another. The transitional pattern is selected by its <paramref name="embellishment"/> type
/// provided when calling the function. Only embellishments matching the current groove level are considered.
/// </summary>
/// <param name="segment">The segment to transition to or <c>null</c> to transition to silence.</param>
/// <param name="embellishment">The embellishment type to use for the transition.</param>
/// <param name="timing">The timing bounding to start playing the transition at.</param>
public void PlayTransition(Segment? segment, Embellishment embellishment, Timing timing)
{
Native.DmPerformance_playSegment(_handle, IntPtr.Zero, timing).Check();
Native.DmPerformance_playTransition(_handle, segment?.Handle ?? IntPtr.Zero, embellishment, timing).Check();
}

/// <summary>
/// <b>Set the playback volume of a performance</b>
/// </summary>
/// <param name="volume">The new volume to set (between 0 and 1).</param>
public void SetVolume(float volume)
{
Native.DmPerformance_setVolume(_handle, volume);
}

/// <summary>
/// <b>Render short PCM samples from the performance.</b>
/// Since the performance is played "on demand", calling this function will advance the internal clock and
/// perform all musical operation for the rendered timeframe. If no segment is currently playing, the output
/// will be set to zero samples.
/// </summary>
/// <param name="pcm">A short array to output the PCM into.</param>
/// <param name="stereo">Set to <c>true</c> to render stereo samples. Renders mono samples otherwise.</param>
/// <seealso cref="RenderPcm(float[],bool)"/>
public void RenderPcm(short[] pcm, bool stereo)
{
var opts = DmRenderOptions.Short;
Expand All @@ -64,6 +112,15 @@ public void RenderPcm(short[] pcm, bool stereo)
Native.DmPerformance_renderPcm(_handle, pcm, (ulong)pcm.Length, opts).Check();
}

/// <summary>
/// <b>Render float PCM samples from the performance.</b>
/// Since the performance is played "on demand", calling this function will advance the internal clock and
/// perform all musical operation for the rendered timeframe. If no segment is currently playing, the output
/// will be set to zero samples.
/// </summary>
/// <param name="pcm">A float array to output the PCM into.</param>
/// <param name="stereo">Set to <c>true</c> to render stereo samples. Renders mono samples otherwise.</param>
/// <seealso cref="RenderPcm(short[],bool)"/>
public void RenderPcm(float[] pcm, bool stereo)
{
var opts = DmRenderOptions.Float;
Expand All @@ -72,9 +129,15 @@ public void RenderPcm(float[] pcm, bool stereo)
Native.DmPerformance_renderPcm(_handle, pcm, (ulong)pcm.Length, opts).Check();
}

public static Performance Create()
/// <summary>
/// <b>Create a new DirectMusic Performance object.</b>
/// </summary>
/// <param name="rate">The sample rate for the synthesizer. Provide 0 to use the default (44100 Hz).</param>
/// <returns>The newly created performance</returns>
/// <exception cref="NativeAccessError">If the operation fails in any way.</exception>
public static Performance Create(int rate)
{
Native.DmPerformance_create(out var handle).Check();
Native.DmPerformance_create(out var handle, (uint)rate).Check();
return new Performance(handle);
}
}
Expand Down
9 changes: 9 additions & 0 deletions DirectMusic/Segment.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using DirectMusic.Util;

namespace DirectMusic
{
Expand All @@ -16,6 +17,14 @@ internal Segment(IntPtr handle)
Native.DmSegment_release(Handle);
}

/// <summary>
/// <b>Download all resources needed by the segment.</b> In order to play a segment, its internal resources,
/// like references to styles and bands need to be resolved and downloaded. This is done by either calling
/// <see cref="Download"/> manually or by setting the <see cref="LoaderOptions.Download"/> flag when creating
/// the loader.
/// </summary>
/// <param name="loader">The loader to use for downloading resources.</param>
/// <exception cref="NativeAccessError">If the operation fails in any way.</exception>
public void Download(Loader loader)
{
Native.DmSegment_download(Handle, loader.Handle).Check();
Expand Down
Binary file modified DirectMusic/runtimes/android-arm64/native/libdmusic.so
Binary file not shown.
Binary file modified DirectMusic/runtimes/linux-x64/native/libdmusic.so
Binary file not shown.
Binary file modified DirectMusic/runtimes/win-x64/native/dmusic.dll
Binary file not shown.

0 comments on commit e3b217c

Please sign in to comment.