Play recorded sounds: Difference between revisions

From Rosetta Code
Content added Content deleted
m (syntax highlighting fixup automation)
Line 20: Line 20:
This snippet of code will stream an unsigned 8-bit PCM sample to the [[wp:Yamaha_YM2612|Yamaha 2612's]] [[wp:Digital-to-analog_converter|DAC]]. Unfortunately, the data needs to be continuously streamed, meaning that the game essentially comes to a halt while this is happening. However, a clever game designer can hide this fact quite easily by only using voice samples at key moments where the player expects a brief pause in the action. Playing with other sounds is possible if the Z80 coprocessor is handling the playback, however the DAC uses a few of the channels and therefore some of them will not be heard if DAC streaming begins during regular FM audio playback.
This snippet of code will stream an unsigned 8-bit PCM sample to the [[wp:Yamaha_YM2612|Yamaha 2612's]] [[wp:Digital-to-analog_converter|DAC]]. Unfortunately, the data needs to be continuously streamed, meaning that the game essentially comes to a halt while this is happening. However, a clever game designer can hide this fact quite easily by only using voice samples at key moments where the player expects a brief pause in the action. Playing with other sounds is possible if the Z80 coprocessor is handling the playback, however the DAC uses a few of the channels and therefore some of them will not be heard if DAC streaming begins during regular FM audio playback.


<lang 68000devpac>dac_data equ $2A
<syntaxhighlight lang="68000devpac">dac_data equ $2A
dac_enable equ $2B
dac_enable equ $2B
LEA pcmsample,a1
LEA pcmsample,a1
Line 64: Line 64:


pcmSample:
pcmSample:
incbin "X:\ResAll\pcmSample.bin"</lang>
incbin "X:\ResAll\pcmSample.bin"</syntaxhighlight>


=={{header|AutoHotkey}}==
=={{header|AutoHotkey}}==
Line 70: Line 70:
1. monitoring timepoints in the sound <br>
1. monitoring timepoints in the sound <br>
2. simultaneous play
2. simultaneous play
<lang AutoHotkey>SoundPlay, %A_WinDir%\Media\tada.wav, wait
<syntaxhighlight lang="autohotkey">SoundPlay, %A_WinDir%\Media\tada.wav, wait
SoundPlay, %A_WinDir%\Media\Windows XP Startup.wav, wait
SoundPlay, %A_WinDir%\Media\Windows XP Startup.wav, wait


Line 80: Line 80:
SoundSet +10 ; increase volume by 10%
SoundSet +10 ; increase volume by 10%
Loop, 2
Loop, 2
SoundPlay, %A_WinDir%\Media\tada.wav, wait</lang>
SoundPlay, %A_WinDir%\Media\tada.wav, wait</syntaxhighlight>


=={{header|Batch File}}==
=={{header|Batch File}}==
Line 102: Line 102:
{{works with|BBC BASIC for Windows}}
{{works with|BBC BASIC for Windows}}
BBC BASIC for Windows has native support for playing MIDI files, and WAV files can be played using simple API calls:
BBC BASIC for Windows has native support for playing MIDI files, and WAV files can be played using simple API calls:
<lang bbcbasic> SND_LOOP = 8
<syntaxhighlight lang="bbcbasic"> SND_LOOP = 8
SND_ASYNC = 1
SND_ASYNC = 1
SND_FILENAME = &20000
SND_FILENAME = &20000
Line 142: Line 142:
PRINT "Stopped MIDI."
PRINT "Stopped MIDI."
END</lang>
END</syntaxhighlight>


=={{header|C sharp|C#}}==
=={{header|C sharp|C#}}==


<lang csharp>using System;
<syntaxhighlight lang="csharp">using System;
using System.Threading;
using System.Threading;
using System.Media;
using System.Media;
Line 169: Line 169:
s1.PlayLooping();
s1.PlayLooping();
}
}
}</lang>
}</syntaxhighlight>


=={{header|Delphi}}==
=={{header|Delphi}}==
<lang Delphi>program PlayRecordedSounds;
<syntaxhighlight lang="delphi">program PlayRecordedSounds;


{$APPTYPE CONSOLE}
{$APPTYPE CONSOLE}
Line 180: Line 180:
begin
begin
sndPlaySound('SoundFile.wav', SND_NODEFAULT OR SND_ASYNC);
sndPlaySound('SoundFile.wav', SND_NODEFAULT OR SND_ASYNC);
end.</lang>
end.</syntaxhighlight>


=={{header|Go}}==
=={{header|Go}}==
Line 190: Line 190:


See the PicoLisp entry for a description of what these particular arguments do.
See the PicoLisp entry for a description of what these particular arguments do.
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 211: Line 211:
log.Fatal(err)
log.Fatal(err)
}
}
}</lang>
}</syntaxhighlight>


=={{header|GUISS}}==
=={{header|GUISS}}==
Line 217: Line 217:
Here we use the media player to play a sound file in the default directory:
Here we use the media player to play a sound file in the default directory:


<lang guiss>Start,Programs,Accessories,Media Player,Menu:File,Open,
<syntaxhighlight lang="guiss">Start,Programs,Accessories,Media Player,Menu:File,Open,
Doubleclick:Icon:Sound.WAV,Button:OK,Button:Play</lang>
Doubleclick:Icon:Sound.WAV,Button:OK,Button:Play</syntaxhighlight>


=={{header|Liberty BASIC}}==
=={{header|Liberty BASIC}}==
<lang lb>'Supports .mid and .wav natively
<syntaxhighlight lang="lb">'Supports .mid and .wav natively
'Midi may be played simultaneously
'Midi may be played simultaneously
'with .wav but only one .wav voice
'with .wav but only one .wav voice
Line 262: Line 262:
calldll #winmm, "waveOutSetVolume", 0 as long, _
calldll #winmm, "waveOutSetVolume", 0 as long, _
dwVol as long, ret as long
dwVol as long, ret as long
</lang>
</syntaxhighlight>




Line 270: Line 270:
This makes a good music player. There is a slight latency to load the player, so very low game
This makes a good music player. There is a slight latency to load the player, so very low game
latencies would not be well supported.
latencies would not be well supported.
<lang julia>using Distributed, Gtk, LibSndFile, MP3
<syntaxhighlight lang="julia">using Distributed, Gtk, LibSndFile, MP3
using FileIO: load
using FileIO: load


Line 407: Line 407:


recordingplayerapp()
recordingplayerapp()
</syntaxhighlight>
</lang>


=={{header|Mathematica}}/{{header|Wolfram Language}}==
=={{header|Mathematica}}/{{header|Wolfram Language}}==
Line 414: Line 414:
stopping before the end of the sound,looping (preferably glitch-free)and setting the volume of each sound
stopping before the end of the sound,looping (preferably glitch-free)and setting the volume of each sound


<lang Mathematica>a = Import["sound1.flac","FLAC"]; b = Import["sound2.flac","FLAC"];
<syntaxhighlight lang="mathematica">a = Import["sound1.flac","FLAC"]; b = Import["sound2.flac","FLAC"];


ListPlay[a, {t, 0, 10}]; ListPlay[b, {t, 0, 10}];
ListPlay[a, {t, 0, 10}]; ListPlay[b, {t, 0, 10}];
Line 424: Line 424:
While[True,ListPlay[{a,b}, {t, 0, 10}];]
While[True,ListPlay[{a,b}, {t, 0, 10}];]


ListPlay[{0.5*a, 0.3*b}, {t, 0, 10}];</lang>
ListPlay[{0.5*a, 0.3*b}, {t, 0, 10}];</syntaxhighlight>


-Supported audio formats :
-Supported audio formats :
Line 445: Line 445:
{{trans|Go}}
{{trans|Go}}
Using "sox" to play two sound files mixed. These are "wav" files but "sox" recognizes a lot of sound formats.
Using "sox" to play two sound files mixed. These are "wav" files but "sox" recognizes a lot of sound formats.
<lang Nim>import osproc
<syntaxhighlight lang="nim">import osproc


let args = ["-m", "-v", "0.75", "a.wav", "-v", "0.25", "b.wav",
let args = ["-m", "-v", "0.75", "a.wav", "-v", "0.25", "b.wav",
Line 451: Line 451:
"trim", "4", "6",
"trim", "4", "6",
"repeat", "5"]
"repeat", "5"]
echo execProcess("sox", args = args, options = {poStdErrToStdOut, poUsePath})</lang>
echo execProcess("sox", args = args, options = {poStdErrToStdOut, poUsePath})</syntaxhighlight>


=={{header|Phix}}==
=={{header|Phix}}==
<lang Phix>integer xPlaySound = 0
<syntaxhighlight lang="phix">integer xPlaySound = 0


procedure play_sound()--string filename)
procedure play_sound()--string filename)
Line 469: Line 469:
end if
end if
end procedure
end procedure
play_sound()</lang>
play_sound()</syntaxhighlight>


=={{header|PicoLisp}}==
=={{header|PicoLisp}}==
Line 479: Line 479:
percent, the second with 25 percent, starting at the 4th second, with a duration
percent, the second with 25 percent, starting at the 4th second, with a duration
of 6 seconds, looping 5 times.
of 6 seconds, looping 5 times.
<lang PicoLisp>(call 'sox
<syntaxhighlight lang="picolisp">(call 'sox
"-m" "-v" "0.75" "a.wav" "-v" "0.25" "b.wav"
"-m" "-v" "0.75" "a.wav" "-v" "0.25" "b.wav"
"-d"
"-d"
"trim" 4 6
"trim" 4 6
"repeat" 5 )</lang>
"repeat" 5 )</syntaxhighlight>


=={{header|PureBasic}}==
=={{header|PureBasic}}==
<lang PureBasic>InitSound()
<syntaxhighlight lang="purebasic">InitSound()
; We need this to use Sound functions
; We need this to use Sound functions
UseOGGSoundDecoder()
UseOGGSoundDecoder()
Line 521: Line 521:


;suitable for 2D games and music playing.
;suitable for 2D games and music playing.
; TODO: There is a Sound3D library for 3D Games, needs to be decribed here too</lang>
; TODO: There is a Sound3D library for 3D Games, needs to be decribed here too</syntaxhighlight>


=={{header|Python}}==
=={{header|Python}}==
Line 529: Line 529:
Pygame is a library for cross-platform game development and depends on the SDL multimedia library (SDL_mixer) for its audio playback. SDL_mixer supports any number of simultaneously playing channels of 16 bit stereo audio, plus a single channel of music, mixed by the popular MikMod MOD, Timidity MIDI, Ogg Vorbis, and SMPEG MP3 libraries.
Pygame is a library for cross-platform game development and depends on the SDL multimedia library (SDL_mixer) for its audio playback. SDL_mixer supports any number of simultaneously playing channels of 16 bit stereo audio, plus a single channel of music, mixed by the popular MikMod MOD, Timidity MIDI, Ogg Vorbis, and SMPEG MP3 libraries.


<lang python>import time
<syntaxhighlight lang="python">import time
from pygame import mixer
from pygame import mixer


Line 556: Line 556:
s1.stop()
s1.stop()
s2.stop()
s2.stop()
mixer.quit()</lang>
mixer.quit()</syntaxhighlight>


To play back .mp3 (or .ogg) files, the music import is used.
To play back .mp3 (or .ogg) files, the music import is used.


<lang python>import time
<syntaxhighlight lang="python">import time
from pygame import mixer
from pygame import mixer
from pygame.mixer import music
from pygame.mixer import music
Line 571: Line 571:


music.stop()
music.stop()
mixer.quit()</lang>
mixer.quit()</syntaxhighlight>


=={{header|R}}==
=={{header|R}}==
Line 578: Line 578:
R's sound package uses system commands to call a built-in media player. On Windows, by default, this is Media Player. You can see the system call with <code>WavPlayer()</code>. Only .WAV files are supported, and the external code call means there is some latency before sounds are played. Samples longer than 10 minutes may correspond to significant chunks of your machine's memory.
R's sound package uses system commands to call a built-in media player. On Windows, by default, this is Media Player. You can see the system call with <code>WavPlayer()</code>. Only .WAV files are supported, and the external code call means there is some latency before sounds are played. Samples longer than 10 minutes may correspond to significant chunks of your machine's memory.


<syntaxhighlight lang="r">
<lang r>
#Setup
#Setup
# Warning: The example files are Windows specific.
# Warning: The example files are Windows specific.
Line 608: Line 608:


#Other actions (not obviously possible)
#Other actions (not obviously possible)
</syntaxhighlight>
</lang>


=={{header|Racket}}==
=={{header|Racket}}==
(Works on all platforms.)
(Works on all platforms.)
<lang racket>
<syntaxhighlight lang="racket">
#lang racket/gui
#lang racket/gui
(play-sound "some-sound.wav" #f)
(play-sound "some-sound.wav" #f)
</syntaxhighlight>
</lang>


=={{header|Ring}}==
=={{header|Ring}}==
<lang ring>
<syntaxhighlight lang="ring">
Load "guilib.ring"
Load "guilib.ring"
new qapp {
new qapp {
Line 736: Line 736:
func mute2
func mute2
q2.setmuted(true)
q2.setmuted(true)
</syntaxhighlight>
</lang>
Output:
Output:


Line 744: Line 744:
There aren't many mature sound libraries for Ruby. The {{libheader|RubyGems}} package [http://rubyforge.org/projects/win32utils/ win32-sound] can play WAV files on the Windows platform only.
There aren't many mature sound libraries for Ruby. The {{libheader|RubyGems}} package [http://rubyforge.org/projects/win32utils/ win32-sound] can play WAV files on the Windows platform only.


<lang ruby>require 'win32/sound'
<syntaxhighlight lang="ruby">require 'win32/sound'
include Win32
include Win32


Line 786: Line 786:


sleep 1
sleep 1
puts "the asynchronous sound is cancelled when the program exits"</lang>
puts "the asynchronous sound is cancelled when the program exits"</syntaxhighlight>


=={{header|Swift}}==
=={{header|Swift}}==
Uses AVFoundation's AVAudioPlayer.
Uses AVFoundation's AVAudioPlayer.
<lang Swift>import AVFoundation
<syntaxhighlight lang="swift">import AVFoundation


// This example uses AVAudioPlayer for playback.
// This example uses AVAudioPlayer for playback.
Line 867: Line 867:
player.player2.play()
player.player2.play()


CFRunLoopRun()</lang>
CFRunLoopRun()</syntaxhighlight>


=={{header|Tcl}}==
=={{header|Tcl}}==
{{libheader|snack}}
{{libheader|snack}}
<lang tcl>package require sound
<syntaxhighlight lang="tcl">package require sound
# Potentially also require driver support for particular formats
# Potentially also require driver support for particular formats


Line 890: Line 890:
after 30000 set done 1; vwait done
after 30000 set done 1; vwait done
s1 stop
s1 stop
s2 stop</lang>
s2 stop</syntaxhighlight>
Note that this library is capable of handling both short and long sounds (sound effects and music).
Note that this library is capable of handling both short and long sounds (sound effects and music).


Line 896: Line 896:


=={{header|TUSCRIPT}}==
=={{header|TUSCRIPT}}==
<lang tuscript>
<syntaxhighlight lang="tuscript">
$$ MODE TUSCRIPT
$$ MODE TUSCRIPT
audiofile="test.wav"
audiofile="test.wav"
ERROR/STOP OPEN (audiofile,READ,-std-)
ERROR/STOP OPEN (audiofile,READ,-std-)
BROWSE $audiofile
BROWSE $audiofile
</syntaxhighlight>
</lang>


=={{header|UNIX Shell}}==
=={{header|UNIX Shell}}==
<lang bash>#!/usr/bin/sh
<syntaxhighlight lang="bash">#!/usr/bin/sh


# play.sh
# play.sh
Line 911: Line 911:
# Usage: play.sh <recorded_sound.au>
# Usage: play.sh <recorded_sound.au>


cat $1 >> /dev/audio # Write file $1 to the speaker's Character Special (/dev/audio).</lang>
cat $1 >> /dev/audio # Write file $1 to the speaker's Character Special (/dev/audio).</syntaxhighlight>


=={{header|VBA}}==
=={{header|VBA}}==
Line 919: Line 919:
Volume can be set using the function waveOutSetVolume, see [http://support.microsoft.com/kb/118377/en-us http://support.microsoft.com/kb/118377/en-us].
Volume can be set using the function waveOutSetVolume, see [http://support.microsoft.com/kb/118377/en-us http://support.microsoft.com/kb/118377/en-us].


<syntaxhighlight lang="vb">
<lang vb>
Declare Function libPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" _
Declare Function libPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" _
(ByVal filename As String, ByVal Flags As Long) As Long
(ByVal filename As String, ByVal Flags As Long) As Long
Line 926: Line 926:
Call libPlaySound(sWav, 1) '1 to play asynchronously
Call libPlaySound(sWav, 1) '1 to play asynchronously
End Sub
End Sub
</syntaxhighlight>
</lang>
Type <pre>Playsound "d:\explode.wav"</pre> in the Immediate window and that sound will play. Nothing will happen if the file d:\explode.wav does not exist.
Type <pre>Playsound "d:\explode.wav"</pre> in the Immediate window and that sound will play. Nothing will happen if the file d:\explode.wav does not exist.


Line 934: Line 934:


It is certainly suitable for game sound effects (it's a game engine) and can play music at CD quality as well.
It is certainly suitable for game sound effects (it's a game engine) and can play music at CD quality as well.
<lang ecmascript>import "audio" for AudioEngine
<syntaxhighlight lang="ecmascript">import "audio" for AudioEngine
import "dome" for Process
import "dome" for Process


Line 966: Line 966:
}
}
}
}
}</lang>
}</syntaxhighlight>


=={{header|Z80 Assembly}}==
=={{header|Z80 Assembly}}==
Line 972: Line 972:
This routine streams an unsigned 8-bit PCM sample to the [[wp:Yamaha_YM2612|Yamaha 2612's]] [[wp:Digital-to-analog_converter|DAC]] in order to play a recorded sound. Unfortunately, the Sega Genesis's Z80 coprocessor can only access a limited pool of memory, so any PCM samples will have to be copied from the cartridge ROM to the shared memory area.
This routine streams an unsigned 8-bit PCM sample to the [[wp:Yamaha_YM2612|Yamaha 2612's]] [[wp:Digital-to-analog_converter|DAC]] in order to play a recorded sound. Unfortunately, the Sega Genesis's Z80 coprocessor can only access a limited pool of memory, so any PCM samples will have to be copied from the cartridge ROM to the shared memory area.


<lang z80>dac_enable equ &2B
<syntaxhighlight lang="z80">dac_enable equ &2B
dac_data equ &2A
dac_data equ &2A


Line 1,014: Line 1,014:
pop de
pop de
pop bc
pop bc
ret</lang>
ret</syntaxhighlight>


{{omit from|Applesoft BASIC}}
{{omit from|Applesoft BASIC}}

Revision as of 01:45, 28 August 2022

Task
Play recorded sounds
You are encouraged to solve this task according to the task description, using any language you may know.

Load at least two prerecorded sounds, and demonstrate as many of these features as you can:

  • playing them individually and simultaneously
  • stopping before the end of the sound
  • looping (preferably glitch-free)
  • setting the volume of each sound
  • stereo or 3D positional mixing
  • performing other actions when marked times in the sound arrive

Describe:

  • The supported audio formats, briefly.
  • Whether it is suitable for game sound effects (low-latency start, resource efficiency, supports many simultaneous sounds, etc.)
  • Whether it is suitable for playing music (long duration ).

[Note: If it seems to be a good idea, this task may be revised to specify a particular timeline rather than just 'demonstrate these features'.]

Where applicable, please categorize examples primarily by the audio facility used (library/API/program/platform) rather than the language if the language is incidental (e.g. "Mac OS X CoreAudio" or "mplayer" rather than "C" or "bash").

68000 Assembly

Works with: Sega Genesis

This snippet of code will stream an unsigned 8-bit PCM sample to the Yamaha 2612's DAC. Unfortunately, the data needs to be continuously streamed, meaning that the game essentially comes to a halt while this is happening. However, a clever game designer can hide this fact quite easily by only using voice samples at key moments where the player expects a brief pause in the action. Playing with other sounds is possible if the Z80 coprocessor is handling the playback, however the DAC uses a few of the channels and therefore some of them will not be heard if DAC streaming begins during regular FM audio playback.

dac_data equ $2A
dac_enable equ $2B
			LEA pcmsample,a1
			LEA $A04000,A3
			MOVEQ #$2B,D0
			MOVEQ #$80,D1    ;assembler might not allow this notation, but MOVEQ #-128,D0 is equivalent.
			jsr FMRegWrite   ;this part is not time-critical so we can use the function call here.
			subq.b #1,d0     ;move.b #dac_data,D0
.dac_loop:
			MOVE.B (a1)+,d1
			beq .dac_done    ;exit on a zero value.

;the core functionality of FMRegWrite had to be reproduced inline 
;in order to play the sample back quickly enough for it to sound correct.

.wait1:
			BTST #7,(A3)      ;check if sound chip is busy
			BNE .wait1        ;loop until it's not busy
			MOVE.B D0,(A3)	  ;write to DAC_DATA register
.wait2:
			BTST #7,(A3)
			BNE .wait2
			MOVE.B D1,(1,A3)  ;data to write
			
			BRA .dac_loop
.dac_done:
        RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FMRegWrite:
	MOVE.L A0,-(SP)
	;IN: D0.B, D1.B
		MOVEA.L #$A04000,A0     ;$A04000 = register selector, $A04001 = data to write.
.wait1:
		BTST #7,(A0)            ;read the YM2612's busy state from $A04000. Bit 7 equals 1 if busy
		BNE .wait1              ;loop until not busy
		MOVE.B D0,(A0)		;select the register ID held in D0.B and write it to $A04000.
.wait2:
		BTST #7,(A0)            ;read the YM2612's busy state from $A04000. Bit 7 equals 1 if busy
		BNE .wait2              ;loop until not busy
		MOVE.B D1,(1,A0)	;write the data for the selected register into $A04001.
	MOVE.L (SP)+,A0
	rts

pcmSample:
        incbin "X:\ResAll\pcmSample.bin"

AutoHotkey

No builtin functions for:
1. monitoring timepoints in the sound
2. simultaneous play

SoundPlay, %A_WinDir%\Media\tada.wav, wait
SoundPlay, %A_WinDir%\Media\Windows XP Startup.wav, wait

; simulaneous play may require a second script

SoundPlay, %A_WinDir%\Media\tada.wav
SoundPlay, Nonexistent  ; stop before finishing

SoundSet +10  ; increase volume by 10%
Loop, 2
    SoundPlay, %A_WinDir%\Media\tada.wav, wait

Batch File

It only works on Windows XP and it only reads Wave (.wav) audio files:

 @echo off
 :main
 cls
 echo Drag a .wav file there and press enter
 set /p file1=
 sndrec32 /embedding /play /close %file1%
 cls
 echo Drag a second .wav file and both will play together
 set /p file2=
 sndrec32 /embedding /play /close %file1% | sndrec32 /embedding /play /close %file2%
 echo Thanks
 pause>nul
 exit

BBC BASIC

BBC BASIC for Windows has native support for playing MIDI files, and WAV files can be played using simple API calls:

      SND_LOOP = 8
      SND_ASYNC = 1
      SND_FILENAME = &20000
      
      PRINT "Playing a MIDI file..."
      *PLAY C:\windows\media\canyon.mid
      
      WAIT 300
      PRINT "Playing the Windows TADA sound quietly..."
      wave$ = "\windows\media\tada.wav"
      volume% = 10000
      SYS "waveOutSetVolume", -1, volume% + (volume% << 16)
      SYS "PlaySound", wave$, 0, SND_FILENAME + SND_ASYNC
      
      WAIT 300
      PRINT "Playing the Windows TADA sound loudly on the left channel..."
      volume% = 65535
      SYS "waveOutSetVolume", -1, volume%
      SYS "PlaySound", wave$, 0, SND_FILENAME + SND_ASYNC
      
      WAIT 300
      PRINT "Playing the Windows TADA sound loudly on the right channel..."
      volume% = 65535
      SYS "waveOutSetVolume", -1, volume% << 16
      SYS "PlaySound", wave$, 0, SND_FILENAME + SND_ASYNC
      
      WAIT 300
      PRINT "Looping the Windows TADA sound on both channels..."
      volume% = 65535
      SYS "waveOutSetVolume", -1, volume% + (volume% << 16)
      SYS "PlaySound", wave$, 0, SND_FILENAME + SND_ASYNC + SND_LOOP
      
      WAIT 300
      SYS "PlaySound", 0, 0, 0
      PRINT "Stopped looping..."
      
      WAIT 300
      SOUND OFF
      PRINT "Stopped MIDI."
      
      END

C#

using System;
using System.Threading;
using System.Media;

class Program
{
    static void Main(string[] args)
    {
        //load sound file
        SoundPlayer s1 = new SoundPlayer(); //
        s1.SoundLocation = file; // or "s1 = new SoundPlayer(file)"

        //play
        s1.Play();     

        //play for 0.1 seconds
        s1.Play();
        Thread.Sleep(100);
        s1.Stop();

        //loops
        s1.PlayLooping();
    }
}

Delphi

program PlayRecordedSounds;

{$APPTYPE CONSOLE}

uses MMSystem;

begin
  sndPlaySound('SoundFile.wav', SND_NODEFAULT OR SND_ASYNC);
end.

Go

Translation of: PicoLisp


Go doesn't have any audio support in its standard library and it's best therefore to invoke a utility such as SoX to complete a task such as this.

Although there is at least one third party library which provides Go bindings to the libsox sound library, for casual use it's far easier to invoke SoX directly with a list of arguments.

See the PicoLisp entry for a description of what these particular arguments do.

package main

import (
    "log"
    "os"
    "os/exec"
)

func main() {
    args := []string{
        "-m", "-v", "0.75", "a.wav", "-v", "0.25", "b.wav",
        "-d",
        "trim", "4", "6",
        "repeat", "5",
    }
    cmd := exec.Command("sox", args...)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }
}

GUISS

Here we use the media player to play a sound file in the default directory:

Start,Programs,Accessories,Media Player,Menu:File,Open,
Doubleclick:Icon:Sound.WAV,Button:OK,Button:Play

Liberty BASIC

'Supports .mid and .wav natively
'Midi may be played simultaneously
'with .wav but only one .wav voice
'is supported. Multiple voices can
'be achieved via API or Dlls such
'as FMOD or Wavemix

'to play a .mid and return its length
playmidi "my.mid", midiLength

'check where we are in .mid
if midipos()<midiLength then
    print "Midi still playing"
else
    print "Midi ended"
end if

'to stop a playing .mid
stopmidi

'to play a .wav and wait for it to end
playwave "my.wav"

'to play a .wav and continue procesing
playwave "my.wav", async

'to loop a .wav and continue processing
playwave "my.wav",loop

'to silence any .wav
playwave ""
or
playwave "next.wav"

'to adjust .wav vol
'set left and right full on
left =65535
right=65535
dwVol=right*65536+left
calldll #winmm, "waveOutSetVolume", 0 as long, _
    dwVol as long, ret as long


Julia

Gtk GUI app version using SoX play. All task suggestions except stereo and position mixing are supported. Supported formats are MP2, WAV, OGG, FLAC, , AIFF, and many others through the libsndfile library. This makes a good music player. There is a slight latency to load the player, so very low game latencies would not be well supported.

using Distributed, Gtk, LibSndFile, MP3
using FileIO: load

function recordingplayerapp(numfiles=2)
    # create window and widgets
    win = GtkWindow("Recorded Sound Player", 400, 400) |> (GtkFrame() |> (vbox = GtkBox(:v)))
    playprocess = @async(0)
    filenames = fill("", numfiles)
    filedurations = zeros(numfiles)
    filebutton = GtkButton[]
    for i in 1:numfiles
        push!(filebutton, GtkButton("Select File $i"))
        push!(vbox, filebutton[i])
    end

    sequencebutton = GtkButton("Play In Sequence")
    simulbutton = GtkButton("Play Simultaneously")
    seqhandlerid = zero(UInt32)
    simulhandlerid = zero(UInt32)
    stopbutton = GtkButton("Stop Play")

    labelstart = GtkLabel("Start Position")
    startcontrol = GtkScale(false, 1:100)
    set_gtk_property!(startcontrol, :hexpand, true)
    adj = GtkAdjustment(startcontrol)

    labelstop = GtkLabel("Stop Position")
    endcontrol = GtkScale(false, 1:100)
    set_gtk_property!(endcontrol, :hexpand, true)
    adj = GtkAdjustment(endcontrol)

    labelrepeat = GtkLabel("Repeats")
    repeats = GtkScale(false, 0:8)
    set_gtk_property!(repeats, :hexpand, true)
    set_gtk_property!(GtkAdjustment(repeats), :value, 0)

    foreach(x -> push!(vbox, x), [sequencebutton, simulbutton, stopbutton, labelstart,
                                  startcontrol, labelstop, endcontrol, labelrepeat, repeats])
    twobox = GtkBox(:h)
    push!(vbox, twobox)

    fboxes = GtkBox[]
    volumecontrols = GtkScale[]
    startcontrols = GtkScale[]
    endcontrols = GtkScale[]
    loopcontrols = GtkScale[]
    for i in 1:numfiles
        push!(fboxes, GtkBox(:v))
        push!(twobox, fboxes[i])

        push!(volumecontrols, GtkScale(false, 0.0:0.05:1.0))
        set_gtk_property!(volumecontrols[i], :hexpand, true)
        adj = GtkAdjustment(volumecontrols[i])
        set_gtk_property!(adj, :value, 0.5)
        push!(fboxes[i], GtkLabel("Volume Stream $i"), volumecontrols[i])

        signal_connect(filebutton[i], :clicked) do widget
            filenames[i] = open_dialog("Pick a sound or music file")
            filenames[i] = replace(filenames[i], "\\" => "/")
            set_gtk_property!(filebutton[i], :label, filenames[i])
            if count(x -> x != "", filenames) > 0
                signal_handler_unblock(sequencebutton, seqhandlerid)
            end
            if count(x -> x != "", filenames) > 1
                signal_handler_unblock(simulbutton, simulhandlerid)
            end
            if occursin(r"\.mp3$", filenames[i])
                buf = load(filenames[i])
                filedurations[i] = MP3.nframes(buf) / MP3.samplerate(buf)
            else
                buf = load(filenames[i])
                filedurations[i] = LibSndFile.nframes(buf) / LibSndFile.samplerate(buf)
            end
            adj = GtkAdjustment(startcontrol)
            set_gtk_property!(adj, :lower, 0.0)
            set_gtk_property!(adj, :upper, maximum(filedurations))
            set_gtk_property!(adj, :value, 0.0)
            adj = GtkAdjustment(endcontrol)
            set_gtk_property!(adj, :lower, 0.0)
            set_gtk_property!(adj, :upper, maximum(filedurations))
            set_gtk_property!(adj, :value, maximum(filedurations))
        end
    end

    # run play after getting parameters from widgets
    function play(simul::Bool)
        args = simul ? String["-m"] : String[]
        tstart = Gtk.GAccessor.value(startcontrol)
        tend = Gtk.GAccessor.value(endcontrol)
        for i in 1:numfiles
            if filenames[i] != ""
                volume = Gtk.GAccessor.value(volumecontrols[i])
                push!(args, "-v", string(volume), filenames[i])
            end
        end
        repeat = Gtk.GAccessor.value(repeats)
        if repeat != 0
            push!(args, "repeat", string(repeat))
        end
        if !(tstart  0.0 && tend  maximum(filedurations))
            push!(args, "trim", string(tstart), string(tend))
        end
        playprocess = run(`play $args`; wait=false)
        clearfornew()
    end

    function clearfornew()
        signal_handler_block(sequencebutton, seqhandlerid)
        if count(x -> x != "", filenames) > 1
            signal_handler_block(simulbutton, simulhandlerid)
        end
        filenames = fill("", numfiles)
        filedurations = zeros(numfiles)
        foreach(i -> set_gtk_property!(filebutton[i], :label, "Select File $i"), 1:numfiles)
        set_gtk_property!(GtkAdjustment(repeats), :value, 0)
        showall(win)
    end
    
    killplay(w) = kill(playprocess)

    playsimul(w) = play(true)
    playsequential(w) = play(false)

    seqhandlerid = signal_connect(playsequential, sequencebutton, :clicked)
    signal_handler_block(sequencebutton, seqhandlerid)
    simulhandlerid = signal_connect(playsimul, simulbutton, :clicked)
    signal_handler_block(simulbutton, simulhandlerid)
    signal_connect(killplay, stopbutton, :clicked)

    cond = Condition()
    endit(w) = notify(cond)
    signal_connect(endit, win, :destroy)
    showall(win)
    wait(cond)
end

recordingplayerapp()

Mathematica/Wolfram Language

This code demonstrates : loading two prerecorded sounds,playing them individually then simultaneously, stopping before the end of the sound,looping (preferably glitch-free)and setting the volume of each sound

a = Import["sound1.flac","FLAC"]; b = Import["sound2.flac","FLAC"];

ListPlay[a, {t, 0, 10}]; ListPlay[b, {t, 0, 10}];

ListPlay[{a,b}, {t, 0, 10}];

Stopping before the end can be done using the GUI or by reducing the parameter range of the ListPlay function. 

While[True,ListPlay[{a,b}, {t, 0, 10}];]

ListPlay[{0.5*a, 0.3*b}, {t, 0, 10}];

-Supported audio formats : AIFF Macintosh sound format (.aif, .aiff), AU Mu law encoding Unix Audio Format (.au), FLAC lossless audio codec (.flac), SND file format, equivalent to AU (.snd), "WAV" WAV format (.wav), "Wave64" Sony Wave64 format (.w64), MIDI format (.mid)

-Suitability for game sound effects : low-latency start : yes resource efficiency : low support for many simultaneous sounds : yes

-Suitable for playing sound of arbitrary long duration.

Nim

Translation of: Go

Using "sox" to play two sound files mixed. These are "wav" files but "sox" recognizes a lot of sound formats.

import osproc

let args = ["-m", "-v", "0.75", "a.wav", "-v", "0.25", "b.wav",
            "-d",
            "trim", "4", "6",
            "repeat", "5"]
echo execProcess("sox", args = args, options = {poStdErrToStdOut, poUsePath})

Phix

integer xPlaySound = 0

procedure play_sound()--string filename)
    if platform()=WINDOWS then
        string filename = join_path({getenv("SYSTEMROOT"),"Media","chord.wav"})
        if xPlaySound=0 then
            atom winmm = open_dll("winmm.dll")
            xPlaySound = define_c_proc(winmm,"sndPlaySoundA",{C_PTR,C_INT})
        end if
        c_proc(xPlaySound,{filename,1})
    elsif platform()=LINUX then
        system("paplay chimes.wav")
        system("paplay chord.wav")
    end if
end procedure
play_sound()

PicoLisp

The obvious way is to call 'sox', the "Swiss Army knife of audio manipulation" (man sox).

The following plays two files "a.wav" and "b.wav" simultaneously (to play them individually, omit the "-m" flag). The first one is played with a volume of 75 percent, the second with 25 percent, starting at the 4th second, with a duration of 6 seconds, looping 5 times.

(call 'sox
   "-m"  "-v" "0.75" "a.wav"  "-v" "0.25" "b.wav"
   "-d"
   "trim" 4 6
   "repeat" 5 )

PureBasic

InitSound()
; We need this to use Sound functions
UseOGGSoundDecoder()
; Now we can not only load wav sound files, but also ogg encoded ones.
;   With movie library more formats can be played (depends on system) but you cannot
;   handle them with Sound functions
If Not LoadSound(1,"Path/to/Sound/1.ogg") Or Not LoadSound(2,"Path/to/Sound/2.wav")
MessageRequester("Error","One of our sounds could not be loaded"+Chr(10)+"Use Debugger to check which one")
EndIf

;- simultaneous playing
PlaySound(1)
PlaySound(2)

;- manipulating sounds
Delay(1000)
; pause for one second, to let user hear something
SoundVolume(1,90)
SoundVolume(2,60)
; reduce volume of the sounds a bit
SoundPan(1,-80)
SoundPan(2,100)
; Sound 1 mostly left speaker, sound 2 only right speaker
SoundFrequency(1,30000)
; play sound one faster
Delay(1000)
; pause for one second, to let user hear effects of previous actions

;- stopping while playing
StopSound(-1)
; value -1 stops all playing sounds
PlaySound(1,#PB_Sound_Loop)
; continous looping without glitch

;suitable for 2D games and music playing.
; TODO: There is a Sound3D library for 3D Games, needs to be decribed here too

Python

Works with: Python version 2.6
Library: pygame

Pygame is a library for cross-platform game development and depends on the SDL multimedia library (SDL_mixer) for its audio playback. SDL_mixer supports any number of simultaneously playing channels of 16 bit stereo audio, plus a single channel of music, mixed by the popular MikMod MOD, Timidity MIDI, Ogg Vorbis, and SMPEG MP3 libraries.

import time
from pygame import mixer

mixer.init(frequency=16000) #set frequency for wav file
s1 = mixer.Sound('test.wav')
s2 = mixer.Sound('test2.wav')

#individual
s1.play(-1)         #loops indefinitely
time.sleep(0.5)

#simultaneously
s2.play()          #play once 
time.sleep(2)
s2.play(2)         #optional parameter loops three times 
time.sleep(10)

#set volume down
s1.set_volume(0.1)
time.sleep(5)

#set volume up
s1.set_volume(1)
time.sleep(5)

s1.stop()
s2.stop()
mixer.quit()

To play back .mp3 (or .ogg) files, the music import is used.

import time
from pygame import mixer
from pygame.mixer import music

mixer.init()
music.load('test.mp3')

music.play()
time.sleep(10)

music.stop()
mixer.quit()

R

Library: sound

R's sound package uses system commands to call a built-in media player. On Windows, by default, this is Media Player. You can see the system call with WavPlayer(). Only .WAV files are supported, and the external code call means there is some latency before sounds are played. Samples longer than 10 minutes may correspond to significant chunks of your machine's memory.

#Setup
# Warning: The example files are Windows specific.
library(sound)
media_dir <- file.path(Sys.getenv("SYSTEMROOT"), "Media")
chimes <- loadSample(file.path(media_dir, "chimes.wav"))
chord <- loadSample(file.path(media_dir, "chord.wav"))

# Play sequentially
play(appendSample(chimes, chord))
 
# Play simultaneously
play(chimes + chord)

# Stopping before the end
play(cutSample(chimes, 0, 0.2))

#Looping
for(i in 1:3) play(chimes)                 #leaves a gap between instances

three_chimes <- lapply(vector(3, mode = "list"), function(x) chimes)
play(do.call(appendSample, three_chimes))  #no gap, see also cutSampleEnds

# Volume control
play(3 * chimes)

#Stereo effect
play(stereo(chimes, chord))

#Other actions (not obviously possible)

Racket

(Works on all platforms.)

#lang racket/gui
(play-sound "some-sound.wav" #f)

Ring

Load "guilib.ring"
new qapp {
         q1=NULL  q2=NULL
         win1 = new qwidget() {
                setwindowtitle("play sound!") show()
                setgeometry(100,100,400,400)
         }
         new qpushbutton(win1) {
             setgeometry(50,50,100,30)
             settext("play1")
             setclickevent("playmusic1()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(200,50,100,30)
             settext("play2")
             setclickevent("playmusic2()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(50,100,100,30)
             settext("pause1")
             setclickevent("pauseplay1()")
         show()
         }
         new qpushbutton(win1) {
             setgeometry(200,100,100,30)
             settext("pause2")
             setclickevent("pauseplay2()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(50,150,100,30)
             settext("stop1")
             setclickevent("stopplay1()")
             show()
                }
         new qpushbutton(win1) {
             setgeometry(200,150,100,30)
             settext("stop2")
             setclickevent("stopplay2()")
             show()
         }
         lineedit1 = new qlineedit(win1) {
                     setGeometry(50,200,100,30)
                     settext("50")
                     show()
         }
         lineedit2 = new qlineedit(win1) {
                     setGeometry(200,200,100,30)
                     settext("50")
                     show()
         }
         new qpushbutton(win1) {
             setgeometry(50,250,100,30)
             settext("volume1")
             setclickevent("volume1()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(200,250,100,30)
             settext("volume2")
             setclickevent("volume2()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(50,300,100,30)
             settext("mute1")
             setclickevent("mute1()")
             show()
         }
         new qpushbutton(win1) {
             setgeometry(200,300,100,30)
             settext("mute2")
             setclickevent("mute2()")
             show()
         }
         exec()
         }

func playmusic1
     q1 = new qmediaplayer(win1)  {
          setmedia(new qurl("music1.wav"))
          setvolume(50) play()
     }

func playmusic2
     q2 = new qmediaplayer(win1)  {
          setmedia(new qurl("music2.wav"))
          setvolume(50) play()
     }

func pauseplay1
     q1.pause()

func pauseplay2
     q2.pause()

func stopplay1
     q1.stop()

func stopplay2
     q2.stop()

func volume1
     lineedit1 { vol1 = text() }
     q1 {setvolume(number(vol1))}

func volume2
     lineedit2 { vol2 = text() }
     q2 {setvolume(number(vol2))}

func mute1
     q1.setmuted(true)

func mute2
     q2.setmuted(true)

Output:

CalmoSoftSounds

Ruby

There aren't many mature sound libraries for Ruby. The

Library: RubyGems

package win32-sound can play WAV files on the Windows platform only.

require 'win32/sound'
include Win32

sound1 = ENV['WINDIR'] + '\Media\Windows XP Startup.wav' 
sound2 = ENV['WINDIR'] + '\Media\Windows XP Shutdown.wav' 

puts "play the sounds sequentially"
[sound1, sound2].each do |s| 
  t1 = Time.now
  Sound.play(s)
  puts "'#{s}' duration: #{(Time.now.to_f - t1.to_f)} seconds"
end

puts "attempt to play the sounds simultaneously"
[sound1, sound2].each {|s| Sound.play(s, Sound::ASYNC)}

puts <<END
the above only plays the second sound2 because the library only appears 
to be able to play one sound at a time.
END

puts "loop a sound for a few seconds"
puts Time.now
Sound.play(sound1, Sound::ASYNC + Sound::LOOP)
sleep 10
Sound.stop
puts Time.now

puts "manipulate the volume"
vol_left, vol_right = Sound.wave_volume
Sound.play(sound1, Sound::ASYNC)
sleep 1
puts "right channel quiet"
Sound.set_wave_volume(vol_left, 0)
sleep 1
puts "left channel quiet"
Sound.set_wave_volume(0, vol_right)
sleep 1
puts "restore volume"
Sound.set_wave_volume(vol_left, vol_right)

sleep 1
puts "the asynchronous sound is cancelled when the program exits"

Swift

Uses AVFoundation's AVAudioPlayer.

import AVFoundation

// This example uses AVAudioPlayer for playback.
// AVAudioPlayer is the player Apple recommends for playback, since it suitable for songs
// and offers control over numerous playback parameters.
// It can play any type that is natively supported by OSX or iOS

class PlayerControl: NSObject, AVAudioPlayerDelegate {
    let player1:AVAudioPlayer!
    let player2:AVAudioPlayer!
    var playedBoth = false
    var volume:Float {
        get {
            return player1.volume
        }
        
        set {
            player1.volume = newValue
            player2.volume = newValue
        }
    }
    
    init(player1:AVAudioPlayer, player2:AVAudioPlayer) {
        super.init()
        self.player1 = player1
        self.player2 = player2
        self.player1.delegate = self
        self.player2.delegate = self
    }
    
    func loop() {
        player1.numberOfLoops = 1
        player1.play()
        
        let time = Int64((Double(player1.duration) + 2.0) * Double(NSEC_PER_SEC))
        
        dispatch_after(dispatch_time(0, time), dispatch_get_main_queue()) {[weak self] in
            println("Stopping track")
            self?.player1.stop()
            exit(0)
        }
    }
    
    func playBoth() {
        player1.play()
        player2.play()
    }
    
    func audioPlayerDidFinishPlaying(player:AVAudioPlayer!, successfully flag:Bool) {
        if player === player2 && !playedBoth {
            playBoth()
            playedBoth = true
        } else if player === player2 && playedBoth {
            loop()
        }
    }
}

let url1 = NSURL(string: "file:///file1.mp3")
let url2 = NSURL(string: "file:///file2.mp3")

var err:NSError?
let player1 = AVAudioPlayer(contentsOfURL: url1, error: &err)
let player2 = AVAudioPlayer(contentsOfURL: url2, error: &err)

let player = PlayerControl(player1: player1, player2: player2)

// Setting the volume
player.volume = 0.5

// Play track 2
// When this track finishes it will play both of them
// by calling the audioPlayerDidFinishPlaying delegate method
// Once both tracks finish playing, it will then loop the first track twice
// stopping the track after 2 seconds of the second play
player.player2.play()

CFRunLoopRun()

Tcl

Library: snack
package require sound
# Potentially also require driver support for particular formats

# Load some sounds in; this part can be slow
snack::sound s1
s1 read $soundFile1
snack::sound s2
s2 read $soundFile2

# Play a sound for a while (0.1 seconds; this is a low-latency operation)
s1 play
after 100 set done 1; vwait done; # Run the event loop for a while
s1 stop

# Play two sounds at once (for 30 seconds) while mixing together
s1 play
s2 play
after 30000 set done 1; vwait done
s1 stop
s2 stop

Note that this library is capable of handling both short and long sounds (sound effects and music).

TUSCRIPT

$$ MODE TUSCRIPT
audiofile="test.wav"
ERROR/STOP OPEN (audiofile,READ,-std-)
BROWSE $audiofile

UNIX Shell

#!/usr/bin/sh

# play.sh

# Plays .au files.
# Usage: play.sh <recorded_sound.au>

cat $1 >> /dev/audio # Write file $1 to the speaker's Character Special (/dev/audio).

VBA

Visual Basic for Applications can play sounds in the .WAV format by calling the multimedia library winmm.dll. See http://support.microsoft.com/kb/158140/en-us. The "Flags" parameter can be used e.g. to play a sound continuously (that is, until the function is called again to stop playing).

Volume can be set using the function waveOutSetVolume, see http://support.microsoft.com/kb/118377/en-us.

Declare Function libPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" _
    (ByVal filename As String, ByVal Flags As Long) As Long

Sub PlaySound(sWav As String)
  Call libPlaySound(sWav, 1) '1 to play asynchronously
End Sub

Type

Playsound "d:\explode.wav"

in the Immediate window and that sound will play. Nothing will happen if the file d:\explode.wav does not exist.

Wren

Library: DOME

The above library currently supports playback of OGG and WAV files, with a sample frequency of 44.1kHz.

It is certainly suitable for game sound effects (it's a game engine) and can play music at CD quality as well.

import "audio" for AudioEngine
import "dome" for Process

class Game {
    static init() {
        // load a .wav file and give it a friendly name
        AudioEngine.load("a", "a.wav")
        // play the file at volume v, looping l and pan p
        __v = 2      // twice 'normal' volume
        __l = true   // loop when finished
        __p = 0.5    // division between left and right audio channels (-1 to +1)
        __chan1 = AudioEngine.play("a", __v, __l, __p)
        __fc = 0     // frame counter, updates at 60 fps
    }

    static update() { __fc = __fc + 1 }

    static draw(dt) {
        if (__fc == 600) {
            // after 10 seconds load and play a second .wav file simultaneously, same settings
            AudioEngine.load("b", "b.wav")
            __chan2 = AudioEngine.play("b", __v, __l, __p)
        }
        if (__fc == 1200) {
            __chan1.stop()   // after a further 10 seconds, stop the first file
            AudioEngine.unload("a")  // and unload it
        } else if (__fc == 1800) {
            __chan2.stop() // after another 10 seconds, stop the second file
            AudioEngine.unload("b")  // and unload it
            Process.exit(0)  // exit the application
        }
    }
}

Z80 Assembly

Works with: Sega Genesis

This routine streams an unsigned 8-bit PCM sample to the Yamaha 2612's DAC in order to play a recorded sound. Unfortunately, the Sega Genesis's Z80 coprocessor can only access a limited pool of memory, so any PCM samples will have to be copied from the cartridge ROM to the shared memory area.

dac_enable equ &2B
dac_data equ &2A

init_dac:
	;hl = pointer to stream
	push bc
	push de
	ld de,&4000
inline_waiting1:
		ld a,(de)
		rlca        
		jr c,inline_waiting1  ;loop until bit 7 of (&4000) is clear.
	;ready to write
	ld a,dac_enable
	ld (de),a		;write &2B to reg select
inline_waiting2:
		ld a,(de)
		rlca
		jr c,inline_waiting2
	ld a,&80
	ld (&4001),a	;write &80 to reg data (dac enable)
	
;now start streaming.
stream_dac:
inline_waiting3:
		ld a,(de)
		rlca
		jr c,inline_waiting3
	ld a,dac_data
	ld (&4000),a
	
inline_waiting4:
		ld a,(de)
		rlca
		jr c,inline_waiting4
	ld a,(hl)
	ld (&4001),a
	inc hl
	or a               ;exit on null terminator.
	jp nz,stream_dac
	pop de
	pop bc
	ret