├── .gitignore
├── .vscode
└── settings.json
├── Assets
├── AudioLoader.cs
├── AudioLoader.cs.meta
├── NLayer.meta
├── NLayer
│ ├── Decoder.meta
│ ├── Decoder
│ │ ├── BitReservoir.cs
│ │ ├── BitReservoir.cs.meta
│ │ ├── FrameBase.cs
│ │ ├── FrameBase.cs.meta
│ │ ├── Huffman.cs
│ │ ├── Huffman.cs.meta
│ │ ├── ID3Frame.cs
│ │ ├── ID3Frame.cs.meta
│ │ ├── LayerDecoderBase.cs
│ │ ├── LayerDecoderBase.cs.meta
│ │ ├── LayerIDecoder.cs
│ │ ├── LayerIDecoder.cs.meta
│ │ ├── LayerIIDecoder.cs
│ │ ├── LayerIIDecoder.cs.meta
│ │ ├── LayerIIDecoderBase.cs
│ │ ├── LayerIIDecoderBase.cs.meta
│ │ ├── LayerIIIDecoder.cs
│ │ ├── LayerIIIDecoder.cs.meta
│ │ ├── MpegFrame.cs
│ │ ├── MpegFrame.cs.meta
│ │ ├── MpegStreamReader.cs
│ │ ├── MpegStreamReader.cs.meta
│ │ ├── RiffHeaderFrame.cs
│ │ ├── RiffHeaderFrame.cs.meta
│ │ ├── VBRInfo.cs
│ │ └── VBRInfo.cs.meta
│ ├── Enums.cs
│ ├── Enums.cs.meta
│ ├── IMpegFrame.cs
│ ├── IMpegFrame.cs.meta
│ ├── LICENSE
│ ├── MpegFile.cs
│ ├── MpegFile.cs.meta
│ ├── MpegFrameDecoder.cs
│ ├── MpegFrameDecoder.cs.meta
│ └── README.md
├── Scenes.meta
└── Scenes
│ ├── SampleScene.unity
│ └── SampleScene.unity.meta
├── Packages
└── manifest.json
├── ProjectSettings
├── AudioManager.asset
├── ClusterInputManager.asset
├── DynamicsManager.asset
├── EditorBuildSettings.asset
├── EditorSettings.asset
├── GraphicsSettings.asset
├── InputManager.asset
├── NavMeshAreas.asset
├── NetworkManager.asset
├── Physics2DSettings.asset
├── PresetManager.asset
├── ProjectSettings.asset
├── ProjectVersion.txt
├── QualitySettings.asset
├── TagManager.asset
├── TimeManager.asset
├── UnityConnectSettings.asset
├── VFXManager.asset
└── XRSettings.asset
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # This .gitignore file should be placed at the root of your Unity project directory
2 | #
3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
4 | #
5 | /[Ll]ibrary/
6 | /[Tt]emp/
7 | /[Oo]bj/
8 | /[Bb]uild/
9 | /[Bb]uilds/
10 | /[Ll]ogs/
11 | /[Uu]ser[Ss]ettings/
12 |
13 | # MemoryCaptures can get excessive in size.
14 | # They also could contain extremely sensitive data
15 | /[Mm]emoryCaptures/
16 |
17 | # Asset meta data should only be ignored when the corresponding asset is also ignored
18 | !/[Aa]ssets/**/*.meta
19 |
20 | # Uncomment this line if you wish to ignore the asset store tools plugin
21 | # /[Aa]ssets/AssetStoreTools*
22 |
23 | # Autogenerated Jetbrains Rider plugin
24 | /[Aa]ssets/Plugins/Editor/JetBrains*
25 |
26 | # Visual Studio cache directory
27 | .vs/
28 |
29 | # Gradle cache directory
30 | .gradle/
31 |
32 | # Autogenerated VS/MD/Consulo solution and project files
33 | ExportedObj/
34 | .consulo/
35 | *.csproj
36 | *.unityproj
37 | *.sln
38 | *.suo
39 | *.tmp
40 | *.user
41 | *.userprefs
42 | *.pidb
43 | *.booproj
44 | *.svd
45 | *.pdb
46 | *.mdb
47 | *.opendb
48 | *.VC.db
49 |
50 | # Unity3D generated meta files
51 | *.pidb.meta
52 | *.pdb.meta
53 | *.mdb.meta
54 |
55 | # Unity3D generated file on crash reports
56 | sysinfo.txt
57 |
58 | # Builds
59 | *.apk
60 | *.unitypackage
61 |
62 | Assets/StreamingAssets/
63 |
64 | # Crashlytics generated file
65 | crashlytics-build.properties
66 |
67 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.exclude":
3 | {
4 | "**/.DS_Store":true,
5 | "**/.git":true,
6 | "**/.gitignore":true,
7 | "**/.gitmodules":true,
8 | "**/*.booproj":true,
9 | "**/*.pidb":true,
10 | "**/*.suo":true,
11 | "**/*.user":true,
12 | "**/*.userprefs":true,
13 | "**/*.unityproj":true,
14 | "**/*.dll":true,
15 | "**/*.exe":true,
16 | "**/*.pdf":true,
17 | "**/*.mid":true,
18 | "**/*.midi":true,
19 | "**/*.wav":true,
20 | "**/*.gif":true,
21 | "**/*.ico":true,
22 | "**/*.jpg":true,
23 | "**/*.jpeg":true,
24 | "**/*.png":true,
25 | "**/*.psd":true,
26 | "**/*.tga":true,
27 | "**/*.tif":true,
28 | "**/*.tiff":true,
29 | "**/*.3ds":true,
30 | "**/*.3DS":true,
31 | "**/*.fbx":true,
32 | "**/*.FBX":true,
33 | "**/*.lxo":true,
34 | "**/*.LXO":true,
35 | "**/*.ma":true,
36 | "**/*.MA":true,
37 | "**/*.obj":true,
38 | "**/*.OBJ":true,
39 | "**/*.asset":true,
40 | "**/*.cubemap":true,
41 | "**/*.flare":true,
42 | "**/*.mat":true,
43 | "**/*.meta":true,
44 | "**/*.prefab":true,
45 | "**/*.unity":true,
46 | "build/":true,
47 | "Build/":true,
48 | "Library/":true,
49 | "library/":true,
50 | "obj/":true,
51 | "Obj/":true,
52 | "ProjectSettings/":true,
53 | "temp/":true,
54 | "Temp/":true
55 | }
56 | }
--------------------------------------------------------------------------------
/Assets/AudioLoader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using UnityEngine;
4 | using UnityEngine.Networking;
5 |
6 | public class AudioLoader : MonoBehaviour
7 | {
8 | public UnityEngine.UI.Text statusText;
9 | public UnityEngine.UI.InputField url;
10 | public AudioSource source;
11 |
12 | void Start()
13 | {
14 |
15 | }
16 |
17 | public void LoadAudio()
18 | {
19 | StartCoroutine(LoadHelper(url.text));
20 | }
21 |
22 | /* bad, no mp3 support at runtime apparently
23 | IEnumerator LoadHelper(string uri)
24 | {
25 | using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(uri, AudioType.MPEG))
26 | {
27 | yield return www.SendWebRequest();
28 |
29 | if (www.isNetworkError)
30 | {
31 | statusText.text = www.error;
32 | }
33 | else
34 | {
35 | statusText.text = "loaded?";
36 | AudioClip myClip = DownloadHandlerAudioClip.GetContent(www);
37 | source.clip = myClip;
38 | }
39 | }
40 | }
41 | */
42 |
43 | /* MP3Stream (LGPL so not compatible with how Unity builds)
44 | IEnumerator LoadHelper(string uri)
45 | {
46 | statusText.text = "Loading...";
47 | UnityWebRequest www = UnityWebRequest.Get(uri);
48 | yield return www.SendWebRequest();
49 |
50 | if(www.isNetworkError || www.isHttpError)
51 | {
52 | statusText.text = www.error;
53 | }
54 | else
55 | {
56 | statusText.text = "loaded";
57 | // Or retrieve results as binary data
58 | byte[] results = www.downloadHandler.data;
59 | var memStream = new System.IO.MemoryStream(results);
60 | var mp3Stream = new MP3Sharp.MP3Stream(memStream);
61 |
62 | var audioDataStream = new System.IO.MemoryStream();
63 | byte[] buffer = new byte[4096];
64 | int bytesReturned = -1;
65 |
66 | while( bytesReturned != 0 )
67 | {
68 | bytesReturned = mp3Stream.Read(buffer, 0, buffer.Length);
69 | audioDataStream.Write(buffer, 0, bytesReturned);
70 | }
71 |
72 | byte[] audioData = audioDataStream.ToArray();
73 |
74 | float[] floatArray = new float[audioData.Length / 2];
75 | for(int i = 0; i < floatArray.Length; ++i)
76 | {
77 | floatArray[i] = (float)(System.BitConverter.ToInt16(audioData, i * 2 ) / 32768.0f);
78 | }
79 |
80 | var clip = AudioClip.Create("foo", floatArray.Length, 2, mp3Stream.Frequency, false);
81 | clip.SetData(floatArray, 0);
82 | source.clip = clip;
83 | }
84 | }
85 | */
86 |
87 | IEnumerator LoadHelper(string uri)
88 | {
89 | statusText.text = "Loading...";
90 | UnityWebRequest www = UnityWebRequest.Get(uri);
91 | yield return www.SendWebRequest();
92 |
93 | if(www.isNetworkError || www.isHttpError)
94 | {
95 | statusText.text = www.error;
96 | }
97 | else
98 | {
99 | statusText.text = "loaded";
100 | // Or retrieve results as binary data
101 | byte[] results = www.downloadHandler.data;
102 | var memStream = new System.IO.MemoryStream(results);
103 | var mpgFile = new NLayer.MpegFile(memStream);
104 | var samples = new float[mpgFile.Length];
105 | mpgFile.ReadSamples(samples, 0, (int)mpgFile.Length);
106 |
107 | var clip = AudioClip.Create("foo", samples.Length, mpgFile.Channels, mpgFile.SampleRate, false);
108 | clip.SetData(samples, 0);
109 | source.clip = clip;
110 | }
111 | }
112 |
113 | void Update()
114 | {
115 | if (!source.isPlaying && source.clip != null && source.clip.loadState == AudioDataLoadState.Loaded)
116 | {
117 | source.Play();
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Assets/AudioLoader.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 747f6edd312a7954ea7875b42d5e23d8
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 05818d71f24bdb74a8c8f8121396bc61
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9ba4b012216b5534aa82e8485d0621f1
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/BitReservoir.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NLayer.Decoder
4 | {
5 | class BitReservoir
6 | {
7 | // Per the spec, the maximum buffer size for layer III is 7680 bits, which is 960 bytes.
8 | // The only catch is if we're decoding a "free" frame, which could be a lot more (since
9 | // some encoders allow higher bitrates to maintain audio transparency).
10 | byte[] _buf = new byte[8192];
11 | int _start = 0, _end = -1, _bitsLeft = 0;
12 | long _bitsRead = 0L;
13 |
14 | static int GetSlots(IMpegFrame frame)
15 | {
16 | var cnt = frame.FrameLength - 4;
17 | if (frame.HasCrc) cnt -= 2;
18 |
19 | if (frame.Version == MpegVersion.Version1 && frame.ChannelMode != MpegChannelMode.Mono) return cnt - 32;
20 | if (frame.Version > MpegVersion.Version1 && frame.ChannelMode == MpegChannelMode.Mono) return cnt - 9;
21 | return cnt - 17;
22 |
23 | }
24 |
25 | public bool AddBits(IMpegFrame frame, int overlap)
26 | {
27 | var originalEnd = _end;
28 |
29 | var slots = GetSlots(frame);
30 | while (--slots >= 0)
31 | {
32 | var temp = frame.ReadBits(8);
33 | if (temp == -1) throw new System.IO.InvalidDataException("Frame did not have enough bytes!");
34 | _buf[++_end] = (byte)temp;
35 | if (_end == _buf.Length - 1) _end = -1;
36 | }
37 |
38 | _bitsLeft = 8;
39 | if (originalEnd == -1)
40 | {
41 | // it's either the start of the stream or we've reset... only return true if overlap says this frame is enough
42 | return overlap == 0;
43 | }
44 | else
45 | {
46 | // it's not the start of the stream so calculate _start based on whether we have enough bytes left
47 |
48 | // if we have enough bytes, reset start to match overlap
49 | if ((originalEnd + 1 - _start + _buf.Length) % _buf.Length >= overlap)
50 | {
51 | _start = (originalEnd + 1 - overlap + _buf.Length) % _buf.Length;
52 | return true;
53 | }
54 | // otherwise, just set start to match the start of the frame (we probably skipped a frame)
55 | else
56 | {
57 | _start = originalEnd;
58 | return false;
59 | }
60 | }
61 | }
62 |
63 | public int GetBits(int count)
64 | {
65 | int bitsRead;
66 | var bits = TryPeekBits(count, out bitsRead);
67 | if (bitsRead < count) throw new System.IO.InvalidDataException("Reservoir did not have enough bytes!");
68 |
69 | SkipBits(count);
70 |
71 | return bits;
72 | }
73 |
74 | public int Get1Bit()
75 | {
76 | // this is an optimized single-bit reader
77 | if (_bitsLeft == 0) throw new System.IO.InvalidDataException("Reservoir did not have enough bytes!");
78 |
79 | --_bitsLeft;
80 | ++_bitsRead;
81 | var val = (_buf[_start] >> _bitsLeft) & 1;
82 |
83 | if (_bitsLeft == 0 && (_start = (_start + 1) % _buf.Length) != _end + 1)
84 | {
85 | _bitsLeft = 8;
86 | }
87 |
88 | return val;
89 | }
90 |
91 | public int TryPeekBits(int count, out int readCount)
92 | {
93 | if (count < 0 || count > 32) throw new ArgumentOutOfRangeException("count", "Must return between 0 and 32 bits!");
94 |
95 | // if we don't have any bits left, just return no bits read
96 | if (_bitsLeft == 0 || count == 0)
97 | {
98 | readCount = 0;
99 | return 0;
100 | }
101 |
102 | // get bits from the current start of the reservoir
103 | var bits = (int)_buf[_start];
104 | if (count < _bitsLeft)
105 | {
106 | // just grab the bits, adjust the "left" count, and return
107 | bits >>= _bitsLeft - count;
108 | bits &= ((1 << count) - 1);
109 | readCount = count;
110 | return bits;
111 | }
112 |
113 | // we have to do it the hard way...
114 | bits &= ((1 << _bitsLeft) - 1);
115 | count -= _bitsLeft;
116 | readCount = _bitsLeft;
117 |
118 | var resStart = _start;
119 |
120 | // arg... gotta grab some more bits...
121 | while (count > 0)
122 | {
123 | // advance the start marker, and if we just advanced it past the end of the buffer, bail
124 | if ((resStart = (resStart + 1) % _buf.Length) == _end + 1)
125 | {
126 | break;
127 | }
128 |
129 | // figure out how many bits to pull from it
130 | var bitsToRead = Math.Min(count, 8);
131 |
132 | // move the existing bits over
133 | bits <<= bitsToRead;
134 | bits |= (_buf[resStart] >> ((8 - bitsToRead) % 8));
135 |
136 | // update our count
137 | count -= bitsToRead;
138 |
139 | // update our remaining bits
140 | readCount += bitsToRead;
141 | }
142 |
143 | return bits;
144 | }
145 |
146 | public int BitsAvailable
147 | {
148 | get
149 | {
150 | if (_bitsLeft > 0)
151 | {
152 | return (((_end + _buf.Length) - _start) % _buf.Length) * 8 + _bitsLeft;
153 | }
154 | return 0;
155 | }
156 | }
157 |
158 | public long BitsRead
159 | {
160 | get { return _bitsRead; }
161 | }
162 |
163 | public void SkipBits(int count)
164 | {
165 | if (count > 0)
166 | {
167 | // make sure we have enough bits to skip
168 | if (count > BitsAvailable) throw new ArgumentOutOfRangeException("count");
169 |
170 | // now calculate the new positions
171 | var offset = (8 - _bitsLeft) + count;
172 | _start = ((offset / 8) + _start) % _buf.Length;
173 | _bitsLeft = 8 - (offset % 8);
174 |
175 | _bitsRead += count;
176 | }
177 | }
178 |
179 | public void RewindBits(int count)
180 | {
181 | _bitsLeft += count;
182 | _bitsRead -= count;
183 | while (_bitsLeft > 8)
184 | {
185 | --_start;
186 | _bitsLeft -= 8;
187 | }
188 | while (_start < 0)
189 | {
190 | _start += _buf.Length;
191 | }
192 | }
193 |
194 | public void FlushBits()
195 | {
196 | if (_bitsLeft < 8)
197 | {
198 | SkipBits(_bitsLeft);
199 | }
200 | }
201 |
202 | public void Reset()
203 | {
204 | _start = 0;
205 | _end = -1;
206 | _bitsLeft = 0;
207 | }
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/BitReservoir.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2a5750753867bb14cbfcba4c6e10ad02
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/FrameBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NLayer.Decoder
4 | {
5 | abstract class FrameBase
6 | {
7 | static int _totalAllocation = 0;
8 | static internal int TotalAllocation
9 | {
10 | get { return System.Threading.Interlocked.CompareExchange(ref _totalAllocation, 0, 0); }
11 | }
12 |
13 | internal long Offset { get; private set; }
14 | internal int Length { get; set; }
15 |
16 | MpegStreamReader _reader;
17 |
18 | byte[] _savedBuffer;
19 |
20 | protected FrameBase() { }
21 |
22 | internal bool Validate(long offset, MpegStreamReader reader)
23 | {
24 | Offset = offset;
25 | _reader = reader;
26 |
27 | var len = Validate();
28 |
29 | if (len > 0)
30 | {
31 | Length = len;
32 | return true;
33 | }
34 | return false;
35 | }
36 |
37 | protected int Read(int offset, byte[] buffer)
38 | {
39 | return Read(offset, buffer, 0, buffer.Length);
40 | }
41 |
42 | protected int Read(int offset, byte[] buffer, int index, int count)
43 | {
44 | if (_savedBuffer != null)
45 | {
46 | if (index < 0 || index + count > buffer.Length) return 0; // check against caller's buffer
47 | if (offset < 0 || offset >= _savedBuffer.Length) return 0; // check against saved buffer
48 | if (offset + count > _savedBuffer.Length) count = _savedBuffer.Length - index; // twiddle the size as needed
49 |
50 | Array.Copy(_savedBuffer, offset, buffer, index, count);
51 | return count;
52 | }
53 | else
54 | {
55 | return _reader.Read(Offset + offset, buffer, index, count);
56 | }
57 | }
58 |
59 | protected int ReadByte(int offset)
60 | {
61 | if (_savedBuffer != null)
62 | {
63 | if (offset < 0) throw new ArgumentOutOfRangeException();
64 | if (offset >= _savedBuffer.Length) return -1;
65 |
66 | return (int)_savedBuffer[offset];
67 | }
68 | else
69 | {
70 | return _reader.ReadByte(Offset + offset);
71 | }
72 | }
73 |
74 | ///
75 | /// Called to validate the frame header
76 | ///
77 | /// The length of the frame, or -1 if frame is invalid
78 | abstract protected int Validate();
79 |
80 | internal void SaveBuffer()
81 | {
82 | _savedBuffer = new byte[Length];
83 | _reader.Read(Offset, _savedBuffer, 0, Length);
84 | System.Threading.Interlocked.Add(ref _totalAllocation, Length);
85 | }
86 |
87 | internal void ClearBuffer()
88 | {
89 | System.Threading.Interlocked.Add(ref _totalAllocation, -Length);
90 | _savedBuffer = null;
91 | }
92 |
93 | ///
94 | /// Called when the stream is not "seek-able"
95 | ///
96 | virtual internal void Parse() { }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/FrameBase.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6d6c9072454d4d749b51b86496ed7373
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/Huffman.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ae03d571c79ef1c4684a2d22ccee9aa5
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/ID3Frame.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace NLayer.Decoder
7 | {
8 | class ID3Frame : FrameBase
9 | {
10 | internal static ID3Frame TrySync(uint syncMark)
11 | {
12 | if ((syncMark & 0xFFFFFF00U) == 0x49443300)
13 | {
14 | return new ID3Frame { _version = 2 };
15 | }
16 |
17 | if ((syncMark & 0xFFFFFF00U) == 0x54414700)
18 | {
19 | if ((syncMark & 0xFF) == 0x2B)
20 | {
21 | return new ID3Frame { _version = 1 };
22 | }
23 | else
24 | {
25 | return new ID3Frame { _version = 0 };
26 | }
27 | }
28 |
29 | return null;
30 | }
31 |
32 | int _version;
33 |
34 | ID3Frame()
35 | {
36 |
37 | }
38 |
39 | protected override int Validate()
40 | {
41 | switch (_version)
42 | {
43 | case 2:
44 | // v2, yay!
45 | var buf = new byte[7];
46 | if (Read(3, buf) == 7)
47 | {
48 | byte flagsMask;
49 | switch (buf[0])
50 | {
51 | case 3:
52 | flagsMask = 0x1F;
53 | break;
54 | case 4:
55 | flagsMask = 0x0F;
56 | break;
57 | default:
58 | return -1;
59 | }
60 |
61 | // ignore the flags (we don't need them for the validation)
62 |
63 | // get the size (7 bits per byte [MSB cleared])
64 | var size = (buf[3] << 21)
65 | | (buf[4] << 14)
66 | | (buf[5] << 7)
67 | | (buf[6]);
68 |
69 | // finally, check to make sure that all the right bits are cleared
70 | if (!(((buf[2] & flagsMask) | (buf[3] & 0x80) | (buf[4] & 0x80) | (buf[5] & 0x80) | (buf[6] & 0x80)) != 0 || buf[1] == 0xFF))
71 | {
72 | return size + 10; // don't forget the sync, flag & size bytes!
73 | }
74 | }
75 | break;
76 | case 1:
77 | return 227 + 128;
78 | case 0:
79 | return 128;
80 | }
81 |
82 | return -1;
83 | }
84 |
85 | internal override void Parse()
86 | {
87 | // assume we have to process it now or else... we can still read the whole frame, so no biggie
88 | switch (_version)
89 | {
90 | case 2:
91 | ParseV2();
92 | break;
93 | case 1:
94 | ParseV1Enh();
95 | break;
96 | case 0:
97 | ParseV1(3);
98 | break;
99 | }
100 | }
101 |
102 | void ParseV1(int offset)
103 | {
104 | //var buffer = new byte[125];
105 | //if (Read(offset, buffer) == 125)
106 | //{
107 | // // v1 tags use ASCII encoding... For now we'll use the built-in encoding, but for Win8 we'll have to build our own.
108 | // var encoding = Encoding.ASCII;
109 | //
110 | // // title (30)
111 | // Title = encoding.GetString(buffer, 0, 30);
112 | //
113 | // // artist (30)
114 | // Artist = encoding.GetString(buffer, 30, 30);
115 | //
116 | // // album (30)
117 | // Album = encoding.GetString(buffer, 60, 30);
118 | //
119 | // // year (4)
120 | // Year = encoding.GetString(buffer, 90, 30);
121 | //
122 | // // comment (30)*
123 | // Comment = encoding.GetString(buffer, 94, 30);
124 | //
125 | // if (buffer[122] == 0)
126 | // {
127 | // // track (1)*
128 | // Track = (int)buffer[123];
129 | // }
130 | //
131 | // // genre (1)
132 | // // ignore for now
133 | //
134 | // // * if byte 29 of comment is 0, track is byte 30. Otherwise, track is unknown.
135 | //}
136 | }
137 |
138 | void ParseV1Enh()
139 | {
140 | ParseV1(230);
141 |
142 | //var buffer = new byte[223];
143 | //if (Read(4, buffer) == 223)
144 | //{
145 | // // v1 tags use ASCII encoding... For now we'll use the built-in encoding, but for Win8 we'll have to build our own.
146 | // var encoding = Encoding.ASCII;
147 | //
148 | // // title (60)
149 | // Title += encoding.GetString(buffer, 0, 60);
150 | //
151 | // // artist (60)
152 | // Artist += encoding.GetString(buffer, 60, 60);
153 | //
154 | // // album (60)
155 | // Album += encoding.GetString(buffer, 120, 60);
156 | //
157 | // // speed (1)
158 | // //var speed = buffer[180];
159 | //
160 | // // genre (30)
161 | // Genre = encoding.GetString(buffer, 181, 30);
162 | //
163 | // // start-time (6)
164 | // // 211
165 | //
166 | // // end-time (6)
167 | // // 217
168 | //}
169 | }
170 |
171 | void ParseV2()
172 | {
173 | // v2 is much more complicated than v1... don't worry about it for now
174 | // look for any merged frames, as well
175 | }
176 |
177 | internal int Version
178 | {
179 | get
180 | {
181 | if (_version == 0) return 1;
182 | return _version;
183 | }
184 | }
185 |
186 | //internal string Title { get; private set; }
187 | //internal string Artist { get; private set; }
188 | //internal string Album { get; private set; }
189 | //internal string Year { get; private set; }
190 | //internal string Comment { get; private set; }
191 | //internal int Track { get; private set; }
192 | //internal string Genre { get; private set; }
193 | // speed
194 | //public TimeSpan StartTime { get; private set; }
195 | //public TimeSpan EndTime { get; private set; }
196 |
197 | internal void Merge(ID3Frame newFrame)
198 | {
199 | // just save off the frame for parsing later
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/ID3Frame.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a98c2c1103a937848a69259c99532070
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/LayerDecoderBase.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * NLayer - A C# MPEG1/2/2.5 audio decoder
3 | *
4 | * Portions of this file are courtesy Fluendo, S.A. They are dual licensed as Ms-PL
5 | * and under the following license:
6 | *
7 | * Copyright <2005-2012> Fluendo S.A.
8 | *
9 | * Unless otherwise indicated, Source Code is licensed under MIT license.
10 | * See further explanation attached in License Statement (distributed in the file
11 | * LICENSE).
12 | *
13 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
14 | * this software and associated documentation files (the "Software"), to deal in
15 | * the Software without restriction, including without limitation the rights to
16 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
17 | * of the Software, and to permit persons to whom the Software is furnished to do
18 | * so, subject to the following conditions:
19 | *
20 | * The above copyright notice and this permission notice shall be included in all
21 | * copies or substantial portions of the Software.
22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 | * SOFTWARE.
30 | *
31 | */
32 |
33 | using System.Collections.Generic;
34 |
35 | namespace NLayer.Decoder
36 | {
37 | abstract class LayerDecoderBase
38 | {
39 | protected const int SBLIMIT = 32;
40 |
41 | const float INV_SQRT_2 = 7.071067811865474617150084668537e-01f;
42 |
43 | #region Tables
44 |
45 | static float[] DEWINDOW_TABLE = {
46 | 0.000000000f, -0.000015259f, -0.000015259f, -0.000015259f,
47 | -0.000015259f, -0.000015259f, -0.000015259f, -0.000030518f,
48 | -0.000030518f, -0.000030518f, -0.000030518f, -0.000045776f,
49 | -0.000045776f, -0.000061035f, -0.000061035f, -0.000076294f,
50 | -0.000076294f, -0.000091553f, -0.000106812f, -0.000106812f,
51 | -0.000122070f, -0.000137329f, -0.000152588f, -0.000167847f,
52 | -0.000198364f, -0.000213623f, -0.000244141f, -0.000259399f,
53 | -0.000289917f, -0.000320435f, -0.000366211f, -0.000396729f,
54 | -0.000442505f, -0.000473022f, -0.000534058f, -0.000579834f,
55 | -0.000625610f, -0.000686646f, -0.000747681f, -0.000808716f,
56 | -0.000885010f, -0.000961304f, -0.001037598f, -0.001113892f,
57 | -0.001205444f, -0.001296997f, -0.001388550f, -0.001480103f,
58 | -0.001586914f, -0.001693726f, -0.001785278f, -0.001907349f,
59 | -0.002014160f, -0.002120972f, -0.002243042f, -0.002349854f,
60 | -0.002456665f, -0.002578735f, -0.002685547f, -0.002792358f,
61 | -0.002899170f, -0.002990723f, -0.003082275f, -0.003173828f,
62 | 0.003250122f, 0.003326416f, 0.003387451f, 0.003433228f,
63 | 0.003463745f, 0.003479004f, 0.003479004f, 0.003463745f,
64 | 0.003417969f, 0.003372192f, 0.003280640f, 0.003173828f,
65 | 0.003051758f, 0.002883911f, 0.002700806f, 0.002487183f,
66 | 0.002227783f, 0.001937866f, 0.001617432f, 0.001266479f,
67 | 0.000869751f, 0.000442505f, -0.000030518f, -0.000549316f,
68 | -0.001098633f, -0.001693726f, -0.002334595f, -0.003005981f,
69 | -0.003723145f, -0.004486084f, -0.005294800f, -0.006118774f,
70 | -0.007003784f, -0.007919312f, -0.008865356f, -0.009841919f,
71 | -0.010848999f, -0.011886597f, -0.012939453f, -0.014022827f,
72 | -0.015121460f, -0.016235352f, -0.017349243f, -0.018463135f,
73 | -0.019577026f, -0.020690918f, -0.021789551f, -0.022857666f,
74 | -0.023910522f, -0.024932861f, -0.025909424f, -0.026840210f,
75 | -0.027725220f, -0.028533936f, -0.029281616f, -0.029937744f,
76 | -0.030532837f, -0.031005859f, -0.031387329f, -0.031661987f,
77 | -0.031814575f, -0.031845093f, -0.031738281f, -0.031478882f,
78 | 0.031082153f, 0.030517578f, 0.029785156f, 0.028884888f,
79 | 0.027801514f, 0.026535034f, 0.025085449f, 0.023422241f,
80 | 0.021575928f, 0.019531250f, 0.017257690f, 0.014801025f,
81 | 0.012115479f, 0.009231567f, 0.006134033f, 0.002822876f,
82 | -0.000686646f, -0.004394531f, -0.008316040f, -0.012420654f,
83 | -0.016708374f, -0.021179199f, -0.025817871f, -0.030609131f,
84 | -0.035552979f, -0.040634155f, -0.045837402f, -0.051132202f,
85 | -0.056533813f, -0.061996460f, -0.067520142f, -0.073059082f,
86 | -0.078628540f, -0.084182739f, -0.089706421f, -0.095169067f,
87 | -0.100540161f, -0.105819702f, -0.110946655f, -0.115921021f,
88 | -0.120697021f, -0.125259399f, -0.129562378f, -0.133590698f,
89 | -0.137298584f, -0.140670776f, -0.143676758f, -0.146255493f,
90 | -0.148422241f, -0.150115967f, -0.151306152f, -0.151962280f,
91 | -0.152069092f, -0.151596069f, -0.150497437f, -0.148773193f,
92 | -0.146362305f, -0.143264771f, -0.139450073f, -0.134887695f,
93 | -0.129577637f, -0.123474121f, -0.116577148f, -0.108856201f,
94 | 0.100311279f, 0.090927124f, 0.080688477f, 0.069595337f,
95 | 0.057617187f, 0.044784546f, 0.031082153f, 0.016510010f,
96 | 0.001068115f, -0.015228271f, -0.032379150f, -0.050354004f,
97 | -0.069168091f, -0.088775635f, -0.109161377f, -0.130310059f,
98 | -0.152206421f, -0.174789429f, -0.198059082f, -0.221984863f,
99 | -0.246505737f, -0.271591187f, -0.297210693f, -0.323318481f,
100 | -0.349868774f, -0.376800537f, -0.404083252f, -0.431655884f,
101 | -0.459472656f, -0.487472534f, -0.515609741f, -0.543823242f,
102 | -0.572036743f, -0.600219727f, -0.628295898f, -0.656219482f,
103 | -0.683914185f, -0.711318970f, -0.738372803f, -0.765029907f,
104 | -0.791213989f, -0.816864014f, -0.841949463f, -0.866363525f,
105 | -0.890090942f, -0.913055420f, -0.935195923f, -0.956481934f,
106 | -0.976852417f, -0.996246338f, -1.014617920f, -1.031936646f,
107 | -1.048156738f, -1.063217163f, -1.077117920f, -1.089782715f,
108 | -1.101211548f, -1.111373901f, -1.120223999f, -1.127746582f,
109 | -1.133926392f, -1.138763428f, -1.142211914f, -1.144287109f,
110 | 1.144989014f, 1.144287109f, 1.142211914f, 1.138763428f,
111 | 1.133926392f, 1.127746582f, 1.120223999f, 1.111373901f,
112 | 1.101211548f, 1.089782715f, 1.077117920f, 1.063217163f,
113 | 1.048156738f, 1.031936646f, 1.014617920f, 0.996246338f,
114 | 0.976852417f, 0.956481934f, 0.935195923f, 0.913055420f,
115 | 0.890090942f, 0.866363525f, 0.841949463f, 0.816864014f,
116 | 0.791213989f, 0.765029907f, 0.738372803f, 0.711318970f,
117 | 0.683914185f, 0.656219482f, 0.628295898f, 0.600219727f,
118 | 0.572036743f, 0.543823242f, 0.515609741f, 0.487472534f,
119 | 0.459472656f, 0.431655884f, 0.404083252f, 0.376800537f,
120 | 0.349868774f, 0.323318481f, 0.297210693f, 0.271591187f,
121 | 0.246505737f, 0.221984863f, 0.198059082f, 0.174789429f,
122 | 0.152206421f, 0.130310059f, 0.109161377f, 0.088775635f,
123 | 0.069168091f, 0.050354004f, 0.032379150f, 0.015228271f,
124 | -0.001068115f, -0.016510010f, -0.031082153f, -0.044784546f,
125 | -0.057617187f, -0.069595337f, -0.080688477f, -0.090927124f,
126 | 0.100311279f, 0.108856201f, 0.116577148f, 0.123474121f,
127 | 0.129577637f, 0.134887695f, 0.139450073f, 0.143264771f,
128 | 0.146362305f, 0.148773193f, 0.150497437f, 0.151596069f,
129 | 0.152069092f, 0.151962280f, 0.151306152f, 0.150115967f,
130 | 0.148422241f, 0.146255493f, 0.143676758f, 0.140670776f,
131 | 0.137298584f, 0.133590698f, 0.129562378f, 0.125259399f,
132 | 0.120697021f, 0.115921021f, 0.110946655f, 0.105819702f,
133 | 0.100540161f, 0.095169067f, 0.089706421f, 0.084182739f,
134 | 0.078628540f, 0.073059082f, 0.067520142f, 0.061996460f,
135 | 0.056533813f, 0.051132202f, 0.045837402f, 0.040634155f,
136 | 0.035552979f, 0.030609131f, 0.025817871f, 0.021179199f,
137 | 0.016708374f, 0.012420654f, 0.008316040f, 0.004394531f,
138 | 0.000686646f, -0.002822876f, -0.006134033f, -0.009231567f,
139 | -0.012115479f, -0.014801025f, -0.017257690f, -0.019531250f,
140 | -0.021575928f, -0.023422241f, -0.025085449f, -0.026535034f,
141 | -0.027801514f, -0.028884888f, -0.029785156f, -0.030517578f,
142 | 0.031082153f, 0.031478882f, 0.031738281f, 0.031845093f,
143 | 0.031814575f, 0.031661987f, 0.031387329f, 0.031005859f,
144 | 0.030532837f, 0.029937744f, 0.029281616f, 0.028533936f,
145 | 0.027725220f, 0.026840210f, 0.025909424f, 0.024932861f,
146 | 0.023910522f, 0.022857666f, 0.021789551f, 0.020690918f,
147 | 0.019577026f, 0.018463135f, 0.017349243f, 0.016235352f,
148 | 0.015121460f, 0.014022827f, 0.012939453f, 0.011886597f,
149 | 0.010848999f, 0.009841919f, 0.008865356f, 0.007919312f,
150 | 0.007003784f, 0.006118774f, 0.005294800f, 0.004486084f,
151 | 0.003723145f, 0.003005981f, 0.002334595f, 0.001693726f,
152 | 0.001098633f, 0.000549316f, 0.000030518f, -0.000442505f,
153 | -0.000869751f, -0.001266479f, -0.001617432f, -0.001937866f,
154 | -0.002227783f, -0.002487183f, -0.002700806f, -0.002883911f,
155 | -0.003051758f, -0.003173828f, -0.003280640f, -0.003372192f,
156 | -0.003417969f, -0.003463745f, -0.003479004f, -0.003479004f,
157 | -0.003463745f, -0.003433228f, -0.003387451f, -0.003326416f,
158 | 0.003250122f, 0.003173828f, 0.003082275f, 0.002990723f,
159 | 0.002899170f, 0.002792358f, 0.002685547f, 0.002578735f,
160 | 0.002456665f, 0.002349854f, 0.002243042f, 0.002120972f,
161 | 0.002014160f, 0.001907349f, 0.001785278f, 0.001693726f,
162 | 0.001586914f, 0.001480103f, 0.001388550f, 0.001296997f,
163 | 0.001205444f, 0.001113892f, 0.001037598f, 0.000961304f,
164 | 0.000885010f, 0.000808716f, 0.000747681f, 0.000686646f,
165 | 0.000625610f, 0.000579834f, 0.000534058f, 0.000473022f,
166 | 0.000442505f, 0.000396729f, 0.000366211f, 0.000320435f,
167 | 0.000289917f, 0.000259399f, 0.000244141f, 0.000213623f,
168 | 0.000198364f, 0.000167847f, 0.000152588f, 0.000137329f,
169 | 0.000122070f, 0.000106812f, 0.000106812f, 0.000091553f,
170 | 0.000076294f, 0.000076294f, 0.000061035f, 0.000061035f,
171 | 0.000045776f, 0.000045776f, 0.000030518f, 0.000030518f,
172 | 0.000030518f, 0.000030518f, 0.000015259f, 0.000015259f,
173 | 0.000015259f, 0.000015259f, 0.000015259f, 0.000015259f
174 | };
175 |
176 | static float[] SYNTH_COS64_TABLE = {
177 | 5.0060299823519627260e-01f, 5.0241928618815567820e-01f, 5.0547095989754364798e-01f, 5.0979557910415917998e-01f,
178 | 5.1544730992262455249e-01f, 5.2249861493968885462e-01f, 5.3104259108978413284e-01f, 5.4119610014619701222e-01f,
179 | 5.5310389603444454210e-01f, 5.6694403481635768927e-01f, 5.8293496820613388554e-01f, 6.0134488693504528634e-01f,
180 | 6.2250412303566482475e-01f, 6.4682178335999007679e-01f, 6.7480834145500567800e-01f, 7.0710678118654746172e-01f,
181 | 7.4453627100229857749e-01f, 7.8815462345125020249e-01f, 8.3934964541552681272e-01f, 8.9997622313641556513e-01f,
182 | 9.7256823786196078263e-01f, 1.0606776859903470633e+00f, 1.1694399334328846596e+00f, 1.3065629648763763537e+00f,
183 | 1.4841646163141661852e+00f, 1.7224470982383341955e+00f, 2.0577810099534108446e+00f, 2.5629154477415054814e+00f,
184 | 3.4076084184687189804e+00f, 5.1011486186891552563e+00f, 1.0190008123548032870e+01f
185 | };
186 |
187 | #endregion
188 |
189 | List _synBuf = new List(2);
190 | List _bufOffset = new List(2);
191 |
192 | float[] _eq;
193 |
194 | internal LayerDecoderBase()
195 | {
196 | StereoMode = StereoMode.Both;
197 | }
198 |
199 | abstract internal int DecodeFrame(IMpegFrame frame, float[] ch0, float[] ch1);
200 |
201 | internal void SetEQ(float[] eq)
202 | {
203 | if (eq == null || eq.Length == 32)
204 | {
205 | _eq = eq;
206 | }
207 | }
208 |
209 | internal StereoMode StereoMode { get; set; }
210 |
211 | virtual internal void ResetForSeek()
212 | {
213 | _synBuf.Clear();
214 | _bufOffset.Clear();
215 | }
216 |
217 | float[] ippuv = new float[512];
218 |
219 | protected void InversePolyPhase(int channel, float[] data)
220 | {
221 | float[] synBuf;
222 | int k;
223 | GetBufAndOffset(channel, out synBuf, out k);
224 |
225 | if (_eq != null)
226 | {
227 | for (int i = 0; i < 32; i++)
228 | {
229 | data[i] *= _eq[i];
230 | }
231 | }
232 |
233 | DCT32(data, synBuf, k);
234 |
235 | BuildUVec(ippuv, synBuf, k);
236 |
237 | DewindowOutput(ippuv, data);
238 | }
239 |
240 | void GetBufAndOffset(int channel, out float[] synBuf, out int k)
241 | {
242 | while (_synBuf.Count <= channel)
243 | {
244 | _synBuf.Add(new float[1024]);
245 | }
246 |
247 | while (_bufOffset.Count <= channel)
248 | {
249 | _bufOffset.Add(0);
250 | }
251 |
252 | synBuf = _synBuf[channel];
253 | k = _bufOffset[channel];
254 |
255 | k = (k - 32) & 511;
256 | _bufOffset[channel] = k;
257 | }
258 |
259 | float[] ei32 = new float[16], eo32 = new float[16], oi32 = new float[16], oo32 = new float[16];
260 |
261 | void DCT32(float[] _in, float[] _out, int k)
262 | {
263 | int i;
264 |
265 | for (i = 0; i < 16; i++)
266 | {
267 | ei32[i] = _in[i] + _in[31 - i];
268 | oi32[i] = (_in[i] - _in[31 - i]) * SYNTH_COS64_TABLE[2 * i];
269 | }
270 |
271 | DCT16(ei32, eo32);
272 | DCT16(oi32, oo32);
273 |
274 | for (i = 0; i < 15; i++)
275 | {
276 | _out[2 * i + k] = eo32[i];
277 | _out[2 * i + 1 + k] = oo32[i] + oo32[i + 1];
278 | }
279 | _out[30 + k] = eo32[15];
280 | _out[31 + k] = oo32[15];
281 | }
282 |
283 | float[] ei16 = new float[8], eo16 = new float[8], oi16 = new float[8], oo16 = new float[8];
284 |
285 | void DCT16(float[] _in, float[] _out)
286 | {
287 | float a, b;
288 |
289 | a = _in[0]; b = _in[15];
290 | ei16[0] = a + b;
291 | oi16[0] = (a - b) * SYNTH_COS64_TABLE[1];
292 | a = _in[1]; b = _in[14];
293 | ei16[1] = a + b;
294 | oi16[1] = (a - b) * SYNTH_COS64_TABLE[5];
295 | a = _in[2]; b = _in[13];
296 | ei16[2] = a + b;
297 | oi16[2] = (a - b) * SYNTH_COS64_TABLE[9];
298 | a = _in[3]; b = _in[12];
299 | ei16[3] = a + b;
300 | oi16[3] = (a - b) * SYNTH_COS64_TABLE[13];
301 | a = _in[4]; b = _in[11];
302 | ei16[4] = a + b;
303 | oi16[4] = (a - b) * SYNTH_COS64_TABLE[17];
304 | a = _in[5]; b = _in[10];
305 | ei16[5] = a + b;
306 | oi16[5] = (a - b) * SYNTH_COS64_TABLE[21];
307 | a = _in[6]; b = _in[9];
308 | ei16[6] = a + b;
309 | oi16[6] = (a - b) * SYNTH_COS64_TABLE[25];
310 | a = _in[7]; b = _in[8];
311 | ei16[7] = a + b;
312 | oi16[7] = (a - b) * SYNTH_COS64_TABLE[29];
313 |
314 | DCT8(ei16, eo16);
315 | DCT8(oi16, oo16);
316 |
317 | _out[0] = eo16[0];
318 | _out[1] = oo16[0] + oo16[1];
319 | _out[2] = eo16[1];
320 | _out[3] = oo16[1] + oo16[2];
321 | _out[4] = eo16[2];
322 | _out[5] = oo16[2] + oo16[3];
323 | _out[6] = eo16[3];
324 | _out[7] = oo16[3] + oo16[4];
325 | _out[8] = eo16[4];
326 | _out[9] = oo16[4] + oo16[5];
327 | _out[10] = eo16[5];
328 | _out[11] = oo16[5] + oo16[6];
329 | _out[12] = eo16[6];
330 | _out[13] = oo16[6] + oo16[7];
331 | _out[14] = eo16[7];
332 | _out[15] = oo16[7];
333 | }
334 |
335 | float[] ei8 = new float[4], tmp8 = new float[6], oi8 = new float[4], oo8 = new float[4];
336 |
337 | void DCT8(float[] _in, float[] _out)
338 | {
339 | /* Even indices */
340 | ei8[0] = _in[0] + _in[7];
341 | ei8[1] = _in[3] + _in[4];
342 | ei8[2] = _in[1] + _in[6];
343 | ei8[3] = _in[2] + _in[5];
344 |
345 | tmp8[0] = ei8[0] + ei8[1];
346 | tmp8[1] = ei8[2] + ei8[3];
347 | tmp8[2] = (ei8[0] - ei8[1]) * SYNTH_COS64_TABLE[7];
348 | tmp8[3] = (ei8[2] - ei8[3]) * SYNTH_COS64_TABLE[23];
349 | tmp8[4] = (float)((tmp8[2] - tmp8[3]) * INV_SQRT_2);
350 |
351 | _out[0] = tmp8[0] + tmp8[1];
352 | _out[2] = tmp8[2] + tmp8[3] + tmp8[4];
353 | _out[4] = (float)((tmp8[0] - tmp8[1]) * INV_SQRT_2);
354 | _out[6] = tmp8[4];
355 |
356 | /* Odd indices */
357 | oi8[0] = (_in[0] - _in[7]) * SYNTH_COS64_TABLE[3];
358 | oi8[1] = (_in[1] - _in[6]) * SYNTH_COS64_TABLE[11];
359 | oi8[2] = (_in[2] - _in[5]) * SYNTH_COS64_TABLE[19];
360 | oi8[3] = (_in[3] - _in[4]) * SYNTH_COS64_TABLE[27];
361 |
362 | tmp8[0] = oi8[0] + oi8[3];
363 | tmp8[1] = oi8[1] + oi8[2];
364 | tmp8[2] = (oi8[0] - oi8[3]) * SYNTH_COS64_TABLE[7];
365 | tmp8[3] = (oi8[1] - oi8[2]) * SYNTH_COS64_TABLE[23];
366 | tmp8[4] = tmp8[2] + tmp8[3];
367 | tmp8[5] = (float)((tmp8[2] - tmp8[3]) * INV_SQRT_2);
368 |
369 | oo8[0] = tmp8[0] + tmp8[1];
370 | oo8[1] = tmp8[4] + tmp8[5];
371 | oo8[2] = (float)((tmp8[0] - tmp8[1]) * INV_SQRT_2);
372 | oo8[3] = tmp8[5];
373 |
374 | _out[1] = oo8[0] + oo8[1];
375 | _out[3] = oo8[1] + oo8[2];
376 | _out[5] = oo8[2] + oo8[3];
377 | _out[7] = oo8[3];
378 | }
379 |
380 | void BuildUVec(float[] u_vec, float[] cur_synbuf, int k)
381 | {
382 | int i, j, uvp = 0;
383 |
384 | for (j = 0; j < 8; j++)
385 | {
386 | for (i = 0; i < 16; i++)
387 | {
388 | /* Copy first 32 elements */
389 | u_vec[uvp + i] = cur_synbuf[k + i + 16];
390 | u_vec[uvp + i + 17] = -cur_synbuf[k + 31 - i];
391 | }
392 |
393 | /* k wraps at the synthesis buffer boundary */
394 | k = (k + 32) & 511;
395 |
396 | for (i = 0; i < 16; i++)
397 | {
398 | /* Copy next 32 elements */
399 | u_vec[uvp + i + 32] = -cur_synbuf[k + 16 - i];
400 | u_vec[uvp + i + 48] = -cur_synbuf[k + i];
401 | }
402 | u_vec[uvp + 16] = 0;
403 |
404 | /* k wraps at the synthesis buffer boundary */
405 | k = (k + 32) & 511;
406 | uvp += 64;
407 | }
408 | }
409 |
410 | void DewindowOutput(float[] u_vec, float[] samples)
411 | {
412 | for (int i = 0; i < 512; i++)
413 | {
414 | u_vec[i] *= DEWINDOW_TABLE[i];
415 | }
416 |
417 | for (int i = 0; i < 32; i++)
418 | {
419 | float sum = u_vec[i];
420 | sum += u_vec[i + (1 << 5)];
421 | sum += u_vec[i + (2 << 5)];
422 | sum += u_vec[i + (3 << 5)];
423 | sum += u_vec[i + (4 << 5)];
424 | sum += u_vec[i + (5 << 5)];
425 | sum += u_vec[i + (6 << 5)];
426 | sum += u_vec[i + (7 << 5)];
427 | sum += u_vec[i + (8 << 5)];
428 | sum += u_vec[i + (9 << 5)];
429 | sum += u_vec[i + (10 << 5)];
430 | sum += u_vec[i + (11 << 5)];
431 | sum += u_vec[i + (12 << 5)];
432 | sum += u_vec[i + (13 << 5)];
433 | sum += u_vec[i + (14 << 5)];
434 | sum += u_vec[i + (15 << 5)];
435 | u_vec[i] = sum;
436 | }
437 |
438 | for (int i = 0; i < 32; i++)
439 | {
440 | samples[i] = u_vec[i];
441 | }
442 | }
443 | }
444 | }
445 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/LayerDecoderBase.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a5d192dc216711441815b262642d6a8d
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/LayerIDecoder.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * NLayer - A C# MPEG1/2/2.5 audio decoder
3 | *
4 | */
5 |
6 | using System;
7 |
8 | namespace NLayer.Decoder
9 | {
10 | // Layer I is really just a special case of Layer II... 1 granule, 4 allocation bits per subband, 1 scalefactor per active subband, no grouping
11 | // That (of course) means we literally have no logic here
12 | class LayerIDecoder : LayerIIDecoderBase
13 | {
14 | static internal bool GetCRC(MpegFrame frame, ref uint crc)
15 | {
16 | return LayerIIDecoderBase.GetCRC(frame, _rateTable, _allocLookupTable, false, ref crc);
17 | }
18 |
19 | // this is simple: all 32 subbands have a 4-bit allocations, and positive allocation values are {bits per sample} - 1
20 | static readonly int[] _rateTable = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
21 |
22 | static readonly int[][] _allocLookupTable = { new int[] { 4, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 } };
23 |
24 | internal LayerIDecoder() : base(_allocLookupTable, 1) { }
25 |
26 | protected override int[] GetRateTable(IMpegFrame frame)
27 | {
28 | return _rateTable;
29 | }
30 |
31 | protected override void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels)
32 | {
33 | // this is a no-op since the base logic uses "2" as the "has energy" marker
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/LayerIDecoder.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d3f842ceb8172bd40bf0b1a5ccab8705
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/LayerIIDecoder.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * NLayer - A C# MPEG1/2/2.5 audio decoder
3 | *
4 | */
5 |
6 | using System;
7 |
8 | namespace NLayer.Decoder
9 | {
10 | // there's not much we have to do here... table selection, granule count, scalefactor selection
11 | class LayerIIDecoder : LayerIIDecoderBase
12 | {
13 | static internal bool GetCRC(MpegFrame frame, ref uint crc)
14 | {
15 | return LayerIIDecoderBase.GetCRC(frame, SelectTable(frame), _allocLookupTable, true, ref crc);
16 | }
17 |
18 | // figure out which rate table to use... basically, high-rate full, high-rate limited, low-rate limited, low-rate minimal, and LSF.
19 | static int[] SelectTable(IMpegFrame frame)
20 | {
21 | var bitRatePerChannel = (frame.BitRate / (frame.ChannelMode == MpegChannelMode.Mono ? 1 : 2)) / 1000;
22 |
23 | if (frame.Version == MpegVersion.Version1)
24 | {
25 | if ((bitRatePerChannel >= 56 && bitRatePerChannel <= 80) || (frame.SampleRate == 48000 && bitRatePerChannel >= 56))
26 | {
27 | return _rateLookupTable[0]; // high-rate, 27 subbands
28 | }
29 | else if (frame.SampleRate != 48000 && bitRatePerChannel >= 96)
30 | {
31 | return _rateLookupTable[1]; // high-rate, 30 subbands
32 | }
33 | else if (frame.SampleRate != 32000 && bitRatePerChannel <= 48)
34 | {
35 | return _rateLookupTable[2]; // low-rate, 8 subbands
36 | }
37 | else
38 | {
39 | return _rateLookupTable[3]; // low-rate, 12 subbands
40 | }
41 | }
42 | else
43 | {
44 | return _rateLookupTable[4]; // lsf, 30 subbands
45 | }
46 | }
47 |
48 | // this table tells us which allocation lookup list to use for each subband
49 | // note that each row has the same number of elements as there are subbands for that type...
50 | static readonly int[][] _rateLookupTable = {
51 | // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
52 | new int[] { 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, // high-rate, 27 subbands
53 | new int[] { 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, // high-rate, 30 subbands
54 | new int[] { 4, 4, 5, 5, 5, 5, 5, 5 }, // low-rate, 7 subbands
55 | new int[] { 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }, // low-rate, 12 subbands
56 | new int[] { 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }, // lsf, 30 subbands
57 | };
58 |
59 | // this tells the decode logic: a) how many bits per allocation, and b) how many bits per sample for the give allocation value
60 | // if negative, read -x bits and handle as a group
61 | static readonly int[][] _allocLookupTable = {
62 | // bits 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
63 | new int[] { 2, 0, -5, -7, 16 }, // 0 (II)
64 | new int[] { 3, 0, -5, -7, 3,-10, 4, 5, 16 }, // 1 (II)
65 | new int[] { 4, 0, -5, -7, 3,-10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16 }, // 2 (II)
66 | new int[] { 4, 0, -5, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, // 3 (II)
67 | new int[] { 4, 0, -5, -7,-10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, // 4 (II, 4, 4 bits per alloc)
68 | new int[] { 3, 0, -5, -7,-10, 4, 5, 6, 9 }, // 5 (II, 4, 3 bits per alloc)
69 | new int[] { 4, 0, -5, -7, 3,-10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, // 6 (II)
70 | new int[] { 2, 0, -5, -7, 3 }, // 7 (II, 4, 2 bits per alloc)
71 | };
72 |
73 | internal LayerIIDecoder() : base(_allocLookupTable, 3) { }
74 |
75 | protected override int[] GetRateTable(IMpegFrame frame)
76 | {
77 | return SelectTable(frame);
78 | }
79 |
80 | protected override void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels)
81 | {
82 | // we'll never have more than 30 active subbands
83 | for (int sb = 0; sb < 30; sb++)
84 | {
85 | for (int ch = 0; ch < channels; ch++)
86 | {
87 | if (scfsi[ch][sb] == 2)
88 | {
89 | scfsi[ch][sb] = frame.ReadBits(2);
90 | }
91 | }
92 | }
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/LayerIIDecoder.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8929914c526417f4e8ca6a89c6a308af
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/LayerIIDecoderBase.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * NLayer - A C# MPEG1/2/2.5 audio decoder
3 | *
4 | */
5 |
6 | using System;
7 |
8 | namespace NLayer.Decoder
9 | {
10 | // Layers I & II are basically identical... Layer II adds sample grouping, per subband allocation schemes, and granules
11 | // Because of this fact, we can use the same decoder for both
12 | abstract class LayerIIDecoderBase : LayerDecoderBase
13 | {
14 | protected const int SSLIMIT = 12;
15 |
16 | static protected bool GetCRC(MpegFrame frame, int[] rateTable, int[][] allocLookupTable, bool readScfsiBits, ref uint crc)
17 | {
18 | // ugh... we basically have to re-implement the allocation logic here.
19 |
20 | // keep up with how many active subbands we need to read selection info for
21 | var scfsiBits = 0;
22 |
23 | // only read as many subbands as we actually need; pay attention to the intensity stereo subbands
24 | var subbandCount = rateTable.Length;
25 | var jsbound = subbandCount;
26 | if (frame.ChannelMode == MpegChannelMode.JointStereo)
27 | {
28 | jsbound = frame.ChannelModeExtension * 4 + 4;
29 | }
30 |
31 | // read the full stereo subbands
32 | var channels = frame.ChannelMode == MpegChannelMode.Mono ? 1 : 2;
33 | var sb = 0;
34 | for (; sb < jsbound; sb++)
35 | {
36 | var bits = allocLookupTable[rateTable[sb]][0];
37 | for (int ch = 0; ch < channels; ch++)
38 | {
39 | var alloc = frame.ReadBits(bits);
40 | if (alloc > 0) scfsiBits += 2;
41 |
42 | MpegFrame.UpdateCRC(alloc, bits, ref crc);
43 | }
44 | }
45 |
46 | // read the intensity stereo subbands
47 | for (; sb < subbandCount; sb++)
48 | {
49 | var bits = allocLookupTable[rateTable[sb]][0];
50 |
51 | var alloc = frame.ReadBits(bits);
52 | if (alloc > 0) scfsiBits += channels * 2;
53 |
54 | MpegFrame.UpdateCRC(alloc, bits, ref crc);
55 | }
56 |
57 | // finally, read the scalefac selection bits
58 | if (readScfsiBits)
59 | {
60 | while (scfsiBits >= 2)
61 | {
62 | MpegFrame.UpdateCRC(frame.ReadBits(2), 2, ref crc);
63 | scfsiBits -= 2;
64 | }
65 | }
66 |
67 | return true;
68 | }
69 |
70 | #region Lookup Tables
71 |
72 | // this is from the formula: C = 1 / (1 / (1 << (Bits / 2 + Bits % 2 - 1)) + .5f)
73 | // index by real bits (Bits / 2 + Bits % 2 - 1)
74 | static readonly float[] _groupedC = { 0, 0, 1.33333333333f, 1.60000000000f, 1.77777777777f };
75 |
76 | // these are always -0.5
77 | // index by real bits (Bits / 2 + Bits % 2 - 1)
78 | static readonly float[] _groupedD = { 0, 0, -0.5f, -0.5f, -0.5f };
79 |
80 | // this is from the formula: 1 / (1 - (1f / (1 << Bits)))
81 | // index by bits
82 | static readonly float[] _C = {
83 | 0.00000000000f,
84 | 0.00000000000f, 1.33333333333f, 1.14285714286f, 1.06666666666f, 1.03225806452f, 1.01587301587f, 1.00787401575f, 1.00392156863f,
85 | 1.00195694716f, 1.00097751711f, 1.00048851979f, 1.00024420024f, 1.00012208522f, 1.00006103888f, 1.00003051851f, 1.00001525902f
86 | };
87 |
88 | // this is from the formula: 1f / (1 << Bits - 1) - 1
89 | // index by bits
90 | static readonly float[] _D = {
91 | 0.00000000000f - 0f,
92 | 0.00000000000f - 0f, 0.50000000000f - 1f, 0.25000000000f - 1f, 0.12500000000f - 1f, 0.062500000000f - 1f, 0.03125000000f - 1f, 0.01562500000f - 1f, 0.00781250000f - 1f,
93 | 0.00390625000f - 1f, 0.00195312500f - 1f, 0.00097656250f - 1f, 0.00048828125f - 1f, 0.000244140630f - 1f, 0.00012207031f - 1f, 0.00006103516f - 1f, 0.00003051758f - 1f
94 | };
95 |
96 | // this is from a (really annoying) formula:
97 | // x = Math.Pow(4, 1 / ((2 << (idx % 3) + 1) - (idx % 3))) / (1 << (idx / 3))
98 | // Basically...
99 | // [0] = Math.Pow(4, 1 / 2), [1] = Math.Pow(4, 1 / 3), [2] = Math.Pow(4, 1 / 6)
100 | // For every remaining element, calculate (in order): [idx] = [idx - 3] / 2
101 | static readonly float[] _denormalMultiplier = {
102 | 2.00000000000000f, 1.58740105196820f, 1.25992104989487f, 1.00000000000000f, 0.79370052598410f, 0.62996052494744f, 0.50000000000000f, 0.39685026299205f,
103 | 0.31498026247372f, 0.25000000000000f, 0.19842513149602f, 0.15749013123686f, 0.12500000000000f, 0.09921256574801f, 0.07874506561843f, 0.06250000000000f,
104 | 0.04960628287401f, 0.03937253280921f, 0.03125000000000f, 0.02480314143700f, 0.01968626640461f, 0.01562500000000f, 0.01240157071850f, 0.00984313320230f,
105 | 0.00781250000000f, 0.00620078535925f, 0.00492156660115f, 0.00390625000000f, 0.00310039267963f, 0.00246078330058f, 0.00195312500000f, 0.00155019633981f,
106 | 0.00123039165029f, 0.00097656250000f, 0.00077509816991f, 0.00061519582514f, 0.00048828125000f, 0.00038754908495f, 0.00030759791257f, 0.00024414062500f,
107 | 0.00019377454248f, 0.00015379895629f, 0.00012207031250f, 0.00009688727124f, 0.00007689947814f, 0.00006103515625f, 0.00004844363562f, 0.00003844973907f,
108 | 0.00003051757813f, 0.00002422181781f, 0.00001922486954f, 0.00001525878906f, 0.00001211090890f, 0.00000961243477f, 0.00000762939453f, 0.00000605545445f,
109 | 0.00000480621738f, 0.00000381469727f, 0.00000302772723f, 0.00000240310869f, 0.00000190734863f, 0.00000151386361f, 0.00000120155435f, 0.00000095367432f
110 | };
111 |
112 | #endregion
113 |
114 | int _channels, _jsbound, _granuleCount;
115 | int[][] _allocLookupTable, _scfsi, _samples;
116 | int[][][] _scalefac;
117 | float[] _polyPhaseBuf;
118 |
119 | int[][] _allocation;
120 |
121 | protected LayerIIDecoderBase(int[][] allocLookupTable, int granuleCount)
122 | : base()
123 | {
124 | _allocLookupTable = allocLookupTable;
125 | _granuleCount = granuleCount;
126 |
127 | _allocation = new int[][] { new int[SBLIMIT], new int[SBLIMIT] };
128 | _scfsi = new int[][] { new int[SBLIMIT], new int[SBLIMIT] };
129 | _samples = new int[][] { new int[SBLIMIT * SSLIMIT * _granuleCount], new int[SBLIMIT * SSLIMIT * _granuleCount] };
130 |
131 | // NB: ReadScaleFactors(...) requires all three granules, even in Layer I
132 | _scalefac = new int[][][] { new int[3][], new int[3][] };
133 | for (int i = 0; i < 3; i++)
134 | {
135 | _scalefac[0][i] = new int[SBLIMIT];
136 | _scalefac[1][i] = new int[SBLIMIT];
137 | }
138 |
139 | _polyPhaseBuf = new float[SBLIMIT];
140 | }
141 |
142 | internal override int DecodeFrame(IMpegFrame frame, float[] ch0, float[] ch1)
143 | {
144 | InitFrame(frame);
145 |
146 | var rateTable = GetRateTable(frame);
147 |
148 | ReadAllocation(frame, rateTable);
149 |
150 | for (int i = 0; i < _scfsi[0].Length; i++)
151 | {
152 | // Since Layer II has to know which subbands have energy, we use the "Layer I valid" selection to mark that energy is present.
153 | // That way Layer I doesn't have to do anything else.
154 | _scfsi[0][i] = _allocation[0][i] != 0 ? 2 : -1;
155 | _scfsi[1][i] = _allocation[1][i] != 0 ? 2 : -1;
156 | }
157 | ReadScaleFactorSelection(frame, _scfsi, _channels);
158 |
159 | ReadScaleFactors(frame);
160 |
161 | ReadSamples(frame);
162 |
163 | return DecodeSamples(ch0, ch1);
164 | }
165 |
166 | // this just reads the channel mode and set a few flags
167 | void InitFrame(IMpegFrame frame)
168 | {
169 | switch (frame.ChannelMode)
170 | {
171 | case MpegChannelMode.Mono:
172 | _channels = 1;
173 | _jsbound = SBLIMIT;
174 | break;
175 | case MpegChannelMode.JointStereo:
176 | _channels = 2;
177 | _jsbound = frame.ChannelModeExtension * 4 + 4;
178 | break;
179 | default:
180 | _channels = 2;
181 | _jsbound = SBLIMIT;
182 | break;
183 | }
184 | }
185 |
186 | abstract protected int[] GetRateTable(IMpegFrame frame);
187 |
188 | void ReadAllocation(IMpegFrame frame, int[] rateTable)
189 | {
190 | var _subBandCount = rateTable.Length;
191 | if (_jsbound > _subBandCount) _jsbound = _subBandCount;
192 |
193 | Array.Clear(_allocation[0], 0, SBLIMIT);
194 | Array.Clear(_allocation[1], 0, SBLIMIT);
195 |
196 | int sb = 0;
197 | for (; sb < _jsbound; sb++)
198 | {
199 | var table = _allocLookupTable[rateTable[sb]];
200 | var bits = table[0];
201 | for (int ch = 0; ch < _channels; ch++)
202 | {
203 | _allocation[ch][sb] = table[frame.ReadBits(bits) + 1];
204 | }
205 | }
206 | for (; sb < _subBandCount; sb++)
207 | {
208 | var table = _allocLookupTable[rateTable[sb]];
209 | _allocation[0][sb] = _allocation[1][sb] = table[frame.ReadBits(table[0]) + 1];
210 | }
211 | }
212 |
213 | abstract protected void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels);
214 |
215 | void ReadScaleFactors(IMpegFrame frame)
216 | {
217 | for (int sb = 0; sb < SBLIMIT; sb++)
218 | {
219 | for (int ch = 0; ch < _channels; ch++)
220 | {
221 | switch (_scfsi[ch][sb])
222 | {
223 | case 0:
224 | // all three
225 | _scalefac[ch][0][sb] = frame.ReadBits(6);
226 | _scalefac[ch][1][sb] = frame.ReadBits(6);
227 | _scalefac[ch][2][sb] = frame.ReadBits(6);
228 | break;
229 | case 1:
230 | // only two (2 = 1)
231 | _scalefac[ch][0][sb] =
232 | _scalefac[ch][1][sb] = frame.ReadBits(6);
233 | _scalefac[ch][2][sb] = frame.ReadBits(6);
234 | break;
235 | case 2:
236 | // only one (3 = 2 = 1)
237 | _scalefac[ch][0][sb] =
238 | _scalefac[ch][1][sb] =
239 | _scalefac[ch][2][sb] = frame.ReadBits(6);
240 | break;
241 | case 3:
242 | // only two (3 = 2)
243 | _scalefac[ch][0][sb] = frame.ReadBits(6);
244 | _scalefac[ch][1][sb] =
245 | _scalefac[ch][2][sb] = frame.ReadBits(6);
246 | break;
247 | default:
248 | // none
249 | _scalefac[ch][0][sb] = 63;
250 | _scalefac[ch][1][sb] = 63;
251 | _scalefac[ch][2][sb] = 63;
252 | break;
253 | }
254 | }
255 | }
256 | }
257 |
258 | void ReadSamples(IMpegFrame frame)
259 | {
260 | // load in all the data for this frame (1152 samples in this case)
261 | // NB: we flatten these into output order
262 | for (int ss = 0, idx = 0; ss < SSLIMIT; ss++, idx += SBLIMIT * (_granuleCount - 1))
263 | {
264 | for (int sb = 0; sb < SBLIMIT; sb++, idx++)
265 | {
266 | for (int ch = 0; ch < _channels; ch++)
267 | {
268 | if (ch == 0 || sb < _jsbound)
269 | {
270 | var alloc = _allocation[ch][sb];
271 | if (alloc != 0)
272 | {
273 | if (alloc < 0)
274 | {
275 | // grouping (Layer II only, so we don't have to play with the granule count)
276 | var val = frame.ReadBits(-alloc);
277 | var levels = (1 << (-alloc / 2 + -alloc % 2 - 1)) + 1;
278 |
279 | _samples[ch][idx] = val % levels;
280 | val /= levels;
281 | _samples[ch][idx + SBLIMIT] = val % levels;
282 | _samples[ch][idx + SBLIMIT * 2] = val / levels;
283 | }
284 | else
285 | {
286 | // non-grouping
287 | for (int gr = 0; gr < _granuleCount; gr++)
288 | {
289 | _samples[ch][idx + SBLIMIT * gr] = frame.ReadBits(alloc);
290 | }
291 | }
292 | }
293 | else
294 | {
295 | // no energy... zero out the samples
296 | for (int gr = 0; gr < _granuleCount; gr++)
297 | {
298 | _samples[ch][idx + SBLIMIT * gr] = 0;
299 | }
300 | }
301 | }
302 | else
303 | {
304 | // copy chan 0 to chan 1
305 | for (int gr = 0; gr < _granuleCount; gr++)
306 | {
307 | _samples[1][idx + SBLIMIT * gr] = _samples[0][idx + SBLIMIT * gr];
308 | }
309 | }
310 | }
311 | }
312 | }
313 | }
314 |
315 | int DecodeSamples(float[] ch0, float[] ch1)
316 | {
317 | // do our stereo mode setup
318 | var chanBufs = new float[2][];
319 | var startChannel = 0;
320 | var endChannel = _channels - 1;
321 | if (_channels == 1 || StereoMode == StereoMode.LeftOnly)
322 | {
323 | chanBufs[0] = ch0;
324 | endChannel = 0;
325 | }
326 | else if (StereoMode == StereoMode.RightOnly)
327 | {
328 | chanBufs[1] = ch0; // this is correct... if there's only a single channel output, it goes in channel 0's buffer
329 | startChannel = 1;
330 | }
331 | else // MpegStereoMode.Both or StereoMode.DownmixToMono
332 | {
333 | chanBufs[0] = ch0;
334 | chanBufs[1] = ch1;
335 | }
336 |
337 | int idx = 0;
338 | for (int ch = startChannel; ch <= endChannel; ch++)
339 | {
340 | idx = 0;
341 | for (int gr = 0; gr < _granuleCount; gr++)
342 | {
343 | for (int ss = 0; ss < SSLIMIT; ss++)
344 | {
345 | for (int sb = 0; sb < SBLIMIT; sb++, idx++)
346 | {
347 | // do the dequant and the denorm; output to _polyPhaseBuf
348 | // NB: Layers I & II use the same algorithm here... Grouping changes the bit counts, but doesn't change the algo
349 | // - Up to 65534 possible values (65535 does not appear to be usable)
350 | // - All values can be handled with 16-bit logic as long as the correct C and D constants are used
351 | // - Make sure to normalize each sample to 16 bits!
352 |
353 | var alloc = _allocation[ch][sb];
354 | if (alloc != 0)
355 | {
356 | float[] c, d;
357 | if (alloc < 0)
358 | {
359 | alloc = -alloc / 2 + -alloc % 2 - 1;
360 | c = _groupedC;
361 | d = _groupedD;
362 | }
363 | else
364 | {
365 | c = _C;
366 | d = _D;
367 | }
368 |
369 | // read sample; normalize, scale & center to [-0.999984741f..0.999984741f]; apply scalefactor
370 | _polyPhaseBuf[sb] = c[alloc] * ((_samples[ch][idx] << (16 - alloc)) / 32768f + d[alloc]) * _denormalMultiplier[_scalefac[ch][gr][sb]];
371 | }
372 | else
373 | {
374 | // no transmitted energy...
375 | _polyPhaseBuf[sb] = 0f;
376 | }
377 | }
378 |
379 | // do the polyphase output for this channel, section, and granule
380 | base.InversePolyPhase(ch, _polyPhaseBuf);
381 | Array.Copy(_polyPhaseBuf, 0, chanBufs[ch], idx - SBLIMIT, SBLIMIT);
382 | }
383 | }
384 | }
385 |
386 | if (_channels == 2 && StereoMode == NLayer.StereoMode.DownmixToMono)
387 | {
388 | for (int i = 0; i < idx; i++)
389 | {
390 | ch0[i] = (ch0[i] + ch1[i]) / 2;
391 | }
392 | }
393 |
394 | return idx;
395 | }
396 | }
397 | }
398 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/LayerIIDecoderBase.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c2712fedc82689349a61da57e56203e5
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/LayerIIIDecoder.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 99407b136754a304a8077e17cf5fe083
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/MpegFrame.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace NLayer.Decoder
6 | {
7 | class MpegFrame : FrameBase, IMpegFrame
8 | {
9 | static readonly int[][][] _bitRateTable =
10 | {
11 | new int[][]
12 | {
13 | new int[] { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 },
14 | new int[] { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 },
15 | new int[] { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }
16 | },
17 | new int[][]
18 | {
19 | new int[] { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 },
20 | new int[] { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 },
21 | new int[] { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }
22 | },
23 | };
24 |
25 | internal static MpegFrame TrySync(uint syncMark)
26 | {
27 | if ((syncMark & 0xFFE00000) == 0xFFE00000 // frame sync
28 | && (syncMark & 0x00180000) != 0x00080000 // MPEG version != reserved
29 | && (syncMark & 0x00060000) != 0x00000000 // layer version != reserved
30 | && (syncMark & 0x0000F000) != 0x0000F000 // bitrate != bad
31 | && (syncMark & 0x00000C00) != 0x00000C00 // sample rate != reserved
32 | )
33 | {
34 | // now check the stereo modes
35 | switch ((syncMark >> 4) & 0xF)
36 | {
37 | case 0x0: // stereo
38 | case 0x4: // joint stereo
39 | case 0x5: // "
40 | case 0x6: // "
41 | case 0x7: // "
42 | case 0x8: // dual channel
43 | case 0xC: // mono
44 | return new MpegFrame { _syncBits = (int)syncMark };
45 | }
46 | }
47 |
48 | return null;
49 | }
50 |
51 | // base: 2xIntPtr, 1x8, 1x4
52 | // total: 3xIntPtr, 4x8, 4x4
53 | // Long Mode: 72 bytes per instance
54 | // Prot Mode: 60 bytes per instance
55 |
56 | // IntPtr
57 | internal MpegFrame Next;
58 | // 4
59 | internal int Number;
60 |
61 | // 4
62 | int _syncBits;
63 |
64 | // 8
65 | int _readOffset, _bitsRead;
66 | // 8
67 | ulong _bitBucket = 0UL;
68 | // 8
69 | long _offset;
70 | // 4
71 | bool _isMuted;
72 |
73 | MpegFrame()
74 | {
75 |
76 | }
77 |
78 | protected override int Validate()
79 | {
80 | // TrySync has already validated version, layer, bitrate, and samplerate
81 |
82 | // if layer2, we have to verify the channel mode is valid for the bitrate selected
83 | if (Layer == MpegLayer.LayerII)
84 | {
85 | switch (BitRate)
86 | {
87 | case 32000:
88 | case 48000:
89 | case 56000:
90 | case 80000:
91 | // don't allow anything except mono
92 | if (ChannelMode != MpegChannelMode.Mono) return -1;
93 | break;
94 | case 224000:
95 | case 256000:
96 | case 320000:
97 | case 384000:
98 | // don't allow mono
99 | if (ChannelMode == MpegChannelMode.Mono) return -1;
100 | break;
101 | }
102 | }
103 |
104 | // calculate the frame's length
105 | int frameSize;
106 | if (BitRateIndex > 0)
107 | {
108 | if (Layer == MpegLayer.LayerI)
109 | {
110 | frameSize = (12 * BitRate / SampleRate + Padding) * 4;
111 | }
112 | else
113 | {
114 | frameSize = 144 * BitRate / SampleRate + Padding;
115 | }
116 | }
117 | else
118 | {
119 | // "free" frame... we have to calculate it later
120 | frameSize = _readOffset + GetSideDataSize() + Padding; // we know the frame will be at least this big...
121 | }
122 |
123 | // now check the crc if one is present
124 | if (HasCrc)
125 | {
126 | // prep for CRC reading
127 | _readOffset = 4 + (HasCrc ? 2 : 0);
128 |
129 | // check the CRC
130 | if (!ValidateCRC())
131 | {
132 | // mute this frame
133 | _isMuted = true;
134 | return 6; // header + crc... force the reader to re-sync
135 | }
136 | }
137 |
138 | // prep for reading
139 | Reset();
140 |
141 | // finally, let our caller know how big this frame is (including the sync header)
142 | return frameSize;
143 | }
144 |
145 | internal int GetSideDataSize()
146 | {
147 | switch (Layer)
148 | {
149 | case MpegLayer.LayerI:
150 | if (ChannelMode == MpegChannelMode.Mono)
151 | {
152 | // mono
153 | return 16;
154 | }
155 | else if (ChannelMode == MpegChannelMode.Stereo || ChannelMode == MpegChannelMode.DualChannel)
156 | {
157 | // full stereo / dual channel
158 | return 32;
159 | }
160 | else
161 | {
162 | // joint stereo... ugh...
163 | switch (ChannelModeExtension)
164 | {
165 | case 0:
166 | return 18;
167 | case 1:
168 | return 20;
169 | case 2:
170 | return 22;
171 | case 3:
172 | return 24;
173 | }
174 | }
175 | break;
176 | case MpegLayer.LayerII:
177 | return 0;
178 | case MpegLayer.LayerIII:
179 | if (ChannelMode == MpegChannelMode.Mono && Version >= MpegVersion.Version2)
180 | {
181 | return 9;
182 | }
183 | else if (ChannelMode != MpegChannelMode.Mono && Version < MpegVersion.Version2)
184 | {
185 | return 32;
186 | }
187 | else
188 | {
189 | return 17;
190 | }
191 | }
192 |
193 | return 0;
194 | }
195 |
196 | bool ValidateCRC()
197 | {
198 | var crc = 0xFFFFU;
199 |
200 | // process the common bits...
201 | UpdateCRC(_syncBits, 16, ref crc);
202 |
203 | var apply = false;
204 | switch (Layer)
205 | {
206 | case MpegLayer.LayerI:
207 | apply = LayerIDecoder.GetCRC(this, ref crc);
208 | break;
209 | case MpegLayer.LayerII:
210 | apply = LayerIIDecoder.GetCRC(this, ref crc);
211 | break;
212 | case MpegLayer.LayerIII:
213 | apply = LayerIIIDecoder.GetCRC(this, ref crc);
214 | break;
215 | }
216 |
217 | if (apply)
218 | {
219 | var checkCrc = ReadByte(4) << 8 | ReadByte(5);
220 | return checkCrc == crc;
221 | }
222 |
223 | return true;
224 | }
225 |
226 | static internal void UpdateCRC(int data, int length, ref uint crc)
227 | {
228 | var masking = 1U << length;
229 | while ((masking >>= 1) != 0)
230 | {
231 | var carry = crc & 0x8000;
232 | crc <<= 1;
233 | if ((carry == 0) ^ ((data & masking) == 0))
234 | {
235 | crc ^= 0x8005;
236 | }
237 | }
238 | crc &= 0xFFFF;
239 | }
240 |
241 | internal VBRInfo ParseVBR()
242 | {
243 | var buf = new byte[4];
244 |
245 | // Xing first
246 | int offset;
247 | if (Version == MpegVersion.Version1 && ChannelMode != MpegChannelMode.Mono)
248 | {
249 | offset = 32 + 4;
250 | }
251 | else if (Version > MpegVersion.Version1 && ChannelMode == MpegChannelMode.Mono)
252 | {
253 | offset = 9 + 4;
254 | }
255 | else
256 | {
257 | offset = 17 + 4;
258 | }
259 |
260 | if (Read(offset, buf) != 4) return null;
261 | if (buf[0] == 'X' && buf[1] == 'i' && buf[2] == 'n' && buf[3] == 'g'
262 | || buf[0] == 'I' && buf[1] == 'n' && buf[2] == 'f' && buf[3] == 'o')
263 | {
264 | return ParseXing(offset + 4);
265 | }
266 |
267 | // then VBRI (kinda rare)
268 | if (Read(36, buf) != 4) return null;
269 | if (buf[0] == 'V' && buf[1] == 'B' && buf[2] == 'R' && buf[3] == 'I')
270 | {
271 | return ParseVBRI();
272 | }
273 |
274 | return null;
275 | }
276 |
277 | VBRInfo ParseXing(int offset)
278 | {
279 | VBRInfo info = new VBRInfo();
280 | info.Channels = Channels;
281 | info.SampleRate = SampleRate;
282 | info.SampleCount = SampleCount;
283 |
284 | var buf = new byte[100];
285 | if (Read(offset, buf, 0, 4) != 4) return null;
286 | offset += 4;
287 |
288 | var flags = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
289 |
290 | // frame count
291 | if ((flags & 0x1) != 0)
292 | {
293 | if (Read(offset, buf, 0, 4) != 4) return null;
294 | offset += 4;
295 | info.VBRFrames = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
296 | }
297 |
298 | // byte count
299 | if ((flags & 0x2) != 0)
300 | {
301 | if (Read(offset, buf, 0, 4) != 4) return null;
302 | offset += 4;
303 | info.VBRBytes = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
304 | }
305 |
306 | // TOC
307 | if ((flags & 0x4) != 0)
308 | {
309 | // we're not using the TOC, so just discard it
310 | if (Read(offset, buf) != 100) return null;
311 | offset += 100;
312 | }
313 |
314 | // scale
315 | if ((flags & 0x8) != 0)
316 | {
317 | if (Read(offset, buf, 0, 4) != 4) return null;
318 | offset += 4;
319 | info.VBRQuality = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
320 | }
321 |
322 | //// now look for a LAME header (note: if it isn't found, it doesn't fail the VBR parse)
323 | //do
324 | //{
325 | // if (Read(offset, buf, 0, 20) != 20) break;
326 | // offset += 20;
327 |
328 | // // LAME tag revision: only 0 and 1 are valid
329 | // if ((buf[9] & 0xF0) > 0x10) break;
330 |
331 | // // VBR mode: 0-6, 8 & 9 are valid
332 | // var mode = buf[9] & 0xF;
333 | // if (mode == 7 || mode > 9) break;
334 |
335 | // // Lowpass filter value
336 | // var lowpass = buf[10] / 100.0;
337 |
338 | // // Replay Gain
339 | // var rgPeak = BitConverter.ToSingle(buf, 11);
340 | // var rgGainRadio = buf[15] << 8 | buf[16];
341 | // var rgGain = buf[17] << 8 | buf[18];
342 | //} while (false);
343 |
344 | return info;
345 | }
346 |
347 | VBRInfo ParseVBRI()
348 | {
349 | VBRInfo info = new VBRInfo();
350 | info.Channels = Channels;
351 | info.SampleRate = SampleRate;
352 | info.SampleCount = SampleCount;
353 |
354 | // VBRI is "fixed" size... Yay. :)
355 | var buf = new byte[26];
356 | if (Read(36, buf) != 26) return null;
357 |
358 | // Version
359 | var version = buf[4] << 8 | buf[5];
360 |
361 | // Delay
362 | info.VBRDelay = buf[6] << 8 | buf[7];
363 |
364 | // Quality
365 | info.VBRQuality = buf[8] << 8 | buf[9];
366 |
367 | // Bytes
368 | info.VBRBytes = buf[10] << 24 | buf[11] << 16 | buf[12] << 8 | buf[13];
369 |
370 | // Frames
371 | info.VBRFrames = buf[14] << 24 | buf[15] << 16 | buf[16] << 8 | buf[17];
372 |
373 | // TOC
374 | // entries
375 | var tocEntries = buf[18] << 8 | buf[19];
376 | var tocScale = buf[20] << 8 | buf[21];
377 | var tocEntrySize = buf[22] << 8 | buf[23];
378 | var tocFramesPerEntry = buf[24] << 8 | buf[25];
379 | var tocSize = tocEntries * tocEntrySize;
380 |
381 | var toc = new byte[tocSize];
382 | if (Read(62, toc) != tocSize) return null;
383 |
384 | return info;
385 | }
386 |
387 | public int FrameLength
388 | {
389 | get { return base.Length; }
390 | }
391 |
392 | public MpegVersion Version
393 | {
394 | get
395 | {
396 | switch ((_syncBits >> 19) & 3)
397 | {
398 | case 0:
399 | return MpegVersion.Version25;
400 | case 2:
401 | return MpegVersion.Version2;
402 | case 3:
403 | return MpegVersion.Version1;
404 | }
405 | return MpegVersion.Unknown;
406 | }
407 | }
408 | public MpegLayer Layer
409 | {
410 | get
411 | {
412 | // the order is backwards, and "0" is invalid
413 | return (MpegLayer)((4 - ((_syncBits >> 17) & 3)) % 4);
414 | }
415 | }
416 | public bool HasCrc
417 | {
418 | get { return (_syncBits & 0x10000) == 0; }
419 | }
420 | public int BitRate
421 | {
422 | get
423 | {
424 | if (BitRateIndex > 0)
425 | {
426 | return _bitRateTable[(int)Version / 10 - 1][(int)Layer - 1][BitRateIndex] * 1000;
427 | }
428 | else
429 | {
430 | // bitrate is always an even multiple of 1000, so round
431 | return ((((FrameLength * 8) * SampleRate) / SampleCount + 499) + 500) / 1000 * 1000;
432 | }
433 | }
434 | }
435 | public int BitRateIndex
436 | {
437 | get { return (_syncBits >> 12) & 0xF; }
438 | }
439 | public int SampleRate
440 | {
441 | get
442 | {
443 | int sr;
444 | switch (SampleRateIndex)
445 | {
446 | case 0: sr = 44100; break;
447 | case 1: sr = 48000; break;
448 | case 2: sr = 32000; break;
449 | default: sr = 0; break;
450 | }
451 | if (Version > MpegVersion.Version1)
452 | {
453 | if (Version == MpegVersion.Version25)
454 | {
455 | sr /= 4;
456 | }
457 | else
458 | {
459 | sr /= 2;
460 | }
461 | }
462 | return sr;
463 | }
464 | }
465 | public int SampleRateIndex
466 | {
467 | get { return (_syncBits >> 10) & 0x3; }
468 | }
469 | private int Padding
470 | {
471 | get { return (_syncBits >> 9) & 0x1; }
472 | }
473 | public MpegChannelMode ChannelMode
474 | {
475 | get { return (MpegChannelMode)((_syncBits >> 6) & 0x3); }
476 | }
477 | public int ChannelModeExtension
478 | {
479 | get { return (_syncBits >> 4) & 0x3; }
480 | }
481 | internal int Channels
482 | {
483 | get { return (ChannelMode == MpegChannelMode.Mono ? 1 : 2); }
484 | }
485 | public bool IsCopyrighted
486 | {
487 | get { return (_syncBits & 0x8) == 0x8; }
488 | }
489 | internal bool IsOriginal
490 | {
491 | get { return (_syncBits & 0x4) == 0x4; }
492 | }
493 | internal int EmphasisMode
494 | {
495 | get { return (_syncBits & 0x3); }
496 | }
497 | public bool IsCorrupted
498 | {
499 | get { return _isMuted; }
500 | }
501 |
502 | public int SampleCount
503 | {
504 | get
505 | {
506 | if (Layer == MpegLayer.LayerI) return 384;
507 | if (Layer == MpegLayer.LayerIII && Version > MpegVersion.Version1) return 576;
508 | return 1152;
509 | }
510 | }
511 | internal long SampleOffset
512 | {
513 | get { return _offset; }
514 | set { _offset = value; }
515 | }
516 |
517 |
518 | public void Reset()
519 | {
520 | _readOffset = 4 + (HasCrc ? 2 : 0);
521 | _bitBucket = 0UL;
522 | _bitsRead = 0;
523 | }
524 |
525 | public int ReadBits(int bitCount)
526 | {
527 | if (bitCount < 1 || bitCount > 32) throw new ArgumentOutOfRangeException("bitCount");
528 | if (_isMuted) return 0;
529 |
530 | while (_bitsRead < bitCount)
531 | {
532 | var b = ReadByte(_readOffset);
533 | if (b == -1) throw new System.IO.EndOfStreamException();
534 |
535 | ++_readOffset;
536 |
537 | _bitBucket <<= 8;
538 | _bitBucket |= (byte)(b & 0xFF);
539 | _bitsRead += 8;
540 | }
541 |
542 | var temp = (int)((_bitBucket >> (_bitsRead - bitCount)) & ((1UL << bitCount) - 1));
543 | _bitsRead -= bitCount;
544 | return temp;
545 | }
546 |
547 | #if DEBUG
548 | public override string ToString()
549 | {
550 | // version
551 | var sb = new StringBuilder("MPEG");
552 | switch (Version)
553 | {
554 | case MpegVersion.Version1: sb.Append("1"); break;
555 | case MpegVersion.Version2: sb.Append("2"); break;
556 | case MpegVersion.Version25: sb.Append("2.5"); break;
557 | }
558 |
559 | // layer
560 | sb.Append(" Layer ");
561 | switch (Layer)
562 | {
563 | case MpegLayer.LayerI: sb.Append("I"); break;
564 | case MpegLayer.LayerII: sb.Append("II"); break;
565 | case MpegLayer.LayerIII: sb.Append("III"); break;
566 | }
567 |
568 | // bitrate
569 | sb.AppendFormat(" {0} kbps ", BitRate / 1000);
570 |
571 | // channel mode
572 | switch (ChannelMode)
573 | {
574 | case MpegChannelMode.Stereo:
575 | sb.Append("Stereo");
576 | break;
577 | case MpegChannelMode.JointStereo:
578 | sb.Append("Joint Stereo");
579 | switch (ChannelModeExtension)
580 | {
581 | case 1: sb.Append(" (I)"); break;
582 | case 2: sb.Append(" (M/S)"); break;
583 | case 3: sb.Append(" (M/S,I)"); break;
584 | }
585 | break;
586 | case MpegChannelMode.DualChannel:
587 | sb.Append("Dual Channel");
588 | break;
589 | case MpegChannelMode.Mono:
590 | sb.Append("Mono");
591 | break;
592 | }
593 |
594 | // sample rate
595 | sb.AppendFormat(" {0} KHz", (float)SampleRate / 1000);
596 |
597 | var flagList = new List();
598 | // protection
599 | if (HasCrc) flagList.Add("CRC");
600 |
601 | // copyright
602 | if (IsCopyrighted) flagList.Add("Copyright");
603 |
604 | // original
605 | if (IsOriginal) flagList.Add("Original");
606 |
607 | // emphasis
608 | switch (EmphasisMode)
609 | {
610 | case 1:
611 | flagList.Add("50/15 ms");
612 | break;
613 | case 2:
614 | flagList.Add("Invalid Emphasis");
615 | break;
616 | case 3:
617 | flagList.Add("CCIT J.17");
618 | break;
619 | }
620 |
621 | if (flagList.Count > 0)
622 | {
623 | sb.AppendFormat(" ({0})", string.Join(",", flagList.ToArray()));
624 | }
625 |
626 | return sb.ToString();
627 | }
628 | #endif
629 | }
630 | }
631 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/MpegFrame.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ad9a27822246ed948990a8d6c4bbc456
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/MpegStreamReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace NLayer.Decoder
5 | {
6 | class MpegStreamReader
7 | {
8 | ID3Frame _id3Frame, _id3v1Frame;
9 | RiffHeaderFrame _riffHeaderFrame;
10 |
11 | VBRInfo _vbrInfo;
12 | MpegFrame _first, _current, _last, _lastFree;
13 |
14 | long _readOffset, _eofOffset;
15 | Stream _source;
16 | bool _canSeek, _endFound, _mixedFrameSize;
17 | object _readLock = new object();
18 | object _frameLock = new object();
19 |
20 | internal MpegStreamReader(Stream source)
21 | {
22 | _source = source;
23 | _canSeek = source.CanSeek;
24 | _readOffset = 0L;
25 | _eofOffset = long.MaxValue;
26 |
27 | // find the first Mpeg frame
28 | var frame = FindNextFrame();
29 | while (frame != null && !(frame is MpegFrame))
30 | {
31 | frame = FindNextFrame();
32 | }
33 |
34 | // if we still don't have a frame, we never sync'ed
35 | if (frame == null) throw new InvalidDataException("Not a valid MPEG file!");
36 |
37 | // the very next frame "should be" an mpeg frame
38 | frame = FindNextFrame();
39 |
40 | // if not, it's not a valid file
41 | if (frame == null || !(frame is MpegFrame)) throw new InvalidDataException("Not a valid MPEG file!");
42 |
43 | // seek to the first frame
44 | _current = _first;
45 | }
46 |
47 | FrameBase FindNextFrame()
48 | {
49 | // if we've found the end, don't bother looking for anything else
50 | if (_endFound) return null;
51 |
52 | var freeFrame = _lastFree;
53 | var lastFrameStart = _readOffset;
54 |
55 | lock (_frameLock)
56 | {
57 | // read 3 bytes
58 | var syncBuf = new byte[4];
59 | try
60 | {
61 | if (Read(_readOffset, syncBuf, 0, 4) == 4)
62 | {
63 | // now loop until a frame is found
64 | do
65 | {
66 | var sync = (uint)(syncBuf[0] << 24 | syncBuf[1] << 16 | syncBuf[2] << 8 | syncBuf[3]);
67 |
68 | lastFrameStart = _readOffset;
69 |
70 | // try ID3 first (for v2 frames)
71 | if (_id3Frame == null)
72 | {
73 | var f = ID3Frame.TrySync(sync);
74 | if (f != null)
75 | {
76 | if (f.Validate(_readOffset, this))
77 | {
78 | if (!_canSeek) f.SaveBuffer();
79 |
80 | _readOffset += f.Length;
81 | DiscardThrough(_readOffset, true);
82 |
83 | return _id3Frame = f;
84 | }
85 | }
86 | }
87 |
88 | // now look for a RIFF header
89 | if (_first == null && _riffHeaderFrame == null)
90 | {
91 | var f = RiffHeaderFrame.TrySync(sync);
92 | if (f != null)
93 | {
94 | if (f.Validate(_readOffset, this))
95 | {
96 | _readOffset += f.Length;
97 | DiscardThrough(_readOffset, true);
98 |
99 | return _riffHeaderFrame = f;
100 | }
101 | }
102 | }
103 |
104 | // finally, just try for an MPEG frame
105 | var frame = MpegFrame.TrySync(sync);
106 | if (frame != null)
107 | {
108 | if (frame.Validate(_readOffset, this)
109 | && !(freeFrame != null
110 | && (frame.Layer != freeFrame.Layer
111 | || frame.Version != freeFrame.Version
112 | || frame.SampleRate != freeFrame.SampleRate
113 | || frame.BitRateIndex > 0
114 | )
115 | )
116 | )
117 | {
118 | if (!_canSeek)
119 | {
120 | frame.SaveBuffer();
121 | DiscardThrough(_readOffset + frame.FrameLength, true);
122 | }
123 |
124 | _readOffset += frame.FrameLength;
125 |
126 | if (_first == null)
127 | {
128 | if (_vbrInfo == null && (_vbrInfo = frame.ParseVBR()) != null)
129 | {
130 | return FindNextFrame();
131 | }
132 | else
133 | {
134 | frame.Number = 0;
135 | _first = _last = frame;
136 | }
137 | }
138 | else
139 | {
140 | if (frame.SampleCount != _first.SampleCount)
141 | {
142 | _mixedFrameSize = true;
143 | }
144 |
145 | frame.SampleOffset = _last.SampleCount + _last.SampleOffset;
146 | frame.Number = _last.Number + 1;
147 | _last = (_last.Next = frame);
148 | }
149 |
150 | if (frame.BitRateIndex == 0)
151 | {
152 | _lastFree = frame;
153 | }
154 |
155 | return frame;
156 | }
157 | }
158 |
159 | // if we've read MPEG frames and can't figure out what frame type we have, try looking for a new ID3 tag
160 | if (_last != null)
161 | {
162 | var f = ID3Frame.TrySync(sync);
163 | if (f != null)
164 | {
165 | if (f.Validate(_readOffset, this))
166 | {
167 | if (!_canSeek) f.SaveBuffer();
168 |
169 | // if it's a v1 tag, go ahead and parse it
170 | if (f.Version == 1)
171 | {
172 | _id3v1Frame = f;
173 | }
174 | else
175 | {
176 | // grrr... the ID3 2.4 spec says tags can be anywhere in the file and that later tags can override earlier ones... boo
177 | _id3Frame.Merge(f);
178 | }
179 |
180 | _readOffset += f.Length;
181 | DiscardThrough(_readOffset, true);
182 |
183 | return f;
184 | }
185 | }
186 | }
187 |
188 | // well, we didn't find anything, so rinse and repeat with the next byte
189 | ++_readOffset;
190 | if (_first == null || !_canSeek) DiscardThrough(_readOffset, true);
191 | Buffer.BlockCopy(syncBuf, 1, syncBuf, 0, 3);
192 | } while (Read(_readOffset + 3, syncBuf, 3, 1) == 1);
193 | }
194 |
195 | // move the "end of frame" marker for the last free format frame (in case we have one)
196 | // this is because we don't include the last four bytes otherwise
197 | lastFrameStart += 4;
198 |
199 | _endFound = true;
200 | return null;
201 | }
202 | finally
203 | {
204 | if (freeFrame != null)
205 | {
206 | freeFrame.Length = (int)(lastFrameStart - freeFrame.Offset);
207 |
208 | if (!_canSeek)
209 | {
210 | // gotta finish filling the buffer!!
211 | throw new InvalidOperationException("Free frames cannot be read properly from forward-only streams!");
212 | }
213 |
214 | // if _lastFree hasn't changed (we got a non-MPEG frame), clear it out
215 | if (_lastFree == freeFrame)
216 | {
217 | _lastFree = null;
218 | }
219 | }
220 | }
221 | }
222 | }
223 |
224 | class ReadBuffer
225 | {
226 | public byte[] Data;
227 | public long BaseOffset;
228 | public int End;
229 | public int DiscardCount;
230 |
231 | object _localLock = new object();
232 |
233 | public ReadBuffer(int initialSize)
234 | {
235 | initialSize = 2 << (int)Math.Log(initialSize, 2);
236 |
237 | Data = new byte[initialSize];
238 | }
239 |
240 | public int Read(MpegStreamReader reader, long offset, byte[] buffer, int index, int count)
241 | {
242 | lock (_localLock)
243 | {
244 | var startIdx = EnsureFilled(reader, offset, ref count);
245 |
246 | Buffer.BlockCopy(Data, startIdx, buffer, index, count);
247 | }
248 | return count;
249 | }
250 |
251 | public int ReadByte(MpegStreamReader reader, long offset)
252 | {
253 | lock (_localLock)
254 | {
255 | var count = 1;
256 | var startIdx = EnsureFilled(reader, offset, ref count);
257 | if (count == 1)
258 | {
259 | return Data[startIdx];
260 | }
261 | }
262 | return -1;
263 | }
264 |
265 | int EnsureFilled(MpegStreamReader reader, long offset, ref int count)
266 | {
267 | // if the offset & count are inside our buffer's range, just return the appropriate index
268 | var startIdx = (int)(offset - BaseOffset);
269 | int endIdx = startIdx + count;
270 | if (startIdx < 0 || endIdx > End)
271 | {
272 | int readStart = 0, readCount = 0, moveCount = 0;
273 | long readOffset = 0;
274 |
275 | #region Decision-Making
276 |
277 | if (startIdx < 0)
278 | {
279 | // if we can't seek, there's nothing we can do
280 | if (!reader._source.CanSeek) throw new InvalidOperationException("Cannot seek backwards on a forward-only stream!");
281 |
282 | // if there's data in the buffer, try to keep it (up to doubling the buffer size)
283 | if (End > 0)
284 | {
285 | // if doubling the buffer would push it past the max size, don't check it
286 | if ((startIdx + Data.Length > 0) || (Data.Length * 2 <= 16384 && startIdx + Data.Length * 2 > 0))
287 | {
288 | endIdx = End;
289 | }
290 | }
291 |
292 | // we know we'll have to start reading here
293 | readOffset = offset;
294 |
295 | // if the end of the request is before the start of our buffer...
296 | if (endIdx < 0)
297 | {
298 | // ... just truncate and move on
299 | Truncate();
300 |
301 | // set up our read parameters
302 | BaseOffset = offset;
303 | startIdx = 0;
304 | endIdx = count;
305 |
306 | // how much do we need to read?
307 | readCount = count;
308 | }
309 | else // i.e., endIdx >= 0
310 | {
311 | // we have overlap with existing data... save as much as possible
312 | moveCount = -endIdx;
313 | readCount = -startIdx;
314 | }
315 | }
316 | else // i.e., startIdx >= 0
317 | {
318 | // we only get to here if at least one byte of the request is past the end of the read data
319 | // start with the simplest scenario and work our way up
320 |
321 | // 1) We just need to fill the buffer a bit more
322 | if (endIdx < Data.Length)
323 | {
324 | readCount = endIdx - End;
325 | readStart = End;
326 | readOffset = BaseOffset + readStart;
327 | }
328 | // 2) We need to discard some bytes, then fill the buffer
329 | else if (endIdx - DiscardCount < Data.Length)
330 | {
331 | moveCount = DiscardCount;
332 | readStart = End;
333 | readCount = endIdx - readStart;
334 | readOffset = BaseOffset + readStart;
335 | }
336 | // 3) We need to expand the buffer to hold all the existing & requested data
337 | else if (Data.Length * 2 <= 16384)
338 | {
339 | // by definition, we discard
340 | moveCount = DiscardCount;
341 | readStart = End;
342 | readCount = endIdx - End;
343 | readOffset = BaseOffset + readStart;
344 | }
345 | // 4) We have to throw away some data that hasn't been discarded
346 | else
347 | {
348 | // just truncate
349 | Truncate();
350 |
351 | // set up our read parameters
352 | BaseOffset = offset;
353 | readOffset = offset;
354 | startIdx = 0;
355 | endIdx = count;
356 |
357 | // how much do we have to read?
358 | readCount = count;
359 | }
360 | }
361 |
362 | #endregion
363 |
364 | #region Buffer Resizing & Data Moving
365 |
366 | if (endIdx - moveCount > Data.Length || readStart + readCount - moveCount > Data.Length)
367 | {
368 | var newSize = Data.Length * 2;
369 | while (newSize < endIdx - moveCount)
370 | {
371 | newSize *= 2;
372 | }
373 |
374 | var newBuf = new byte[newSize];
375 | if (moveCount < 0)
376 | {
377 | // reverse copy
378 | Buffer.BlockCopy(Data, 0, newBuf, -moveCount, End + moveCount);
379 |
380 | DiscardCount = 0;
381 | }
382 | else
383 | {
384 | // forward or neutral copy
385 | Buffer.BlockCopy(Data, moveCount, newBuf, 0, End - moveCount);
386 |
387 | DiscardCount -= moveCount;
388 | }
389 | Data = newBuf;
390 | }
391 | else if (moveCount != 0)
392 | {
393 | if (moveCount > 0)
394 | {
395 | // forward move
396 | Buffer.BlockCopy(Data, moveCount, Data, 0, End - moveCount);
397 |
398 | DiscardCount -= moveCount;
399 | }
400 | else
401 | {
402 | // backward move
403 | for (int i = 0, srcIdx = Data.Length - 1, destIdx = Data.Length - 1 - moveCount; i < moveCount; i++, srcIdx--, destIdx--)
404 | {
405 | Data[destIdx] = Data[srcIdx];
406 | }
407 |
408 | DiscardCount = 0;
409 | }
410 | }
411 |
412 | BaseOffset += moveCount;
413 | readStart -= moveCount;
414 | startIdx -= moveCount;
415 | endIdx -= moveCount;
416 | End -= moveCount;
417 |
418 | #endregion
419 |
420 | #region Buffer Filling
421 |
422 | lock (reader._readLock)
423 | {
424 | if (readCount > 0 && reader._source.Position != readOffset && readOffset < reader._eofOffset)
425 | {
426 | if (reader._canSeek)
427 | {
428 | try
429 | {
430 | reader._source.Position = readOffset;
431 | }
432 | catch (EndOfStreamException)
433 | {
434 | reader._eofOffset = reader._source.Length;
435 | readCount = 0;
436 | }
437 | }
438 | else
439 | {
440 | // ugh, gotta read bytes until we've reached the desired offset
441 | var seekCount = readOffset - reader._source.Position;
442 | while (--seekCount >= 0)
443 | {
444 | if (reader._source.ReadByte() == -1)
445 | {
446 | reader._eofOffset = reader._source.Position;
447 | readCount = 0;
448 | break;
449 | }
450 | }
451 | }
452 | }
453 |
454 | while (readCount > 0 && readOffset < reader._eofOffset)
455 | {
456 | var temp = reader._source.Read(Data, readStart, readCount);
457 | if (temp == 0)
458 | {
459 | break;
460 | }
461 | readStart += temp;
462 | readOffset += temp;
463 | readCount -= temp;
464 | }
465 |
466 | if (readStart > End)
467 | {
468 | End = readStart;
469 | }
470 |
471 | if (End < endIdx)
472 | {
473 | // we didn't get a full read...
474 | count = Math.Max(0, End - startIdx);
475 | }
476 | // NB: if desired, switch to "minimal reads" by commenting-out this clause
477 | else if (End < Data.Length)
478 | {
479 | // try to finish filling the buffer
480 | var temp = reader._source.Read(Data, End, Data.Length - End);
481 | End += temp;
482 | }
483 | }
484 |
485 | #endregion
486 | }
487 |
488 | return startIdx;
489 | }
490 |
491 | public void DiscardThrough(long offset)
492 | {
493 | lock (_localLock)
494 | {
495 | var count = (int)(offset - BaseOffset);
496 | DiscardCount = Math.Max(count, DiscardCount);
497 |
498 | if (DiscardCount >= Data.Length) CommitDiscard();
499 | }
500 | }
501 |
502 | void Truncate()
503 | {
504 | End = 0;
505 | DiscardCount = 0;
506 | }
507 |
508 | void CommitDiscard()
509 | {
510 | if (DiscardCount >= Data.Length || DiscardCount >= End)
511 | {
512 | // we have been told to discard the entire buffer
513 | BaseOffset += DiscardCount;
514 | End = 0;
515 | }
516 | else
517 | {
518 | // just discard the first part...
519 | //Array.Copy(_readBuf, _readBufDiscardCount, _readBuf, 0, _readBufEnd - _readBufDiscardCount);
520 | Buffer.BlockCopy(Data, DiscardCount, Data, 0, End - DiscardCount);
521 | BaseOffset += DiscardCount;
522 | End -= DiscardCount;
523 | }
524 | DiscardCount = 0;
525 | }
526 | }
527 |
528 | ReadBuffer _readBuf = new ReadBuffer(2048);
529 |
530 | internal int Read(long offset, byte[] buffer, int index, int count)
531 | {
532 | // make sure the offset is at least positive
533 | if (offset < 0L) throw new ArgumentOutOfRangeException("offset");
534 |
535 | // make sure the buffer is valid
536 | if (index < 0 || index + count > buffer.Length) throw new ArgumentOutOfRangeException("index");
537 |
538 | return _readBuf.Read(this, offset, buffer, index, count);
539 | }
540 |
541 | internal int ReadByte(long offset)
542 | {
543 | if (offset < 0L) throw new ArgumentOutOfRangeException("offset");
544 |
545 | return _readBuf.ReadByte(this, offset);
546 | }
547 |
548 | internal void DiscardThrough(long offset, bool minimalRead)
549 | {
550 | _readBuf.DiscardThrough(offset);
551 | }
552 |
553 |
554 | internal void ReadToEnd()
555 | {
556 | try
557 | {
558 | var maxAllocation = 40000;
559 | if (_id3Frame != null)
560 | {
561 | maxAllocation += _id3Frame.Length;
562 | }
563 |
564 | while (!_endFound)
565 | {
566 | FindNextFrame();
567 |
568 | while (!_canSeek && FrameBase.TotalAllocation >= maxAllocation)
569 | {
570 | #if NET35
571 | System.Threading.Thread.Sleep(500);
572 | #else
573 | System.Threading.Tasks.Task.Delay(500).Wait(); //
574 | #endif
575 | }
576 | }
577 | }
578 | catch (ObjectDisposedException)
579 | {
580 | // in case the stream was disposed before we finished...
581 | }
582 | }
583 |
584 |
585 | internal bool CanSeek
586 | {
587 | get { return _canSeek; }
588 | }
589 |
590 | internal long SampleCount
591 | {
592 | get
593 | {
594 | if (_vbrInfo != null) return _vbrInfo.VBRStreamSampleCount;
595 |
596 | if (!_canSeek) return -1;
597 |
598 | ReadToEnd();
599 | return _last.SampleCount + _last.SampleOffset;
600 | }
601 | }
602 |
603 | internal int SampleRate
604 | {
605 | get
606 | {
607 | if (_vbrInfo != null) return _vbrInfo.SampleRate;
608 | return _first.SampleRate;
609 | }
610 | }
611 |
612 | internal int Channels
613 | {
614 | get
615 | {
616 | if (_vbrInfo != null) return _vbrInfo.Channels;
617 | return _first.Channels;
618 | }
619 | }
620 |
621 | internal int FirstFrameSampleCount
622 | {
623 | get { return (_first != null ? _first.SampleCount : 0); }
624 | }
625 |
626 |
627 | internal long SeekTo(long sampleNumber)
628 | {
629 | if (!_canSeek) throw new InvalidOperationException("Cannot seek!");
630 |
631 | // first try to "seek" by calculating the frame number
632 | var cnt = (int)(sampleNumber / _first.SampleCount);
633 | var frame = _first;
634 | if (_current != null && _current.Number <= cnt && _current.SampleOffset <= sampleNumber)
635 | {
636 | // if this fires, we can short-circuit things a bit...
637 | frame = _current;
638 | cnt -= frame.Number;
639 | }
640 | while (!_mixedFrameSize && --cnt >= 0 && frame != null)
641 | {
642 | // make sure we have more frames to look at
643 | if (frame == _last && !_endFound)
644 | {
645 | do
646 | {
647 | FindNextFrame();
648 | } while (frame == _last && !_endFound);
649 | }
650 |
651 | // if we've found a different frame size, fall through...
652 | if (_mixedFrameSize)
653 | {
654 | break;
655 | }
656 |
657 | frame = frame.Next;
658 | }
659 |
660 | // this should not run unless we found mixed frames...
661 | while (frame != null && frame.SampleOffset + frame.SampleCount < sampleNumber)
662 | {
663 | if (frame == _last && !_endFound)
664 | {
665 | do
666 | {
667 | FindNextFrame();
668 | } while (frame == _last && !_endFound);
669 | }
670 |
671 | frame = frame.Next;
672 | }
673 | if (frame == null) return -1;
674 | return (_current = frame).SampleOffset;
675 | }
676 |
677 | internal MpegFrame NextFrame()
678 | {
679 | // if _current is null, we've returned the last frame already
680 | var frame = _current;
681 | if (frame != null)
682 | {
683 | if (_canSeek)
684 | {
685 | frame.SaveBuffer();
686 | DiscardThrough(frame.Offset + frame.FrameLength, false);
687 | }
688 |
689 | if (frame == _last && !_endFound)
690 | {
691 | do
692 | {
693 | FindNextFrame();
694 | } while (frame == _last && !_endFound);
695 | }
696 |
697 | _current = frame.Next;
698 |
699 | if (!_canSeek)
700 | {
701 | // if we're in a forward-only stream, don't bother keeping the frames that have already been processed
702 | lock (_frameLock)
703 | {
704 | var temp = _first;
705 | _first = temp.Next;
706 | temp.Next = null;
707 | }
708 | }
709 | }
710 | return frame;
711 | }
712 |
713 | internal MpegFrame GetCurrentFrame()
714 | {
715 | return _current;
716 | }
717 | }
718 | }
719 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/MpegStreamReader.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 67d22a767a6116146987148f2fd497a7
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/RiffHeaderFrame.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace NLayer.Decoder
7 | {
8 | ///
9 | /// RIFF header reader
10 | ///
11 | class RiffHeaderFrame : FrameBase
12 | {
13 | internal static RiffHeaderFrame TrySync(uint syncMark)
14 | {
15 | if (syncMark == 0x52494646U)
16 | {
17 | return new RiffHeaderFrame();
18 | }
19 |
20 | return null;
21 | }
22 |
23 | RiffHeaderFrame()
24 | {
25 |
26 | }
27 |
28 | protected override int Validate()
29 | {
30 | var buf = new byte[4];
31 |
32 | // we expect this to be the "WAVE" chunk
33 | if (Read(8, buf) != 4) return -1;
34 | if (buf[0] != 'W' || buf[1] != 'A' || buf[2] != 'V' || buf[3] != 'E') return -1;
35 |
36 | // now the "fmt " chunk
37 | if (Read(12, buf) != 4) return -1;
38 | if (buf[0] != 'f' || buf[1] != 'm' || buf[2] != 't' || buf[3] != ' ') return -1;
39 |
40 | // we've found the fmt chunk, so look for the data chunk
41 | var offset = 16;
42 | while (true)
43 | {
44 | // read the length and seek forward
45 | if (Read(offset, buf) != 4) return -1;
46 | offset += 4 + BitConverter.ToInt32(buf, 0);
47 |
48 | // get the chunk ID
49 | if (Read(offset, buf) != 4) return -1;
50 | offset += 4;
51 |
52 | // if it's not the data chunk, try again
53 | if (buf[0] == 'd' && buf[1] == 'a' && buf[2] == 't' && buf[3] == 'a') break;
54 | }
55 |
56 | // ... and now we know exactly where the frame ends
57 | return offset + 4;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/RiffHeaderFrame.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9ef179b275bbcac4ea84513f0a807569
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/VBRInfo.cs:
--------------------------------------------------------------------------------
1 | namespace NLayer.Decoder
2 | {
3 | class VBRInfo
4 | {
5 | internal VBRInfo() { }
6 |
7 | internal int SampleCount { get; set; }
8 | internal int SampleRate { get; set; }
9 | internal int Channels { get; set; }
10 | internal int VBRFrames { get; set; }
11 | internal int VBRBytes { get; set; }
12 | internal int VBRQuality { get; set; }
13 | internal int VBRDelay { get; set; }
14 |
15 | internal long VBRStreamSampleCount
16 | {
17 | get
18 | {
19 | // we assume the entire stream is consistent wrt samples per frame
20 | return VBRFrames * SampleCount;
21 | }
22 | }
23 |
24 | internal int VBRAverageBitrate
25 | {
26 | get
27 | {
28 | return (int)((VBRBytes / (VBRStreamSampleCount / (double)SampleRate)) * 8);
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Assets/NLayer/Decoder/VBRInfo.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: fe1615e88db66374cbca2f9a65fd3db8
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/Enums.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace NLayer
7 | {
8 | public enum MpegVersion
9 | {
10 | Unknown = 0,
11 | Version1 = 10,
12 | Version2 = 20,
13 | Version25 = 25,
14 | }
15 |
16 | public enum MpegLayer
17 | {
18 | Unknown = 0,
19 | LayerI = 1,
20 | LayerII = 2,
21 | LayerIII = 3,
22 | }
23 |
24 | public enum MpegChannelMode
25 | {
26 | Stereo,
27 | JointStereo,
28 | DualChannel,
29 | Mono,
30 | }
31 |
32 | public enum StereoMode
33 | {
34 | Both,
35 | LeftOnly,
36 | RightOnly,
37 | DownmixToMono,
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Assets/NLayer/Enums.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0e7ccac2ed335b342ab3bf9c22dc5b86
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/IMpegFrame.cs:
--------------------------------------------------------------------------------
1 | namespace NLayer
2 | {
3 | ///
4 | /// Defines a standard way of representing a MPEG frame to the decoder
5 | ///
6 | public interface IMpegFrame
7 | {
8 | ///
9 | /// Sample rate of this frame
10 | ///
11 | int SampleRate { get; }
12 |
13 | ///
14 | /// The samplerate index (directly from the header)
15 | ///
16 | int SampleRateIndex { get; }
17 |
18 | ///
19 | /// Frame length in bytes
20 | ///
21 | int FrameLength { get; }
22 |
23 | ///
24 | /// Bit Rate
25 | ///
26 | int BitRate { get; }
27 |
28 | ///
29 | /// MPEG Version
30 | ///
31 | MpegVersion Version { get; }
32 |
33 | ///
34 | /// MPEG Layer
35 | ///
36 | MpegLayer Layer { get; }
37 |
38 | ///
39 | /// Channel Mode
40 | ///
41 | MpegChannelMode ChannelMode { get; }
42 |
43 | ///
44 | /// The number of samples in this frame
45 | ///
46 | int ChannelModeExtension { get; }
47 |
48 | ///
49 | /// The channel extension bits
50 | ///
51 | int SampleCount { get; }
52 |
53 | ///
54 | /// The bitrate index (directly from the header)
55 | ///
56 | int BitRateIndex { get; }
57 |
58 | ///
59 | /// Whether the Copyright bit is set
60 | ///
61 | bool IsCopyrighted { get; }
62 |
63 | ///
64 | /// Whether a CRC is present
65 | ///
66 | bool HasCrc { get; }
67 |
68 | ///
69 | /// Whether the CRC check failed (use error concealment strategy)
70 | ///
71 | bool IsCorrupted { get; }
72 |
73 | ///
74 | /// Resets the bit reader so frames can be reused
75 | ///
76 | void Reset();
77 |
78 | ///
79 | /// Provides sequential access to the bitstream in the frame (after the header and optional CRC)
80 | ///
81 | /// The number of bits to read
82 | /// -1 if the end of the frame has been encountered, otherwise the bits requested
83 | int ReadBits(int bitCount);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Assets/NLayer/IMpegFrame.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c4ffa58092fc20d44971f3225962eb55
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Mark Heath, Andrew Ward & Contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Assets/NLayer/MpegFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NLayer
4 | {
5 | public class MpegFile : IDisposable
6 | {
7 | System.IO.Stream _stream;
8 | bool _closeStream, _eofFound;
9 |
10 | Decoder.MpegStreamReader _reader;
11 | MpegFrameDecoder _decoder;
12 |
13 | object _seekLock = new object();
14 | long _position;
15 |
16 | ///
17 | /// Construct Mpeg file representation from filename.
18 | ///
19 | /// The file which contains Mpeg data.
20 | public MpegFile(string fileName)
21 | {
22 | Init(System.IO.File.Open(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read), true);
23 | }
24 |
25 | ///
26 | /// Construct Mpeg file representation from stream.
27 | ///
28 | /// The input stream which contains Mpeg data.
29 | public MpegFile(System.IO.Stream stream)
30 | {
31 | Init(stream, false);
32 | }
33 |
34 | void Init(System.IO.Stream stream, bool closeStream)
35 | {
36 | _stream = stream;
37 | _closeStream = closeStream;
38 |
39 | _reader = new Decoder.MpegStreamReader(_stream);
40 |
41 | _decoder = new MpegFrameDecoder();
42 | }
43 |
44 | ///
45 | /// Implements IDisposable.Dispose.
46 | ///
47 | public void Dispose()
48 | {
49 | if (_closeStream)
50 | {
51 | _stream.Dispose();
52 | _closeStream = false;
53 | }
54 | }
55 | ///
56 | /// Sample rate of source Mpeg, in Hertz.
57 | ///
58 | public int SampleRate { get { return _reader.SampleRate; } }
59 |
60 | ///
61 | /// Channel count of source Mpeg.
62 | ///
63 | public int Channels { get { return _reader.Channels; } }
64 |
65 | ///
66 | /// Whether the Mpeg stream supports seek operation.
67 | ///
68 | public bool CanSeek { get { return _reader.CanSeek; } }
69 |
70 | ///
71 | /// Data length of decoded data, in PCM.
72 | ///
73 | public long Length { get { return _reader.SampleCount * _reader.Channels * sizeof(float); } }
74 |
75 | ///
76 | /// Media duration of the Mpeg file.
77 | ///
78 | public TimeSpan Duration
79 | {
80 | get
81 | {
82 | var len = _reader.SampleCount;
83 | if (len == -1) return TimeSpan.Zero;
84 | return TimeSpan.FromSeconds((double)len / _reader.SampleRate);
85 | }
86 | }
87 |
88 | ///
89 | /// Current decode position, in number of sample. Calling the setter will result in a seeking operation.
90 | ///
91 | public long Position
92 | {
93 | get { return _position; }
94 | set
95 | {
96 | if (!_reader.CanSeek) throw new InvalidOperationException("Cannot Seek!");
97 | if (value < 0L) throw new ArgumentOutOfRangeException("value");
98 |
99 | // we're thinking in 4-byte samples, pcmStep interleaved... adjust accordingly
100 | var samples = value / sizeof(float) / _reader.Channels;
101 | var sampleOffset = 0;
102 |
103 | // seek to the frame preceding the one we want (unless we're seeking to the first frame)
104 | if (samples >= _reader.FirstFrameSampleCount)
105 | {
106 | sampleOffset = _reader.FirstFrameSampleCount;
107 | samples -= sampleOffset;
108 | }
109 |
110 | lock (_seekLock)
111 | {
112 | // seek the stream
113 | var newPos = _reader.SeekTo(samples);
114 | if (newPos == -1) throw new ArgumentOutOfRangeException("value");
115 |
116 | _decoder.Reset();
117 |
118 | // if we have a sample offset, decode the next frame
119 | if (sampleOffset != 0)
120 | {
121 | _decoder.DecodeFrame(_reader.NextFrame(), _readBuf, 0); // throw away a frame (but allow the decoder to resync)
122 | newPos += sampleOffset;
123 | }
124 |
125 | _position = newPos * sizeof(float) * _reader.Channels;
126 | _eofFound = false;
127 |
128 | // clear the decoder & buffer
129 | _readBufOfs = _readBufLen = 0;
130 | }
131 | }
132 | }
133 |
134 | ///
135 | /// Current decode position, represented by time. Calling the setter will result in a seeking operation.
136 | ///
137 | public TimeSpan Time
138 | {
139 | get { return TimeSpan.FromSeconds((double)_position / sizeof(float) / _reader.Channels / _reader.SampleRate); }
140 | set { Position = (long)(value.TotalSeconds * _reader.SampleRate * _reader.Channels * sizeof(float)); }
141 | }
142 |
143 | ///
144 | /// Set the equalizer.
145 | ///
146 | /// The equalizer, represented by an array of 32 adjustments in dB.
147 | public void SetEQ(float[] eq)
148 | {
149 | _decoder.SetEQ(eq);
150 | }
151 |
152 | ///
153 | /// Stereo mode used in decoding.
154 | ///
155 | public StereoMode StereoMode
156 | {
157 | get { return _decoder.StereoMode; }
158 | set { _decoder.StereoMode = value; }
159 | }
160 |
161 | ///
162 | /// Read specified samples into provided buffer. Do exactly the same as
163 | /// except that the data is written in type of byte, while still representing single-precision float (in local endian).
164 | ///
165 | /// Buffer to write. Floating point data will be actually written into this byte array.
166 | /// Writing offset on the destination buffer.
167 | /// Length of samples to be read, in bytes.
168 | /// Sample size actually reads, in bytes.
169 | public int ReadSamples(byte[] buffer, int index, int count)
170 | {
171 | if (index < 0 || index + count > buffer.Length) throw new ArgumentOutOfRangeException("index");
172 |
173 | // make sure we're asking for an even number of samples
174 | count -= (count % sizeof(float));
175 |
176 | return ReadSamplesImpl(buffer, index, count);
177 | }
178 |
179 | ///
180 | /// Read specified samples into provided buffer, as PCM format.
181 | /// Result varies with diffirent :
182 | ///
183 | /// -
184 | /// For , sample data on both two channels will occur in turn (left first).
185 | ///
186 | /// -
187 | /// For and , only data on
188 | /// specified channel will occur.
189 | ///
190 | /// -
191 | /// For , two channels will be down-mixed into single channel.
192 | ///
193 | ///
194 | ///
195 | /// Buffer to write.
196 | /// Writing offset on the destination buffer.
197 | /// Count of samples to be read.
198 | /// Sample count actually reads.
199 | public int ReadSamples(float[] buffer, int index, int count)
200 | {
201 | if (index < 0 || index + count > buffer.Length) throw new ArgumentOutOfRangeException("index");
202 |
203 | // ReadSampleImpl "thinks" in bytes, so adjust accordingly
204 | return ReadSamplesImpl(buffer, index * sizeof(float), count * sizeof(float)) / sizeof(float);
205 | }
206 |
207 | float[] _readBuf = new float[1152 * 2];
208 | int _readBufLen, _readBufOfs;
209 |
210 | int ReadSamplesImpl(Array buffer, int index, int count)
211 | {
212 | var cnt = 0;
213 |
214 | // lock around the entire read operation so seeking doesn't bork our buffers as we decode
215 | lock (_seekLock)
216 | {
217 | while (count > 0)
218 | {
219 | if (_readBufLen > _readBufOfs)
220 | {
221 | // we have bytes in the buffer, so copy them first
222 | var temp = _readBufLen - _readBufOfs;
223 | if (temp > count) temp = count;
224 | Buffer.BlockCopy(_readBuf, _readBufOfs, buffer, index, temp);
225 |
226 | // now update our counters...
227 | cnt += temp;
228 |
229 | count -= temp;
230 | index += temp;
231 |
232 | _position += temp;
233 | _readBufOfs += temp;
234 |
235 | // finally, mark the buffer as empty if we've read everything in it
236 | if (_readBufOfs == _readBufLen)
237 | {
238 | _readBufLen = 0;
239 | }
240 | }
241 |
242 | // if the buffer is empty, try to fill it
243 | // NB: If we've already satisfied the read request, we'll still try to fill the buffer.
244 | // This ensures there's data in the pipe on the next call
245 | if (_readBufLen == 0)
246 | {
247 | if (_eofFound)
248 | {
249 | break;
250 | }
251 |
252 | // decode the next frame (update _readBufXXX)
253 | var frame = _reader.NextFrame();
254 | if (frame == null)
255 | {
256 | _eofFound = true;
257 | break;
258 | }
259 |
260 | try
261 | {
262 | _readBufLen = _decoder.DecodeFrame(frame, _readBuf, 0) * sizeof(float);
263 | _readBufOfs = 0;
264 | }
265 | catch (System.IO.InvalidDataException)
266 | {
267 | // bad frame... try again...
268 | _decoder.Reset();
269 | _readBufOfs = _readBufLen = 0;
270 | continue;
271 | }
272 | catch (System.IO.EndOfStreamException)
273 | {
274 | // no more frames
275 | _eofFound = true;
276 | break;
277 | }
278 | finally
279 | {
280 | frame.ClearBuffer();
281 | }
282 | }
283 | }
284 | }
285 | return cnt;
286 | }
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/Assets/NLayer/MpegFile.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5a7824182eed1d0449687336eb5f61ec
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/MpegFrameDecoder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NLayer
4 | {
5 | public class MpegFrameDecoder
6 | {
7 | Decoder.LayerIDecoder _layerIDecoder;
8 | Decoder.LayerIIDecoder _layerIIDecoder;
9 | Decoder.LayerIIIDecoder _layerIIIDecoder;
10 |
11 | float[] _eqFactors;
12 |
13 | // channel buffers for getting data out of the decoders...
14 | // we do it this way so the stereo interleaving code is in one place: DecodeFrameImpl(...)
15 | // if we ever add support for multi-channel, we'll have to add a pass after the initial
16 | // stereo decode (since multi-channel basically uses the stereo channels as a reference)
17 | float[] _ch0, _ch1;
18 |
19 | public MpegFrameDecoder()
20 | {
21 | _ch0 = new float[1152];
22 | _ch1 = new float[1152];
23 | }
24 |
25 | ///
26 | /// Set the equalizer.
27 | ///
28 | /// The equalizer, represented by an array of 32 adjustments in dB.
29 | public void SetEQ(float[] eq)
30 | {
31 | if (eq != null)
32 | {
33 | var factors = new float[32];
34 | for (int i = 0; i < eq.Length; i++)
35 | {
36 | // convert from dB -> scaling
37 | factors[i] = (float)Math.Pow(2, eq[i] / 6);
38 | }
39 | _eqFactors = factors;
40 | }
41 | else
42 | {
43 | _eqFactors = null;
44 | }
45 | }
46 |
47 | ///
48 | /// Stereo mode used in decoding.
49 | ///
50 | public StereoMode StereoMode { get; set; }
51 |
52 | ///
53 | /// Decode the Mpeg frame into provided buffer. Do exactly the same as
54 | /// except that the data is written in type as byte array, while still representing single-precision float (in local endian).
55 | ///
56 | /// The Mpeg frame to be decoded.
57 | /// Destination buffer. Decoded PCM (single-precision floating point array) will be written into it.
58 | /// Writing offset on the destination buffer.
59 | ///
60 | public int DecodeFrame(IMpegFrame frame, byte[] dest, int destOffset)
61 | {
62 | if (frame == null) throw new ArgumentNullException("frame");
63 | if (dest == null) throw new ArgumentNullException("dest");
64 | if (destOffset % 4 != 0) throw new ArgumentException("Must be an even multiple of 4", "destOffset");
65 |
66 | var bufferAvailable = (dest.Length - destOffset) / 4;
67 | if (bufferAvailable < (frame.ChannelMode == MpegChannelMode.Mono ? 1 : 2) * frame.SampleCount)
68 | {
69 | throw new ArgumentException("Buffer not large enough! Must be big enough to hold the frame's entire output. This is up to 9,216 bytes.", "dest");
70 | }
71 |
72 | return DecodeFrameImpl(frame, dest, destOffset / 4) * 4;
73 | }
74 |
75 | ///
76 | /// Decode the Mpeg frame into provided buffer.
77 | /// Result varies with diffirent :
78 | ///
79 | /// -
80 | /// For , sample data on both two channels will occur in turn (left first).
81 | ///
82 | /// -
83 | /// For and , only data on
84 | /// specified channel will occur.
85 | ///
86 | /// -
87 | /// For , two channels will be down-mixed into single channel.
88 | ///
89 | ///
90 | ///
91 | /// The Mpeg frame to be decoded.
92 | /// Destination buffer. Decoded PCM (single-precision floating point array) will be written into it.
93 | /// Writing offset on the destination buffer.
94 | ///
95 | public int DecodeFrame(IMpegFrame frame, float[] dest, int destOffset)
96 | {
97 | if (frame == null) throw new ArgumentNullException("frame");
98 | if (dest == null) throw new ArgumentNullException("dest");
99 |
100 | if (dest.Length - destOffset < (frame.ChannelMode == MpegChannelMode.Mono ? 1 : 2) * frame.SampleCount)
101 | {
102 | throw new ArgumentException("Buffer not large enough! Must be big enough to hold the frame's entire output. This is up to 2,304 elements.", "dest");
103 | }
104 |
105 | return DecodeFrameImpl(frame, dest, destOffset);
106 | }
107 |
108 | int DecodeFrameImpl(IMpegFrame frame, Array dest, int destOffset)
109 | {
110 | frame.Reset();
111 |
112 | Decoder.LayerDecoderBase curDecoder = null;
113 | switch (frame.Layer)
114 | {
115 | case MpegLayer.LayerI:
116 | if (_layerIDecoder == null)
117 | {
118 | _layerIDecoder = new Decoder.LayerIDecoder();
119 | }
120 | curDecoder = _layerIDecoder;
121 | break;
122 | case MpegLayer.LayerII:
123 | if (_layerIIDecoder == null)
124 | {
125 | _layerIIDecoder = new Decoder.LayerIIDecoder();
126 | }
127 | curDecoder = _layerIIDecoder;
128 | break;
129 | case MpegLayer.LayerIII:
130 | if (_layerIIIDecoder == null)
131 | {
132 | _layerIIIDecoder = new Decoder.LayerIIIDecoder();
133 | }
134 | curDecoder = _layerIIIDecoder;
135 | break;
136 | }
137 |
138 | if (curDecoder != null)
139 | {
140 | curDecoder.SetEQ(_eqFactors);
141 | curDecoder.StereoMode = StereoMode;
142 |
143 | var cnt = curDecoder.DecodeFrame(frame, _ch0, _ch1);
144 |
145 | if (frame.ChannelMode == MpegChannelMode.Mono)
146 | {
147 | Buffer.BlockCopy(_ch0, 0, dest, destOffset * sizeof(float), cnt * sizeof(float));
148 | }
149 | else
150 | {
151 | // This is kinda annoying... if we're doing a downmix, we should technically only output a single channel
152 | // The problem is, our caller is probably expecting stereo output. Grrrr....
153 |
154 | // We use Buffer.BlockCopy here because we don't know dest's type, but do know it's big enough to do the copy
155 | for (int i = 0; i < cnt; i++)
156 | {
157 | Buffer.BlockCopy(_ch0, i * sizeof(float), dest, destOffset * sizeof(float), sizeof(float));
158 | ++destOffset;
159 | Buffer.BlockCopy(_ch1, i * sizeof(float), dest, destOffset * sizeof(float), sizeof(float));
160 | ++destOffset;
161 | }
162 | cnt *= 2;
163 | }
164 |
165 | return cnt;
166 | }
167 |
168 | return 0;
169 | }
170 |
171 | ///
172 | /// Reset the decoder.
173 | ///
174 | public void Reset()
175 | {
176 | // the synthesis filters need to be cleared
177 | if (_layerIDecoder != null)
178 | {
179 | _layerIDecoder.ResetForSeek();
180 | }
181 | if (_layerIIDecoder != null)
182 | {
183 | _layerIIDecoder.ResetForSeek();
184 | }
185 | if (_layerIIIDecoder != null)
186 | {
187 | _layerIIIDecoder.ResetForSeek();
188 | }
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/Assets/NLayer/MpegFrameDecoder.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 59b74552279cc4a4ebc5e80c73c786a4
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Assets/NLayer/README.md:
--------------------------------------------------------------------------------
1 | # NLayer
2 |
3 | NLayer is a fully managed MP3 to WAV decoder. The code was originally based
4 | on [JavaLayer](http://www.javazoom.net/javalayer/javalayer.html) (v1.0.1),
5 | which has been ported to C#.
6 |
7 | Was previously hosted at [nlayer.codeplex.com](http://nlayer.codeplex.com/).
8 | Please see the history there for full details of contributors.
9 |
10 | ## Usage
11 |
12 | To use NLayer for decoding MP3, first reference NLayer.
13 |
14 | ```cs
15 | using NLayer;
16 | ```
17 |
18 | Then create an `MpegFile`, pass a file name or a stream to the constructor, and use `ReadSamples` for decoding the content:
19 |
20 | ```cs
21 | var fileName = "myMp3File.mp3";
22 | var mpegFile = new MpegFile(filename);
23 | float[] samples = new float[44100];
24 | mpegFile.ReadSamples(samples, 0, 44100);
25 | ```
26 |
27 | More information could be found in code documents.
28 |
29 | ## Use with NAudio
30 |
31 | NLayer is capable of using in conjunction with [NAudio](https://github.com/naudio/NAudio/)
32 | for file conversion and real-time playback.
33 |
34 | You need to reference NAudio, NLayer and NLayer.NAudioSupport first.
35 |
36 | ```cs
37 | using NAudio.Wave;
38 | using NLayer.NAudioSupport;
39 | ```
40 |
41 | Then create an `Mp3FileReader`, passing in a `FrameDecompressorBuilder` that uses the `Mp3FrameDecompressor` from NLayer.NAudioSupport:
42 |
43 | ```cs
44 | var fileName = "myMp3File.mp3";
45 | var builder = new Mp3FileReader.FrameDecompressorBuilder(wf => new Mp3FrameDecompressor(wf));
46 | var reader = new Mp3FileReader(fileName, builder);
47 | // play or process the file, e.g.:
48 | waveOut.Init(reader);
49 | waveOut.Play();
50 | ```
51 |
--------------------------------------------------------------------------------
/Assets/Scenes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 23759f4280160a844be9f2637260278e
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Assets/Scenes/SampleScene.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2cda990e2423bbf4892e6590ba056729
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Packages/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "com.unity.2d.sprite": "1.0.0",
4 | "com.unity.collab-proxy": "1.2.16",
5 | "com.unity.ide.rider": "1.1.4",
6 | "com.unity.ide.vscode": "1.2.0",
7 | "com.unity.test-framework": "1.1.14",
8 | "com.unity.textmeshpro": "2.0.1",
9 | "com.unity.timeline": "1.2.14",
10 | "com.unity.ugui": "1.0.0",
11 | "com.unity.modules.ai": "1.0.0",
12 | "com.unity.modules.androidjni": "1.0.0",
13 | "com.unity.modules.animation": "1.0.0",
14 | "com.unity.modules.assetbundle": "1.0.0",
15 | "com.unity.modules.audio": "1.0.0",
16 | "com.unity.modules.cloth": "1.0.0",
17 | "com.unity.modules.director": "1.0.0",
18 | "com.unity.modules.imageconversion": "1.0.0",
19 | "com.unity.modules.imgui": "1.0.0",
20 | "com.unity.modules.jsonserialize": "1.0.0",
21 | "com.unity.modules.particlesystem": "1.0.0",
22 | "com.unity.modules.physics": "1.0.0",
23 | "com.unity.modules.physics2d": "1.0.0",
24 | "com.unity.modules.screencapture": "1.0.0",
25 | "com.unity.modules.terrain": "1.0.0",
26 | "com.unity.modules.terrainphysics": "1.0.0",
27 | "com.unity.modules.tilemap": "1.0.0",
28 | "com.unity.modules.ui": "1.0.0",
29 | "com.unity.modules.uielements": "1.0.0",
30 | "com.unity.modules.umbra": "1.0.0",
31 | "com.unity.modules.unityanalytics": "1.0.0",
32 | "com.unity.modules.unitywebrequest": "1.0.0",
33 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0",
34 | "com.unity.modules.unitywebrequestaudio": "1.0.0",
35 | "com.unity.modules.unitywebrequesttexture": "1.0.0",
36 | "com.unity.modules.unitywebrequestwww": "1.0.0",
37 | "com.unity.modules.vehicles": "1.0.0",
38 | "com.unity.modules.video": "1.0.0",
39 | "com.unity.modules.vr": "1.0.0",
40 | "com.unity.modules.wind": "1.0.0",
41 | "com.unity.modules.xr": "1.0.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ProjectSettings/AudioManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!11 &1
4 | AudioManager:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 2
7 | m_Volume: 1
8 | Rolloff Scale: 1
9 | Doppler Factor: 1
10 | Default Speaker Mode: 2
11 | m_SampleRate: 0
12 | m_DSPBufferSize: 1024
13 | m_VirtualVoiceCount: 512
14 | m_RealVoiceCount: 32
15 | m_SpatializerPlugin:
16 | m_AmbisonicDecoderPlugin:
17 | m_DisableAudio: 0
18 | m_VirtualizeEffects: 1
19 | m_RequestedDSPBufferSize: 1024
20 |
--------------------------------------------------------------------------------
/ProjectSettings/ClusterInputManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!236 &1
4 | ClusterInputManager:
5 | m_ObjectHideFlags: 0
6 | m_Inputs: []
7 |
--------------------------------------------------------------------------------
/ProjectSettings/DynamicsManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!55 &1
4 | PhysicsManager:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 11
7 | m_Gravity: {x: 0, y: -9.81, z: 0}
8 | m_DefaultMaterial: {fileID: 0}
9 | m_BounceThreshold: 2
10 | m_SleepThreshold: 0.005
11 | m_DefaultContactOffset: 0.01
12 | m_DefaultSolverIterations: 6
13 | m_DefaultSolverVelocityIterations: 1
14 | m_QueriesHitBackfaces: 0
15 | m_QueriesHitTriggers: 1
16 | m_EnableAdaptiveForce: 0
17 | m_ClothInterCollisionDistance: 0
18 | m_ClothInterCollisionStiffness: 0
19 | m_ContactsGeneration: 1
20 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
21 | m_AutoSimulation: 1
22 | m_AutoSyncTransforms: 0
23 | m_ReuseCollisionCallbacks: 1
24 | m_ClothInterCollisionSettingsToggle: 0
25 | m_ContactPairsMode: 0
26 | m_BroadphaseType: 0
27 | m_WorldBounds:
28 | m_Center: {x: 0, y: 0, z: 0}
29 | m_Extent: {x: 250, y: 250, z: 250}
30 | m_WorldSubdivisions: 8
31 | m_FrictionType: 0
32 | m_EnableEnhancedDeterminism: 0
33 | m_EnableUnifiedHeightmaps: 1
34 | m_DefaultMaxAngluarSpeed: 7
35 |
--------------------------------------------------------------------------------
/ProjectSettings/EditorBuildSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1045 &1
4 | EditorBuildSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 2
7 | m_Scenes: []
8 | m_configObjects: {}
9 |
--------------------------------------------------------------------------------
/ProjectSettings/EditorSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!159 &1
4 | EditorSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 9
7 | m_ExternalVersionControlSupport: Visible Meta Files
8 | m_SerializationMode: 2
9 | m_LineEndingsForNewScripts: 0
10 | m_DefaultBehaviorMode: 1
11 | m_PrefabRegularEnvironment: {fileID: 0}
12 | m_PrefabUIEnvironment: {fileID: 0}
13 | m_SpritePackerMode: 4
14 | m_SpritePackerPaddingPower: 1
15 | m_EtcTextureCompressorBehavior: 1
16 | m_EtcTextureFastCompressor: 1
17 | m_EtcTextureNormalCompressor: 2
18 | m_EtcTextureBestCompressor: 4
19 | m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref
20 | m_ProjectGenerationRootNamespace:
21 | m_CollabEditorSettings:
22 | inProgressEnabled: 1
23 | m_EnableTextureStreamingInEditMode: 1
24 | m_EnableTextureStreamingInPlayMode: 1
25 | m_AsyncShaderCompilation: 1
26 | m_EnterPlayModeOptionsEnabled: 0
27 | m_EnterPlayModeOptions: 3
28 | m_ShowLightmapResolutionOverlay: 1
29 | m_UseLegacyProbeSampleCount: 1
30 | m_AssetPipelineMode: 1
31 | m_CacheServerMode: 0
32 | m_CacheServerEndpoint:
33 | m_CacheServerNamespacePrefix: default
34 | m_CacheServerEnableDownload: 1
35 | m_CacheServerEnableUpload: 1
36 |
--------------------------------------------------------------------------------
/ProjectSettings/GraphicsSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!30 &1
4 | GraphicsSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 13
7 | m_Deferred:
8 | m_Mode: 1
9 | m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}
10 | m_DeferredReflections:
11 | m_Mode: 1
12 | m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0}
13 | m_ScreenSpaceShadows:
14 | m_Mode: 1
15 | m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0}
16 | m_LegacyDeferred:
17 | m_Mode: 1
18 | m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0}
19 | m_DepthNormals:
20 | m_Mode: 1
21 | m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0}
22 | m_MotionVectors:
23 | m_Mode: 1
24 | m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0}
25 | m_LightHalo:
26 | m_Mode: 1
27 | m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0}
28 | m_LensFlare:
29 | m_Mode: 1
30 | m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0}
31 | m_AlwaysIncludedShaders:
32 | - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
33 | - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
34 | - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0}
35 | - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0}
36 | - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0}
37 | m_PreloadedShaders: []
38 | m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,
39 | type: 0}
40 | m_CustomRenderPipeline: {fileID: 0}
41 | m_TransparencySortMode: 0
42 | m_TransparencySortAxis: {x: 0, y: 0, z: 1}
43 | m_DefaultRenderingPath: 1
44 | m_DefaultMobileRenderingPath: 1
45 | m_TierSettings: []
46 | m_LightmapStripping: 0
47 | m_FogStripping: 0
48 | m_InstancingStripping: 0
49 | m_LightmapKeepPlain: 1
50 | m_LightmapKeepDirCombined: 1
51 | m_LightmapKeepDynamicPlain: 1
52 | m_LightmapKeepDynamicDirCombined: 1
53 | m_LightmapKeepShadowMask: 1
54 | m_LightmapKeepSubtractive: 1
55 | m_FogKeepLinear: 1
56 | m_FogKeepExp: 1
57 | m_FogKeepExp2: 1
58 | m_AlbedoSwatchInfos: []
59 | m_LightsUseLinearIntensity: 0
60 | m_LightsUseColorTemperature: 0
61 | m_LogWhenShaderIsCompiled: 0
62 | m_AllowEnlightenSupportForUpgradedProject: 1
63 |
--------------------------------------------------------------------------------
/ProjectSettings/InputManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!13 &1
4 | InputManager:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 2
7 | m_Axes:
8 | - serializedVersion: 3
9 | m_Name: Horizontal
10 | descriptiveName:
11 | descriptiveNegativeName:
12 | negativeButton: left
13 | positiveButton: right
14 | altNegativeButton: a
15 | altPositiveButton: d
16 | gravity: 3
17 | dead: 0.001
18 | sensitivity: 3
19 | snap: 1
20 | invert: 0
21 | type: 0
22 | axis: 0
23 | joyNum: 0
24 | - serializedVersion: 3
25 | m_Name: Vertical
26 | descriptiveName:
27 | descriptiveNegativeName:
28 | negativeButton: down
29 | positiveButton: up
30 | altNegativeButton: s
31 | altPositiveButton: w
32 | gravity: 3
33 | dead: 0.001
34 | sensitivity: 3
35 | snap: 1
36 | invert: 0
37 | type: 0
38 | axis: 0
39 | joyNum: 0
40 | - serializedVersion: 3
41 | m_Name: Fire1
42 | descriptiveName:
43 | descriptiveNegativeName:
44 | negativeButton:
45 | positiveButton: left ctrl
46 | altNegativeButton:
47 | altPositiveButton: mouse 0
48 | gravity: 1000
49 | dead: 0.001
50 | sensitivity: 1000
51 | snap: 0
52 | invert: 0
53 | type: 0
54 | axis: 0
55 | joyNum: 0
56 | - serializedVersion: 3
57 | m_Name: Fire2
58 | descriptiveName:
59 | descriptiveNegativeName:
60 | negativeButton:
61 | positiveButton: left alt
62 | altNegativeButton:
63 | altPositiveButton: mouse 1
64 | gravity: 1000
65 | dead: 0.001
66 | sensitivity: 1000
67 | snap: 0
68 | invert: 0
69 | type: 0
70 | axis: 0
71 | joyNum: 0
72 | - serializedVersion: 3
73 | m_Name: Fire3
74 | descriptiveName:
75 | descriptiveNegativeName:
76 | negativeButton:
77 | positiveButton: left shift
78 | altNegativeButton:
79 | altPositiveButton: mouse 2
80 | gravity: 1000
81 | dead: 0.001
82 | sensitivity: 1000
83 | snap: 0
84 | invert: 0
85 | type: 0
86 | axis: 0
87 | joyNum: 0
88 | - serializedVersion: 3
89 | m_Name: Jump
90 | descriptiveName:
91 | descriptiveNegativeName:
92 | negativeButton:
93 | positiveButton: space
94 | altNegativeButton:
95 | altPositiveButton:
96 | gravity: 1000
97 | dead: 0.001
98 | sensitivity: 1000
99 | snap: 0
100 | invert: 0
101 | type: 0
102 | axis: 0
103 | joyNum: 0
104 | - serializedVersion: 3
105 | m_Name: Mouse X
106 | descriptiveName:
107 | descriptiveNegativeName:
108 | negativeButton:
109 | positiveButton:
110 | altNegativeButton:
111 | altPositiveButton:
112 | gravity: 0
113 | dead: 0
114 | sensitivity: 0.1
115 | snap: 0
116 | invert: 0
117 | type: 1
118 | axis: 0
119 | joyNum: 0
120 | - serializedVersion: 3
121 | m_Name: Mouse Y
122 | descriptiveName:
123 | descriptiveNegativeName:
124 | negativeButton:
125 | positiveButton:
126 | altNegativeButton:
127 | altPositiveButton:
128 | gravity: 0
129 | dead: 0
130 | sensitivity: 0.1
131 | snap: 0
132 | invert: 0
133 | type: 1
134 | axis: 1
135 | joyNum: 0
136 | - serializedVersion: 3
137 | m_Name: Mouse ScrollWheel
138 | descriptiveName:
139 | descriptiveNegativeName:
140 | negativeButton:
141 | positiveButton:
142 | altNegativeButton:
143 | altPositiveButton:
144 | gravity: 0
145 | dead: 0
146 | sensitivity: 0.1
147 | snap: 0
148 | invert: 0
149 | type: 1
150 | axis: 2
151 | joyNum: 0
152 | - serializedVersion: 3
153 | m_Name: Horizontal
154 | descriptiveName:
155 | descriptiveNegativeName:
156 | negativeButton:
157 | positiveButton:
158 | altNegativeButton:
159 | altPositiveButton:
160 | gravity: 0
161 | dead: 0.19
162 | sensitivity: 1
163 | snap: 0
164 | invert: 0
165 | type: 2
166 | axis: 0
167 | joyNum: 0
168 | - serializedVersion: 3
169 | m_Name: Vertical
170 | descriptiveName:
171 | descriptiveNegativeName:
172 | negativeButton:
173 | positiveButton:
174 | altNegativeButton:
175 | altPositiveButton:
176 | gravity: 0
177 | dead: 0.19
178 | sensitivity: 1
179 | snap: 0
180 | invert: 1
181 | type: 2
182 | axis: 1
183 | joyNum: 0
184 | - serializedVersion: 3
185 | m_Name: Fire1
186 | descriptiveName:
187 | descriptiveNegativeName:
188 | negativeButton:
189 | positiveButton: joystick button 0
190 | altNegativeButton:
191 | altPositiveButton:
192 | gravity: 1000
193 | dead: 0.001
194 | sensitivity: 1000
195 | snap: 0
196 | invert: 0
197 | type: 0
198 | axis: 0
199 | joyNum: 0
200 | - serializedVersion: 3
201 | m_Name: Fire2
202 | descriptiveName:
203 | descriptiveNegativeName:
204 | negativeButton:
205 | positiveButton: joystick button 1
206 | altNegativeButton:
207 | altPositiveButton:
208 | gravity: 1000
209 | dead: 0.001
210 | sensitivity: 1000
211 | snap: 0
212 | invert: 0
213 | type: 0
214 | axis: 0
215 | joyNum: 0
216 | - serializedVersion: 3
217 | m_Name: Fire3
218 | descriptiveName:
219 | descriptiveNegativeName:
220 | negativeButton:
221 | positiveButton: joystick button 2
222 | altNegativeButton:
223 | altPositiveButton:
224 | gravity: 1000
225 | dead: 0.001
226 | sensitivity: 1000
227 | snap: 0
228 | invert: 0
229 | type: 0
230 | axis: 0
231 | joyNum: 0
232 | - serializedVersion: 3
233 | m_Name: Jump
234 | descriptiveName:
235 | descriptiveNegativeName:
236 | negativeButton:
237 | positiveButton: joystick button 3
238 | altNegativeButton:
239 | altPositiveButton:
240 | gravity: 1000
241 | dead: 0.001
242 | sensitivity: 1000
243 | snap: 0
244 | invert: 0
245 | type: 0
246 | axis: 0
247 | joyNum: 0
248 | - serializedVersion: 3
249 | m_Name: Submit
250 | descriptiveName:
251 | descriptiveNegativeName:
252 | negativeButton:
253 | positiveButton: return
254 | altNegativeButton:
255 | altPositiveButton: joystick button 0
256 | gravity: 1000
257 | dead: 0.001
258 | sensitivity: 1000
259 | snap: 0
260 | invert: 0
261 | type: 0
262 | axis: 0
263 | joyNum: 0
264 | - serializedVersion: 3
265 | m_Name: Submit
266 | descriptiveName:
267 | descriptiveNegativeName:
268 | negativeButton:
269 | positiveButton: enter
270 | altNegativeButton:
271 | altPositiveButton: space
272 | gravity: 1000
273 | dead: 0.001
274 | sensitivity: 1000
275 | snap: 0
276 | invert: 0
277 | type: 0
278 | axis: 0
279 | joyNum: 0
280 | - serializedVersion: 3
281 | m_Name: Cancel
282 | descriptiveName:
283 | descriptiveNegativeName:
284 | negativeButton:
285 | positiveButton: escape
286 | altNegativeButton:
287 | altPositiveButton: joystick button 1
288 | gravity: 1000
289 | dead: 0.001
290 | sensitivity: 1000
291 | snap: 0
292 | invert: 0
293 | type: 0
294 | axis: 0
295 | joyNum: 0
296 |
--------------------------------------------------------------------------------
/ProjectSettings/NavMeshAreas.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!126 &1
4 | NavMeshProjectSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 2
7 | areas:
8 | - name: Walkable
9 | cost: 1
10 | - name: Not Walkable
11 | cost: 1
12 | - name: Jump
13 | cost: 2
14 | - name:
15 | cost: 1
16 | - name:
17 | cost: 1
18 | - name:
19 | cost: 1
20 | - name:
21 | cost: 1
22 | - name:
23 | cost: 1
24 | - name:
25 | cost: 1
26 | - name:
27 | cost: 1
28 | - name:
29 | cost: 1
30 | - name:
31 | cost: 1
32 | - name:
33 | cost: 1
34 | - name:
35 | cost: 1
36 | - name:
37 | cost: 1
38 | - name:
39 | cost: 1
40 | - name:
41 | cost: 1
42 | - name:
43 | cost: 1
44 | - name:
45 | cost: 1
46 | - name:
47 | cost: 1
48 | - name:
49 | cost: 1
50 | - name:
51 | cost: 1
52 | - name:
53 | cost: 1
54 | - name:
55 | cost: 1
56 | - name:
57 | cost: 1
58 | - name:
59 | cost: 1
60 | - name:
61 | cost: 1
62 | - name:
63 | cost: 1
64 | - name:
65 | cost: 1
66 | - name:
67 | cost: 1
68 | - name:
69 | cost: 1
70 | - name:
71 | cost: 1
72 | m_LastAgentTypeID: -887442657
73 | m_Settings:
74 | - serializedVersion: 2
75 | agentTypeID: 0
76 | agentRadius: 0.5
77 | agentHeight: 2
78 | agentSlope: 45
79 | agentClimb: 0.75
80 | ledgeDropHeight: 0
81 | maxJumpAcrossDistance: 0
82 | minRegionArea: 2
83 | manualCellSize: 0
84 | cellSize: 0.16666667
85 | manualTileSize: 0
86 | tileSize: 256
87 | accuratePlacement: 0
88 | debug:
89 | m_Flags: 0
90 | m_SettingNames:
91 | - Humanoid
92 |
--------------------------------------------------------------------------------
/ProjectSettings/NetworkManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!149 &1
4 | NetworkManager:
5 | m_ObjectHideFlags: 0
6 | m_DebugLevel: 0
7 | m_Sendrate: 15
8 | m_AssetToPrefab: {}
9 |
--------------------------------------------------------------------------------
/ProjectSettings/Physics2DSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!19 &1
4 | Physics2DSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 4
7 | m_Gravity: {x: 0, y: -9.81}
8 | m_DefaultMaterial: {fileID: 0}
9 | m_VelocityIterations: 8
10 | m_PositionIterations: 3
11 | m_VelocityThreshold: 1
12 | m_MaxLinearCorrection: 0.2
13 | m_MaxAngularCorrection: 8
14 | m_MaxTranslationSpeed: 100
15 | m_MaxRotationSpeed: 360
16 | m_BaumgarteScale: 0.2
17 | m_BaumgarteTimeOfImpactScale: 0.75
18 | m_TimeToSleep: 0.5
19 | m_LinearSleepTolerance: 0.01
20 | m_AngularSleepTolerance: 2
21 | m_DefaultContactOffset: 0.01
22 | m_JobOptions:
23 | serializedVersion: 2
24 | useMultithreading: 0
25 | useConsistencySorting: 0
26 | m_InterpolationPosesPerJob: 100
27 | m_NewContactsPerJob: 30
28 | m_CollideContactsPerJob: 100
29 | m_ClearFlagsPerJob: 200
30 | m_ClearBodyForcesPerJob: 200
31 | m_SyncDiscreteFixturesPerJob: 50
32 | m_SyncContinuousFixturesPerJob: 50
33 | m_FindNearestContactsPerJob: 100
34 | m_UpdateTriggerContactsPerJob: 100
35 | m_IslandSolverCostThreshold: 100
36 | m_IslandSolverBodyCostScale: 1
37 | m_IslandSolverContactCostScale: 10
38 | m_IslandSolverJointCostScale: 10
39 | m_IslandSolverBodiesPerJob: 50
40 | m_IslandSolverContactsPerJob: 50
41 | m_AutoSimulation: 1
42 | m_QueriesHitTriggers: 1
43 | m_QueriesStartInColliders: 1
44 | m_CallbacksOnDisable: 1
45 | m_ReuseCollisionCallbacks: 1
46 | m_AutoSyncTransforms: 0
47 | m_AlwaysShowColliders: 0
48 | m_ShowColliderSleep: 1
49 | m_ShowColliderContacts: 0
50 | m_ShowColliderAABB: 0
51 | m_ContactArrowScale: 0.2
52 | m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412}
53 | m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432}
54 | m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745}
55 | m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804}
56 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
57 |
--------------------------------------------------------------------------------
/ProjectSettings/PresetManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1386491679 &1
4 | PresetManager:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 2
7 | m_DefaultPresets: {}
8 |
--------------------------------------------------------------------------------
/ProjectSettings/ProjectSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!129 &1
4 | PlayerSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 20
7 | productGUID: 2a7e8dc3a4869db43bd752865d444df7
8 | AndroidProfiler: 0
9 | AndroidFilterTouchesWhenObscured: 0
10 | AndroidEnableSustainedPerformanceMode: 0
11 | defaultScreenOrientation: 4
12 | targetDevice: 2
13 | useOnDemandResources: 0
14 | accelerometerFrequency: 60
15 | companyName: DefaultCompany
16 | productName: unity-audio-html
17 | defaultCursor: {fileID: 0}
18 | cursorHotspot: {x: 0, y: 0}
19 | m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}
20 | m_ShowUnitySplashScreen: 1
21 | m_ShowUnitySplashLogo: 1
22 | m_SplashScreenOverlayOpacity: 1
23 | m_SplashScreenAnimation: 1
24 | m_SplashScreenLogoStyle: 1
25 | m_SplashScreenDrawMode: 0
26 | m_SplashScreenBackgroundAnimationZoom: 1
27 | m_SplashScreenLogoAnimationZoom: 1
28 | m_SplashScreenBackgroundLandscapeAspect: 1
29 | m_SplashScreenBackgroundPortraitAspect: 1
30 | m_SplashScreenBackgroundLandscapeUvs:
31 | serializedVersion: 2
32 | x: 0
33 | y: 0
34 | width: 1
35 | height: 1
36 | m_SplashScreenBackgroundPortraitUvs:
37 | serializedVersion: 2
38 | x: 0
39 | y: 0
40 | width: 1
41 | height: 1
42 | m_SplashScreenLogos: []
43 | m_VirtualRealitySplashScreen: {fileID: 0}
44 | m_HolographicTrackingLossScreen: {fileID: 0}
45 | defaultScreenWidth: 1024
46 | defaultScreenHeight: 768
47 | defaultScreenWidthWeb: 960
48 | defaultScreenHeightWeb: 600
49 | m_StereoRenderingPath: 0
50 | m_ActiveColorSpace: 0
51 | m_MTRendering: 1
52 | m_StackTraceTypes: 010000000100000001000000010000000100000001000000
53 | iosShowActivityIndicatorOnLoading: -1
54 | androidShowActivityIndicatorOnLoading: -1
55 | iosUseCustomAppBackgroundBehavior: 0
56 | iosAllowHTTPDownload: 1
57 | allowedAutorotateToPortrait: 1
58 | allowedAutorotateToPortraitUpsideDown: 1
59 | allowedAutorotateToLandscapeRight: 1
60 | allowedAutorotateToLandscapeLeft: 1
61 | useOSAutorotation: 1
62 | use32BitDisplayBuffer: 1
63 | preserveFramebufferAlpha: 0
64 | disableDepthAndStencilBuffers: 0
65 | androidStartInFullscreen: 1
66 | androidRenderOutsideSafeArea: 1
67 | androidUseSwappy: 0
68 | androidBlitType: 0
69 | defaultIsNativeResolution: 1
70 | macRetinaSupport: 1
71 | runInBackground: 1
72 | captureSingleScreen: 0
73 | muteOtherAudioSources: 0
74 | Prepare IOS For Recording: 0
75 | Force IOS Speakers When Recording: 0
76 | deferSystemGesturesMode: 0
77 | hideHomeButton: 0
78 | submitAnalytics: 1
79 | usePlayerLog: 1
80 | bakeCollisionMeshes: 0
81 | forceSingleInstance: 0
82 | useFlipModelSwapchain: 1
83 | resizableWindow: 0
84 | useMacAppStoreValidation: 0
85 | macAppStoreCategory: public.app-category.games
86 | gpuSkinning: 0
87 | xboxPIXTextureCapture: 0
88 | xboxEnableAvatar: 0
89 | xboxEnableKinect: 0
90 | xboxEnableKinectAutoTracking: 0
91 | xboxEnableFitness: 0
92 | visibleInBackground: 1
93 | allowFullscreenSwitch: 1
94 | fullscreenMode: 1
95 | xboxSpeechDB: 0
96 | xboxEnableHeadOrientation: 0
97 | xboxEnableGuest: 0
98 | xboxEnablePIXSampling: 0
99 | metalFramebufferOnly: 0
100 | xboxOneResolution: 0
101 | xboxOneSResolution: 0
102 | xboxOneXResolution: 3
103 | xboxOneMonoLoggingLevel: 0
104 | xboxOneLoggingLevel: 1
105 | xboxOneDisableEsram: 0
106 | xboxOneEnableTypeOptimization: 0
107 | xboxOnePresentImmediateThreshold: 0
108 | switchQueueCommandMemory: 0
109 | switchQueueControlMemory: 16384
110 | switchQueueComputeMemory: 262144
111 | switchNVNShaderPoolsGranularity: 33554432
112 | switchNVNDefaultPoolsGranularity: 16777216
113 | switchNVNOtherPoolsGranularity: 16777216
114 | stadiaPresentMode: 0
115 | stadiaTargetFramerate: 0
116 | vulkanNumSwapchainBuffers: 3
117 | vulkanEnableSetSRGBWrite: 0
118 | m_SupportedAspectRatios:
119 | 4:3: 1
120 | 5:4: 1
121 | 16:10: 1
122 | 16:9: 1
123 | Others: 1
124 | bundleVersion: 0.1
125 | preloadedAssets: []
126 | metroInputSource: 0
127 | wsaTransparentSwapchain: 0
128 | m_HolographicPauseOnTrackingLoss: 1
129 | xboxOneDisableKinectGpuReservation: 1
130 | xboxOneEnable7thCore: 1
131 | vrSettings:
132 | cardboard:
133 | depthFormat: 0
134 | enableTransitionView: 0
135 | daydream:
136 | depthFormat: 0
137 | useSustainedPerformanceMode: 0
138 | enableVideoLayer: 0
139 | useProtectedVideoMemory: 0
140 | minimumSupportedHeadTracking: 0
141 | maximumSupportedHeadTracking: 1
142 | hololens:
143 | depthFormat: 1
144 | depthBufferSharingEnabled: 1
145 | lumin:
146 | depthFormat: 0
147 | frameTiming: 2
148 | enableGLCache: 0
149 | glCacheMaxBlobSize: 524288
150 | glCacheMaxFileSize: 8388608
151 | oculus:
152 | sharedDepthBuffer: 1
153 | dashSupport: 1
154 | lowOverheadMode: 0
155 | protectedContext: 0
156 | v2Signing: 1
157 | enable360StereoCapture: 0
158 | isWsaHolographicRemotingEnabled: 0
159 | enableFrameTimingStats: 0
160 | useHDRDisplay: 0
161 | D3DHDRBitDepth: 0
162 | m_ColorGamuts: 00000000
163 | targetPixelDensity: 30
164 | resolutionScalingMode: 0
165 | androidSupportedAspectRatio: 1
166 | androidMaxAspectRatio: 2.1
167 | applicationIdentifier: {}
168 | buildNumber: {}
169 | AndroidBundleVersionCode: 1
170 | AndroidMinSdkVersion: 19
171 | AndroidTargetSdkVersion: 0
172 | AndroidPreferredInstallLocation: 1
173 | aotOptions:
174 | stripEngineCode: 1
175 | iPhoneStrippingLevel: 0
176 | iPhoneScriptCallOptimization: 0
177 | ForceInternetPermission: 0
178 | ForceSDCardPermission: 0
179 | CreateWallpaper: 0
180 | APKExpansionFiles: 0
181 | keepLoadedShadersAlive: 0
182 | StripUnusedMeshComponents: 1
183 | VertexChannelCompressionMask: 4054
184 | iPhoneSdkVersion: 988
185 | iOSTargetOSVersionString: 10.0
186 | tvOSSdkVersion: 0
187 | tvOSRequireExtendedGameController: 0
188 | tvOSTargetOSVersionString: 10.0
189 | uIPrerenderedIcon: 0
190 | uIRequiresPersistentWiFi: 0
191 | uIRequiresFullScreen: 1
192 | uIStatusBarHidden: 1
193 | uIExitOnSuspend: 0
194 | uIStatusBarStyle: 0
195 | appleTVSplashScreen: {fileID: 0}
196 | appleTVSplashScreen2x: {fileID: 0}
197 | tvOSSmallIconLayers: []
198 | tvOSSmallIconLayers2x: []
199 | tvOSLargeIconLayers: []
200 | tvOSLargeIconLayers2x: []
201 | tvOSTopShelfImageLayers: []
202 | tvOSTopShelfImageLayers2x: []
203 | tvOSTopShelfImageWideLayers: []
204 | tvOSTopShelfImageWideLayers2x: []
205 | iOSLaunchScreenType: 0
206 | iOSLaunchScreenPortrait: {fileID: 0}
207 | iOSLaunchScreenLandscape: {fileID: 0}
208 | iOSLaunchScreenBackgroundColor:
209 | serializedVersion: 2
210 | rgba: 0
211 | iOSLaunchScreenFillPct: 100
212 | iOSLaunchScreenSize: 100
213 | iOSLaunchScreenCustomXibPath:
214 | iOSLaunchScreeniPadType: 0
215 | iOSLaunchScreeniPadImage: {fileID: 0}
216 | iOSLaunchScreeniPadBackgroundColor:
217 | serializedVersion: 2
218 | rgba: 0
219 | iOSLaunchScreeniPadFillPct: 100
220 | iOSLaunchScreeniPadSize: 100
221 | iOSLaunchScreeniPadCustomXibPath:
222 | iOSUseLaunchScreenStoryboard: 0
223 | iOSLaunchScreenCustomStoryboardPath:
224 | iOSDeviceRequirements: []
225 | iOSURLSchemes: []
226 | iOSBackgroundModes: 0
227 | iOSMetalForceHardShadows: 0
228 | metalEditorSupport: 1
229 | metalAPIValidation: 1
230 | iOSRenderExtraFrameOnPause: 0
231 | appleDeveloperTeamID:
232 | iOSManualSigningProvisioningProfileID:
233 | tvOSManualSigningProvisioningProfileID:
234 | iOSManualSigningProvisioningProfileType: 0
235 | tvOSManualSigningProvisioningProfileType: 0
236 | appleEnableAutomaticSigning: 0
237 | iOSRequireARKit: 0
238 | iOSAutomaticallyDetectAndAddCapabilities: 1
239 | appleEnableProMotion: 0
240 | clonedFromGUID: 5f34be1353de5cf4398729fda238591b
241 | templatePackageId: com.unity.template.2d@3.3.0
242 | templateDefaultScene: Assets/Scenes/SampleScene.unity
243 | AndroidTargetArchitectures: 1
244 | AndroidSplashScreenScale: 0
245 | androidSplashScreen: {fileID: 0}
246 | AndroidKeystoreName:
247 | AndroidKeyaliasName:
248 | AndroidBuildApkPerCpuArchitecture: 0
249 | AndroidTVCompatibility: 0
250 | AndroidIsGame: 1
251 | AndroidEnableTango: 0
252 | androidEnableBanner: 1
253 | androidUseLowAccuracyLocation: 0
254 | androidUseCustomKeystore: 0
255 | m_AndroidBanners:
256 | - width: 320
257 | height: 180
258 | banner: {fileID: 0}
259 | androidGamepadSupportLevel: 0
260 | AndroidValidateAppBundleSize: 1
261 | AndroidAppBundleSizeToValidate: 150
262 | m_BuildTargetIcons: []
263 | m_BuildTargetPlatformIcons: []
264 | m_BuildTargetBatching: []
265 | m_BuildTargetGraphicsJobs:
266 | - m_BuildTarget: MacStandaloneSupport
267 | m_GraphicsJobs: 0
268 | - m_BuildTarget: Switch
269 | m_GraphicsJobs: 0
270 | - m_BuildTarget: MetroSupport
271 | m_GraphicsJobs: 0
272 | - m_BuildTarget: AppleTVSupport
273 | m_GraphicsJobs: 0
274 | - m_BuildTarget: BJMSupport
275 | m_GraphicsJobs: 0
276 | - m_BuildTarget: LinuxStandaloneSupport
277 | m_GraphicsJobs: 0
278 | - m_BuildTarget: PS4Player
279 | m_GraphicsJobs: 0
280 | - m_BuildTarget: iOSSupport
281 | m_GraphicsJobs: 0
282 | - m_BuildTarget: WindowsStandaloneSupport
283 | m_GraphicsJobs: 0
284 | - m_BuildTarget: XboxOnePlayer
285 | m_GraphicsJobs: 0
286 | - m_BuildTarget: LuminSupport
287 | m_GraphicsJobs: 0
288 | - m_BuildTarget: AndroidPlayer
289 | m_GraphicsJobs: 0
290 | - m_BuildTarget: WebGLSupport
291 | m_GraphicsJobs: 0
292 | m_BuildTargetGraphicsJobMode:
293 | - m_BuildTarget: PS4Player
294 | m_GraphicsJobMode: 0
295 | - m_BuildTarget: XboxOnePlayer
296 | m_GraphicsJobMode: 0
297 | m_BuildTargetGraphicsAPIs:
298 | - m_BuildTarget: AndroidPlayer
299 | m_APIs: 150000000b000000
300 | m_Automatic: 0
301 | m_BuildTargetVRSettings: []
302 | openGLRequireES31: 0
303 | openGLRequireES31AEP: 0
304 | openGLRequireES32: 0
305 | m_TemplateCustomTags: {}
306 | mobileMTRendering:
307 | Android: 1
308 | iPhone: 1
309 | tvOS: 1
310 | m_BuildTargetGroupLightmapEncodingQuality: []
311 | m_BuildTargetGroupLightmapSettings: []
312 | playModeTestRunnerEnabled: 0
313 | runPlayModeTestAsEditModeTest: 0
314 | actionOnDotNetUnhandledException: 1
315 | enableInternalProfiler: 0
316 | logObjCUncaughtExceptions: 1
317 | enableCrashReportAPI: 0
318 | cameraUsageDescription:
319 | locationUsageDescription:
320 | microphoneUsageDescription:
321 | switchNetLibKey:
322 | switchSocketMemoryPoolSize: 6144
323 | switchSocketAllocatorPoolSize: 128
324 | switchSocketConcurrencyLimit: 14
325 | switchScreenResolutionBehavior: 2
326 | switchUseCPUProfiler: 0
327 | switchApplicationID: 0x01004b9000490000
328 | switchNSODependencies:
329 | switchTitleNames_0:
330 | switchTitleNames_1:
331 | switchTitleNames_2:
332 | switchTitleNames_3:
333 | switchTitleNames_4:
334 | switchTitleNames_5:
335 | switchTitleNames_6:
336 | switchTitleNames_7:
337 | switchTitleNames_8:
338 | switchTitleNames_9:
339 | switchTitleNames_10:
340 | switchTitleNames_11:
341 | switchTitleNames_12:
342 | switchTitleNames_13:
343 | switchTitleNames_14:
344 | switchPublisherNames_0:
345 | switchPublisherNames_1:
346 | switchPublisherNames_2:
347 | switchPublisherNames_3:
348 | switchPublisherNames_4:
349 | switchPublisherNames_5:
350 | switchPublisherNames_6:
351 | switchPublisherNames_7:
352 | switchPublisherNames_8:
353 | switchPublisherNames_9:
354 | switchPublisherNames_10:
355 | switchPublisherNames_11:
356 | switchPublisherNames_12:
357 | switchPublisherNames_13:
358 | switchPublisherNames_14:
359 | switchIcons_0: {fileID: 0}
360 | switchIcons_1: {fileID: 0}
361 | switchIcons_2: {fileID: 0}
362 | switchIcons_3: {fileID: 0}
363 | switchIcons_4: {fileID: 0}
364 | switchIcons_5: {fileID: 0}
365 | switchIcons_6: {fileID: 0}
366 | switchIcons_7: {fileID: 0}
367 | switchIcons_8: {fileID: 0}
368 | switchIcons_9: {fileID: 0}
369 | switchIcons_10: {fileID: 0}
370 | switchIcons_11: {fileID: 0}
371 | switchIcons_12: {fileID: 0}
372 | switchIcons_13: {fileID: 0}
373 | switchIcons_14: {fileID: 0}
374 | switchSmallIcons_0: {fileID: 0}
375 | switchSmallIcons_1: {fileID: 0}
376 | switchSmallIcons_2: {fileID: 0}
377 | switchSmallIcons_3: {fileID: 0}
378 | switchSmallIcons_4: {fileID: 0}
379 | switchSmallIcons_5: {fileID: 0}
380 | switchSmallIcons_6: {fileID: 0}
381 | switchSmallIcons_7: {fileID: 0}
382 | switchSmallIcons_8: {fileID: 0}
383 | switchSmallIcons_9: {fileID: 0}
384 | switchSmallIcons_10: {fileID: 0}
385 | switchSmallIcons_11: {fileID: 0}
386 | switchSmallIcons_12: {fileID: 0}
387 | switchSmallIcons_13: {fileID: 0}
388 | switchSmallIcons_14: {fileID: 0}
389 | switchManualHTML:
390 | switchAccessibleURLs:
391 | switchLegalInformation:
392 | switchMainThreadStackSize: 1048576
393 | switchPresenceGroupId:
394 | switchLogoHandling: 0
395 | switchReleaseVersion: 0
396 | switchDisplayVersion: 1.0.0
397 | switchStartupUserAccount: 0
398 | switchTouchScreenUsage: 0
399 | switchSupportedLanguagesMask: 0
400 | switchLogoType: 0
401 | switchApplicationErrorCodeCategory:
402 | switchUserAccountSaveDataSize: 0
403 | switchUserAccountSaveDataJournalSize: 0
404 | switchApplicationAttribute: 0
405 | switchCardSpecSize: -1
406 | switchCardSpecClock: -1
407 | switchRatingsMask: 0
408 | switchRatingsInt_0: 0
409 | switchRatingsInt_1: 0
410 | switchRatingsInt_2: 0
411 | switchRatingsInt_3: 0
412 | switchRatingsInt_4: 0
413 | switchRatingsInt_5: 0
414 | switchRatingsInt_6: 0
415 | switchRatingsInt_7: 0
416 | switchRatingsInt_8: 0
417 | switchRatingsInt_9: 0
418 | switchRatingsInt_10: 0
419 | switchRatingsInt_11: 0
420 | switchRatingsInt_12: 0
421 | switchLocalCommunicationIds_0:
422 | switchLocalCommunicationIds_1:
423 | switchLocalCommunicationIds_2:
424 | switchLocalCommunicationIds_3:
425 | switchLocalCommunicationIds_4:
426 | switchLocalCommunicationIds_5:
427 | switchLocalCommunicationIds_6:
428 | switchLocalCommunicationIds_7:
429 | switchParentalControl: 0
430 | switchAllowsScreenshot: 1
431 | switchAllowsVideoCapturing: 1
432 | switchAllowsRuntimeAddOnContentInstall: 0
433 | switchDataLossConfirmation: 0
434 | switchUserAccountLockEnabled: 0
435 | switchSystemResourceMemory: 16777216
436 | switchSupportedNpadStyles: 22
437 | switchNativeFsCacheSize: 32
438 | switchIsHoldTypeHorizontal: 0
439 | switchSupportedNpadCount: 8
440 | switchSocketConfigEnabled: 0
441 | switchTcpInitialSendBufferSize: 32
442 | switchTcpInitialReceiveBufferSize: 64
443 | switchTcpAutoSendBufferSizeMax: 256
444 | switchTcpAutoReceiveBufferSizeMax: 256
445 | switchUdpSendBufferSize: 9
446 | switchUdpReceiveBufferSize: 42
447 | switchSocketBufferEfficiency: 4
448 | switchSocketInitializeEnabled: 1
449 | switchNetworkInterfaceManagerInitializeEnabled: 1
450 | switchPlayerConnectionEnabled: 1
451 | ps4NPAgeRating: 12
452 | ps4NPTitleSecret:
453 | ps4NPTrophyPackPath:
454 | ps4ParentalLevel: 11
455 | ps4ContentID: ED1633-NPXX51362_00-0000000000000000
456 | ps4Category: 0
457 | ps4MasterVersion: 01.00
458 | ps4AppVersion: 01.00
459 | ps4AppType: 0
460 | ps4ParamSfxPath:
461 | ps4VideoOutPixelFormat: 0
462 | ps4VideoOutInitialWidth: 1920
463 | ps4VideoOutBaseModeInitialWidth: 1920
464 | ps4VideoOutReprojectionRate: 60
465 | ps4PronunciationXMLPath:
466 | ps4PronunciationSIGPath:
467 | ps4BackgroundImagePath:
468 | ps4StartupImagePath:
469 | ps4StartupImagesFolder:
470 | ps4IconImagesFolder:
471 | ps4SaveDataImagePath:
472 | ps4SdkOverride:
473 | ps4BGMPath:
474 | ps4ShareFilePath:
475 | ps4ShareOverlayImagePath:
476 | ps4PrivacyGuardImagePath:
477 | ps4NPtitleDatPath:
478 | ps4RemotePlayKeyAssignment: -1
479 | ps4RemotePlayKeyMappingDir:
480 | ps4PlayTogetherPlayerCount: 0
481 | ps4EnterButtonAssignment: 1
482 | ps4ApplicationParam1: 0
483 | ps4ApplicationParam2: 0
484 | ps4ApplicationParam3: 0
485 | ps4ApplicationParam4: 0
486 | ps4DownloadDataSize: 0
487 | ps4GarlicHeapSize: 2048
488 | ps4ProGarlicHeapSize: 2560
489 | playerPrefsMaxSize: 32768
490 | ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ
491 | ps4pnSessions: 1
492 | ps4pnPresence: 1
493 | ps4pnFriends: 1
494 | ps4pnGameCustomData: 1
495 | playerPrefsSupport: 0
496 | enableApplicationExit: 0
497 | resetTempFolder: 1
498 | restrictedAudioUsageRights: 0
499 | ps4UseResolutionFallback: 0
500 | ps4ReprojectionSupport: 0
501 | ps4UseAudio3dBackend: 0
502 | ps4UseLowGarlicFragmentationMode: 1
503 | ps4SocialScreenEnabled: 0
504 | ps4ScriptOptimizationLevel: 0
505 | ps4Audio3dVirtualSpeakerCount: 14
506 | ps4attribCpuUsage: 0
507 | ps4PatchPkgPath:
508 | ps4PatchLatestPkgPath:
509 | ps4PatchChangeinfoPath:
510 | ps4PatchDayOne: 0
511 | ps4attribUserManagement: 0
512 | ps4attribMoveSupport: 0
513 | ps4attrib3DSupport: 0
514 | ps4attribShareSupport: 0
515 | ps4attribExclusiveVR: 0
516 | ps4disableAutoHideSplash: 0
517 | ps4videoRecordingFeaturesUsed: 0
518 | ps4contentSearchFeaturesUsed: 0
519 | ps4attribEyeToEyeDistanceSettingVR: 0
520 | ps4IncludedModules: []
521 | ps4attribVROutputEnabled: 0
522 | monoEnv:
523 | splashScreenBackgroundSourceLandscape: {fileID: 0}
524 | splashScreenBackgroundSourcePortrait: {fileID: 0}
525 | blurSplashScreenBackground: 1
526 | spritePackerPolicy:
527 | webGLMemorySize: 16
528 | webGLExceptionSupport: 1
529 | webGLNameFilesAsHashes: 0
530 | webGLDataCaching: 1
531 | webGLDebugSymbols: 0
532 | webGLEmscriptenArgs:
533 | webGLModulesDirectory:
534 | webGLTemplate: APPLICATION:Default
535 | webGLAnalyzeBuildSize: 0
536 | webGLUseEmbeddedResources: 0
537 | webGLCompressionFormat: 1
538 | webGLLinkerTarget: 1
539 | webGLThreadsSupport: 0
540 | webGLWasmStreaming: 0
541 | scriptingDefineSymbols: {}
542 | platformArchitecture: {}
543 | scriptingBackend: {}
544 | il2cppCompilerConfiguration: {}
545 | managedStrippingLevel: {}
546 | incrementalIl2cppBuild: {}
547 | allowUnsafeCode: 0
548 | additionalIl2CppArgs:
549 | scriptingRuntimeVersion: 1
550 | gcIncremental: 0
551 | gcWBarrierValidation: 0
552 | apiCompatibilityLevelPerPlatform: {}
553 | m_RenderingPath: 1
554 | m_MobileRenderingPath: 1
555 | metroPackageName: Template_2D
556 | metroPackageVersion:
557 | metroCertificatePath:
558 | metroCertificatePassword:
559 | metroCertificateSubject:
560 | metroCertificateIssuer:
561 | metroCertificateNotAfter: 0000000000000000
562 | metroApplicationDescription: Template_2D
563 | wsaImages: {}
564 | metroTileShortName:
565 | metroTileShowName: 0
566 | metroMediumTileShowName: 0
567 | metroLargeTileShowName: 0
568 | metroWideTileShowName: 0
569 | metroSupportStreamingInstall: 0
570 | metroLastRequiredScene: 0
571 | metroDefaultTileSize: 1
572 | metroTileForegroundText: 2
573 | metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0}
574 | metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628,
575 | a: 1}
576 | metroSplashScreenUseBackgroundColor: 0
577 | platformCapabilities: {}
578 | metroTargetDeviceFamilies: {}
579 | metroFTAName:
580 | metroFTAFileTypes: []
581 | metroProtocolName:
582 | XboxOneProductId:
583 | XboxOneUpdateKey:
584 | XboxOneSandboxId:
585 | XboxOneContentId:
586 | XboxOneTitleId:
587 | XboxOneSCId:
588 | XboxOneGameOsOverridePath:
589 | XboxOnePackagingOverridePath:
590 | XboxOneAppManifestOverridePath:
591 | XboxOneVersion: 1.0.0.0
592 | XboxOnePackageEncryption: 0
593 | XboxOnePackageUpdateGranularity: 2
594 | XboxOneDescription:
595 | XboxOneLanguage:
596 | - enus
597 | XboxOneCapability: []
598 | XboxOneGameRating: {}
599 | XboxOneIsContentPackage: 0
600 | XboxOneEnableGPUVariability: 1
601 | XboxOneSockets: {}
602 | XboxOneSplashScreen: {fileID: 0}
603 | XboxOneAllowedProductIds: []
604 | XboxOnePersistentLocalStorageSize: 0
605 | XboxOneXTitleMemory: 8
606 | XboxOneOverrideIdentityName:
607 | XboxOneOverrideIdentityPublisher:
608 | vrEditorSettings:
609 | daydream:
610 | daydreamIconForeground: {fileID: 0}
611 | daydreamIconBackground: {fileID: 0}
612 | cloudServicesEnabled:
613 | UNet: 1
614 | luminIcon:
615 | m_Name:
616 | m_ModelFolderPath:
617 | m_PortalFolderPath:
618 | luminCert:
619 | m_CertPath:
620 | m_SignPackage: 1
621 | luminIsChannelApp: 0
622 | luminVersion:
623 | m_VersionCode: 1
624 | m_VersionName:
625 | apiCompatibilityLevel: 6
626 | cloudProjectId:
627 | framebufferDepthMemorylessMode: 0
628 | projectName:
629 | organizationId:
630 | cloudEnabled: 0
631 | enableNativePlatformBackendsForNewInputSystem: 0
632 | disableOldInputManagerSupport: 0
633 | legacyClampBlendShapeWeights: 0
634 |
--------------------------------------------------------------------------------
/ProjectSettings/ProjectVersion.txt:
--------------------------------------------------------------------------------
1 | m_EditorVersion: 2019.4.0f1
2 | m_EditorVersionWithRevision: 2019.4.0f1 (0af376155913)
3 |
--------------------------------------------------------------------------------
/ProjectSettings/QualitySettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!47 &1
4 | QualitySettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 5
7 | m_CurrentQuality: 3
8 | m_QualitySettings:
9 | - serializedVersion: 2
10 | name: Very Low
11 | pixelLightCount: 0
12 | shadows: 0
13 | shadowResolution: 0
14 | shadowProjection: 1
15 | shadowCascades: 1
16 | shadowDistance: 15
17 | shadowNearPlaneOffset: 3
18 | shadowCascade2Split: 0.33333334
19 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
20 | shadowmaskMode: 0
21 | blendWeights: 1
22 | textureQuality: 1
23 | anisotropicTextures: 0
24 | antiAliasing: 0
25 | softParticles: 0
26 | softVegetation: 0
27 | realtimeReflectionProbes: 0
28 | billboardsFaceCameraPosition: 0
29 | vSyncCount: 0
30 | lodBias: 0.3
31 | maximumLODLevel: 0
32 | particleRaycastBudget: 4
33 | asyncUploadTimeSlice: 2
34 | asyncUploadBufferSize: 16
35 | resolutionScalingFixedDPIFactor: 1
36 | excludedTargetPlatforms: []
37 | - serializedVersion: 2
38 | name: Low
39 | pixelLightCount: 0
40 | shadows: 0
41 | shadowResolution: 0
42 | shadowProjection: 1
43 | shadowCascades: 1
44 | shadowDistance: 20
45 | shadowNearPlaneOffset: 3
46 | shadowCascade2Split: 0.33333334
47 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
48 | shadowmaskMode: 0
49 | blendWeights: 2
50 | textureQuality: 0
51 | anisotropicTextures: 0
52 | antiAliasing: 0
53 | softParticles: 0
54 | softVegetation: 0
55 | realtimeReflectionProbes: 0
56 | billboardsFaceCameraPosition: 0
57 | vSyncCount: 0
58 | lodBias: 0.4
59 | maximumLODLevel: 0
60 | particleRaycastBudget: 16
61 | asyncUploadTimeSlice: 2
62 | asyncUploadBufferSize: 16
63 | resolutionScalingFixedDPIFactor: 1
64 | excludedTargetPlatforms: []
65 | - serializedVersion: 2
66 | name: Medium
67 | pixelLightCount: 1
68 | shadows: 0
69 | shadowResolution: 0
70 | shadowProjection: 1
71 | shadowCascades: 1
72 | shadowDistance: 20
73 | shadowNearPlaneOffset: 3
74 | shadowCascade2Split: 0.33333334
75 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
76 | shadowmaskMode: 0
77 | blendWeights: 2
78 | textureQuality: 0
79 | anisotropicTextures: 0
80 | antiAliasing: 0
81 | softParticles: 0
82 | softVegetation: 0
83 | realtimeReflectionProbes: 0
84 | billboardsFaceCameraPosition: 0
85 | vSyncCount: 1
86 | lodBias: 0.7
87 | maximumLODLevel: 0
88 | particleRaycastBudget: 64
89 | asyncUploadTimeSlice: 2
90 | asyncUploadBufferSize: 16
91 | resolutionScalingFixedDPIFactor: 1
92 | excludedTargetPlatforms: []
93 | - serializedVersion: 2
94 | name: High
95 | pixelLightCount: 2
96 | shadows: 0
97 | shadowResolution: 1
98 | shadowProjection: 1
99 | shadowCascades: 2
100 | shadowDistance: 40
101 | shadowNearPlaneOffset: 3
102 | shadowCascade2Split: 0.33333334
103 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
104 | shadowmaskMode: 1
105 | blendWeights: 2
106 | textureQuality: 0
107 | anisotropicTextures: 0
108 | antiAliasing: 0
109 | softParticles: 0
110 | softVegetation: 1
111 | realtimeReflectionProbes: 0
112 | billboardsFaceCameraPosition: 0
113 | vSyncCount: 1
114 | lodBias: 1
115 | maximumLODLevel: 0
116 | particleRaycastBudget: 256
117 | asyncUploadTimeSlice: 2
118 | asyncUploadBufferSize: 16
119 | resolutionScalingFixedDPIFactor: 1
120 | excludedTargetPlatforms: []
121 | - serializedVersion: 2
122 | name: Very High
123 | pixelLightCount: 3
124 | shadows: 0
125 | shadowResolution: 2
126 | shadowProjection: 1
127 | shadowCascades: 2
128 | shadowDistance: 70
129 | shadowNearPlaneOffset: 3
130 | shadowCascade2Split: 0.33333334
131 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
132 | shadowmaskMode: 1
133 | blendWeights: 4
134 | textureQuality: 0
135 | anisotropicTextures: 0
136 | antiAliasing: 0
137 | softParticles: 0
138 | softVegetation: 1
139 | realtimeReflectionProbes: 0
140 | billboardsFaceCameraPosition: 0
141 | vSyncCount: 1
142 | lodBias: 1.5
143 | maximumLODLevel: 0
144 | particleRaycastBudget: 1024
145 | asyncUploadTimeSlice: 2
146 | asyncUploadBufferSize: 16
147 | resolutionScalingFixedDPIFactor: 1
148 | excludedTargetPlatforms: []
149 | - serializedVersion: 2
150 | name: Ultra
151 | pixelLightCount: 4
152 | shadows: 0
153 | shadowResolution: 0
154 | shadowProjection: 1
155 | shadowCascades: 4
156 | shadowDistance: 150
157 | shadowNearPlaneOffset: 3
158 | shadowCascade2Split: 0.33333334
159 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
160 | shadowmaskMode: 1
161 | blendWeights: 4
162 | textureQuality: 0
163 | anisotropicTextures: 0
164 | antiAliasing: 0
165 | softParticles: 0
166 | softVegetation: 1
167 | realtimeReflectionProbes: 0
168 | billboardsFaceCameraPosition: 0
169 | vSyncCount: 1
170 | lodBias: 2
171 | maximumLODLevel: 0
172 | particleRaycastBudget: 4096
173 | asyncUploadTimeSlice: 2
174 | asyncUploadBufferSize: 16
175 | resolutionScalingFixedDPIFactor: 1
176 | excludedTargetPlatforms: []
177 | m_PerPlatformDefaultQuality:
178 | Android: 2
179 | Nintendo 3DS: 5
180 | Nintendo Switch: 5
181 | PS4: 5
182 | PSM: 5
183 | PSP2: 2
184 | Stadia: 5
185 | Standalone: 5
186 | Tizen: 2
187 | WebGL: 3
188 | WiiU: 5
189 | Windows Store Apps: 5
190 | XboxOne: 5
191 | iPhone: 2
192 | tvOS: 2
193 |
--------------------------------------------------------------------------------
/ProjectSettings/TagManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!78 &1
4 | TagManager:
5 | serializedVersion: 2
6 | tags: []
7 | layers:
8 | - Default
9 | - TransparentFX
10 | - Ignore Raycast
11 | -
12 | - Water
13 | - UI
14 | -
15 | -
16 | -
17 | -
18 | -
19 | -
20 | -
21 | -
22 | -
23 | -
24 | -
25 | -
26 | -
27 | -
28 | -
29 | -
30 | -
31 | -
32 | -
33 | -
34 | -
35 | -
36 | -
37 | -
38 | -
39 | -
40 | m_SortingLayers:
41 | - name: Default
42 | uniqueID: 0
43 | locked: 0
44 |
--------------------------------------------------------------------------------
/ProjectSettings/TimeManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!5 &1
4 | TimeManager:
5 | m_ObjectHideFlags: 0
6 | Fixed Timestep: 0.02
7 | Maximum Allowed Timestep: 0.1
8 | m_TimeScale: 1
9 | Maximum Particle Timestep: 0.03
10 |
--------------------------------------------------------------------------------
/ProjectSettings/UnityConnectSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!310 &1
4 | UnityConnectSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 1
7 | m_Enabled: 0
8 | m_TestMode: 0
9 | m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events
10 | m_EventUrl: https://cdp.cloud.unity3d.com/v1/events
11 | m_ConfigUrl: https://config.uca.cloud.unity3d.com
12 | m_TestInitMode: 0
13 | CrashReportingSettings:
14 | m_EventUrl: https://perf-events.cloud.unity3d.com
15 | m_Enabled: 0
16 | m_LogBufferSize: 10
17 | m_CaptureEditorExceptions: 1
18 | UnityPurchasingSettings:
19 | m_Enabled: 0
20 | m_TestMode: 0
21 | UnityAnalyticsSettings:
22 | m_Enabled: 0
23 | m_TestMode: 0
24 | m_InitializeOnStartup: 1
25 | UnityAdsSettings:
26 | m_Enabled: 0
27 | m_InitializeOnStartup: 1
28 | m_TestMode: 0
29 | m_IosGameId:
30 | m_AndroidGameId:
31 | m_GameIds: {}
32 | m_GameId:
33 | PerformanceReportingSettings:
34 | m_Enabled: 0
35 |
--------------------------------------------------------------------------------
/ProjectSettings/VFXManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!937362698 &1
4 | VFXManager:
5 | m_ObjectHideFlags: 0
6 | m_IndirectShader: {fileID: 0}
7 | m_CopyBufferShader: {fileID: 0}
8 | m_SortShader: {fileID: 0}
9 | m_StripUpdateShader: {fileID: 0}
10 | m_RenderPipeSettingsPath:
11 | m_FixedTimeStep: 0.016666668
12 | m_MaxDeltaTime: 0.05
13 |
--------------------------------------------------------------------------------
/ProjectSettings/XRSettings.asset:
--------------------------------------------------------------------------------
1 | {
2 | "m_SettingKeys": [
3 | "VR Device Disabled",
4 | "VR Device User Alert"
5 | ],
6 | "m_SettingValues": [
7 | "False",
8 | "False"
9 | ]
10 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Example of loading mp3 at runtime in Unity
2 |
3 | I am not a unity expert but apparently Unity doesn't
4 | load mp3s at runtime.
5 |
6 | This example uses the [NLayer library](https://github.com/naudio/NLayer) which is just enough to decode an mp3.
7 |
8 | Note: It does not stream mp3s. Instead, first the entire mp3 file will be downloaded. It will then be converted
9 | to raw audio data. That data is then passed to Unity.
10 |
11 | The relevant code is in `AudioLoader.cs`
12 |
13 | ```
14 | IEnumerator LoadHelper(string uri)
15 | {
16 | UnityWebRequest www = UnityWebRequest.Get(uri);
17 | yield return www.SendWebRequest();
18 |
19 | if(www.isNetworkError || www.isHttpError)
20 | {
21 | Debug.Log(www.error);
22 | }
23 | else
24 | {
25 | // Or retrieve results as binary data
26 | byte[] results = www.downloadHandler.data;
27 | var memStream = new System.IO.MemoryStream(results);
28 | var mpgFile = new NLayer.MpegFile(memStream);
29 | var samples = new float[mpgFile.Length];
30 | mpgFile.ReadSamples(samples, 0, (int)mpgFile.Length);
31 |
32 | var clip = AudioClip.Create("foo", samples.Length, mpgFile.Channels, mpgFile.SampleRate, false);
33 | clip.SetData(samples, 0);
34 | source.clip = clip;
35 | }
36 | }
37 | ```
38 |
39 | Tested on Windows and on [a WebGL build](https://greggman.github.io/unity-load-mp3-at-runtime/) in 2019.4.1. (man I want to punch Unity in the face for that stupid naming! 😆)
40 |
41 | ## Notes
42 |
43 | 1. I have no idea what the implications of mp3 licensing are
44 |
45 | 2. The NLayer code is licensed under the MIT License as is this example
46 |
47 | 3. I originally tried the [MP3Stream library](https://github.com/ZaneDubya/MP3Sharp).
48 | It works but it is [LGPL](https://github.com/ZaneDubya/MP3Sharp/blob/master/license.txt)
49 | which AFAICT is entirely incompatible with how Unity works. The LGPL requires
50 | linking at runtime via dynamic libraries. Unity doesn't support that. Instead
51 | it re-builds the library into your app. So, you can't ship a game made with
52 | Unity using the MP3Sharp library unless you either (a) license your game as
53 | LGPL or GPL... not sure that's even possible or (b) you find some creative way to keep
54 | the MP3Stream code separate and load it at runtime.
55 |
--------------------------------------------------------------------------------