Sine wave

From Rosetta Code
Revision as of 20:35, 10 June 2022 by Jjuanhdez (talk | contribs) (Sine wave in FreeBASIC)
Sine wave is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.


Task

Generate a sine wave:

  1. you choose the frequency of the wave
  2. generate a sine wave for 5 seconds
  3. play sound

AmigaBASIC

<lang basic>SOUND 440,77</lang> The maximum allowed sound duration parameter in AmigaBASIC is 77 units. Since a second equals 18.2 sound units, this tone will only play for 4.23 seconds.

BASIC256

<lang BASIC256>sound 440, 5000</lang>

C

Translation of: Python

<lang c>#include <stdio.h>

  1. include <math.h>
  2. include <stdlib.h>

// AU header (CD-quality audio) int header[] = {46, 115, 110, 100, 0, 0, 0, 24,

               255, 255, 255, 255, 0, 0, 0, 3,
               0, 0, 172, 68, 0, 0, 0, 1};

int main(int argc, char *argv[]){

       float freq, dur;
       long i, v;
       if (argc < 3) {
               printf("Usage:\n");
               printf("  csine <frequency> <duration>\n");
               exit(1);
       }
       freq = atof(argv[1]);
       dur = atof(argv[2]);
       for (i = 0; i < 24; i++)
               putchar(header[i]);
       for (i = 0; i < dur * 44100; i++) {
               v = (long) round(32000. * sin(2. * M_PI * freq * i / 44100.));
               v = v % 65536;
               putchar(v >> 8);
               putchar(v % 256);
       }

}</lang> Test: <lang bash>gcc -o csine csine.c -lm ./csine 440 5 | play - # Now either pipe output into SoX to play ./csine 440 5 > test.au # or redirect output to a file.</lang>

Delphi

Copy of Andreas Rejbrand example found here [1]. <lang Delphi> program Sine_wave;

{$APPTYPE CONSOLE}

uses

 System.SysUtils,
 Winapi.Windows,
 Winapi.MMSystem;

type

 TWaveformSample = integer; // signed 32-bit; -2147483648..2147483647
 TWaveformSamples = packed array of TWaveformSample; // one channel

var

 Samples: TWaveformSamples;
 fmt: TWaveFormatEx;

procedure InitAudioSys; begin

 with fmt do
 begin
   wFormatTag := WAVE_FORMAT_PCM;
   nChannels := 1;
   nSamplesPerSec := 44100;
   wBitsPerSample := 32;
   nAvgBytesPerSec := nChannels * nSamplesPerSec * wBitsPerSample div 8;
   nBlockAlign := nChannels * wBitsPerSample div 8;
   cbSize := 0;
 end;

end;

                                         // Hz                     // msec

procedure CreatePureSineTone(const AFreq: integer; const ADuration: integer;

 const AVolume: double { in [0, 1] });

var

 i: Integer;
 omega, dt, t: double;
 vol: double;

begin

 omega := 2 * Pi * AFreq;
 dt := 1 / fmt.nSamplesPerSec;
 t := 0;
 vol := MaxInt * AVolume;
 SetLength(Samples, Round((ADuration / 1000) * fmt.nSamplesPerSec));
 for i := 0 to high(Samples) do
 begin
   Samples[i] := round(vol * sin(omega * t));
   t := t + dt;
 end;

end;

procedure PlaySound; var

 wo: integer;
 hdr: TWaveHdr;

begin

 if Length(samples) = 0 then
 begin
   Writeln('Error: No audio has been created yet.');
   Exit;
 end;
 if waveOutOpen(@wo, WAVE_MAPPER, @fmt, 0, 0, CALLBACK_NULL) = MMSYSERR_NOERROR then
 try
   ZeroMemory(@hdr, sizeof(hdr));
   with hdr do
   begin
     lpData := @samples[0];
     dwBufferLength := fmt.nChannels * Length(Samples) * sizeof(TWaveformSample);
     dwFlags := 0;
   end;
   waveOutPrepareHeader(wo, @hdr, sizeof(hdr));
   waveOutWrite(wo, @hdr, sizeof(hdr));
   sleep(500);
   while waveOutUnprepareHeader(wo, @hdr, sizeof(hdr)) = WAVERR_STILLPLAYING do
     sleep(100);
 finally
   waveOutClose(wo);
 end;

end;

begin

 try
   InitAudioSys;
   CreatePureSineTone(440, 5000, 0.7);
   PlaySound;
 except
   on E: Exception do
   begin
     Writeln(E.Classname, ': ', E.Message);
     Readln;
   end;
 end;

end.</lang>

Emacs Lisp

Note that this code does not work on Windows because playing sound from Emacs Lisp data variables is not supported there.

8-bit samples

<lang lisp>(defun play-sine (freq dur)

 "Play a sine wave for dur seconds."
 (setq header (unibyte-string     ; AU header:
                 46 115 110 100   ;   ".snd" magic number
                 0 0 0 24         ;   start of data bytes
                 255 255 255 255  ;   file size is unknown
                 0 0 0 3          ;   16 bit PCM samples
                 0 0 172 68       ;   44,100 samples/s
                 0 0 0 1))        ;   mono
 (setq s (apply #'concat header (mapcar (lambda (x) (unibyte-string
              (mod (round (* 127 (sin (* 2 pi freq x (/ 44100.0))))) 256) 0))
              (number-sequence 0 (* dur 44100)))))
 (play-sound `(sound :data ,s)))

(play-sine 440 5)</lang> While the generated AU sound file is 16 bit, the samples themselves are 8 bit because only their high byte is set by the sine function. Therefore you will hear some faint hiss in the background due to the higher noise floor of 8-bit audio.

16-bit samples

This (slightly slower) version of the function creates proper 16-bit samples by setting both high and low bytes, resulting in less playback noise. <lang lisp>(defun play-sine16 (freq dur)

 "Play a sine wave for dur seconds."
 (setq header (unibyte-string     ; AU header:
                 46 115 110 100   ;   ".snd" magic number
                 0 0 0 24         ;   start of data bytes
                 255 255 255 255  ;   file size is unknown
                 0 0 0 3          ;   16 bit PCM samples
                 0 0 172 68       ;   44,100 samples/s
                 0 0 0 1))        ;   mono
 (setq v (mapcar (lambda (x)
              (mod (round (* 32000 (sin (* 2 pi freq x (/ 44100.0))))) 65536))
              (number-sequence 0 (* dur 44100))))
 (setq s (apply #'concat header (flatten-list (mapcar (lambda (x)
           (list (unibyte-string (ash x -8))
                 (unibyte-string (mod x 256))))
           v))))
 (play-sound `(sound :data ,s)))

(play-sine16 440 5)</lang>

FreeBASIC

This subroutine is able to generate complex sounds using only Windows API.

Original code programmed by Angelo Rosina [2] <lang freebasic>Enum FLAGS

   _ASYNC       = &h000001
   _NODEFAULT   = &h000002
   _MEMORY      = &h000004
   _LOOP        = &h000008
   _NOSTOP      = &h000010
   _PURGE       = &h000040
   _APPLICATION = &h000080
   _NOWAIT      = &h002000
   _ALIAS       = &h010000
   _FILENAME    = &h020000
   _RESOURCE    = &h040000 Or _MEMORY
   _ALIAS_ID    = &h100000 Or _ALIAS

End Enum

  1. define PLAYFLAG _NOWAIT Or _NOSTOP Or _MEMORY Or _ASYNC
  2. define SNDPLAYFLAG _NOSTOP Or _MEMORY Or _ASYNC
  3. define FCC(c) *(cptr(Uinteger Ptr,@##c))

Declare Function PlayMemory Alias "PlaySoundA" (Byval As Any Ptr, Byval hModule As Any Ptr = 0, Byval flag As Integer = PLAYFLAG) As Integer Declare Function sndPlayMemory Alias "sndPlaySoundA" (Byval As Any Ptr, Byval flag As Uinteger = SNDPLAYFLAG) As Integer

  1. inclib "winmm"

Sub PrepareBuffer(Byval lpBuffer As Any Ptr, _

   Byval nSamples  As Uinteger, _
   Byval nRate     As Uinteger = 44100, _
   Byval nBits     As Uinteger = 16, _
   Byval nChannels As Uinteger = 2)
   
   Dim As Uinteger Ptr h = lpBuffer 
   Dim As Uinteger BlkAlign = (nBits\8) * nChannels
   Dim As Uinteger DataSize = BlkAlign * nSamples
   h[ 0]=FCC("RIFF")                   ' RIFF chunk
   h[ 1]=36 + DataSize                 ' size of WAVE chunk + data size
   h[ 2]=FCC("WAVE")                   ' WAVE chunk
   h[ 3]=FCC("fmt ")                   ' fmt chunk
   h[ 4]=16                            ' size of fmt chunk
   h[ 5]=(nChannels Shl 16) Or 1       ' channels + PCM_FORMAT flag
   h[ 6]=nRate                         ' playback rate
   h[ 7]=BlkAlign*nRate                ' bytes per sec.
   h[ 8]=(nBits Shl 16) Or BlkAlign    ' bits per sample + blockalign
   h[ 9]=FCC("data")                   ' data chunk
   h[10]=DataSize                      ' size of data chunk

End Sub

Type PCM_SAMPLE Field = 2

   As Short l, r                       ' left and right channel

End Type

Sub Sound (Frequency As Double, Duration As Single, MaxVol As Integer = 128,_

   ca As Single = 0, cd As Single = 0, csl As Single = 1, cr As Single = 0,_
   MFrequency As Double = 0, ModStart As Double = 0, MaxModulator As Double = 0,_
   ma As Single = 0, md As Single = 0, msl As Single = 1, mr As Single = 0)
   
   Const nBuffers = 2
   Var nSamples = 44100*Int(Duration/18.2) ' seconds
   Var CS = 1 - CA - CD - CR
   Var MS = 1 - MA - MD - MR
   
   Dim As Double CEnvelopeInc, CEnvelopeDecD, CEnvelopeDecR
   CEnvelopeInc  = 100 * MaxVol / (nSamples * CA + 1)
   CEnvelopeDecD = 100 * MaxVol * (1 - CSL) / (nSamples * CD + 1)
   CEnvelopeDecR = 100 * MaxVol * CSL / (nSamples * CR + 1)
   
   CD = CD + CA : CS = CS + CD : CR = CR + CS
   
   Dim As Double MEnvelopeInc, MEnvelopeDecD, MEnvelopeDecR
   MEnvelopeInc  = MaxModulator / (nSamples * MA + 1)
   MEnvelopeDecD = MaxModulator * (1 - MSL) / (nSamples * MD + 1)
   MEnvelopeDecR = MaxModulator * MSL / (nSamples * MR + 1)
   
   MD = MD + MA : MS = MS + MD : MR = MR + MS
   
   Dim As PCM_SAMPLE PlayBuffers(nBuffers-1, -22 To nSamples-1)
   For i As Integer = 0 To nBuffers-1
       PrepareBuffer(@PlayBuffers(i,-22), nSamples)
   Next
   
   Dim Waveform As Single
   Dim Modulator As Single
   Dim As Integer buffer,ret
   Dim Volume As Double = 0
   Dim MAmp As Double = 0
   
   ' fill the first buffer with sine wave
   For i As Integer = 0 To nSamples-1
       
       If i <= CA * nSamples Then
           Volume = Volume + CEnvelopeInc
       Elseif i < CD * nSamples Then
           Volume = Volume - CEnvelopeDecD
       Elseif i < CS * nSamples Then
       Elseif i < CR * nSamples Then
           Volume = Volume - CEnvelopeDecR
       End If
       
       If i <= MA * nSamples Then
           Mamp = Mamp + MEnvelopeInc
       Elseif i < MD * nSamples Then
           Mamp = Mamp - MEnvelopeDecD
       Elseif i < MS * nSamples Then
       Elseif i < MR * nSamples Then
           Mamp = Mamp - MEnvelopeDecR
       End If
       
       Modulator = Cos(6.28/44100 * i * MFrequency + ModStart) * MAmp
       Waveform  = Sin(6.28/44100 * i * Frequency + Modulator) * Volume
       PlayBuffers(buffer,i).l = Waveform
       PlayBuffers(buffer,i).r = Waveform
   Next
   
   ret = PlayMemory(@PlayBuffers(buffer, -22))

End Sub


Sound 440, 5000 'This is the base syntax, works exactly like in QB 'Sound Frequency, Duration , Volume, A, D, S, R, ModFreq, ModStart, ModAmplitude, MA, MD, MS, MR </lang>

Go

Works with: Ubuntu 16.04


Go lacks audio support in its standard library and, whilst there are third party packages that could be used, an easier approach is to invoke the SoX utility's 'play' command as was done in the second Kotlin example. <lang go>package main

import (

   "fmt"
   "os/exec"

)

func main() {

   synthType := "sine"
   duration := "5"
   frequency := "440"
   cmd := exec.Command("play", "-n", "synth", duration, synthType, frequency)
   err := cmd.Run()
   if err != nil {
       fmt.Println(err)
   }

}</lang>

J

Approximately 1751 Hz (slightly flat A, two octaves above middle C): <lang J>require'media/wav'

  4 wavplay wavmake <.1e3*1 o.i.55e3

</lang>

JavaScript

<lang javascript>let ctx = new (window.AudioContext || window.webkitAudioContext)(); let osc = ctx.createOscillator(); osc.frequency.setValueAtTime(440, ctx.currentTime); osc.connect(ctx.destination); osc.start(); osc.stop(ctx.currentTime + 5);</lang>

Julia

PortAudio library version. <lang julia>using PortAudio

function paudio()

   devs = PortAudio.devices()
   devnum = findfirst(x -> x.maxoutchans > 0, devs)
   (devnum == nothing) && error("No output device for audio found")
   return ostream = PortAudioStream(devs[devnum].name, 0, 2)

end

function play(ostream, pitch, durationseconds)

   sinewave(t) = 0.6sin(t) + 0.2sin(2t) + .05sin(8t)
   timesamples = 0:(1 / 44100):(durationseconds * 0.98)
   v = Float64[sinewave(2π * pitch * t) for t in timesamples]
   write(ostream, v)
   sleep(durationseconds * 0.9)

end

play(paudio(), 440.0, 5.0) </lang>

Kotlin

Using Java Sound API

<lang scala>// Version 1.2.41

import javax.sound.sampled.AudioFormat import javax.sound.sampled.AudioSystem import kotlin.math.sin import kotlin.math.PI

fun sineWave(frequency: Int, seconds: Int, sampleRate: Int): ByteArray {

   val samples = seconds * sampleRate
   val result = ByteArray(samples)
   val interval = sampleRate.toDouble() / frequency
   for (i in 0 until samples) {
       val angle = 2.0 * PI * i / interval
       result[i] = (sin(angle) * 127).toByte()
   }
   return result

}

fun main(args: Array<String>) {

   val sampleRate = 44000
   val buffer = sineWave(440, 5, sampleRate)
   val format = AudioFormat(sampleRate.toFloat(), 8, 1, true, true)
   val line = AudioSystem.getSourceDataLine(format)
   with (line) {
       open(format)
       start()
       write(buffer, 0, buffer.size)
       drain()
       close()
   }

}</lang>

Invoking SoX

An easier approach invoking the SoX utility's 'play' command which has this stuff built-in. The following was tested on Ubuntu 16.04. <lang scala>// Version 1.2.41

fun main(args:Array<String>) {

   val synthType = "sine"
   val duration = "5"
   val frequency = "440"
   val pb = ProcessBuilder("play", "-n", "synth", duration, synthType, frequency)
   pb.directory(null)
   val proc = pb.start()
   proc.waitFor()

}</lang>

Mathematica/Wolfram Language

<lang Mathematica>EmitSound[Play[Sin[440 2 Pi t], {t, 0, 5}]]</lang>

Nim

Using Sox external player. <lang Nim>import osproc, strutils

proc getIntValue(msg: string; minval, maxval: int): int =

 while true:
   stdout.write msg
   stdout.flushFile()
   try:
     result = stdin.readLine.strip().parseInt()
     if result notin minval..maxval:
       echo "Invalid value"
     else:
       return
   except ValueError:
     echo "Error: invalid value."
   except EOFError:
     echo()
     quit "Quitting.", QuitFailure

let freq = getIntValue("Enter frequency in Hz (40 to 10000): ", 40, 10_000) let duration = 5 let kind = "sine" let args = ["-n", "synth", $duration, $kind, $freq] echo execProcess("play", args = args, options = {poStdErrToStdOut, poUsePath})</lang>

OCaml

Library: SFML
Library: ocaml-sfml

<lang ocaml>module BA = Bigarray module BA1 = Bigarray.Array1

let () =

 let samples = 44100 in
 let sample_rate = 44100 in
 let amplitude = 30000. in
 
 let raw = BA1.create BA.int16_signed BA.c_layout samples in
 let two_pi = 6.28318 in
 let increment = 440.0 /. 44100.0 in
 let x = ref 0.0 in
 for i = 0 to samples - 1 do
   raw.{i} <- truncate (amplitude *. sin (!x *. two_pi));
   x := !x +. increment;
 done;
 
 let buffer = SFSoundBuffer.loadFromSamples raw 1 sample_rate in
 let snd = SFSound.create () in
 SFSound.setBuffer snd buffer;
 SFSound.setLoop snd true;
 SFSound.play snd;
 while true do
   SFTime.sleep (SFTime.of_milliseconds 100_l);
 done</lang>

To run this code in script mode you can add this at the beginning of the file:

<lang ocaml>#directory "+sfml"

  1. load "bigarray.cma"
  2. load "sfml_system.cma"
  3. load "sfml_audio.cma"</lang>

Then run:

ocaml sine_wave.ml

Or to compile to native code:

ocamlopt -I +sfml bigarray.cmxa sfml_system.cmxa sfml_audio.cmxa sine_wave.ml -o sine_wave.exe

Perl

<lang perl>use Audio::NoiseGen qw(play sine);

Audio::NoiseGen::init() || die 'No access to sound hardware?';

alarm 5; play( gen => sine( freq => 440 ) );</lang>

Phix

without js -- (dll/c_proc, system, prompt_number)
atom k32=NULL, xBeep
 
procedure beep(integer frequency, duration=5000)
    if platform()=WINDOWS then
        if k32=NULL then
            k32 = open_dll("kernel32.dll")
            xBeep = define_c_proc(k32, "Beep", {C_INT,C_INT})
        end if
        c_proc(xBeep,{frequency,duration})
    elsif platform()=LINUX then
	system(sprintf("play -n synth %f sine %d", {duration/1000, frequency}))
    end if
end procedure
 
beep(prompt_number("Enter Frequency (100..10000 recommended):",{0x25,0x7FFF}))

Python

Translation of: Emacs Lisp

<lang python>#!/usr/bin/env python

import os from math import pi, sin

  1. AU file header

au_header = bytearray(

           [46, 115, 110, 100,   #   ".snd" magic number
             0,   0,   0,  24,   #   start of data bytes
           255, 255, 255, 255,   #   file size is unknown
             0,   0,   0,   3,   #   16 bit PCM samples
             0,   0, 172,  68,   #   44,100 samples/s
             0,   0,   0,   1])  #   mono

def f(x, freq):

   "Compute sine wave as 16-bit integer"
   return round(32000 * sin(2 * pi * freq * x / 44100)) % 65536

def play_sine(freq=440, duration=5, oname="pysine.au"):

   "Play a sine wave for `duration` seconds"
   out = open(oname, 'wb')
   out.write(au_header)
   v = [f(x, freq) for x in range(duration * 44100 + 1)]
   s = []
   for i in v:
       s.append(i >> 8)
       s.append(i % 256)
   out.write(bytearray(s))
   out.close()
   os.system("vlc " + oname)   # starts an external media player to play file

play_sine()</lang> Creates an AU file with the sine wave and plays it with VLC.

Racket

See https://docs.racket-lang.org/rsound/index.html <lang Racket>

  1. lang racket

(require rsound)

440 Hz, 50% volume, 5 seconds

(play (make-tone 440 0.50 (* 5 FRAME-RATE))) </lang>

Raku

(formerly Perl 6)

Works with: Rakudo version 2018.04.01

What a horribly underspecified task. Ah well, gives me lots of wiggle room to cheat in various ways.

<lang perl6>my ($rows,$cols) = qx/stty size/.words; my $v = floor $rows / 2; print "\e[H\e[J", 'Generating sine wave of zero amplitude and zero frequency for 5 seconds...',

 "\e[$v;0H", '_' x $cols;

sleep 5; say "\e[H\e[J", 'No?, ok how about this:';

use SVG; my $filename = 'sine.svg'; my $out = open($filename, :w) orelse .die; $out.say: SVG.serialize(

   svg => [
       width => 400, height => 150, style => 'stroke:rgb(0,0,255)',
       :rect[:width<100%>, :height<100%>, :fill<white>],
       :path[ :fill<none>, :d('M0,25 C36.42,25,63.58,125,100,125 M100,125 C136.42,125,163.58,25,200,25 M200,25 C236.42,25,263.58,125,300,125 M300,125 C336.42,125,363.58,25,400,25') ],
   ],

); close $out; say "Sine wave generated to {$filename.IO.absolute}, better open it quickly..."; sleep 5; unlink $filename; say 'Oops, too late.'; say 'Still no? Ok how about:'; shell 'play -n -c1 synth 5.0 sin %-12';</lang>

REXX

Note:   This REXX program will only work for PC/REXX or Personal REXX. <lang REXX>/*REXX program produces a sine wave (of a specified frequency) for N seconds. */ parse arg freq time . /*obtain optional arguments from the CL*/ if freq== | freq=="," then freq= 880 /*Not specified? Then use the default.*/ if time== | time=="," then time= 5 /* " " " " " " */ call sound freq, time /*invoke a BIF to generate a sine wave.*/ exit 0 /*stick a fork in it, we're all done. */</lang>
<br<

Scala

<lang Scala>import javax.sound.sampled.{AudioFormat, AudioSystem, SourceDataLine}

import scala.math.{Pi, sin}

object SineWave extends App {

 val sampleRate = 44000
 val buffer = beep(440, 5, sampleRate)
 val line: SourceDataLine = AudioSystem.getSourceDataLine(format)
 def format = new AudioFormat(sampleRate.toFloat, 8, 1, true, false)
 def beep(frequency: Int, seconds: Int, sampleRate: Int) = {
   val samples = seconds * sampleRate
   val interval = sampleRate / frequency
   val angle = 2.0 * Pi  / interval
   (0 until samples).map(i => (sin(angle * i) * 127).toByte).toArray
 }
 line.open()
 line.start()
 line.write(buffer, 0, buffer.length)
 line.drain()
 line.close()

}</lang>

Wren

Translation of: Kotlin
Library: Wren-sound

As Wren-cli doesn't have any built-in audio support, we instead build a .wav file which can then be played using a utility such as rhythmbox or SoX, <lang ecmascript>import "/sound" for Wav

var sineWave = Fn.new { |frequency, seconds, sampleRate|

   var samples = seconds * sampleRate
   var result = List.filled(samples, 0)
   var interval = sampleRate / frequency
   for (i in 0...samples) {
       var angle = 2 * Num.pi * i / interval
       var b = (angle.sin * 127).truncate
       result[i] = (b >= 0) ? b : 256 + b
   }
   return result

}

var sampleRate = 44000 var buffer = sineWave.call(440, 5, sampleRate) Wav.create("sinewave.wav", buffer, sampleRate)</lang>

Yabasic

<lang Yabasic>// Rosetta Code problem: http://rosettacode.org/wiki/Sine_wave // by Galileo, 05/2022

sub MyBeep(frequency, duration)

   local n
   
   if duration = 0 duration = 5000
   
   if peek$("os") = "unix" then
       system("play -n synth " + str$(duration/1000) + " sine " + str$(frequency))
   else
       n = foreign_function_call("kernel32.dll", "uint8","Beep","uint32",frequency,"uint32",duration)
   end if

end sub

MyBeep(440)</lang>

zkl

Translation of: Go

Running on Mint Linux <lang zkl>fcn sineWave(durationInSec=5, frequency=440){

  synthType:="sine";
  cmd:="play -n synth %d %s %d".fmt(durationInSec,synthType,frequency);
  s:=System.cmd(cmd);

// if(s!=0) play issued error }

sineWave();</lang>