Usage of PlaySoundFile()

HomeForumsMonoBrick EV3 FirmwareUsage of PlaySoundFile()

This topic contains 9 replies, has 6 voices, and was last updated by Author Image Tcm0 6 months ago.

Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • #5493
    Author Image
    Erwin Castricum
    Participant

    Hi,
    I am writing a program for the brick for a school project. The documentation is really well and everything works, except the PlaySoundFile function. I cant get it to work. I tried 16Bit signed and unsigned WAV files. The only thing the brick does is make a clicking noise for about a second and after that it doesnt do anything. Could someone tell me what im doing wrong?
    Thanks in advance,
    Erwin

    #5494
    Author Image
    Lars Jeppesen
    Key Master

    Hi,

    I don’t think you do anything wrong. There is a known problem with the sound playback.
    Hopefully we will get it fixed soon.

    Best regards
    Lars

    #5601
    Author Image
    Heinrich Braasch
    Participant

    This is an old post, but I’m also now in April 2016 trying to playback a .wav file. Has this problem been fixed?
    Thanks

    #5602
    Author Image
    Tcm0
    Participant

    No, it hasn’t.

    #5603
    Author Image
    Heinrich Braasch
    Participant

    Thanks for the reply. Sorry to hear.

    Would be sad if support for MonoBrick is waning? :-( But being provided free and all….

    #5614
    Author Image
    rasep retrep
    Participant

    Hi,

    I stumbled upon the same issue and took the time to fix it (my daughter desperately wanted to add minion sounds to our robot).
    For this just took the LeJOS implementation for this which looked a bit more robust and migrated it to c#
    The (only?) reason it didnt work is that the sound device was sent data without checking if it is ready, so the whole sample would just be almost instantaneously fed to the sound device which results in this odd “crackling” sound.

    Btw., Samples sent to the function MUST be 8 bit, mono 8000hz
    for converting samples I just used http://www.online-convert.com/ to convert my mp3s to this rather unusual format.

    I am not using git right now, so I am sorry for not adding this to the official repo and just posting it here. Maybe Lars can add it.
    To fix it just replace the following 2 files in the MonoBrickFirmware project and recompile

    ————————–
    Sound\Speaker.cs
    ————————–

    using System;
    using System.IO;
    using MonoBrickFirmware.Native;

    namespace MonoBrickFirmware.Sound
    {

    public enum AudioMode{ Break = 0, Tone = 1, Play = 2, Repeat = 3, Service = 4}

    public class Speaker
    {
    private UnixDevice soundDevice = new UnixDevice(“/dev/lms_sound”);
    //private MemoryArea soundMemory;
    private int currentVolume;
    private const UInt16 beepFrequency = 600;
    private const UInt16 buzzFrequency = 100;
    private const UInt16 clickFrequency = 100;

    private const UInt16 buzzDurationMs = 300;
    private const UInt16 beepDurationMs = 300;
    private const UInt16 clickDurationMs = 100;

    private const int RIFF_HDR_SIZE = 44;
    private const int RIFF_RIFF_SIG = 0×52494646;
    private const int RIFF_WAVE_SIG = 0×57415645;
    private const int RIFF_FMT_SIG = 0x666d7420;
    private const short RIFF_FMT_PCM = 0×0100;
    private const short RIFF_FMT_1CHAN = 0×0100;
    private const short RIFF_FMT_8BITS = 0×0800;
    private const int RIFF_DATA_SIG = 0×64617461;
    private const int PCM_BUFFER_SIZE = 250;

    public Speaker (int volume)
    {
    currentVolume = volume;
    }

    public int Volume {
    get{return currentVolume;}
    set{currentVolume = value; }
    }

    /// <summary>
    /// Play a tone.
    /// </summary>
    /// <param name=”volume”>Volume.</param>
    /// <param name=”frequency”>Frequency of the tone</param>
    /// <param name=”durationMs”>Duration in ms.</param>
    public void PlayTone(UInt16 frequency, UInt16 durationMs){
    PlayTone(frequency,durationMs, Volume);
    }

    /// <summary>
    /// Play a tone.
    /// </summary>
    /// <param name=”volume”>Volume.</param>
    /// <param name=”frequency”>Frequency of the tone</param>
    /// <param name=”durationMs”>Duration in ms.</param>
    /// <param name=”durationMs”>Volume .</param>
    public void PlayTone(UInt16 frequency, UInt16 durationMs, int volume){
    if (volume < 0)
    volume = -volume;
    var command = new MonoBrickFirmware.Tools.ByteArrayCreator();
    command.Append(AudioMode.Tone);
    command.Append((byte)volume);
    command.Append(frequency);
    command.Append(durationMs);
    command.Print();
    soundDevice.Write(command.Data);
    System.Threading.Thread.Sleep(durationMs);
    }

    /// <summary>
    /// Make the brick beep
    /// </summary>
    public void Beep(){
    Beep(beepDurationMs, Volume);
    }

    /// <summary>
    /// Make the brick beep
    /// </summary>
    /// <param name=”durationMs”>Duration in ms.</param>
    public void Beep(UInt16 durationMs){
    Beep(durationMs, Volume);
    }

    /// <summary>
    /// Make the brick beep
    /// </summary>
    /// <param name=”durationMs”>Duration in ms.</param>
    /// <param name=”volume”>Volume of the beep</param>
    public void Beep(UInt16 durationMs, int volume){
    PlayTone(beepFrequency, durationMs,volume);
    }

    /// <summary>
    /// Make the brick buzz
    /// </summary>
    public void Buzz ()
    {
    Buzz(buzzDurationMs, Volume);
    }

    /// <summary>
    /// Make the brick buzz
    /// </summary>
    /// <param name=”durationMs”>Duration in ms.</param>
    public void Buzz (UInt16 durationMs)
    {
    Buzz(durationMs, Volume);
    }

    /// <summary>
    /// Make the brick buzz
    /// </summary>
    /// <param name=”durationMs”>Duration in ms.</param>
    /// <param name=”volume”>Volume of the beep</param>
    public void Buzz (UInt16 durationMs, int volume)
    {
    PlayTone(buzzFrequency, durationMs, volume);
    }

    /// <summary>
    /// Make the brick click
    /// </summary>
    public void Click ()
    {
    Click(Volume);
    }

    /// <summary>
    /// Make the brick click
    /// </summary>
    public void Click (int volume)
    {
    //Click(clickDurationMs, Volume);
    }

    /// <summary>
    /// Play a sound file.
    /// </summary>
    /// <param name=”name”>Name the name of the file to play</param>
    /// <param name=”volume”>Volume.</param>
    /// <param name=”repeat”>If set to <c>true</c> the file will play in a loop</param>
    public int PlaySoundFile(string name){
    return PlaySoundFile(name, Volume);
    }

    private int readLSBInt(Stream d)
    {
    int val = d.ReadByte() & 0xff;
    val |= (d.ReadByte() & 0xff) << 8;
    val |= (d.ReadByte() & 0xff) << 16;
    val |= (d.ReadByte() & 0xff) << 24;
    return val;
    }

    private int readInt (Stream d)
    {
    int val = (d.ReadByte() & 0xff) << 24;
    val |= (d.ReadByte() & 0xff) << 16;
    val |= (d.ReadByte() & 0xff) << 8;
    val |= (d.ReadByte() & 0xff);
    return val;
    }

    private Int16 readShort(Stream d)
    {

    Int16 val = (Int16) ((d.ReadByte() & 0xff) << 8);
    val |= (Int16) (d.ReadByte() & 0xff);
    return val;
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name=”name”></param>
    /// <param name=”volume”></param>
    /// <returns>0 == ok else error </returns>
    public int PlaySoundFile(string name, int volume)
    {

    if (new FileInfo(name).Length < RIFF_HDR_SIZE)
    throw new IOException(“Not a valid sound file”);

    using (var afs = File.Open(name, FileMode.Open))
    {
    int offset = 0;
    int sampleRate = 0;
    int dataLen = 0;
    //check file

    if (readInt(afs) != RIFF_RIFF_SIG)
    return -1;

    readInt(afs);

    if (readInt(afs) != RIFF_WAVE_SIG)
    return -2;

    if (readInt(afs) != RIFF_FMT_SIG)
    return -3;

    offset += 16;
    int sz = readLSBInt(afs);

    if (readShort(afs) != RIFF_FMT_PCM)
    return -4;
    if (readShort(afs) != RIFF_FMT_1CHAN)
    return -5;

    sampleRate = readLSBInt(afs);
    readInt(afs);
    readShort(afs);

    if (readShort(afs) != RIFF_FMT_8BITS)
    return -6;

    // Skip any data in this chunk after the 16 bytes above
    sz -= 16;
    offset += 20 + sz;
    while (sz– > 0)
    afs.ReadByte();

    // Skip optional chunks until we find a data sig (or we hit eof!)
    for (; ; )
    {
    int chunk = readInt(afs);
    dataLen = readLSBInt(afs);
    offset += 8;
    if (chunk == RIFF_DATA_SIG) break;
    // Skip to the start of the next chunk
    offset += dataLen;
    while (dataLen– > 0)
    afs.ReadByte();
    }

    if (volume < 0)
    volume = -volume;

    byte[] buf = new byte[2];
    // get ready to play, set the volume
    buf[0] = (byte)AudioMode.Play;
    buf[1] = (byte)volume;
    soundDevice.Write(buf);

    buf = new byte[PCM_BUFFER_SIZE * 4 + 1];

    while ((dataLen = afs.Read(buf, 1, buf.Length – 1)) > 0)
    {
    // now make sure we write all of the data
    offset = 0;
    while (offset < dataLen)
    {
    buf[offset] = (byte) AudioMode.Service;
    int len = dataLen – offset;
    if (len > PCM_BUFFER_SIZE) len = PCM_BUFFER_SIZE;

    byte [] buf2 = new byte [len+1];
    Array.Copy(buf, offset, buf2, 0, len + 1);
    int bytesWritten = soundDevice.Write(buf2);
    if (bytesWritten <= 0)
    {
    System.Threading.Thread.Sleep(1);
    }
    else
    {
    offset += bytesWritten;
    }
    }
    }
    }
    return 0;
    }

    /// <summary>
    /// Stops all sound playback.
    /// </summary>
    public void StopSoundPlayback(){
    /*var command = new Command(0,0,123,reply);
    command.Append(ByteCodes.Sound);
    command.Append(SoundSubCodes.Break);
    connection.Send(command);
    if(reply){
    var brickReply = connection.Receive();
    Error.CheckForError(brickReply,123);
    }*/
    }
    }
    }

    ————————–
    native\libc.cs
    ————————–

    using System;
    using System.Runtime.InteropServices;

    namespace MonoBrickFirmware.Native
    {
    static public class Libc
    {
    public enum OpenFlags
    {
    O_RDONLY = 0×0000, /* open for reading only */
    O_WRONLY = 0×0001, /* open for writing only */
    O_RDWR = 0×0002, /* open for reading and writing */
    O_ACCMODE = 0×0003 /* mask for above modes */
    }
    public enum ProtectionFlags
    {
    PROT_NONE = 0,
    PROT_READ = 1,
    PROT_WRITE = 2,
    PROT_EXEC = 4
    }
    public enum MMapFlags
    {
    MAP_FILE = 0,
    MAP_SHARED = 1,
    MAP_PRIVATE = 2,
    MAP_TYPE = 0xf,
    MAP_FIXED = 0×10,
    MAP_ANONYMOUS = 0×20,
    MAP_ANON = 0×20
    }

    [DllImport("libc.so.6")]
    public static extern int open(byte[] name, OpenFlags flags);

    [DllImport("libc.so.6")]
    public static extern IntPtr mmap(IntPtr addr, uint len, ProtectionFlags prot, MMapFlags flags, int fd, int offset);

    [DllImport("libc.so.6")]
    public static extern int write(int file, IntPtr buffer, uint count);

    [DllImport("libc.so.6")]
    public static extern int read(int file, IntPtr buffer,uint length);

    [DllImport("libc.so.6")]
    public static extern int ioctl(int fd, int cmd, IntPtr buffer);

    [DllImport("libc.so.6")]
    public static extern int close(int fd);
    }

    public class UnixDevice : IDisposable
    {
    static System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
    int fd = -1;
    public UnixDevice(string name)
    {

    fd = Libc.open(encoding.GetBytes(name + Char.MinValue), Libc.OpenFlags.O_RDWR);
    if (fd < 0)
    throw new InvalidOperationException(“Couldn’t open device: ” + name);
    }

    public MemoryArea MMap(uint size, int offset)
    {
    IntPtr ptr = Libc.mmap(IntPtr.Zero, size, Libc.ProtectionFlags.PROT_READ | Libc.ProtectionFlags.PROT_WRITE, Libc.MMapFlags.MAP_SHARED, fd, offset);
    if ((int)ptr == -1)
    throw new InvalidOperationException(“MMap operation failed”);
    return new MemoryArea(ptr, size);
    }

    public int Write (byte[] data)
    {
    IntPtr pnt = IntPtr.Zero;
    bool hasError = false;
    Exception inner = null;
    int bytesWritten = 0;

    try {
    int size = Marshal.SizeOf (data [0]) * data.Length;
    pnt = Marshal.AllocHGlobal (size);
    Marshal.Copy (data, 0, pnt, data.Length);
    bytesWritten = Libc.write (fd, pnt,(uint) size);
    if (bytesWritten == -1)
    hasError = true;
    }
    catch (Exception e) {
    hasError = true;
    inner = e;
    }
    finally {
    if (pnt != IntPtr.Zero)
    Marshal.FreeHGlobal (pnt);
    }
    if (hasError) {
    if (inner != null) {
    throw inner;
    }
    else
    {
    throw new InvalidOperationException(“Failed to write to Unix device”);
    }
    }
    return bytesWritten;
    }

    public byte[] Read (int length)
    {

    byte[] reply = new byte[length];
    Exception inner = null;
    IntPtr pnt = IntPtr.Zero;
    int bytesRead = 0;
    bool hasError = false;
    try {
    pnt = Marshal.AllocHGlobal (Marshal.SizeOf (reply [0]) * length);
    bytesRead = Libc.read (fd, pnt, (uint)length);
    if (bytesRead == -1) {
    hasError = true;
    Marshal.FreeHGlobal (pnt);
    pnt = IntPtr.Zero;
    } else {
    if (bytesRead != length)
    reply = new byte[bytesRead];
    Marshal.Copy (pnt, reply, 0, bytesRead);
    Marshal.FreeHGlobal (pnt);
    pnt = IntPtr.Zero;
    }

    } catch (Exception e) {
    hasError = true;
    inner = e;
    } finally {
    if (pnt != IntPtr.Zero) {
    Marshal.FreeHGlobal(pnt);
    }
    }
    if(hasError){
    if (inner != null) {
    throw inner;
    }
    else
    {
    throw new InvalidOperationException(“Failed to read from Unix device”);
    }
    }
    return reply;
    }

    /// <summary>
    /// IO control command that copies to the IO output to a buffer
    /// </summary>
    /// <returns>Zero if successful</returns>
    /// <param name=”cmd”>IoCtl request code</param>
    /// <param name=”input”>Input arguments</param>
    /// <param name=”output”>Output buffer</param>
    /// <param name=”ioOutputIndex”>IO start index to copy to output buffer</param>
    public int IoCtl (int cmd, byte[] input, byte[] output, int indexToOutput)
    {
    IntPtr pnt = IntPtr.Zero;
    bool hasError = false;
    Exception inner = null;
    int result = -1;
    try {
    int size = Marshal.SizeOf (typeof(byte))* input.Length;
    pnt = Marshal.AllocHGlobal (size);
    Marshal.Copy (input, 0, pnt, input.Length);
    result = Libc.ioctl (fd, cmd, pnt);
    if (result == -1) {
    hasError = true;
    } else {
    output = new byte[input.Length - indexToOutput];
    Marshal.Copy (pnt, output, indexToOutput, input.Length – indexToOutput);
    }
    } catch (Exception e) {
    hasError = true;
    inner = e;
    } finally {
    if (pnt != IntPtr.Zero) {
    Marshal.FreeHGlobal (pnt);
    }
    }
    if (hasError) {
    if (inner != null) {
    throw inner;
    }
    else
    {
    throw new InvalidOperationException(“Failed to excute IO control command”);
    }
    }
    return result;
    }

    /// <summary>
    /// IO control command. Output is copied back to buffer
    /// </summary>
    /// <returns>Zero if successful</returns>
    /// <param name=”requestCode”>IoCtl request code</param>
    /// <param name=”arguments”>IO arguments</param>
    public int IoCtl (int requestCode, byte[] arguments)
    {
    IntPtr pnt = IntPtr.Zero;
    bool hasError = false;
    Exception inner = null;
    int result = -1;
    try {
    int size = Marshal.SizeOf (typeof(byte))* arguments.Length;
    pnt = Marshal.AllocHGlobal (size);
    Marshal.Copy (arguments, 0, pnt, arguments.Length);
    result = Libc.ioctl (fd, requestCode, pnt);
    if (result == -1) {
    hasError = true;
    }
    else{
    Marshal.Copy (pnt, arguments, 0, arguments.Length);
    }
    } catch (Exception e) {
    hasError = true;
    inner = e;
    } finally {
    if (pnt != IntPtr.Zero) {
    Marshal.FreeHGlobal (pnt);
    }
    }
    if (hasError) {
    if (inner != null) {
    throw inner;
    }
    else
    {
    throw new InvalidOperationException(“Failed to excute IO control command”);
    }
    }
    return result;
    }

    public void Dispose()
    {
    Libc.close(fd);
    fd = -1;
    }

    ~UnixDevice()
    {
    if (fd >= 0)
    {
    Libc.close(fd);
    }
    }
    }

    public class MemoryArea
    {
    IntPtr ptr;
    uint size;
    public MemoryArea(IntPtr ptr, uint size)
    {
    this.ptr = ptr;
    this.size = size;
    }

    /// <summary>
    /// Write a byte array to the memory map
    /// </summary>
    /// <param name=”data”>Data.</param>
    public void Write (byte[] data)
    {
    Write(0, data);
    }

    /// <summary>
    /// Write a byte array to the memory map
    /// </summary>
    /// <param name=”offset”>Memory map offset</param>
    /// <param name=”data”>Data to write</param>
    public void Write (int offset, byte[] data)
    {
    if (offset + data.Length > size)
    throw new IndexOutOfRangeException (string.Format (“Out of range accessing index {0}, max {1}”, offset + data.Length, size));
    if (offset != 0) {
    Marshal.Copy (data, 0, ptr.Add (offset), data.Length);
    }
    else
    {
    Marshal.Copy (data, 0, ptr , data.Length);
    }
    }

    /// <summary>
    /// Copy the whole memory map into an array and return it
    /// </summary>
    public byte[] Read ()
    {
    return Read(0,(int)size);
    }

    /// <summary>
    /// Copy part of the memory map into an array and return it
    /// </summary>
    /// <param name=”offset”>Memory map offset</param>
    /// <param name=”length”>Number of bytes to read</param>
    public byte[] Read (int offset, int length)
    {
    if (offset + length > size)
    throw new IndexOutOfRangeException (string.Format (“Out of range accessing index {0}, max {1}”, offset + length, size));
    byte[] reply = new byte[length];
    if (offset != 0) {
    Marshal.Copy (ptr.Add(offset), reply, 0, length);
    }
    else
    {
    Marshal.Copy (ptr, reply, 0, length);
    }
    return reply;
    }
    }

    public static class IntPtrExtensions
    {
    /// <summary>
    /// Add a value to a int pointer
    /// </summary>
    /// <param name=”ptr”>Pointer to add value to</param>
    /// <param name=”val”>Value to add</param>
    public static IntPtr Add(this IntPtr ptr, int val)
    {
    return new IntPtr(ptr.ToInt64() + val);
    }
    }
    }

    #5615
    Author Image
    Tcm0
    Participant

    Can you please upload your monobrickfirmware.dll? I tried to copy the fixes and it works but the file doesn’t stop playing.

    #5616

    Hello,

    I just created a pull request with the suggestions coming from Rasep Retrep.
    In the meanwhile you can access the forked repository here
    Full credit for the solution of this annoying bug goes to Rasep Retrep.

    Cheers,
    Riccardo.

    #5618

    Hello again,

    Attached to this post there should be the modified .dll

    Cheers,
    Riccardo.

    Edit. Dll files cannot be attached into the forum for reasonable security reasons.

    #5621
    Author Image
    Tcm0
    Participant

    Thanks for linking the github pull request. I included the support for the Hitechnic IR Link sensor that I ported from LeJOS and compiled everything. I didn’t test it yet but everything should work.
    Mirror 1 (Dropbox)
    Mirror 2 (Mediafire)
    Mirror 3 (my private server. Isn’t very relieable)

    • This reply was modified 6 months ago by Author Image Tcm0.
Viewing 10 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic.

Posted in

Make a donation