private SampleChunkInfo GetMinJitterTimeReference(uint sampleRate) { DateTime startTime = this.sampleInfo[0].Timestamp; uint startSampleTick = this.sampleInfo[0].SampleTick; double minJitter = 100; SampleChunkInfo bestTuple = null; uint firstSyncSourceId = this.sampleInfo[0].SyncSourceID; foreach (SampleChunkInfo tuple in this.sampleInfo) { if (tuple.SyncSourceID != firstSyncSourceId) { break; } double seconds = tuple.Timestamp.Subtract(startTime).TotalSeconds; uint sampleTicks = tuple.SampleTick - startSampleTick; //avoid using the first chunk if (sampleTicks > 0 && seconds * sampleRate - sampleTicks < minJitter) { minJitter = seconds * sampleRate - sampleTicks; bestTuple = tuple; } } return(bestTuple); }
public override int Read(byte[] buffer, int offset, int count) { int bytesRead; lock (this.pcm16Stream) { lock (this.audioStream.tempFileStream) { while (this.writePosition < this.readPosition + count && this.sampleChunkIndex < this.audioStream.sampleInfo.Count) { this.pcm16Stream.Position = writePosition; SampleChunkInfo sampleInfo = this.audioStream.sampleInfo[this.sampleChunkIndex]; if (this.currentSyncSourceID == null) { this.currentSyncSourceID = sampleInfo.SyncSourceID; } else if (this.currentSyncSourceID.Value != sampleInfo.SyncSourceID) { //new source this.currentSyncSourceID = sampleInfo.SyncSourceID; this.firstSampleTick = sampleInfo.SampleTick - this.writePosition / 2; if (this.firstSampleTick < 0) { SharedUtils.Logger.Log("Changing VoIP first sample tick from " + this.firstSampleTick + " to 0", SharedUtils.Logger.EventLogEntryType.Warning); this.firstSampleTick = 0; } } this.audioStream.tempFileStream.Position = sampleInfo.TempFsPosition; byte[] inputBuffer = new byte[sampleInfo.DataLength]; int inputBytesRead = this.audioStream.tempFileStream.Read(inputBuffer, 0, inputBuffer.Length); if (this.insertSilenceOnMissingSamples) { while (sampleInfo.SampleTick > firstSampleTick + this.writePosition / 2) { byte[] firstSample = Utils.ByteConverter.ToByteArray((ushort)this.decompressionTable[inputBuffer[0]], true); this.pcm16Stream.Write(firstSample, 0, 2);//write first value of the input stream this.writePosition += 2; } } //byte[] pcm16 = new byte[inputBytesRead * 2]; for (int i = 0; i < inputBytesRead; i++) { //byte[] b2 = Utils.ByteConverter.ToByteArray((ushort)decompressionTable[inputBuffer[i]], true); //pcm16[2 * i] = b2[0]; //pcm16[2 * i + 1] = b2[1]; this.pcm16Stream.Write(Utils.ByteConverter.ToByteArray((ushort)decompressionTable[inputBuffer[i]], true), 0, 2); this.writePosition += 2; } //this.pcm16Stream.Write(pcm16, 0, pcm16.Length); //this.writePosition += pcm16.Length; this.sampleChunkIndex++; } } this.pcm16Stream.Position = this.readPosition; bytesRead = this.pcm16Stream.Read(buffer, offset, count); this.readPosition += bytesRead; } return(bytesRead); }
public FileTransfer.FileStreamAssembler MergeAsStereoWavAssembler(AudioStream other) { uint sampleRate = 8000; FileTransfer.WavFileAssembler mergedAssembler = new FileTransfer.WavFileAssembler("MergedAudioStreams-" + FiveTuple.GetHashCode() + ".wav", this.fileStreamAssemblerList, this.FiveTuple, FileTransfer.FileStreamTypes.RTP, this.initialFrameNumber, this.StartTime, sampleRate); //figure out if sample rates match with sampleTicks etc. //double thisSampleRateSkew = this.GetSampleTicksPerSecond() / sampleRate; //double otherSampleRateSkew = other.GetSampleTicksPerSecond() / sampleRate; //if (thisSampleRateSkew > 0.9 && thisSampleRateSkew < 1.11 && otherSampleRateSkew > 0.9 && otherSampleRateSkew < 1.11) { //figure out the correct start time and lock that to a sampleTick double nanosecondHundredsPerSample = 10000000.0 / sampleRate;//8000Hz => 1250 SampleChunkInfo thisTimeReference = this.GetMinJitterTimeReference(sampleRate); TimeSpan thisTicksReferenceOffset = new TimeSpan((long)(nanosecondHundredsPerSample * ((int)thisTimeReference.SampleTick - this.sampleInfo[0].SampleTick))); DateTime thisFirstSampleTimestamp = thisTimeReference.Timestamp.Subtract(thisTicksReferenceOffset); SampleChunkInfo otherTimeReference = other.GetMinJitterTimeReference(sampleRate); TimeSpan otherTicksReferenceOffset = new TimeSpan((long)(nanosecondHundredsPerSample * ((int)otherTimeReference.SampleTick - other.sampleInfo[0].SampleTick))); DateTime otherFirstSampleTimestamp = otherTimeReference.Timestamp.Subtract(otherTicksReferenceOffset); long thisSampleTicksOffset, otherSampleTicksOffset; if (thisFirstSampleTimestamp < otherFirstSampleTimestamp) { thisSampleTicksOffset = this.sampleInfo[0].SampleTick; otherSampleTicksOffset = otherTimeReference.SampleTick - (long)(otherTimeReference.Timestamp.Subtract(thisFirstSampleTimestamp).Ticks / nanosecondHundredsPerSample); } else { thisSampleTicksOffset = thisTimeReference.SampleTick - (long)(thisTimeReference.Timestamp.Subtract(otherFirstSampleTimestamp).Ticks / nanosecondHundredsPerSample); otherSampleTicksOffset = other.sampleInfo[0].SampleTick; } var thisLastTuple = this.sampleInfo[this.sampleInfo.Count - 1]; var otherLastTuple = other.sampleInfo[other.sampleInfo.Count - 1]; //uint metaDataLength = 0; mergedAssembler.TryActivate(); //nSamples might be incorrect here Pcm16BitSampleStream thisStream = new Pcm16BitSampleStream(this, thisSampleTicksOffset, true); Pcm16BitSampleStream otherStream = new Pcm16BitSampleStream(other, otherSampleTicksOffset, true); //uint nSamples = (uint)Math.Max(thisLastTuple.SampleTick + thisLastTuple.DataLength - thisSampleTicksOffset, otherLastTuple.SampleTick + otherLastTuple.DataLength - otherSampleTicksOffset); uint nSamples = mergedAssembler.CountSamplesInStreams(thisStream, otherStream); //reset positions thisStream.Position = 0; otherStream.Position = 0; mergedAssembler.WriteSampleStreamToFile(nSamples, thisStream, otherStream); //mergedAssembler.FinishAssembling(); return(mergedAssembler); }