Initial commit
This commit is contained in:
parent
780c394acc
commit
14c7db0ae5
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "editor-dotnet"]
|
||||
path = editor-dotnet
|
||||
url = git@gitea.azcona-becker.net:universaleditor/editor-dotnet
|
||||
BIN
audio-dotnet/src/Output/Debug/MBS.Audio.MIDI.dll
Normal file
BIN
audio-dotnet/src/Output/Debug/MBS.Audio.MIDI.dll
Normal file
Binary file not shown.
BIN
audio-dotnet/src/Output/Debug/MBS.Audio.MIDI.pdb
Normal file
BIN
audio-dotnet/src/Output/Debug/MBS.Audio.MIDI.pdb
Normal file
Binary file not shown.
3
audio-dotnet/src/audio-dotnet/.vscode/settings.json
vendored
Normal file
3
audio-dotnet/src/audio-dotnet/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"dotnet.preferCSharpExtension": true
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI
|
||||
{
|
||||
[Flags()]
|
||||
public enum DeviceOptionalFunctionality : uint
|
||||
{
|
||||
None = 0x0,
|
||||
/// <summary>
|
||||
/// Supports volume control.
|
||||
/// </summary>
|
||||
Volume = 0x1,
|
||||
/// <summary>
|
||||
/// Supports separate left and right volume control.
|
||||
/// </summary>
|
||||
StereoVolume = 0x2,
|
||||
/// <summary>
|
||||
/// Supports patch caching.
|
||||
/// </summary>
|
||||
PatchCaching = 0x4,
|
||||
/// <summary>
|
||||
/// Provides direct support for the midiStreamOut function.
|
||||
/// </summary>
|
||||
Streaming = 0x8
|
||||
}
|
||||
}
|
||||
40
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/DeviceType.cs
Normal file
40
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/DeviceType.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI
|
||||
{
|
||||
public enum DeviceType : ushort
|
||||
{
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// MIDI hardware port.
|
||||
/// </summary>
|
||||
HardwarePort = 1,
|
||||
/// <summary>
|
||||
/// Synthesizer.
|
||||
/// </summary>
|
||||
Synthesizer = 2,
|
||||
/// <summary>
|
||||
/// Square wave synthesizer.
|
||||
/// </summary>
|
||||
SquareWaveSynthesizer = 3,
|
||||
/// <summary>
|
||||
/// FM synthesizer.
|
||||
/// </summary>
|
||||
FMSynthesizer = 4,
|
||||
/// <summary>
|
||||
/// Microsoft MIDI mapper.
|
||||
/// </summary>
|
||||
MicrosoftMIDIMapper = 5,
|
||||
/// <summary>
|
||||
/// Hardware wavetable synthesizer.
|
||||
/// </summary>
|
||||
HardwareWavetable = 6,
|
||||
/// <summary>
|
||||
/// Software synthesizer.
|
||||
/// </summary>
|
||||
Software = 7
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
//
|
||||
// Constants.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2013 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
|
||||
namespace MBS.Audio.MIDI.Internal.Linux.Alsa
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
public enum snd_rawmidi_stream
|
||||
{
|
||||
Output = 0,
|
||||
Input = 1
|
||||
}
|
||||
|
||||
[Flags()]
|
||||
public enum SoundOpenFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// No flags specified
|
||||
/// </summary>
|
||||
None = 0x0000,
|
||||
/// <summary>
|
||||
/// Non-blocking mode
|
||||
/// </summary>
|
||||
NonBlocking = 0x0001,
|
||||
/// <summary>
|
||||
/// Async notification
|
||||
/// </summary>
|
||||
Async = 0x0002,
|
||||
/// <summary>
|
||||
/// Read-only mode
|
||||
/// </summary>
|
||||
ReadOnly = 0x0004
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBS.Audio.MIDI.Internal.Linux.Alsa
|
||||
{
|
||||
internal static class Methods
|
||||
{
|
||||
private const string LIBRARY_FILENAME = "libasound.so.2";
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the ID of the sound card directly after the sound card with ID cardNum.
|
||||
/// </summary>
|
||||
/// <param name="cardNum">The ID of the sound card at which to begin enumeration. This can be -1 to retrieve the first sound card.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_card_next(ref int cardNum);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the card located at the specified device path and returns the handle in <see cref="cardHandle" />.
|
||||
/// </summary>
|
||||
/// <param name="cardHandle">Returns a handle to the sound card associated with the device at the specified path.</param>
|
||||
/// <param name="devicePath">The path to the device to open.</param>
|
||||
/// <param name="flags"></param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_ctl_open(ref IntPtr cardHandle, string devicePath, Constants.SoundOpenFlags flags);
|
||||
|
||||
/// <summary>
|
||||
/// Closes the sound card referenced by the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="cardHandle">The handle of the sound card to close.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern void snd_ctl_close(IntPtr cardHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a friendly description for the specified error code.
|
||||
/// </summary>
|
||||
/// <param name="error">The error code for which to return a description.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern string snd_strerror(int error);
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of the next MIDI device on the specified card.
|
||||
/// </summary>
|
||||
/// <param name="cardHandle">Handle to the sound card on which to fetch the next MIDI device.</param>
|
||||
/// <param name="devNum">Receives the ID of the next MIDI device on the specified card.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_ctl_rawmidi_next_device(IntPtr cardHandle, ref int devNum);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the MIDI device at the specified device path.
|
||||
/// </summary>
|
||||
/// <param name="unknown">Unknown.</param>
|
||||
/// <param name="handle">Receives a handle to the MIDI device at the specified device path.</param>
|
||||
/// <param name="devicePath">The path of the MIDI device to open; i.e. "hw:cardnum,devnum,subdevnum"</param>
|
||||
/// <param name="flags"></param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_rawmidi_open(ref IntPtr inputHandle, ref IntPtr outputHandle, string devicePath, int flags);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified buffer to the MIDI device with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle to the MIDI device on which to write.</param>
|
||||
/// <param name="buffer">The data to write to the MIDI device.</param>
|
||||
/// <param name="bufferLength">The length of the buffer.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_rawmidi_write(IntPtr handle, byte[] buffer, int bufferLength);
|
||||
/// <summary>
|
||||
/// Read the specified buffer from the MIDI device with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle to the MIDI device on which to read.</param>
|
||||
/// <param name="buffer">The data to write to the MIDI device.</param>
|
||||
/// <param name="bufferLength">The length of the buffer.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_rawmidi_read(IntPtr handle, byte[] buffer, int bufferLength);
|
||||
|
||||
/// <summary>
|
||||
/// Closes the MIDI device referenced by the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the MIDI device to close.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_rawmidi_close(IntPtr handle);
|
||||
|
||||
#region RawMidi Info
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_rawmidi_info_malloc(ref IntPtr hInfo);
|
||||
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern void snd_rawmidi_info_free(IntPtr hInfo);
|
||||
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_rawmidi_info(IntPtr handle, IntPtr hInfo);
|
||||
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int snd_ctl_rawmidi_info(IntPtr handle, IntPtr hInfo);
|
||||
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern string snd_rawmidi_info_get_name(ref IntPtr hInfo);
|
||||
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern void snd_rawmidi_info_set_device(IntPtr hInfo, uint deviceID);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern void snd_rawmidi_info_set_subdevice(IntPtr hInfo, int subDeviceID);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern void snd_rawmidi_info_set_stream(IntPtr hInfo, Constants.snd_rawmidi_stream stream);
|
||||
#endregion
|
||||
|
||||
public static void snd_error_code_to_exception(int error)
|
||||
{
|
||||
if (error < 0)
|
||||
{
|
||||
throw new Exception(snd_strerror(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI.Internal.Windows
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
// from:
|
||||
// https://github.com/downpoured/downpoured_midi_audio/blob/master/benmidi/trilled-midisketch/CSharpMidiToolkitV4_demo/Multimedia.Midi/Device%20Classes/DeviceException.cs
|
||||
|
||||
public enum MidiError
|
||||
{
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Unspecified error.
|
||||
/// </summary>
|
||||
Unspecified = 1,
|
||||
/// <summary>
|
||||
/// The specified device identifier is out of range.
|
||||
/// </summary>
|
||||
BadDeviceID = 2,
|
||||
/// <summary>
|
||||
/// Driver failed to enable.
|
||||
/// </summary>
|
||||
NotEnabled = 3,
|
||||
/// <summary>
|
||||
/// The specified resource is already allocated.
|
||||
/// </summary>
|
||||
AlreadyAllocated = 4,
|
||||
/// <summary>
|
||||
/// The device handle is invalid.
|
||||
/// </summary>
|
||||
InvalidHandle = 5,
|
||||
/// <summary>
|
||||
/// No device driver is present.
|
||||
/// </summary>
|
||||
NoDriver = 6,
|
||||
/// <summary>
|
||||
/// The system is unable to allocate or lock memory.
|
||||
/// </summary>
|
||||
MemoryError = 7,
|
||||
Unsupported = 8,
|
||||
BadErrorNumber = 9,
|
||||
InvalidFlag = 10,
|
||||
/// <summary>
|
||||
/// The specified pointer or structure is invalid.
|
||||
/// </summary>
|
||||
InvalidParameter = 11,
|
||||
HandleBusy = 12,
|
||||
/// <summary>
|
||||
/// No MIDI port was found. This error occurs only when the mapper is opened.
|
||||
/// </summary>
|
||||
NoDevice = 68
|
||||
}
|
||||
/// <summary>
|
||||
/// Callback flag for opening the device.
|
||||
/// </summary>
|
||||
public enum MidiOpenFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// There is no callback mechanism. This value is the default setting.
|
||||
/// </summary>
|
||||
None = 0x00000000,
|
||||
/// <summary>
|
||||
/// The dwCallback parameter is a window handle.
|
||||
/// </summary>
|
||||
Window = 0x00010000,
|
||||
/// <summary>
|
||||
/// The dwCallback parameter is a thread identifier.
|
||||
/// </summary>
|
||||
Thread = 0x00020000,
|
||||
/// <summary>
|
||||
/// The dwCallback parameter is a callback function address.
|
||||
/// </summary>
|
||||
Function = 0x00030000,
|
||||
/// <summary>
|
||||
/// The dwCallback parameter is an event handle. This callback mechanism is for output
|
||||
/// only.
|
||||
/// </summary>
|
||||
Event = 0x00050000
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI.Internal.Windows
|
||||
{
|
||||
internal static class Delegates
|
||||
{
|
||||
/// <summary>
|
||||
/// The MidiCallback function is the callback function for handling incoming and outgoing MIDI
|
||||
/// messages. MidiCallback is a placeholder for the application-supplied function name. The
|
||||
/// address of the function can be specified in the callback-address parameter of the
|
||||
/// midiOutOpen/midiInOpen functions.
|
||||
/// </summary>
|
||||
/// <param name="hmo">Handle to the MIDI device associated with the callback function.</param>
|
||||
/// <param name="wMsg">MIDI input/output message.</param>
|
||||
/// <param name="dwInstance">Instance data supplied by using the midiInOpen/midiOutOpen function.</param>
|
||||
/// <param name="dwParam1">Message parameter.</param>
|
||||
/// <param name="dwParam2">Message parameter.</param>
|
||||
/// <remarks>
|
||||
/// Applications should not call any multimedia functions from inside the callback function, as
|
||||
/// doing so can cause a deadlock. Other system functions can safely be called from the
|
||||
/// callback.
|
||||
/// </remarks>
|
||||
public delegate void MidiCallback(IntPtr hmo, uint wMsg, IntPtr dwInstance, uint dwMidiMessage, uint dwTimestamp);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI.Internal.Windows
|
||||
{
|
||||
internal static class Methods
|
||||
{
|
||||
public const string LIBRARY_FILENAME = "winmm.dll";
|
||||
|
||||
#region Input
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern uint midiInGetNumDevs();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiInOpen(out IntPtr lphMidiIn, uint uDeviceID, Delegates.MidiCallback dwCallback, IntPtr dwCallbackInstance, Constants.MidiOpenFlags dwFlags);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiInGetDevCaps(uint uDeviceID, out Structures.MIDIINCAPS lpMidiInCaps, uint cbMidiInCaps);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiInReset(IntPtr hmo);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiInStart(IntPtr hMidiIn);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiInStop(IntPtr hMidiIn);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiInClose(IntPtr hmo);
|
||||
#endregion
|
||||
|
||||
#region Output
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern uint midiOutGetNumDevs();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiOutOpen(out IntPtr lphmo, uint uDeviceID, Delegates.MidiCallback dwCallback, IntPtr dwCallbackInstance, Constants.MidiOpenFlags dwFlags);
|
||||
/// <summary>
|
||||
/// Queries a specified MIDI output device to determine its capabilities.
|
||||
/// </summary>
|
||||
/// <param name="uDeviceID">
|
||||
/// Identifier of the MIDI output device. The device identifier specified by this parameter
|
||||
/// varies from zero to one less than the number of devices present. The MIDI_MAPPER constant
|
||||
/// is also a valid device identifier. This parameter can also be a properly cast device
|
||||
/// handle.
|
||||
/// </param>
|
||||
/// <param name="lpMidiOutCaps">
|
||||
/// Pointer to a <see cref="MIDIOUTCAPS"/> structure. This structure is filled with
|
||||
/// information about the capabilities of the device.
|
||||
/// </param>
|
||||
/// <param name="cbMidiOutCaps">
|
||||
/// Size, in bytes, of the <see cref="MIDIOUTCAPS" /> structure. Only cbMidiOutCaps bytes (or
|
||||
/// less) of information is copied to the location pointed to by lpMidiOutCaps. If
|
||||
/// cbMidiOutCaps is zero, nothing is copied, and the function returns MMSYSERR_NOERROR.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiOutGetDevCaps(uint uDeviceID, out Structures.MIDIOUTCAPS lpMidiOutCaps, uint cbMidiOutCaps);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiOutReset(IntPtr hmo);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiOutClose(IntPtr hmo);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.MidiError midiOutShortMsg(uint mvarID, int dwMsg);
|
||||
#endregion
|
||||
|
||||
[System.Diagnostics.DebuggerNonUserCode()]
|
||||
internal static void midiErrorToException(Constants.MidiError error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case Constants.MidiError.InvalidHandle:
|
||||
{
|
||||
throw new ArgumentException("Invalid handle");
|
||||
}
|
||||
case Constants.MidiError.InvalidParameter:
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
case Constants.MidiError.BadDeviceID:
|
||||
{
|
||||
throw new ArgumentException("Bad device ID");
|
||||
}
|
||||
case Constants.MidiError.Unsupported:
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI.Internal.Windows
|
||||
{
|
||||
internal static class Structures
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MIDIOUTCAPS
|
||||
{
|
||||
/// <summary>
|
||||
/// Manufacturer identifier of the device driver for the MIDI output device.
|
||||
/// </summary>
|
||||
public ushort wMid;
|
||||
/// <summary>
|
||||
/// Product identifier of the MIDI output device.
|
||||
/// </summary>
|
||||
public ushort wPid;
|
||||
|
||||
#region MMVERSION
|
||||
/// <summary>
|
||||
/// Major version number of the device driver for the MIDI output device.
|
||||
/// </summary>
|
||||
public byte vDriverVersionMinor;
|
||||
/// <summary>
|
||||
/// Minor version number of the device driver for the MIDI output device.
|
||||
/// </summary>
|
||||
public byte vDriverVersionMajor;
|
||||
|
||||
// extra two bytes in Windows 7 MMVERSION ???
|
||||
public byte vDriverVersionBuild;
|
||||
public byte vDriverVersionRevision;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Product name in a null-terminated string.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||
public string szPname;
|
||||
/// <summary>
|
||||
/// Type of the MIDI output device.
|
||||
/// </summary>
|
||||
public DeviceType wTechnology;
|
||||
/// <summary>
|
||||
/// Number of voices supported by an internal synthesizer device. If the device is a port,
|
||||
/// this member is not meaningful and is set to 0.
|
||||
/// </summary>
|
||||
public ushort wVoices;
|
||||
/// <summary>
|
||||
/// Maximum number of simultaneous notes that can be played by an internal synthesizer
|
||||
/// device. If the device is a port, this member is not meaningful and is set to 0.
|
||||
/// </summary>
|
||||
public ushort wNotes;
|
||||
/// <summary>
|
||||
/// Channels that an internal synthesizer device responds to, where the least significant
|
||||
/// bit refers to channel 0 and the most significant bit to channel 15. Port devices that
|
||||
/// transmit on all channels set this member to 0xFFFF.
|
||||
/// </summary>
|
||||
public ushort wChannelMask;
|
||||
/// <summary>
|
||||
/// Optional functionality supported by the device.
|
||||
/// </summary>
|
||||
public DeviceOptionalFunctionality dwSupport;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MIDIINCAPS
|
||||
{
|
||||
public ushort wMid;
|
||||
public ushort wPid;
|
||||
|
||||
#region MMVERSION
|
||||
/// <summary>
|
||||
/// Major version number of the device driver for the MIDI input device.
|
||||
/// </summary>
|
||||
public byte vDriverVersionMinor;
|
||||
/// <summary>
|
||||
/// Minor version number of the device driver for the MIDI input device.
|
||||
/// </summary>
|
||||
public byte vDriverVersionMajor;
|
||||
|
||||
// extra two bytes in Windows 7 MMVERSION ???
|
||||
public byte vDriverVersionBuild;
|
||||
public byte vDriverVersionRevision;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Product name in a null-terminated string.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||
public string szPname;
|
||||
|
||||
/// <summary>
|
||||
/// Optional functionality supported by the device.
|
||||
/// </summary>
|
||||
public DeviceOptionalFunctionality dwSupport;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/Listener.cs
Normal file
60
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/Listener.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI
|
||||
{
|
||||
public class Listener
|
||||
{
|
||||
public MidiDevice InputDevice { get; private set; } = null;
|
||||
|
||||
public SoundCard SoundCard { get; set; } = null;
|
||||
|
||||
private static MessageReceivedEventHandler eventHandler = null;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (SoundCard == null)
|
||||
{
|
||||
SoundCard = SoundCard.GetDefaultSoundCard();
|
||||
}
|
||||
if (SoundCard != null)
|
||||
{
|
||||
SoundCard.Open();
|
||||
if (InputDevice == null)
|
||||
{
|
||||
InputDevice = SoundCard.GetDefaultMidiInputDevice();
|
||||
}
|
||||
// output = sc.GetMidiOutputDevices()[1];
|
||||
}
|
||||
|
||||
if (InputDevice != null)
|
||||
{
|
||||
InputDevice.Open();
|
||||
InputDevice.MessageReceived += Listener_MessageReceived;
|
||||
InputDevice.Start();
|
||||
}
|
||||
}
|
||||
public void Stop()
|
||||
{
|
||||
if (InputDevice != null)
|
||||
{
|
||||
InputDevice.Stop();
|
||||
InputDevice.Close();
|
||||
}
|
||||
SoundCard.Close();
|
||||
}
|
||||
|
||||
public event MessageReceivedEventHandler MessageReceived;
|
||||
protected virtual void OnMessageReceived(MessageReceivedEventArgs e)
|
||||
{
|
||||
MessageReceived?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void Listener_MessageReceived(object sender, MBS.Audio.MIDI.MessageReceivedEventArgs e)
|
||||
{
|
||||
OnMessageReceived(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{DDC1CE36-60E0-4B09-A288-CB14ACE252DD}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MBS.Audio.MIDI</RootNamespace>
|
||||
<AssemblyName>MBS.Audio.MIDI</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProductVersion>10.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<OutputPath>..\..\Output\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
<OutputPath>..\..\Output\Release</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MidiDevice.cs" />
|
||||
<Compile Include="DeviceOptionalFunctionality.cs" />
|
||||
<Compile Include="DeviceType.cs" />
|
||||
<Compile Include="Internal\Windows\Constants.cs" />
|
||||
<Compile Include="Internal\Windows\Delegates.cs" />
|
||||
<Compile Include="Internal\Windows\Methods.cs" />
|
||||
<Compile Include="Internal\Windows\Structures.cs" />
|
||||
<Compile Include="Listener.cs" />
|
||||
<Compile Include="Message.cs" />
|
||||
<Compile Include="MessageType.cs" />
|
||||
<Compile Include="MessageReceivedEvent.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SoundCard.cs" />
|
||||
<Compile Include="Internal\Linux\Alsa\Methods.cs" />
|
||||
<Compile Include="Internal\Linux\Alsa\Constants.cs" />
|
||||
<Compile Include="MidiDeviceFunctionality.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup />
|
||||
</Project>
|
||||
65
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/Message.cs
Normal file
65
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/Message.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI
|
||||
{
|
||||
[System.Diagnostics.DebuggerNonUserCode()]
|
||||
public class Message
|
||||
{
|
||||
private MessageType mvarMessageType = MessageType.ControlChange;
|
||||
public MessageType MessageType { get { return mvarMessageType; } set { mvarMessageType = value; } }
|
||||
|
||||
private byte mvarChannel = 0;
|
||||
public byte Channel
|
||||
{
|
||||
get { return mvarChannel; }
|
||||
set
|
||||
{
|
||||
if (value < 1 || value > 16)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Channel", "value for channel must be between 1 and 16, inclusive");
|
||||
}
|
||||
mvarChannel = value;
|
||||
}
|
||||
}
|
||||
|
||||
private byte mvarParameter1 = 0;
|
||||
public byte Parameter1
|
||||
{
|
||||
get { return mvarParameter1; }
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > 127)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Parameter1", "value for parameter 1 must be between 0 and 127, inclusive");
|
||||
}
|
||||
mvarParameter1 = value;
|
||||
}
|
||||
}
|
||||
|
||||
private byte mvarParameter2 = 0;
|
||||
public byte Parameter2
|
||||
{
|
||||
get { return mvarParameter2; }
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > 127)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Parameter2", "value for parameter 2 must be between 0 and 127, inclusive");
|
||||
}
|
||||
mvarParameter2 = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Message(MessageType messageType, byte channel, byte parameter1, byte parameter2)
|
||||
{
|
||||
mvarMessageType = messageType;
|
||||
Channel = channel;
|
||||
Parameter1 = parameter1;
|
||||
Parameter2 = parameter2;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI
|
||||
{
|
||||
public delegate void MessageReceivedEventHandler(object sender, MessageReceivedEventArgs e);
|
||||
public class MessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
private Message mvarMessage = null;
|
||||
public Message Message { get { return mvarMessage; } }
|
||||
|
||||
private IntPtr mvarInstanceData = IntPtr.Zero;
|
||||
public IntPtr InstanceData { get { return mvarInstanceData; } }
|
||||
|
||||
public MessageReceivedEventArgs(Message message, IntPtr instanceData)
|
||||
{
|
||||
mvarMessage = message;
|
||||
mvarInstanceData = instanceData;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/MessageType.cs
Normal file
38
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/MessageType.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI
|
||||
{
|
||||
public enum MessageType : byte
|
||||
{
|
||||
NoteOff = 0x80,
|
||||
NoteOn = 0x90,
|
||||
Aftertouch = 0xA0,
|
||||
ControlChange = 0xB0,
|
||||
PatchChange = 0xC0,
|
||||
ChannelPressure = 0xD0,
|
||||
PitchBend = 0xE0,
|
||||
|
||||
/// <summary>
|
||||
/// start of system exclusive message
|
||||
/// </summary>
|
||||
SysexStart = 0xF0,
|
||||
SysexTimecode = 0xF1,
|
||||
SysexSongPosition = 0xF2,
|
||||
SysexSongSelect = 0xF3,
|
||||
SysexF4 = 0xF4,
|
||||
SysexF5 = 0xF5,
|
||||
SysexTuneRequest = 0xF6,
|
||||
SysexEnd = 0xF7,
|
||||
SysexRealtimeClock = 0xF8,
|
||||
SysexF9 = 0xF9,
|
||||
SysexRealtimeStart = 0xFA,
|
||||
SysexRealtimeContinue = 0xFB,
|
||||
SysexRealtimeStop = 0xFC,
|
||||
SysexFD = 0xFD,
|
||||
SysexActiveSensing = 0xFE,
|
||||
SysexReset = 0xFF
|
||||
}
|
||||
}
|
||||
436
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/MidiDevice.cs
Normal file
436
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/MidiDevice.cs
Normal file
@ -0,0 +1,436 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.MIDI
|
||||
{
|
||||
#if !DEBUG
|
||||
[System.Diagnostics.DebuggerNonUserCode()]
|
||||
#endif
|
||||
public class MidiDevice
|
||||
{
|
||||
protected uint mvarID = 0;
|
||||
public uint ID { get { return mvarID; } }
|
||||
|
||||
protected IntPtr mvarInputHandle = IntPtr.Zero;
|
||||
protected IntPtr mvarOutputHandle = IntPtr.Zero;
|
||||
public IntPtr Handle { get { return mvarInputHandle; } }
|
||||
|
||||
public SoundCard Parent { get; private set; }
|
||||
|
||||
internal MidiDevice(uint id, SoundCard parent)
|
||||
{
|
||||
mvarID = id;
|
||||
Parent = parent;
|
||||
try
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void Open(MidiDeviceFunctionality functionality = MidiDeviceFunctionality.Any)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
mvarInputHandle = IntPtr.Zero;
|
||||
mvarOutputHandle = IntPtr.Zero;
|
||||
IntPtr dummy = IntPtr.Zero;
|
||||
string str = "hw:" + Parent.ID.ToString() + "," + mvarID.ToString() + ",0";
|
||||
int error = Internal.Linux.Alsa.Methods.snd_rawmidi_open(ref mvarInputHandle, ref mvarOutputHandle, str, 0);
|
||||
Internal.Linux.Alsa.Methods.snd_error_code_to_exception(error);
|
||||
|
||||
Refresh();
|
||||
return;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
mvarInputHandle = IntPtr.Zero;
|
||||
|
||||
mvarCallback = new Internal.Windows.Delegates.MidiCallback(MidiCallback);
|
||||
Internal.Windows.Constants.MidiError error = Internal.Windows.Methods.midiInOpen(out mvarInputHandle, mvarID, mvarCallback, IntPtr.Zero, Internal.Windows.Constants.MidiOpenFlags.Function);
|
||||
Internal.Windows.Methods.midiErrorToException(error);
|
||||
|
||||
byHandle[mvarInputHandle] = this;
|
||||
|
||||
error = Internal.Windows.Methods.midiOutOpen(out mvarOutputHandle, mvarID, new Internal.Windows.Delegates.MidiCallback(MidiCallback), IntPtr.Zero, Internal.Windows.Constants.MidiOpenFlags.Function);
|
||||
Internal.Windows.Methods.midiErrorToException(error);
|
||||
return;
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
public bool AutoFlush { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the MIDI device information.
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
if (mvarInputHandle == IntPtr.Zero) return;
|
||||
|
||||
IntPtr hInfo = IntPtr.Zero;
|
||||
Internal.Linux.Alsa.Methods.snd_rawmidi_info_malloc(ref hInfo);
|
||||
Internal.Linux.Alsa.Methods.snd_rawmidi_info_set_device(hInfo, mvarID);
|
||||
Internal.Linux.Alsa.Methods.snd_rawmidi_info_set_stream(hInfo, Internal.Linux.Alsa.Constants.snd_rawmidi_stream.Output);
|
||||
Internal.Linux.Alsa.Methods.snd_rawmidi_info_set_subdevice(hInfo, -1);
|
||||
|
||||
Internal.Linux.Alsa.Methods.snd_rawmidi_info(mvarInputHandle, hInfo);
|
||||
// mvarName = Internal.Linux.Alsa.Methods.snd_rawmidi_info_get_name(ref hInfo);
|
||||
Internal.Linux.Alsa.Methods.snd_rawmidi_info_free(hInfo);
|
||||
return;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
// output
|
||||
Internal.Windows.Structures.MIDIOUTCAPS caps = new Internal.Windows.Structures.MIDIOUTCAPS();
|
||||
Internal.Windows.Methods.midiOutGetDevCaps(mvarID, out caps, (uint)System.Runtime.InteropServices.Marshal.SizeOf(caps));
|
||||
|
||||
mvarManufacturerID = caps.wMid;
|
||||
mvarProductID = caps.wPid;
|
||||
mvarName = caps.szPname;
|
||||
mvarDeviceType = caps.wTechnology;
|
||||
mvarMaximumVoices = caps.wVoices;
|
||||
mvarMaximumNotes = caps.wNotes;
|
||||
mvarDriverVersion = new Version(caps.vDriverVersionMajor, caps.vDriverVersionMinor);
|
||||
mvarSupportedChannels = caps.wChannelMask;
|
||||
mvarSupportedFunctionality = caps.dwSupport;
|
||||
|
||||
// input
|
||||
Internal.Windows.Structures.MIDIINCAPS capsIn = new Internal.Windows.Structures.MIDIINCAPS();
|
||||
Internal.Windows.Methods.midiInGetDevCaps(mvarID, out capsIn, (uint)System.Runtime.InteropServices.Marshal.SizeOf(capsIn));
|
||||
|
||||
mvarManufacturerID = capsIn.wMid;
|
||||
mvarProductID = capsIn.wPid;
|
||||
mvarName = capsIn.szPname;
|
||||
mvarDriverVersion = new Version(capsIn.vDriverVersionMajor, capsIn.vDriverVersionMinor);
|
||||
mvarSupportedFunctionality = capsIn.dwSupport;
|
||||
return;
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
/// <summary>
|
||||
/// Turns off all notes on all MIDI channels for this MIDI output device.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
if (mvarInputHandle != IntPtr.Zero)
|
||||
{
|
||||
Internal.Windows.Constants.MidiError error = Internal.Windows.Methods.midiInReset(mvarInputHandle);
|
||||
Internal.Windows.Methods.midiErrorToException(error);
|
||||
}
|
||||
if (mvarOutputHandle != IntPtr.Zero)
|
||||
{
|
||||
Internal.Windows.Constants.MidiError error = Internal.Windows.Methods.midiInReset(mvarOutputHandle);
|
||||
Internal.Windows.Methods.midiErrorToException(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public int Flush()
|
||||
{
|
||||
// get the bytes currently in the stream
|
||||
byte[] buffer = stream.ToArray();
|
||||
|
||||
// clear out the memory stream
|
||||
stream = new System.IO.MemoryStream();
|
||||
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
int error = Internal.Linux.Alsa.Methods.snd_rawmidi_write(mvarOutputHandle, buffer, buffer.Length);
|
||||
Internal.Linux.Alsa.Methods.snd_error_code_to_exception(error);
|
||||
return error;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
System.IO.BinaryReader br = new System.IO.BinaryReader(new System.IO.MemoryStream(buffer));
|
||||
while (br.BaseStream.Position < br.BaseStream.Length)
|
||||
{
|
||||
byte msgtype = br.ReadByte();
|
||||
byte param1 = 0;
|
||||
byte param2 = 0;
|
||||
if (br.BaseStream.Position < br.BaseStream.Length)
|
||||
{
|
||||
param1 = br.ReadByte();
|
||||
if (br.BaseStream.Position < br.BaseStream.Length)
|
||||
{
|
||||
param2 = br.ReadByte();
|
||||
}
|
||||
}
|
||||
|
||||
int dwMsg = BitConverter.ToInt32(new byte[] { msgtype, param1, param2, 0 }, 0);
|
||||
|
||||
Internal.Windows.Constants.MidiError error = Internal.Windows.Methods.midiOutShortMsg((uint)mvarOutputHandle.ToInt32(), dwMsg);
|
||||
Internal.Windows.Methods.midiErrorToException(error);
|
||||
}
|
||||
br.Close();
|
||||
buffer = new byte[0];
|
||||
return 0;
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
/// <summary>
|
||||
/// Closes this MIDI device.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
if (mvarInputHandle != IntPtr.Zero)
|
||||
{
|
||||
int error = Internal.Linux.Alsa.Methods.snd_rawmidi_close(mvarInputHandle);
|
||||
Internal.Linux.Alsa.Methods.snd_error_code_to_exception(error);
|
||||
mvarInputHandle = IntPtr.Zero;
|
||||
}
|
||||
if (mvarOutputHandle != IntPtr.Zero)
|
||||
{
|
||||
int error = Internal.Linux.Alsa.Methods.snd_rawmidi_close(mvarOutputHandle);
|
||||
Internal.Linux.Alsa.Methods.snd_error_code_to_exception(error);
|
||||
mvarOutputHandle = IntPtr.Zero;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
try
|
||||
{
|
||||
if (mvarInputHandle != IntPtr.Zero)
|
||||
{
|
||||
Internal.Windows.Methods.midiInClose(mvarInputHandle);
|
||||
}
|
||||
if (mvarOutputHandle != IntPtr.Zero)
|
||||
{
|
||||
Internal.Windows.Methods.midiOutClose(mvarOutputHandle);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
if (byHandle.ContainsKey(mvarInputHandle))
|
||||
{
|
||||
byHandle.Remove(mvarInputHandle);
|
||||
}
|
||||
if (byHandle.ContainsKey(mvarOutputHandle))
|
||||
{
|
||||
byHandle.Remove(mvarOutputHandle);
|
||||
}
|
||||
|
||||
mvarInputHandle = IntPtr.Zero;
|
||||
mvarOutputHandle = IntPtr.Zero;
|
||||
return;
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
|
||||
private System.IO.MemoryStream stream = new System.IO.MemoryStream();
|
||||
public int Write(byte[] buffer)
|
||||
{
|
||||
return Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
public int Write(byte[] buffer, int start, int length)
|
||||
{
|
||||
stream.Write(buffer, start, length);
|
||||
if (AutoFlush) return Flush();
|
||||
return length;
|
||||
}
|
||||
|
||||
protected string mvarName = String.Empty;
|
||||
public string Name { get { return mvarName; } }
|
||||
|
||||
public event MessageReceivedEventHandler MessageReceived;
|
||||
protected virtual void OnMessageReceived(MessageReceivedEventArgs e)
|
||||
{
|
||||
MessageReceived?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private static Dictionary<IntPtr, MidiDevice> byHandle = new Dictionary<IntPtr, MidiDevice>();
|
||||
|
||||
private static void MidiCallback(IntPtr hmo, uint wMsg, IntPtr dwInstance, uint dwMidiMessage, uint dwTimestamp)
|
||||
{
|
||||
MidiDevice device = null;
|
||||
if (byHandle.ContainsKey(hmo)) device = byHandle[hmo];
|
||||
if (device == null) return;
|
||||
|
||||
byte[] msgdata = BitConverter.GetBytes(dwMidiMessage);
|
||||
MessageType type = (MessageType)(msgdata[0] & 0xF0);
|
||||
byte channel = (byte)((msgdata[0] & 0x0F) + 1);
|
||||
|
||||
byte parameter1 = (byte)msgdata[1];
|
||||
byte parameter2 = (byte)msgdata[2];
|
||||
|
||||
device.OnMessageReceived(new MessageReceivedEventArgs(new Message(type, channel, parameter1, parameter2), dwInstance));
|
||||
}
|
||||
|
||||
public void Send(params Message[] messages)
|
||||
{
|
||||
foreach (Message message in messages)
|
||||
{
|
||||
byte status = (byte)((byte)message.MessageType | (message.Channel - 1));
|
||||
|
||||
System.IO.MemoryStream ms = new System.IO.MemoryStream();
|
||||
System.IO.BinaryWriter bw = new System.IO.BinaryWriter(ms);
|
||||
byte[] payload = new byte[] { status, message.Parameter1, message.Parameter2 }; // 93, 90 };
|
||||
bw.Write(payload);
|
||||
bw.Close();
|
||||
|
||||
Write(ms.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private Internal.Windows.Delegates.MidiCallback mvarCallback = null;
|
||||
|
||||
private System.Threading.Thread tLinuxThread = null;
|
||||
private void tLinuxThread_ThreadStart()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
Internal.Linux.Alsa.Methods.snd_rawmidi_read(mvarInputHandle, buffer, buffer.Length);
|
||||
|
||||
byte channelId = (byte)(((byte)(buffer[0] << 4)) >> 4);
|
||||
byte messageTypeId = (byte)((byte)(buffer[0] >> 4) << 4);
|
||||
OnMessageReceived(new MessageReceivedEventArgs(new Message((MessageType)messageTypeId, (byte)(channelId + 1), buffer[1], buffer[2]), IntPtr.Zero));
|
||||
|
||||
System.Threading.Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Starts listening for MIDI input.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
tLinuxThread = new System.Threading.Thread(tLinuxThread_ThreadStart);
|
||||
tLinuxThread.Start();
|
||||
break;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
Internal.Windows.Constants.MidiError error = Internal.Windows.Methods.midiInStart(mvarInputHandle);
|
||||
Internal.Windows.Methods.midiErrorToException(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops listening for MIDI input.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
tLinuxThread.Abort();
|
||||
tLinuxThread = null;
|
||||
break;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
Internal.Windows.Constants.MidiError error = Internal.Windows.Methods.midiInStop(mvarInputHandle);
|
||||
Internal.Windows.Methods.midiErrorToException(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected uint mvarManufacturerID = 0;
|
||||
public uint ManufacturerID { get { return mvarManufacturerID; } }
|
||||
protected uint mvarProductID = 0;
|
||||
public uint ProductID { get { return mvarProductID; } }
|
||||
protected ushort mvarMaximumVoices = 0;
|
||||
public ushort MaximumVoices { get { return mvarMaximumVoices; } }
|
||||
protected ushort mvarMaximumNotes = 0;
|
||||
public ushort MaximumNotes { get { return mvarMaximumNotes; } }
|
||||
protected ushort mvarSupportedChannels = 0;
|
||||
public ushort SupportedChannels { get { return mvarSupportedChannels; } }
|
||||
protected Version mvarDriverVersion = new Version(1, 0);
|
||||
public Version DriverVersion { get { return mvarDriverVersion; } }
|
||||
protected DeviceType mvarDeviceType = DeviceType.None;
|
||||
public DeviceType DeviceType { get { return mvarDeviceType; } }
|
||||
protected DeviceOptionalFunctionality mvarSupportedFunctionality = DeviceOptionalFunctionality.None;
|
||||
public DeviceOptionalFunctionality SupportedFunctionality { get { return mvarSupportedFunctionality; } }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
//
|
||||
// MidiDeviceFunctionality.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2021 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.MIDI
|
||||
{
|
||||
[Flags()]
|
||||
public enum MidiDeviceFunctionality
|
||||
{
|
||||
None = 0,
|
||||
Input = 1,
|
||||
Output = 2,
|
||||
Any = Input | Output
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MonoMidi")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("City of Orlando")]
|
||||
[assembly: AssemblyProduct("MonoMidi")]
|
||||
[assembly: AssemblyCopyright("Copyright © City of Orlando 2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("c04f487c-f580-48e6-b81a-5b550bbd213b")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
257
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/SoundCard.cs
Normal file
257
audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/SoundCard.cs
Normal file
@ -0,0 +1,257 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MBS.Audio.MIDI
|
||||
{
|
||||
public class SoundCard
|
||||
{
|
||||
public static SoundCard GetDefaultSoundCard()
|
||||
{
|
||||
return GetSoundCardByID(0);
|
||||
}
|
||||
public static SoundCard GetSoundCardByID(int id)
|
||||
{
|
||||
SoundCard[] cards = GetAllSoundCards();
|
||||
if (id >= 0 && id < cards.Length)
|
||||
{
|
||||
return cards[id];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static SoundCard[] GetAllSoundCards()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
int cardNum = -1;
|
||||
List<SoundCard> cards = new List<SoundCard>();
|
||||
while (true)
|
||||
{
|
||||
int error = Internal.Linux.Alsa.Methods.snd_card_next(ref cardNum);
|
||||
Internal.Linux.Alsa.Methods.snd_error_code_to_exception(error);
|
||||
if (cardNum == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
cards.Add(SoundCard.FromID(cardNum));
|
||||
}
|
||||
return cards.ToArray();
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
List<SoundCard> cards = new List<SoundCard>();
|
||||
SoundCard card = new SoundCard(0);
|
||||
cards.Add(card);
|
||||
return cards.ToArray();
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
private int mvarID = 0;
|
||||
public int ID { get { return mvarID; } }
|
||||
|
||||
private SoundCard(int id)
|
||||
{
|
||||
mvarID = id;
|
||||
}
|
||||
|
||||
public static SoundCard FromID(int id)
|
||||
{
|
||||
return new SoundCard(id);
|
||||
}
|
||||
|
||||
private IntPtr mvarHandle = IntPtr.Zero;
|
||||
public IntPtr Handle { get { return mvarHandle; } }
|
||||
|
||||
public void Open()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
mvarHandle = IntPtr.Zero;
|
||||
int error = Internal.Linux.Alsa.Methods.snd_ctl_open(ref mvarHandle, "hw:" + mvarID.ToString(), Internal.Linux.Alsa.Constants.SoundOpenFlags.None);
|
||||
Internal.Linux.Alsa.Methods.snd_error_code_to_exception(error);
|
||||
return;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
return;
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public void Close()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
Internal.Linux.Alsa.Methods.snd_ctl_close(mvarHandle);
|
||||
mvarHandle = IntPtr.Zero;
|
||||
return;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
return;
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
public MidiDevice GetDefaultMidiInputDevice()
|
||||
{
|
||||
MidiDevice[] devices = GetMidiInputDevices();
|
||||
if (devices.Length == 0) return null;
|
||||
return devices[0];
|
||||
}
|
||||
public MidiDevice[] GetMidiInputDevices()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
if (mvarHandle == IntPtr.Zero) throw new InvalidOperationException("Sound card is not open");
|
||||
|
||||
// Start with the first MIDI device on this card
|
||||
int devNum = -1;
|
||||
List<MidiDevice> devices = new List<MidiDevice>();
|
||||
while (true)
|
||||
{
|
||||
// Get the number of the next MIDI device on this card
|
||||
int error = Internal.Linux.Alsa.Methods.snd_ctl_rawmidi_next_device(mvarHandle, ref devNum);
|
||||
Internal.Linux.Alsa.Methods.snd_error_code_to_exception(error);
|
||||
|
||||
// No more MIDI devices on this card? ALSA sets "devNum" to -1 if so.
|
||||
// NOTE: It's possible that this sound card may have no MIDI devices on it
|
||||
// at all, for example if it's only a digital audio card
|
||||
if (devNum < 0) break;
|
||||
|
||||
MidiDevice device = new MidiDevice((uint)devNum, this);
|
||||
if (device == null) continue;
|
||||
|
||||
devices.Add(device);
|
||||
}
|
||||
return devices.ToArray();
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
uint count = Internal.Windows.Methods.midiInGetNumDevs();
|
||||
List<MidiDevice> list = new List<MidiDevice>();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
MidiDevice device = new MidiDevice(i, this);
|
||||
if (device == null) continue;
|
||||
list.Add(device);
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public MidiDevice GetDefaultMidiOutputDevice()
|
||||
{
|
||||
MidiDevice[] devices = GetMidiOutputDevices();
|
||||
if (devices.Length == 0) return null;
|
||||
return devices[0];
|
||||
}
|
||||
public MidiDevice[] GetMidiOutputDevices()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
// Start with the first MIDI device on this card
|
||||
int devNum = -1;
|
||||
List<MidiDevice> devices = new List<MidiDevice>();
|
||||
while (true)
|
||||
{
|
||||
// Get the number of the next MIDI device on this card
|
||||
int error = Internal.Linux.Alsa.Methods.snd_ctl_rawmidi_next_device(mvarHandle, ref devNum);
|
||||
Internal.Linux.Alsa.Methods.snd_error_code_to_exception(error);
|
||||
|
||||
// No more MIDI devices on this card? ALSA sets "devNum" to -1 if so.
|
||||
// NOTE: It's possible that this sound card may have no MIDI devices on it
|
||||
// at all, for example if it's only a digital audio card
|
||||
if (devNum < 0) break;
|
||||
|
||||
MidiDevice device = new MidiDevice((uint)devNum, this);
|
||||
if (device == null) continue;
|
||||
|
||||
devices.Add(device);
|
||||
}
|
||||
return devices.ToArray();
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
{
|
||||
uint count = Internal.Windows.Methods.midiOutGetNumDevs();
|
||||
List<MidiDevice> list = new List<MidiDevice>();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
MidiDevice device = new MidiDevice(i, this);
|
||||
if (device == null) continue;
|
||||
list.Add(device);
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
public MidiDevice[] GetMidiDevices(MidiDeviceFunctionality functionality)
|
||||
{
|
||||
List<MidiDevice> list = new List<MidiDevice>();
|
||||
if ((functionality & MidiDeviceFunctionality.Input) == MidiDeviceFunctionality.Input)
|
||||
{
|
||||
list.AddRange(GetMidiInputDevices());
|
||||
}
|
||||
if ((functionality & MidiDeviceFunctionality.Input) == MidiDeviceFunctionality.Input)
|
||||
{
|
||||
list.AddRange(GetMidiOutputDevices());
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
d68884522d7a041218a83ca43006c6fff4b0b7b2
|
||||
@ -0,0 +1,6 @@
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/Output/Debug/MBS.Audio.MIDI.dll
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/Output/Debug/MBS.Audio.MIDI.pdb
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/obj/Debug/MBS.Audio.MIDI.csproj.AssemblyReference.cache
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/obj/Debug/MBS.Audio.MIDI.csproj.CoreCompileInputs.cache
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/obj/Debug/MBS.Audio.MIDI.dll
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/audio-dotnet/MBS.Audio.MIDI/obj/Debug/MBS.Audio.MIDI.pdb
|
||||
Binary file not shown.
Binary file not shown.
18
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioDevice.cs
Normal file
18
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioDevice.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public abstract class AudioDevice
|
||||
{
|
||||
public abstract int MaximumInputChannels { get; }
|
||||
public abstract int MaximumOutputChannels { get; }
|
||||
|
||||
public abstract double DefaultLowInputLatency { get; }
|
||||
public abstract double DefaultLowOutputLatency { get; }
|
||||
public abstract double DefaultHighInputLatency { get; }
|
||||
public abstract double DefaultHighOutputLatency { get; }
|
||||
|
||||
public abstract double DefaultSampleRate { get; }
|
||||
|
||||
public static AudioDevice None { get; } = null;
|
||||
}
|
||||
}
|
||||
68
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioEngine.cs
Normal file
68
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioEngine.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public abstract class AudioEngine : IDisposable
|
||||
{
|
||||
public abstract AudioDevice DefaultInputDevice { get; }
|
||||
public abstract AudioDevice DefaultOutputDevice { get; }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
InitializeInternal();
|
||||
}
|
||||
protected abstract void InitializeInternal();
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
TerminateInternal();
|
||||
}
|
||||
protected abstract void TerminateInternal();
|
||||
|
||||
public abstract Guid ID { get; }
|
||||
public abstract string Title { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private bool _disposed = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed state (managed objects).
|
||||
}
|
||||
|
||||
// free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||
// set large fields to null.
|
||||
Terminate();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public AudioEngine()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
~AudioEngine() => Dispose(false);
|
||||
|
||||
protected abstract AudioStream CreateAudioStreamInternal(AudioDevice inputDevice, short inputChannelCount, AudioSampleFormat inputSampleFormat, double inputSuggestedLatency, AudioDevice outputDevice, short outputChannelCount, AudioSampleFormat outputSampleFormat, double outputSuggestedLatency, double sampleRate, int framesPerBuffer, AudioStreamFlags flags);
|
||||
public AudioStream CreateAudioStream(AudioDevice inputDevice, short inputChannelCount, AudioSampleFormat inputSampleFormat, double inputSuggestedLatency, AudioDevice outputDevice, short outputChannelCount, AudioSampleFormat outputSampleFormat, double outputSuggestedLatency, double sampleRate, int framesPerBuffer, AudioStreamFlags flags)
|
||||
{
|
||||
return CreateAudioStreamInternal(inputDevice, inputChannelCount, inputSampleFormat, inputSuggestedLatency, outputDevice, outputChannelCount, outputSampleFormat, outputSuggestedLatency, sampleRate, framesPerBuffer, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
167
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioPlayer.cs
Normal file
167
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioPlayer.cs
Normal file
@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using UniversalEditor.ObjectModels.Multimedia.Audio.Waveform;
|
||||
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public class AudioPlayer : ITransport
|
||||
{
|
||||
public bool IsPlaying { get { return mvarState != AudioPlayerState.Stopped; } }
|
||||
|
||||
private System.Threading.Thread PlayThread = null;
|
||||
|
||||
public AudioDevice InputDevice { get; set; } = AudioDevice.None;
|
||||
public AudioDevice OutputDevice { get; set; } = AudioDevice.None;
|
||||
|
||||
public event EventHandler<AudioPlayerStateChangedEventArgs> StateChanged;
|
||||
|
||||
protected virtual void OnStateChanged(AudioPlayerStateChangedEventArgs e)
|
||||
{
|
||||
StateChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private AudioPlayerState mvarState = AudioPlayerState.Stopped;
|
||||
public AudioPlayerState State { get { return mvarState; } private set { if (mvarState != value) { mvarState = value; OnStateChanged(new AudioPlayerStateChangedEventArgs(value, AudioPlayerStateChangedReason.UserAction)); } } }
|
||||
|
||||
private AudioTimestamp mvarTimestamp = AudioTimestamp.Empty;
|
||||
public AudioTimestamp Timestamp { get { return mvarTimestamp; } set { mvarTimestamp = value; i = mvarTimestamp.TotalSamples; } }
|
||||
|
||||
public void Play()
|
||||
{
|
||||
Play(false);
|
||||
}
|
||||
public void Play(bool async)
|
||||
{
|
||||
if (Document == null) throw new NullReferenceException();
|
||||
|
||||
if (PlayThread != null)
|
||||
{
|
||||
PlayThread.Abort();
|
||||
PlayThread = null;
|
||||
}
|
||||
|
||||
PlayThread = new System.Threading.Thread(PlayThread_ThreadStart);
|
||||
PlayThread.Start();
|
||||
|
||||
if (!async)
|
||||
{
|
||||
while (IsPlaying)
|
||||
{
|
||||
System.Threading.Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Play(WaveformAudioObjectModel wave)
|
||||
{
|
||||
Play(wave, false);
|
||||
}
|
||||
public void Play(WaveformAudioObjectModel wave, bool async)
|
||||
{
|
||||
Document = wave;
|
||||
Play(async);
|
||||
}
|
||||
|
||||
public double Volume { get; set; } = 0.5;
|
||||
|
||||
public AudioEngine AudioEngine { get; }
|
||||
|
||||
public AudioPlayer(AudioEngine ae)
|
||||
{
|
||||
AudioEngine = ae;
|
||||
}
|
||||
|
||||
private long i = 0;
|
||||
private void PlayThread_ThreadStart()
|
||||
{
|
||||
int bufferSize = Document.Header.ChannelCount;
|
||||
|
||||
AudioSampleFormat asf = AudioSampleFormat.Int16;
|
||||
switch (Document.Header.BitsPerSample)
|
||||
{
|
||||
case 8: asf = AudioSampleFormat.Int8; break;
|
||||
case 16: asf = AudioSampleFormat.Int16; break;
|
||||
case 24: asf = AudioSampleFormat.Int24; break;
|
||||
case 32: asf = AudioSampleFormat.Int32; break;
|
||||
}
|
||||
|
||||
AudioStream audio = AudioEngine.CreateAudioStream(AudioEngine.DefaultInputDevice, Document.Header.ChannelCount, asf, 0, AudioEngine.DefaultOutputDevice, Document.Header.ChannelCount, asf, 0, Document.Header.SampleRate * Document.Header.ChannelCount, 0, AudioStreamFlags.None);
|
||||
|
||||
mvarState = AudioPlayerState.Playing;
|
||||
OnStateChanged(new AudioPlayerStateChangedEventArgs(AudioPlayerState.Playing, AudioPlayerStateChangedReason.UserAction));
|
||||
|
||||
long start = 0;
|
||||
|
||||
mvarTimestamp = AudioTimestamp.FromSamples((int)0, Document.Header.SampleRate * Document.Header.ChannelCount);
|
||||
|
||||
while (State != AudioPlayerState.Stopped)
|
||||
{
|
||||
if (State != AudioPlayerState.Paused)
|
||||
{
|
||||
for (i = mvarTimestamp.TotalSamples; i < Document.RawSamples.Length;)
|
||||
{
|
||||
short[] buffer = Document.RawSamples[(int)(i * bufferSize), bufferSize];
|
||||
|
||||
/*
|
||||
short sampleL = mvarDocument.RawSamples[mvarTimestamp.TotalSamples];
|
||||
short sampleR = sampleL;
|
||||
if (i + 1 < mvarDocument.RawSamples.Length)
|
||||
{
|
||||
sampleR = mvarDocument.RawSamples[i + 1];
|
||||
}
|
||||
|
||||
audio.Write(new short[] { sampleL, sampleR });
|
||||
|
||||
mvarTimestamp.TotalSamples += 2;
|
||||
*/
|
||||
|
||||
for (int j = 0; j < buffer.Length; j++)
|
||||
{
|
||||
buffer[j] = (byte)(Volume * buffer[j]);
|
||||
}
|
||||
|
||||
audio.Write(buffer);
|
||||
mvarTimestamp.TotalSamples += buffer.Length;
|
||||
|
||||
i = mvarTimestamp.TotalSamples;
|
||||
// start = i;
|
||||
if (State != AudioPlayerState.Playing) break;
|
||||
}
|
||||
}
|
||||
System.Threading.Thread.Sleep(500);
|
||||
if (mvarTimestamp.TotalSamples >= Document.RawSamples.Length)
|
||||
{
|
||||
mvarState = AudioPlayerState.Stopped;
|
||||
OnStateChanged(new AudioPlayerStateChangedEventArgs(mvarState, AudioPlayerStateChangedReason.SongEnded));
|
||||
}
|
||||
}
|
||||
audio.Flush();
|
||||
|
||||
}
|
||||
public void Stop()
|
||||
{
|
||||
if (!IsPlaying) return;
|
||||
|
||||
mvarState = AudioPlayerState.Stopped;
|
||||
OnStateChanged(new AudioPlayerStateChangedEventArgs(AudioPlayerState.Stopped, AudioPlayerStateChangedReason.UserAction));
|
||||
|
||||
mvarTimestamp = AudioTimestamp.FromSamples((int)0, Document.Header.SampleRate * 2);
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
if (State == AudioPlayerState.Playing)
|
||||
{
|
||||
State = AudioPlayerState.Paused;
|
||||
}
|
||||
else if (State == AudioPlayerState.Paused)
|
||||
{
|
||||
State = AudioPlayerState.Playing;
|
||||
}
|
||||
}
|
||||
|
||||
public WaveformAudioObjectModel Document { get; set; } = null;
|
||||
public int ChannelCount { get; set; } = 2;
|
||||
}
|
||||
}
|
||||
14
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioPlayerState.cs
Normal file
14
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioPlayerState.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public enum AudioPlayerState
|
||||
{
|
||||
Stopped = 0,
|
||||
Playing = 2,
|
||||
Paused = 4
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public delegate void AudioPlayerStateChangedEventHandler(object sender, AudioPlayerStateChangedEventArgs e);
|
||||
public class AudioPlayerStateChangedEventArgs : EventArgs
|
||||
{
|
||||
public AudioPlayerState State { get; private set; } = AudioPlayerState.Stopped;
|
||||
public AudioPlayerStateChangedReason Reason { get; private set; } = AudioPlayerStateChangedReason.Unknown;
|
||||
|
||||
public AudioPlayerStateChangedEventArgs(AudioPlayerState state, AudioPlayerStateChangedReason reason)
|
||||
{
|
||||
State = state;
|
||||
Reason = reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public enum AudioPlayerStateChangedReason
|
||||
{
|
||||
Unknown,
|
||||
UserAction,
|
||||
SongEnded
|
||||
}
|
||||
}
|
||||
20
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioSampleFormat.cs
Normal file
20
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioSampleFormat.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public enum AudioSampleFormat : uint
|
||||
{
|
||||
None = 0,
|
||||
Float32 = 1u,
|
||||
Int32 = 2u,
|
||||
Int24 = 4u,
|
||||
Int16 = 8u,
|
||||
Int8 = 16u,
|
||||
UInt8 = 32u,
|
||||
CustomFormat = 65536u,
|
||||
NonInterleaved = 2147483648u
|
||||
}
|
||||
}
|
||||
129
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioStream.cs
Normal file
129
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioStream.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using System;
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public abstract class AudioStream : System.IO.Stream
|
||||
{
|
||||
public AudioDevice InputDevice { get; }
|
||||
public int InputChannelCount { get; }
|
||||
public AudioSampleFormat InputSampleFormat { get; }
|
||||
public double InputSuggestedLatency { get; }
|
||||
|
||||
public AudioDevice OutputDevice { get; }
|
||||
public int OutputChannelCount { get; }
|
||||
public AudioSampleFormat OutputSampleFormat { get; }
|
||||
public double OutputSuggestedLatency { get; }
|
||||
|
||||
public double SampleRate { get; }
|
||||
public int FramesPerBuffer { get; }
|
||||
public AudioStreamFlags Flags { get; }
|
||||
|
||||
public AudioStream(AudioDevice inputDevice, AudioDevice outputDevice, AudioSampleFormat sampleFormat)
|
||||
: this(inputDevice, outputDevice, sampleFormat, sampleFormat)
|
||||
{
|
||||
}
|
||||
public AudioStream(AudioDevice inputDevice, AudioDevice outputDevice, AudioSampleFormat sampleFormat, double sampleRate)
|
||||
: this(inputDevice, outputDevice, sampleFormat, sampleFormat, sampleRate)
|
||||
{
|
||||
}
|
||||
public AudioStream(AudioDevice inputDevice, AudioDevice outputDevice, AudioSampleFormat inputSampleFormat, AudioSampleFormat outputSampleFormat)
|
||||
: this(inputDevice, inputDevice.MaximumInputChannels, inputSampleFormat, outputDevice, outputDevice.MaximumOutputChannels, outputSampleFormat, outputDevice.DefaultSampleRate, 0)
|
||||
{
|
||||
}
|
||||
public AudioStream(AudioDevice inputDevice, AudioDevice outputDevice, AudioSampleFormat inputSampleFormat, AudioSampleFormat outputSampleFormat, double sampleRate)
|
||||
: this(inputDevice, inputDevice.MaximumInputChannels, inputSampleFormat, outputDevice, outputDevice.MaximumOutputChannels, outputSampleFormat, sampleRate, 0)
|
||||
{
|
||||
}
|
||||
public AudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double sampleRate)
|
||||
: this(inputDevice, inputChannelCount, inputSampleFormat, (inputDevice == null) ? 0 : inputDevice.DefaultLowInputLatency, outputDevice, outputChannelCount, outputSampleFormat, (outputDevice == null) ? 0 : outputDevice.DefaultLowOutputLatency, sampleRate, 0, AudioStreamFlags.None)
|
||||
{
|
||||
}
|
||||
public AudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double sampleRate, int framesPerBuffer)
|
||||
: this(inputDevice, inputChannelCount, inputSampleFormat, (inputDevice == null) ? 0 : inputDevice.DefaultLowInputLatency, outputDevice, outputChannelCount, outputSampleFormat, (outputDevice == null) ? 0 : outputDevice.DefaultLowOutputLatency, sampleRate, framesPerBuffer, AudioStreamFlags.None)
|
||||
{
|
||||
}
|
||||
public AudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double sampleRate, int framesPerBuffer, AudioStreamFlags flags)
|
||||
: this(inputDevice, inputChannelCount, inputSampleFormat, (inputDevice == null) ? 0 : inputDevice.DefaultLowInputLatency, outputDevice, outputChannelCount, outputSampleFormat, (outputDevice == null) ? 0 : outputDevice.DefaultLowOutputLatency, sampleRate, framesPerBuffer, flags)
|
||||
{
|
||||
}
|
||||
public AudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, double inputSuggestedLatency, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double outputSuggestedLatency, double sampleRate, int framesPerBuffer)
|
||||
: this(inputDevice, inputChannelCount, inputSampleFormat, inputSuggestedLatency, outputDevice, outputChannelCount, outputSampleFormat, outputSuggestedLatency, sampleRate, framesPerBuffer, AudioStreamFlags.None)
|
||||
{
|
||||
}
|
||||
public AudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, double inputSuggestedLatency, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double outputSuggestedLatency, double sampleRate, int framesPerBuffer, AudioStreamFlags flags)
|
||||
{
|
||||
InputDevice = inputDevice;
|
||||
InputChannelCount = inputChannelCount;
|
||||
InputSampleFormat = inputSampleFormat;
|
||||
InputSuggestedLatency = inputSuggestedLatency;
|
||||
OutputDevice = outputDevice;
|
||||
OutputChannelCount = outputChannelCount;
|
||||
OutputSampleFormat = outputSampleFormat;
|
||||
OutputSuggestedLatency = outputSuggestedLatency;
|
||||
SampleRate = sampleRate;
|
||||
FramesPerBuffer = framesPerBuffer;
|
||||
Flags = flags;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
protected virtual void InitializeInternal()
|
||||
{
|
||||
}
|
||||
private void Initialize()
|
||||
{
|
||||
InitializeInternal();
|
||||
}
|
||||
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public abstract void Read(short[] buffer);
|
||||
public abstract void Write(short[] buffer);
|
||||
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
18
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioStreamFlags.cs
Normal file
18
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioStreamFlags.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio
|
||||
{
|
||||
[Flags()]
|
||||
public enum AudioStreamFlags : uint
|
||||
{
|
||||
None,
|
||||
ClipOff,
|
||||
DitherOff,
|
||||
NeverDropInput,
|
||||
PrimeOutputBuffersUsingStreamCallback,
|
||||
PlatformSpecificFlags
|
||||
}
|
||||
}
|
||||
187
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioTimestamp.cs
Normal file
187
audio-dotnet/src/audio-dotnet/MBS.Audio/AudioTimestamp.cs
Normal file
@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public struct AudioTimestamp : IComparable<AudioTimestamp>
|
||||
{
|
||||
public static AudioTimestamp FromSamples(long totalSamples, int samplesPerSecond)
|
||||
{
|
||||
AudioTimestamp timestamp = new AudioTimestamp();
|
||||
timestamp.TotalSamples = totalSamples;
|
||||
timestamp.mvarSamplesPerSecond = samplesPerSecond;
|
||||
timestamp._IsNotEmpty = true;
|
||||
return timestamp;
|
||||
}
|
||||
public static AudioTimestamp FromSamples(long totalSamples, int samplesPerSecond, int bars, int beats, int ticks, float beatsPerBar, double ticksPerBeat)
|
||||
{
|
||||
AudioTimestamp timestamp = new AudioTimestamp();
|
||||
timestamp.TotalSamples = totalSamples;
|
||||
timestamp.mvarSamplesPerSecond = samplesPerSecond;
|
||||
timestamp._IsNotEmpty = true;
|
||||
timestamp.Bars = bars;
|
||||
timestamp.Beats = beats;
|
||||
timestamp.Ticks = ticks;
|
||||
timestamp.BeatsPerBar = beatsPerBar;
|
||||
timestamp.TicksPerBeat = ticksPerBeat;
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public static AudioTimestamp FromHMS(int hours, int minutes, int seconds, int milliseconds, int samplesPerSecond)
|
||||
{
|
||||
return FromHMS(0, hours, minutes, seconds, milliseconds, samplesPerSecond);
|
||||
}
|
||||
public static AudioTimestamp FromHMS(int days, int hours, int minutes, int seconds, int milliseconds, int samplesPerSecond)
|
||||
{
|
||||
AudioTimestamp timestamp = new AudioTimestamp();
|
||||
timestamp.TotalSamples = (int)(((double)milliseconds / 100) + (seconds) + (minutes * 60) + (hours * 3600) + (days * 24 * 3600)) * samplesPerSecond;
|
||||
timestamp.mvarSamplesPerSecond = samplesPerSecond;
|
||||
timestamp._IsNotEmpty = true;
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
private bool _IsNotEmpty;
|
||||
public bool IsEmpty { get { return !_IsNotEmpty; } }
|
||||
|
||||
public int Bars { get; private set; }
|
||||
public int Beats { get; private set; }
|
||||
public int Ticks { get; private set; }
|
||||
public float BeatsPerBar { get; private set; }
|
||||
public double TicksPerBeat { get; private set; }
|
||||
|
||||
public string ToBBTString(string separator = ".")
|
||||
{
|
||||
return String.Format("{0}{3}{1}{3}{2}", Bars.ToString().PadLeft(3, '0'), Beats.ToString().PadLeft(2, '0'), Ticks.ToString().PadLeft(4, '0'), separator);
|
||||
}
|
||||
|
||||
public BarBeatTick ToBBTTimeSpan()
|
||||
{
|
||||
BarBeatTick bbt = BarBeatTick.FromBBT(Bars, Beats, Ticks, BeatsPerBar, TicksPerBeat);
|
||||
return bbt;
|
||||
}
|
||||
|
||||
public static readonly AudioTimestamp Empty = new AudioTimestamp();
|
||||
|
||||
private int mvarSamplesPerSecond;
|
||||
|
||||
private long mvarTotalSamples;
|
||||
public long TotalSamples { get { return mvarTotalSamples; } set { mvarTotalSamples = value; } }
|
||||
|
||||
public int Days
|
||||
{
|
||||
get { return (int)(TotalDays % 365); }
|
||||
}
|
||||
public int Hours
|
||||
{
|
||||
get { return (int)(TotalHours % 24); }
|
||||
}
|
||||
public int Minutes
|
||||
{
|
||||
get { return (int)(TotalMinutes % 60); }
|
||||
}
|
||||
public int Seconds
|
||||
{
|
||||
get { return (int)(TotalSeconds % 60); }
|
||||
}
|
||||
public int Milliseconds
|
||||
{
|
||||
get { return (int)(TotalMilliseconds % 1000); }
|
||||
}
|
||||
public long Samples
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mvarSamplesPerSecond == 0) return 0;
|
||||
return mvarTotalSamples % mvarSamplesPerSecond;
|
||||
}
|
||||
}
|
||||
|
||||
public double TotalDays
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((double)TotalHours / 24);
|
||||
}
|
||||
}
|
||||
public double TotalHours
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((double)TotalMinutes / 60);
|
||||
}
|
||||
}
|
||||
public double TotalMinutes
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((double)TotalSeconds / 60);
|
||||
}
|
||||
}
|
||||
public double TotalSeconds
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mvarSamplesPerSecond == 0) return 0;
|
||||
return ((double)mvarTotalSamples / mvarSamplesPerSecond);
|
||||
}
|
||||
}
|
||||
public double TotalMilliseconds
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mvarSamplesPerSecond == 0) return 0;
|
||||
return (((double)mvarTotalSamples / mvarSamplesPerSecond * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// return Hours.ToString().PadLeft(2, '0') + ":" + Minutes.ToString().PadLeft(2, '0') + ":" + Seconds.ToString().PadLeft(2, '0') + "." + Milliseconds.ToString() + "/" + Samples.ToString();
|
||||
return Hours.ToString().PadLeft(2, '0') + ":" + Minutes.ToString().PadLeft(2, '0') + ":" + Seconds.ToString().PadLeft(2, '0') + "." + Milliseconds.ToString().PadLeft(3, '0');
|
||||
}
|
||||
|
||||
public TimeSpan ToTimeSpan()
|
||||
{
|
||||
double tsecs = 0;
|
||||
double tms = 0;
|
||||
if (mvarTotalSamples != 0)
|
||||
{
|
||||
tsecs = ((double)mvarTotalSamples / mvarSamplesPerSecond);
|
||||
tms = (((double)mvarTotalSamples / mvarSamplesPerSecond) * 1000) % 1000;
|
||||
}
|
||||
|
||||
int days = (int)(((((double)tsecs / 60) / 60) / 24) % 365);
|
||||
int hours = (int)((((double)tsecs / 60) / 60) % 24);
|
||||
int mins = (int)(((double)tsecs / 60) % 24);
|
||||
int secs = (int)((double)tsecs % 60);
|
||||
int ms = (int)(tms);
|
||||
|
||||
TimeSpan ts = new TimeSpan(days, hours, mins, secs, ms);
|
||||
return ts;
|
||||
}
|
||||
|
||||
public int CompareTo(AudioTimestamp other)
|
||||
{
|
||||
return this.ToTimeSpan().CompareTo(other.ToTimeSpan());
|
||||
}
|
||||
|
||||
public static bool operator <(AudioTimestamp left, AudioTimestamp right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
public static bool operator >(AudioTimestamp left, AudioTimestamp right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
public static bool operator <=(AudioTimestamp left, AudioTimestamp right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
public static bool operator >=(AudioTimestamp left, AudioTimestamp right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
143
audio-dotnet/src/audio-dotnet/MBS.Audio/BarBeatTick.cs
Normal file
143
audio-dotnet/src/audio-dotnet/MBS.Audio/BarBeatTick.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using System;
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public struct BarBeatTick : IComparable<BarBeatTick>
|
||||
{
|
||||
public int Bars { get; private set; }
|
||||
public int Beats { get; private set; }
|
||||
public int Ticks { get; private set; }
|
||||
|
||||
public float? BeatsPerBar { get; private set; }
|
||||
public double? TicksPerBeat { get; private set; }
|
||||
|
||||
public static BarBeatTick FromBBT(int bars, int beats, int ticks, float? beatsPerBar = null, double? ticksPerBeat = null)
|
||||
{
|
||||
BarBeatTick bbt = new BarBeatTick();
|
||||
bbt.Bars = bars;
|
||||
bbt.Beats = beats;
|
||||
bbt.Ticks = ticks;
|
||||
bbt.BeatsPerBar = beatsPerBar;
|
||||
bbt.TicksPerBeat = ticksPerBeat;
|
||||
bbt._isNotEmpty = true;
|
||||
return bbt;
|
||||
}
|
||||
|
||||
private bool _isNotEmpty;
|
||||
public bool IsEmpty { get { return !_isNotEmpty; } }
|
||||
|
||||
public static readonly BarBeatTick Empty = new BarBeatTick();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is BarBeatTick bbt)
|
||||
{
|
||||
return (Bars == bbt.Bars && Beats == bbt.Beats && Ticks == bbt.Ticks && BeatsPerBar == bbt.BeatsPerBar && TicksPerBeat == bbt.TicksPerBeat && IsEmpty == bbt.IsEmpty);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int CompareTo(BarBeatTick other)
|
||||
{
|
||||
if (Bars == other.Bars)
|
||||
{
|
||||
if (Beats == other.Beats)
|
||||
{
|
||||
if (Ticks == other.Ticks)
|
||||
{
|
||||
// completely equal
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Ticks.CompareTo(other.Ticks);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Beats.CompareTo(other.Beats);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Bars.CompareTo(other.Bars);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(BarBeatTick left, BarBeatTick right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
public static bool operator !=(BarBeatTick left, BarBeatTick right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator >=(BarBeatTick left, BarBeatTick right)
|
||||
{
|
||||
return (left.CompareTo(right) >= 0);
|
||||
}
|
||||
public static bool operator >(BarBeatTick left, BarBeatTick right)
|
||||
{
|
||||
return (left.CompareTo(right) > 0);
|
||||
}
|
||||
public static bool operator <=(BarBeatTick left, BarBeatTick right)
|
||||
{
|
||||
return (left.CompareTo(right) < 0);
|
||||
}
|
||||
public static bool operator <(BarBeatTick left, BarBeatTick right)
|
||||
{
|
||||
return (left.CompareTo(right) <= 0);
|
||||
}
|
||||
|
||||
public static BarBeatTick operator +(BarBeatTick left, BarBeatTick right)
|
||||
{
|
||||
return left.Add(right);
|
||||
}
|
||||
|
||||
public BarBeatTick Add(int ticks)
|
||||
{
|
||||
BarBeatTick thiss = new BarBeatTick();
|
||||
thiss.Bars = Bars;
|
||||
thiss.Beats = Beats;
|
||||
thiss.Ticks = Ticks + ticks;
|
||||
thiss.TicksPerBeat = TicksPerBeat;
|
||||
thiss.BeatsPerBar = BeatsPerBar;
|
||||
thiss._isNotEmpty = true;
|
||||
return thiss;
|
||||
}
|
||||
public BarBeatTick Add(BarBeatTick other)
|
||||
{
|
||||
int bars = Bars + other.Bars;
|
||||
int beats = Beats + other.Beats;
|
||||
int ticks = Ticks + other.Ticks;
|
||||
|
||||
float? beatsPerBar = BeatsPerBar;
|
||||
if (beatsPerBar == null) beatsPerBar = other.BeatsPerBar;
|
||||
double? ticksPerBeat = TicksPerBeat;
|
||||
if (ticksPerBeat == null) ticksPerBeat = other.TicksPerBeat;
|
||||
if (ticksPerBeat == null) ticksPerBeat = 2000;
|
||||
|
||||
while (ticks > ticksPerBeat)
|
||||
{
|
||||
beats++;
|
||||
ticks -= (int)ticksPerBeat.GetValueOrDefault();
|
||||
}
|
||||
while (beats > beatsPerBar)
|
||||
{
|
||||
bars++;
|
||||
beats -= (int)beatsPerBar.GetValueOrDefault();
|
||||
}
|
||||
|
||||
return BarBeatTick.FromBBT(bars, beats, ticks, beatsPerBar, ticksPerBeat);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ToString(" | ");
|
||||
}
|
||||
public string ToString(string separator)
|
||||
{
|
||||
return String.Format("{0}{1}{2}{1}{3}", Bars.ToString().PadLeft(3, '0'), separator, Beats.ToString().PadLeft(2, '0'), Ticks.ToString().PadLeft(4, '0'));
|
||||
}
|
||||
}
|
||||
}
|
||||
58
audio-dotnet/src/audio-dotnet/MBS.Audio/CustomTransport.cs
Normal file
58
audio-dotnet/src/audio-dotnet/MBS.Audio/CustomTransport.cs
Normal file
@ -0,0 +1,58 @@
|
||||
//
|
||||
// CustomTransport.cs
|
||||
//
|
||||
// Author:
|
||||
// beckermj <>
|
||||
//
|
||||
// Copyright (c) 2021 ${CopyrightHolder}
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio
|
||||
{
|
||||
public class CustomTransport : ITransport
|
||||
{
|
||||
public CustomTransport(EventHandler play_handler, EventHandler stop_handler, EventHandler pause_handler)
|
||||
{
|
||||
_play_handler = play_handler;
|
||||
_stop_handler = stop_handler;
|
||||
_pause_handler = pause_handler;
|
||||
}
|
||||
|
||||
public AudioTimestamp Timestamp { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
public bool IsPlaying => throw new NotImplementedException();
|
||||
|
||||
public AudioPlayerState State => throw new NotImplementedException();
|
||||
|
||||
public event EventHandler<AudioPlayerStateChangedEventArgs> StateChanged;
|
||||
|
||||
private EventHandler _play_handler = null, _stop_handler = null, _pause_handler = null;
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
_pause_handler?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
_play_handler?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_stop_handler?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
95
audio-dotnet/src/audio-dotnet/MBS.Audio/ITransport.cs
Normal file
95
audio-dotnet/src/audio-dotnet/MBS.Audio/ITransport.cs
Normal file
@ -0,0 +1,95 @@
|
||||
//
|
||||
// ITransport.cs - interface for defining the minimum functionality required for an audio transport
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020-2021 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MBS.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the minimum functionality required for an audio transport that
|
||||
/// can play, pause, stop, and seek within the audio.
|
||||
/// </summary>
|
||||
public interface ITransport
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="AudioTimestamp" /> position of the
|
||||
/// transport.
|
||||
/// </summary>
|
||||
/// <value>The timestamp to get or set.</value>
|
||||
AudioTimestamp Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="ITransport" /> is
|
||||
/// currently playing (rolling).
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if the transport is playing (rolling);
|
||||
/// otherwise, <c>false</c>.</value>
|
||||
bool IsPlaying { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the transport is currently
|
||||
/// stopped, playing, or paused.
|
||||
/// </summary>
|
||||
/// <value>The state of the transport.</value>
|
||||
AudioPlayerState State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Stops playback of the audio stream associated with this
|
||||
/// <see cref="ITransport" />. This is equivalent to calling
|
||||
/// <see cref="Pause" /> and setting <see cref="Timestamp" /> to the
|
||||
/// beginning of the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There is no concept of "pausing" with respect to "stopping" in the
|
||||
/// JACK transport. Therefore, JACK's definition of "stopping" is
|
||||
/// equivalent to "pausing" here, and "stopping" here refers to
|
||||
/// "pausing" followed by resetting the <see cref="Timestamp" /> to the
|
||||
/// beginning of the audio stream (if possible).
|
||||
/// </remarks>
|
||||
void Stop();
|
||||
/// <summary>
|
||||
/// Starts playback of the audio stream associated with this
|
||||
/// <see cref="ITransport" />. If the audio stream is currently
|
||||
/// paused, this MAY result in simply resuming the audio stream where
|
||||
/// it left off.
|
||||
/// </summary>
|
||||
void Play();
|
||||
/// <summary>
|
||||
/// Pauses playback of the audio stream associated with this
|
||||
/// <see cref="ITransport" />. This essentially stops the playback
|
||||
/// but does not reset the <see cref="Timestamp" /> to the beginning
|
||||
/// of the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There is no concept of "pausing" with respect to "stopping" in the
|
||||
/// JACK transport. Therefore, JACK's definition of "stopping" is
|
||||
/// equivalent to "pausing" here, and "stopping" here refers to
|
||||
/// "pausing" followed by resetting the <see cref="Timestamp" /> to the
|
||||
/// beginning of the audio stream (if possible).
|
||||
/// </remarks>
|
||||
void Pause();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the state of the <see cref="ITransport" /> changes.
|
||||
/// </summary>
|
||||
event EventHandler<AudioPlayerStateChangedEventArgs> StateChanged;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.Internal.PortAudio
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public enum PaStreamCallbackFlags : uint
|
||||
{
|
||||
paInputUnderflow = 1u,
|
||||
paInputOverflow,
|
||||
paOutputUnderflow = 4u,
|
||||
paOutputOverflow = 8u,
|
||||
paPrimingOutput = 16u
|
||||
}
|
||||
public enum PaStreamCallbackResult : uint
|
||||
{
|
||||
paContinue,
|
||||
paComplete,
|
||||
paAbort
|
||||
}
|
||||
public enum PaError
|
||||
{
|
||||
paNoError,
|
||||
paNotInitialized = -10000,
|
||||
paUnanticipatedHostError,
|
||||
paInvalidChannelCount,
|
||||
paInvalidSampleRate,
|
||||
paInvalidDevice,
|
||||
paInvalidFlag,
|
||||
paSampleFormatNotSupported,
|
||||
paBadIODeviceCombination,
|
||||
paInsufficientMemory,
|
||||
paBufferTooBig,
|
||||
paBufferTooSmall,
|
||||
paNullCallback,
|
||||
paBadStreamPtr,
|
||||
paTimedOut,
|
||||
paInternalError,
|
||||
paDeviceUnavailable,
|
||||
paIncompatibleHostApiSpecificStreamInfo,
|
||||
paStreamIsStopped,
|
||||
paStreamIsNotStopped,
|
||||
paInputOverflowed,
|
||||
paOutputUnderflowed,
|
||||
paHostApiNotFound,
|
||||
paInvalidHostApi,
|
||||
paCanNotReadFromACallbackStream,
|
||||
paCanNotWriteToACallbackStream,
|
||||
paCanNotReadFromAnOutputOnlyStream,
|
||||
paCanNotWriteToAnInputOnlyStream,
|
||||
paIncompatibleStreamHostApi,
|
||||
paBadBufferPtr
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBS.Audio.Internal.PortAudio
|
||||
{
|
||||
public static class Delegates
|
||||
{
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate Constants.PaStreamCallbackResult PaStreamCallbackDelegate(IntPtr input, IntPtr output, uint frameCount, ref Structures.PaStreamCallbackTimeInfo timeInfo, Constants.PaStreamCallbackFlags statusFlags, IntPtr userData);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void PaStreamFinishedCallbackDelegate(IntPtr userData);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBS.Audio.Internal.PortAudio.Linux
|
||||
{
|
||||
internal static class Methods
|
||||
{
|
||||
private const string LIBRARY_FILENAME = "libportaudio.so.2";
|
||||
|
||||
#region Initialization/Termination
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_Initialize();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_Terminate();
|
||||
#endregion
|
||||
#region Device Enumeration
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int Pa_GetDeviceCount();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int Pa_GetDefaultInputDevice();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int Pa_GetDefaultOutputDevice();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern IntPtr Pa_GetDeviceInfo(int device);
|
||||
#endregion
|
||||
#region Stream Initialization
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_OpenStream(out IntPtr stream, ref Structures.PaStreamParameters inputParameters, ref Structures.PaStreamParameters outputParameters, double sampleRate, uint framesPerBuffer, Audio.PortAudio.PortAudioStreamFlags streamFlags, Delegates.PaStreamCallbackDelegate streamCallback, IntPtr userData);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_OpenDefaultStream(out IntPtr stream, int numInputChannels, int numOutputChannels, uint sampleFormat, double sampleRate, uint framesPerBuffer, Delegates.PaStreamCallbackDelegate streamCallback, IntPtr userData);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_CloseStream(IntPtr stream);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_SetStreamFinishedCallback(ref IntPtr stream, [MarshalAs(UnmanagedType.FunctionPtr)] Delegates.PaStreamFinishedCallbackDelegate streamFinishedCallback);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_StartStream(IntPtr stream);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_StopStream(IntPtr stream);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_AbortStream(IntPtr stream);
|
||||
#endregion
|
||||
#region Stream Reading
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] float[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] byte[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] sbyte[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] ushort[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] short[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] uint[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] int[] buffer, uint frames);
|
||||
#endregion
|
||||
#region Stream Writing
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] float[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] byte[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] sbyte[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] ushort[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] short[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] uint[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] int[] buffer, uint frames);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,641 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBS.Audio.Internal.PortAudio
|
||||
{
|
||||
public static class Methods
|
||||
{
|
||||
#region Initialization/Termination
|
||||
public static Constants.PaError Pa_Initialize()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_Initialize();
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_Initialize();
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_Terminate()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_Terminate();
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_Terminate();
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
#endregion
|
||||
#region Device Enumeration
|
||||
public static int Pa_GetDeviceCount()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_GetDeviceCount();
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_GetDeviceCount();
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static int Pa_GetDefaultInputDevice()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_GetDefaultInputDevice();
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_GetDefaultInputDevice();
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static int Pa_GetDefaultOutputDevice()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_GetDefaultOutputDevice();
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_GetDefaultOutputDevice();
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Structures.PaDeviceInfo Pa_GetDeviceInfo(int index)
|
||||
{
|
||||
IntPtr ptr = IntPtr.Zero;
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
ptr = Internal.PortAudio.Linux.Methods.Pa_GetDeviceInfo(index);
|
||||
break;
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
ptr = Internal.PortAudio.Windows.Methods.Pa_GetDeviceInfo(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Structures.PaDeviceInfo devinfo = new Structures.PaDeviceInfo();
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
devinfo = (Structures.PaDeviceInfo)Marshal.PtrToStructure(ptr, typeof(Structures.PaDeviceInfo));
|
||||
}
|
||||
return devinfo;
|
||||
}
|
||||
#endregion
|
||||
#region Stream Initialization
|
||||
public static Constants.PaError Pa_OpenStream(out IntPtr stream, ref Structures.PaStreamParameters inputParameters, ref Structures.PaStreamParameters outputParameters, double sampleRate, uint framesPerBuffer, Audio.PortAudio.PortAudioStreamFlags streamFlags, Delegates.PaStreamCallbackDelegate streamCallback, IntPtr userData)
|
||||
{
|
||||
stream = IntPtr.Zero;
|
||||
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_OpenDefaultStream(out IntPtr stream, int numInputChannels, int numOutputChannels, uint sampleFormat, double sampleRate, uint framesPerBuffer, Delegates.PaStreamCallbackDelegate streamCallback, IntPtr userData)
|
||||
{
|
||||
stream = IntPtr.Zero;
|
||||
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_OpenDefaultStream(out stream, numInputChannels, numOutputChannels, sampleFormat, sampleRate, framesPerBuffer, streamCallback, userData);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_OpenDefaultStream(out stream, numInputChannels, numOutputChannels, sampleFormat, sampleRate, framesPerBuffer, streamCallback, userData);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_CloseStream(IntPtr stream)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_CloseStream(stream);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_CloseStream(stream);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_SetStreamFinishedCallback(ref IntPtr stream, [MarshalAs(UnmanagedType.FunctionPtr)] Delegates.PaStreamFinishedCallbackDelegate streamFinishedCallback)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_SetStreamFinishedCallback(ref stream, streamFinishedCallback);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_SetStreamFinishedCallback(ref stream, streamFinishedCallback);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_StartStream(IntPtr stream)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_StartStream(stream);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_StartStream(stream);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_StopStream(IntPtr stream)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_StopStream(stream);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_StopStream(stream);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_AbortStream(IntPtr stream)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_AbortStream(stream);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_AbortStream(stream);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
#endregion
|
||||
#region Stream Reading
|
||||
public static Constants.PaError Pa_ReadStream(IntPtr stream, [Out] float[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_ReadStream(IntPtr stream, [Out] byte[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_ReadStream(IntPtr stream, [Out] sbyte[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_ReadStream(IntPtr stream, [Out] ushort[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_ReadStream(IntPtr stream, [Out] short[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_ReadStream(IntPtr stream, [Out] uint[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_ReadStream(IntPtr stream, [Out] int[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_ReadStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
#endregion
|
||||
#region Stream Writing
|
||||
public static Constants.PaError Pa_WriteStream(IntPtr stream, [In] float[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_WriteStream(IntPtr stream, [In] byte[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_WriteStream(IntPtr stream, [In] sbyte[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_WriteStream(IntPtr stream, [In] ushort[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_WriteStream(IntPtr stream, [In] short[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_WriteStream(IntPtr stream, [In] uint[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
public static Constants.PaError Pa_WriteStream(IntPtr stream, [In] int[] buffer, uint frames)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
return Internal.PortAudio.Linux.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
case PlatformID.WinCE:
|
||||
case PlatformID.Xbox:
|
||||
{
|
||||
return Internal.PortAudio.Windows.Methods.Pa_WriteStream(stream, buffer, frames);
|
||||
}
|
||||
}
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static void Pa_ResultToException(Constants.PaError result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case Internal.PortAudio.Constants.PaError.paBadBufferPtr:
|
||||
throw new ArgumentException("Bad buffer pointer.");
|
||||
case Internal.PortAudio.Constants.PaError.paBadIODeviceCombination:
|
||||
throw new ArgumentException("Bad input/output device combination.");
|
||||
case Internal.PortAudio.Constants.PaError.paBadStreamPtr:
|
||||
throw new ArgumentException("Bad stream pointer.");
|
||||
case Internal.PortAudio.Constants.PaError.paBufferTooBig:
|
||||
throw new ArgumentException("Buffer too big.");
|
||||
case Internal.PortAudio.Constants.PaError.paBufferTooSmall:
|
||||
throw new ArgumentException("Buffer too small.");
|
||||
case Internal.PortAudio.Constants.PaError.paCanNotReadFromACallbackStream:
|
||||
throw new System.IO.IOException("Cannot read from a callback stream.");
|
||||
case Internal.PortAudio.Constants.PaError.paCanNotReadFromAnOutputOnlyStream:
|
||||
throw new System.IO.IOException("Cannot read from an output-only stream.");
|
||||
case Internal.PortAudio.Constants.PaError.paCanNotWriteToACallbackStream:
|
||||
throw new System.IO.IOException("Cannot write to a callback stream.");
|
||||
case Internal.PortAudio.Constants.PaError.paCanNotWriteToAnInputOnlyStream:
|
||||
throw new System.IO.IOException("Cannot write to an input-only stream.");
|
||||
case Internal.PortAudio.Constants.PaError.paDeviceUnavailable:
|
||||
throw new System.IO.IOException("The device is unavailable.");
|
||||
case Internal.PortAudio.Constants.PaError.paHostApiNotFound:
|
||||
throw new InvalidOperationException("Host API not found.");
|
||||
case Internal.PortAudio.Constants.PaError.paIncompatibleHostApiSpecificStreamInfo:
|
||||
throw new InvalidOperationException("Incompatible host API-specific stream information.");
|
||||
case Internal.PortAudio.Constants.PaError.paIncompatibleStreamHostApi:
|
||||
throw new InvalidOperationException("Incompatible stream host API.");
|
||||
case Internal.PortAudio.Constants.PaError.paInputOverflowed:
|
||||
throw new OverflowException("Input overflowed.");
|
||||
case Internal.PortAudio.Constants.PaError.paInsufficientMemory:
|
||||
throw new OutOfMemoryException("Insufficient memory.");
|
||||
case Internal.PortAudio.Constants.PaError.paInternalError:
|
||||
throw new Exception("Internal error.");
|
||||
case Internal.PortAudio.Constants.PaError.paInvalidChannelCount:
|
||||
throw new ArgumentException("Invalid channel count.");
|
||||
case Internal.PortAudio.Constants.PaError.paInvalidDevice:
|
||||
throw new ArgumentException("Invalid device.");
|
||||
case Internal.PortAudio.Constants.PaError.paInvalidFlag:
|
||||
throw new ArgumentException("Invalid flag.");
|
||||
case Internal.PortAudio.Constants.PaError.paInvalidHostApi:
|
||||
throw new ArgumentException("Invalid host API.");
|
||||
case Internal.PortAudio.Constants.PaError.paInvalidSampleRate:
|
||||
throw new ArgumentException("Invalid sample rate.");
|
||||
case Internal.PortAudio.Constants.PaError.paNotInitialized:
|
||||
throw new InvalidOperationException("PortAudio has not been initialized.");
|
||||
case Internal.PortAudio.Constants.PaError.paNullCallback:
|
||||
throw new InvalidOperationException("Null callback.");
|
||||
case Internal.PortAudio.Constants.PaError.paOutputUnderflowed:
|
||||
// throw new OverflowException("Output underflowed.");
|
||||
break;
|
||||
case Internal.PortAudio.Constants.PaError.paSampleFormatNotSupported:
|
||||
throw new NotSupportedException("Sample format not supported.");
|
||||
case Internal.PortAudio.Constants.PaError.paStreamIsNotStopped:
|
||||
throw new InvalidOperationException("Stream is not stopped.");
|
||||
case Internal.PortAudio.Constants.PaError.paStreamIsStopped:
|
||||
throw new InvalidOperationException("Stream is stopped.");
|
||||
case Internal.PortAudio.Constants.PaError.paTimedOut:
|
||||
throw new TimeoutException();
|
||||
case Internal.PortAudio.Constants.PaError.paUnanticipatedHostError:
|
||||
throw new Exception("Unanticipated host error.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBS.Audio.Internal.PortAudio
|
||||
{
|
||||
public static class Structures
|
||||
{
|
||||
public struct PaDeviceInfo
|
||||
{
|
||||
public int structVersion;
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string name;
|
||||
public int hostApi;
|
||||
public int maxInputChannels;
|
||||
public int maxOutputChannels;
|
||||
public double defaultLowInputLatency;
|
||||
public double defaultLowOutputLatency;
|
||||
public double defaultHighInputLatency;
|
||||
public double defaultHighOutputLatency;
|
||||
public double defaultSampleRate;
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Concat(new object[]
|
||||
{
|
||||
"[",
|
||||
base.GetType().Name,
|
||||
"]\nname: ",
|
||||
this.name,
|
||||
"\nhostApi: ",
|
||||
this.hostApi,
|
||||
"\nmaxInputChannels: ",
|
||||
this.maxInputChannels,
|
||||
"\nmaxOutputChannels: ",
|
||||
this.maxOutputChannels,
|
||||
"\ndefaultLowInputLatency: ",
|
||||
this.defaultLowInputLatency,
|
||||
"\ndefaultLowOutputLatency: ",
|
||||
this.defaultLowOutputLatency,
|
||||
"\ndefaultHighInputLatency: ",
|
||||
this.defaultHighInputLatency,
|
||||
"\ndefaultHighOutputLatency: ",
|
||||
this.defaultHighOutputLatency,
|
||||
"\ndefaultSampleRate: ",
|
||||
this.defaultSampleRate
|
||||
});
|
||||
}
|
||||
}
|
||||
public struct PaStreamCallbackTimeInfo
|
||||
{
|
||||
public double inputBufferAdcTime;
|
||||
public double currentTime;
|
||||
public double outputBufferDacTime;
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Concat(new object[]
|
||||
{
|
||||
"[",
|
||||
base.GetType().Name,
|
||||
"]\ncurrentTime: ",
|
||||
this.currentTime,
|
||||
"\ninputBufferAdcTime: ",
|
||||
this.inputBufferAdcTime,
|
||||
"\noutputBufferDacTime: ",
|
||||
this.outputBufferDacTime
|
||||
});
|
||||
}
|
||||
}
|
||||
public struct PaStreamParameters
|
||||
{
|
||||
public int device;
|
||||
public int channelCount;
|
||||
public AudioSampleFormat sampleFormat;
|
||||
public double suggestedLatency;
|
||||
public IntPtr hostApiSpecificStreamInfo;
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Concat(new object[]
|
||||
{
|
||||
"[",
|
||||
base.GetType().Name,
|
||||
"]\ndevice: ",
|
||||
this.device,
|
||||
"\nchannelCount: ",
|
||||
this.channelCount,
|
||||
"\nsampleFormat: ",
|
||||
this.sampleFormat,
|
||||
"\nsuggestedLatency: ",
|
||||
this.suggestedLatency
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBS.Audio.Internal.PortAudio.Windows
|
||||
{
|
||||
internal static class Methods
|
||||
{
|
||||
private const string LIBRARY_FILENAME = "PortAudio.dll";
|
||||
|
||||
#region Initialization/Termination
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_Initialize();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_Terminate();
|
||||
#endregion
|
||||
#region Device Enumeration
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int Pa_GetDeviceCount();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int Pa_GetDefaultInputDevice();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int Pa_GetDefaultOutputDevice();
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern IntPtr Pa_GetDeviceInfo(int device);
|
||||
#endregion
|
||||
#region Stream Initialization
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_OpenStream(out IntPtr stream, ref Structures.PaStreamParameters inputParameters, ref Structures.PaStreamParameters outputParameters, double sampleRate, uint framesPerBuffer, Audio.PortAudio.PortAudioStreamFlags streamFlags, Delegates.PaStreamCallbackDelegate streamCallback, IntPtr userData);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_OpenDefaultStream(out IntPtr stream, int numInputChannels, int numOutputChannels, uint sampleFormat, double sampleRate, uint framesPerBuffer, Delegates.PaStreamCallbackDelegate streamCallback, IntPtr userData);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_CloseStream(IntPtr stream);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_SetStreamFinishedCallback(ref IntPtr stream, [MarshalAs(UnmanagedType.FunctionPtr)] Delegates.PaStreamFinishedCallbackDelegate streamFinishedCallback);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_StartStream(IntPtr stream);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_StopStream(IntPtr stream);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_AbortStream(IntPtr stream);
|
||||
#endregion
|
||||
#region Stream Reading
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] float[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] byte[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] sbyte[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] ushort[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] short[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] uint[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_ReadStream(IntPtr stream, [Out] int[] buffer, uint frames);
|
||||
#endregion
|
||||
#region Stream Writing
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] float[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] byte[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] sbyte[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] ushort[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] short[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] uint[] buffer, uint frames);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern Constants.PaError Pa_WriteStream(IntPtr stream, [In] int[] buffer, uint frames);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
//
|
||||
// Constants.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack.Internal
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
public enum JackStatus
|
||||
{
|
||||
Success = 0x00,
|
||||
/// <summary>
|
||||
/// Overall operation failed.
|
||||
/// </summary>
|
||||
Failure = 0x01,
|
||||
/// <summary>
|
||||
/// The operation contained an invalid or unsupported option.
|
||||
/// </summary>
|
||||
InvalidOption = 0x02,
|
||||
/// <summary>
|
||||
/// The desired client name was not unique. With the @ref
|
||||
/// JackUseExactName option this situation is fatal. Otherwise,
|
||||
/// the name was modified by appending a dash and a two-digit
|
||||
/// number in the range "-01" to "-99". The
|
||||
/// jack_get_client_name() function will return the exact string
|
||||
/// that was used. If the specified @a client_name plus these
|
||||
/// extra characters would be too long, the open fails instead.
|
||||
/// </summary>
|
||||
NameNotUnique = 0x04,
|
||||
/// <summary>
|
||||
/// The JACK server was started as a result of this operation.
|
||||
/// Otherwise, it was running already. In either case the caller
|
||||
/// is now connected to jackd, so there is no race condition.
|
||||
/// When the server shuts down, the client will find out.
|
||||
/// </summary>
|
||||
ServerStarted = 0x08,
|
||||
/// <summary>
|
||||
/// Unable to connect to the JACK server.
|
||||
/// </summary>
|
||||
ServerFailed = 0x10,
|
||||
/// <summary>
|
||||
/// Communication error with the JACK server.
|
||||
/// </summary>
|
||||
ServerError = 0x20,
|
||||
/// <summary>
|
||||
/// Requested client does not exist.
|
||||
/// </summary>
|
||||
NoSuchClient = 0x40,
|
||||
/// <summary>
|
||||
/// Unable to load internal client
|
||||
/// </summary>
|
||||
LoadFailure = 0x80,
|
||||
/// <summary>
|
||||
/// Unable to initialize client
|
||||
/// </summary>
|
||||
InitFailure = 0x100,
|
||||
/// <summary>
|
||||
/// Unable to access shared memory
|
||||
/// </summary>
|
||||
ShmFailure = 0x200,
|
||||
/// <summary>
|
||||
/// Client's protocol version does not match
|
||||
/// </summary>
|
||||
VersionError = 0x400,
|
||||
/// <summary>
|
||||
/// Backend error
|
||||
/// </summary>
|
||||
BackendError = 0x800,
|
||||
/// <summary>
|
||||
/// Client zombified failure
|
||||
/// </summary>
|
||||
ClientZombie = 0x1000
|
||||
}
|
||||
|
||||
public enum JackPositionBits
|
||||
{
|
||||
/// <summary>
|
||||
/// Bar, Beat, Tick
|
||||
/// </summary>
|
||||
PositionBBT = 0x10,
|
||||
/// <summary>
|
||||
/// External timecode
|
||||
/// </summary>
|
||||
PositionTimecode = 0x20,
|
||||
/// <summary>
|
||||
/// Frame offset of BBT information
|
||||
/// </summary>
|
||||
BBTFrameOffset = 0x40,
|
||||
/// <summary>
|
||||
/// Audio frames per video frame.
|
||||
/// </summary>
|
||||
AudioVideoRatio = 0x80,
|
||||
/// <summary>
|
||||
/// Frame offset of first video frame.
|
||||
/// </summary>
|
||||
VideoFrameOffset = 0x100
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
namespace MBS.Audio.Jack.Internal
|
||||
{
|
||||
public class Delegates
|
||||
{
|
||||
/// <summary>
|
||||
/// Prototype for the client supplied function that is called
|
||||
/// whenever a port is registered or unregistered.
|
||||
/// </summary>
|
||||
/// <param name="port">the ID of the port</param>
|
||||
/// <param name="register">
|
||||
/// non-zero if the port is being registered, zero if the port is
|
||||
/// being unregistered
|
||||
/// </param>
|
||||
/// <param name="arg">pointer to a client supplied data</param>
|
||||
public delegate void JackPortRegistrationCallback(uint /*jack_port_id_t*/ port, int register, IntPtr arg);
|
||||
/// <summary>
|
||||
/// Prototype for the client supplied function that is called
|
||||
/// by the engine anytime there is work to be done.
|
||||
/// </summary>
|
||||
/// <param name="nframes">number of frames to process</param>
|
||||
/// <param name="arg">pointer to a client supplied structure</param>
|
||||
/// <returns>zero on success; non-zero on error</returns>
|
||||
public delegate int JackProcessCallback(uint /*jack_nframes_t*/ nframes, IntPtr arg);
|
||||
}
|
||||
}
|
||||
253
audio-dotnet/src/audio-dotnet/MBS.Audio/Jack/Internal/Methods.cs
Normal file
253
audio-dotnet/src/audio-dotnet/MBS.Audio/Jack/Internal/Methods.cs
Normal file
@ -0,0 +1,253 @@
|
||||
//
|
||||
// Methods.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBS.Audio.Jack.Internal
|
||||
{
|
||||
internal static class Methods
|
||||
{
|
||||
public const string LIBRARY_FILENAME = "jack";
|
||||
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern IntPtr /*jack_client_t*/ jack_client_open(string client_name, JackOpenOptions options, ref Constants.JackStatus status);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern IntPtr jack_get_client_name(IntPtr /*jack_client_t*/ client);
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int jack_client_name_size();
|
||||
|
||||
/// <summary>
|
||||
/// Start the JACK transport rolling. Any client can make this
|
||||
/// request at any time. It takes effect no sooner than the next
|
||||
/// process cycle, perhaps later if there are slow-sync clients.
|
||||
/// This function is realtime-safe.
|
||||
/// </summary>
|
||||
/// <see cref="jack_set_sync_callback" />
|
||||
/// <param name="client">the JACK client structure</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int jack_transport_start(IntPtr /*jack_client_t*/ client);
|
||||
|
||||
/// <summary>
|
||||
/// Stop the JACK transport. Any client can make this request at any
|
||||
/// time. It takes effect no sooner than the next process cycle,
|
||||
/// perhaps later if there are slow-sync clients. This function is
|
||||
/// realtime-safe.
|
||||
/// </summary>
|
||||
/// <param name="client">the JACK client structure</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern void jack_transport_stop(IntPtr /*jack_client_t*/ client);
|
||||
|
||||
/// <summary>
|
||||
/// Establish a connection between two ports. When a connection exists, data written to the source port will
|
||||
/// be available to be read at the destination port.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The port types must be identical.
|
||||
/// The <see cref="JackPortFlags" /> of the <paramref name="source_port" /> must include <see cref="JackPortFlags.IsOutput" />.
|
||||
/// The <see cref="JackPortFlags" /> of the <paramref name="destination_port" /> must include <see cref="JackPortFlags.IsInput" />.
|
||||
/// </remarks>
|
||||
/// <returns>0 on success, EEXIST if the connection is already made, otherwise a non-zero error code.</returns>
|
||||
/// <param name="client">Client.</param>
|
||||
/// <param name="source_port">Source port.</param>
|
||||
/// <param name="destination_port">Destination port.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int jack_connect(IntPtr /*jack_client_t*/ client, string source_port, string destination_port);
|
||||
|
||||
/// <summary>
|
||||
/// return JACK's current system time in microseconds, using the JACK clock source.
|
||||
/// </summary>
|
||||
/// <remarks>The value returned is guaranteed to be monotonic, but not linear.</remarks>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern ulong jack_get_time();
|
||||
|
||||
/// <summary>
|
||||
/// Tell the JACK server to call
|
||||
/// <paramref name="registration_callback" /> whenever a port is
|
||||
/// registered or unregistered, passing <paramref name="arg" /> as a
|
||||
/// parameter.
|
||||
///
|
||||
/// All "notification events" are received in a separated non RT thread,
|
||||
/// the code in the supplied function does not need to be
|
||||
/// suitable for real-time execution.
|
||||
///
|
||||
/// NOTE: this function cannot be called while the client is activated
|
||||
/// (after jack_activate has been called.)
|
||||
/// </summary>
|
||||
/// <returns>0 on success, otherwise a non-zero error code</returns>
|
||||
/// <param name="client">Client.</param>
|
||||
/// <param name="registration_callback">Registration callback.</param>
|
||||
/// <param name="arg">Argument.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int jack_set_port_registration_callback(IntPtr /*jack_client_t*/ client, Delegates.JackPortRegistrationCallback registration_callback, IntPtr arg);
|
||||
/// <summary>
|
||||
/// Tell the Jack server to call <paramref name="process_callback" />
|
||||
/// whenever there is work be done, passing <paramref name="arg" />
|
||||
/// as the second argument.
|
||||
///
|
||||
/// The code in the supplied function must be suitable for real-time
|
||||
/// execution.That means that it cannot call functions that might
|
||||
/// block for a long time. This includes malloc, free, printf,
|
||||
/// pthread_mutex_lock, sleep, wait, poll, select, pthread_join,
|
||||
/// pthread_cond_wait, etc, etc. See
|
||||
/// http://jackit.sourceforge.net/docs/design/design.html#SECTION00411000000000000000
|
||||
/// for more information.
|
||||
///
|
||||
/// NOTE: this function cannot be called while the client is activated
|
||||
/// (after jack_activate has been called.)
|
||||
/// </summary>
|
||||
/// <returns>The set process callback.</returns>
|
||||
/// <param name="client">Client.</param>
|
||||
/// <param name="process_callback">Process callback.</param>
|
||||
/// <param name="arg">Argument.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int jack_set_process_callback(IntPtr /*jack_client_t*/ client, Delegates.JackProcessCallback process_callback, IntPtr arg);
|
||||
|
||||
/// <summary>
|
||||
/// Tell the Jack server that the program is ready to start
|
||||
/// processing audio.
|
||||
/// </summary>
|
||||
/// <returns>0 on success, otherwise a non-zero error code</returns>
|
||||
/// <param name="handle">Handle.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int jack_activate(IntPtr handle);
|
||||
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern IntPtr /*jack_port_t*/ jack_port_register(IntPtr /*jack_client_t*/ client, string port_name, string port_type, JackPortFlags flags, uint buffer_size);
|
||||
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern IntPtr /*jack_port_t*/ jack_port_by_id(IntPtr /*jack_client_t*/ client, uint /*jack_port_id_t*/ port_id);
|
||||
|
||||
/// <summary>
|
||||
/// This returns a pointer to the memory area associated with the
|
||||
/// specified port. For an output port, it will be a memory area
|
||||
/// that can be written to; for an input port, it will be an area
|
||||
/// containing the data from the port's connection(s), or
|
||||
/// zero-filled. if there are multiple inbound connections, the data
|
||||
/// will be mixed appropriately.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Caching output ports is DEPRECATED in Jack 2.0, due to some new optimization(like "pipelining").
|
||||
/// Port buffers have to be retrieved in each callback for proper functioning.
|
||||
/// </remarks>
|
||||
/// <returns>A pointer to the memory area associated with the specified port.</returns>
|
||||
/// <param name="port">Port whose buffer is to be returned.</param>
|
||||
/// <param name="nframes">The number of frames to return.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern IntPtr /*void */ jack_port_get_buffer(IntPtr /*jack_port_t*/ port, uint /*jack_nframes_t*/ nframes);
|
||||
|
||||
[DllImport(LIBRARY_FILENAME, EntryPoint = "jack_port_get_buffer")]
|
||||
public static extern float[] jack_port_get_buffer_f(IntPtr /*jack_port_t*/ port, uint /*jack_nframes_t*/ nframes);
|
||||
|
||||
public static void jack_status_to_exception(Constants.JackStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Constants.JackStatus.BackendError: throw new InvalidOperationException("Backend error");
|
||||
case Constants.JackStatus.ClientZombie: throw new InvalidOperationException("Client zombified");
|
||||
case Constants.JackStatus.Failure: throw new Exception("General failure");
|
||||
case Constants.JackStatus.InitFailure: throw new Exception("Initialization failure");
|
||||
case Constants.JackStatus.InvalidOption: throw new ArgumentOutOfRangeException("Invalid operation", (Exception)null);
|
||||
case Constants.JackStatus.LoadFailure: throw new Exception("Load failure");
|
||||
case Constants.JackStatus.NameNotUnique: throw new ArgumentException("must be unique", "JackClient.Name");
|
||||
case Constants.JackStatus.NoSuchClient: throw new ArgumentException("no such client", "Client");
|
||||
case Constants.JackStatus.ServerError: throw new ServerException();
|
||||
case Constants.JackStatus.ServerFailed: throw new ServerException("unable to connect");
|
||||
case Constants.JackStatus.ServerStarted: break;
|
||||
case Constants.JackStatus.ShmFailure: throw new InsufficientMemoryException("unable to access shared memory");
|
||||
case Constants.JackStatus.Success: break;
|
||||
case Constants.JackStatus.VersionError: throw new VersionMismatchException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the port from the client, disconnecting any existing connections.
|
||||
/// </summary>
|
||||
/// <param name="client">Client.</param>
|
||||
/// <param name="port">Port.</param>
|
||||
/// <returns>0 if successful; nonzero otherwise</returns>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int jack_port_unregister(IntPtr /*jack_client_t*/ client, IntPtr /*jack_port_t*/ port);
|
||||
|
||||
/// <summary>
|
||||
/// The free function to be used on memory returned by jack_port_get_connections,
|
||||
/// jack_port_get_all_connections, jack_get_ports and jack_get_internal_client_name functions.
|
||||
/// This is MANDATORY on Windows when otherwise all nasty runtime version related crashes can occur.
|
||||
/// Developers are strongly encouraged to use this function instead of the standard "free" function in new code.
|
||||
/// </summary>
|
||||
/// <param name="value">Value.</param>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern void jack_free(IntPtr value);
|
||||
|
||||
/// <summary>
|
||||
/// Do not call this function; it is not implemented.
|
||||
/// </summary>
|
||||
/// <param name="client">Client on which to perform the operation.</param>
|
||||
[DllImport(LIBRARY_FILENAME), Obsolete("This function has never been implemented")]
|
||||
public static extern void jack_off(IntPtr /*jack_client_t*/ client);
|
||||
|
||||
/// <summary>
|
||||
/// Query the current transport state and position.
|
||||
///
|
||||
/// This function is realtime-safe, and can be called from any
|
||||
/// thread. If called from the process thread,
|
||||
/// <paramref name="pos" /> corresponds to the first frame of the
|
||||
/// current cycle and the state returned is valid for the entire
|
||||
/// cycle.
|
||||
/// </summary>
|
||||
/// <param name="client">the JACK client structure</param>
|
||||
/// <param name="pos">
|
||||
/// pointer to structure for returning current transport position;
|
||||
/// <paramref name="pos" />->valid will show which fields contain
|
||||
/// valid data. If <paramref name="pos" /> is NULL, do not return
|
||||
/// position information.
|
||||
/// </param>
|
||||
/// <returns>Current transport state.</returns>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern JackTransportState jack_transport_query(IntPtr /*const jack_client_t*/ client, ref Structures.jack_position_t pos);
|
||||
|
||||
/// <summary>
|
||||
/// Return an estimate of the current transport frame, including any
|
||||
/// time elapsed since the last transport positional update.
|
||||
/// </summary>
|
||||
/// <param name="client">the JACK client structure</param>
|
||||
/// <returns>an estimate of the current transport frame</returns>
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern uint jack_get_current_transport_frame(IntPtr /*jack_client_t*/ client);
|
||||
|
||||
/// <summary>
|
||||
/// Request a new transport position.
|
||||
///
|
||||
/// May be called at any time by any client. The new position takes
|
||||
/// effect in two process cycles.If there are slow-sync clients and
|
||||
/// the transport is already rolling, it will enter the
|
||||
/// ::JackTransportStarting state and begin invoking their @a
|
||||
/// sync_callbacks until ready.This function is realtime-safe.
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns>0 if valid request, EINVAL if position structure rejected</returns>
|
||||
/// <param name="client">client the JACK client structure</param>
|
||||
/// <param name="position">requested new transport position</param>
|
||||
/// <see cref="jack_transport_locate" />
|
||||
/// <see cref="jack_set_sync_callback" />
|
||||
[DllImport(LIBRARY_FILENAME)]
|
||||
public static extern int jack_transport_reposition(IntPtr /*jack_client_t*/ client, Structures.jack_position_t position);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
//
|
||||
// Structures.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack.Internal
|
||||
{
|
||||
internal class Structures
|
||||
{
|
||||
public struct jack_position_t
|
||||
{
|
||||
/* these four cannot be set from clients: the server sets them */
|
||||
/// <summary>
|
||||
/// unique ID
|
||||
/// </summary>
|
||||
public ulong /*jack_unique_t*/ unique_1;
|
||||
/// <summary>
|
||||
/// monotonic, free-rolling
|
||||
/// </summary>
|
||||
public ulong /*jack_time_t*/ usecs;
|
||||
/// <summary>
|
||||
/// current frame rate (per second)
|
||||
/// </summary>
|
||||
public uint /*jack_nframes_t*/ frame_rate;
|
||||
/// <summary>
|
||||
/// frame number, always present
|
||||
/// </summary>
|
||||
public uint /*jack_nframes_t*/ frame;
|
||||
|
||||
/// <summary>
|
||||
/// which other fields are valid
|
||||
/// </summary>
|
||||
public Constants.JackPositionBits valid;
|
||||
|
||||
/* JackPositionBBT fields: */
|
||||
/// <summary>
|
||||
/// Current bar.
|
||||
/// </summary>
|
||||
public int bar;
|
||||
/// <summary>
|
||||
/// Current beat-within-bar.
|
||||
/// </summary>
|
||||
public int beat;
|
||||
/// <summary>
|
||||
/// Current tick-within-beat.
|
||||
/// </summary>
|
||||
public int tick;
|
||||
public double bar_start_tick;
|
||||
|
||||
/// <summary>
|
||||
/// Time signatue numerator.
|
||||
/// </summary>
|
||||
public float beats_per_bar;
|
||||
/// <summary>
|
||||
/// Timee signature denominator.
|
||||
/// </summary>
|
||||
public float beat_type;
|
||||
public double ticks_per_beat;
|
||||
public double beats_per_minute;
|
||||
|
||||
/* JackPositionTimecode fields: (EXPERIMENTAL: could change) */
|
||||
/// <summary>
|
||||
/// Current time in seconds.
|
||||
/// </summary>
|
||||
public double frame_time;
|
||||
/// <summary>
|
||||
/// Next sequential frame_time (unless repositioned).
|
||||
/// </summary>
|
||||
public double next_time;
|
||||
|
||||
/* JackBBTFrameOffset fields: */
|
||||
/// <summary>
|
||||
/// frame offset for the BBT fields (the given bar, beat, and tick
|
||||
/// values actually refer to a time <c>frame_offset</c> frames
|
||||
/// before the start of the cycle), should be assumed to be 0 if
|
||||
/// JackBBTFrameOffset is not set.If JackBBTFrameOffset is set and
|
||||
/// this value is zero, the BBT time refers to the first frame of
|
||||
/// this cycle. If the value is positive, the BBT time refers to
|
||||
/// a frame that many frames before the start of the cycle.
|
||||
/// </summary>
|
||||
public uint /*jack_nframes_t*/ bbt_offset;
|
||||
|
||||
/* JACK video positional data (experimental) */
|
||||
/// <summary>
|
||||
/// number of audio frames per video frame. Should be assumed
|
||||
/// zero if JackAudioVideoRatio is not set. If
|
||||
/// JackAudioVideoRatio is set and the value is zero, no video
|
||||
/// data exists within the JACK graph
|
||||
/// </summary>
|
||||
public float audio_frames_per_video_frame;
|
||||
|
||||
/// <summary>
|
||||
/// audio frame at which the first video frame in this cycle
|
||||
/// occurs. Should be assumed to be 0 if JackVideoFrameOffset
|
||||
/// is not set. If JackVideoFrameOffset is set, but the value is
|
||||
/// zero, there is no video frame within this cycle.
|
||||
/// </summary>
|
||||
public uint /*jack_nframes_t*/ video_offset;
|
||||
|
||||
/// <summary>
|
||||
/// For binary compatibility, new fields should be allocated from
|
||||
/// this padding area with new valid bits controlling access, so
|
||||
/// the existing structure size and offsets are preserved.
|
||||
/// </summary>
|
||||
public int padding1 /*[7]*/;
|
||||
public int padding2 /*[7]*/;
|
||||
|
||||
|
||||
public int padding3 /*[7]*/;
|
||||
public int padding4 /*[7]*/;
|
||||
public int padding5 /*[7]*/;
|
||||
public int padding6 /*[7]*/;
|
||||
public int padding7 /*[7]*/;
|
||||
|
||||
/// <summary>
|
||||
/// When (<see cref="unique_1" /> == <see cref="unique_2" />) the
|
||||
/// contents are consistent.
|
||||
/// </summary>
|
||||
public ulong /*jack_unique_t*/ unique_2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
//
|
||||
// JackAudioEngine.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public class JackAudioEngine : AudioEngine
|
||||
{
|
||||
public override Guid ID => new Guid("{658df958-7d57-482d-ac14-caca38e8d249}");
|
||||
public override string Title => "JACK";
|
||||
|
||||
public override AudioDevice DefaultInputDevice => throw new NotImplementedException();
|
||||
|
||||
public override AudioDevice DefaultOutputDevice => throw new NotImplementedException();
|
||||
|
||||
protected override AudioStream CreateAudioStreamInternal(AudioDevice inputDevice, short inputChannelCount, AudioSampleFormat inputSampleFormat, double inputSuggestedLatency, AudioDevice outputDevice, short outputChannelCount, AudioSampleFormat outputSampleFormat, double outputSuggestedLatency, double sampleRate, int framesPerBuffer, AudioStreamFlags flags)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void TerminateInternal()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
277
audio-dotnet/src/audio-dotnet/MBS.Audio/Jack/JackClient.cs
Normal file
277
audio-dotnet/src/audio-dotnet/MBS.Audio/Jack/JackClient.cs
Normal file
@ -0,0 +1,277 @@
|
||||
//
|
||||
// JackClient.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public class JackClient
|
||||
{
|
||||
public JackTransport Transport { get; private set; } = null;
|
||||
|
||||
public JackClient()
|
||||
{
|
||||
_registration_callback_d = new Internal.Delegates.JackPortRegistrationCallback(_registration_callback);
|
||||
_process_callback_d = new Internal.Delegates.JackProcessCallback(_process_callback);
|
||||
Transport = new JackTransport(this);
|
||||
}
|
||||
public JackClient(string name) : this()
|
||||
{
|
||||
if (name.Length > MaximumNameLength)
|
||||
{
|
||||
|
||||
}
|
||||
Name = name;
|
||||
Transport = new JackTransport(this);
|
||||
}
|
||||
|
||||
private int? _MaximumNameLength = null;
|
||||
public int MaximumNameLength
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_MaximumNameLength == null)
|
||||
{
|
||||
_MaximumNameLength = Internal.Methods.jack_client_name_size();
|
||||
}
|
||||
return _MaximumNameLength.GetValueOrDefault(0);
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr Handle { get; private set; } = IntPtr.Zero;
|
||||
public string Name { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual name of the client as assigned by the JACK server.
|
||||
/// </summary>
|
||||
/// <value>The name of the client.</value>
|
||||
public string ClientName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Handle == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
IntPtr hName = Internal.Methods.jack_get_client_name(Handle);
|
||||
string value = System.Runtime.InteropServices.Marshal.PtrToStringAuto(hName);
|
||||
|
||||
Internal.Methods.jack_free(hName);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open an external client session with a JACK server. Clients may
|
||||
/// choose which of several servers to connect, and control
|
||||
/// whether and how to start the server automatically, if it was not
|
||||
/// already running. There is also an option for JACK to generate a
|
||||
/// unique client name, when necessary.
|
||||
/// </summary>
|
||||
/// <param name="options"><see cref="JackOpenOptions" /> for opening an external client.</param>
|
||||
public void Open(JackOpenOptions options = JackOpenOptions.None)
|
||||
{
|
||||
string clientName = Name;
|
||||
if (clientName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Name));
|
||||
}
|
||||
|
||||
Internal.Constants.JackStatus status = Internal.Constants.JackStatus.Success;
|
||||
IntPtr handle = Internal.Methods.jack_client_open(clientName, options, ref status);
|
||||
|
||||
// set up the event handlers
|
||||
// Internal.Methods.jack_set_process_callback(handle, _process_callback_d, IntPtr.Zero);
|
||||
Internal.Methods.jack_set_port_registration_callback(handle, _registration_callback_d, IntPtr.Zero);
|
||||
|
||||
Internal.Methods.jack_status_to_exception(status);
|
||||
Handle = handle;
|
||||
}
|
||||
|
||||
private Internal.Delegates.JackPortRegistrationCallback _registration_callback_d;
|
||||
private void _registration_callback(uint /*jack_port_id_t*/ port, int register, IntPtr arg)
|
||||
{
|
||||
IntPtr hPort = Internal.Methods.jack_port_by_id(Handle, port);
|
||||
if (hPort != IntPtr.Zero)
|
||||
{
|
||||
JackPortRegisteredEventArgs e = new JackPortRegisteredEventArgs(hPort);
|
||||
OnPortRegistered(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Internal.Delegates.JackProcessCallback _process_callback_d;
|
||||
private int _process_callback(uint nframes, IntPtr arg)
|
||||
{
|
||||
JackProcessEventArgs ee = new JackProcessEventArgs(nframes, arg);
|
||||
OnProcess(ee);
|
||||
return ee.ReturnValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tell the Jack server that the program is ready to start
|
||||
/// processing audio.
|
||||
/// </summary>
|
||||
public void Activate()
|
||||
{
|
||||
Internal.Methods.jack_activate(Handle);
|
||||
}
|
||||
|
||||
public event EventHandler<JackProcessEventArgs> Process;
|
||||
protected virtual void OnProcess(JackProcessEventArgs e)
|
||||
{
|
||||
Process?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public event EventHandler<JackPortRegisteredEventArgs> PortRegistered;
|
||||
protected virtual void OnPortRegistered(JackPortRegisteredEventArgs e)
|
||||
{
|
||||
PortRegistered?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public void Connect(string sourcePortName, string destinationPortName)
|
||||
{
|
||||
if (Handle == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("please Open() the JackClient first!");
|
||||
}
|
||||
Internal.Methods.jack_connect(Handle, sourcePortName, destinationPortName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new port for the client. This is an object used for moving
|
||||
/// data of any type in or out of the client. Ports may be connected
|
||||
/// in various ways.
|
||||
///
|
||||
/// Each port has a short name. The port's full name contains the name
|
||||
/// of the client concatenated with a colon (:) followed by its short
|
||||
/// name. The jack_port_name_size() is the maximum length of this full
|
||||
/// name. Exceeding that will cause the port registration to fail and
|
||||
/// return NULL.
|
||||
///
|
||||
/// The @a port_name must be unique among all ports owned by this client.
|
||||
/// If the name is not unique, the registration will fail.
|
||||
///
|
||||
/// All ports have a type, which may be any non-NULL and non-zero
|
||||
/// length string, passed as an argument. Some port types are built
|
||||
/// into the JACK API, currently only JACK_DEFAULT_AUDIO_TYPE.
|
||||
/// </summary>
|
||||
/// <param name="portName">
|
||||
/// Non-empty short name for the new port, not including the leading
|
||||
/// <c>"client_name:"</c>. Must be unique within the client.
|
||||
/// </param>
|
||||
/// <param name="portType">
|
||||
/// Port type name. If longer than
|
||||
/// <see cref="JackPort.MaximumTypeNameLength" />, only that many
|
||||
/// characters are significant.
|
||||
/// </param>
|
||||
/// <param name="flags">Flags.</param>
|
||||
/// <param name="bufferSize">
|
||||
/// Must be non-zero if this is not a built-in <c>port_type</c>.
|
||||
/// Otherwise, it is ignored
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A <see cref="JackInputPort" /> or <see cref="JackOutputPort" />,
|
||||
/// depending on the value of <paramref name="flags" />, on success;
|
||||
/// otherwise, <see langword="null" />.
|
||||
/// </returns>
|
||||
private JackPort RegisterPort(string portName, string portType, JackPortFlags flags, long bufferSize)
|
||||
{
|
||||
IntPtr handle = Internal.Methods.jack_port_register(Handle, portName, portType, flags, (uint)bufferSize);
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("jack client not valid");
|
||||
}
|
||||
|
||||
if ((flags & JackPortFlags.IsInput) == JackPortFlags.IsInput)
|
||||
{
|
||||
JackInputPort port = new JackInputPort(this, handle, portName, portType, bufferSize, (flags & JackPortFlags.CanMonitor) == JackPortFlags.CanMonitor, (flags & JackPortFlags.IsPhysical) == JackPortFlags.IsPhysical, (flags & JackPortFlags.IsTerminal) == JackPortFlags.IsTerminal);
|
||||
return port;
|
||||
}
|
||||
else if ((flags & JackPortFlags.IsOutput) == JackPortFlags.IsOutput)
|
||||
{
|
||||
JackOutputPort port = new JackOutputPort(this, handle, portName, portType, bufferSize, (flags & JackPortFlags.CanMonitor) == JackPortFlags.CanMonitor, (flags & JackPortFlags.IsPhysical) == JackPortFlags.IsPhysical, (flags & JackPortFlags.IsTerminal) == JackPortFlags.IsTerminal);
|
||||
return port;
|
||||
}
|
||||
throw new InvalidOperationException("port must be either input or output");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new input port for the client. This is an object used for
|
||||
/// moving data of any type into the client. Ports may be connected
|
||||
/// in various ways.
|
||||
///
|
||||
/// Each port has a short name. The port's full name contains the name
|
||||
/// of the client concatenated with a colon (:) followed by its short
|
||||
/// name. The jack_port_name_size() is the maximum length of this full
|
||||
/// name. Exceeding that will cause the port registration to fail and
|
||||
/// return NULL.
|
||||
///
|
||||
/// The <paramref name="portName" /> must be unique among all ports
|
||||
/// owned by this client. If the name is not unique, the registration
|
||||
/// will fail.
|
||||
///
|
||||
/// All ports have a type, which may be any non-NULL and non-zero
|
||||
/// length string, passed as an argument. Some port types are built
|
||||
/// into the JACK API, currently only
|
||||
/// <see cref="JackPortTypes.DefaultAudioType"/> and
|
||||
/// <see cref="JackPortTypes.DefaultMidiType" />.
|
||||
/// </summary>
|
||||
/// <param name="portName">
|
||||
/// Non-empty short name for the new port, not including the leading
|
||||
/// <c>"client_name:"</c>. Must be unique within the client.
|
||||
/// </param>
|
||||
/// <param name="portType">
|
||||
/// Port type name. If longer than
|
||||
/// <see cref="JackPort.MaximumTypeNameLength" />, only that many
|
||||
/// characters are significant.
|
||||
/// </param>
|
||||
/// <param name="flags">Flags.</param>
|
||||
/// <param name="bufferSize">
|
||||
/// Must be non-zero if this is not a built-in <c>port_type</c>.
|
||||
/// Otherwise, it is ignored
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A <see cref="JackInputPort" /> or <see cref="JackOutputPort" />,
|
||||
/// depending on the value of <paramref name="flags" />, on success;
|
||||
/// otherwise, <see langword="null" />.
|
||||
/// </returns>
|
||||
public JackInputPort RegisterInput(string portName, string portType = null, long bufferSize = 0, bool isMonitor = false, bool isPhysical = false, bool isTerminal = false)
|
||||
{
|
||||
JackPortFlags flags = JackPortFlags.IsInput;
|
||||
if (isMonitor) flags |= JackPortFlags.CanMonitor;
|
||||
if (isPhysical) flags |= JackPortFlags.IsPhysical;
|
||||
if (isTerminal) flags |= JackPortFlags.IsTerminal;
|
||||
if (portType == null) portType = JackPort.DefaultPortType;
|
||||
return (JackInputPort)RegisterPort(portName, portType, flags, bufferSize);
|
||||
}
|
||||
public JackOutputPort RegisterOutput(string portName, string portType = null, long bufferSize = 0, bool isMonitor = false, bool isPhysical = false, bool isTerminal = false)
|
||||
{
|
||||
JackPortFlags flags = JackPortFlags.IsOutput;
|
||||
if (isMonitor) flags |= JackPortFlags.CanMonitor;
|
||||
if (isPhysical) flags |= JackPortFlags.IsPhysical;
|
||||
if (isTerminal) flags |= JackPortFlags.IsTerminal;
|
||||
if (portType == null) portType = JackPort.DefaultPortType;
|
||||
return (JackOutputPort)RegisterPort(portName, portType, flags, bufferSize);
|
||||
}
|
||||
|
||||
public void UnregisterPort(JackPort port)
|
||||
{
|
||||
Internal.Methods.jack_port_unregister(Handle, port.Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
/// <summary>
|
||||
/// The base class for all JACK <see cref="Exception" />s that are not
|
||||
/// provided by the un
|
||||
/// </summary>
|
||||
public class JackException : Exception
|
||||
{
|
||||
public JackException()
|
||||
{
|
||||
}
|
||||
|
||||
protected JackException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
public JackException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public JackException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
//
|
||||
// JackInputPort.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public class JackInputPort : JackPort
|
||||
{
|
||||
public JackInputPort(JackClient client, IntPtr handle, string portName, string portType, long bufferSize, bool isMonitor, bool isPhysical, bool isTerminal) : base(client, handle, portName, portType, bufferSize, true, false, isMonitor, isPhysical, isTerminal)
|
||||
{
|
||||
}
|
||||
|
||||
public float[] Read(long frameCount)
|
||||
{
|
||||
uint fc = (uint)frameCount;
|
||||
IntPtr hBuffer = Internal.Methods.jack_port_get_buffer(Handle, fc);
|
||||
|
||||
float[] buffer = new float[fc];
|
||||
System.Runtime.InteropServices.Marshal.Copy(hBuffer, buffer, 0, (int)fc);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
//
|
||||
// JackOpenOptions.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public enum JackOpenOptions
|
||||
{
|
||||
None = 0x00,
|
||||
/// <summary>
|
||||
/// Do not automatically start the JACK server when it is not
|
||||
/// already running. This option is always selected if
|
||||
/// $JACK_NO_START_SERVER is defined in the calling process
|
||||
/// environment.
|
||||
/// </summary>
|
||||
NoStartServer = 0x01,
|
||||
/// <summary>
|
||||
/// Use the exact client name requested. Otherwise, JACK
|
||||
/// automatically generates a unique one, if needed.
|
||||
/// </summary>
|
||||
UseExactName = 0x02,
|
||||
/// <summary>
|
||||
/// Open with optional <c>server_name</c> parameter.
|
||||
/// </summary>
|
||||
ServerName = 0x04,
|
||||
/// <summary>
|
||||
/// Pass a SessionID Token this allows the sessionmanager to identify the client again.
|
||||
/// </summary>
|
||||
SessionID = 0x20
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
//
|
||||
// JackOutputPort.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public class JackOutputPort : JackPort
|
||||
{
|
||||
public JackOutputPort(JackClient client, IntPtr handle, string portName, string portType, long bufferSize, bool isMonitor, bool isPhysical, bool isTerminal) : base(client, handle, portName, portType, bufferSize, false, true, isMonitor, isPhysical, isTerminal)
|
||||
{
|
||||
}
|
||||
|
||||
public void Write(float[] buffer, long frameCount)
|
||||
{
|
||||
uint fc = (uint)frameCount;
|
||||
IntPtr hBuffer = Internal.Methods.jack_port_get_buffer(Handle, fc);
|
||||
|
||||
System.Runtime.InteropServices.Marshal.Copy(buffer, 0, hBuffer, (int)fc);
|
||||
}
|
||||
}
|
||||
}
|
||||
91
audio-dotnet/src/audio-dotnet/MBS.Audio/Jack/JackPort.cs
Normal file
91
audio-dotnet/src/audio-dotnet/MBS.Audio/Jack/JackPort.cs
Normal file
@ -0,0 +1,91 @@
|
||||
//
|
||||
// JackPort.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public abstract class JackPort
|
||||
{
|
||||
public JackClient Client { get; private set; } = null;
|
||||
public IntPtr Handle { get; private set; } = IntPtr.Zero;
|
||||
|
||||
public string Name { get; private set; } = null;
|
||||
public string Type { get; private set; } = null;
|
||||
public long BufferSize { get; private set; } = 0;
|
||||
|
||||
public static string DefaultPortType = JackPortTypes.DefaultAudioType;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the port can receive data.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if the port can receive data; otherwise, <c>false</c>.</value>
|
||||
public bool IsInput { get; private set; } = false;
|
||||
/// <summary>
|
||||
/// Indicates that data can be read from the port.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if data can be read from the port; otherwise, <c>false</c>.</value>
|
||||
public bool IsOutput { get; private set; } = false;
|
||||
/// <summary>
|
||||
/// Indicates that a call on this port to
|
||||
/// <see cref="JackPort.RequestMonitor"/> makes sense.
|
||||
///
|
||||
/// Precisely what this means is dependent on the client. A typical
|
||||
/// result of it being called with TRUE as the second argument is
|
||||
/// that data that would be available from an output port (with
|
||||
/// <see cref="IsPhysical" /> set) is sent to a physical output connector
|
||||
/// as well, so that it can be heard/seen/whatever.
|
||||
///
|
||||
/// Clients that do not control physical interfaces
|
||||
/// should never create ports with this bit set.
|
||||
/// </summary>
|
||||
public bool CanMonitor { get; private set; } = false;
|
||||
/// <summary>
|
||||
/// Indicates that the port corresponds to some kind of physical I/O connector.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is physical; otherwise, <c>false</c>.</value>
|
||||
public bool IsPhysical { get; private set; } = false;
|
||||
/// <summary>
|
||||
/// For an input port, indicates that data received by the port will
|
||||
/// not be passed on or made available at any other port.
|
||||
///
|
||||
/// For an output port, indicates that data available at the port
|
||||
/// does not originate from any other port.
|
||||
///
|
||||
/// Audio synthesizers, I/O hardware interface clients, HDR
|
||||
/// systems are examples of clients that would set this flag for
|
||||
/// their ports.
|
||||
/// </summary>
|
||||
public bool IsTerminal { get; private set; } = false;
|
||||
|
||||
internal JackPort(JackClient client, IntPtr handle, string portName, string portType, long bufferSize, bool isInput, bool isOutput, bool canMonitor, bool isPhysical, bool isTerminal)
|
||||
{
|
||||
Client = client;
|
||||
Handle = handle;
|
||||
Name = portName;
|
||||
Type = portType;
|
||||
BufferSize = bufferSize;
|
||||
IsInput = isInput;
|
||||
IsOutput = isOutput;
|
||||
CanMonitor = canMonitor;
|
||||
IsPhysical = isPhysical;
|
||||
IsTerminal = isTerminal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
//
|
||||
// JackPortFlags.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
/// <summary>
|
||||
/// A port has a set of flags that are formed by AND-ing together the
|
||||
/// desired values from this enum. The flags <see cref="IsInput" /> and
|
||||
/// <see cref="IsOutput" /> are mutually exclusive and it is an error to
|
||||
/// use them both.
|
||||
/// </summary>
|
||||
[Flags()]
|
||||
public enum JackPortFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// The port can receive data.
|
||||
/// </summary>
|
||||
IsInput = 0x1,
|
||||
/// <summary>
|
||||
/// Data can be read from the port.
|
||||
/// </summary>
|
||||
IsOutput = 0x2,
|
||||
/// <summary>
|
||||
/// The port corresponds to some kind of physical I/O connector.
|
||||
/// </summary>
|
||||
IsPhysical = 0x4,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a call on this port to
|
||||
/// <see cref="JackPort.RequestMonitor"/> makes sense.
|
||||
///
|
||||
/// Precisely what this means is dependent on the client. A typical
|
||||
/// result of it being called with TRUE as the second argument is
|
||||
/// that data that would be available from an output port (with
|
||||
/// <see cref="IsPhysical" /> set) is sent to a physical output connector
|
||||
/// as well, so that it can be heard/seen/whatever.
|
||||
///
|
||||
/// Clients that do not control physical interfaces
|
||||
/// should never create ports with this bit set.
|
||||
/// </summary>
|
||||
CanMonitor = 0x8,
|
||||
/// <summary>
|
||||
/// For an input port, indicates that data received by the port will
|
||||
/// not be passed on or made available at any other port.
|
||||
///
|
||||
/// For an output port, indicates that data available at the port
|
||||
/// does not originate from any other port.
|
||||
///
|
||||
/// Audio synthesizers, I/O hardware interface clients, HDR
|
||||
/// systems are examples of clients that would set this flag for
|
||||
/// their ports.
|
||||
/// </summary>
|
||||
IsTerminal = 0x10,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public class JackPortRegisteredEventArgs : EventArgs
|
||||
{
|
||||
public IntPtr Handle { get; private set; } = IntPtr.Zero;
|
||||
|
||||
public JackPortRegisteredEventArgs(IntPtr handle)
|
||||
{
|
||||
Handle = handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// JackPortTypes.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public static class JackPortTypes
|
||||
{
|
||||
public const string DefaultAudioType = "32 bit float mono audio";
|
||||
public const string DefaultMidiType = "8 bit raw midi";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public class JackProcessEventArgs : EventArgs
|
||||
{
|
||||
public long FrameCount { get; private set; } = 0;
|
||||
public IntPtr UserData { get; private set; } = IntPtr.Zero;
|
||||
public int ReturnValue { get; set; } = 0;
|
||||
|
||||
public JackProcessEventArgs(long nframes, IntPtr arg)
|
||||
{
|
||||
FrameCount = nframes;
|
||||
UserData = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
146
audio-dotnet/src/audio-dotnet/MBS.Audio/Jack/JackTransport.cs
Normal file
146
audio-dotnet/src/audio-dotnet/MBS.Audio/Jack/JackTransport.cs
Normal file
@ -0,0 +1,146 @@
|
||||
//
|
||||
// JackTransport.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public class JackTransport : ITransport
|
||||
{
|
||||
public JackClient Client { get; private set; }
|
||||
|
||||
internal JackTransport(JackClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
|
||||
public JackTransportState TransportState
|
||||
{
|
||||
get
|
||||
{
|
||||
Internal.Structures.jack_position_t pos = new Internal.Structures.jack_position_t();
|
||||
JackTransportState state = Internal.Methods.jack_transport_query(Client.Handle, ref pos);
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
public AudioTimestamp Timestamp
|
||||
{
|
||||
get
|
||||
{
|
||||
Internal.Structures.jack_position_t pos = new Internal.Structures.jack_position_t();
|
||||
JackTransportState state = Internal.Methods.jack_transport_query(Client.Handle, ref pos);
|
||||
|
||||
return AudioTimestamp.FromSamples((long)pos.frame, (int)pos.frame_rate, pos.bar, pos.beat, pos.tick, pos.beats_per_bar, pos.ticks_per_beat);
|
||||
}
|
||||
set
|
||||
{
|
||||
Internal.Structures.jack_position_t pos = new Internal.Structures.jack_position_t();
|
||||
Internal.Methods.jack_transport_query(Client.Handle, ref pos);
|
||||
|
||||
pos.bar = value.Bars;
|
||||
pos.beat = value.Beats;
|
||||
pos.tick = value.Ticks;
|
||||
|
||||
pos.frame_time = (uint)(value.TotalSamples);
|
||||
pos.valid = Internal.Constants.JackPositionBits.PositionTimecode;
|
||||
Internal.Methods.jack_transport_reposition(Client.Handle, pos);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPlaying => State != AudioPlayerState.Stopped;
|
||||
|
||||
public AudioPlayerState State
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (TransportState)
|
||||
{
|
||||
case JackTransportState.Looping:
|
||||
case JackTransportState.Rolling:
|
||||
{
|
||||
return AudioPlayerState.Playing;
|
||||
}
|
||||
case JackTransportState.NetworkStarting:
|
||||
case JackTransportState.Starting:
|
||||
{
|
||||
return AudioPlayerState.Stopped;
|
||||
}
|
||||
case JackTransportState.Stopped:
|
||||
{
|
||||
if (_paused)
|
||||
return AudioPlayerState.Paused;
|
||||
return AudioPlayerState.Stopped;
|
||||
}
|
||||
}
|
||||
return AudioPlayerState.Stopped;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _paused = false;
|
||||
|
||||
public event EventHandler<AudioPlayerStateChangedEventArgs> StateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Start the JACK transport rolling. Any client can make this
|
||||
/// request at any time. It takes effect no sooner than the next
|
||||
/// process cycle, perhaps later if there are slow-sync clients.
|
||||
/// This function is realtime-safe.
|
||||
/// </summary>
|
||||
public void Play()
|
||||
{
|
||||
_paused = false;
|
||||
Internal.Methods.jack_transport_start(Client.Handle);
|
||||
}
|
||||
/// <summary>
|
||||
/// Stop the JACK transport. Any client can make this request at any
|
||||
/// time. It takes effect no sooner than the next process cycle,
|
||||
/// perhaps later if there are slow-sync clients. This function is
|
||||
/// realtime-safe.
|
||||
/// </summary>
|
||||
public void Pause()
|
||||
{
|
||||
_paused = !_paused;
|
||||
Internal.Methods.jack_transport_stop(Client.Handle);
|
||||
}
|
||||
/// <summary>
|
||||
/// Stop the JACK transport and reset the position to the beginning.
|
||||
/// Any client can make this request at any time. It takes effect no
|
||||
/// sooner than the next process cycle, perhaps later if there are
|
||||
/// slow-sync clients. This function is realtime-safe.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
Pause();
|
||||
Seek(0);
|
||||
|
||||
_paused = false;
|
||||
}
|
||||
|
||||
public void Seek(long totalSamples)
|
||||
{
|
||||
// TODO: implement this
|
||||
Internal.Structures.jack_position_t pos = new Internal.Structures.jack_position_t();
|
||||
pos.valid = Internal.Constants.JackPositionBits.PositionTimecode;
|
||||
pos.frame_time = totalSamples;
|
||||
Internal.Methods.jack_transport_reposition(Client.Handle, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
//
|
||||
// JackTransportState.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
/// <summary>
|
||||
/// Transport states.
|
||||
/// </summary>
|
||||
public enum JackTransportState
|
||||
{
|
||||
/* the order matters for binary compatibility */
|
||||
/// <summary>
|
||||
/// Transport is halted.
|
||||
/// </summary>
|
||||
Stopped = 0,
|
||||
/// <summary>
|
||||
/// Transport is playing.
|
||||
/// </summary>
|
||||
Rolling = 1,
|
||||
/// <summary>
|
||||
/// Ignored.
|
||||
/// </summary>
|
||||
Looping = 2,
|
||||
/// <summary>
|
||||
/// Waiting for sync ready.
|
||||
/// </summary>
|
||||
Starting = 3,
|
||||
/// <summary>
|
||||
/// Waiting for sync ready on the network.
|
||||
/// </summary>
|
||||
NetworkStarting = 4,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
namespace MBS.Audio.Jack.Networking.Internal
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
public static readonly System.Net.IPAddress DEFAULT_MULTICAST_IP = System.Net.IPAddress.Parse("225.3.19.154");
|
||||
public const int DEFAULT_PORT = 19000;
|
||||
public const int DEFAULT_MTU = 1500;
|
||||
public const int MASTER_NAME_SIZE = 256;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBS.Audio.Jack.Networking.Internal
|
||||
{
|
||||
internal static class Methods
|
||||
{
|
||||
[DllImport(Jack.Internal.Methods.LIBRARY_FILENAME)]
|
||||
public static extern IntPtr /*jack_net_slave_t*/ jack_net_slave_open(string ip, int port, string name, ref Internal.Structures.jack_slave_t request, ref Internal.Structures.jack_master_t result);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBS.Audio.Jack.Networking.Internal
|
||||
{
|
||||
internal static class Structures
|
||||
{
|
||||
public struct jack_slave_t
|
||||
{
|
||||
public int audio_input; // from master or to slave (-1 to take master audio physical inputs)
|
||||
public int audio_output; // to master or from slave (-1 to take master audio physical outputs)
|
||||
public int midi_input; // from master or to slave (-1 to take master MIDI physical inputs)
|
||||
public int midi_output; // to master or from slave (-1 to take master MIDI physical outputs)
|
||||
public int mtu; // network Maximum Transmission Unit
|
||||
public int time_out; // in second, -1 means infinite
|
||||
public int encoder; // encoder type (one of JackNetEncoder)
|
||||
public int kbps; // KB per second for CELT or OPUS codec
|
||||
public int latency; // network latency in number of buffers
|
||||
}
|
||||
|
||||
public struct jack_master_t
|
||||
{
|
||||
public int audio_input; // master audio physical outputs (-1 to take slave wanted audio inputs)
|
||||
public int audio_output; // master audio physical inputs (-1 to take slave wanted audio outputs)
|
||||
public int midi_input; // master MIDI physical outputs (-1 to take slave wanted MIDI inputs)
|
||||
public int midi_output; // master MIDI physical inputs (-1 to take slave wanted MIDI outputs)
|
||||
public uint /*jack_nframes_t*/ buffer_size; // master buffer size
|
||||
public uint /*jack_nframes_t*/ sample_rate; // master sample rate
|
||||
[MarshalAs(UnmanagedType.LPWStr, SizeConst = Constants.MASTER_NAME_SIZE)]
|
||||
public string master_name; // master machine name
|
||||
int time_out; // in second, -1 means infinite
|
||||
int partial_cycle; // if 'true', partial buffers will be used
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
namespace MBS.Audio.Jack.Networking
|
||||
{
|
||||
public class JackNetworkSlave
|
||||
{
|
||||
public IntPtr Handle { get; private set; } = IntPtr.Zero;
|
||||
|
||||
public void Open(System.Net.IPAddress ipAddress, int port, string name)
|
||||
{
|
||||
Internal.Structures.jack_slave_t request = new Internal.Structures.jack_slave_t();
|
||||
Internal.Structures.jack_master_t result = new Internal.Structures.jack_master_t();
|
||||
|
||||
IntPtr handle = Internal.Methods.jack_net_slave_open(ipAddress.ToString(), port, name, ref request, ref result);
|
||||
if (handle != IntPtr.Zero)
|
||||
{
|
||||
Handle = handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public class ServerException : JackException
|
||||
{
|
||||
public ServerException()
|
||||
{
|
||||
}
|
||||
|
||||
protected ServerException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
public ServerException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ServerException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace MBS.Audio.Jack
|
||||
{
|
||||
public class VersionMismatchException : JackException
|
||||
{
|
||||
public VersionMismatchException() : base("client protocol version mismatch")
|
||||
{
|
||||
}
|
||||
|
||||
protected VersionMismatchException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
public VersionMismatchException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public VersionMismatchException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
118
audio-dotnet/src/audio-dotnet/MBS.Audio/MBS.Audio.csproj
Normal file
118
audio-dotnet/src/audio-dotnet/MBS.Audio/MBS.Audio.csproj
Normal file
@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{E0897B7B-617A-4709-A4C6-FC0F6B441B2A}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MBS.Audio</RootNamespace>
|
||||
<AssemblyName>MBS.Audio</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\Output\Release</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Internal\PortAudio\Constants.cs" />
|
||||
<Compile Include="Internal\PortAudio\Delegates.cs" />
|
||||
<Compile Include="Internal\PortAudio\Linux\Methods.cs" />
|
||||
<Compile Include="Internal\PortAudio\Methods.cs" />
|
||||
<Compile Include="Internal\PortAudio\Structures.cs" />
|
||||
<Compile Include="Internal\PortAudio\Windows\Methods.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Jack\JackClient.cs" />
|
||||
<Compile Include="Jack\JackOpenOptions.cs" />
|
||||
<Compile Include="Jack\Internal\Methods.cs" />
|
||||
<Compile Include="Jack\Internal\Constants.cs" />
|
||||
<Compile Include="Jack\JackPort.cs" />
|
||||
<Compile Include="Jack\JackPortFlags.cs" />
|
||||
<Compile Include="Jack\JackInputPort.cs" />
|
||||
<Compile Include="Jack\JackOutputPort.cs" />
|
||||
<Compile Include="Jack\JackPortTypes.cs" />
|
||||
<Compile Include="Jack\JackTransport.cs" />
|
||||
<Compile Include="Jack\JackTransportState.cs" />
|
||||
<Compile Include="Jack\Internal\Structures.cs" />
|
||||
<Compile Include="Jack\ServerException.cs" />
|
||||
<Compile Include="Jack\JackException.cs" />
|
||||
<Compile Include="Jack\VersionMismatchException.cs" />
|
||||
<Compile Include="Jack\JackPortRegisteredEvent.cs" />
|
||||
<Compile Include="Jack\Internal\Delegates.cs" />
|
||||
<Compile Include="Jack\JackProcessEvent.cs" />
|
||||
<Compile Include="Jack\Networking\JackNetworkSlave.cs" />
|
||||
<Compile Include="Jack\Networking\Internal\Methods.cs" />
|
||||
<Compile Include="Jack\Networking\Internal\Structures.cs" />
|
||||
<Compile Include="Jack\Networking\Internal\Constants.cs" />
|
||||
<Compile Include="PortAudio\PortAudioDevice.cs" />
|
||||
<Compile Include="PortAudio\PortAudioStream.cs" />
|
||||
<Compile Include="ITransport.cs" />
|
||||
<Compile Include="BarBeatTick.cs" />
|
||||
<Compile Include="AudioTimestamp.cs" />
|
||||
<Compile Include="AudioPlayerState.cs" />
|
||||
<Compile Include="AudioPlayerStateChangedEvent.cs" />
|
||||
<Compile Include="AudioPlayerStateChangedReason.cs" />
|
||||
<Compile Include="AudioSampleFormat.cs" />
|
||||
<Compile Include="PortAudio\PortAudioEngine.cs" />
|
||||
<Compile Include="AudioEngine.cs" />
|
||||
<Compile Include="Jack\JackAudioEngine.cs" />
|
||||
<Compile Include="CustomTransport.cs" />
|
||||
<Compile Include="Metronome\Metronome.cs" />
|
||||
<Compile Include="AudioDevice.cs" />
|
||||
<Compile Include="AudioStream.cs" />
|
||||
<Compile Include="AudioStreamFlags.cs" />
|
||||
<Compile Include="PortAudio\PortAudioStreamFlags.cs" />
|
||||
<Compile Include="AudioPlayer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\UniversalEditor\Libraries\UniversalEditor.Essential\UniversalEditor.Essential.csproj">
|
||||
<Project>{30467E5C-05BC-4856-AADC-13906EF4CADD}</Project>
|
||||
<Name>UniversalEditor.Essential</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\UniversalEditor\Plugins\UniversalEditor.Plugins.Multimedia\UniversalEditor.Plugins.Multimedia.csproj">
|
||||
<Project>{BE4D0BA3-0888-42A5-9C09-FC308A4509D2}</Project>
|
||||
<Name>UniversalEditor.Plugins.Multimedia</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\UniversalEditor\Libraries\UniversalEditor.Core\UniversalEditor.Core.csproj">
|
||||
<Project>{2D4737E6-6D95-408A-90DB-8DFF38147E85}</Project>
|
||||
<Name>UniversalEditor.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Jack\" />
|
||||
<Folder Include="Jack\Internal\" />
|
||||
<Folder Include="Jack\Networking\" />
|
||||
<Folder Include="Jack\Networking\Internal\" />
|
||||
<Folder Include="PortAudio\" />
|
||||
<Folder Include="Metronome\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
179
audio-dotnet/src/audio-dotnet/MBS.Audio/Metronome/Metronome.cs
Normal file
179
audio-dotnet/src/audio-dotnet/MBS.Audio/Metronome/Metronome.cs
Normal file
@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MBS.Audio.PortAudio;
|
||||
using UniversalEditor;
|
||||
using UniversalEditor.Accessors;
|
||||
using UniversalEditor.DataFormats.Multimedia.Audio.Waveform.MicrosoftWave;
|
||||
using UniversalEditor.ObjectModels.Multimedia.Audio.Waveform;
|
||||
|
||||
namespace MBS.Audio.Metronome
|
||||
{
|
||||
public class Metronome
|
||||
{
|
||||
public AudioEngine AudioEngine { get; } = null;
|
||||
|
||||
public string AudioSamplePath { get; set; } = String.Empty;
|
||||
|
||||
private Dictionary<string, WaveformAudioObjectModel> waves = new Dictionary<string, WaveformAudioObjectModel>();
|
||||
|
||||
private double mvarTempo = 120.0;
|
||||
public double Tempo { get { return mvarTempo; } set { mvarTempo = value; } }
|
||||
|
||||
private System.Threading.Thread _thread = null;
|
||||
|
||||
public bool IsPlaying
|
||||
{
|
||||
get { return (_thread != null && _thread.IsAlive); }
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_thread != null)
|
||||
{
|
||||
_thread.Abort();
|
||||
_thread = null;
|
||||
}
|
||||
_thread = new System.Threading.Thread(_thread_ThreadStart);
|
||||
_thread.Start();
|
||||
}
|
||||
public void Stop()
|
||||
{
|
||||
if (_thread == null) return;
|
||||
_thread.Abort();
|
||||
_thread = null;
|
||||
}
|
||||
|
||||
|
||||
private void _thread_ThreadStart()
|
||||
{
|
||||
WaveformAudioObjectModel click = waves["Click"];
|
||||
|
||||
PortAudioStream stream = new PortAudioStream(AudioEngine.DefaultInputDevice as PortAudioDevice, 2, AudioSampleFormat.Int16, AudioEngine.DefaultOutputDevice as PortAudioDevice, click.Header.ChannelCount, AudioSampleFormat.Int16, click.Header.SampleRate * click.Header.ChannelCount, 0, AudioStreamFlags.ClipOff);
|
||||
|
||||
WaveformAudioObjectModel one = waves["One"];
|
||||
WaveformAudioObjectModel two = waves["Two"];
|
||||
WaveformAudioObjectModel three = waves["Three"];
|
||||
WaveformAudioObjectModel four = waves["Four"];
|
||||
|
||||
WaveformAudioObjectModel[] countoffs = new WaveformAudioObjectModel[]
|
||||
{
|
||||
one,
|
||||
null,
|
||||
two,
|
||||
null,
|
||||
one,
|
||||
two,
|
||||
three,
|
||||
four
|
||||
};
|
||||
|
||||
// 1/120 minutes per beat =
|
||||
double bpm = (1000 - (mvarTempo * ((double)500 / (double)120)));
|
||||
int ms = (int)bpm;
|
||||
|
||||
int icountoff = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// short[] rawSamples = (click.RawSamples.Clone() as short[]);
|
||||
short[] rawSamples = click.RawSamples.RawData;
|
||||
|
||||
if (icountoff < countoffs.Length)
|
||||
{
|
||||
WaveformAudioObjectModel countoff = countoffs[icountoff];
|
||||
if (countoff != null)
|
||||
{
|
||||
rawSamples = countoff.RawSamples.RawData;
|
||||
/*
|
||||
// mix the countoff into the click
|
||||
if (countoff.RawSamples.Length > click.RawSamples.Length)
|
||||
{
|
||||
rawSamples = (countoff.RawSamples.Clone() as short[]);
|
||||
for (int i = 0; i < click.RawSamples.Length; i++)
|
||||
{
|
||||
rawSamples[i] = (short)((click.RawSamples[i] + rawSamples[i]) - ((click.RawSamples[i] + rawSamples[i]) / short.MaxValue));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < rawSamples.Length; i++)
|
||||
{
|
||||
rawSamples[i] = (short)((countoff.RawSamples[i] + rawSamples[i]) - ((countoff.RawSamples[i] + rawSamples[i]) / short.MaxValue));
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
stream.Write(rawSamples);
|
||||
OnTick(EventArgs.Empty);
|
||||
|
||||
System.Threading.Thread.Sleep(ms - 30);
|
||||
|
||||
if (icountoff <= countoffs.Length) icountoff++;
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler Tick;
|
||||
protected virtual void OnTick(EventArgs e)
|
||||
{
|
||||
if (Tick != null) Tick(this, e);
|
||||
}
|
||||
|
||||
public Metronome(string path, double tempo = 120.0)
|
||||
{
|
||||
string[] FileNames = new string[]
|
||||
{
|
||||
path + System.IO.Path.DirectorySeparatorChar.ToString() + "Click.wav",
|
||||
path + System.IO.Path.DirectorySeparatorChar.ToString() + "One.wav",
|
||||
path + System.IO.Path.DirectorySeparatorChar.ToString() + "Two.wav",
|
||||
path + System.IO.Path.DirectorySeparatorChar.ToString() + "Three.wav",
|
||||
path + System.IO.Path.DirectorySeparatorChar.ToString() + "Four.wav"
|
||||
};
|
||||
|
||||
foreach (string filename in FileNames)
|
||||
{
|
||||
string filetitle = System.IO.Path.GetFileNameWithoutExtension(filename);
|
||||
WaveformAudioObjectModel wave = new WaveformAudioObjectModel();
|
||||
MicrosoftWaveDataFormat wav = new MicrosoftWaveDataFormat();
|
||||
Document.Load(wave, wav, new FileAccessor(filename, false, false), true);
|
||||
|
||||
waves.Add(filetitle, wave);
|
||||
}
|
||||
|
||||
WaveformAudioObjectModel one = waves["One"];
|
||||
WaveformAudioObjectModel two = waves["Two"];
|
||||
WaveformAudioObjectModel three = waves["Three"];
|
||||
WaveformAudioObjectModel four = waves["Four"];
|
||||
WaveformAudioObjectModel click = waves["Click"];
|
||||
WaveformAudioObjectModel[] countoffs = new WaveformAudioObjectModel[] { one, two, three, four };
|
||||
|
||||
foreach (WaveformAudioObjectModel countoff in countoffs)
|
||||
{
|
||||
short[] rawSamples = null;
|
||||
if (countoff.RawSamples.Length > click.RawSamples.Length)
|
||||
{
|
||||
rawSamples = countoff.RawSamples.RawData;
|
||||
for (int i = 0; i < click.RawSamples.Length; i++)
|
||||
{
|
||||
rawSamples[i] = (short)((click.RawSamples[i] + rawSamples[i]) - ((click.RawSamples[i] + rawSamples[i]) / (short.MaxValue + 1)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rawSamples = (click.RawSamples.Clone() as short[]);
|
||||
for (int i = 0; i < rawSamples.Length; i++)
|
||||
{
|
||||
rawSamples[i] = (short)((countoff.RawSamples[i] + rawSamples[i]) - ((countoff.RawSamples[i] + rawSamples[i]) / (short.MaxValue + 1)));
|
||||
}
|
||||
countoff.RawSamples = new WaveformAudioSamples(rawSamples);
|
||||
}
|
||||
}
|
||||
|
||||
AudioSamplePath = path;
|
||||
mvarTempo = tempo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.PortAudio
|
||||
{
|
||||
public class PortAudioDevice : AudioDevice
|
||||
{
|
||||
internal PortAudioDevice(int handle)
|
||||
{
|
||||
Handle = handle;
|
||||
|
||||
Internal.PortAudio.Structures.PaDeviceInfo devinfo = Internal.PortAudio.Methods.Pa_GetDeviceInfo(handle);
|
||||
_maximumInputChannels = devinfo.maxInputChannels;
|
||||
_maximumOutputChannels = devinfo.maxOutputChannels;
|
||||
_defaultSampleRate = devinfo.defaultSampleRate;
|
||||
_defaultLowInputLatency = devinfo.defaultLowInputLatency;
|
||||
_defaultHighInputLatency = devinfo.defaultHighInputLatency;
|
||||
_defaultLowOutputLatency = devinfo.defaultLowOutputLatency;
|
||||
_defaultHighOutputLatency = devinfo.defaultHighOutputLatency;
|
||||
HostAPI = devinfo.hostApi;
|
||||
Name = devinfo.name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public int HostAPI { get; } = 0;
|
||||
|
||||
private int _maximumInputChannels;
|
||||
public override int MaximumInputChannels => _maximumInputChannels;
|
||||
private int _maximumOutputChannels;
|
||||
public override int MaximumOutputChannels => _maximumOutputChannels;
|
||||
|
||||
private double _defaultHighInputLatency;
|
||||
public override double DefaultHighInputLatency => _defaultHighInputLatency;
|
||||
private double _defaultHighOutputLatency;
|
||||
public override double DefaultHighOutputLatency => _defaultHighOutputLatency;
|
||||
private double _defaultLowInputLatency;
|
||||
public override double DefaultLowInputLatency => _defaultLowInputLatency;
|
||||
private double _defaultLowOutputLatency;
|
||||
public override double DefaultLowOutputLatency => _defaultLowOutputLatency;
|
||||
|
||||
private double _defaultSampleRate;
|
||||
public override double DefaultSampleRate => _defaultSampleRate;
|
||||
public int Handle { get; } = 0;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
//
|
||||
// PortAudioEngine.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MBS.Audio.PortAudio
|
||||
{
|
||||
public class PortAudioEngine : AudioEngine
|
||||
{
|
||||
private PortAudioDevice mvarDefaultInput = null;
|
||||
public override AudioDevice DefaultInputDevice
|
||||
{
|
||||
get
|
||||
{
|
||||
int defaultOutputDeviceHandle = Internal.PortAudio.Methods.Pa_GetDefaultInputDevice();
|
||||
mvarDefaultOutput = new PortAudioDevice(defaultOutputDeviceHandle);
|
||||
return mvarDefaultOutput;
|
||||
}
|
||||
}
|
||||
private PortAudioDevice mvarDefaultOutput = null;
|
||||
public override AudioDevice DefaultOutputDevice
|
||||
{
|
||||
get
|
||||
{
|
||||
int defaultOutputDeviceHandle = Internal.PortAudio.Methods.Pa_GetDefaultOutputDevice();
|
||||
mvarDefaultOutput = new PortAudioDevice(defaultOutputDeviceHandle);
|
||||
return mvarDefaultOutput;
|
||||
}
|
||||
}
|
||||
|
||||
public override Guid ID => new Guid("{361b1dd6-b3d7-4358-bdee-b8741f48c7fe}");
|
||||
public override string Title => "PortAudio";
|
||||
|
||||
private static PortAudioDevice[] mvarDevices = null;
|
||||
public static PortAudioDevice[] GetDevices()
|
||||
{
|
||||
if (mvarDevices == null || mvarDevices.Length == 0)
|
||||
{
|
||||
List<PortAudioDevice> devices = new List<PortAudioDevice>();
|
||||
int count = Internal.PortAudio.Methods.Pa_GetDeviceCount();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
PortAudioDevice device = new PortAudioDevice(i);
|
||||
devices.Add(device);
|
||||
}
|
||||
mvarDevices = devices.ToArray();
|
||||
}
|
||||
return mvarDevices;
|
||||
}
|
||||
|
||||
public PortAudioDevice OpenAudioDevice(int handle)
|
||||
{
|
||||
return new PortAudioDevice(handle);
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
Internal.PortAudio.Constants.PaError result = Internal.PortAudio.Methods.Pa_Initialize();
|
||||
Internal.PortAudio.Methods.Pa_ResultToException(result);
|
||||
|
||||
int defaultInputDeviceHandle = Internal.PortAudio.Methods.Pa_GetDefaultInputDevice();
|
||||
Internal.PortAudio.Methods.Pa_ResultToException(result);
|
||||
|
||||
int defaultOutputDeviceHandle = Internal.PortAudio.Methods.Pa_GetDefaultOutputDevice();
|
||||
Internal.PortAudio.Methods.Pa_ResultToException(result);
|
||||
mvarDefaultInput = new PortAudioDevice(defaultInputDeviceHandle);
|
||||
mvarDefaultOutput = new PortAudioDevice(defaultOutputDeviceHandle);
|
||||
}
|
||||
|
||||
protected override AudioStream CreateAudioStreamInternal(AudioDevice inputDevice, short inputChannelCount, AudioSampleFormat inputSampleFormat, double inputSuggestedLatency, AudioDevice outputDevice, short outputChannelCount, AudioSampleFormat outputSampleFormat, double outputSuggestedLatency, double sampleRate, int framesPerBuffer, AudioStreamFlags flags)
|
||||
{
|
||||
return new PortAudioStream(inputDevice, inputChannelCount, inputSampleFormat, inputSuggestedLatency, outputDevice, outputChannelCount, outputSampleFormat, outputSuggestedLatency, sampleRate, framesPerBuffer, flags);
|
||||
}
|
||||
|
||||
protected override void TerminateInternal()
|
||||
{
|
||||
Internal.PortAudio.Constants.PaError result = Internal.PortAudio.Methods.Pa_Terminate();
|
||||
Internal.PortAudio.Methods.Pa_ResultToException(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MBS.Audio.PortAudio
|
||||
{
|
||||
public class PortAudioStream : AudioStream
|
||||
{
|
||||
public PortAudioStream(AudioDevice inputDevice, AudioDevice outputDevice, AudioSampleFormat sampleFormat)
|
||||
: this(inputDevice, outputDevice, sampleFormat, sampleFormat)
|
||||
{
|
||||
}
|
||||
public PortAudioStream(AudioDevice inputDevice, AudioDevice outputDevice, AudioSampleFormat sampleFormat, double sampleRate)
|
||||
: this(inputDevice, outputDevice, sampleFormat, sampleFormat, sampleRate)
|
||||
{
|
||||
}
|
||||
public PortAudioStream(AudioDevice inputDevice, AudioDevice outputDevice, AudioSampleFormat inputSampleFormat, AudioSampleFormat outputSampleFormat)
|
||||
: this(inputDevice, inputDevice.MaximumInputChannels, inputSampleFormat, outputDevice, outputDevice.MaximumOutputChannels, outputSampleFormat, outputDevice.DefaultSampleRate, 0)
|
||||
{
|
||||
}
|
||||
public PortAudioStream(AudioDevice inputDevice, AudioDevice outputDevice, AudioSampleFormat inputSampleFormat, AudioSampleFormat outputSampleFormat, double sampleRate)
|
||||
: this(inputDevice, inputDevice.MaximumInputChannels, inputSampleFormat, outputDevice, outputDevice.MaximumOutputChannels, outputSampleFormat, sampleRate, 0)
|
||||
{
|
||||
}
|
||||
public PortAudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double sampleRate)
|
||||
: this(inputDevice, inputChannelCount, inputSampleFormat, (inputDevice == null) ? 0 : inputDevice.DefaultLowInputLatency, outputDevice, outputChannelCount, outputSampleFormat, (outputDevice == null) ? 0 : outputDevice.DefaultLowOutputLatency, sampleRate, 0, AudioStreamFlags.None)
|
||||
{
|
||||
}
|
||||
public PortAudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double sampleRate, int framesPerBuffer)
|
||||
: this(inputDevice, inputChannelCount, inputSampleFormat, (inputDevice == null) ? 0 : inputDevice.DefaultLowInputLatency, outputDevice, outputChannelCount, outputSampleFormat, (outputDevice == null) ? 0 : outputDevice.DefaultLowOutputLatency, sampleRate, framesPerBuffer, AudioStreamFlags.None)
|
||||
{
|
||||
}
|
||||
public PortAudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double sampleRate, int framesPerBuffer, AudioStreamFlags flags)
|
||||
: this(inputDevice, inputChannelCount, inputSampleFormat, (inputDevice == null) ? 0 : inputDevice.DefaultLowInputLatency, outputDevice, outputChannelCount, outputSampleFormat, (outputDevice == null) ? 0 : outputDevice.DefaultLowOutputLatency, sampleRate, framesPerBuffer, flags)
|
||||
{
|
||||
}
|
||||
public PortAudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, double inputSuggestedLatency, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double outputSuggestedLatency, double sampleRate, int framesPerBuffer)
|
||||
: this(inputDevice, inputChannelCount, inputSampleFormat, inputSuggestedLatency, outputDevice, outputChannelCount, outputSampleFormat, outputSuggestedLatency, sampleRate, framesPerBuffer, AudioStreamFlags.None)
|
||||
{
|
||||
}
|
||||
public PortAudioStream(AudioDevice inputDevice, int inputChannelCount, AudioSampleFormat inputSampleFormat, double inputSuggestedLatency, AudioDevice outputDevice, int outputChannelCount, AudioSampleFormat outputSampleFormat, double outputSuggestedLatency, double sampleRate, int framesPerBuffer, AudioStreamFlags flags) :
|
||||
base(inputDevice, inputChannelCount, inputSampleFormat, inputSuggestedLatency, outputDevice, outputChannelCount, outputSampleFormat, outputSuggestedLatency, sampleRate, framesPerBuffer, flags)
|
||||
{
|
||||
}
|
||||
|
||||
private IntPtr _handle = IntPtr.Zero;
|
||||
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
base.InitializeInternal();
|
||||
|
||||
Internal.PortAudio.Structures.PaStreamParameters inputParameters = new Internal.PortAudio.Structures.PaStreamParameters();
|
||||
Internal.PortAudio.Structures.PaStreamParameters outputParameters = new Internal.PortAudio.Structures.PaStreamParameters();
|
||||
|
||||
if (InputDevice != null)
|
||||
{
|
||||
inputParameters.channelCount = InputChannelCount;
|
||||
inputParameters.device = (InputDevice as PortAudioDevice).Handle;
|
||||
inputParameters.sampleFormat = InputSampleFormat;
|
||||
inputParameters.suggestedLatency = InputSuggestedLatency;
|
||||
}
|
||||
else if (OutputDevice != null)
|
||||
{
|
||||
inputParameters.channelCount = OutputChannelCount;
|
||||
inputParameters.device = (OutputDevice as PortAudioDevice).Handle;
|
||||
inputParameters.sampleFormat = OutputSampleFormat;
|
||||
inputParameters.suggestedLatency = OutputSuggestedLatency;
|
||||
}
|
||||
|
||||
if (OutputDevice != null)
|
||||
{
|
||||
outputParameters.channelCount = OutputChannelCount;
|
||||
outputParameters.device = (OutputDevice as PortAudioDevice).Handle;
|
||||
outputParameters.sampleFormat = OutputSampleFormat;
|
||||
outputParameters.suggestedLatency = OutputSuggestedLatency;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputParameters.channelCount = InputChannelCount;
|
||||
outputParameters.device = (InputDevice as PortAudioDevice).Handle;
|
||||
outputParameters.sampleFormat = InputSampleFormat;
|
||||
outputParameters.suggestedLatency = InputSuggestedLatency;
|
||||
}
|
||||
mvarOutputChannelCount = outputParameters.channelCount;
|
||||
|
||||
Internal.PortAudio.Delegates.PaStreamCallbackDelegate streamCallback = null; // new Internal.PortAudio.Delegates.PaStreamCallbackDelegate(_streamCallback);
|
||||
IntPtr userData = IntPtr.Zero;
|
||||
|
||||
Internal.PortAudio.Constants.PaError result1 = Internal.PortAudio.Methods.Pa_OpenStream(out _handle, ref inputParameters, ref outputParameters, SampleRate, (uint)FramesPerBuffer, AudioStreamFlagsToPortAudioStreamFlags(Flags), streamCallback, userData);
|
||||
if (result1 == Internal.PortAudio.Constants.PaError.paNoError)
|
||||
{
|
||||
Internal.PortAudio.Constants.PaError result2 = Internal.PortAudio.Methods.Pa_StartStream(_handle);
|
||||
Internal.PortAudio.Methods.Pa_ResultToException(result2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// result1 = Internal.PortAudio.Methods.Pa_OpenDefaultStream(out mvarHandle, inputChannelCount, outputChannelCount, (uint)outputSampleFormat, sampleRate, framesPerBuffer, streamCallback, userData);
|
||||
Internal.PortAudio.Methods.Pa_ResultToException(result1);
|
||||
}
|
||||
}
|
||||
|
||||
private static PortAudioStreamFlags AudioStreamFlagsToPortAudioStreamFlags(AudioStreamFlags flags)
|
||||
{
|
||||
PortAudioStreamFlags flags2 = PortAudioStreamFlags.None;
|
||||
if ((flags & AudioStreamFlags.ClipOff) == AudioStreamFlags.ClipOff) flags2 |= PortAudioStreamFlags.ClipOff;
|
||||
if ((flags & AudioStreamFlags.DitherOff) == AudioStreamFlags.DitherOff) flags2 |= PortAudioStreamFlags.DitherOff;
|
||||
if ((flags & AudioStreamFlags.NeverDropInput) == AudioStreamFlags.NeverDropInput) flags2 |= PortAudioStreamFlags.NeverDropInput;
|
||||
// if ((flags & AudioStreamFlags.PlatformSpecificFlags) == AudioStreamFlags.PlatformSpecificFlags) flags2 |= PortAudioStreamFlags.PlatformSpecificFlags;
|
||||
if ((flags & AudioStreamFlags.PrimeOutputBuffersUsingStreamCallback) == AudioStreamFlags.PrimeOutputBuffersUsingStreamCallback) flags2 |= PortAudioStreamFlags.PrimeOutputBuffersUsingStreamCallback;
|
||||
return flags2;
|
||||
}
|
||||
|
||||
private int mvarOutputChannelCount = 0;
|
||||
|
||||
private Internal.PortAudio.Constants.PaStreamCallbackResult _streamCallback(IntPtr input, IntPtr output, uint frameCount, ref Internal.PortAudio.Structures.PaStreamCallbackTimeInfo timeInfo, Internal.PortAudio.Constants.PaStreamCallbackFlags statusFlags, IntPtr userData)
|
||||
{
|
||||
return Internal.PortAudio.Constants.PaStreamCallbackResult.paComplete;
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
Internal.PortAudio.Methods.Pa_StopStream(_handle);
|
||||
Internal.PortAudio.Methods.Pa_StartStream(_handle);
|
||||
}
|
||||
|
||||
public override void Read(short[] buffer)
|
||||
{
|
||||
Internal.PortAudio.Constants.PaError result = Internal.PortAudio.Methods.Pa_ReadStream(_handle, buffer, (uint)buffer.Length);
|
||||
Internal.PortAudio.Methods.Pa_ResultToException(result);
|
||||
}
|
||||
|
||||
public override void Write(short[] buffer)
|
||||
{
|
||||
Internal.PortAudio.Constants.PaError result = Internal.PortAudio.Methods.Pa_WriteStream(_handle, buffer, (uint)(buffer.Length));
|
||||
Internal.PortAudio.Methods.Pa_ResultToException(result);
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
Internal.PortAudio.Constants.PaError result = Internal.PortAudio.Methods.Pa_WriteStream(_handle, buffer, (uint)count);
|
||||
Internal.PortAudio.Methods.Pa_ResultToException(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
namespace MBS.Audio.PortAudio
|
||||
{
|
||||
[Flags()]
|
||||
public enum PortAudioStreamFlags : uint
|
||||
{
|
||||
None,
|
||||
ClipOff,
|
||||
DitherOff,
|
||||
NeverDropInput = 4u,
|
||||
PrimeOutputBuffersUsingStreamCallback = 8u,
|
||||
PlatformSpecificFlags = 4294901760u
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Intelligent Sound Engine")]
|
||||
[assembly: AssemblyDescription("Interface with PortAudio for .NET")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Mike Becker's Software")]
|
||||
[assembly: AssemblyProduct("Intelligent Sound Engine")]
|
||||
[assembly: AssemblyCopyright("Copyright ©2012 Mike Becker's Software")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2f5f69df-3795-4d81-9717-950d69f6ab21")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework 4")]
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
b8e3b9299bf3d3b899326bf341d4c6b745e38237
|
||||
@ -0,0 +1,10 @@
|
||||
/home/beckermj/Documents/Projects/MBS_OLD/MBS.Audio/Output/Debug/MBS.Audio.dll
|
||||
/home/beckermj/Documents/Projects/MBS_OLD/MBS.Audio/Output/Debug/MBS.Audio.pdb
|
||||
/home/beckermj/Documents/Projects/MBS_OLD/MBS.Audio/Libraries/MBS.Audio/obj/Debug/MBS.Audio.csproj.CoreCompileInputs.cache
|
||||
/home/beckermj/Documents/Projects/MBS_OLD/MBS.Audio/Libraries/MBS.Audio/obj/Debug/MBS.Audio.csproj.CopyComplete
|
||||
/home/beckermj/Documents/Projects/MBS_OLD/MBS.Audio/Libraries/MBS.Audio/obj/Debug/MBS.Audio.dll
|
||||
/home/beckermj/Documents/Projects/MBS_OLD/MBS.Audio/Libraries/MBS.Audio/obj/Debug/MBS.Audio.pdb
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/audio-dotnet/MBS.Audio/obj/Debug/MBS.Audio.csproj.AssemblyReference.cache
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/audio-dotnet/MBS.Audio/obj/Debug/MBS.Audio.csproj.CoreCompileInputs.cache
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/audio-dotnet/MBS.Audio/obj/Debug/MBS.Audio.dll
|
||||
/home/beckermj/Documents/Projects/alcetech/audio-dotnet/audio-dotnet/src/audio-dotnet/MBS.Audio/obj/Debug/MBS.Audio.pdb
|
||||
BIN
audio-dotnet/src/audio-dotnet/MBS.Audio/obj/Debug/MBS.Audio.dll
Normal file
BIN
audio-dotnet/src/audio-dotnet/MBS.Audio/obj/Debug/MBS.Audio.dll
Normal file
Binary file not shown.
BIN
audio-dotnet/src/audio-dotnet/MBS.Audio/obj/Debug/MBS.Audio.pdb
Normal file
BIN
audio-dotnet/src/audio-dotnet/MBS.Audio/obj/Debug/MBS.Audio.pdb
Normal file
Binary file not shown.
31
audio-dotnet/src/audio-dotnet/audio-dotnet.sln
Normal file
31
audio-dotnet/src/audio-dotnet/audio-dotnet.sln
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.002.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MBS.Audio", "MBS.Audio\MBS.Audio.csproj", "{A64EAB72-EB03-4529-BD16-5ED4D243DC72}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MBS.Audio.MIDI", "MBS.Audio.MIDI\MBS.Audio.MIDI.csproj", "{37B4859E-DE4C-4700-B313-99D04177EE04}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A64EAB72-EB03-4529-BD16-5ED4D243DC72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A64EAB72-EB03-4529-BD16-5ED4D243DC72}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A64EAB72-EB03-4529-BD16-5ED4D243DC72}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A64EAB72-EB03-4529-BD16-5ED4D243DC72}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{37B4859E-DE4C-4700-B313-99D04177EE04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{37B4859E-DE4C-4700-B313-99D04177EE04}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{37B4859E-DE4C-4700-B313-99D04177EE04}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{37B4859E-DE4C-4700-B313-99D04177EE04}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {64A50985-7081-414C-9364-5294B4E1A14A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
1
editor-dotnet
Submodule
1
editor-dotnet
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 62f6a45689041e2fd45451c671fd911ce244fce3
|
||||
Loading…
x
Reference in New Issue
Block a user