private void CreatePlayback() { Playback = OpenALHelper.PlaybackDevices[0].OpenStream(Voice.SampleRate, OpenALAudioFormat.Mono16Bit); Playback.Listener.Position = new Vector3() { X = 0.0f, Y = 0.0f, Z = 0.0f }; Playback.Listener.Velocity = new Vector3() { X = 0.0f, Y = 0.0f, Z = 0.0f }; Playback.Listener.Orientation = new Orientation() { At = new Vector3() { X = 0.0f, Y = 0.0f, Z = 1.0f }, Up = new Vector3() { X = 0.0f, Y = 1.0f, Z = 0.0f } }; Playback.ALPosition = new Vector3() { X = 0.0f, Y = 0.0f, Z = 0.0f }; Playback.Velocity = new Vector3() { X = 0.0f, Y = 0.0f, Z = 0.0f }; }
private void CreatePlayback() { Playback = OpenALHelper.PlaybackDevices[0].OpenStream(Voice.SampleRate, OpenALAudioFormat.Mono16Bit); Playback.Listener.Position = new Vector3(0f, 0f, 0f); Playback.Listener.Velocity = new Vector3(0f, 0f, 0f); Playback.Listener.Orientation = new Orientation(new Vector3(0f, 0f, 1f), new Vector3(0f, 1f, 0f)); Playback.ALPosition = new Vector3(0f, 0f, 0f); Playback.Velocity = new Vector3(0f, 0f, 0f); Playback.SetVolume(Voice.Volume / 100f); }
// Playback public byte[] StartPlayback() { if (_mode != Mode.Playback) { Debug.LogError("SessionCapture: Cannot call StartPlayback on record session."); return(null); } if (_playing) { Debug.LogError("SessionCapture: StartPlayback() has been called on a session that's already playing. Ignoring. This is a bug!"); return(null); } // Create a queue to hold updates that are ready for playback _playbackDeltaUpdates = new Queue <DeltaUpdate>(); // Create a dictionary of deserialized incoming reliable updates. These will be used to calculate roundtrip time. _clientToIncomingReliableUpdatesMap = new Dictionary <int, Dictionary <uint, DeltaUpdate> >(); // Create a queue to hold updates that we've deserialized from the primary stream that haven't been processed yet. _primaryPlaybackDeltaUpdatesToBeProcessed = new Queue <DeltaUpdate>(); // Open up file stream for the primary capture file _primaryPlaybackStream = new PlaybackStream(_primaryPlaybackFilePath); // Read the file header + initial datastore byte[] datastore = _primaryPlaybackStream.ReadHeader(); // Start playback at the primary session capture start time. _playbackTime = _primaryPlaybackStream.startTimestamp; // Open up file streams for secondary capture files _secondaryPlaybackStreams = new Dictionary <int, PlaybackStream>(); if (_secondaryPlaybackFilePaths != null) { foreach (string secondaryPlaybackFilePath in _secondaryPlaybackFilePaths) { PlaybackStream playbackStream = new PlaybackStream(secondaryPlaybackFilePath); playbackStream.ReadHeader(); // Fast forward to the primary stream start time playbackStream.SkipToTime(_playbackTime); _secondaryPlaybackStreams.Add(playbackStream.clientID, playbackStream); } } // Mark the session as playing _playing = true; return(datastore); }
static void Main(string[] args) { _readBuffer = new byte[960]; Console.WriteLine("Opening \"{0}\" for playback", OpenALHelper.PlaybackDevices[0].DeviceName); Console.WriteLine("Opening \"{0}\" for capture", OpenALHelper.CaptureDevices[0].DeviceName); _playback = OpenALHelper.PlaybackDevices[0].OpenStream(48000, OpenALAudioFormat.Mono16Bit); _playback.Listener.Position = new Vector3() { X = 0.0f, Y = 0.0f, Z = 0.0f }; _playback.Listener.Velocity = new Vector3() { X = 0.0f, Y = 0.0f, Z = 0.0f }; _playback.Listener.Orientation = new Orientation() { At = new Vector3() { X = 0.0f, Y = 0.0f, Z = 1.0f }, Up = new Vector3() { X = 0.0f, Y = 1.0f, Z = 0.0f } }; _playback.ALPosition = new Vector3() { X = 0.0f, Y = 0.0f, Z = 0.0f }; _playback.Velocity = new Vector3() { X = 0.0f, Y = 0.0f, Z = 0.0f }; _capture = OpenALHelper.CaptureDevices[0].OpenStream(48000, OpenALAudioFormat.Mono16Bit, 10); _capture.BeginRead(_readBuffer, 0, _readBuffer.Length, Callback, null); while (true) { PrintUI(); if (ProcessInput()) { break; } } _playback.Close(); _playback.Dispose(); _capture.Close(); _capture.Dispose(); }
public void StopPlayback() { // Dispose playback streams if (_primaryPlaybackStream != null) { _primaryPlaybackStream.Dispose(); _primaryPlaybackStream = null; } if (_secondaryPlaybackStreams != null) { foreach (KeyValuePair <int, PlaybackStream> secondaryPlaybackStream in _secondaryPlaybackStreams) { secondaryPlaybackStream.Value.Dispose(); } _secondaryPlaybackStreams.Clear(); _secondaryPlaybackStreams = null; } _playing = false; }
private static bool AdjustPlaybackStreamSendTimestampOffsetWithOutgoingReliableDeltaUpdate(PlaybackStream playbackStream, Dictionary <uint, DeltaUpdate> incomingReliableUpdates, DeltaUpdate outgoingReliableDeltaUpdate) { // Check for a matching reliable delta update. If we haven't received it yet, then bail, it's not time to decode this update yet. DeltaUpdate incomingReliableDeltaUpdate; if (incomingReliableUpdates == null || !incomingReliableUpdates.TryGetValue(outgoingReliableDeltaUpdate.updateID, out incomingReliableDeltaUpdate)) { return(false); } // Found a matching reliable update. Use the round trip time to adjust outgoing unreliable updates. playbackStream.sendTimestampOffset = incomingReliableDeltaUpdate.timestamp - outgoingReliableDeltaUpdate.timestamp; // Remove from incoming reliable updates incomingReliableUpdates.Remove(outgoingReliableDeltaUpdate.updateID); return(true); }
private static bool AdjustOutgoingUnreliableDeltaUpdateTimestamp(double playbackTime, PlaybackStream playbackStream, DeltaUpdate deltaUpdate) { // Adjust the timestamp double timestamp = deltaUpdate.timestamp + playbackStream.sendTimestampOffset; // If the update is now newer than the current playback time, then bail. if (timestamp > playbackTime) { return(false); } // Adjust update timestamp deltaUpdate.timestamp += playbackStream.sendTimestampOffset; return(true); }
public void PlaybackTick(double deltaTime) { if (!_playing) { return; } // Increment playback time _playbackTime += deltaTime; // Keep track of whether this frame produced any updates for playback bool didAddDeltaUpdateToPlaybackQueue = false; // Read delta updates from primary playback stream DeltaUpdate deltaUpdate = new DeltaUpdate(); while (_primaryPlaybackStream.ReadDeltaUpdate(_playbackTime, deltaUpdate)) { // Add all outgoing updates to the queue to be processed if (deltaUpdate.outgoing) { _primaryPlaybackDeltaUpdatesToBeProcessed.Enqueue(deltaUpdate); } // If it's an incoming reliable update, keep track so we can use the timestamp to calculate timestamp offsets. if (deltaUpdate.incoming && deltaUpdate.reliable) { // If this is an incoming reliable update, add it directly to the queue of updates to play back. It doesn't need any timestamp adjustments or anything. _playbackDeltaUpdates.Enqueue(deltaUpdate); didAddDeltaUpdateToPlaybackQueue = true; // Retrieve dictionary for this sender. Create a new one if needed. Dictionary <uint, DeltaUpdate> incomingReliableUpdates; if (!_clientToIncomingReliableUpdatesMap.TryGetValue(deltaUpdate.sender, out incomingReliableUpdates)) { incomingReliableUpdates = new Dictionary <uint, DeltaUpdate>(); _clientToIncomingReliableUpdatesMap.Add(deltaUpdate.sender, incomingReliableUpdates); } // Add the update incomingReliableUpdates.Add(deltaUpdate.updateID, deltaUpdate); } // Create a new update for the next loop deltaUpdate = new DeltaUpdate(); } // Adjust the timestamps for outgoing unreliable messages by measuring the roundtrip time on reliable messages. // We're using incoming reliable updates for playback (because the server assigns uniqueIDs and things), but unreliable updates are recorded when they're sent. // That means that the reliable updates will have timestamps that are later than when they were actually sent. To fix this, we adjust the timestamps of the // unreliable updates to account for the roundtrip delay that we're seeing. // Grab the incoming reliable updates for the primary stream's client ID Dictionary <uint, DeltaUpdate> primaryPlaybackClientIncomingReliableUpdates; _clientToIncomingReliableUpdatesMap.TryGetValue(_primaryPlaybackStream.clientID, out primaryPlaybackClientIncomingReliableUpdates); // Loop through updates and process them until we run out or we hit one that requires us to wait for more time to pass / more frames to be decoded while (_primaryPlaybackDeltaUpdatesToBeProcessed.Count > 0) { // Peek at update deltaUpdate = _primaryPlaybackDeltaUpdatesToBeProcessed.Peek(); // Outgoing unreliable update. if (deltaUpdate.outgoing && deltaUpdate.unreliable) { bool success = AdjustOutgoingUnreliableDeltaUpdateTimestamp(_playbackTime, _primaryPlaybackStream, deltaUpdate); // If we were unable to successfully adjust the update, break out of the loop. We'll try again next frame after more time has passed. if (!success) { break; } // Add to outgoing updates _playbackDeltaUpdates.Enqueue(deltaUpdate); didAddDeltaUpdateToPlaybackQueue = true; } // Outgoing reliable update. Find the matching incoming reliable update and use it to adjust the unreliableTimestampOffset else if (deltaUpdate.outgoing && deltaUpdate.reliable) { bool success = AdjustPlaybackStreamSendTimestampOffsetWithOutgoingReliableDeltaUpdate(_primaryPlaybackStream, primaryPlaybackClientIncomingReliableUpdates, deltaUpdate); // If we were unable to successfully adjust the send timestamp offset, break out of the loop. We'll try again next frame after more time has passed. if (!success) { break; } } // If we hit this point, the update has been processed, dequeue it and move onto the next one. _primaryPlaybackDeltaUpdatesToBeProcessed.Dequeue(); } // Loop through each secondary stream. Perform the timestamp adjustments and add them to the playback queue. foreach (KeyValuePair <int, PlaybackStream> secondaryPlaybackStream in _secondaryPlaybackStreams) { int clientID = secondaryPlaybackStream.Key; PlaybackStream playbackStream = secondaryPlaybackStream.Value; // Grab the incoming reliable updates for this stream's clientID Dictionary <uint, DeltaUpdate> incomingReliableUpdates; _clientToIncomingReliableUpdatesMap.TryGetValue(clientID, out incomingReliableUpdates); // Read updates up until playbackTime processing them as we go. Stop if we run out of updates or we hit an update that we can't process yet. deltaUpdate = new DeltaUpdate(); while (playbackStream.ReadDeltaUpdate(_playbackTime, deltaUpdate)) { // Outgoing unreliable update. if (deltaUpdate.outgoing && deltaUpdate.unreliable) { bool success = AdjustOutgoingUnreliableDeltaUpdateTimestamp(_playbackTime, playbackStream, deltaUpdate); // If we were unable to successfully adjust the update, break out of the loop. We'll try again next frame after more time has passed. if (!success) { break; } // Add to outgoing updates _playbackDeltaUpdates.Enqueue(deltaUpdate); didAddDeltaUpdateToPlaybackQueue = true; } // Outgoing reliable update. Find the matching incoming reliable update and use it to adjust the unreliableTimestampOffset else if (deltaUpdate.outgoing && deltaUpdate.reliable) { bool success = AdjustPlaybackStreamSendTimestampOffsetWithOutgoingReliableDeltaUpdate(playbackStream, incomingReliableUpdates, deltaUpdate); // If we were unable to successfully adjust the send timestamp offset, break out of the loop. We'll try again next frame after more time has passed. if (!success) { break; } } } } // If we added any updates to the playback queue, sort it to make sure that they're in order after timestamp adjustments were made. if (didAddDeltaUpdateToPlaybackQueue) { _playbackDeltaUpdates = new Queue <DeltaUpdate>(_playbackDeltaUpdates.OrderBy(playbackDeltaUpdate => playbackDeltaUpdate.timestamp)); } // If we've finished reading updates, we're done playing. _playing = _primaryPlaybackStream.reading; }