I'm trying to run primitive game server. So far I get to this point (commented line) and server runs smootly. But if I send object again from client, it doens't update more than once.
So for example, client sends serialized player object with new position Vector2(400,50), but server deserliaze it to object that got old position.
Player code
namespace Commons.Game
{
[Serializable]
public class Unit
{
#region Fields
public int ID;
public Vector2 position;
public string name;
public int HP;
public int XP;
public int Lvl;
public bool active;
public float speed;
public string password;
#endregion
public Unit(Vector2 position, int HP, float speed, string name, string password, int ID)
{
active = true;
this.position = position;
this.HP = HP;
this.XP = 0;
this.speed = speed;
this.name = name;
this.Lvl = 1;
this.password = password;
this.ID = ID;
}
Server code
namespace SocketServer.Connection
{
class Server
{
#region Fields
Unit[] players;
UdpClient playersData;
Thread INHandlePlayers;
BinaryFormatter bf;
IPEndPoint playersEP;
#endregion
public Server()
{
this.players = new Unit[5];
bf = new BinaryFormatter();
this.playersData = new UdpClient(new IPEndPoint(IPAddress.Any, 3001));
this.playersEP = new IPEndPoint(IPAddress.Any, 3001);
this.INHandlePlayers = new Thread(new ThreadStart(HandleIncomePlayers));
this.INHandlePlayers.Name = "Handle income players.";
this.INHandlePlayers.Start();
}
private void HandleIncomePlayers()
{
Console.Out.WriteLine("Players income handler started.");
MemoryStream ms = new MemoryStream();
while (true)
{
byte[] data = playersData.Receive(ref playersEP);
ms.Write(data, 0, data.Length);
ms.Position = 0;
Unit player = null;
player = bf.Deserialize(ms) as Unit; //<-- 1st deserialization is OK, bu after another client update, object doesn't change. So I change Vector with position at client, that sends correct position I want, but at server side position doesn't change after first deserialization.
Console.Out.WriteLine(player.name + " " + player.position.X + " " + player.position.Y);
ms.Flush();
for (int i = 0; i < players.Length; i++)
{
if (players[i] != null && player.ID == players[i].ID)
{
players[i] = player;
break;
}
}
}
}
}
Dude, I'm not quite sure about it, but I'd instantiate the MemoryStream object inside your loop, like below.
I can't try this code right know, sorry about that.
private void HandleIncomePlayers()
{
Console.Out.WriteLine("Players income handler started.");
MemoryStream ms;
while (true)
{
ms = new MemoryStream(); //here
byte[] data = playersData.Receive(ref playersEP);
ms.Write(data, 0, data.Length);
ms.Position = 0;
Unit player = null;
player = bf.Deserialize(ms) as Unit;
Console.Out.WriteLine(player.name + " " + player.position.X + " " + player.position.Y);
ms.Dispose(); //and here
for (int i = 0; i < players.Length; i++)
{
if (players[i] != null && player.ID == players[i].ID)
{
players[i] = player;
break;
}
}
}
}
Good day user1181369,
I've not used MemoryStream before myself, but I've been doing a little research through the MSDN library and my suspicion is that 'ms' is not clearing and all data you've loaded is actually being added to the stream, rather than replacing it.
MemorySteam.Flush(), for example, doesn't actually do anything (http://msdn.microsoft.com/en-us/library/system.io.memorystream.flush.aspx). If this is the case, then the for-loop would break upon finding the first instance of a specific player ID, not finding the newer versions.
Also, I am uncertain how the code you have supplied would deserialise multiple players, but that is outside the scope of this question and also, perhaps, beyond my current field of knowledge.
Regrettably, while I think I may have diagnosed the problem, I am not equipped at this point to offer a solution beyond instantiating a new memorystream in each loop.
Related
So I have an infinite platformer runner game where I keep values of total travelled distance and total collected coins during the game. From this video, my boy Brackeys taught how to save and load critical data by formatting the data using Binary Formatter. I used his code to create my own high score saving system. I can save highest travelled distance. Check above code:
HighScoreData.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class HighScoreData
{
public float bestDistanceCount;
public HighScoreData(Player player){
bestDistanceCount = player.distanceCount;
}
}
HighScoreSaveSystem.cs
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static class HighScoreSaveSystem
{
public static void SaveHighScore(Player player){
BinaryFormatter formatter = new BinaryFormatter();
string path = Application.persistentDataPath + "/highscore.highscorefile";
FileStream stream = new FileStream(path,FileMode.Create);
HighScoreData data = new HighScoreData(player);
formatter.Serialize(stream,data);
stream.Close();
}
public static HighScoreData LoadHighScore(){
string path = Application.persistentDataPath + "/highscore.highscorefile";
if(File.Exists(path)){
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path,FileMode.Open);
HighScoreData data = formatter.Deserialize(stream) as HighScoreData;
stream.Close();
return data;
}
else{
Debug.LogError("Save file not found!");
return null;
}
}
}
And by calling these methods everytime my player die in KillPlayer() method,
public void KillPlayer(){
isDead = true;
HighScoreData data = HighScoreSaveSystem.LoadHighScore();
if(distanceCount > data.bestDistanceCount){
HighScoreSaveSystem.SaveHighScore(this);
}
Time.timeScale = 0f;
}
This is working just fine. But when it comes to saving coins, I couldn't figure it out.
I have to create a variable in this binary file which will take 0 when player installs the game. And everytime player die, coins collected in that level should be added to the one I keep in my binary file. But I don't know how to implement it.
What did I try?
I tried adding totalCoins variable to HighScoreData.cs:
HighScoreData.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class HighScoreData
{
public float bestDistanceCount;
public int totalCoins;
public HighScoreData(Player player){
bestDistanceCount = player.distanceCount;
totalCoins += player.coinCount;
}
}
And in my KillPlayer() method, I tried to store that totalCoins data in a temp variable, add current coin count to that temp variable and update data.totalCoins with the temp variable, which named totalCoinRef.
public void KillPlayer(){
isDead = true;
HighScoreData data = HighScoreSaveSystem.LoadHighScore();
int totalCoinRef = data.totalCoins;
if(distanceCount > data.bestDistanceCount){
totalCoinRef += coinCount;
data.totalCoins = totalCoinRef;
HighScoreSaveSystem.SaveHighScore(this);
}
totalCoinRef += coinCount;
data.totalCoins = totalCoinRef;
HighScoreSaveSystem.SaveHighScore(this);
Time.timeScale = 0f;
}
Result:
This solution only keeps the count of coins collected each level. It is not keeping a sum of total coins. For example if I collect 5 coins, data.TotalCoins will return 5. If i collect 6 coins, data.TotalCoins will return 6. I need it to return 11.
I hope this is clear. Thanks a lot for your time.
In general STOP USING BinaryFormatter!
Use other formats e.g. (JSON, raw text file, etc) or a custom binary serialization.
Then in SaveHighScore you do
HighScoreData data = new HighScoreData(player);
completely ignoring any previous existing data!
You seem to believe that
int totalCoinRef = data.totalCoins;
creates a reference so editing totalCoinRef will also automatically edit data.totalCoins .. it does not! int is a Value Type and therefore you create a COPY of the value which is in no way connected to the data anymore at all!
I would always rather directly pass in a HighScoreData and go (will simply demo using JSON for now)
[System.Serializable]
public class HighScoreData
{
public float bestDistanceCount;
public int totalCoins;
}
and
private static readonly string path = Path.Combine(Application.persistentDataPath, "highscore.highscorefile");
public static void SaveHighScore(HighScoreData data)
{
var json = JsonUtility.ToJson(data, true);
File.WriteAllText(path, json);
}
public static HighScoreData LoadHighScore()
{
if(!File.Exists(path)) return new HighScoreData();
var json = File.ReadAllText(path);
return JsonUtility.FromJson<HighScoreData>(json);
}
And then finally in your player
public void KillPlayer()
{
isDead = true;
var data = HighScoreSaveSystem.LoadHighScore();
data.totalCoins += coinCount;
data.bestDistanceCount = Mathf.Max(data.bestDistanceCount, distanceCount);
HighScoreSaveSystem.SaveHighScore(data);
Time.timeScale = 0f;
}
If you want to stay binary you can also look into BinaryWriter and BinaryReader .. in your case JSON might be a bit overkill since you anyway only really need 8 bytes .. 4 for the float bestDistanceCount and 4 for the int totalCoins. JSON of course adds quite some overhead and string parsing is always more expensive than simple byte conversions
Might look like e.g.
public static void SaveHighScore(HighScoreData data)
{
using(var stream = File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write)
{
using(var writer = new BinaryWriter(stream))
{
writer.Write(data.bestDistanceCount);
writer.Write(data.totalCoins);
}
}
}
public static HighScoreData LoadHighScore()
{
var data = new HighScoreData();
if(File.Exists(path))
{
using(var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)
{
using(var reader = new BinaryReader(stream))
{
data.bestDistanceCount = reader.ReadSingle();
data.totalCoins = reader.ReadInt32();
}
}
}
return data;
}
Either way (even - and maybe especially - using BinaryFormatter) have in mind that a user has full access to the persistent data and can just modify their values there, you can use some encryption to make it harder .. but in order to fully prevent cheating you will need a server solution ;)
on this line, which is line 10 it says "AudioClip" does not take a constructor that takes 0 arguments. How to fix it? I know that AudioClip is a default class in Unity. What parameters shall I pass there or how to solve this issue?
AudioClip _clipRecord = new AudioClip()
int _sampleWindow = 128;
How to solve it?
public class MicInput : MonoBehaviour {
public static float MicLoudness;
private string _device;
//mic initialization
void InitMic(){
if(_device == null) _device = Microphone.devices[0];
_clipRecord = Microphone.Start(_device, true, 999, 44100);
}
void StopMicrophone()
{
Microphone.End(_device);
}
AudioClip _clipRecord = new AudioClip();
int _sampleWindow = 128;
//get data from microphone into audioclip
float LevelMax()
{
float levelMax = 0;
float[] waveData = new float[_sampleWindow];
int micPosition = Microphone.GetPosition(null)-(_sampleWindow+1); // null means the first microphone
if (micPosition < 0) return 0;
_clipRecord.GetData(waveData, micPosition);
// Getting a peak on the last 128 samples
for (int i = 0; i < _sampleWindow; i++) {
float wavePeak = waveData[i] * waveData[i];
if (levelMax < wavePeak) {
levelMax = wavePeak;
}
}
return levelMax;
}
void Update()
{
// levelMax equals to the highest normalized value power 2, a small number because < 1
// pass the value to a static var so we can access it from anywhere
MicLoudness = LevelMax ();
}
bool _isInitialized;
// start mic when scene starts
void OnEnable()
{
InitMic();
_isInitialized=true;
}
//stop mic when loading a new level or quit application
void OnDisable()
{
StopMicrophone();
}
void OnDestroy()
{
StopMicrophone();
}
// make sure the mic gets started & stopped when application gets focused
void OnApplicationFocus(bool focus) {
if (focus)
{
//Debug.Log("Focus");
if(!_isInitialized){
//Debug.Log("Init Mic");
InitMic();
_isInitialized=true;
}
}
if (!focus)
{
//Debug.Log("Pause");
StopMicrophone();
//Debug.Log("Stop Mic");
_isInitialized=false;
}
}
}
on this line, which is line 10 it says "AudioClip " does not take a constructor that takes 0 arguments. How to fix it?
----AudioClip _clipRecord = new AudioClip()----
---int _sampleWindow = 128;----
How to solve it?
In the AudioClip class add the following:
public AudioClip(){}
This issue arises because although you do initially get an empty ctor when you create a class, you lose that once you create a non-empty ctor so you MUST add the empty one if you wish to use it.
In the new unity version just change new AudioClip();
with
AudioClip _clipRecord = null;
Hope it helps
I'm making an audio player using XAudio2. We are streaming data in packets of 640 bytes, at a sample rate of 8000Hz and sample depth of 16 bytes. We are using SlimDX to access XAudio2.
But when playing sound, we are noticing that the sound quality is bad. This, for example, is a 3KHz sine curve, captured with Audacity.
I have condensed the audio player to the bare basics, but the audio quality is still bad. Is this a bug in XAudio2, SlimDX, or my code, or is this simply an artifact that occurs when one go from 8KHz to 44.1KHz? The last one seems unreasonable, as we also generate PCM wav files which are played perfectly by Windows Media Player.
The following is the basic implementation, which generates the broken Sine.
public partial class MainWindow : Window
{
private XAudio2 device = new XAudio2();
private WaveFormatExtensible format = new WaveFormatExtensible();
private SourceVoice sourceVoice = null;
private MasteringVoice masteringVoice = null;
private Guid KSDATAFORMAT_SUBTYPE_PCM = new Guid("00000001-0000-0010-8000-00aa00389b71");
private AutoResetEvent BufferReady = new AutoResetEvent(false);
private PlayBufferPool PlayBuffers = new PlayBufferPool();
public MainWindow()
{
InitializeComponent();
Closing += OnClosing;
format.Channels = 1;
format.BitsPerSample = 16;
format.FormatTag = WaveFormatTag.Extensible;
format.BlockAlignment = (short)(format.Channels * (format.BitsPerSample / 8));
format.SamplesPerSecond = 8000;
format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlignment;
format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
private void OnClosing(object sender, CancelEventArgs cancelEventArgs)
{
sourceVoice.Stop();
sourceVoice.Dispose();
masteringVoice.Dispose();
PlayBuffers.Dispose();
}
private void button_Click(object sender, RoutedEventArgs e)
{
masteringVoice = new MasteringVoice(device);
PlayBuffer buffer = PlayBuffers.NextBuffer();
GenerateSine(buffer.Buffer);
buffer.AudioBuffer.AudioBytes = 640;
sourceVoice = new SourceVoice(device, format, VoiceFlags.None, 8);
sourceVoice.BufferStart += new EventHandler<ContextEventArgs>(sourceVoice_BufferStart);
sourceVoice.BufferEnd += new EventHandler<ContextEventArgs>(sourceVoice_BufferEnd);
sourceVoice.SubmitSourceBuffer(buffer.AudioBuffer);
sourceVoice.Start();
}
private void sourceVoice_BufferEnd(object sender, ContextEventArgs e)
{
BufferReady.Set();
}
private void sourceVoice_BufferStart(object sender, ContextEventArgs e)
{
BufferReady.WaitOne(1000);
PlayBuffer nextBuffer = PlayBuffers.NextBuffer();
nextBuffer.DataStream.Position = 0;
nextBuffer.AudioBuffer.AudioBytes = 640;
GenerateSine(nextBuffer.Buffer);
Result r = sourceVoice.SubmitSourceBuffer(nextBuffer.AudioBuffer);
}
private void GenerateSine(byte[] buffer)
{
double sampleRate = 8000.0;
double amplitude = 0.25 * short.MaxValue;
double frequency = 3000.0;
for (int n = 0; n < buffer.Length / 2; n++)
{
short[] s = { (short)(amplitude * Math.Sin((2 * Math.PI * n * frequency) / sampleRate)) };
Buffer.BlockCopy(s, 0, buffer, n * 2, 2);
}
}
}
public class PlayBuffer : IDisposable
{
#region Private variables
private IntPtr BufferPtr;
private GCHandle BufferHandle;
#endregion
#region Constructors
public PlayBuffer()
{
Index = 0;
Buffer = new byte[640 * 4]; // 640 = 30ms
BufferHandle = GCHandle.Alloc(this.Buffer, GCHandleType.Pinned);
BufferPtr = new IntPtr(BufferHandle.AddrOfPinnedObject().ToInt32());
DataStream = new DataStream(BufferPtr, 640 * 4, true, false);
AudioBuffer = new AudioBuffer();
AudioBuffer.AudioData = DataStream;
}
public PlayBuffer(int index)
: this()
{
Index = index;
}
#endregion
#region Destructor
~PlayBuffer()
{
Dispose();
}
#endregion
#region Properties
protected int Index { get; private set; }
public byte[] Buffer { get; private set; }
public DataStream DataStream { get; private set; }
public AudioBuffer AudioBuffer { get; private set; }
#endregion
#region Public functions
public void Dispose()
{
if (AudioBuffer != null)
{
AudioBuffer.Dispose();
AudioBuffer = null;
}
if (DataStream != null)
{
DataStream.Dispose();
DataStream = null;
}
}
#endregion
}
public class PlayBufferPool : IDisposable
{
#region Private variables
private int _currentIndex = -1;
private PlayBuffer[] _buffers = new PlayBuffer[2];
#endregion
#region Constructors
public PlayBufferPool()
{
for (int i = 0; i < 2; i++)
Buffers[i] = new PlayBuffer(i);
}
#endregion
#region Desctructor
~PlayBufferPool()
{
Dispose();
}
#endregion
#region Properties
protected int CurrentIndex
{
get { return _currentIndex; }
set { _currentIndex = value; }
}
protected PlayBuffer[] Buffers
{
get { return _buffers; }
set { _buffers = value; }
}
#endregion
#region Public functions
public void Dispose()
{
for (int i = 0; i < Buffers.Length; i++)
{
if (Buffers[i] == null)
continue;
Buffers[i].Dispose();
Buffers[i] = null;
}
}
public PlayBuffer NextBuffer()
{
CurrentIndex = (CurrentIndex + 1) % Buffers.Length;
return Buffers[CurrentIndex];
}
#endregion
}
Some extra details:
This is used to replay recorded voice with various compression such as ALAW, µLAW or TrueSpeech. The data is sent in small packets, decoded and sent to this player. This is the reason for why we're using so low sampling rate, and so small buffers.
There are no problems with our data, however, as generating a WAV file with the data results in perfect replay by WMP or VLC.
edit: We have now "solved" this by rewriting the player in NAudio.
I'd still be interested in any input as to what is happening here. Is it our approach in the PlayBuffers, or is it simply a bug/limitation in DirectX, or the wrappers? I tried using SharpDX instead of SlimDX, but that did not change the result anything.
It looks as if the upsampling is done without a proper anti-aliasing (reconstruction) filter. The cutoff frequency is far too high (above the original Nyquist frequency) and therefore a lot of the aliases are being preserved, resulting in output resembling piecewise-linear interpolation between the samples taken at 8000 Hz.
Although all your different options are doing an upconversion from 8kHz to 44.1kHz, the way in which they do that is important, and the fact that one library does it well is no proof that the upconversion is not the source of error in the other.
It's been a while since I worked with sound and frequencies, but here is what I remember: You have a sample rate of 8000Hz and want a sine frequency of 3000Hz. So for 1 second you have 8000 samples and in that second you want your sine to oscillate 3000 times. That is below the Nyquist-frequency (half your sample rate) but barely (see Nyquist–Shannon sampling theorem). So I would not expect a good quality here.
In fact: step through the GenerateSine-method and you'll see that s[0] will contain the values 0, 5792, -8191, 5792, 0, -5792, 8191, -5792, 0, 5792...
None the less this doesn't explain the odd sine you recorded back and I'm not sure how much samples the human ear need to hear a "good" sine wave.
Whole day I was looking for some tutorial or piece of code, "just" to play simple sin wave for "infinity" time. I know it sounds a little crazy.
But I want to be able to change frequency of tone in time, for instance - increase it.
Imagine that I want to play tone A, and increase it to C in "+5" frequency steps each 3ms (it's really just example), don't want to have free places, stop the tone.
Is it possible? Or can you help me?
Use NAudio library for audio output.
Make notes wave provider:
class NotesWaveProvider : WaveProvider32
{
public NotesWaveProvider(Queue<Note> notes)
{
this.Notes = notes;
}
public readonly Queue<Note> Notes;
int sample = 0;
Note NextNote()
{
for (; ; )
{
if (Notes.Count == 0)
return null;
var note = Notes.Peek();
if (sample < note.Duration.TotalSeconds * WaveFormat.SampleRate)
return note;
Notes.Dequeue();
sample = 0;
}
}
public override int Read(float[] buffer, int offset, int sampleCount)
{
int sampleRate = WaveFormat.SampleRate;
for (int n = 0; n < sampleCount; n++)
{
var note = NextNote();
if (note == null)
buffer[n + offset] = 0;
else
buffer[n + offset] = (float)(note.Amplitude * Math.Sin((2 * Math.PI * sample * note.Frequency) / sampleRate));
sample++;
}
return sampleCount;
}
}
class Note
{
public float Frequency;
public float Amplitude = 1.0f;
public TimeSpan Duration = TimeSpan.FromMilliseconds(50);
}
start play:
WaveOut waveOut;
this.Notes = new Queue<Note>(new[] { new Note { Frequency = 1000 }, new Note { Frequency = 1100 } });
var waveProvider = new NotesWaveProvider(Notes);
waveProvider.SetWaveFormat(16000, 1); // 16kHz mono
waveOut = new WaveOut();
waveOut.Init(waveProvider);
waveOut.Play();
add new notes:
void Timer_Tick(...)
{
if (Notes.Count < 10)
Notes.Add(new Note{Frecuency = 900});
}
ps this code is idea only. for real using add mt-locking etc
use NAudio and SineWaveProvider32: http://mark-dot-net.blogspot.com/2009/10/playback-of-sine-wave-in-naudio.html
private WaveOut waveOut;
private void button1_Click(object sender, EventArgs e)
{
StartStopSineWave();
}
private void StartStopSineWave()
{
if (waveOut == null)
{
var sineWaveProvider = new SineWaveProvider32();
sineWaveProvider.SetWaveFormat(16000, 1); // 16kHz mono
sineWaveProvider.Frequency = 1000;
sineWaveProvider.Amplitude = 0.25f;
waveOut = new WaveOut();
waveOut.Init(sineWaveProvider);
waveOut.Play();
}
else
{
waveOut.Stop();
waveOut.Dispose();
waveOut = null;
}
}
at the moment im trying to figure out how i can manage to play a wave file in C# by filling up the secondary buffer with data from the wave file through threading and then play the wave file.
Any help or sample coding i can use?
thanks
sample code being used:
public delegate void PullAudio(short[] buffer, int length);
public class SoundPlayer : IDisposable
{
private Device soundDevice;
private SecondaryBuffer soundBuffer;
private int samplesPerUpdate;
private AutoResetEvent[] fillEvent = new AutoResetEvent[2];
private Thread thread;
private PullAudio pullAudio;
private short channels;
private bool halted;
private bool running;
public SoundPlayer(Control owner, PullAudio pullAudio, short channels)
{
this.channels = channels;
this.pullAudio = pullAudio;
this.soundDevice = new Device();
this.soundDevice.SetCooperativeLevel(owner, CooperativeLevel.Priority);
// Set up our wave format to 44,100Hz, with 16 bit resolution
WaveFormat wf = new WaveFormat();
wf.FormatTag = WaveFormatTag.Pcm;
wf.SamplesPerSecond = 44100;
wf.BitsPerSample = 16;
wf.Channels = channels;
wf.BlockAlign = (short)(wf.Channels * wf.BitsPerSample / 8);
wf.AverageBytesPerSecond = wf.SamplesPerSecond * wf.BlockAlign;
this.samplesPerUpdate = 512;
// Create a buffer with 2 seconds of sample data
BufferDescription bufferDesc = new BufferDescription(wf);
bufferDesc.BufferBytes = this.samplesPerUpdate * wf.BlockAlign * 2;
bufferDesc.ControlPositionNotify = true;
bufferDesc.GlobalFocus = true;
this.soundBuffer = new SecondaryBuffer(bufferDesc, this.soundDevice);
Notify notify = new Notify(this.soundBuffer);
fillEvent[0] = new AutoResetEvent(false);
fillEvent[1] = new AutoResetEvent(false);
// Set up two notification events, one at halfway, and one at the end of the buffer
BufferPositionNotify[] posNotify = new BufferPositionNotify[2];
posNotify[0] = new BufferPositionNotify();
posNotify[0].Offset = bufferDesc.BufferBytes / 2 - 1;
posNotify[0].EventNotifyHandle = fillEvent[0].Handle;
posNotify[1] = new BufferPositionNotify();
posNotify[1].Offset = bufferDesc.BufferBytes - 1;
posNotify[1].EventNotifyHandle = fillEvent[1].Handle;
notify.SetNotificationPositions(posNotify);
this.thread = new Thread(new ThreadStart(SoundPlayback));
this.thread.Priority = ThreadPriority.Highest;
this.Pause();
this.running = true;
this.thread.Start();
}
public void Pause()
{
if (this.halted) return;
this.halted = true;
Monitor.Enter(this.thread);
}
public void Resume()
{
if (!this.halted) return;
this.halted = false;
Monitor.Pulse(this.thread);
Monitor.Exit(this.thread);
}
private void SoundPlayback()
{
lock (this.thread)
{
if (!this.running) return;
// Set up the initial sound buffer to be the full length
int bufferLength = this.samplesPerUpdate * 2 * this.channels;
short[] soundData = new short[bufferLength];
// Prime it with the first x seconds of data
this.pullAudio(soundData, soundData.Length);
this.soundBuffer.Write(0, soundData, LockFlag.None);
// Start it playing
this.soundBuffer.Play(0, BufferPlayFlags.Looping);
int lastWritten = 0;
while (this.running)
{
if (this.halted)
{
Monitor.Pulse(this.thread);
Monitor.Wait(this.thread);
}
// Wait on one of the notification events
WaitHandle.WaitAny(this.fillEvent, 3, true);
// Get the current play position (divide by two because we are using 16 bit samples)
int tmp = this.soundBuffer.PlayPosition / 2;
// Generate new sounds from lastWritten to tmp in the sound buffer
if (tmp == lastWritten)
{
continue;
}
else
{
soundData = new short[(tmp - lastWritten + bufferLength) % bufferLength];
}
this.pullAudio(soundData, soundData.Length);
// Write in the generated data
soundBuffer.Write(lastWritten * 2, soundData, LockFlag.None);
// Save the position we were at
lastWritten = tmp;
}
}
}
public void Dispose()
{
this.running = false;
this.Resume();
if (this.soundBuffer != null)
{
this.soundBuffer.Dispose();
}
if (this.soundDevice != null)
{
this.soundDevice.Dispose();
}
}
}
}
The concept is the same that im using but i can't manage to get a set on wave byte [] data to play
I have not done this.
But the first place i would look is XNA.
I know that the c# managed directx project was ditched in favor of XNA and i have found it to be good for graphics - i prefer using it to directx.
what is the reason that you decided not to just use soundplayer, as per this msdn entry below?
private SoundPlayer Player = new SoundPlayer();
private void loadSoundAsync()
{
// Note: You may need to change the location specified based on
// the location of the sound to be played.
this.Player.SoundLocation = http://www.tailspintoys.com/sounds/stop.wav";
this.Player.LoadAsync();
}
private void Player_LoadCompleted (
object sender,
System.ComponentModel.AsyncCompletedEventArgs e)
{
if (this.Player.IsLoadCompleted)
{
this.Player.PlaySync();
}
}
usually i just load them all up in a thread, or asynch delegate, then play or playsynch them when needed.
You can use the DirectSound support in SlimDX: http://slimdx.org/ :-)
You can use nBASS or better FMOD both are great audio libraries and can work nicely together with .NET.
DirectSound is where you want to go. It's a piece of cake to use, but I'm not sure what formats it can play besides .wav
http://msdn.microsoft.com/en-us/library/windows/desktop/ee416960(v=vs.85).aspx