Files
2026-02-21 16:45:37 +08:00

305 lines
7.5 KiB
C#

using System;
using System.IO;
using System.Text;
using Crosstales.Radio.Model;
using Crosstales.Radio.Util;
using UnityEngine;
namespace Crosstales.Radio.Tool
{
[RequireComponent(typeof(AudioSource))]
[HelpURLAttribute("https://crosstales.com/media/data/assets/radio/api/class_crosstales_1_1_radio_1_1_tool_1_1_stream_saver.html")]
public class StreamSaver : MonoBehaviour
{
[Tooltip("Origin Player.")]
public BasePlayer Player;
[Tooltip("Silence the origin (default: true).")]
public bool SilenceSource = true;
[Tooltip("Output path for the audio files.")]
public string OutputPath;
[Tooltip("Record delay in seconds before start saving the audio (default: 0).")]
[Range(0f, 20f)]
public float RecordStartDelay;
[Range(0f, 20f)]
[Tooltip("Record delay in seconds before stop saving the audio (default: 0).")]
public float RecordStopDelay;
[Tooltip("Add the station name to the audio files (default: true).")]
public bool AddStationName = true;
[Tooltip("Add the current timestamp to the audio files (default: false).")]
public bool AddTimestamp;
private FileStream fileStream;
private const int HEADER_SIZE = 44;
private const float RESCALE_FACTOR = 32767f;
private AudioSource audioSource;
private bool recOutput;
private bool stopped = true;
private long dataPosition;
private string fileName;
public bool isSilenceSource
{
get
{
return SilenceSource;
}
set
{
SilenceSource = value;
}
}
public void Awake()
{
audioSource = GetComponent<AudioSource>();
audioSource.playOnAwake = false;
audioSource.Stop();
}
public void Start()
{
if (Player == null)
{
Debug.LogWarning("No 'Player' added to the StreamSaver!");
}
else
{
Player.isCaptureDataStream = true;
Player.isLegacyMode = false;
}
if (string.IsNullOrEmpty(OutputPath))
{
Debug.LogWarning("No 'OutputPath' added to the StreamSaver, saving in the project root!");
}
}
public void Update()
{
if (Player != null && Player.isAudioPlaying)
{
if (stopped)
{
stopped = false;
dataPosition = Player.DataStream.Position;
AudioClip clip = AudioClip.Create(Player.RadioStation.Name, int.MaxValue, Player.Channels, Player.SampleRate, true, readPCMData);
audioSource.clip = clip;
audioSource.Play();
if (SilenceSource)
{
Player.Silence();
}
}
}
else if (!stopped)
{
audioSource.Stop();
audioSource.clip = null;
stopped = true;
}
}
public void OnEnable()
{
if (Player != null)
{
Player.OnAudioEnd += onAudioEnd;
Player.OnNextRecordChange += onNextRecordChange;
}
}
public void OnDisable()
{
if (Player != null)
{
Player.OnAudioEnd -= onAudioEnd;
Player.OnNextRecordChange -= onNextRecordChange;
}
closeFile();
audioSource.Stop();
audioSource.clip = null;
stopped = true;
}
public void OnValidate()
{
if (!string.IsNullOrEmpty(OutputPath))
{
OutputPath = Helper.ValidatePath(OutputPath);
}
}
private void openFile()
{
if (Config.DEBUG)
{
Debug.Log("openFile: " + fileName);
}
if (fileStream != null && fileStream.CanWrite)
{
closeFile();
}
try
{
Directory.CreateDirectory(Path.GetDirectoryName(fileName));
fileStream = new FileStream(fileName, FileMode.Create);
byte value = 0;
for (int i = 0; i < 44; i++)
{
fileStream.WriteByte(value);
}
recOutput = true;
}
catch (Exception ex)
{
Debug.LogError("Could not open file '" + fileName + "': " + ex);
}
}
private void convertAndWrite(float[] dataSource)
{
if (fileStream != null && fileStream.CanWrite)
{
short[] array = new short[dataSource.Length];
byte[] array2 = new byte[dataSource.Length * 2];
byte[] array3 = new byte[2];
for (int i = 0; i < dataSource.Length; i++)
{
array[i] = (short)(dataSource[i] * 32767f);
array3 = BitConverter.GetBytes(array[i]);
array3.CopyTo(array2, i * 2);
}
try
{
fileStream.Write(array2, 0, array2.Length);
}
catch (Exception ex)
{
Debug.LogError("Could write to file '" + fileName + "': " + ex);
}
}
}
private void closeFile()
{
if (Config.DEBUG)
{
Debug.Log("closeFile");
}
recOutput = false;
if (fileStream == null || !fileStream.CanWrite)
{
return;
}
try
{
fileStream.Seek(0L, SeekOrigin.Begin);
byte[] bytes = Encoding.UTF8.GetBytes("RIFF");
fileStream.Write(bytes, 0, 4);
byte[] bytes2 = BitConverter.GetBytes(fileStream.Length - 8);
fileStream.Write(bytes2, 0, 4);
byte[] bytes3 = Encoding.UTF8.GetBytes("WAVE");
fileStream.Write(bytes3, 0, 4);
byte[] bytes4 = Encoding.UTF8.GetBytes("fmt ");
fileStream.Write(bytes4, 0, 4);
byte[] bytes5 = BitConverter.GetBytes(16);
fileStream.Write(bytes5, 0, 4);
ushort value = 1;
byte[] bytes6 = BitConverter.GetBytes(value);
fileStream.Write(bytes6, 0, 2);
byte[] bytes7 = BitConverter.GetBytes(Player.Channels);
fileStream.Write(bytes7, 0, 2);
byte[] bytes8 = BitConverter.GetBytes(Player.SampleRate);
fileStream.Write(bytes8, 0, 4);
byte[] bytes9 = BitConverter.GetBytes(Player.SampleRate * Player.Channels * 2);
fileStream.Write(bytes9, 0, 4);
ushort value2 = (ushort)(Player.Channels * 2);
fileStream.Write(BitConverter.GetBytes(value2), 0, 2);
ushort value3 = 16;
byte[] bytes10 = BitConverter.GetBytes(value3);
fileStream.Write(bytes10, 0, 2);
byte[] bytes11 = Encoding.UTF8.GetBytes("data");
fileStream.Write(bytes11, 0, 4);
byte[] bytes12 = BitConverter.GetBytes(fileStream.Length - 44);
fileStream.Write(bytes12, 0, 4);
}
catch (Exception ex)
{
Debug.LogError("Could write header for file '" + fileName + "': " + ex);
}
finally
{
try
{
fileStream.Close();
}
catch (Exception ex2)
{
Debug.LogError("Could close file '" + fileName + "': " + ex2);
}
}
}
private void readPCMData(float[] data)
{
if (Player.isAudioPlaying && Player.DataStream != null)
{
byte[] array = new byte[data.Length * 2];
long position = Player.DataStream.Position;
Player.DataStream.Position = dataPosition;
int num;
if ((num = Player.DataStream.Read(array, 0, array.Length)) > 0)
{
float[] array2 = Helper.ConvertByteArrayToFloatArray(array, num);
Buffer.BlockCopy(array2, 0, data, 0, data.Length * 4);
if (recOutput)
{
convertAndWrite(array2);
}
dataPosition += num;
}
Player.DataStream.Position = position;
}
else
{
Buffer.BlockCopy(new float[data.Length], 0, data, 0, data.Length * 4);
}
}
private void onAudioEnd(RadioStation station)
{
if (Config.DEBUG)
{
Debug.Log("onAudioEnd");
}
closeFile();
}
private void onNextRecordChange(RadioStation station, RecordInfo nextRecord, float delay)
{
if (Config.DEBUG)
{
Debug.Log("onNextRecordChange: " + delay);
}
if (delay > 0f)
{
Invoke("closeFile", delay - RecordStopDelay - 0.2f);
}
fileName = Helper.ValidateFile(string.Concat(str1: (!string.IsNullOrEmpty(nextRecord.Artist) && !string.IsNullOrEmpty(nextRecord.Title)) ? (((!AddStationName) ? string.Empty : (station.Name + " - ")) + ((!AddTimestamp) ? string.Empty : (DateTime.Now.ToString("yyyyMMdd HH_mm_ss") + " - ")) + nextRecord.Artist + " - " + nextRecord.Title + ".wav") : (station.Name + " - " + DateTime.Now.ToString("yyyyMMdd HH_mm_ss") + ".wav"), str0: OutputPath));
Invoke("openFile", delay + RecordStartDelay + 0.2f);
}
}
}