How would I fix my MediaPlayer code, that receives bytes, than creates a temp file, that is saving input, but for each input, the player starts from the beginning, and I want it to just play. This is my code:
Java.IO.File temp = Java.IO.File.CreateTempFile("temp", "mp3");
Java.IO.FileOutputStream fos = new Java.IO.FileOutputStream(temp);
Java.IO.FileInputStream fis = new Java.IO.FileInputStream(temp);
temp.DeleteOnExit();
MediaPlayer player = new MediaPlayer();
player.SetDataSource(fis.FD);
// If set here, there is an error
//12-09 17:29:44.472 V/MediaPlayer( 9927): setDataSource(58, 0, 576460752303423487)
//12-09 17:29:44.472 E/MediaPlayer( 9927): Unable to to create media player
while (true)
{
try
{
byte[] myReadBuffer = new byte[10000]; //Input array
mmInStream.Read(myReadBuffer, 0, myReadBuffer.Length); //Reads the incoming array into myReadBuffer
fos.Write(myReadBuffer, 0, myReadBuffer.Length); //Writes it into temp file
MediaPlayer player = new MediaPlayer(); //Creates a new object
player.SetDataSource(fis.FD); // If here, it would just start from the start each time and add more // Sets the data source to temp file
player.Prepare();
player.Start();
while (true)
{
// Checks if it can release resources
if (!player.IsPlaying)
{
player.Release();
break;
}
}
}
catch (System.IO.IOException ex)
{
System.Diagnostics.Debug.WriteLine("Input stream was disconnected", ex);
}
}
I am using Xamari Forms.
Basicaly, I get an array of bytes, that I store in a temporary file, and than try to play them. I know the MediaPlayer is recreated on every loop, since there I define the data source, but if I put it outside of the loop, it would get an error(as above).
Example:
The song starts, is played for about 2s and than it starts over but is now played for 4s, and over again and now for 6s. Each time, more of the song is revealed.
If it was a string it would be like this:
123
123456
123456789
How would I make it to play continuously, but each time it would play a only the new part?
This is a logic issue. Essentially you're writing chunks to a stream, then playing that chunk, then writing more without resetting the stream, then playing from the beginning of that stream.
What you need it to do is write a chunk to your stream, play that stream, then write a new chunk to the stream and play that stream.
Move your Java.IO.FileOutputStream fos = new Java.IO.FileOutputStream(temp); inside of your outer while() loop.
The reason this works is that you're writing to your fos then playing, then writing again but not disposing of your initial buffer data. Moving fos into your while loop forces a new object to be created which will contain the new buffer data, then it will play that. There's going to be an issue with audio skipping because of the loop and having to re-load the new data to be played.
To correct the skip you'll need to load your buffer asynchronously while it's being played. You can do this with a separate thread. You might need to adjust your buffer size or set a buffer condition. MediaPlayer contains a BufferingProgress property that might help.
Related
I'm using C#, WPF, and NAudio.
I play an embedded resource mp3 in the application exe when a key is pressed.
If a key is pressed repeatedly, RAM usage continues to climb past 400MB and never drops.
Using Flush() and Dispose() on the objects doesn't seem to free memory even when GC is called.
This did not used to happen when I played from external resource on the hard drive using string path instead of MemoryStream. It used to stay around 50MB RAM.
public static MemoryStream ms = null;
public static WaveStream wav = null;
public static WaveOutEvent output = null;
// Embedded Resource sound1.mp3
MemoryStream sound1 = new MemoryStream(Properties.Resources.sound1);
// Key Press
//
if (e.Key == Key.Space) {
ms = new MemoryStream(StreamToBytes(sound1));
wav = new Mp3FileReader(ms);
output = new WaveOutEvent();
output.PlaybackStopped += new EventHandler<StoppedEventArgs>(Media_Ended);
output.Init(wav);
output.Play();
}
// MP3 Playback Ended
//
public static void Media_Ended(object sender, EventArgs e)
{
if (output.PlaybackState == PlaybackState.Stopped)
{
ms.Flush();
ms = null;
wav.Close();
output.Dispose();
}
}
// Convert Stream to Byte Array
//
public static byte[] StreamToBytes(MemoryStream stream)
{
...
}
Stream to Byte Array
https://stackoverflow.com/a/1080445/6806643
I convert to Byte Array back to a new Stream or the playback will not layer and will crash if 2 sounds play at once.
It's because you clicking space bar too fast :)
Each key click overwrites variables with new values. So when you click space bar 10 times in few seconds it will create 10 resources. But you keep reference to only last one created. When Media_Ended will start incoming, it will try to dispose only latest created resource.
I have developed a system in which a C# program receives sound buffers (byte arrays) from another subsystem. It is supposed to play the incoming buffers continuously. I searched in the web and I decided to use SoundPlayer. It works perfectly in the offline mode (play the buffers after receiving them all). However, I have a problem in the real-time mode.
In the real-time mode the program at first waits for a number of buffer arrays (for example 200) to receive and accumulate them. Then it adds a wav header and plays it. However, after that for each next 200 arrays it plays repeatedly the first buffer.
I have read following pages:
Play wav/mp3 from memory
https://social.msdn.microsoft.com/Forums/vstudio/en-US/8ac2847c-3e2f-458c-b8ff-533728e267e0/c-problems-with-mediasoundplayer?forum=netfxbcl
and according to their suggestions I implemented my code as follow:
public class MediaPlayer
{
System.Media.SoundPlayer soundPlayer;
public MediaPlayer(byte[] buffer)
{
byte[] headerPlusBuffer = AddWaveHeader(buffer, false, 1, 16, 8000, buffer.Length / 2); //add wav header to the **first** buffer
MemoryStream memoryStream = new MemoryStream(headerPlusBuffer, true);
soundPlayer = new System.Media.SoundPlayer(memoryStream);
}
public void Play()
{
soundPlayer.PlaySync();
}
public void Play(byte[] buffer)
{
soundPlayer.Stream.Seek(0, SeekOrigin.Begin);
soundPlayer.Stream.Write(buffer, 0, buffer.Length);
soundPlayer.PlaySync();
}
}
I use it like this:
MediaPlayer _mediaPlayer;
if (firstBuffer)
{
_mediaPlayer = new MediaPlayer(dataRaw);
_mediaPlayer.Play();
}
else
{
_mediaPlayer.Play(dataRaw);
}
Each time _mediaPlayer.Play(dataRaw) is called, the first buffer is played again and again; the dataRaw is updated though.
I appreciate your help.
I have a video player which downloads a video file in chunks. After a chunk of 1MB has been downloaded, an event is called giving the MediaElement its source, and making it play. Whilst the video is being played, the rest of the 1MB chunks are downloaded until the file is complete. If only 1MB of the video is downloaded, the playback time is equal to 17 seconds(This will come in later).
When the file is completely downloaded, permission is given to the user to change the position of the video or seek it. If the user seeks to a position under or equal to 17 seconds, the MediaElement will change its position and keep on playing, however if the user seeks to a position greater than 17s, the video freezes.
This could be for the fact that the MediaElement has buffered only 1MB of the video so it'll only seek withing that timeframe, but it doesn't make sense because if I let it play without interruption, it'll play the whole video without any problem. Can someone tell me whats going on?
Code:
private void downloadchunks()
for (int i = 1; i <= 20; i++)
{
WriteStream = new System.IO.FileStream(DownloadLocation, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.ReadWrite);
//request and receive a response of 1MB of a file
rpstream = response.GetResponseStream();
byte[] buffer;
using (var SReader = new MemoryStream())
{
rpstream.CopyTo(SReader);
buffer =SReader.ToArray();
WriteStream.Seek(WritePos,SeekOrigin.Begin);
WriteStream.Write(buffer, 0, buffer.Length);
WriteStream.Close();
}
if (i==1)
{
PlayVideo();
}
}
private void PlayVideo()
{
MediaElement.Source = new uri(DownloadLocation);
MediaElement.Play();
}
I've figured it out. Just create a dummmy file before you assign it to the MediaElement, and then start the download.
File.WriteAllBytes(location, new byte[filesize]);
I would like to access the audio bytes that is being recorded by MediaRecorder during the recording process so I can send them with UdpClient to a server application.
I'm able to do this with AudioRecord by doing the following (note the while(true) loop)
endRecording = false;
isRecording = true;
audioBuffer = new Byte[1024];
audioRecord = new AudioRecord (
// Hardware source of recording.
AudioSource.Mic,
// Frequency
11025,
// Mono or stereo
ChannelIn.Mono,
// Audio encoding
Android.Media.Encoding.Pcm16bit,
// Length of the audio clip.
audioBuffer.Length
);
audioRecord.StartRecording ();
while (true) {
if (endRecording) {
endRecording = false;
break;
}
try {
// Keep reading the buffer while there is audio input.
int numBytes = await audioRecord.ReadAsync (audioBuffer, 0, audioBuffer.Length);
//Send the audio data with the DataReceived event where it gets send over UdpClient in the Activity code
byte[] encoded = audioBuffer; //TODO: encode audio data, for now just stick with regular PCM audio
DataReceived(encoded);
} catch (Exception ex) {
Console.Out.WriteLine (ex.Message);
break;
}
}
audioRecord.Stop ();
audioRecord.Release ();
isRecording = false;
But I'm not sure how to get the bytes out of MediaRecorder so I can do something similar. Most of the examples I see only work with a file after the recording has been finished like the following example code from here and here.
I don't want to wait for a complete recording before it starts to send. I don't need MediaRecorder to record a file, just give me access to the bytes. But having the option to both write to a file and send the bytes would work well. Is there a way to do this, perhaps by using ParcelFileDescriptor or something else?
I am trying to do something very simple.
In finding the default buffersize associated with the StreamWriter(string path) constructor too small for the information I am logging, I am attempting to use the following constructor:
public StreamWriter(
string path,
bool append,
Encoding encoding,
int bufferSize
)
The resulting log file is completely empty - which is worse.
NOTE: My original post cited the error "Error: Attempted to read past end of stream" , but this relates to functionality later in the method, which I can't log information for because of this log file problem.
Here is the old constructor usage from my code:
drawGameBoardLog = new StreamWriter(debugFileName);
And here is the new constructor which ironically makes things worse:
drawGameBoardLog = new StreamWriter(debugFileName, false, System.Text.Encoding.Default, 65535);
I am totally baffled by this.
UPDATE: Some more detail:
This is the start of the method for which I am logging activity:
public void DrawGameBoard()
{
StreamWriter drawGameBoardLog;
bool debug = true;
// Logging---------------------------------------------------------------------------------
// Build timestamp string
DateTime currentDateTime = DateTime.Now;
string timeStampString = currentDateTime.ToString("ddMMyyyy_HHmmss");
// Build filename, concat timestamp and .txt extension.
string debugFileName = "D:\\Programming\\PacmanLogs\\DrawGameBoard"+timeStampString+".txt";
// Create filestream and pass this to a stream writer, setting a nice big buffer.
drawGameBoardLog = new StreamWriter(debugFileName, false, System.Text.Encoding.Default, 65535);
//drawGameBoardLog = new StreamWriter(debugFileName);
// Write to the file:
drawGameBoardLog.WriteLine(DateTime.Now);
drawGameBoardLog.WriteLine("timeStampString = {0}",timeStampString);
// -------------------------------------------------------------------------------------------
if(debug){drawGameBoardLog.WriteLine("DrawGameBoard()...");}
The line "DrawGameBoard()..." is not even appearing when using the StreamWriter constructor which accepts path, append, encoding, and buffersize. Whereas before I was getting content which took the file size to 1K. Here is the log file up to that point (I'm starting out with graphics programming by the way):
19/08/2012 14:13:21
timeStampString = 19082012_141321
DrawGameBoard()...
noOfIndexes = [6], noOfVertices = [4]
...just set stremSize = [80]
...creating vertices DataStream...
...building Matrices...
...Scaling matrix DONE...
...Rotation matrix DONE...
...Translation matrix DONE...
...Orthogonal matrix DONE...
...Perspective matrix DONE...
...COMBINED matrix DONE...
...creating Vector3 and Vector2 arrays to hold positions and texture coords...
...Writing Texture coords....
...Building Index Array (order of vertices to be drawn for a quad)....
...Declaring indicesStream. Set size of stream to [24]....
...Created data stream for indices OK. Now writing indices array to it....
...DONE. Just set position back to ZERO...
...Created new index buffer OK....
...configure the Input Assembler....
...Getting Vectors for position [0,0]...
...Got Vectors into myVectorPositions....
myVectorPositions[0] = [X:0 Y:0 Z:0.5]
myVectorPositions[1] = [X:20 Y:0 Z:0.5]
myVectorPositions[2] = [X:0 Y:20 Z:0.5]
The default buffer size kicks in right there.
You are not closing your StreamWriter. At the end type:
drawGameBoardLog.Close();
Or use a using block:
using(StreamWriter sw = new StreamWriter(path))
{
sw.WriteLine("Sth");
}
Or use a try finally:
StreamWriter sw;
try
{
sw = new StreamWriter(path);
sw.WriteLine("sth");
}
finally
{
sw.Close();
}
Please change this line
drawGameBoardLog = new StreamWriter(debugFileName, false,
System.Text.Encoding.Default, 65535);
in
using(StreamWriter drawGameBoardLog = new StreamWriter(debugFileName, false,
System.Text.Encoding.Default, 65535))
{
.... write all the stuff in your log
}
The using statement block will ensure that the streamwriter will be closed and written to disk
Probably, your first attempt using a StreamWriter without buffer worked partially, but, when you change the buffer to a big value, then only when you reach that dimension the file is written to disk.