Skip to content

Commit

Permalink
com.utilities.encoder.wav 1.2.0 (#7)
Browse files Browse the repository at this point in the history
- Updated com.utilities.audio -> 1.2.0
- Fixed microphone always using "default" recording device (Now properly uses the device set by RecordingManager)
- Added support for WebGL microphone recording
- Adds ability to stream recording without saving to disk
- Updated sample scene (now requires TextMeshPro)
  • Loading branch information
StephenHodgson authored Jun 18, 2024
1 parent aed4164 commit 7fb8364
Show file tree
Hide file tree
Showing 16 changed files with 2,545 additions and 490 deletions.
43 changes: 28 additions & 15 deletions Runtime/WavEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ public async Task<Tuple<string, AudioClip>> StreamSaveToDiskAsync(AudioClip clip
throw new InvalidOperationException($"{nameof(StreamSaveToDiskAsync)} can only be called from {nameof(RecordingManager.StartRecordingAsync)}");
}

if (!Microphone.IsRecording(null))
var device = RecordingManager.DefaultRecordingDevice;

if (!Microphone.IsRecording(device))
{
throw new InvalidOperationException("Microphone is not initialized!");
}
Expand Down Expand Up @@ -106,21 +108,32 @@ public async Task<Tuple<string, AudioClip>> StreamSaveToDiskAsync(AudioClip clip
Debug.Log($"[{nameof(RecordingManager)}] Initializing data for {clipName}. Channels: {channels}, Sample Rate: {sampleRate}, Sample buffer size: {bufferSize}, Max Sample Length: {maxSamples}");
}

if (!Directory.Exists(saveDirectory))
Stream finalStream;
var outputPath = string.Empty;

if (!string.IsNullOrWhiteSpace(saveDirectory))
{
Directory.CreateDirectory(saveDirectory);
}
if (!Directory.Exists(saveDirectory))
{
Directory.CreateDirectory(saveDirectory);
}

var path = $"{saveDirectory}/{clipName}.wav";
outputPath = $"{saveDirectory}/{clipName}.wav";

if (File.Exists(path))
if (File.Exists(outputPath))
{
Debug.LogWarning($"[{nameof(RecordingManager)}] {outputPath} already exists, attempting to delete...");
File.Delete(outputPath);
}

finalStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write);
}
else
{
Debug.LogWarning($"[{nameof(RecordingManager)}] {path} already exists, attempting to delete...");
File.Delete(path);
finalStream = new MemoryStream();
}

var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
var writer = new BinaryWriter(fileStream);
var writer = new BinaryWriter(finalStream);

try
{
Expand All @@ -138,7 +151,7 @@ public async Task<Tuple<string, AudioClip>> StreamSaveToDiskAsync(AudioClip clip
{
// Expected to be on the Unity Main Thread.
await Awaiters.UnityMainThread;
var microphonePosition = Microphone.GetPosition(null);
var microphonePosition = Microphone.GetPosition(device);

if (microphonePosition <= 0 && lastMicrophonePosition == 0)
{
Expand Down Expand Up @@ -204,14 +217,14 @@ public async Task<Tuple<string, AudioClip>> StreamSaveToDiskAsync(AudioClip clip
finally
{
RecordingManager.IsRecording = false;
Microphone.End(null);
Microphone.End(device);

if (RecordingManager.EnableDebug)
{
Debug.Log($"[{nameof(RecordingManager)}] Recording stopped, writing end of stream...");
}

var fileSize = fileStream.Position;
var fileSize = finalStream.Position;
// rewind and write header file size
writer.Seek(4, SeekOrigin.Begin);
// Size of the overall file - 8 bytes, in bytes (32-bit integer).
Expand Down Expand Up @@ -244,7 +257,7 @@ public async Task<Tuple<string, AudioClip>> StreamSaveToDiskAsync(AudioClip clip
}

await writer.DisposeAsync().ConfigureAwait(false);
await fileStream.DisposeAsync().ConfigureAwait(false);
await finalStream.DisposeAsync().ConfigureAwait(false);
}

if (RecordingManager.EnableDebug)
Expand All @@ -262,7 +275,7 @@ public async Task<Tuple<string, AudioClip>> StreamSaveToDiskAsync(AudioClip clip
// Create a new copy of the final recorded clip.
var newClip = AudioClip.Create(clipName, microphoneData.Length, channels, sampleRate, false);
newClip.SetData(microphoneData, 0);
var result = new Tuple<string, AudioClip>(path, newClip);
var result = new Tuple<string, AudioClip>(outputPath, newClip);

RecordingManager.IsProcessing = false;

Expand Down
3 changes: 0 additions & 3 deletions Samples~/Utilities.Encoder.Wav.Samples.asmdef

This file was deleted.

7 changes: 0 additions & 7 deletions Samples~/Utilities.Encoder.Wav.Samples.asmdef.meta

This file was deleted.

File renamed without changes.
File renamed without changes.
61 changes: 61 additions & 0 deletions Samples~/WavRecording/MicrophoneDropdownSelector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Linq;
using TMPro;
using UnityEngine;
using Utilities.Audio;
using Microphone = Utilities.Audio.Microphone;

namespace Utilities.Encoder.Wav.Samples.Recording
{
[RequireComponent(typeof(TMP_Dropdown))]
public class MicrophoneDropdownSelector : MonoBehaviour
{
[SerializeField]
private TMP_Dropdown dropdown;

private void OnValidate()
{
if (dropdown == null)
{
dropdown = GetComponent<TMP_Dropdown>();
}
}

private void Awake()
{
dropdown.onValueChanged.AddListener(OnDeviceSelected);
}

private void OnEnable()
{
RefreshDeviceList();
}

private void Update()
{
if (Microphone.devices.Length != dropdown.options.Count)
{
RefreshDeviceList();
}
}

private void OnDestroy()
{
dropdown.onValueChanged.RemoveListener(OnDeviceSelected);
}

public void RefreshDeviceList()
{
dropdown.ClearOptions();
var dropdownOptions = Microphone.devices.Select(device => new TMP_Dropdown.OptionData(device)).ToList();
dropdown.AddOptions(dropdownOptions);
}

private void OnDeviceSelected(int index)
{
if (index < 0) { return; }
var selectedDevice = Microphone.devices[index];
Debug.Log(selectedDevice);
RecordingManager.DefaultRecordingDevice = selectedDevice;
}
}
}
11 changes: 11 additions & 0 deletions Samples~/WavRecording/MicrophoneDropdownSelector.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 7fb8364

Please sign in to comment.