VST host playback timing issues (VST.NET + NAudio) - c#

I'm just getting started in trying to work out VST plugin hosting for a small music program that I've been working on for a while. I've now reached the point where I'm able to take melodies stored within my program and send the midi data to a hosted plugin (using VST.NET) and outputting the audio to WaveOut (NAudio). The problem is that the audio output is playing far too quickly and also not in time.
Here's the code that I'm using for playback, parts of which are based on the example host within the VST.NET sample projects:
public class PhraseEditorWaveProvider : VstWaveProvider
{
public PhraseEditor PhraseEditor { get; private set; }
public Rational PlaybackBeat { get; private set; }
public PhraseEditorWaveProvider(PhraseEditor phraseEditor, string pluginPath, WaveFormat waveFormat = null)
: base(pluginPath, waveFormat)
{
PhraseEditor = phraseEditor;
}
public override int Read(byte[] buffer, int offset, int count)
{
decimal framesPerBeat = (60 / (decimal)PhraseEditor.Phrase.Tempo) * WaveFormat.SampleRate;
Rational startBeat = PlaybackBeat;
Rational endBeat = startBeat + Rational.FromDecimal(count / framesPerBeat);
//Get list of note starts and note ends that occur within the beat range
List<VstEvent> vstEvents = new List<VstEvent>();
foreach(Note note in PhraseEditor.Phrase.Notes)
{
if(note.StartBeat >= startBeat && note.StartBeat < endBeat)
vstEvents.Add(NoteOnEvent(1, (byte)note.Pitch.Value, 100, (int)(note.Duration * framesPerBeat), (int)((note.StartBeat - startBeat) * framesPerBeat)));
if(note.EndBeat >= startBeat && note.EndBeat < endBeat)
vstEvents.Add(NoteOffEvent(1, (byte)note.Pitch.Value, (int)((note.EndBeat - startBeat) * framesPerBeat)));
}
foreach(Chord chord in PhraseEditor.Phrase.Chords)
{
if(chord.StartBeat >= startBeat && chord.StartBeat < endBeat)
{
//Play each note within a chord in the 4th octave, with velocity 70
foreach (Pitch pitch in chord.Pitches)
vstEvents.Add(NoteOnEvent(1, (byte)((pitch.Value % 12) + 48), 70, (int)(chord.Duration * framesPerBeat), (int)((chord.StartBeat - startBeat) * framesPerBeat)));
}
if(chord.EndBeat >= startBeat && chord.EndBeat < endBeat)
{
foreach(Pitch pitch in chord.Pitches)
vstEvents.Add(NoteOffEvent(1, (byte)((pitch.Value % 12) + 48), (int)((chord.EndBeat - startBeat) * framesPerBeat)));
}
}
PlaybackBeat = endBeat;
return base.Read(vstEvents.OrderBy(x => x.DeltaFrames).ToArray(), buffer, offset, count);
}
}
public abstract class VstWaveProvider : IWaveProvider
{
private WaveFormat _waveFormat;
public WaveFormat WaveFormat
{
get
{
return _waveFormat;
}
set
{
_waveFormat = value;
BytesPerWaveSample = _waveFormat.BitsPerSample / 8;
}
}
public VstPluginContext VstContext { get; private set; }
public int BytesPerWaveSample { get; private set; }
public VstWaveProvider(VstPluginContext vstContext, WaveFormat waveFormat = null)
{
WaveFormat = (waveFormat == null) ? new WaveFormat(44100, 2) : waveFormat;
VstContext = vstContext;
}
public VstWaveProvider(string pluginPath, WaveFormat waveFormat = null)
{
WaveFormat = (waveFormat == null) ? new WaveFormat(44100, 2) : waveFormat;
VstContext = OpenPlugin(pluginPath);
}
public abstract int Read(byte[] buffer, int offset, int count);
protected int Read(VstEvent[] vstEvents, byte[] outputBuffer, int offset, int count)
{
VstAudioBufferManager inputBuffers = new VstAudioBufferManager(
VstContext.PluginInfo.AudioInputCount,
count / (Math.Max(1, VstContext.PluginInfo.AudioInputCount) * BytesPerWaveSample)
);
return Read(inputBuffers, vstEvents, outputBuffer, offset, count);
}
protected int Read(VstAudioBufferManager inputBuffers, VstEvent[] vstEvents, byte[] outputBuffer, int offset, int count)
{
VstAudioBufferManager outputBuffers = new VstAudioBufferManager(
VstContext.PluginInfo.AudioOutputCount,
count / (VstContext.PluginInfo.AudioOutputCount * BytesPerWaveSample)
);
VstContext.PluginCommandStub.StartProcess();
if(vstEvents.Length > 0)
VstContext.PluginCommandStub.ProcessEvents(vstEvents);
VstContext.PluginCommandStub.ProcessReplacing(inputBuffers.ToArray(), outputBuffers.ToArray());
VstContext.PluginCommandStub.StopProcess();
//Convert from multi-track to interleaved data
int bufferIndex = offset;
for (int i = 0; i < outputBuffers.BufferSize; i++)
{
foreach (VstAudioBuffer vstBuffer in outputBuffers)
{
Int16 waveValue = (Int16)((vstBuffer[i] + 1) * 128);
byte[] bytes = BitConverter.GetBytes(waveValue);
outputBuffer[bufferIndex] = bytes[0];
outputBuffer[bufferIndex + 1] = bytes[1];
bufferIndex += 2;
}
}
return count;
}
private VstPluginContext OpenPlugin(string pluginPath)
{
HostCommandStub hostCmdStub = new HostCommandStub();
hostCmdStub.PluginCalled += new EventHandler<PluginCalledEventArgs>(HostCmdStub_PluginCalled);
VstPluginContext ctx = VstPluginContext.Create(pluginPath, hostCmdStub);
ctx.Set("PluginPath", pluginPath);
ctx.Set("HostCmdStub", hostCmdStub);
ctx.PluginCommandStub.Open();
ctx.PluginCommandStub.MainsChanged(true);
return ctx;
}
private void HostCmdStub_PluginCalled(object sender, PluginCalledEventArgs e)
{
Debug.WriteLine(e.Message);
}
protected VstMidiEvent NoteOnEvent(byte channel, byte pitch, byte velocity, int noteLength, int deltaFrames = 0)
{
return new VstMidiEvent(deltaFrames, noteLength, 0, new byte[] { (byte)(144 + channel), pitch, velocity, 0 }, 0, 0);
}
protected VstMidiEvent NoteOffEvent(byte channel, byte pitch, int deltaFrames = 0)
{
return new VstMidiEvent(deltaFrames, 0, 0, new byte[] { (byte)(144 + channel), pitch, 0, 0 }, 0, 0);
}
}
Which would get called by the following:
WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback());
waveOut.Init(new PhraseEditorWaveProvider(this, #"C:\Users\james\Downloads\Cobalt\Cobalt\Cobalt 64bit\Cobalt.dll"));
waveOut.Play();
Where Cobalt is the current plugin that I'm using for testing.
For context, Rational is my own data type since other parts of my program are doing lots of manipulation of melodies and I found that doubles and decimals weren't giving me the precision that I required.
Also, both the VST plugin context and WaveOut are set to have sample rates of 44.1kHz, so there shouldn't need to be any up/down-sampling when passing the plugin output data into the WaveOut buffer.
I'm at a complete loss as to why the audio is playing back faster than expected. It seems to be roughly 4x faster than expected. If anyone can give any pointers what may be causing this I'd be hugely grateful.
With it playing out of time, I suspect that this is down to me not understanding correctly how the deltaFrame property works within VstMidiEvent. I've tried playing around with both deltaFrame and noteOffset, though don't seem to be having much luck with either, I'm currently working under the assumption that they measure the number of audio frames from the start of the current block of data, to the time of the event within that block. Unfortunately I've been struggling to find much useful documentation on this so it could be that I'm totally wrong about this though.
Look forward to any responses
Kind regards
James

Ok, I think I found what was causing the problem, it was in this section of code:
public override int Read(byte[] buffer, int offset, int count)
{
decimal framesPerBeat = (60 / (decimal)PhraseEditor.Phrase.Tempo) * WaveFormat.SampleRate;
Rational startBeat = PlaybackBeat;
Rational endBeat = startBeat + Rational.FromDecimal(count / framesPerBeat);
...
}
Which I just changed to this:
public override int Read(byte[] buffer, int offset, int count)
{
decimal framesPerBeat = (60 / (decimal)PhraseEditor.Phrase.Tempo) * WaveFormat.SampleRate;
int samplesRequired = count / (WaveFormat.Channels * (WaveFormat.BitsPerSample / 8));
Rational startBeat = PlaybackBeat;
Rational endBeat = startBeat + Rational.FromDecimal(samplesRequired / framesPerBeat);
...
}
Dumb mistake on my part, I'd been converting from bit-rate to sample-rate everywhere except in my method for getting upcoming midi events. My audio is now playing at a rate far closer to what I'd expect, and seems to be more reliable on the timing, though I haven't had a chance to fully test this yet.

Related

ByteStream source in SharpDX.MediaFoundation hanging

I have been trying to get a custom audio stream to work with SharpDX.MediaFoundation.
To this end I have wrapped my audio object in a class that implements System.IO.Stream as follows:
public class AudioReaderWaveStream : System.IO.Stream
{
byte[] waveHeader = new byte[44];
AudioCore.IAudioReader reader = null;
ulong readHandle = 0xffffffff;
long readPosition = 0;
public AudioReaderWaveStream(AudioCore.CEditedAudio content)
{
reader = content as AudioCore.IAudioReader;
readHandle = reader.OpenDevice();
int sampleRate = 0;
short channels = 0;
content.GetFormat(out sampleRate, out channels);
System.IO.MemoryStream memStream = new System.IO.MemoryStream(waveHeader);
using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(memStream))
{
bw.Write("RIFF".ToCharArray());
bw.Write((Int32)Length - 8);
bw.Write("WAVE".ToCharArray());
bw.Write("fmt ".ToCharArray());
bw.Write((Int32)16);
bw.Write((Int16)3);
bw.Write((Int16)1);
bw.Write((Int32)sampleRate);
bw.Write((Int32)sampleRate * 4);
bw.Write((Int16)4);
bw.Write((Int16)32);
bw.Write("data".ToCharArray());
bw.Write((Int32)reader.GetSampleCount() * 4);
}
}
protected override void Dispose(bool disposing)
{
if (readHandle != 0xffffffff)
{
reader.CloseDevice(readHandle);
readHandle = 0xfffffffff;
}
base.Dispose(disposing);
}
~AudioReaderWaveStream()
{
Dispose();
}
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanSeek
{
get
{
return true;
}
}
public override bool CanWrite
{
get
{
return false;
}
}
public override long Length
{
get
{
// Number of float samples + header of 44 bytes.
return (reader.GetSampleCount() * 4) + 44;
}
}
public override long Position
{
get
{
return readPosition;
}
set
{
readPosition = value;
}
}
public override void Flush()
{
//throw new NotImplementedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
if (count <= 0)
return 0;
int retCount = count;
if (Position < 44)
{
int headerCount = count;
if ( Position + count >= 44 )
{
headerCount = 44 - (int)Position;
}
Array.Copy(waveHeader, Position, buffer, offset, headerCount);
offset += headerCount;
Position += headerCount;
count -= headerCount;
}
if (count > 0)
{
float[] readBuffer = new float[count/4];
reader.Seek(readHandle, Position - 44);
reader.ReadAudio(readHandle, readBuffer);
Array.Copy(readBuffer, 0, buffer, offset, count);
}
return retCount;
}
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
if (origin == System.IO.SeekOrigin.Begin)
{
readPosition = offset;
}
else if (origin == System.IO.SeekOrigin.Current)
{
readPosition += offset;
}
else
{
readPosition = Length - offset;
}
return readPosition;
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
I then take this object and create a source resolver using it as follows:
// Create a source resolver.
SharpDX.MediaFoundation.ByteStream sdxByteStream = new ByteStream( ARWS );
SharpDX.MediaFoundation.SourceResolver resolver = new SharpDX.MediaFoundation.SourceResolver();
ComObject source = (ComObject)resolver.CreateObjectFromStream( sdxByteStream, "File.wav", SourceResolverFlags.MediaSource );
However every time I'm doing this it hangs on the CreateObjectFromStream call. I've had a look inside SharpDX to see whats going on and it seems the actual hang occurs when it makes the call to the underlying interface through CreateObjectFromByteStream. I've also looked to see what data is actually read from the byte stream. It reads the first 16 bytes which includes the 'RIFF', the RIFF size, the 'WAVE' and the 'fmt '. Then nothing else.
Has anyone got any ideas what I could be doing wrong. I've tried all sorts of combinations of the SourceResolverFlags but nothing seems to make any difference. It just hangs.
It does remind me somewhat of interthread marshalling but all the media foundation calls are made from the same thread so I don't think its that. I'm also fairly sure that MediaFoundation uses free threading so this shouldn't be a problem anyway.
Has anyone any idea what I could possibly be doing wrong?
Thanks!
Ok I have come up with a solution to this. It looks like I may be having a COM threading issue. The read happens in a thread and that thread was calling back to the main thread which the function was called from.
So I used the async version of the call and perform an Application.DoEvents() to hand across control where necessary.
Callback cb = new Callback( resolver );
IUnknown cancel = null;
resolver.BeginCreateObjectFromByteStream( sdxByteStream, "File.wav", (int)(SourceResolverFlags.MediaSource | SourceResolverFlags.ByteStream), null, out cancel, cb, null );
if ( cancel != null )
{
cancel.Dispose();
}
while( cb.MediaSource == null )
{
System.Windows.Forms.Application.DoEvents();
}
SharpDX.MediaFoundation.MediaSource mediaSource = cb.MediaSource;
I really hate COM's threading model ...

How can I get the data of 8bit wav file

I'm making a demo about sound in WindowsForm, I created 3 classes for taking data of wave file. Code is below:
RiffBlock
public class RiffBlock
{
private byte[] riffID;
private uint riffSize;
private byte[] riffFormat;
public byte[] RiffID
{
get { return riffID; }
}
public uint RiffSize
{
get { return (riffSize); }
}
public byte[] RiffFormat
{
get { return riffFormat; }
}
public RiffBlock()
{
riffID = new byte[4];
riffFormat = new byte[4];
}
public void ReadRiff(FileStream inFS)
{
inFS.Read(riffID, 0, 4);
BinaryReader binRead = new BinaryReader(inFS);
riffSize = binRead.ReadUInt32();
inFS.Read(riffFormat, 0, 4);
}
}
FormatBlock
public class FormatBlock
{
private byte[] fmtID;
private uint fmtSize;
private ushort fmtTag;
private ushort fmtChannels;
private uint fmtSamplesPerSec;
private uint fmtAverageBytesPerSec;
private ushort fmtBlockAlign;
private ushort fmtBitsPerSample;
public byte[] FmtID
{
get { return fmtID; }
}
public uint FmtSize
{
get { return fmtSize; }
}
public ushort FmtTag
{
get { return fmtTag; }
}
public ushort Channels
{
get { return fmtChannels; }
}
public uint SamplesPerSec
{
get { return fmtSamplesPerSec; }
}
public uint AverageBytesPerSec
{
get { return fmtAverageBytesPerSec; }
}
public ushort BlockAlign
{
get { return fmtBlockAlign; }
}
public ushort BitsPerSample
{
get { return fmtBitsPerSample; }
}
public FormatBlock()
{
fmtID = new byte[4];
}
public void ReadFmt(FileStream inFS)
{
inFS.Read(fmtID, 0, 4);
BinaryReader binRead = new BinaryReader(inFS);
fmtSize = binRead.ReadUInt32();
fmtTag = binRead.ReadUInt16();
fmtChannels = binRead.ReadUInt16();
fmtSamplesPerSec = binRead.ReadUInt32();
fmtAverageBytesPerSec = binRead.ReadUInt32();
fmtBlockAlign = binRead.ReadUInt16();
fmtBitsPerSample = binRead.ReadUInt16();
// This accounts for the variable format header size
// 12 bytes of Riff Header, 4 bytes for FormatId, 4 bytes for FormatSize & the Actual size of the Format Header
inFS.Seek(fmtSize + 20, System.IO.SeekOrigin.Begin);
}
}
DataBlock
public class DataBlock
{
private byte[] dataID;
private uint dataSize;
private Int16[] data;
private int dataNumSamples;
public byte[] DataID
{
get { return dataID; }
}
public uint DataSize
{
get { return dataSize; }
}
public Int16 this[int pos]
{
get { return data[pos]; }
}
public int NumSamples
{
get { return dataNumSamples; }
}
public DataBlock()
{
dataID = new byte[4];
}
public void ReadData(FileStream inFS)
{
inFS.Read(dataID, 0, 4);
BinaryReader binRead = new BinaryReader(inFS);
dataSize = binRead.ReadUInt32();
data = new Int16[dataSize];
inFS.Seek(40, SeekOrigin.Begin);
dataNumSamples = (int)(dataSize / 2);
for (int i = 0; i < dataNumSamples; i++)
{
data[i] = binRead.ReadInt16();
}
}
}
It works ok with only 16bit wave file, but when I choose a 8 bit wav file or another, the result of this command dataSize = binRead.ReadUInt32();is only 4 although the file size is big.
How I can get the data of 8bit, 24bit... wav file?
Some solutions is appreciated, thank you very much.
Your reading methodology is flawed. The length is correct but for an 8 bits per sample file you should be reading bytes not words; as it stands the data will be incorrect and the value returned by the NumSamples property will be wrong.
In my case, the sub chunk size is 1160, the number of channels is 1 (mono) and the bits per sample is 8 (byte). You will need to decode the bits per sample and adjust your reading accordingly. For the WAV file I used, your program allocated a data array the correct length but 16 bit, divided the data length by 2 and called this the number of samples (wrong, it should be 1160) and then proceeded to read 580 word values from the stream.
Edit: My ancient code will not cut it in the modern age (I seem to recall having to modify it some years ago to cope with at least one additional chunk type but the details escape me).
This is what you get; anything more and your question should read "Could someone write me a program to load WAV files", as it is, we are way beyond the original question and it is time for you to knuckle down and make it work how you need it to :-)
References:
http://www.neurophys.wisc.edu/auditory/riff-format.txt
http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
http://www.shikadi.net/moddingwikiResource_Interchange_File_Format_(RIFF)
http://soundfile.sapp.org/doc/WaveFormat/
All are very useful.
///<summary>
///Reads up to 16 bit WAV files
///</summary>
///<remarks> Things have really changed in the last 15 years</remarks>
public class RiffLoader
{
public enum RiffStatus { Unknown = 0, OK, FileError, FormatError, UnsupportedFormat };
LinkedList<Chunk> chunks = new LinkedList<Chunk>();
RiffStatus status = RiffStatus.Unknown;
List<String> errorMessages = new List<string>();
String source;
public String SourceName { get { return source; } }
public RiffStatus Status { get { return status; } }
public String[] Messages { get { return errorMessages.ToArray(); } }
enum chunkType { Unknown = 0, NoMore, Riff, Fmt, Fact, Data, Error = -1 };
static Int32 scan32bits(byte[] source, int offset = 0)
{
return source[offset] | source[offset + 1] << 8 | source[offset + 2] << 16 | source[offset + 3] << 24;
}
static Int32 scan16bits(byte[] source, int offset = 0)
{
return source[offset] | source[offset + 1] << 8;
}
static Int32 scan8bits(byte[] source, int offset = 0)
{
return source[offset];
}
abstract class Chunk
{
public chunkType Ident = chunkType.Unknown;
public int ByteCount = 0;
}
class RiffChunk : Chunk
{
public RiffChunk(int count)
{
this.Ident = chunkType.Riff;
this.ByteCount = count;
}
}
class FmtChunk : Chunk
{
int formatCode = 0;
int channels = 0;
int samplesPerSec = 0;
int avgBytesPerSec = 0;
int blockAlign = 0;
int bitsPerSample = 0;
int significantBits = 0;
public int Format { get { return formatCode; } }
public int Channels { get { return channels; } }
public int BlockAlign { get { return blockAlign; } }
public int BytesPerSample { get { return bitsPerSample / 8 + ((bitsPerSample % 8) > 0 ? 1 : 0); } }
public int BitsPerSample
{
get
{
if (significantBits > 0)
return significantBits;
return bitsPerSample;
}
}
public FmtChunk(byte[] buffer) : base()
{
int size = buffer.Length;
// if the length is 18 then buffer 16,17 should be 00 00 (I don't bother checking)
if (size != 16 && size != 18 && size != 40)
return;
formatCode = scan16bits(buffer, 0);
channels = scan16bits(buffer, 2);
samplesPerSec = scan32bits(buffer, 4);
avgBytesPerSec = scan32bits(buffer, 8);
blockAlign = scan16bits(buffer, 12);
bitsPerSample = scan16bits(buffer, 14);
if (formatCode == 0xfffe) // EXTENSIBLE
{
if (size != 40)
return;
significantBits = scan16bits(buffer, 18);
// skiping speaker map
formatCode = scan16bits(buffer, 24); // first two bytes of the GUID
// the rest of the GUID is fixed, decode it and check it if you wish
}
this.Ident = chunkType.Fmt;
this.ByteCount = size;
}
}
class DataChunk : Chunk
{
byte[] samples = null;
///<summary>
///Create a data chunk
///<para>
///The supplied buffer must be correctly sized with zero offset and must be purely for this class
///</para>
///<summary>
///<param name="buffer">source array</param>
public DataChunk(byte[] buffer)
{
this.Ident = chunkType.Data;
this.ByteCount = buffer.Length;
samples = buffer;
}
public enum SampleStatus { OK, Duff }
public class Samples
{
public SampleStatus Status = SampleStatus.Duff;
public List<int[]> Channels = new List<int[]>();
#if false // debugger helper method
/*
** Change #if false to #if true to include this
** Break at end of GetSamples on "return retval"
** open immediate window and type retval.DumpLast(16)
** look in output window for dump of last 16 entries
*/
public int DumpLast(int count)
{
for (int i = Channels[0].Length - count; i < Channels[0].Length; i++)
Console.WriteLine(String.Format("{0:X4} {1:X4},{2:X4}", i, Channels[0][i], Channels[1][i]));
return 0;
}
#endif
}
/*
** Return the decoded samples
*/
public Samples GetSamples(FmtChunk format)
{
Samples retval = new Samples();
int samplesPerChannel = this.ByteCount / (format.BytesPerSample * format.Channels);
int mask = 0, sign=0;
int [][] samples = new int [format.Channels][];
for (int c = 0; c < format.Channels; c++)
samples[c] = new int[samplesPerChannel];
if (format.BitsPerSample >= 8 && format.BitsPerSample <= 16) // 24+ is left as an excercise
{
sign = (int)Math.Floor(Math.Pow(2, format.BitsPerSample - 1));
mask = (int)Math.Floor(Math.Pow(2, format.BitsPerSample)) - 1;
int offset = 0, index = 0;
int s = 0;
while (index < samplesPerChannel)
{
for (int c = 0; c < format.Channels; c++)
{
switch (format.BytesPerSample)
{
case 1:
s = scan8bits(this.samples, offset) & mask;
break;
case 2:
s = scan16bits(this.samples, offset) & mask;
break;
}
// sign extend the data to Int32
samples[c][index] = s | ((s & sign) != 0 ? ~mask : 0);
offset += format.BytesPerSample;
}
++index;
}
retval.Channels = new List<int[]>(samples);
retval.Status = SampleStatus.OK;
}
return retval;
}
}
class FactChunk : Chunk
{
int samplesPerChannel;
public int SamplesPerChannel { get { return samplesPerChannel; } }
public FactChunk(byte[] buffer)
{
this.Ident = chunkType.Fact;
this.ByteCount = buffer.Length;
if (buffer.Length >= 4)
samplesPerChannel = scan32bits(buffer);
}
}
class DummyChunk : Chunk
{
public DummyChunk(int size, chunkType type = chunkType.Unknown)
{
this.Ident = type;
this.ByteCount = size;
}
}
public RiffLoader(String fileName)
{
source = fileName;
try
{
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
using (BinaryReader reader = new BinaryReader(fs))
{
Chunk c = getChunk(fs, reader);
if (c.Ident != chunkType.Riff)
{
status = RiffStatus.FileError;
errorMessages.Add(String.Format("Error loading \"{0}\": No valid header"));
}
chunks.AddLast(c);
c = getChunk(fs, reader);
if (c.Ident != chunkType.Fmt)
{
status = RiffStatus.FileError;
errorMessages.Add(String.Format("Error loading \"{0}\": No format chunk"));
}
chunks.AddLast(c);
/*
** From now on we just keep scanning to the end of the file
*/
while (fs.Position < fs.Length)
{
c = getChunk(fs, reader);
switch (c.Ident)
{
case chunkType.Fact:
case chunkType.Data:
chunks.AddLast(c);
break;
case chunkType.Unknown:
break; // skip it - don't care what it is
}
}
FmtChunk format = null;
foreach (var chunk in chunks)
{
switch(chunk.Ident)
{
case chunkType.Fmt:
format = chunk as FmtChunk;
break;
case chunkType.Data:
if (format != null)
{
DataChunk dc = chunk as DataChunk;
var x = dc.GetSamples(format);
}
break;
}
}
}
}
catch (Exception e)
{
status = RiffStatus.FileError;
errorMessages.Add(String.Format("Error loading \"{0}\": {1}", e.Message));
}
}
/*
** Get a chunk of data from the file - knows nothing of the internal format of the chunk.
*/
Chunk getChunk(FileStream stream, BinaryReader reader)
{
byte[] buffer;
int size;
buffer = reader.ReadBytes(8);
if (buffer.Length == 8)
{
String prefix = new String(Encoding.ASCII.GetChars(buffer, 0, 4));
size = scan32bits(buffer, 4);
if (size + stream.Position <= stream.Length) // skip if there isn't enough data
{
if (String.Compare(prefix, "RIFF") == 0)
{
/*
** only "WAVE" type is acceptable
**
** Don't read size bytes or the entire file will end up in the RIFF chunk
*/
if (size >= 4)
{
buffer = reader.ReadBytes(4);
String ident = new String(Encoding.ASCII.GetChars(buffer, 0, 4));
if (String.CompareOrdinal(ident, "WAVE") == 0)
return new RiffChunk(size - 4);
}
}
else if (String.Compare(prefix, "fmt ") == 0)
{
if (size >= 16)
{
buffer = reader.ReadBytes(size);
if (buffer.Length == size)
return new FmtChunk(buffer);
}
}
else if (String.Compare(prefix, "fact") == 0)
{
if (size >= 4)
{
buffer = reader.ReadBytes(4);
if (buffer.Length == size)
return new FactChunk(buffer);
}
}
else if (String.Compare(prefix, "data") == 0)
{
// assume that there has to be data
if (size > 0)
{
buffer = reader.ReadBytes(size);
if ((size & 1) != 0) // odd length?
{
if (stream.Position < stream.Length)
reader.ReadByte();
else
size = -1; // force an error - there should be a pad byte
}
if (buffer.Length == size)
return new DataChunk(buffer);
}
}
else
{
/*
** there are a number of weird and wonderful block types - assume there has to be data
*/
if (size > 0)
{
buffer = reader.ReadBytes(size);
if ((size & 1) != 0) // odd length?
{
if (stream.Position < stream.Length)
reader.ReadByte();
else
size = -1; // force an error - there should be a pad byte
}
if (buffer.Length == size)
{
DummyChunk skip = new DummyChunk(size);
return skip;
}
}
}
}
}
return new DummyChunk(0, chunkType.Error);
}
}
You will need to add properties as required and code to navigate the returned linked list. Particular properties you may need are the sample rates in the format block, assuming you are going to process the data in some way.
Adding 24 to 32 bits is simple, if you need to go beyond 32 bits you will have to switch to int64's.
I have tested it with some good samples from http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Samples.html, as well as your 8 bit file, and it decodes OK and seems to have the correct data.
You should be more than capable of making it work now, good luck.
Edit (again, like the proverbial dog...):
Of course, I should have sign extended the value, so DataChunk.GetSamples() now does that. Looking at the Codeproject files (it isn't the greatest code by the way, but the guy does say that he is only just learning C#, so fair play for tackling a graphical user control) it is obvious that the data is signed. It's a shame that he didn't standardise his source to be an array of Int32's, then it wouldn't matter how many bits the WAV was encoded in.
You could use the Naudio library:
https://naudio.codeplex.com/ (take a look at their website, there are quite a lot of tutorials).
Hope this helps :).

Encrypt file but can decrypt from any point

Basically i need to encrypt a file and then be able to decrypt the file from almost any point in the file. The reason i need this is i would like to use this for files like Video etc and still be able to jump though the file or video. Also the file would be served over the web so not needing to download the whole file is important. Where i am storing the file supports partial downloads so i can request any part of the file i need and this works for an un encrypted file. The question is how could i make this work for an encrypted file. I need to encrypt and decrypt the file in C# but don't really have any other restrictions than that. Symmetric keys are preferred but if that wont work it is not a deal breaker.
Another example of where i only want to download part of a file and decrypt is where i have joined multiple files together but just need to retrieve one of them. This would generally be used for files smaller than 50MB like pictures and info files.
--- EDIT ---
To be clear i am looking for a working implementation or library that does not increase the size of the source file. Stream cipher seems ideal but i have not seen one in c# that works for any point in the stream or anything apart from the start of the stream. Would consider block based implementation if it works form set blocks in stream. Basically i want to pass a raw stream though this and have unencrypted come out other side of the stream. Happy to set the starting offset it represents in the whole file/stream. Looking for something than works as i am not encryption expert. At the minute i get the parts of the file from a data source in 512kb to 5mb blocks depending on client config and i use streams CopyTo method to write it out to a file on disk. I don't get these parts in order. I am looking for a stream wrapper that i could use to pass into the CopyTo method on stream.
Your best best is probably to treat the file as a list of chunks (of whatever size is convenient for your application; let's say 50 kB) and encrypt each separately. This would allow you to decrypt each chunk independently of the others.
For each chunk, derive new keys from your master key, generate a new IV, and encrypt-then-MAC the chunk.
This method has higher storage overhead than encrypting the entire file at once and takes a bit more computation as well due to the key regeneration that it requires.
If you use a stream cipher instead of a block cipher, you'd be able to start decrypting at any byte offset, as long as the decryptor was able to get the current IV from somewhere.
For those interested i managed to work it out based on a number of examples i found plus some of my own code. It uses bouncycastle but should also work with dotnet AES with a few tweaks. This allows the decryption/encryption from any point in the stream.
using System;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
namespace StreamHelpers
{
public class StreamEncryptDecrypt : Stream
{
private readonly Stream _streamToWrap;
private readonly IBlockCipher _cipher;
private readonly ICipherParameters _key;
private readonly byte[] _iv;
private readonly byte[] _counter;
private readonly byte[] _counterOut;
private readonly byte[] _output;
private long currentBlockCount;
public StreamEncryptDecrypt(Stream streamToWrap, IBlockCipher cipher, ParametersWithIV keyAndIv)
{
_streamToWrap = streamToWrap;
_cipher = cipher;
_key = keyAndIv.Parameters;
_cipher.Init(true, _key);
_iv = keyAndIv.GetIV();
_counter = new byte[_cipher.GetBlockSize()];
_counterOut = new byte[_cipher.GetBlockSize()];
_output = new byte[_cipher.GetBlockSize()];
if (_iv.Length != _cipher.GetBlockSize())
{
throw new Exception("IV must be the same size as the cipher block size");
}
InitCipher();
}
private void InitCipher()
{
long position = _streamToWrap.Position;
Array.Copy(_iv, 0, _counter, 0, _counter.Length);
currentBlockCount = 0;
var targetBlock = position/_cipher.GetBlockSize();
while (currentBlockCount < targetBlock)
{
IncrementCounter(false);
}
_cipher.ProcessBlock(_counter, 0, _counterOut, 0);
}
private void IncrementCounter(bool updateCounterOut = true)
{
currentBlockCount++;
// Increment the counter
int j = _counter.Length;
while (--j >= 0 && ++_counter[j] == 0)
{
}
_cipher.ProcessBlock(_counter, 0, _counterOut, 0);
}
public override long Position
{
get { return _streamToWrap.Position; }
set
{
_streamToWrap.Position = value;
InitCipher();
}
}
public override long Seek(long offset, SeekOrigin origin)
{
var result = _streamToWrap.Seek(offset, origin);
InitCipher();
return result;
}
public void ProcessBlock(
byte[] input,
int offset,
int length, long streamPosition)
{
if (input.Length < offset+length)
throw new ArgumentException("input does not match offset and length");
var blockSize = _cipher.GetBlockSize();
var startingBlock = streamPosition / blockSize;
var blockOffset = (int)(streamPosition - (startingBlock * blockSize));
while (currentBlockCount < streamPosition / blockSize)
{
IncrementCounter();
}
//process the left over from current block
if (blockOffset !=0)
{
var blockLength = blockSize - blockOffset;
blockLength = blockLength > length ? length : blockLength;
//
// XOR the counterOut with the plaintext producing the cipher text
//
for (int i = 0; i < blockLength; i++)
{
input[offset + i] = (byte)(_counterOut[blockOffset + i] ^ input[offset + i]);
}
offset += blockLength;
length -= blockLength;
blockOffset = 0;
if (length > 0)
{
IncrementCounter();
}
}
//need to loop though the rest of the data and increament counter when needed
while (length > 0)
{
var blockLength = blockSize > length ? length : blockSize;
//
// XOR the counterOut with the plaintext producing the cipher text
//
for (int i = 0; i < blockLength; i++)
{
input[offset + i] = (byte)(_counterOut[i] ^ input[offset + i]);
}
offset += blockLength;
length -= blockLength;
if (length > 0)
{
IncrementCounter();
}
}
}
public override int Read(byte[] buffer, int offset, int count)
{
var pos = _streamToWrap.Position;
var result = _streamToWrap.Read(buffer, offset, count);
ProcessBlock(buffer, offset, result, pos);
return result;
}
public override void Write(byte[] buffer, int offset, int count)
{
var input = new byte[count];
Array.Copy(buffer, offset, input, 0, count);
ProcessBlock(input, 0, count, _streamToWrap.Position);
_streamToWrap.Write(input, offset, count);
}
public override void Flush()
{
_streamToWrap.Flush();
}
public override void SetLength(long value)
{
_streamToWrap.SetLength(value);
}
public override bool CanRead
{
get { return _streamToWrap.CanRead; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return _streamToWrap.CanWrite; }
}
public override long Length
{
get { return _streamToWrap.Length; }
}
protected override void Dispose(bool disposing)
{
if (_streamToWrap != null)
{
_streamToWrap.Dispose();
}
base.Dispose(disposing);
}
}
}

How to prevent number from getting to large from wave

So this is might become overly complex to explain, but I'll try to keep it simple yet informative. my program, which is written in C#.net, monitors a microphone for 2 seconds and returns the Maximum value from a sample. I'm not super well versed with how sound and so forth is generated from winmm.dll, but my program is based loosely on NAudio and another project from CodeProject to visualize a wave. The wave format that I am using is this
//WaveIn.cs
private WaveFormat Format= new WaveFormat(8000, 16,1);
//waveFormat.cs
[StructLayout(LayoutKind.Sequential)]
public class WaveFormat
{
public short wFormatTag;
public short nChannels;
public int nSamplesPerSec;
public int nAvgBytesPerSec;
public short nBlockAlign;
public short wBitsPerSample;
public short cbSize;
public WaveFormat(int rate, int bits, short channels)
{
wFormatTag = (short)WaveFormats.Pcm;
nChannels = channels;
nSamplesPerSec = rate;
wBitsPerSample = (short)bits;
cbSize = 0;
nBlockAlign = (short)(nChannels * (wBitsPerSample / 8));
nAvgBytesPerSec = nSamplesPerSec * nBlockAlign;
}
(I think i may have just found my problem, by posting this but i'm still going to ask)
so then i setup a event for max sound level in my wavein file. If i understand the source code correctly it fires when the buffer is full. here is that code
private void CallBack(IntPtr waveInHandle, WaveMessage message, int userData, ref WaveHeader waveHeader, IntPtr reserved)
{
if (message == WaveMessage.WIM_DATA)
{
GCHandle hBuffer = (GCHandle)waveHeader.dwUser;
WaveInBuffer buffer = (WaveInBuffer)hBuffer.Target;
Exception exception = null;
if (DataAvailable != null)
{
DataAvailable(buffer.Data, buffer.BytesRecorded);
}
if (MaxSoundLevel != null) //FOLLOW THIS ONE
{
byte[] waveStream = new byte[buffer.BytesRecorded];
Marshal.Copy(buffer.Data, waveStream, 0, buffer.BytesRecorded);
MaxSoundLevel(GetMaxSound(GetWaveChannels(waveStream)));
}
if (recording)
{
try
{
buffer.Reuse();
}
catch (Exception e)
{
recording = false;
exception = e;
}
}
}
}
private short[] GetWaveChannels(byte[] waveStream)
{
short[] monoWave = new short[waveStream.Length/2];
int h=0;
for (int i = 0 ; i < waveStream.Length; i += 2)
{
monoWave[h] = BitConverter.ToInt16(waveStream, i);
h++;
}
return monoWave;
}
private int GetMaxSound(short[] wave)
{
int maxSound = 0;
for (int i = 0; i < wave.Length; i++)
{
maxSound = Math.Max(maxSound, Math.Abs(wave[i]));
}
return maxSound;
}
so when i monitor it from this test here it won't crash if i keep sound levels to "normal"
[Test]
public void TestSound()
{
var waveIn = new WaveIn();
waveIn.MaxSoundLevel += new WaveIn.MaxSoundHandler(waveIn_MaxSoundLevel);
waveIn.StartRecording();
Console.WriteLine("Starting to record");
Thread.Sleep(4800); //record for 4.8 seconds.
waveIn.StopRecording();
Console.WriteLine("Done Recording");
}
void waveIn_MaxSoundLevel(int MaxSound)
{
Console.WriteLine("MaxSound:{0}", MaxSound);
}
here is my output
MaxSound:28
MaxSound:24
MaxSound:31
MaxSound:17
MaxSound:18760
Unhandled Exception: System.OverflowException: Negating the minimum value of a twos complement number is invalid.
I once got it to give me MaxSound:32767 (0x7FFF).
So i figured that my problem lied within it trying to convert a 32 bit number to a 16 bit number which is why i switched GetMaxSound from short to int. So I don't know. I am stumped. So why am I having this problem? doesn't my wave suggest it's max is 32,767 and that the winmm.dll would know that and not go past that? and since it is just converting 2 bytes of data to a short should it never encounter this problem? Please help :)
My solution, for those who may be looking into this, was fairly simple in nature. A 16bit signed number's maximum positive value is 32767. it's maximum negative number is -32768. If you take the absolute value of 32768 and try to put it into a 16 bit number it will result in a overflow exception being thrown. So the solution is to cast the short value to a 32 bit number before i try to take the absolute value of it. Here is the corrected function
private int GetMaxSound(short[] wave)
{
int maxSound = 0;
for (int i = 0; i < wave.Length; i++)
{
maxSound = Math.Max(maxSound, Math.Abs((int)wave[i]));
}
return maxSound;
}
I could probably just have stuck with an unsigned number as well by using ushort but the Math.Abs does

How to implement a lazy stream chunk enumerator?

I'm trying to split a byte stream into chunks of increasing size.
The source stream contains an unknown number of bytes and is expensive to read. The output of the enumerator should be byte arrays of increasing size, starting at 8KB up to 1MB.
This is very simple to do by simply reading the whole stream, storing it in an array and taking the relevant pieces out. However, since the stream may be very large, reading it at once is unfeasible. Also, while performance is not the main concern, it is important to keep system load very low.
While implementing this I noticed that it's relatively difficult to keep the code short and maintainable. There are a few stream related issues to keep in mind, too (for instance, Stream.Read might not fill the buffer even though it succeeded).
I did not find any existing classes that help for my case, nor could I find something close on the net. How would you implement such a class?
public IEnumerable<BufferWrapper> getBytes(Stream stream)
{
List<int> bufferSizes = new List<int>() { 8192, 65536, 220160, 1048576 };
int count = 0;
int bufferSizePostion = 0;
byte[] buffer = new byte[bufferSizes[0]];
bool done = false;
while (!done)
{
BufferWrapper nextResult = new BufferWrapper();
nextResult.bytesRead = stream.Read(buffer, 0, buffer.Length);
nextResult.buffer = buffer;
done = nextResult.bytesRead == 0;
if (!done)
{
yield return nextResult;
count++;
if (count > 10 && bufferSizePostion < bufferSizes.Count)
{
count = 0;
bufferSizePostion++;
buffer = new byte[bufferSizes[bufferSizePostion]];
}
}
}
}
public class BufferWrapper
{
public byte[] buffer { get; set; }
public int bytesRead { get; set; }
}
Obviously the logic for when to move up in buffer size, and how to choose what that size is could be altered.
Someone could also probably find a better way of handling the last buffer to be sent, as this isn't the most efficient way.
For reference, the implementation I currently use, already with improvements as per the answer by #Servy
private const int InitialBlockSize = 8 * 1024;
private const int MaximumBlockSize = 1024 * 1024;
private Stream _Stream;
private int _Size = InitialBlockSize;
public byte[] Current
{
get;
private set;
}
public bool MoveNext ()
{
if (_Size < 0) {
return false;
}
var buf = new byte[_Size];
int count = 0;
while (count < _Size) {
int read = _Stream.Read (buf, count, _Size - count);
if (read == 0) {
break;
}
count += read;
}
if (count == _Size) {
Current = buf;
if (_Size <= MaximumBlockSize / 2) {
_Size *= 2;
}
}
else {
Current = new byte[count];
Array.Copy (buf, Current, count);
_Size = -1;
}
return true;
}

Categories