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
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"
Ada
The SFML library is suitable for sound effects and music. The supported audio formats are ogg, wav, flac, mp3, aiff, au, raw, paf, svx, nist, voc, ircam, w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64.
with Ada.Text_IO; use Ada.Text_IO;
with Sf.Audio.Music; use Sf, Sf.Audio, Sf.Audio.Music;
procedure Play is
Sound1 : sfMusic_Ptr;
Sound2 : sfMusic_Ptr;
begin
Sound1 := createFromFile ("sound1.ogg");
Sound2 := createFromFile ("sound2.ogg");
if Sound1 = null or Sound2 = null then
Put_Line (Standard_Error, "Error: Sound files not found!");
return;
end if;
-- Looping for both sounds
setLoop (Sound1, sfTrue);
setLoop (Sound2, sfTrue);
-- Setting the volume of each sound
setVolume (Sound1, 100.0);
setVolume (Sound2, 75.0);
Put_Line ("Playing Sound1 individually... ");
play (Sound1);
delay 5.0;
Put_Line ("Playing Sound1 and Sound2 simultaneously... ");
play (Sound2);
delay 10.0;
Put_Line ("Stopping Sound1 before the end...");
stop (Sound1);
Put_Line ("Playing Sound2 alone for 5 seconds...");
delay 5.0;
destroy (Sound1);
destroy (Sound2);
end Play;
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
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:
https://github.com/DanielStuardo/Gadget
/*
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;
}
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.
FreeBASIC
#include "windows.bi"
#include "win/mmsystem.bi"
Const SND_LOOP = &H8
Const SND_ASYNC = &H1
Const SND_FILENAME = &H20000
' Reproducir un archivo MIDI
Dim As String midiFile = "canyon.mid"
mciSendString("play " & midiFile, 0, 0, 0)
Print "Playing a MIDI file..."
Print !"\nPress a key to stop playback"
Do: Loop Until Inkey <> ""
mciSendString("stop " & midiFile, 0, 0, 0)
Print !"Stopped MIDI.\n"
Sleep 4000
Dim As String waveFile = "C:\windows\media\tada.wav"
Dim As Integer volume = 10000
waveOutSetVolume(0, volume Or (volume Shl 16))
PlaySound(waveFile, 0, SND_FILENAME Or SND_ASYNC)
Print "Playing the Windows TADA sound quietly..."
Sleep 4000
volume = 65535
waveOutSetVolume(0, volume)
PlaySound(waveFile, 0, SND_FILENAME Or SND_ASYNC)
Print "Playing the Windows TADA sound loudly on the left channel..."
Sleep 4000
waveOutSetVolume(0, volume Shl 16)
PlaySound(waveFile, 0, SND_FILENAME Or SND_ASYNC)
Print "Playing the Windows TADA sound loudly on the right channel..."
Sleep 4000
waveOutSetVolume(0, volume Or (volume Shl 16))
PlaySound(waveFile, 0, SND_FILENAME Or SND_ASYNC Or SND_LOOP)
Print "Looping the Windows TADA sound on both channels..."
Sleep 4000
PlaySound(0, 0, 0)
Print "Stopped looping..."
Sleep
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.
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
- Output:
Go
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
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";
}
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
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
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
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:
Ruby
There aren't many mature sound libraries for Ruby. The
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
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
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
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
- Programming Tasks
- Temporal media
- Games
- 68000 Assembly
- Ada
- ASFML
- AutoHotkey
- Batch File
- BBC BASIC
- C
- Gadget
- C sharp
- Delphi
- FreeBASIC
- FutureBasic
- Go
- GUISS
- Liberty BASIC
- Java
- Julia
- Mathematica
- Wolfram Language
- Nim
- Phix
- PicoLisp
- PureBasic
- Python
- Pygame
- R
- Sound
- Racket
- Ring
- Ruby
- RubyGems
- Swift
- Tcl
- Snack
- TI-83 BASIC/Omit
- TI-89 BASIC/Omit
- TUSCRIPT
- UNIX Shell
- VBA
- Wren
- DOME
- Z80 Assembly
- Applesoft BASIC/Omit
- Batch File/Omit
- Blast/Omit
- Integer BASIC/Omit
- Lotus 123 Macro Scripting/Omit
- Maxima/Omit
- M4/Omit
- Openscad/Omit
- PARI/GP/Omit
- PHP/Omit
- Unlambda/Omit