Hello everybody,
I'm currently working on SFML.Net to expand with mp3 support. Therefore I wrote a Stream class which uses NLayer MpegFile to decode the mp3.
public class Mp3StreamSFML
: SoundStream
{ private MpegFile mp3file
; private int currentBufferSize
; private short[] currentBuffer
; public Mp3StreamSFML
(String _filename
) { mp3file
= new MpegFile
(_filename
); Initialize
((uint)mp3file
.Channels,
(uint)mp3file
.SampleRate); currentBufferSize
= 0; currentBuffer
= new short[currentBufferSize
]; } #region implemented abstract members of SoundStream protected override bool OnGetData
(out short[] samples
) { if (currentBufferSize
<= mp3file
.Position) { byte[] buffer
= new byte[512]; if (mp3file
.ReadSamples(buffer,
0, buffer
.Length) > 0) { Array
.Resize(ref currentBuffer, currentBuffer
.Length + (buffer
.Length / 2)); Buffer
.BlockCopy(buffer,
0, currentBuffer, currentBufferSize, buffer
.Length); currentBufferSize
= currentBuffer
.Length; } samples
= currentBuffer
; return true; } else { samples
= currentBuffer
; return false; } } protected override void OnSeek
(TimeSpan timeOffset
) { mp3file
.Position = (long)timeOffset
.TotalSeconds; } #endregion }
I use it this way:
try { stream
= new Mp3StreamSFML
(this.objProgram.getObjCuesheet().getAudiofilePath(true)); stream
.Play(); log
.debug("samplerate = " + stream
.SampleRate); } catch(Exception ex
) { log
.fatal(ex
.ToString()); }
Unfortunately, there is not the correct sound played, its just "juttering" and sound really weird. What I'm doing wrong? Seems like a problem between the 2 Frameworks.
NLayer:https://nlayer.codeplex.com/ (https://nlayer.codeplex.com/)
Thanks for your help ;).
Sven
Thanks for your help, I changed the Method this way:
protected override bool OnGetData
(out short[] samples
) { if (currentBufferSize
<= mp3file
.Position) { byte[] buffer
= new byte[512]; int readSamples
= mp3file
.ReadSamples(buffer,
0, buffer
.Length); if (readSamples
> 0) { Array
.Resize(ref currentBuffer, currentBuffer
.Length + (readSamples
/ 2)); Buffer
.BlockCopy(buffer,
0, currentBuffer, currentBufferSize, readSamples
); currentBufferSize
= currentBuffer
.Length; } samples
= currentBuffer
; return true; } else { samples
= currentBuffer
; return false; } }
Also, I do not believe simply converting the array as you are doing is the proper way to convert audio data from a byte array to a short array (PCM) that SFML requires.
I have read in the internet, that this would be the safest way of converting byte[] to short[]. How should it be done?
I tried now this (from http://www.codeproject.com/Articles/501521/How-to-convert-between-most-audio-formats-in-NET (http://www.codeproject.com/Articles/501521/How-to-convert-between-most-audio-formats-in-NET)), but this also doesn't work:
protected override bool OnGetData
(out short[] samples
) { if (currentBufferSize
<= mp3file
.Position) { byte[] buffer
= new byte[512]; int readSamples
= mp3file
.ReadSamples(buffer,
0, buffer
.Length); if (readSamples
> 0) { Array
.Resize(ref currentBuffer, currentBuffer
.Length + buffer
.Length); //Buffer.BlockCopy(buffer, 0, currentBuffer, currentBufferSize, readSamples); int destOffset
= currentBufferSize
; for (int sample
= 0; sample
< buffer
.Length; sample
++) { // adjust volume float sample32
= buffer
[sample
]; // clip if (sample32
> 1
.0f
) sample32
= 1
.0f
; if (sample32
< -1
.0f
) sample32
= -1
.0f
; currentBuffer
[destOffset
++] = (short)(sample32
* 32767); } currentBufferSize
= currentBuffer
.Length; } samples
= currentBuffer
; return true; } else { samples
= currentBuffer
; return false; } }
Working with this code, I get only some "clicking" sound:
protected override bool OnGetData
(out short[] samples
) { if (currentBufferSize
<= mp3file
.Position) { byte[] buffer
= new byte[512]; int readSamples
= mp3file
.ReadSamples(buffer,
0, buffer
.Length); if (readSamples
> 0) { Array
.Resize(ref currentBuffer, currentBuffer
.Length + buffer
.Length); //Buffer.BlockCopy(buffer, 0, currentBuffer, currentBufferSize, readSamples); for (int i
= 0; i
< (buffer
.Length / 4); i
++) { currentBuffer
[currentBufferSize
+ (i
/4)] = (short)(BitConverter
.ToSingle(buffer, i
* 4) * 32767); } } samples
= currentBuffer
; return true; } else { samples
= currentBuffer
; return false; } }
How can I check, what soundformat is read by nlayer?
Thank you for your great help. I use this code, but now I hear nothing (turned the volume up to 100 ;)).:
protected override bool OnGetData
(out short[] samples
) { if (currentBufferSize
<= mp3file
.Position) { byte[] buffer
= new byte[512]; int readSamples
= mp3file
.ReadSamples(buffer,
0, buffer
.Length); if (readSamples
> 0) { float[] normalizedaudiodata
= new float[readSamples
/ 4];// { 1.0f, -1.0f, 0.5f, -0.5f, 2.0f, -2.0f }; // normalized data from decoder Buffer
.BlockCopy(buffer,
0, normalizedaudiodata,
0, buffer
.Length); short[] pcmaudiodata
= new short[normalizedaudiodata
.Length]; // converted data for (int i
= 0; i
< normalizedaudiodata
.Length; i
++) { // clip the data if (normalizedaudiodata
[i
] > 1
.0f
) { normalizedaudiodata
[i
] = 1
.0f
; } else { if (normalizedaudiodata
[i
] < -1
.0f
) { normalizedaudiodata
[i
] = -1
.0f
; } } // convert to pcm data pcmaudiodata
[i
] = (short)(normalizedaudiodata
[i
] * short.MaxValue); } // do whatever you want with the converted data int startIndex
= currentBuffer
.Length; Array
.Resize(ref currentBuffer, currentBuffer
.Length + pcmaudiodata
.Length); for (int i
= startIndex
; i
< currentBuffer
.Length; i
++) { currentBuffer
[i
] = pcmaudiodata
[i
- startIndex
]; } //Array.Resize(ref currentBuffer, currentBuffer.Length + buffer.Length); //Buffer.BlockCopy(buffer, 0, currentBuffer, currentBufferSize, readSamples);// for (int i = 0; i < (buffer.Length / 4); i++)// {// currentBuffer[currentBufferSize + (i/4)] = (short)(BitConverter.ToSingle(buffer, i * 4) * 32767);// } } samples
= currentBuffer
; return true; } else { samples
= currentBuffer
; return false; } }
What I am doing wrong? How can I find an error?
if (currentBufferSize <= mp3file.Position)
What's the purpose of this test? I don't think you need it at all.
Array.Resize(ref currentBuffer, currentBuffer.Length + pcmaudiodata.Length);
OnGetData only expects a new chunk of fresh data. You mustn't return all the samples read in the previous calls, those have already been processed.