Mp3读取,Effect Remove

Signed-off-by: TRAfoer <lhf190@outlook.com>
This commit is contained in:
2025-07-12 18:27:10 +08:00
parent 2ccac78620
commit 47ec9ddb21
164 changed files with 308674 additions and 50659 deletions

View File

@@ -0,0 +1,209 @@
using System;
namespace NLayer.Decoder
{
class BitReservoir
{
// Per the spec, the maximum buffer size for layer III is 7680 bits, which is 960 bytes.
// The only catch is if we're decoding a "free" frame, which could be a lot more (since
// some encoders allow higher bitrates to maintain audio transparency).
byte[] _buf = new byte[8192];
int _start = 0, _end = -1, _bitsLeft = 0;
long _bitsRead = 0L;
static int GetSlots(IMpegFrame frame)
{
var cnt = frame.FrameLength - 4;
if (frame.HasCrc) cnt -= 2;
if (frame.Version == MpegVersion.Version1 && frame.ChannelMode != MpegChannelMode.Mono) return cnt - 32;
if (frame.Version > MpegVersion.Version1 && frame.ChannelMode == MpegChannelMode.Mono) return cnt - 9;
return cnt - 17;
}
public bool AddBits(IMpegFrame frame, int overlap)
{
var originalEnd = _end;
var slots = GetSlots(frame);
while (--slots >= 0)
{
var temp = frame.ReadBits(8);
if (temp == -1) throw new System.IO.InvalidDataException("Frame did not have enough bytes!");
_buf[++_end] = (byte)temp;
if (_end == _buf.Length - 1) _end = -1;
}
_bitsLeft = 8;
if (originalEnd == -1)
{
// it's either the start of the stream or we've reset... only return true if overlap says this frame is enough
return overlap == 0;
}
else
{
// it's not the start of the stream so calculate _start based on whether we have enough bytes left
// if we have enough bytes, reset start to match overlap
if ((originalEnd + 1 - _start + _buf.Length) % _buf.Length >= overlap)
{
_start = (originalEnd + 1 - overlap + _buf.Length) % _buf.Length;
return true;
}
// otherwise, just set start to match the start of the frame (we probably skipped a frame)
else
{
_start = originalEnd + overlap;
return false;
}
}
}
public int GetBits(int count)
{
int bitsRead;
var bits = TryPeekBits(count, out bitsRead);
if (bitsRead < count) throw new System.IO.InvalidDataException("Reservoir did not have enough bytes!");
SkipBits(count);
return bits;
}
public int Get1Bit()
{
// this is an optimized single-bit reader
if (_bitsLeft == 0) throw new System.IO.InvalidDataException("Reservoir did not have enough bytes!");
--_bitsLeft;
++_bitsRead;
var val = (_buf[_start] >> _bitsLeft) & 1;
if (_bitsLeft == 0 && (_start = (_start + 1) % _buf.Length) != _end + 1)
{
_bitsLeft = 8;
}
return val;
}
public int TryPeekBits(int count, out int readCount)
{
if (count < 0 || count > 32) throw new ArgumentOutOfRangeException("count", "Must return between 0 and 32 bits!");
// if we don't have any bits left, just return no bits read
if (_bitsLeft == 0 || count == 0)
{
readCount = 0;
return 0;
}
// get bits from the current start of the reservoir
var bits = (int)_buf[_start];
if (count < _bitsLeft)
{
// just grab the bits, adjust the "left" count, and return
bits >>= _bitsLeft - count;
bits &= ((1 << count) - 1);
readCount = count;
return bits;
}
// we have to do it the hard way...
bits &= ((1 << _bitsLeft) - 1);
count -= _bitsLeft;
readCount = _bitsLeft;
var resStart = _start;
// arg... gotta grab some more bits...
while (count > 0)
{
// advance the start marker, and if we just advanced it past the end of the buffer, bail
if ((resStart = (resStart + 1) % _buf.Length) == _end + 1)
{
break;
}
// figure out how many bits to pull from it
var bitsToRead = Math.Min(count, 8);
// move the existing bits over
bits <<= bitsToRead;
bits |= (_buf[resStart] >> ((8 - bitsToRead) % 8));
// update our count
count -= bitsToRead;
// update our remaining bits
readCount += bitsToRead;
}
return bits;
}
public int BitsAvailable
{
get
{
if (_bitsLeft > 0)
{
return (((_end + _buf.Length) - _start) % _buf.Length) * 8 + _bitsLeft;
}
return 0;
}
}
public long BitsRead
{
get { return _bitsRead; }
}
public void SkipBits(int count)
{
if (count > 0)
{
// make sure we have enough bits to skip
if (count > BitsAvailable) throw new ArgumentOutOfRangeException("count");
// now calculate the new positions
var offset = (8 - _bitsLeft) + count;
_start = ((offset / 8) + _start) % _buf.Length;
_bitsLeft = 8 - (offset % 8);
_bitsRead += count;
}
}
public void RewindBits(int count)
{
_bitsLeft += count;
_bitsRead -= count;
while (_bitsLeft > 8)
{
--_start;
_bitsLeft -= 8;
}
while (_start < 0)
{
_start += _buf.Length;
}
}
public void FlushBits()
{
if (_bitsLeft < 8)
{
SkipBits(_bitsLeft);
}
}
public void Reset()
{
_start = 0;
_end = -1;
_bitsLeft = 0;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6509f5ab6766c1c4d89e1ba31de2cba0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
using System;
namespace NLayer.Decoder
{
abstract class FrameBase
{
static int _totalAllocation = 0;
static internal int TotalAllocation
{
get { return System.Threading.Interlocked.CompareExchange(ref _totalAllocation, 0, 0); }
}
internal long Offset { get; private set; }
internal int Length { get; set; }
MpegStreamReader _reader;
byte[] _savedBuffer;
protected FrameBase() { }
internal bool Validate(long offset, MpegStreamReader reader)
{
Offset = offset;
_reader = reader;
var len = Validate();
if (len > 0)
{
Length = len;
return true;
}
return false;
}
protected int Read(int offset, byte[] buffer)
{
return Read(offset, buffer, 0, buffer.Length);
}
protected int Read(int offset, byte[] buffer, int index, int count)
{
if (_savedBuffer != null)
{
if (index < 0 || index + count > buffer.Length) return 0; // check against caller's buffer
if (offset < 0 || offset >= _savedBuffer.Length) return 0; // check against saved buffer
if (offset + count > _savedBuffer.Length) count = _savedBuffer.Length - index; // twiddle the size as needed
Array.Copy(_savedBuffer, offset, buffer, index, count);
return count;
}
else
{
return _reader.Read(Offset + offset, buffer, index, count);
}
}
protected int ReadByte(int offset)
{
if (_savedBuffer != null)
{
if (offset < 0) throw new ArgumentOutOfRangeException();
if (offset >= _savedBuffer.Length) return -1;
return (int)_savedBuffer[offset];
}
else
{
return _reader.ReadByte(Offset + offset);
}
}
/// <summary>
/// Called to validate the frame header
/// </summary>
/// <returns>The length of the frame, or -1 if frame is invalid</returns>
abstract protected int Validate();
internal void SaveBuffer()
{
_savedBuffer = new byte[Length];
_reader.Read(Offset, _savedBuffer, 0, Length);
System.Threading.Interlocked.Add(ref _totalAllocation, Length);
}
internal void ClearBuffer()
{
System.Threading.Interlocked.Add(ref _totalAllocation, -Length);
_savedBuffer = null;
}
/// <summary>
/// Called when the stream is not "seek-able"
/// </summary>
virtual internal void Parse() { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c67e024d60626842b00f2248877db3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e67d41da55d301a4681b6a496cf52dd9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NLayer.Decoder
{
class ID3Frame : FrameBase
{
internal static ID3Frame TrySync(uint syncMark)
{
if ((syncMark & 0xFFFFFF00U) == 0x49443300)
{
return new ID3Frame { _version = 2 };
}
if ((syncMark & 0xFFFFFF00U) == 0x54414700)
{
if ((syncMark & 0xFF) == 0x2B)
{
return new ID3Frame { _version = 1 };
}
else
{
return new ID3Frame { _version = 0 };
}
}
return null;
}
int _version;
ID3Frame()
{
}
protected override int Validate()
{
switch (_version)
{
case 2:
// v2, yay!
var buf = new byte[7];
if (Read(3, buf) == 7)
{
byte flagsMask;
switch (buf[0])
{
case 2:
flagsMask = 0x3F;
break;
case 3:
flagsMask = 0x1F;
break;
case 4:
flagsMask = 0x0F;
break;
default:
return -1;
}
// ignore the flags (we don't need them for the validation)
// get the size (7 bits per byte [MSB cleared])
var size = (buf[3] << 21)
| (buf[4] << 14)
| (buf[5] << 7)
| (buf[6]);
// finally, check to make sure that all the right bits are cleared
if (!(((buf[2] & flagsMask) | (buf[3] & 0x80) | (buf[4] & 0x80) | (buf[5] & 0x80) | (buf[6] & 0x80)) != 0 || buf[1] == 0xFF))
{
return size + 10; // don't forget the sync, flag & size bytes!
}
}
break;
case 1:
return 227 + 128;
case 0:
return 128;
}
return -1;
}
internal override void Parse()
{
// assume we have to process it now or else... we can still read the whole frame, so no biggie
switch (_version)
{
case 2:
ParseV2();
break;
case 1:
ParseV1Enh();
break;
case 0:
ParseV1(3);
break;
}
}
void ParseV1(int offset)
{
//var buffer = new byte[125];
//if (Read(offset, buffer) == 125)
//{
// // v1 tags use ASCII encoding... For now we'll use the built-in encoding, but for Win8 we'll have to build our own.
// var encoding = Encoding.ASCII;
//
// // title (30)
// Title = encoding.GetString(buffer, 0, 30);
//
// // artist (30)
// Artist = encoding.GetString(buffer, 30, 30);
//
// // album (30)
// Album = encoding.GetString(buffer, 60, 30);
//
// // year (4)
// Year = encoding.GetString(buffer, 90, 30);
//
// // comment (30)*
// Comment = encoding.GetString(buffer, 94, 30);
//
// if (buffer[122] == 0)
// {
// // track (1)*
// Track = (int)buffer[123];
// }
//
// // genre (1)
// // ignore for now
//
// // * if byte 29 of comment is 0, track is byte 30. Otherwise, track is unknown.
//}
}
void ParseV1Enh()
{
ParseV1(230);
//var buffer = new byte[223];
//if (Read(4, buffer) == 223)
//{
// // v1 tags use ASCII encoding... For now we'll use the built-in encoding, but for Win8 we'll have to build our own.
// var encoding = Encoding.ASCII;
//
// // title (60)
// Title += encoding.GetString(buffer, 0, 60);
//
// // artist (60)
// Artist += encoding.GetString(buffer, 60, 60);
//
// // album (60)
// Album += encoding.GetString(buffer, 120, 60);
//
// // speed (1)
// //var speed = buffer[180];
//
// // genre (30)
// Genre = encoding.GetString(buffer, 181, 30);
//
// // start-time (6)
// // 211
//
// // end-time (6)
// // 217
//}
}
void ParseV2()
{
// v2 is much more complicated than v1... don't worry about it for now
// look for any merged frames, as well
}
internal int Version
{
get
{
if (_version == 0) return 1;
return _version;
}
}
//internal string Title { get; private set; }
//internal string Artist { get; private set; }
//internal string Album { get; private set; }
//internal string Year { get; private set; }
//internal string Comment { get; private set; }
//internal int Track { get; private set; }
//internal string Genre { get; private set; }
// speed
//public TimeSpan StartTime { get; private set; }
//public TimeSpan EndTime { get; private set; }
internal void Merge(ID3Frame newFrame)
{
// just save off the frame for parsing later
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c0263a912bb5e3409a00a5d44e91944
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,444 @@
/*
* NLayer - A C# MPEG1/2/2.5 audio decoder
*
* Portions of this file are courtesy Fluendo, S.A. They are dual licensed as Ms-PL
* and under the following license:
*
* Copyright <2005-2012> Fluendo S.A.
*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* See further explanation attached in License Statement (distributed in the file
* LICENSE).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
using System.Collections.Generic;
namespace NLayer.Decoder
{
abstract class LayerDecoderBase
{
protected const int SBLIMIT = 32;
const float INV_SQRT_2 = 7.071067811865474617150084668537e-01f;
#region Tables
static float[] DEWINDOW_TABLE = {
0.000000000f, -0.000015259f, -0.000015259f, -0.000015259f,
-0.000015259f, -0.000015259f, -0.000015259f, -0.000030518f,
-0.000030518f, -0.000030518f, -0.000030518f, -0.000045776f,
-0.000045776f, -0.000061035f, -0.000061035f, -0.000076294f,
-0.000076294f, -0.000091553f, -0.000106812f, -0.000106812f,
-0.000122070f, -0.000137329f, -0.000152588f, -0.000167847f,
-0.000198364f, -0.000213623f, -0.000244141f, -0.000259399f,
-0.000289917f, -0.000320435f, -0.000366211f, -0.000396729f,
-0.000442505f, -0.000473022f, -0.000534058f, -0.000579834f,
-0.000625610f, -0.000686646f, -0.000747681f, -0.000808716f,
-0.000885010f, -0.000961304f, -0.001037598f, -0.001113892f,
-0.001205444f, -0.001296997f, -0.001388550f, -0.001480103f,
-0.001586914f, -0.001693726f, -0.001785278f, -0.001907349f,
-0.002014160f, -0.002120972f, -0.002243042f, -0.002349854f,
-0.002456665f, -0.002578735f, -0.002685547f, -0.002792358f,
-0.002899170f, -0.002990723f, -0.003082275f, -0.003173828f,
0.003250122f, 0.003326416f, 0.003387451f, 0.003433228f,
0.003463745f, 0.003479004f, 0.003479004f, 0.003463745f,
0.003417969f, 0.003372192f, 0.003280640f, 0.003173828f,
0.003051758f, 0.002883911f, 0.002700806f, 0.002487183f,
0.002227783f, 0.001937866f, 0.001617432f, 0.001266479f,
0.000869751f, 0.000442505f, -0.000030518f, -0.000549316f,
-0.001098633f, -0.001693726f, -0.002334595f, -0.003005981f,
-0.003723145f, -0.004486084f, -0.005294800f, -0.006118774f,
-0.007003784f, -0.007919312f, -0.008865356f, -0.009841919f,
-0.010848999f, -0.011886597f, -0.012939453f, -0.014022827f,
-0.015121460f, -0.016235352f, -0.017349243f, -0.018463135f,
-0.019577026f, -0.020690918f, -0.021789551f, -0.022857666f,
-0.023910522f, -0.024932861f, -0.025909424f, -0.026840210f,
-0.027725220f, -0.028533936f, -0.029281616f, -0.029937744f,
-0.030532837f, -0.031005859f, -0.031387329f, -0.031661987f,
-0.031814575f, -0.031845093f, -0.031738281f, -0.031478882f,
0.031082153f, 0.030517578f, 0.029785156f, 0.028884888f,
0.027801514f, 0.026535034f, 0.025085449f, 0.023422241f,
0.021575928f, 0.019531250f, 0.017257690f, 0.014801025f,
0.012115479f, 0.009231567f, 0.006134033f, 0.002822876f,
-0.000686646f, -0.004394531f, -0.008316040f, -0.012420654f,
-0.016708374f, -0.021179199f, -0.025817871f, -0.030609131f,
-0.035552979f, -0.040634155f, -0.045837402f, -0.051132202f,
-0.056533813f, -0.061996460f, -0.067520142f, -0.073059082f,
-0.078628540f, -0.084182739f, -0.089706421f, -0.095169067f,
-0.100540161f, -0.105819702f, -0.110946655f, -0.115921021f,
-0.120697021f, -0.125259399f, -0.129562378f, -0.133590698f,
-0.137298584f, -0.140670776f, -0.143676758f, -0.146255493f,
-0.148422241f, -0.150115967f, -0.151306152f, -0.151962280f,
-0.152069092f, -0.151596069f, -0.150497437f, -0.148773193f,
-0.146362305f, -0.143264771f, -0.139450073f, -0.134887695f,
-0.129577637f, -0.123474121f, -0.116577148f, -0.108856201f,
0.100311279f, 0.090927124f, 0.080688477f, 0.069595337f,
0.057617187f, 0.044784546f, 0.031082153f, 0.016510010f,
0.001068115f, -0.015228271f, -0.032379150f, -0.050354004f,
-0.069168091f, -0.088775635f, -0.109161377f, -0.130310059f,
-0.152206421f, -0.174789429f, -0.198059082f, -0.221984863f,
-0.246505737f, -0.271591187f, -0.297210693f, -0.323318481f,
-0.349868774f, -0.376800537f, -0.404083252f, -0.431655884f,
-0.459472656f, -0.487472534f, -0.515609741f, -0.543823242f,
-0.572036743f, -0.600219727f, -0.628295898f, -0.656219482f,
-0.683914185f, -0.711318970f, -0.738372803f, -0.765029907f,
-0.791213989f, -0.816864014f, -0.841949463f, -0.866363525f,
-0.890090942f, -0.913055420f, -0.935195923f, -0.956481934f,
-0.976852417f, -0.996246338f, -1.014617920f, -1.031936646f,
-1.048156738f, -1.063217163f, -1.077117920f, -1.089782715f,
-1.101211548f, -1.111373901f, -1.120223999f, -1.127746582f,
-1.133926392f, -1.138763428f, -1.142211914f, -1.144287109f,
1.144989014f, 1.144287109f, 1.142211914f, 1.138763428f,
1.133926392f, 1.127746582f, 1.120223999f, 1.111373901f,
1.101211548f, 1.089782715f, 1.077117920f, 1.063217163f,
1.048156738f, 1.031936646f, 1.014617920f, 0.996246338f,
0.976852417f, 0.956481934f, 0.935195923f, 0.913055420f,
0.890090942f, 0.866363525f, 0.841949463f, 0.816864014f,
0.791213989f, 0.765029907f, 0.738372803f, 0.711318970f,
0.683914185f, 0.656219482f, 0.628295898f, 0.600219727f,
0.572036743f, 0.543823242f, 0.515609741f, 0.487472534f,
0.459472656f, 0.431655884f, 0.404083252f, 0.376800537f,
0.349868774f, 0.323318481f, 0.297210693f, 0.271591187f,
0.246505737f, 0.221984863f, 0.198059082f, 0.174789429f,
0.152206421f, 0.130310059f, 0.109161377f, 0.088775635f,
0.069168091f, 0.050354004f, 0.032379150f, 0.015228271f,
-0.001068115f, -0.016510010f, -0.031082153f, -0.044784546f,
-0.057617187f, -0.069595337f, -0.080688477f, -0.090927124f,
0.100311279f, 0.108856201f, 0.116577148f, 0.123474121f,
0.129577637f, 0.134887695f, 0.139450073f, 0.143264771f,
0.146362305f, 0.148773193f, 0.150497437f, 0.151596069f,
0.152069092f, 0.151962280f, 0.151306152f, 0.150115967f,
0.148422241f, 0.146255493f, 0.143676758f, 0.140670776f,
0.137298584f, 0.133590698f, 0.129562378f, 0.125259399f,
0.120697021f, 0.115921021f, 0.110946655f, 0.105819702f,
0.100540161f, 0.095169067f, 0.089706421f, 0.084182739f,
0.078628540f, 0.073059082f, 0.067520142f, 0.061996460f,
0.056533813f, 0.051132202f, 0.045837402f, 0.040634155f,
0.035552979f, 0.030609131f, 0.025817871f, 0.021179199f,
0.016708374f, 0.012420654f, 0.008316040f, 0.004394531f,
0.000686646f, -0.002822876f, -0.006134033f, -0.009231567f,
-0.012115479f, -0.014801025f, -0.017257690f, -0.019531250f,
-0.021575928f, -0.023422241f, -0.025085449f, -0.026535034f,
-0.027801514f, -0.028884888f, -0.029785156f, -0.030517578f,
0.031082153f, 0.031478882f, 0.031738281f, 0.031845093f,
0.031814575f, 0.031661987f, 0.031387329f, 0.031005859f,
0.030532837f, 0.029937744f, 0.029281616f, 0.028533936f,
0.027725220f, 0.026840210f, 0.025909424f, 0.024932861f,
0.023910522f, 0.022857666f, 0.021789551f, 0.020690918f,
0.019577026f, 0.018463135f, 0.017349243f, 0.016235352f,
0.015121460f, 0.014022827f, 0.012939453f, 0.011886597f,
0.010848999f, 0.009841919f, 0.008865356f, 0.007919312f,
0.007003784f, 0.006118774f, 0.005294800f, 0.004486084f,
0.003723145f, 0.003005981f, 0.002334595f, 0.001693726f,
0.001098633f, 0.000549316f, 0.000030518f, -0.000442505f,
-0.000869751f, -0.001266479f, -0.001617432f, -0.001937866f,
-0.002227783f, -0.002487183f, -0.002700806f, -0.002883911f,
-0.003051758f, -0.003173828f, -0.003280640f, -0.003372192f,
-0.003417969f, -0.003463745f, -0.003479004f, -0.003479004f,
-0.003463745f, -0.003433228f, -0.003387451f, -0.003326416f,
0.003250122f, 0.003173828f, 0.003082275f, 0.002990723f,
0.002899170f, 0.002792358f, 0.002685547f, 0.002578735f,
0.002456665f, 0.002349854f, 0.002243042f, 0.002120972f,
0.002014160f, 0.001907349f, 0.001785278f, 0.001693726f,
0.001586914f, 0.001480103f, 0.001388550f, 0.001296997f,
0.001205444f, 0.001113892f, 0.001037598f, 0.000961304f,
0.000885010f, 0.000808716f, 0.000747681f, 0.000686646f,
0.000625610f, 0.000579834f, 0.000534058f, 0.000473022f,
0.000442505f, 0.000396729f, 0.000366211f, 0.000320435f,
0.000289917f, 0.000259399f, 0.000244141f, 0.000213623f,
0.000198364f, 0.000167847f, 0.000152588f, 0.000137329f,
0.000122070f, 0.000106812f, 0.000106812f, 0.000091553f,
0.000076294f, 0.000076294f, 0.000061035f, 0.000061035f,
0.000045776f, 0.000045776f, 0.000030518f, 0.000030518f,
0.000030518f, 0.000030518f, 0.000015259f, 0.000015259f,
0.000015259f, 0.000015259f, 0.000015259f, 0.000015259f
};
static float[] SYNTH_COS64_TABLE = {
5.0060299823519627260e-01f, 5.0241928618815567820e-01f, 5.0547095989754364798e-01f, 5.0979557910415917998e-01f,
5.1544730992262455249e-01f, 5.2249861493968885462e-01f, 5.3104259108978413284e-01f, 5.4119610014619701222e-01f,
5.5310389603444454210e-01f, 5.6694403481635768927e-01f, 5.8293496820613388554e-01f, 6.0134488693504528634e-01f,
6.2250412303566482475e-01f, 6.4682178335999007679e-01f, 6.7480834145500567800e-01f, 7.0710678118654746172e-01f,
7.4453627100229857749e-01f, 7.8815462345125020249e-01f, 8.3934964541552681272e-01f, 8.9997622313641556513e-01f,
9.7256823786196078263e-01f, 1.0606776859903470633e+00f, 1.1694399334328846596e+00f, 1.3065629648763763537e+00f,
1.4841646163141661852e+00f, 1.7224470982383341955e+00f, 2.0577810099534108446e+00f, 2.5629154477415054814e+00f,
3.4076084184687189804e+00f, 5.1011486186891552563e+00f, 1.0190008123548032870e+01f
};
#endregion
List<float[]> _synBuf = new List<float[]>(2);
List<int> _bufOffset = new List<int>(2);
float[] _eq;
internal LayerDecoderBase()
{
StereoMode = StereoMode.Both;
}
abstract internal int DecodeFrame(IMpegFrame frame, float[] ch0, float[] ch1);
internal void SetEQ(float[] eq)
{
if (eq == null || eq.Length == 32)
{
_eq = eq;
}
}
internal StereoMode StereoMode { get; set; }
virtual internal void ResetForSeek()
{
_synBuf.Clear();
_bufOffset.Clear();
}
float[] ippuv = new float[512];
protected void InversePolyPhase(int channel, float[] data)
{
float[] synBuf;
int k;
GetBufAndOffset(channel, out synBuf, out k);
if (_eq != null)
{
for (int i = 0; i < 32; i++)
{
data[i] *= _eq[i];
}
}
DCT32(data, synBuf, k);
BuildUVec(ippuv, synBuf, k);
DewindowOutput(ippuv, data);
}
void GetBufAndOffset(int channel, out float[] synBuf, out int k)
{
while (_synBuf.Count <= channel)
{
_synBuf.Add(new float[1024]);
}
while (_bufOffset.Count <= channel)
{
_bufOffset.Add(0);
}
synBuf = _synBuf[channel];
k = _bufOffset[channel];
k = (k - 32) & 511;
_bufOffset[channel] = k;
}
float[] ei32 = new float[16], eo32 = new float[16], oi32 = new float[16], oo32 = new float[16];
void DCT32(float[] _in, float[] _out, int k)
{
int i;
for (i = 0; i < 16; i++)
{
ei32[i] = _in[i] + _in[31 - i];
oi32[i] = (_in[i] - _in[31 - i]) * SYNTH_COS64_TABLE[2 * i];
}
DCT16(ei32, eo32);
DCT16(oi32, oo32);
for (i = 0; i < 15; i++)
{
_out[2 * i + k] = eo32[i];
_out[2 * i + 1 + k] = oo32[i] + oo32[i + 1];
}
_out[30 + k] = eo32[15];
_out[31 + k] = oo32[15];
}
float[] ei16 = new float[8], eo16 = new float[8], oi16 = new float[8], oo16 = new float[8];
void DCT16(float[] _in, float[] _out)
{
float a, b;
a = _in[0]; b = _in[15];
ei16[0] = a + b;
oi16[0] = (a - b) * SYNTH_COS64_TABLE[1];
a = _in[1]; b = _in[14];
ei16[1] = a + b;
oi16[1] = (a - b) * SYNTH_COS64_TABLE[5];
a = _in[2]; b = _in[13];
ei16[2] = a + b;
oi16[2] = (a - b) * SYNTH_COS64_TABLE[9];
a = _in[3]; b = _in[12];
ei16[3] = a + b;
oi16[3] = (a - b) * SYNTH_COS64_TABLE[13];
a = _in[4]; b = _in[11];
ei16[4] = a + b;
oi16[4] = (a - b) * SYNTH_COS64_TABLE[17];
a = _in[5]; b = _in[10];
ei16[5] = a + b;
oi16[5] = (a - b) * SYNTH_COS64_TABLE[21];
a = _in[6]; b = _in[9];
ei16[6] = a + b;
oi16[6] = (a - b) * SYNTH_COS64_TABLE[25];
a = _in[7]; b = _in[8];
ei16[7] = a + b;
oi16[7] = (a - b) * SYNTH_COS64_TABLE[29];
DCT8(ei16, eo16);
DCT8(oi16, oo16);
_out[0] = eo16[0];
_out[1] = oo16[0] + oo16[1];
_out[2] = eo16[1];
_out[3] = oo16[1] + oo16[2];
_out[4] = eo16[2];
_out[5] = oo16[2] + oo16[3];
_out[6] = eo16[3];
_out[7] = oo16[3] + oo16[4];
_out[8] = eo16[4];
_out[9] = oo16[4] + oo16[5];
_out[10] = eo16[5];
_out[11] = oo16[5] + oo16[6];
_out[12] = eo16[6];
_out[13] = oo16[6] + oo16[7];
_out[14] = eo16[7];
_out[15] = oo16[7];
}
float[] ei8 = new float[4], tmp8 = new float[6], oi8 = new float[4], oo8 = new float[4];
void DCT8(float[] _in, float[] _out)
{
/* Even indices */
ei8[0] = _in[0] + _in[7];
ei8[1] = _in[3] + _in[4];
ei8[2] = _in[1] + _in[6];
ei8[3] = _in[2] + _in[5];
tmp8[0] = ei8[0] + ei8[1];
tmp8[1] = ei8[2] + ei8[3];
tmp8[2] = (ei8[0] - ei8[1]) * SYNTH_COS64_TABLE[7];
tmp8[3] = (ei8[2] - ei8[3]) * SYNTH_COS64_TABLE[23];
tmp8[4] = (float)((tmp8[2] - tmp8[3]) * INV_SQRT_2);
_out[0] = tmp8[0] + tmp8[1];
_out[2] = tmp8[2] + tmp8[3] + tmp8[4];
_out[4] = (float)((tmp8[0] - tmp8[1]) * INV_SQRT_2);
_out[6] = tmp8[4];
/* Odd indices */
oi8[0] = (_in[0] - _in[7]) * SYNTH_COS64_TABLE[3];
oi8[1] = (_in[1] - _in[6]) * SYNTH_COS64_TABLE[11];
oi8[2] = (_in[2] - _in[5]) * SYNTH_COS64_TABLE[19];
oi8[3] = (_in[3] - _in[4]) * SYNTH_COS64_TABLE[27];
tmp8[0] = oi8[0] + oi8[3];
tmp8[1] = oi8[1] + oi8[2];
tmp8[2] = (oi8[0] - oi8[3]) * SYNTH_COS64_TABLE[7];
tmp8[3] = (oi8[1] - oi8[2]) * SYNTH_COS64_TABLE[23];
tmp8[4] = tmp8[2] + tmp8[3];
tmp8[5] = (float)((tmp8[2] - tmp8[3]) * INV_SQRT_2);
oo8[0] = tmp8[0] + tmp8[1];
oo8[1] = tmp8[4] + tmp8[5];
oo8[2] = (float)((tmp8[0] - tmp8[1]) * INV_SQRT_2);
oo8[3] = tmp8[5];
_out[1] = oo8[0] + oo8[1];
_out[3] = oo8[1] + oo8[2];
_out[5] = oo8[2] + oo8[3];
_out[7] = oo8[3];
}
void BuildUVec(float[] u_vec, float[] cur_synbuf, int k)
{
int i, j, uvp = 0;
for (j = 0; j < 8; j++)
{
for (i = 0; i < 16; i++)
{
/* Copy first 32 elements */
u_vec[uvp + i] = cur_synbuf[k + i + 16];
u_vec[uvp + i + 17] = -cur_synbuf[k + 31 - i];
}
/* k wraps at the synthesis buffer boundary */
k = (k + 32) & 511;
for (i = 0; i < 16; i++)
{
/* Copy next 32 elements */
u_vec[uvp + i + 32] = -cur_synbuf[k + 16 - i];
u_vec[uvp + i + 48] = -cur_synbuf[k + i];
}
u_vec[uvp + 16] = 0;
/* k wraps at the synthesis buffer boundary */
k = (k + 32) & 511;
uvp += 64;
}
}
void DewindowOutput(float[] u_vec, float[] samples)
{
for (int i = 0; i < 512; i++)
{
u_vec[i] *= DEWINDOW_TABLE[i];
}
for (int i = 0; i < 32; i++)
{
float sum = u_vec[i];
sum += u_vec[i + (1 << 5)];
sum += u_vec[i + (2 << 5)];
sum += u_vec[i + (3 << 5)];
sum += u_vec[i + (4 << 5)];
sum += u_vec[i + (5 << 5)];
sum += u_vec[i + (6 << 5)];
sum += u_vec[i + (7 << 5)];
sum += u_vec[i + (8 << 5)];
sum += u_vec[i + (9 << 5)];
sum += u_vec[i + (10 << 5)];
sum += u_vec[i + (11 << 5)];
sum += u_vec[i + (12 << 5)];
sum += u_vec[i + (13 << 5)];
sum += u_vec[i + (14 << 5)];
sum += u_vec[i + (15 << 5)];
u_vec[i] = sum;
}
for (int i = 0; i < 32; i++)
{
samples[i] = u_vec[i];
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6acdad93d9a14f41835fd290220b0c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
/*
* NLayer - A C# MPEG1/2/2.5 audio decoder
*
*/
using System;
namespace NLayer.Decoder
{
// 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
// That (of course) means we literally have no logic here
class LayerIDecoder : LayerIIDecoderBase
{
static internal bool GetCRC(MpegFrame frame, ref uint crc)
{
return LayerIIDecoderBase.GetCRC(frame, _rateTable, _allocLookupTable, false, ref crc);
}
// this is simple: all 32 subbands have a 4-bit allocations, and positive allocation values are {bits per sample} - 1
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 };
static readonly int[][] _allocLookupTable = { new int[] { 4, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 } };
internal LayerIDecoder() : base(_allocLookupTable, 1) { }
protected override int[] GetRateTable(IMpegFrame frame)
{
return _rateTable;
}
protected override void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels)
{
// this is a no-op since the base logic uses "2" as the "has energy" marker
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b219f25e94951c6468591adda1c84cff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,95 @@
/*
* NLayer - A C# MPEG1/2/2.5 audio decoder
*
*/
using System;
namespace NLayer.Decoder
{
// there's not much we have to do here... table selection, granule count, scalefactor selection
class LayerIIDecoder : LayerIIDecoderBase
{
static internal bool GetCRC(MpegFrame frame, ref uint crc)
{
return LayerIIDecoderBase.GetCRC(frame, SelectTable(frame), _allocLookupTable, true, ref crc);
}
// figure out which rate table to use... basically, high-rate full, high-rate limited, low-rate limited, low-rate minimal, and LSF.
static int[] SelectTable(IMpegFrame frame)
{
var bitRatePerChannel = (frame.BitRate / (frame.ChannelMode == MpegChannelMode.Mono ? 1 : 2)) / 1000;
if (frame.Version == MpegVersion.Version1)
{
if ((bitRatePerChannel >= 56 && bitRatePerChannel <= 80) || (frame.SampleRate == 48000 && bitRatePerChannel >= 56))
{
return _rateLookupTable[0]; // high-rate, 27 subbands
}
else if (frame.SampleRate != 48000 && bitRatePerChannel >= 96)
{
return _rateLookupTable[1]; // high-rate, 30 subbands
}
else if (frame.SampleRate != 32000 && bitRatePerChannel <= 48)
{
return _rateLookupTable[2]; // low-rate, 8 subbands
}
else
{
return _rateLookupTable[3]; // low-rate, 12 subbands
}
}
else
{
return _rateLookupTable[4]; // lsf, 30 subbands
}
}
// this table tells us which allocation lookup list to use for each subband
// note that each row has the same number of elements as there are subbands for that type...
static readonly int[][] _rateLookupTable = {
// 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
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
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
new int[] { 4, 4, 5, 5, 5, 5, 5, 5 }, // low-rate, 7 subbands
new int[] { 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }, // low-rate, 12 subbands
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
};
// this tells the decode logic: a) how many bits per allocation, and b) how many bits per sample for the give allocation value
// if negative, read -x bits and handle as a group
static readonly int[][] _allocLookupTable = {
// bits 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
new int[] { 2, 0, -5, -7, 16 }, // 0 (II)
new int[] { 3, 0, -5, -7, 3,-10, 4, 5, 16 }, // 1 (II)
new int[] { 4, 0, -5, -7, 3,-10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16 }, // 2 (II)
new int[] { 4, 0, -5, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, // 3 (II)
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)
new int[] { 3, 0, -5, -7,-10, 4, 5, 6, 9 }, // 5 (II, 4, 3 bits per alloc)
new int[] { 4, 0, -5, -7, 3,-10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, // 6 (II)
new int[] { 2, 0, -5, -7, 3 }, // 7 (II, 4, 2 bits per alloc)
};
internal LayerIIDecoder() : base(_allocLookupTable, 3) { }
protected override int[] GetRateTable(IMpegFrame frame)
{
return SelectTable(frame);
}
protected override void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels)
{
// we'll never have more than 30 active subbands
for (int sb = 0; sb < 30; sb++)
{
for (int ch = 0; ch < channels; ch++)
{
if (scfsi[ch][sb] == 2)
{
scfsi[ch][sb] = frame.ReadBits(2);
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6dc13f162ae472a4b9c4fdeedeb745ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,397 @@
/*
* NLayer - A C# MPEG1/2/2.5 audio decoder
*
*/
using System;
namespace NLayer.Decoder
{
// Layers I & II are basically identical... Layer II adds sample grouping, per subband allocation schemes, and granules
// Because of this fact, we can use the same decoder for both
abstract class LayerIIDecoderBase : LayerDecoderBase
{
protected const int SSLIMIT = 12;
static protected bool GetCRC(MpegFrame frame, int[] rateTable, int[][] allocLookupTable, bool readScfsiBits, ref uint crc)
{
// ugh... we basically have to re-implement the allocation logic here.
// keep up with how many active subbands we need to read selection info for
var scfsiBits = 0;
// only read as many subbands as we actually need; pay attention to the intensity stereo subbands
var subbandCount = rateTable.Length;
var jsbound = subbandCount;
if (frame.ChannelMode == MpegChannelMode.JointStereo)
{
jsbound = frame.ChannelModeExtension * 4 + 4;
}
// read the full stereo subbands
var channels = frame.ChannelMode == MpegChannelMode.Mono ? 1 : 2;
var sb = 0;
for (; sb < jsbound; sb++)
{
var bits = allocLookupTable[rateTable[sb]][0];
for (int ch = 0; ch < channels; ch++)
{
var alloc = frame.ReadBits(bits);
if (alloc > 0) scfsiBits += 2;
MpegFrame.UpdateCRC(alloc, bits, ref crc);
}
}
// read the intensity stereo subbands
for (; sb < subbandCount; sb++)
{
var bits = allocLookupTable[rateTable[sb]][0];
var alloc = frame.ReadBits(bits);
if (alloc > 0) scfsiBits += channels * 2;
MpegFrame.UpdateCRC(alloc, bits, ref crc);
}
// finally, read the scalefac selection bits
if (readScfsiBits)
{
while (scfsiBits >= 2)
{
MpegFrame.UpdateCRC(frame.ReadBits(2), 2, ref crc);
scfsiBits -= 2;
}
}
return true;
}
#region Lookup Tables
// this is from the formula: C = 1 / (1 / (1 << (Bits / 2 + Bits % 2 - 1)) + .5f)
// index by real bits (Bits / 2 + Bits % 2 - 1)
static readonly float[] _groupedC = { 0, 0, 1.33333333333f, 1.60000000000f, 1.77777777777f };
// these are always -0.5
// index by real bits (Bits / 2 + Bits % 2 - 1)
static readonly float[] _groupedD = { 0, 0, -0.5f, -0.5f, -0.5f };
// this is from the formula: 1 / (1 - (1f / (1 << Bits)))
// index by bits
static readonly float[] _C = {
0.00000000000f,
0.00000000000f, 1.33333333333f, 1.14285714286f, 1.06666666666f, 1.03225806452f, 1.01587301587f, 1.00787401575f, 1.00392156863f,
1.00195694716f, 1.00097751711f, 1.00048851979f, 1.00024420024f, 1.00012208522f, 1.00006103888f, 1.00003051851f, 1.00001525902f
};
// this is from the formula: 1f / (1 << Bits - 1) - 1
// index by bits
static readonly float[] _D = {
0.00000000000f - 0f,
0.00000000000f - 0f, 0.50000000000f - 1f, 0.25000000000f - 1f, 0.12500000000f - 1f, 0.062500000000f - 1f, 0.03125000000f - 1f, 0.01562500000f - 1f, 0.00781250000f - 1f,
0.00390625000f - 1f, 0.00195312500f - 1f, 0.00097656250f - 1f, 0.00048828125f - 1f, 0.000244140630f - 1f, 0.00012207031f - 1f, 0.00006103516f - 1f, 0.00003051758f - 1f
};
// this is from a (really annoying) formula:
// x = Math.Pow(4, 1 / ((2 << (idx % 3) + 1) - (idx % 3))) / (1 << (idx / 3))
// Basically...
// [0] = Math.Pow(4, 1 / 2), [1] = Math.Pow(4, 1 / 3), [2] = Math.Pow(4, 1 / 6)
// For every remaining element, calculate (in order): [idx] = [idx - 3] / 2
static readonly float[] _denormalMultiplier = {
2.00000000000000f, 1.58740105196820f, 1.25992104989487f, 1.00000000000000f, 0.79370052598410f, 0.62996052494744f, 0.50000000000000f, 0.39685026299205f,
0.31498026247372f, 0.25000000000000f, 0.19842513149602f, 0.15749013123686f, 0.12500000000000f, 0.09921256574801f, 0.07874506561843f, 0.06250000000000f,
0.04960628287401f, 0.03937253280921f, 0.03125000000000f, 0.02480314143700f, 0.01968626640461f, 0.01562500000000f, 0.01240157071850f, 0.00984313320230f,
0.00781250000000f, 0.00620078535925f, 0.00492156660115f, 0.00390625000000f, 0.00310039267963f, 0.00246078330058f, 0.00195312500000f, 0.00155019633981f,
0.00123039165029f, 0.00097656250000f, 0.00077509816991f, 0.00061519582514f, 0.00048828125000f, 0.00038754908495f, 0.00030759791257f, 0.00024414062500f,
0.00019377454248f, 0.00015379895629f, 0.00012207031250f, 0.00009688727124f, 0.00007689947814f, 0.00006103515625f, 0.00004844363562f, 0.00003844973907f,
0.00003051757813f, 0.00002422181781f, 0.00001922486954f, 0.00001525878906f, 0.00001211090890f, 0.00000961243477f, 0.00000762939453f, 0.00000605545445f,
0.00000480621738f, 0.00000381469727f, 0.00000302772723f, 0.00000240310869f, 0.00000190734863f, 0.00000151386361f, 0.00000120155435f, 0.00000095367432f
};
#endregion
int _channels, _jsbound, _granuleCount;
int[][] _allocLookupTable, _scfsi, _samples;
int[][][] _scalefac;
float[] _polyPhaseBuf;
int[][] _allocation;
protected LayerIIDecoderBase(int[][] allocLookupTable, int granuleCount)
: base()
{
_allocLookupTable = allocLookupTable;
_granuleCount = granuleCount;
_allocation = new int[][] { new int[SBLIMIT], new int[SBLIMIT] };
_scfsi = new int[][] { new int[SBLIMIT], new int[SBLIMIT] };
_samples = new int[][] { new int[SBLIMIT * SSLIMIT * _granuleCount], new int[SBLIMIT * SSLIMIT * _granuleCount] };
// NB: ReadScaleFactors(...) requires all three granules, even in Layer I
_scalefac = new int[][][] { new int[3][], new int[3][] };
for (int i = 0; i < 3; i++)
{
_scalefac[0][i] = new int[SBLIMIT];
_scalefac[1][i] = new int[SBLIMIT];
}
_polyPhaseBuf = new float[SBLIMIT];
}
internal override int DecodeFrame(IMpegFrame frame, float[] ch0, float[] ch1)
{
InitFrame(frame);
var rateTable = GetRateTable(frame);
ReadAllocation(frame, rateTable);
for (int i = 0; i < _scfsi[0].Length; i++)
{
// Since Layer II has to know which subbands have energy, we use the "Layer I valid" selection to mark that energy is present.
// That way Layer I doesn't have to do anything else.
_scfsi[0][i] = _allocation[0][i] != 0 ? 2 : -1;
_scfsi[1][i] = _allocation[1][i] != 0 ? 2 : -1;
}
ReadScaleFactorSelection(frame, _scfsi, _channels);
ReadScaleFactors(frame);
ReadSamples(frame);
return DecodeSamples(ch0, ch1);
}
// this just reads the channel mode and set a few flags
void InitFrame(IMpegFrame frame)
{
switch (frame.ChannelMode)
{
case MpegChannelMode.Mono:
_channels = 1;
_jsbound = SBLIMIT;
break;
case MpegChannelMode.JointStereo:
_channels = 2;
_jsbound = frame.ChannelModeExtension * 4 + 4;
break;
default:
_channels = 2;
_jsbound = SBLIMIT;
break;
}
}
abstract protected int[] GetRateTable(IMpegFrame frame);
void ReadAllocation(IMpegFrame frame, int[] rateTable)
{
var _subBandCount = rateTable.Length;
if (_jsbound > _subBandCount) _jsbound = _subBandCount;
Array.Clear(_allocation[0], 0, SBLIMIT);
Array.Clear(_allocation[1], 0, SBLIMIT);
int sb = 0;
for (; sb < _jsbound; sb++)
{
var table = _allocLookupTable[rateTable[sb]];
var bits = table[0];
for (int ch = 0; ch < _channels; ch++)
{
_allocation[ch][sb] = table[frame.ReadBits(bits) + 1];
}
}
for (; sb < _subBandCount; sb++)
{
var table = _allocLookupTable[rateTable[sb]];
_allocation[0][sb] = _allocation[1][sb] = table[frame.ReadBits(table[0]) + 1];
}
}
abstract protected void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels);
void ReadScaleFactors(IMpegFrame frame)
{
for (int sb = 0; sb < SBLIMIT; sb++)
{
for (int ch = 0; ch < _channels; ch++)
{
switch (_scfsi[ch][sb])
{
case 0:
// all three
_scalefac[ch][0][sb] = frame.ReadBits(6);
_scalefac[ch][1][sb] = frame.ReadBits(6);
_scalefac[ch][2][sb] = frame.ReadBits(6);
break;
case 1:
// only two (2 = 1)
_scalefac[ch][0][sb] =
_scalefac[ch][1][sb] = frame.ReadBits(6);
_scalefac[ch][2][sb] = frame.ReadBits(6);
break;
case 2:
// only one (3 = 2 = 1)
_scalefac[ch][0][sb] =
_scalefac[ch][1][sb] =
_scalefac[ch][2][sb] = frame.ReadBits(6);
break;
case 3:
// only two (3 = 2)
_scalefac[ch][0][sb] = frame.ReadBits(6);
_scalefac[ch][1][sb] =
_scalefac[ch][2][sb] = frame.ReadBits(6);
break;
default:
// none
_scalefac[ch][0][sb] = 63;
_scalefac[ch][1][sb] = 63;
_scalefac[ch][2][sb] = 63;
break;
}
}
}
}
void ReadSamples(IMpegFrame frame)
{
// load in all the data for this frame (1152 samples in this case)
// NB: we flatten these into output order
for (int ss = 0, idx = 0; ss < SSLIMIT; ss++, idx += SBLIMIT * (_granuleCount - 1))
{
for (int sb = 0; sb < SBLIMIT; sb++, idx++)
{
for (int ch = 0; ch < _channels; ch++)
{
if (ch == 0 || sb < _jsbound)
{
var alloc = _allocation[ch][sb];
if (alloc != 0)
{
if (alloc < 0)
{
// grouping (Layer II only, so we don't have to play with the granule count)
var val = frame.ReadBits(-alloc);
var levels = (1 << (-alloc / 2 + -alloc % 2 - 1)) + 1;
_samples[ch][idx] = val % levels;
val /= levels;
_samples[ch][idx + SBLIMIT] = val % levels;
_samples[ch][idx + SBLIMIT * 2] = val / levels;
}
else
{
// non-grouping
for (int gr = 0; gr < _granuleCount; gr++)
{
_samples[ch][idx + SBLIMIT * gr] = frame.ReadBits(alloc);
}
}
}
else
{
// no energy... zero out the samples
for (int gr = 0; gr < _granuleCount; gr++)
{
_samples[ch][idx + SBLIMIT * gr] = 0;
}
}
}
else
{
// copy chan 0 to chan 1
for (int gr = 0; gr < _granuleCount; gr++)
{
_samples[1][idx + SBLIMIT * gr] = _samples[0][idx + SBLIMIT * gr];
}
}
}
}
}
}
int DecodeSamples(float[] ch0, float[] ch1)
{
// do our stereo mode setup
var chanBufs = new float[2][];
var startChannel = 0;
var endChannel = _channels - 1;
if (_channels == 1 || StereoMode == StereoMode.LeftOnly)
{
chanBufs[0] = ch0;
endChannel = 0;
}
else if (StereoMode == StereoMode.RightOnly)
{
chanBufs[1] = ch0; // this is correct... if there's only a single channel output, it goes in channel 0's buffer
startChannel = 1;
}
else // MpegStereoMode.Both or StereoMode.DownmixToMono
{
chanBufs[0] = ch0;
chanBufs[1] = ch1;
}
int idx = 0;
for (int ch = startChannel; ch <= endChannel; ch++)
{
idx = 0;
for (int gr = 0; gr < _granuleCount; gr++)
{
for (int ss = 0; ss < SSLIMIT; ss++)
{
for (int sb = 0; sb < SBLIMIT; sb++, idx++)
{
// do the dequant and the denorm; output to _polyPhaseBuf
// NB: Layers I & II use the same algorithm here... Grouping changes the bit counts, but doesn't change the algo
// - Up to 65534 possible values (65535 does not appear to be usable)
// - All values can be handled with 16-bit logic as long as the correct C and D constants are used
// - Make sure to normalize each sample to 16 bits!
var alloc = _allocation[ch][sb];
if (alloc != 0)
{
float[] c, d;
if (alloc < 0)
{
alloc = -alloc / 2 + -alloc % 2 - 1;
c = _groupedC;
d = _groupedD;
}
else
{
c = _C;
d = _D;
}
// read sample; normalize, scale & center to [-0.999984741f..0.999984741f]; apply scalefactor
_polyPhaseBuf[sb] = c[alloc] * ((_samples[ch][idx] << (16 - alloc)) / 32768f + d[alloc]) * _denormalMultiplier[_scalefac[ch][gr][sb]];
}
else
{
// no transmitted energy...
_polyPhaseBuf[sb] = 0f;
}
}
// do the polyphase output for this channel, section, and granule
base.InversePolyPhase(ch, _polyPhaseBuf);
Array.Copy(_polyPhaseBuf, 0, chanBufs[ch], idx - SBLIMIT, SBLIMIT);
}
}
}
if (_channels == 2 && StereoMode == NLayer.StereoMode.DownmixToMono)
{
for (int i = 0; i < idx; i++)
{
ch0[i] = (ch0[i] + ch1[i]) / 2;
}
}
return idx;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0edfa5e3af494c1438f3141a1392b091
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 166e05065bf611649939e09ca44d4cfc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,630 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace NLayer.Decoder
{
class MpegFrame : FrameBase, IMpegFrame
{
static readonly int[][][] _bitRateTable =
{
new int[][]
{
new int[] { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 },
new int[] { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 },
new int[] { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }
},
new int[][]
{
new int[] { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 },
new int[] { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 },
new int[] { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }
},
};
internal static MpegFrame TrySync(uint syncMark)
{
if ((syncMark & 0xFFE00000) == 0xFFE00000 // frame sync
&& (syncMark & 0x00180000) != 0x00080000 // MPEG version != reserved
&& (syncMark & 0x00060000) != 0x00000000 // layer version != reserved
&& (syncMark & 0x0000F000) != 0x0000F000 // bitrate != bad
&& (syncMark & 0x00000C00) != 0x00000C00 // sample rate != reserved
)
{
// now check the stereo modes
switch ((syncMark >> 4) & 0xF)
{
case 0x0: // stereo
case 0x4: // joint stereo
case 0x5: // "
case 0x6: // "
case 0x7: // "
case 0x8: // dual channel
case 0xC: // mono
return new MpegFrame { _syncBits = (int)syncMark };
}
}
return null;
}
// base: 2xIntPtr, 1x8, 1x4
// total: 3xIntPtr, 4x8, 4x4
// Long Mode: 72 bytes per instance
// Prot Mode: 60 bytes per instance
// IntPtr
internal MpegFrame Next;
// 4
internal int Number;
// 4
int _syncBits;
// 8
int _readOffset, _bitsRead;
// 8
ulong _bitBucket = 0UL;
// 8
long _offset;
// 4
bool _isMuted;
MpegFrame()
{
}
protected override int Validate()
{
// TrySync has already validated version, layer, bitrate, and samplerate
// if layer2, we have to verify the channel mode is valid for the bitrate selected
if (Layer == MpegLayer.LayerII)
{
switch (BitRate)
{
case 32000:
case 48000:
case 56000:
case 80000:
// don't allow anything except mono
if (ChannelMode != MpegChannelMode.Mono) return -1;
break;
case 224000:
case 256000:
case 320000:
case 384000:
// don't allow mono
if (ChannelMode == MpegChannelMode.Mono) return -1;
break;
}
}
// calculate the frame's length
int frameSize;
if (BitRateIndex > 0)
{
if (Layer == MpegLayer.LayerI)
{
frameSize = (12 * BitRate / SampleRate + Padding) * 4;
}
else
{
frameSize = 144 * BitRate / SampleRate + Padding;
}
}
else
{
// "free" frame... we have to calculate it later
frameSize = _readOffset + GetSideDataSize() + Padding; // we know the frame will be at least this big...
}
// now check the crc if one is present
if (HasCrc)
{
// prep for CRC reading
_readOffset = 4 + (HasCrc ? 2 : 0);
// check the CRC
if (!ValidateCRC())
{
// mute this frame
_isMuted = true;
return 6; // header + crc... force the reader to re-sync
}
}
// prep for reading
Reset();
// finally, let our caller know how big this frame is (including the sync header)
return frameSize;
}
internal int GetSideDataSize()
{
switch (Layer)
{
case MpegLayer.LayerI:
if (ChannelMode == MpegChannelMode.Mono)
{
// mono
return 16;
}
else if (ChannelMode == MpegChannelMode.Stereo || ChannelMode == MpegChannelMode.DualChannel)
{
// full stereo / dual channel
return 32;
}
else
{
// joint stereo... ugh...
switch (ChannelModeExtension)
{
case 0:
return 18;
case 1:
return 20;
case 2:
return 22;
case 3:
return 24;
}
}
break;
case MpegLayer.LayerII:
return 0;
case MpegLayer.LayerIII:
if (ChannelMode == MpegChannelMode.Mono && Version >= MpegVersion.Version2)
{
return 9;
}
else if (ChannelMode != MpegChannelMode.Mono && Version < MpegVersion.Version2)
{
return 32;
}
else
{
return 17;
}
}
return 0;
}
bool ValidateCRC()
{
var crc = 0xFFFFU;
// process the common bits...
UpdateCRC(_syncBits, 16, ref crc);
var apply = false;
switch (Layer)
{
case MpegLayer.LayerI:
apply = LayerIDecoder.GetCRC(this, ref crc);
break;
case MpegLayer.LayerII:
apply = LayerIIDecoder.GetCRC(this, ref crc);
break;
case MpegLayer.LayerIII:
apply = LayerIIIDecoder.GetCRC(this, ref crc);
break;
}
if (apply)
{
var checkCrc = ReadByte(4) << 8 | ReadByte(5);
return checkCrc == crc;
}
return true;
}
static internal void UpdateCRC(int data, int length, ref uint crc)
{
var masking = 1U << length;
while ((masking >>= 1) != 0)
{
var carry = crc & 0x8000;
crc <<= 1;
if ((carry == 0) ^ ((data & masking) == 0))
{
crc ^= 0x8005;
}
}
crc &= 0xFFFF;
}
internal VBRInfo ParseVBR()
{
var buf = new byte[4];
// Xing first
int offset;
if (Version == MpegVersion.Version1 && ChannelMode != MpegChannelMode.Mono)
{
offset = 32 + 4;
}
else if (Version > MpegVersion.Version1 && ChannelMode == MpegChannelMode.Mono)
{
offset = 9 + 4;
}
else
{
offset = 17 + 4;
}
if (Read(offset, buf) != 4) return null;
if (buf[0] == 'X' && buf[1] == 'i' && buf[2] == 'n' && buf[3] == 'g'
|| buf[0] == 'I' && buf[1] == 'n' && buf[2] == 'f' && buf[3] == 'o')
{
return ParseXing(offset + 4);
}
// then VBRI (kinda rare)
if (Read(36, buf) != 4) return null;
if (buf[0] == 'V' && buf[1] == 'B' && buf[2] == 'R' && buf[3] == 'I')
{
return ParseVBRI();
}
return null;
}
VBRInfo ParseXing(int offset)
{
VBRInfo info = new VBRInfo();
info.Channels = Channels;
info.SampleRate = SampleRate;
info.SampleCount = SampleCount;
var buf = new byte[100];
if (Read(offset, buf, 0, 4) != 4) return null;
offset += 4;
var flags = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
// frame count
if ((flags & 0x1) != 0)
{
if (Read(offset, buf, 0, 4) != 4) return null;
offset += 4;
info.VBRFrames = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
// byte count
if ((flags & 0x2) != 0)
{
if (Read(offset, buf, 0, 4) != 4) return null;
offset += 4;
info.VBRBytes = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
// TOC
if ((flags & 0x4) != 0)
{
// we're not using the TOC, so just discard it
if (Read(offset, buf) != 100) return null;
offset += 100;
}
// scale
if ((flags & 0x8) != 0)
{
if (Read(offset, buf, 0, 4) != 4) return null;
offset += 4;
info.VBRQuality = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
//// now look for a LAME header (note: if it isn't found, it doesn't fail the VBR parse)
//do
//{
// if (Read(offset, buf, 0, 20) != 20) break;
// offset += 20;
// // LAME tag revision: only 0 and 1 are valid
// if ((buf[9] & 0xF0) > 0x10) break;
// // VBR mode: 0-6, 8 & 9 are valid
// var mode = buf[9] & 0xF;
// if (mode == 7 || mode > 9) break;
// // Lowpass filter value
// var lowpass = buf[10] / 100.0;
// // Replay Gain
// var rgPeak = BitConverter.ToSingle(buf, 11);
// var rgGainRadio = buf[15] << 8 | buf[16];
// var rgGain = buf[17] << 8 | buf[18];
//} while (false);
return info;
}
VBRInfo ParseVBRI()
{
VBRInfo info = new VBRInfo();
info.Channels = Channels;
info.SampleRate = SampleRate;
info.SampleCount = SampleCount;
// VBRI is "fixed" size... Yay. :)
var buf = new byte[26];
if (Read(36, buf) != 26) return null;
// Version
var version = buf[4] << 8 | buf[5];
// Delay
info.VBRDelay = buf[6] << 8 | buf[7];
// Quality
info.VBRQuality = buf[8] << 8 | buf[9];
// Bytes
info.VBRBytes = buf[10] << 24 | buf[11] << 16 | buf[12] << 8 | buf[13];
// Frames
info.VBRFrames = buf[14] << 24 | buf[15] << 16 | buf[16] << 8 | buf[17];
// TOC
// entries
var tocEntries = buf[18] << 8 | buf[19];
var tocScale = buf[20] << 8 | buf[21];
var tocEntrySize = buf[22] << 8 | buf[23];
var tocFramesPerEntry = buf[24] << 8 | buf[25];
var tocSize = tocEntries * tocEntrySize;
var toc = new byte[tocSize];
if (Read(62, toc) != tocSize) return null;
return info;
}
public int FrameLength
{
get { return base.Length; }
}
public MpegVersion Version
{
get
{
switch ((_syncBits >> 19) & 3)
{
case 0:
return MpegVersion.Version25;
case 2:
return MpegVersion.Version2;
case 3:
return MpegVersion.Version1;
}
return MpegVersion.Unknown;
}
}
public MpegLayer Layer
{
get
{
// the order is backwards, and "0" is invalid
return (MpegLayer)((4 - ((_syncBits >> 17) & 3)) % 4);
}
}
public bool HasCrc
{
get { return (_syncBits & 0x10000) == 0; }
}
public int BitRate
{
get
{
if (BitRateIndex > 0)
{
return _bitRateTable[(int)Version / 10 - 1][(int)Layer - 1][BitRateIndex] * 1000;
}
else
{
// bitrate is always an even multiple of 1000, so round
return ((((FrameLength * 8) * SampleRate) / SampleCount + 499) + 500) / 1000 * 1000;
}
}
}
public int BitRateIndex
{
get { return (_syncBits >> 12) & 0xF; }
}
public int SampleRate
{
get
{
int sr;
switch (SampleRateIndex)
{
case 0: sr = 44100; break;
case 1: sr = 48000; break;
case 2: sr = 32000; break;
default: sr = 0; break;
}
if (Version > MpegVersion.Version1)
{
if (Version == MpegVersion.Version25)
{
sr /= 4;
}
else
{
sr /= 2;
}
}
return sr;
}
}
public int SampleRateIndex
{
get { return (_syncBits >> 10) & 0x3; }
}
private int Padding
{
get { return (_syncBits >> 9) & 0x1; }
}
public MpegChannelMode ChannelMode
{
get { return (MpegChannelMode)((_syncBits >> 6) & 0x3); }
}
public int ChannelModeExtension
{
get { return (_syncBits >> 4) & 0x3; }
}
internal int Channels
{
get { return (ChannelMode == MpegChannelMode.Mono ? 1 : 2); }
}
public bool IsCopyrighted
{
get { return (_syncBits & 0x8) == 0x8; }
}
internal bool IsOriginal
{
get { return (_syncBits & 0x4) == 0x4; }
}
internal int EmphasisMode
{
get { return (_syncBits & 0x3); }
}
public bool IsCorrupted
{
get { return _isMuted; }
}
public int SampleCount
{
get
{
if (Layer == MpegLayer.LayerI) return 384;
if (Layer == MpegLayer.LayerIII && Version > MpegVersion.Version1) return 576;
return 1152;
}
}
internal long SampleOffset
{
get { return _offset; }
set { _offset = value; }
}
public void Reset()
{
_readOffset = 4 + (HasCrc ? 2 : 0);
_bitBucket = 0UL;
_bitsRead = 0;
}
public int ReadBits(int bitCount)
{
if (bitCount < 1 || bitCount > 32) throw new ArgumentOutOfRangeException("bitCount");
if (_isMuted) return 0;
while (_bitsRead < bitCount)
{
var b = ReadByte(_readOffset);
if (b == -1) throw new System.IO.EndOfStreamException();
++_readOffset;
_bitBucket <<= 8;
_bitBucket |= (byte)(b & 0xFF);
_bitsRead += 8;
}
var temp = (int)((_bitBucket >> (_bitsRead - bitCount)) & ((1UL << bitCount) - 1));
_bitsRead -= bitCount;
return temp;
}
#if DEBUG
public override string ToString()
{
// version
var sb = new StringBuilder("MPEG");
switch (Version)
{
case MpegVersion.Version1: sb.Append("1"); break;
case MpegVersion.Version2: sb.Append("2"); break;
case MpegVersion.Version25: sb.Append("2.5"); break;
}
// layer
sb.Append(" Layer ");
switch (Layer)
{
case MpegLayer.LayerI: sb.Append("I"); break;
case MpegLayer.LayerII: sb.Append("II"); break;
case MpegLayer.LayerIII: sb.Append("III"); break;
}
// bitrate
sb.AppendFormat(" {0} kbps ", BitRate / 1000);
// channel mode
switch (ChannelMode)
{
case MpegChannelMode.Stereo:
sb.Append("Stereo");
break;
case MpegChannelMode.JointStereo:
sb.Append("Joint Stereo");
switch (ChannelModeExtension)
{
case 1: sb.Append(" (I)"); break;
case 2: sb.Append(" (M/S)"); break;
case 3: sb.Append(" (M/S,I)"); break;
}
break;
case MpegChannelMode.DualChannel:
sb.Append("Dual Channel");
break;
case MpegChannelMode.Mono:
sb.Append("Mono");
break;
}
// sample rate
sb.AppendFormat(" {0} KHz", (float)SampleRate / 1000);
var flagList = new List<string>();
// protection
if (HasCrc) flagList.Add("CRC");
// copyright
if (IsCopyrighted) flagList.Add("Copyright");
// original
if (IsOriginal) flagList.Add("Original");
// emphasis
switch (EmphasisMode)
{
case 1:
flagList.Add("50/15 ms");
break;
case 2:
flagList.Add("Invalid Emphasis");
break;
case 3:
flagList.Add("CCIT J.17");
break;
}
if (flagList.Count > 0)
{
sb.AppendFormat(" ({0})", string.Join(",", flagList.ToArray()));
}
return sb.ToString();
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e57cfee9f5052d14e9107fde87d08a77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,718 @@
using System;
using System.IO;
namespace NLayer.Decoder
{
class MpegStreamReader
{
ID3Frame _id3Frame, _id3v1Frame;
RiffHeaderFrame _riffHeaderFrame;
VBRInfo _vbrInfo;
MpegFrame _first, _current, _last, _lastFree;
long _readOffset, _eofOffset;
Stream _source;
bool _canSeek, _endFound, _mixedFrameSize;
object _readLock = new object();
object _frameLock = new object();
internal MpegStreamReader(Stream source)
{
_source = source;
_canSeek = source.CanSeek;
_readOffset = 0L;
_eofOffset = long.MaxValue;
// find the first Mpeg frame
var frame = FindNextFrame();
while (frame != null && !(frame is MpegFrame))
{
frame = FindNextFrame();
}
// if we still don't have a frame, we never sync'ed
if (frame == null) throw new InvalidDataException("Not a valid MPEG file!");
// the very next frame "should be" an mpeg frame
frame = FindNextFrame();
// if not, it's not a valid file
if (frame == null || !(frame is MpegFrame)) throw new InvalidDataException("Not a valid MPEG file!");
// seek to the first frame
_current = _first;
}
FrameBase FindNextFrame()
{
// if we've found the end, don't bother looking for anything else
if (_endFound) return null;
var freeFrame = _lastFree;
var lastFrameStart = _readOffset;
lock (_frameLock)
{
// read 3 bytes
var syncBuf = new byte[4];
try
{
if (Read(_readOffset, syncBuf, 0, 4) == 4)
{
// now loop until a frame is found
do
{
var sync = (uint)(syncBuf[0] << 24 | syncBuf[1] << 16 | syncBuf[2] << 8 | syncBuf[3]);
lastFrameStart = _readOffset;
// try ID3 first (for v2 frames)
if (_id3Frame == null)
{
var f = ID3Frame.TrySync(sync);
if (f != null)
{
if (f.Validate(_readOffset, this))
{
if (!_canSeek) f.SaveBuffer();
_readOffset += f.Length;
DiscardThrough(_readOffset, true);
return _id3Frame = f;
}
}
}
// now look for a RIFF header
if (_first == null && _riffHeaderFrame == null)
{
var f = RiffHeaderFrame.TrySync(sync);
if (f != null)
{
if (f.Validate(_readOffset, this))
{
_readOffset += f.Length;
DiscardThrough(_readOffset, true);
return _riffHeaderFrame = f;
}
}
}
// finally, just try for an MPEG frame
var frame = MpegFrame.TrySync(sync);
if (frame != null)
{
if (frame.Validate(_readOffset, this)
&& !(freeFrame != null
&& (frame.Layer != freeFrame.Layer
|| frame.Version != freeFrame.Version
|| frame.SampleRate != freeFrame.SampleRate
|| frame.BitRateIndex > 0
)
)
)
{
if (!_canSeek)
{
frame.SaveBuffer();
DiscardThrough(_readOffset + frame.FrameLength, true);
}
_readOffset += frame.FrameLength;
if (_first == null)
{
if (_vbrInfo == null && (_vbrInfo = frame.ParseVBR()) != null)
{
return FindNextFrame();
}
else
{
frame.Number = 0;
_first = _last = frame;
}
}
else
{
if (frame.SampleCount != _first.SampleCount)
{
_mixedFrameSize = true;
}
frame.SampleOffset = _last.SampleCount + _last.SampleOffset;
frame.Number = _last.Number + 1;
_last = (_last.Next = frame);
}
if (frame.BitRateIndex == 0)
{
_lastFree = frame;
}
return frame;
}
}
// if we've read MPEG frames and can't figure out what frame type we have, try looking for a new ID3 tag
if (_last != null)
{
var f = ID3Frame.TrySync(sync);
if (f != null)
{
if (f.Validate(_readOffset, this))
{
if (!_canSeek) f.SaveBuffer();
// if it's a v1 tag, go ahead and parse it
if (f.Version == 1)
{
_id3v1Frame = f;
}
else
{
// grrr... the ID3 2.4 spec says tags can be anywhere in the file and that later tags can override earlier ones... boo
_id3Frame.Merge(f);
}
_readOffset += f.Length;
DiscardThrough(_readOffset, true);
return f;
}
}
}
// well, we didn't find anything, so rinse and repeat with the next byte
++_readOffset;
if (_first == null || !_canSeek) DiscardThrough(_readOffset, true);
Buffer.BlockCopy(syncBuf, 1, syncBuf, 0, 3);
} while (Read(_readOffset + 3, syncBuf, 3, 1) == 1);
}
// move the "end of frame" marker for the last free format frame (in case we have one)
// this is because we don't include the last four bytes otherwise
lastFrameStart += 4;
_endFound = true;
return null;
}
finally
{
if (freeFrame != null)
{
freeFrame.Length = (int)(lastFrameStart - freeFrame.Offset);
if (!_canSeek)
{
// gotta finish filling the buffer!!
throw new InvalidOperationException("Free frames cannot be read properly from forward-only streams!");
}
// if _lastFree hasn't changed (we got a non-MPEG frame), clear it out
if (_lastFree == freeFrame)
{
_lastFree = null;
}
}
}
}
}
class ReadBuffer
{
public byte[] Data;
public long BaseOffset;
public int End;
public int DiscardCount;
object _localLock = new object();
public ReadBuffer(int initialSize)
{
initialSize = 2 << (int)Math.Log(initialSize, 2);
Data = new byte[initialSize];
}
public int Read(MpegStreamReader reader, long offset, byte[] buffer, int index, int count)
{
lock (_localLock)
{
var startIdx = EnsureFilled(reader, offset, ref count);
Buffer.BlockCopy(Data, startIdx, buffer, index, count);
}
return count;
}
public int ReadByte(MpegStreamReader reader, long offset)
{
lock (_localLock)
{
var count = 1;
var startIdx = EnsureFilled(reader, offset, ref count);
if (count == 1)
{
return Data[startIdx];
}
}
return -1;
}
int EnsureFilled(MpegStreamReader reader, long offset, ref int count)
{
// if the offset & count are inside our buffer's range, just return the appropriate index
var startIdx = (int)(offset - BaseOffset);
int endIdx = startIdx + count;
if (startIdx < 0 || endIdx > End)
{
int readStart = 0, readCount = 0, moveCount = 0;
long readOffset = 0;
#region Decision-Making
if (startIdx < 0)
{
// if we can't seek, there's nothing we can do
if (!reader._source.CanSeek) throw new InvalidOperationException("Cannot seek backwards on a forward-only stream!");
// if there's data in the buffer, try to keep it (up to doubling the buffer size)
if (End > 0)
{
// if doubling the buffer would push it past the max size, don't check it
if ((startIdx + Data.Length > 0) || (Data.Length * 2 <= 16384 && startIdx + Data.Length * 2 > 0))
{
endIdx = End;
}
}
// we know we'll have to start reading here
readOffset = offset;
// if the end of the request is before the start of our buffer...
if (endIdx < 0)
{
// ... just truncate and move on
Truncate();
// set up our read parameters
BaseOffset = offset;
startIdx = 0;
endIdx = count;
// how much do we need to read?
readCount = count;
}
else // i.e., endIdx >= 0
{
// we have overlap with existing data... save as much as possible
moveCount = -endIdx;
readCount = -startIdx;
}
}
else // i.e., startIdx >= 0
{
// we only get to here if at least one byte of the request is past the end of the read data
// start with the simplest scenario and work our way up
// 1) We just need to fill the buffer a bit more
if (endIdx < Data.Length)
{
readCount = endIdx - End;
readStart = End;
readOffset = BaseOffset + readStart;
}
// 2) We need to discard some bytes, then fill the buffer
else if (endIdx - DiscardCount < Data.Length)
{
moveCount = DiscardCount;
readStart = End;
readCount = endIdx - readStart;
readOffset = BaseOffset + readStart;
}
// 3) We need to expand the buffer to hold all the existing & requested data
else if (Data.Length * 2 <= 16384)
{
// by definition, we discard
moveCount = DiscardCount;
readStart = End;
readCount = endIdx - End;
readOffset = BaseOffset + readStart;
}
// 4) We have to throw away some data that hasn't been discarded
else
{
// just truncate
Truncate();
// set up our read parameters
BaseOffset = offset;
readOffset = offset;
startIdx = 0;
endIdx = count;
// how much do we have to read?
readCount = count;
}
}
#endregion
#region Buffer Resizing & Data Moving
if (endIdx - moveCount > Data.Length || readStart + readCount - moveCount > Data.Length)
{
var newSize = Data.Length * 2;
while (newSize < endIdx - moveCount)
{
newSize *= 2;
}
var newBuf = new byte[newSize];
if (moveCount < 0)
{
// reverse copy
Buffer.BlockCopy(Data, 0, newBuf, -moveCount, End + moveCount);
DiscardCount = 0;
}
else
{
// forward or neutral copy
Buffer.BlockCopy(Data, moveCount, newBuf, 0, End - moveCount);
DiscardCount -= moveCount;
}
Data = newBuf;
}
else if (moveCount != 0)
{
if (moveCount > 0)
{
// forward move
Buffer.BlockCopy(Data, moveCount, Data, 0, End - moveCount);
DiscardCount -= moveCount;
}
else
{
// backward move
for (int i = 0, srcIdx = Data.Length - 1, destIdx = Data.Length - 1 - moveCount; i < moveCount; i++, srcIdx--, destIdx--)
{
Data[destIdx] = Data[srcIdx];
}
DiscardCount = 0;
}
}
BaseOffset += moveCount;
readStart -= moveCount;
startIdx -= moveCount;
endIdx -= moveCount;
End -= moveCount;
#endregion
#region Buffer Filling
lock (reader._readLock)
{
if (readCount > 0 && reader._source.Position != readOffset && readOffset < reader._eofOffset)
{
if (reader._canSeek)
{
try
{
reader._source.Position = readOffset;
}
catch (EndOfStreamException)
{
reader._eofOffset = reader._source.Length;
readCount = 0;
}
}
else
{
// ugh, gotta read bytes until we've reached the desired offset
var seekCount = readOffset - reader._source.Position;
while (--seekCount >= 0)
{
if (reader._source.ReadByte() == -1)
{
reader._eofOffset = reader._source.Position;
readCount = 0;
break;
}
}
}
}
while (readCount > 0 && readOffset < reader._eofOffset)
{
var temp = reader._source.Read(Data, readStart, readCount);
if (temp == 0)
{
break;
}
readStart += temp;
readOffset += temp;
readCount -= temp;
}
if (readStart > End)
{
End = readStart;
}
if (End < endIdx)
{
// we didn't get a full read...
count = Math.Max(0, End - startIdx);
}
// NB: if desired, switch to "minimal reads" by commenting-out this clause
else if (End < Data.Length)
{
// try to finish filling the buffer
var temp = reader._source.Read(Data, End, Data.Length - End);
End += temp;
}
}
#endregion
}
return startIdx;
}
public void DiscardThrough(long offset)
{
lock (_localLock)
{
var count = (int)(offset - BaseOffset);
DiscardCount = Math.Max(count, DiscardCount);
if (DiscardCount >= Data.Length) CommitDiscard();
}
}
void Truncate()
{
End = 0;
DiscardCount = 0;
}
void CommitDiscard()
{
if (DiscardCount >= Data.Length || DiscardCount >= End)
{
// we have been told to discard the entire buffer
BaseOffset += DiscardCount;
End = 0;
}
else
{
// just discard the first part...
//Array.Copy(_readBuf, _readBufDiscardCount, _readBuf, 0, _readBufEnd - _readBufDiscardCount);
Buffer.BlockCopy(Data, DiscardCount, Data, 0, End - DiscardCount);
BaseOffset += DiscardCount;
End -= DiscardCount;
}
DiscardCount = 0;
}
}
ReadBuffer _readBuf = new ReadBuffer(2048);
internal int Read(long offset, byte[] buffer, int index, int count)
{
// make sure the offset is at least positive
if (offset < 0L) throw new ArgumentOutOfRangeException("offset");
// make sure the buffer is valid
if (index < 0 || index + count > buffer.Length) throw new ArgumentOutOfRangeException("index");
return _readBuf.Read(this, offset, buffer, index, count);
}
internal int ReadByte(long offset)
{
if (offset < 0L) throw new ArgumentOutOfRangeException("offset");
return _readBuf.ReadByte(this, offset);
}
internal void DiscardThrough(long offset, bool minimalRead)
{
_readBuf.DiscardThrough(offset);
}
internal void ReadToEnd()
{
try
{
var maxAllocation = 40000;
if (_id3Frame != null)
{
maxAllocation += _id3Frame.Length;
}
while (!_endFound)
{
FindNextFrame();
while (!_canSeek && FrameBase.TotalAllocation >= maxAllocation)
{
#if NET35
System.Threading.Thread.Sleep(500);
#else
System.Threading.Tasks.Task.Delay(500).Wait(); //
#endif
}
}
}
catch (ObjectDisposedException)
{
// in case the stream was disposed before we finished...
}
}
internal bool CanSeek
{
get { return _canSeek; }
}
internal long SampleCount
{
get
{
if (_vbrInfo != null) return _vbrInfo.VBRStreamSampleCount;
if (!_canSeek) return -1;
ReadToEnd();
return _last.SampleCount + _last.SampleOffset;
}
}
internal int SampleRate
{
get
{
if (_vbrInfo != null) return _vbrInfo.SampleRate;
return _first.SampleRate;
}
}
internal int Channels
{
get
{
if (_vbrInfo != null) return _vbrInfo.Channels;
return _first.Channels;
}
}
internal int FirstFrameSampleCount
{
get { return (_first != null ? _first.SampleCount : 0); }
}
internal long SeekTo(long sampleNumber)
{
if (!_canSeek) throw new InvalidOperationException("Cannot seek!");
// first try to "seek" by calculating the frame number
var cnt = (int)(sampleNumber / _first.SampleCount);
var frame = _first;
if (_current != null && _current.Number <= cnt && _current.SampleOffset <= sampleNumber)
{
// if this fires, we can short-circuit things a bit...
frame = _current;
cnt -= frame.Number;
}
while (!_mixedFrameSize && --cnt >= 0 && frame != null)
{
// make sure we have more frames to look at
if (frame == _last && !_endFound)
{
do
{
FindNextFrame();
} while (frame == _last && !_endFound);
}
// if we've found a different frame size, fall through...
if (_mixedFrameSize)
{
break;
}
frame = frame.Next;
}
// this should not run unless we found mixed frames...
while (frame != null && frame.SampleOffset + frame.SampleCount < sampleNumber)
{
if (frame == _last && !_endFound)
{
do
{
FindNextFrame();
} while (frame == _last && !_endFound);
}
frame = frame.Next;
}
if (frame == null) return -1;
return (_current = frame).SampleOffset;
}
internal MpegFrame NextFrame()
{
// if _current is null, we've returned the last frame already
var frame = _current;
if (frame != null)
{
if (_canSeek)
{
frame.SaveBuffer();
DiscardThrough(frame.Offset + frame.FrameLength, false);
}
if (frame == _last && !_endFound)
{
do
{
FindNextFrame();
} while (frame == _last && !_endFound);
}
_current = frame.Next;
if (!_canSeek)
{
// if we're in a forward-only stream, don't bother keeping the frames that have already been processed
lock (_frameLock)
{
var temp = _first;
_first = temp.Next;
temp.Next = null;
}
}
}
return frame;
}
internal MpegFrame GetCurrentFrame()
{
return _current;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 310b96f197b6c73448981d66c46231db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NLayer.Decoder
{
/// <summary>
/// RIFF header reader
/// </summary>
class RiffHeaderFrame : FrameBase
{
internal static RiffHeaderFrame TrySync(uint syncMark)
{
if (syncMark == 0x52494646U)
{
return new RiffHeaderFrame();
}
return null;
}
RiffHeaderFrame()
{
}
protected override int Validate()
{
var buf = new byte[4];
// we expect this to be the "WAVE" chunk
if (Read(8, buf) != 4) return -1;
if (buf[0] != 'W' || buf[1] != 'A' || buf[2] != 'V' || buf[3] != 'E') return -1;
// now the "fmt " chunk
if (Read(12, buf) != 4) return -1;
if (buf[0] != 'f' || buf[1] != 'm' || buf[2] != 't' || buf[3] != ' ') return -1;
// we've found the fmt chunk, so look for the data chunk
var offset = 16;
while (true)
{
// read the length and seek forward
if (Read(offset, buf) != 4) return -1;
offset += 4 + BitConverter.ToInt32(buf, 0);
// get the chunk ID
if (Read(offset, buf) != 4) return -1;
offset += 4;
// if it's not the data chunk, try again
if (buf[0] == 'd' && buf[1] == 'a' && buf[2] == 't' && buf[3] == 'a') break;
}
// ... and now we know exactly where the frame ends
return offset + 4;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 235a0e0ad3c05444586c72bfea1bb911
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
namespace NLayer.Decoder
{
class VBRInfo
{
internal VBRInfo() { }
internal int SampleCount { get; set; }
internal int SampleRate { get; set; }
internal int Channels { get; set; }
internal int VBRFrames { get; set; }
internal int VBRBytes { get; set; }
internal int VBRQuality { get; set; }
internal int VBRDelay { get; set; }
internal long VBRStreamSampleCount
{
get
{
// we assume the entire stream is consistent wrt samples per frame
return VBRFrames * SampleCount;
}
}
internal int VBRAverageBitrate
{
get
{
return (int)((VBRBytes / (VBRStreamSampleCount / (double)SampleRate)) * 8);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 00675fd6a1b19054687f4208b26035e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: