Play recorded sounds: Difference between revisions

m
→‎{{header|Wren}}: Changed to Wren S/H
(Added Wren)
m (→‎{{header|Wren}}: Changed to Wren S/H)
(19 intermediate revisions by 7 users not shown)
Line 15:
 
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").
 
=={{header|68000 Assembly}}==
{{works with|Sega Genesis}}
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.
 
<syntaxhighlight lang="68000devpac">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"</syntaxhighlight>
 
=={{header|AutoHotkey}}==
Line 20 ⟶ 70:
1. monitoring timepoints in the sound <br>
2. simultaneous play
<langsyntaxhighlight AutoHotkeylang="autohotkey">SoundPlay, %A_WinDir%\Media\tada.wav, wait
SoundPlay, %A_WinDir%\Media\Windows XP Startup.wav, wait
 
Line 30 ⟶ 80:
SoundSet +10 ; increase volume by 10%
Loop, 2
SoundPlay, %A_WinDir%\Media\tada.wav, wait</langsyntaxhighlight>
 
=={{header|Batch File}}==
Line 52 ⟶ 102:
{{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:
<langsyntaxhighlight lang="bbcbasic"> SND_LOOP = 8
SND_ASYNC = 1
SND_FILENAME = &20000
Line 92 ⟶ 142:
PRINT "Stopped MIDI."
END</langsyntaxhighlight>
 
=={{header|C}}==
==={{libheader|Gadget}}===
<p>The functions written below access the operating system, running "aplay" (Linux Debian 11 and derivatives).
They work very well, and you can see an example of them working in a terminal game called "Pacman.c", which can be found at the following path:</p>
 
https://github.com/DanielStuardo/Gadget
 
<syntaxhighlight lang="c">
/*
plays the sound file, and returns a string with the PID number of that play.
 
Call:
char* pid_sound = put_sound( "file.wav" );
or
String pid_sound;
....
Fn_let( pid_sound, put_sound( "file.wav" ) );
*/
char * put_sound( char* file_sound )
{
String PID_SOUND;
system( file_sound );
PID_SOUND = `pidof aplay`;
char ot = Set_new_sep(' ');
Fn_let( PID_SOUND, Get_token(PID_SOUND, 1));
Set_token_sep(ot);
return PID_SOUND;
}
 
/*
Deletes a sound that is playing.
It may happen that when trying to kill the process, "aplay" has already finished.
Call:
kill_sound( pid_sound );
Free secure pid_sound;
*/
void kill_sound( char * PID_SOUND )
{
String pid;
pid = `pidof aplay`;
if( Occurs( PID_SOUND, pid ){
char strkill[256];
sprintf( strkill, "kill -9 %s </dev/null >/dev/null 2>&1 &", PID_SOUND);
system(strkill);
}
Free secure pid;
}
 
/*
Clears all sounds that are playing.
Call:
kill_all_sounds();
and then free all string of pid's:
Free secure pid1, pid2, ... ;
*/
void kill_all_sounds()
{
String PID;
Fn_let ( PID, Get_sys("pidof aplay" ));
if (strlen(PID)>0){
char cpids[256];
sprintf(cpids,"kill -9 %s </dev/null >/dev/null 2>&1",PID);
system(cpids);
}
Free secure PID;
}
</syntaxhighlight>
 
=={{header|C sharp|C#}}==
 
<langsyntaxhighlight lang="csharp">using System;
using System.Threading;
using System.Media;
Line 119 ⟶ 238:
s1.PlayLooping();
}
}</langsyntaxhighlight>
 
=={{header|Delphi}}==
<langsyntaxhighlight Delphilang="delphi">program PlayRecordedSounds;
 
{$APPTYPE CONSOLE}
Line 130 ⟶ 249:
begin
sndPlaySound('SoundFile.wav', SND_NODEFAULT OR SND_ASYNC);
end.</langsyntaxhighlight>
 
 
=={{header|FutureBasic}}==
'''Library: AVFoundation'''
 
FB has several native ways to play recorded sounds, ranging from simple to commercial. It also can play a variety of audio formats including mp3, m4a, aiff, wav, etc. This task code uses the AVFoundation library to create a basic player with simple controls including a popup menu from which bundled audio files can be selected.
<syntaxhighlight lang="futurebasic">
include "Tlbx AVFoundation.incl"
 
include resources "Here Comes the Sun.mp3"
include resources "Wake Me Up.mp3"
include resources "I Walk the Line.aif"
 
_window = 1
begin enum 1
_progInd
_timeLabel
_durLabel
_playBtn
_pauseBtn
_stopBtn
_selectBtn
end enum
 
void local fn FixButtons
dispatchmain // configure UI elements on main thread
AVAudioPlayerRef player = fn AppProperty( @"Player" )
if ( player )
button _playBtn, NO
if ( fn AVAudioPlayerIsPlaying( player ) )
button _pauseBtn, YES,, @"Pause"
button _stopBtn, YES
else
button _pauseBtn, YES,, @"Resume"
end if
else
textlabel _timeLabel, @"--.-"
progressindicator _progInd, 0.0
textlabel _durLabel, @"--.-"
button _playBtn, YES
button _pauseBtn, NO,, @"Pause"
button _stopBtn, NO
end if
dispatchend
end fn
 
void local fn BuildWindow
window _window, @"AVAudioPlayer", (0,0,480,87), NSWindowStyleMaskTitled + NSWindowStyleMaskClosable
textlabel _timeLabel, @"--.-", (18,52,38,16)
ControlSetAlignment( _timeLabel, NSTextAlignmentRight )
progressindicator _progInd,, (62,48,356,20)
ProgressIndicatorSetUsesThreadedAnimation( _progInd, NO )
textlabel _durLabel, @"--.-", (424,52,38,16)
button _playBtn,,, @"Start", (14,13,89,32)
button _pauseBtn, NO,, @"Pause", (103,13,89,32)
button _stopBtn, NO,, @"Stop", (192,13,89,32)
popupbutton _selectBtn,,, @"Here Comes the Sun;Wake Me Up;I Walk the Line", (290,17,170,25)
end fn
 
void local fn Cleanup
fn FixButtons
AppRemoveProperty( @"Player" )
AppRemoveProperty( @"Timer" )
end fn
 
void local fn DidFinishPlayingHandler( player as AVAudioPlayerRef, success as BOOL, userData as ptr )
fn Cleanup
end fn
 
local fn MyAppTimer( timer as CFRunLoopTimerRef )
dispatchmain // configure UI elements on main thread
CFTimeInterval ti, dur
AVAudioPlayerRef player = fn AppProperty( @"Player" )
if ( player )
ti = fn AVAudioPlayerCurrentTime( player )
dur = fn AVAudioPlayerDuration( player )
ProgressIndicatorSetDoubleValue( _progInd, ti*100/dur )
textlabel _timeLabel, fn StringWithFormat( @"%.1f", ti )
end if
dispatchend
end fn
 
void local fn PlayAction
CFURLRef url = fn AppProperty( @"songURL" )
AVAudioPlayerRef player = fn AVAudioPlayerWithContentsOfURL( url, NULL )
AVAudioPlayerSetDidFinishPlayingHandler( player, @fn DidFinishPlayingHandler, NULL )
AppSetProperty( @"Player", player )
textlabel _durLabel, fn StringWithFormat(@"%.1f",fn AVAudioPlayerDuration(player))
CFRunLoopTimerRef t = fn AppSetTimer( 0.1, @fn MyAppTimer, YES )
AppSetProperty( @"Timer", t )
fn AVAudioPlayerPlay( player )
fn FixButtons
end fn
 
void local fn PauseAction
AVAudioPlayerRef player = fn AppProperty( @"Player" )
if ( player )
if ( fn AVAudioPlayerIsPlaying( player ) )
AVAudioPlayerPause( player )
else
fn AVAudioPlayerPlay( player )
end if
end if
fn FixButtons
end fn
 
void local fn StopAction
AVAudioPlayerRef player = fn AppProperty( @"Player" )
if ( player )
AVAudioPlayerStop( player )
end if
fn Cleanup
end fn
 
 
void local fn SongURL( songTitle as CFStringRef )
CFURLRef url
select (songTitle)
case @"Here Comes the Sun" : url = fn BundleURLForResource( fn BundleMain, songTitle, @"mp3", NULL )
case @"Wake Me Up" : url = fn BundleURLForResource( fn BundleMain, songTitle, @"mp3", NULL )
case @"I Walk the Line" : url = fn BundleURLForResource( fn BundleMain, songTitle, @"aif", NULL )
end select
AppSetProperty( @"songURL", url )
end fn
 
 
void local fn DoDialog( ev as long, tag as long )
select ( ev )
case _btnClick
select ( tag )
case _playBtn : fn StopAction : fn SongURL( fn PopUpButtonTitleOfSelectedItem( _selectBtn ) ) : fn PlayAction
case _pauseBtn : fn PauseAction
case _stopBtn : fn StopAction
case _selectBtn : fn StopAction : fn SongURL( fn PopUpButtonTitleOfSelectedItem( _selectBtn ) ) : fn PlayAction
end select
case _windowWillClose : end
end select
end fn
 
fn BuildWindow
 
on dialog fn DoDialog
 
HandleEvents
</syntaxhighlight>
{{output}}
[[File:AVAudio Player.png]]
 
 
 
 
=={{header|Go}}==
Line 140 ⟶ 410:
 
See the PicoLisp entry for a description of what these particular arguments do.
<langsyntaxhighlight lang="go">package main
 
import (
Line 161 ⟶ 431:
log.Fatal(err)
}
}</langsyntaxhighlight>
 
=={{header|GUISS}}==
Line 167 ⟶ 437:
Here we use the media player to play a sound file in the default directory:
 
<langsyntaxhighlight lang="guiss">Start,Programs,Accessories,Media Player,Menu:File,Open,
Doubleclick:Icon:Sound.WAV,Button:OK,Button:Play</langsyntaxhighlight>
 
=={{header|Liberty BASIC}}==
<langsyntaxhighlight lang="lb">'Supports .mid and .wav natively
'Midi may be played simultaneously
'with .wav but only one .wav voice
Line 212 ⟶ 482:
calldll #winmm, "waveOutSetVolume", 0 as long, _
dwVol as long, ret as long
</langsyntaxhighlight>
 
=={{header|Java}}==
<syntaxhighlight lang="java">
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
 
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
 
public final class PlayRecordedSounds {
 
public static void main(String[] aArgs) {
PlayRecordedSounds soundPlayer = new PlayRecordedSounds();
soundPlayer.play();
scanner = new Scanner(System.in);
int choice = 0;
while ( choice != 6 ) {
System.out.println("1. Pause");
System.out.println("2. Resume");
System.out.println("3. Restart");
System.out.println("4. Jump to specific time");
System.out.println("5. Stop");
System.out.println("6. Quit the program");
choice = scanner.nextInt();
soundPlayer.select(choice);
}
scanner.close();
}
private enum Status { PLAYING, PAUSED, STOPPED }
 
private PlayRecordedSounds() {
resetAudioStream();
}
private void select(int aChoice) {
switch ( aChoice ) {
case 1 -> pause();
case 2 -> resume();
case 3 -> restart();
case 4 -> jump();
case 5 -> stop();
case 6 -> quit();
default -> { /* Take no action */ }
}
}
private void play() {
status = Status.PLAYING;
clip.start();
}
private void pause() {
if ( status == Status.PAUSED ) {
System.out.println("The audio is already paused");
return;
}
currentClipPosition = clip.getMicrosecondPosition();
clip.stop();
status = Status.PAUSED;
}
private void resume() {
if ( status == Status.PLAYING ) {
System.out.println("The audio is already being played");
return;
}
clip.close();
resetAudioStream();
clip.setMicrosecondPosition(currentClipPosition);
status = Status.PLAYING;
play();
}
private void restart() {
clip.stop();
clip.close();
resetAudioStream();
currentClipPosition = 0;
clip.setMicrosecondPosition(currentClipPosition);
status = Status.PLAYING;
play();
}
private void jump() {
System.out.println("Select a time between 0 and " + clip.getMicrosecondLength());
final long request = scanner.nextLong();
if ( request > 0 && request < clip.getMicrosecondLength() ) {
clip.stop();
clip.close();
resetAudioStream();
currentClipPosition = request;
clip.setMicrosecondPosition(currentClipPosition);
status = Status.PLAYING;
play();
}
}
private void stop() {
currentClipPosition = 0;
clip.stop();
clip.close();
status = Status.STOPPED;
}
private void quit() {
try {
scanner.close();
clip.close();
audioStream.close();
Runtime.getRuntime().exit(0);
} catch (IOException ioe) {
ioe.printStackTrace(System.err);
}
}
private void resetAudioStream() {
try {
audioStream = AudioSystem.getAudioInputStream( new File(FILE_PATH) );
clip = AudioSystem.getClip();
clip.open(audioStream);
clip.loop(Clip.LOOP_CONTINUOUSLY);
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException exception) {
exception.printStackTrace(System.err);
}
}
 
private static Scanner scanner;
private static Clip clip;
private static long currentClipPosition;
private static Status status;
private static AudioInputStream audioStream;
private static final String FILE_PATH = "./test_piece.wav";
}
</syntaxhighlight>
 
=={{header|Julia}}==
Line 220 ⟶ 635:
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.
<langsyntaxhighlight lang="julia">using Distributed, Gtk, LibSndFile, MP3
using FileIO: load
 
Line 357 ⟶ 772:
 
recordingplayerapp()
</syntaxhighlight>
</lang>
 
=={{header|Mathematica}}/{{header|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
 
<langsyntaxhighlight Mathematicalang="mathematica">a = Import["sound1.flac","FLAC"]; b = Import["sound2.flac","FLAC"];
 
ListPlay[a, {t, 0, 10}]; ListPlay[b, {t, 0, 10}];
Line 374 ⟶ 789:
While[True,ListPlay[{a,b}, {t, 0, 10}];]
 
ListPlay[{0.5*a, 0.3*b}, {t, 0, 10}];</langsyntaxhighlight>
 
-Supported audio formats :
Line 391 ⟶ 806:
 
-Suitable for playing sound of arbitrary long duration.
 
=={{header|Nim}}==
{{trans|Go}}
Using "sox" to play two sound files mixed. These are "wav" files but "sox" recognizes a lot of sound formats.
<syntaxhighlight lang="nim">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})</syntaxhighlight>
 
=={{header|Phix}}==
<langsyntaxhighlight Phixlang="phix">integer xPlaySound = 0
 
procedure play_sound()--string filename)
Line 408 ⟶ 834:
end if
end procedure
play_sound()</langsyntaxhighlight>
 
=={{header|PicoLisp}}==
Line 418 ⟶ 844:
percent, the second with 25 percent, starting at the 4th second, with a duration
of 6 seconds, looping 5 times.
<langsyntaxhighlight PicoLisplang="picolisp">(call 'sox
"-m" "-v" "0.75" "a.wav" "-v" "0.25" "b.wav"
"-d"
"trim" 4 6
"repeat" 5 )</langsyntaxhighlight>
 
=={{header|PureBasic}}==
<langsyntaxhighlight PureBasiclang="purebasic">InitSound()
; We need this to use Sound functions
UseOGGSoundDecoder()
Line 460 ⟶ 886:
 
;suitable for 2D games and music playing.
; TODO: There is a Sound3D library for 3D Games, needs to be decribed here too</langsyntaxhighlight>
 
=={{header|Python}}==
Line 468 ⟶ 894:
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.
 
<langsyntaxhighlight lang="python">import time
from pygame import mixer
 
Line 495 ⟶ 921:
s1.stop()
s2.stop()
mixer.quit()</langsyntaxhighlight>
 
To play back .mp3 (or .ogg) files, the music import is used.
 
<langsyntaxhighlight lang="python">import time
from pygame import mixer
from pygame.mixer import music
Line 510 ⟶ 936:
 
music.stop()
mixer.quit()</langsyntaxhighlight>
 
=={{header|R}}==
Line 517 ⟶ 943:
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
# Warning: The example files are Windows specific.
Line 547 ⟶ 973:
 
#Other actions (not obviously possible)
</syntaxhighlight>
</lang>
 
=={{header|Racket}}==
(Works on all platforms.)
<langsyntaxhighlight lang="racket">
#lang racket/gui
(play-sound "some-sound.wav" #f)
</syntaxhighlight>
</lang>
 
=={{header|Ring}}==
<langsyntaxhighlight lang="ring">
Load "guilib.ring"
new qapp {
Line 675 ⟶ 1,101:
func mute2
q2.setmuted(true)
</syntaxhighlight>
</lang>
Output:
 
Line 683 ⟶ 1,109:
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.
 
<langsyntaxhighlight lang="ruby">require 'win32/sound'
include Win32
 
Line 725 ⟶ 1,151:
 
sleep 1
puts "the asynchronous sound is cancelled when the program exits"</langsyntaxhighlight>
 
=={{header|Swift}}==
Uses AVFoundation's AVAudioPlayer.
<langsyntaxhighlight Swiftlang="swift">import AVFoundation
 
// This example uses AVAudioPlayer for playback.
Line 806 ⟶ 1,232:
player.player2.play()
 
CFRunLoopRun()</langsyntaxhighlight>
 
=={{header|Tcl}}==
{{libheader|snack}}
<langsyntaxhighlight lang="tcl">package require sound
# Potentially also require driver support for particular formats
 
Line 829 ⟶ 1,255:
after 30000 set done 1; vwait done
s1 stop
s2 stop</langsyntaxhighlight>
Note that this library is capable of handling both short and long sounds (sound effects and music).
 
Line 835 ⟶ 1,261:
 
=={{header|TUSCRIPT}}==
<langsyntaxhighlight lang="tuscript">
$$ MODE TUSCRIPT
audiofile="test.wav"
ERROR/STOP OPEN (audiofile,READ,-std-)
BROWSE $audiofile
</syntaxhighlight>
</lang>
 
=={{header|UNIX Shell}}==
<langsyntaxhighlight lang="bash">#!/usr/bin/sh
 
# play.sh
Line 850 ⟶ 1,276:
# Usage: play.sh <recorded_sound.au>
 
cat $1 >> /dev/audio # Write file $1 to the speaker's Character Special (/dev/audio).</langsyntaxhighlight>
 
=={{header|VBA}}==
Line 858 ⟶ 1,284:
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" _
(ByVal filename As String, ByVal Flags As Long) As Long
Line 865 ⟶ 1,291:
Call libPlaySound(sWav, 1) '1 to play asynchronously
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.
 
Line 873 ⟶ 1,299:
 
It is certainly suitable for game sound effects (it's a game engine) and can play music at CD quality as well.
<langsyntaxhighlight ecmascriptlang="wren">import "audio" for AudioEngine
import "dome" for Process
 
Line 905 ⟶ 1,331:
}
}
}</langsyntaxhighlight>
 
=={{header|Z80 Assembly}}==
{{works with|Sega Genesis}}
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.
 
<syntaxhighlight lang="z80">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</syntaxhighlight>
 
{{omit from|Applesoft BASIC}}
9,479

edits