Skip to content

Commit fd32cfe

Browse files
committed
feat: audio data stream
1 parent 4a76cfd commit fd32cfe

File tree

3 files changed

+161
-1
lines changed

3 files changed

+161
-1
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Runtime.InteropServices;
5+
using Aurio.Streams;
6+
using Aurio.Streams.Generic;
7+
using Microsoft.VisualStudio.TestTools.UnitTesting;
8+
9+
namespace Aurio.UnitTest.Streams.Generic;
10+
11+
[TestClass]
12+
public class AudioDataStreamTests
13+
{
14+
[TestMethod]
15+
public void CanReadSeekWriteFlags()
16+
{
17+
var samples = new float[] { 0.1f, -0.2f, 0.3f, -0.4f };
18+
using var src = new MemorySourceStream(samples, 44100, 1);
19+
using var stream = new AudioDataStream(src);
20+
21+
Assert.IsTrue(stream.CanRead);
22+
Assert.IsTrue(stream.CanSeek);
23+
Assert.IsFalse(stream.CanWrite);
24+
}
25+
26+
[TestMethod]
27+
public void LengthAndPositionExposeUnderlyingStream()
28+
{
29+
var samples = new float[] { 0.1f, 0.2f, 0.3f, 0.4f };
30+
using var src = new MemorySourceStream(samples, 48000, 1);
31+
using var stream = new AudioDataStream(src);
32+
33+
Assert.AreEqual(src.Length, stream.Length);
34+
Assert.AreEqual(0L, stream.Position);
35+
36+
stream.Position = 4; // advance by one float sample (4 bytes)
37+
Assert.AreEqual(4L, stream.Position);
38+
Assert.AreEqual(4L, src.Position);
39+
}
40+
41+
[TestMethod]
42+
public void ReadPassesThroughData()
43+
{
44+
// Prepare 4 float samples => 16 bytes
45+
var input = new float[] { 0.5f, -0.25f, 0.75f, -1.0f };
46+
var inputBytes = MemoryMarshal.AsBytes(input.AsSpan()).ToArray();
47+
using var src = new MemorySourceStream(input, 44100, 1);
48+
using var stream = new AudioDataStream(src);
49+
50+
var buffer = new byte[16];
51+
var read = stream.Read(buffer, 0, buffer.Length);
52+
53+
CollectionAssert.AreEqual(inputBytes, buffer);
54+
}
55+
56+
[TestMethod]
57+
public void SeekChangesPositionCorrectly()
58+
{
59+
using var sine = new SineGeneratorStream(44100, 440, TimeSpan.FromSeconds(1));
60+
using var stream = new AudioDataStream(sine);
61+
62+
// Seek from begin
63+
var pos = stream.Seek(100, SeekOrigin.Begin);
64+
Assert.AreEqual(100L, pos);
65+
Assert.AreEqual(100L, stream.Position);
66+
67+
// Seek relative to current
68+
pos = stream.Seek(50, SeekOrigin.Current);
69+
Assert.AreEqual(150L, pos);
70+
71+
// Seek relative to end (negative offset)
72+
pos = stream.Seek(-200, SeekOrigin.End);
73+
Assert.AreEqual(sine.Length - 200, pos);
74+
}
75+
76+
[TestMethod]
77+
[ExpectedException(typeof(NotImplementedException))]
78+
public void SetLengthNotSupported()
79+
{
80+
using var src = new MemorySourceStream(new float[] { 0.1f, 0.2f }, 44100, 1);
81+
using var stream = new AudioDataStream(src);
82+
83+
stream.SetLength(0);
84+
}
85+
86+
[TestMethod]
87+
[ExpectedException(typeof(NotImplementedException))]
88+
public void WriteNotSupported()
89+
{
90+
using var src = new MemorySourceStream(new float[] { 0.1f, 0.2f }, 44100, 1);
91+
using var stream = new AudioDataStream(src);
92+
93+
stream.Write(new byte[4], 0, 4);
94+
}
95+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace Aurio.Streams.Generic;
5+
6+
/// <summary>
7+
/// Provides a read-only stream for accessing audio data from an underlying audio stream source.
8+
/// </summary>
9+
/// <remarks>This stream supports reading and seeking operations but does not support writing. It is intended for
10+
/// scenarios where raw audio data needs to be consumed in a stream-based manner.</remarks>
11+
public class AudioDataStream : Stream
12+
{
13+
private readonly IAudioStream _audioStream;
14+
15+
public AudioDataStream(IAudioStream audioStream)
16+
{
17+
_audioStream = audioStream;
18+
}
19+
20+
public override bool CanRead => true;
21+
22+
public override bool CanSeek => true;
23+
24+
public override bool CanWrite => false;
25+
26+
public override long Length => _audioStream.Length;
27+
28+
public override long Position
29+
{
30+
get => _audioStream.Position;
31+
set => _audioStream.Position = value;
32+
}
33+
34+
public override void Flush()
35+
{
36+
// noop
37+
}
38+
39+
public override int Read(byte[] buffer, int offset, int count)
40+
{
41+
return _audioStream.Read(buffer, offset, count);
42+
}
43+
44+
public override long Seek(long offset, SeekOrigin origin)
45+
{
46+
var position = origin switch
47+
{
48+
SeekOrigin.Begin => offset,
49+
SeekOrigin.Current => _audioStream.Position + offset,
50+
SeekOrigin.End => _audioStream.Length + offset,
51+
_ => throw new ArgumentOutOfRangeException(nameof(origin), origin, null),
52+
};
53+
return _audioStream.Position = position;
54+
}
55+
56+
public override void SetLength(long value)
57+
{
58+
throw new NotImplementedException();
59+
}
60+
61+
public override void Write(byte[] buffer, int offset, int count)
62+
{
63+
throw new NotImplementedException();
64+
}
65+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
using System.Collections.Generic;
2121
using System.IO;
2222

23-
namespace Aurio.Streams
23+
namespace Aurio.Streams.Generic
2424
{
2525
/// <summary>
2626
/// Combines/concatenates multiple files or streams into a single stream.

0 commit comments

Comments
 (0)