-
Notifications
You must be signed in to change notification settings - Fork 315
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
replaces portaudio with a new backend
- Loading branch information
Showing
25 changed files
with
765 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Runtime.InteropServices; | ||
using NAudio.Wave; | ||
using NAudio.Wave.SampleProviders; | ||
using OpenUtau.Core.Util; | ||
|
||
namespace OpenUtau.Audio { | ||
public class MiniAudioOutput : IAudioOutput, IDisposable { | ||
const int channels = 2; | ||
const int sampleRate = 44100; | ||
|
||
public PlaybackState PlaybackState { get; private set; } | ||
public int DeviceNumber { get; private set; } | ||
|
||
|
||
private ISampleProvider? sampleProvider; | ||
private double currentTimeMs; | ||
private bool eof; | ||
|
||
private List<AudioOutputDevice> devices = new List<AudioOutputDevice>(); | ||
private IntPtr callbackPtr = IntPtr.Zero; | ||
private IntPtr nativeContext = IntPtr.Zero; | ||
private Guid selectedDevice = Guid.Empty; | ||
|
||
public MiniAudioOutput() { | ||
UpdateDeviceList(); | ||
unsafe { | ||
var f = (ou_audio_data_callback_t)DataCallback; | ||
GCHandle.Alloc(f); | ||
callbackPtr = Marshal.GetFunctionPointerForDelegate(f); | ||
} | ||
if (Guid.TryParse(Preferences.Default.PlaybackDevice, out var guid)) { | ||
SelectDevice(guid, Preferences.Default.PlaybackDeviceNumber); | ||
} else { | ||
SelectDevice(devices[0].guid, devices[0].deviceNumber); | ||
} | ||
} | ||
|
||
private void UpdateDeviceList() { | ||
devices.Clear(); | ||
unsafe { | ||
ou_audio_device_info_t* device_infos = stackalloc ou_audio_device_info_t[16]; | ||
int count = ou_get_audio_device_infos(device_infos, 16); | ||
if (count == 0) { | ||
throw new Exception("Failed to get any audio device info"); | ||
} | ||
if (count > 16) { | ||
ou_free_audio_device_infos(device_infos, 16); | ||
count = ou_get_audio_device_infos(device_infos, count); | ||
} | ||
for (int i = 0; i < count; i++) { | ||
var guidData = new byte[16]; | ||
fixed (byte* guidPtr = guidData) { | ||
*(ulong*)guidPtr = device_infos[i].api_id; | ||
*(ulong*)(guidPtr + 8) = device_infos[i].id; | ||
} | ||
devices.Add(new AudioOutputDevice { | ||
name = Marshal.PtrToStringUTF8(device_infos[i].name), | ||
api = Marshal.PtrToStringUTF8(device_infos[i].api), | ||
deviceNumber = i, | ||
guid = new Guid(guidData), | ||
}); | ||
} | ||
ou_free_audio_device_infos(device_infos, count); | ||
} | ||
} | ||
|
||
public void Init(ISampleProvider sampleProvider) { | ||
PlaybackState = PlaybackState.Stopped; | ||
eof = false; | ||
currentTimeMs = 0; | ||
if (sampleRate != sampleProvider.WaveFormat.SampleRate) { | ||
sampleProvider = new WdlResamplingSampleProvider(sampleProvider, sampleRate); | ||
} | ||
this.sampleProvider = sampleProvider.ToStereo(); | ||
} | ||
|
||
public void Play() { | ||
if (PlaybackState != PlaybackState.Playing) { | ||
CheckError(ou_audio_device_start(nativeContext)); | ||
} | ||
PlaybackState = PlaybackState.Playing; | ||
currentTimeMs = 0; | ||
eof = false; | ||
} | ||
|
||
public void Pause() { | ||
if (PlaybackState == PlaybackState.Playing) { | ||
CheckError(ou_audio_device_stop(nativeContext)); | ||
} | ||
PlaybackState = PlaybackState.Paused; | ||
} | ||
|
||
public void Stop() { | ||
if (PlaybackState == PlaybackState.Playing) { | ||
CheckError(ou_audio_device_stop(nativeContext)); | ||
} | ||
PlaybackState = PlaybackState.Stopped; | ||
} | ||
|
||
private unsafe void DataCallback(float* buffer, uint channels, uint frame_count) { | ||
var temp = new float[channels * frame_count]; | ||
if (sampleProvider != null) { | ||
int n = sampleProvider.Read(temp, 0, temp.Length); | ||
if (n == 0) { | ||
eof = true; | ||
} | ||
} | ||
Marshal.Copy(temp, 0, (IntPtr)buffer, temp.Length); | ||
currentTimeMs += frame_count * 1000.0 / sampleRate; | ||
} | ||
|
||
public long GetPosition() { | ||
if (eof && PlaybackState == PlaybackState.Playing) { | ||
Stop(); | ||
} | ||
return (long)(Math.Max(0, currentTimeMs) / 1000 * sampleRate * 2 /* 16 bit */ * channels); | ||
} | ||
|
||
public void SelectDevice(Guid guid, int deviceNumber) { | ||
if (selectedDevice != Guid.Empty && selectedDevice == guid) { | ||
return; | ||
} | ||
if (nativeContext != IntPtr.Zero) { | ||
CheckError(ou_free_audio_device(nativeContext)); | ||
nativeContext = IntPtr.Zero; | ||
selectedDevice = Guid.Empty; | ||
} | ||
for (int i = 0; i < devices.Count; i++) { | ||
if (devices[i].guid == guid) { | ||
deviceNumber = i; | ||
break; | ||
} | ||
if (i == devices.Count - 1) { | ||
guid = devices[0].guid; | ||
deviceNumber = devices[0].deviceNumber; | ||
} | ||
} | ||
uint api_id; | ||
ulong id; | ||
unsafe { | ||
fixed (byte* guidPtr = guid.ToByteArray()) { | ||
api_id = (uint)*(ulong*)guidPtr; | ||
id = *(ulong*)(guidPtr + 8); | ||
} | ||
} | ||
unsafe { | ||
nativeContext = ou_init_audio_device(api_id, id, callbackPtr); | ||
if (nativeContext == IntPtr.Zero) { | ||
throw new Exception("Failed to init audio device"); | ||
} | ||
} | ||
selectedDevice = guid; | ||
DeviceNumber = deviceNumber; | ||
if (Preferences.Default.PlaybackDevice != guid.ToString()) { | ||
Preferences.Default.PlaybackDevice = guid.ToString(); | ||
Preferences.Default.PlaybackDeviceNumber = deviceNumber; | ||
Preferences.Save(); | ||
} | ||
} | ||
|
||
public List<AudioOutputDevice> GetOutputDevices() { | ||
return devices; | ||
} | ||
|
||
#region binding | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
private unsafe struct ou_audio_device_info_t { | ||
public IntPtr name; | ||
public ulong id; | ||
public IntPtr api; | ||
public uint api_id; | ||
} | ||
|
||
[UnmanagedFunctionPointer(callingConvention: CallingConvention.Cdecl)] | ||
private unsafe delegate void ou_audio_data_callback_t(float* buffer, uint channels, uint frame_count); | ||
|
||
[DllImport("worldline")] private static extern unsafe int ou_get_audio_device_infos(ou_audio_device_info_t* device_infos, int max_count); | ||
[DllImport("worldline")] private static extern unsafe void ou_free_audio_device_infos(ou_audio_device_info_t* device_infos, int count); | ||
[DllImport("worldline")] private static extern IntPtr ou_init_audio_device(uint api_id, ulong id, IntPtr callback); | ||
[DllImport("worldline")] private static extern int ou_free_audio_device(IntPtr context); | ||
[DllImport("worldline")] private static extern int ou_audio_device_start(IntPtr context); | ||
[DllImport("worldline")] private static extern int ou_audio_device_stop(IntPtr context); | ||
[DllImport("worldline")] private static extern IntPtr ou_audio_get_error_message(int error_code); | ||
|
||
private static void CheckError(int errorCode) { | ||
if (errorCode == 0) { | ||
return; | ||
} | ||
IntPtr ptr = ou_audio_get_error_message(errorCode); | ||
throw new Exception(Marshal.PtrToStringUTF8(ptr)); | ||
} | ||
|
||
#endregion | ||
|
||
#region disposable | ||
|
||
private bool disposedValue; | ||
|
||
protected virtual void Dispose(bool disposing) { | ||
if (!disposedValue) { | ||
if (disposing) { | ||
// dispose managed state (managed objects) | ||
} | ||
|
||
// free unmanaged resources (unmanaged objects) and override finalizer | ||
if (nativeContext != IntPtr.Zero) { | ||
ou_free_audio_device(nativeContext); | ||
nativeContext = IntPtr.Zero; | ||
} | ||
|
||
// set large fields to null | ||
|
||
disposedValue = true; | ||
} | ||
} | ||
|
||
~MiniAudioOutput() { | ||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method | ||
Dispose(disposing: false); | ||
} | ||
|
||
public void Dispose() { | ||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method | ||
Dispose(disposing: true); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
#endregion | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,16 @@ | ||
build --enable_platform_specific_config | ||
common --enable_platform_specific_config | ||
common --experimental_cc_shared_library | ||
common --enable_bzlmod | ||
|
||
run --define absl=1 | ||
test --define absl=1 | ||
|
||
build:linux --cxxopt='-std=c++17' | ||
build:macos --cxxopt='-std=c++17' | ||
build:windows --cxxopt='/std:c++17' | ||
|
||
build:ubuntu-aarch64 --cxxopt='-std=c++17' | ||
build:ubuntu-aarch64 --linkopt='-lm' | ||
build:ubuntu-aarch64 --crosstool_top=//toolchain:arm_suite | ||
build:ubuntu-aarch64 --cpu=aarch64 | ||
build:ubuntu-aarch64 --compiler=gcc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
7.0.2 | ||
6.5.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
"""Worldline""" | ||
module(name = "worldline") | ||
"""OpenUtau C++ module.""" | ||
module(name = "openutau-cpp") | ||
|
||
bazel_dep(name = "abseil-cpp", version = "20240116.1", repo_name = "absl") | ||
bazel_dep(name = "googletest", version = "1.14.0", repo_name = "gtest") | ||
|
||
bazel_dep(name = "xxhash", version = "0.8.2") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#!/bin/bash | ||
|
||
setup() | ||
{ | ||
sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi binutils-arm-linux-gnueabi | ||
sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu | ||
} | ||
|
||
build() | ||
{ | ||
mkdir -p ../runtimes/linux-$1/native | ||
bazel build //worldline:worldline -c opt $2 | ||
chmod +w bazel-bin/worldline/libworldline.so | ||
cp bazel-bin/worldline/libworldline.so ../runtimes/linux-$1/native | ||
} | ||
|
||
build x64 "--cpu=k8" | ||
build arm64 "--config=ubuntu-aarch64" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#!/bin/bash | ||
|
||
build() | ||
{ | ||
bazel build //worldline:worldline -c opt $2 | ||
chmod +w bazel-bin/worldline/libworldline.dylib | ||
cp bazel-bin/worldline/libworldline.dylib ../runtimes/osx/native/libworldline-$1.dylib | ||
} | ||
|
||
mkdir -p ../runtimes/osx/native | ||
|
||
build x64 "--cpu=darwin_x86_64" | ||
build arm64 "--cpu=darwin_arm64" | ||
|
||
lipo -create ../runtimes/osx/native/libworldline-x64.dylib ../runtimes/osx/native/libworldline-arm64.dylib -output ../runtimes/osx/native/libworldline.dylib | ||
rm ../runtimes/osx/native/libworldline-x64.dylib ../runtimes/osx/native/libworldline-arm64.dylib |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
@goto :MAIN | ||
|
||
:BUILD | ||
@echo Building %~1 | ||
|
||
if not exist ..\runtimes\%~1\native mkdir ..\runtimes\%~1\native | ||
bazel build //worldline:worldline -c opt --cpu=%~2 | ||
attrib -r bazel-bin\worldline\worldline.dll | ||
copy bazel-bin\worldline\worldline.dll ..\runtimes\%~1\native | ||
|
||
@EXIT /B | ||
|
||
:MAIN | ||
@call :BUILD win-x64 x64_windows | ||
@call :BUILD win-x86 x64_x86_windows | ||
@call :BUILD win-arm64 arm64_windows |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package(default_visibility = ["//visibility:public"]) | ||
|
||
cc_library( | ||
name = "miniaudio", | ||
hdrs = ["miniaudio.h"], | ||
includes = ["."], | ||
) |
Oops, something went wrong.