Play system sound or save a wave within an ipf file?

I would like to have a sound played after the completion of a longer procedure. For an existing sound wave in a project, this works with the following procedure. But this has the consequence that I would have to copy the sound wave into every further procedure. (The sound wave is an imported .wave file created with Logic Pro.)

function f_playsound()
    if (waveexists(root:temp:sound)==1)
            wave sound=root:temp:sound
            playsound sound
    endif
end

So my question is if there is a way to include the sound wave in the ipf file. Basically I could use "make" to create the wave and then write the original sound wave for each value. But with 48510 rows and 2 columns this is very complicated.
Or are there ready-made sounds or system sounds that you could play instead?

You could just use:

Beep

and then set the appropriate system sound to whatever you like to hear here.

Thanks... this is at least a solution for the case that the sound wave is missing.

Beep is a good way to go. But that's far too simple.....

 

Function EncodeWaveToNewWindowUserdata(WAVE inputWave)
    ImageTransform compress inputWave
    WAVE W_Compressed   // a byte wave that contains the data as well as metadata from inputWave
    Variable numBytesCompressed = numpnts(W_Compressed)
    String outStringRaw = ""
   
    // Transfer the contents of W_Compressed into a string. The string
    // will simply contain the raw binary data from the wave.
    outStringRaw = PadString(outStringRaw, numBytesCompressed, 0)
    Variable n
    For (n=0; n < numBytesCompressed; n++)
        outStringRaw[n] = num2char(W_Compressed[n], 1)
    EndFor
    Display
    print strlen(outStringRaw)
    // Base64 encode the string to get rid of any null characters and
    // other characters that might cause problems.
    String outStringBase64Encoded = Base64Encode(outStringRaw)
    print "b64 encoded", strlen(outStringBase64Encoded)
    String dataWinName = S_name
    // Store the contents of the string in the window's user data.
    SetWindow $dataWinName, userdata(audioWave)=outStringBase64Encoded
End

Function RecreateWaveFromWindowUserdata(String windowName)
    // Get the userdata
    String audioUserDataBase64Encoded = GetUserData(windowName, "", "audioWave")
    print strlen(audioUserDataBase64Encoded)
    String audioUserDataRaw = Base64Decode(audioUserDataBase64Encoded)
    print strlen(audioUserDataRaw)
   
    // Put the binary data in the string into a byte wave of the same length.
    Variable rawStringLength = strlen(audioUserDataRaw)
    Make/B/U/O/N=(rawStringLength) audioDataByteWave
    audioDataByteWave = char2num(audioUserDataRaw[p])
   
    // Decompress the data
    ImageTransform decompress audioDataByteWave // Creates W_Decompressed
    WAVE W_Decompressed
   
    PlaySound W_Decompressed
End

 

To use this, execute:

EncodeWaveToNewWindowUserdata(mySoundWave1)

That creates a blank graph window whose named userdata contains the mySoundWave1 wave after compressing the wave and then base64 encoding.

Then, with that window still on top, execute:

RecreateWaveFromWindowUserdata("Graph0")

That command should play the wave that is encoded in the specified window's named userdata.

To use this in real life, you would call EncodeWaveToNewWindowUserdata to create the window. Then you would save the window's recreation macro to your procedure file.

Then, when this code runs on the user's computer, you would call the window recreation macro for the window, then call RecreateWaveFromWindowUserdata to create the audio wave. You would probably then want to move that wave somewhere else and kill the empty graph.

You could use this technique for any kind of wave data, not just audio data.

If you are distributing your package as a bunch of procedure files, you could also include a .ibw (igor binary wave) or .itx (igor text) file in the package that contains the data, and then have your code load that file using LoadWave.

Okay, um... thanks a lot! I tried the compression and the window macro is unfortunately very long (for my test sound wave about 4000 lines of code).
I think that for a "the procedure is finished" several beeps should be enough. From the second beep on it also seems to be louder, surely has to do with the power saving mode of the headphones.

function f_playsound()
    beep
    sleep 00:00:01
    beep
    sleep 00:00:01
    beep
    sleep 00:00:01
    beep
    sleep 00:00:01
    beep
end


Thanks for the helpful tips.

Another option would be to use ExecuteScriptText to make a system call to make a noise. That would be more difficult to make cross platform, but is likely possible. On macOS you can use the system "say" command to use text to speech. I have my C++ compiler set up to say "finished" to me when a build of Igor is done.

On Windows it's more complicated. Here's the command I have executed. I haven't tried calling this from Igor though, so it may be more complicated.

cmd.exe /C PowerShell -Command "Add-Type –AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak('finished');"

 

Is it possible to return the operating system and the system language as string?

ExecuteScriptText("say \"finished\"")

 

To get the OS use IgorInfo(). No idea about the system language, though.

For the OS you can test either with IgorInfo(2) or you can use conditional compilation. Here's an example of the later:

Function test()
#ifdef WINDOWS
    print "windows"
#else
    print "macintosh"
#endif
End

For more info on conditional compilation, execute:

DisplayHelpTopic "Conditional Compilation"

You probably don't need an example on how to use IgorInfo().

 

As for getting the system language, on Windows you can use this Igor function. Note that it takes a few seconds to execute, so you'll want to run this only once and store the value in a global.

Function/S GetSystemLocaleWindows()
    String cmd = "cmd.exe /C systeminfo | findstr /B /C:\"System Locale\""
    ExecuteScriptText/B/UNQ/W=5 cmd
    return S_value
End

On macOS, you can use ExecuteScriptText to call the "locale" command and look at the value of the LANG variable that is returned.

Since Igor itself is has only English and Japanese translations (the LOCALE key of IgorInfo(3) output will give you "Japan" for the Japanese translation), you can probably assume that your users have at least basic familiarity with written English.

In reply to by aclight

aclight wrote:

Since Igor itself is has only English and Japanese translations (the LOCALE key of IgorInfo(3) output will give you "Japan" for the Japanese translation), you can probably assume that your users have at least basic familiarity with written English.

The user is not the problem, but the speech output of macOS. It does not recognize the language, but reads it as the text would sound in the system language.

Thanks again to everyone for the helpful information.