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 메서드 호출시키시면 됩니다 ^^