using UnityEngine;
using System.Collections.Generic;
using System;
/// <summary>
/// 구간 루프를 지원하는 배경음악 스크립트입니다.
/// </summary>
public class SectionLoopingMusic : MonoBehaviour
{
#region Inspector
[Space(8)]
[SerializeField]
[Tooltip("구간 루프를 적용시킬 AudioClip입니다.")]
private AudioClip audioClip;
[Space(8)]
[SerializeField]
[Tooltip("로딩후 바로 재생합니다.")]
private bool playAfterLoad;
[Space(8)]
[SerializeField]
[Tooltip("루프 섹션이 시작될 초단위의 지점입니다.")]
private float loopSectionStartPoint;
[SerializeField]
[Tooltip("루프 섹션이 끝날 초단위의 지점입니다.")]
private float loopSectionEndPoint;
#endregion
#region Member variable
AudioSource audioSource;
float[] audioBuffer;
int position;
int loopStartPoint;
int loopEndPoint;
#endregion
#region Property
/// <summary>
/// 현재 AudioClip 인스턴스를 가져옵니다.
/// </summary>
/// <remarks>
/// 현재 재생중인 AudioClip을 변경하려면 <seealso cref="Renew(AudioClip, float)"/>를 확인하세요.
/// </remarks>
public AudioClip AudioClip
{
get
{
return this.audioClip;
}
}
/// <summary>
/// 루프 구간 시작 지점을 가져옵니다.
/// </summary>
/// <remarks>
/// 단위는 '초'입니다.
/// 루프 구간 종료 지점을 가져오려면 <seealso cref="LoopSectionEndPoint"/>를 참조하세요.
/// </remarks>
public float LoopSectionStartPoint
{
get
{
return this.loopSectionStartPoint;
}
}
/// <summary>
/// 루프 구간 종료 지점을 가져옵니다.
/// </summary>
/// <remarks>
/// 단위는 '초'입니다.
/// 루프 구간 시작 지점을 가져오려면 <seealso cref="LoopSectionStartPoint"/>를 참조하세요.
/// </remarks>
public float LoopSectionEndPoint
{
get
{
return this.loopSectionEndPoint;
}
}
/// <summary>
/// 진행 시간을 가져오거나 설정합니다.
/// </summary>
public float TimePosition
{
get
{
return this.position / this.audioBuffer.Length * this.audioClip.length;
}
set
{
this.position = (int)(value * this.audioBuffer.Length / this.audioClip.length);
}
}
/// <summary>
/// 현재 버퍼의 위치를 가져오거나 설정합니다.
/// </summary>
public int Position
{
get
{
return this.position;
}
set
{
if (value < audioBuffer.Length || value >= 0)
{
position = value;
}
else if (value < 0)
{
throw new System.ArgumentException("Value is lesser then 0.");
}
else
{
throw new System.ArgumentException("Value is larger then AudioClip.Samples * AudioClip.chennels.");
}
}
}
/// <summary>
/// 현재 <see cref="UnityEngine.AudioSource"/> 인스턴스를 가져옵니다.
/// </summary>
/// <remarks>
/// <see cref="UnityEngine.AudioSource"/>에 직접 접근하는 것은 안전하지 않습니다.
/// 배경 음악 재생에 필요한 기능들은 거의 이 스크립트에서 제공하고 있습니다.
/// 정말 필요한 경우에만 제한적으로 접근하십시오.
/// </remarks>
[Obsolete("AudioSource에 직접 접근 하는 것은 안전하지 않습니다.")]
AudioSource AudioSource
{
get
{
return this.audioSource;
}
}
#endregion
#region Related to audio
#region Method
/// <summary>
/// 재생합니다.
/// </summary>
public void Play()
{
this.audioSource.Play();
}
/// <summary>
/// 중지합니다.
/// </summary>
public void Stop()
{
this.audioSource.Stop();
this.position = 0;
}
/// <summary>
/// 일시중지합니다.
/// </summary>
public void Pause()
{
this.audioSource.Pause();
}
#endregion
#region Property
/// <summary>
/// 음의 크기를 설정하거나 가져옵니다.
/// </summary>
public float Volume
{
get
{
return this.audioSource.volume;
}
set
{
this.audioSource.volume = value;
}
}
/// <summary>
/// 음의 높이를 설정하거나 가져옵니다.
/// </summary>
public float Pitch
{
get
{
return this.audioSource.pitch;
}
set
{
this.audioSource.pitch = value;
}
}
/// <summary>
/// 음의 좌우 위치를 설정하거나 가져옵니다.
/// </summary>
public float Pan
{
get
{
return this.audioSource.panStereo;
}
set
{
this.audioSource.panStereo = value;
}
}
#endregion
#endregion
#region Script control
/// <summary>
/// AudioClip과 루프 구간을 갱신합니다.
/// </summary>
/// <param name="audioClip">새 AudioClip 인스턴스입니다.</param>
/// <param name="loopSectionStartPoint">새 루프 구간 시작 지점입니다. 단위는 '초'입니다.</param>
/// <param name="loopSectionEndPoint">새 루프 구간 종료 지점입니다. 단위는 '초'입니다.</param>
public void Renew(AudioClip audioClip, float loopSectionStartPoint, float loopSectionEndPoint)
{
this.audioSource.Stop();
// 할당
this.audioClip = audioClip;
this.loopSectionStartPoint = loopSectionStartPoint;
this.loopSectionEndPoint = loopSectionEndPoint;
// 지역변수는 멤버변수보다 빠름.
var channelCount = this.audioClip.channels;
// 버퍼에 대한 루프 시작점을 연산해둔다.
this.loopStartPoint = (int)((this.loopSectionStartPoint / this.audioClip.length) * this.audioClip.samples * channelCount);
this.loopEndPoint = (int)((this.loopSectionEndPoint / this.audioClip.length) * this.audioClip.samples * channelCount);
// 오디오 버퍼를 생성한다.
this.audioBuffer = new float[this.audioClip.samples * channelCount];
this.audioClip.GetData(audioBuffer, 0);
// 제어할 AudioClip생성. 이후에는 audioClip에 접근하지 않는 것이 좋음.
this.audioClip = AudioClip.Create(this.audioClip.name + "_loop", this.audioClip.samples, channelCount, this.audioClip.frequency, true, this.AudioRead);
if (this.loopSectionEndPoint < this.loopSectionStartPoint)
{
this.loopSectionEndPoint = this.audioBuffer.Length;
}
this.audioSource.loop = true;
this.audioSource.clip = this.audioClip;
this.position = 0;
if (this.playAfterLoad)
this.Play();
}
/// <summary>
/// 스크립트 초기화시 호출됩니다.
/// </summary>
/// <exception cref="System.NullReferenceException">
/// AudioSource를 찾을 수 없을 경우 발생합니다.
/// </exception>
public void Start()
{
// AudioSource 컴포넌트 가져오기.
this.audioSource = GetComponent<AudioSource>();
if (audioSource == null)
{
throw new System.NullReferenceException("Not found AudioSource component.");
}
this.Renew(this.audioClip, this.loopSectionStartPoint, this.loopSectionEndPoint);
}
void AudioRead(float[] buffer)
{
var dataLength = buffer.Length;
int bufferPosition = this.position;
for (int i = 0; i < dataLength; i++)
{
buffer[i] = this.audioBuffer[bufferPosition];
bufferPosition++;
if (bufferPosition >= this.loopEndPoint)
{
bufferPosition = this.loopStartPoint;
}
}
this.position = bufferPosition;
}
#endregion
}
시작후 알아서 이 스크립트 얻어내서 Play 메서드 호출시키시면 됩니다 ^^


