shepard_risset_glissando Program

Uses

  • program~~shepard_risset_glissando~~UsesGraph program~shepard_risset_glissando shepard_risset_glissando module~envelopes envelopes program~shepard_risset_glissando->module~envelopes module~forsynth forsynth program~shepard_risset_glissando->module~forsynth module~wav_file_class wav_file_class program~shepard_risset_glissando->module~wav_file_class module~envelopes->module~forsynth module~tape_recorder_class tape_recorder_class module~envelopes->module~tape_recorder_class iso_fortran_env iso_fortran_env module~forsynth->iso_fortran_env module~wav_file_class->module~forsynth module~wav_file_class->iso_fortran_env module~wav_file_class->module~tape_recorder_class module~tape_recorder_class->module~forsynth

A Shepard-Risset glissando, giving the illusion of an ever increasing or decreasing pitch. It is the continuous version of the Shepard scale. Obtaining a good glissando is not easy, as you need to understand that each sin(omega * t) is in fact sin(omega(t) * t). Using a common time for all causes problems as t increases. In this version, each new component has its own time: sin(omega(tj) * tj). https://en.wikipedia.org/wiki/Shepard_tone


Calls

program~~shepard_risset_glissando~~CallsGraph program~shepard_risset_glissando shepard_risset_glissando proc~amplitude amplitude program~shepard_risset_glissando->proc~amplitude proc~apply_fade_in apply_fade_in program~shepard_risset_glissando->proc~apply_fade_in proc~apply_fade_out apply_fade_out program~shepard_risset_glissando->proc~apply_fade_out proc~close_wav_file WAV_file%close_WAV_file program~shepard_risset_glissando->proc~close_wav_file proc~create_wav_file WAV_file%create_WAV_file program~shepard_risset_glissando->proc~create_wav_file proc~get_name WAV_file%get_name program~shepard_risset_glissando->proc~get_name proc~mix_tracks tape_recorder%mix_tracks program~shepard_risset_glissando->proc~mix_tracks proc~write_amplitude_envelope write_amplitude_envelope program~shepard_risset_glissando->proc~write_amplitude_envelope proc~linear1 linear1 proc~amplitude->proc~linear1 proc~linear2 linear2 proc~amplitude->proc~linear2 proc~finalize tape_recorder%finalize proc~close_wav_file->proc~finalize proc~write_normalized_data WAV_file%write_normalized_data proc~close_wav_file->proc~write_normalized_data proc~new tape_recorder%new proc~create_wav_file->proc~new proc~write_header WAV_file%write_header proc~create_wav_file->proc~write_header proc~write_amplitude_envelope->proc~amplitude proc~clear_tracks tape_recorder%clear_tracks proc~new->proc~clear_tracks

Variables

Type Attributes Name Initial
real(kind=wp) :: Amp
integer, parameter :: cmax = 10
real(kind=wp), parameter :: d = 5._wp
type(WAV_file) :: demo
real(kind=wp) :: f(cmax)
real(kind=wp), parameter :: fmax = fmin*2**cmax
real(kind=wp), parameter :: fmin = 20._wp
integer :: i
real(kind=wp), parameter :: increase = 2**(+1/(d*RATE))
integer :: j
real(kind=wp), parameter :: length = 120._wp
real(kind=wp), parameter :: muf = ((log10(fmin)+log10(fmax))/2)-0.6
real(kind=wp) :: omega
real(kind=wp), parameter :: sigma = 0.27_wp
real(kind=wp) :: t(cmax)
logical, parameter :: upward = .true.

Functions

pure function amplitude(freq)

Envelope of the glissando. A gaussian, plus linear sections at the extremities, to reach the 0 level.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: freq

Return Value real(kind=wp)

pure function linear1(freq, f1, f2)

Returns an amplitude rising from 0 to 1, from f1 to f2. And 0 outside.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: freq
real(kind=wp), intent(in) :: f1
real(kind=wp), intent(in) :: f2

Return Value real(kind=wp)

pure function linear2(freq, f1, f2)

Returns an amplitude falling from 1 to 0, from f1 to f2. And 0 outside.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: freq
real(kind=wp), intent(in) :: f1
real(kind=wp), intent(in) :: f2

Return Value real(kind=wp)


Subroutines

Useful for debugging and setting the envelope parameters:

Arguments

None

Source Code

program shepard_risset_glissando
    use forsynth, only: wp, dt, RATE, PI
    use wav_file_class, only: WAV_file
    use envelopes, only: apply_fade_in, apply_fade_out

    implicit none
    type(WAV_file) :: demo
    !--------------------------
    ! Glissando parameters:
    !--------------------------
    logical, parameter :: upward = .true.
    ! Bandwidth 20-20480 Hz: 10 octaves
    integer, parameter  :: cmax = 10
    real(wp), parameter :: fmin = 20._wp
    real(wp), parameter :: fmax = fmin * 2**cmax
    ! Proper times of each component:
    real(wp) :: t(cmax)
    ! Frequencies of each component:
    real(wp) :: f(cmax)
    ! Gaussian window, central frequency in log scale, with a shift:
    real(wp), parameter :: muf  = ((log10(fmin) + log10(fmax)) / 2) - 0.6
    ! Standard deviation of the gaussian window:
    real(wp), parameter :: sigma = 0.27_wp
    ! Total duration of the WAV:
    real(wp), parameter :: length = 120._wp
    ! Time in seconds between two new components:
    real(wp), parameter :: d = 5._wp
    ! Setting the frequency increase/decrease rate at each step:
    real(wp), parameter :: increase = 2**(+1/(d*RATE))
    !--------------------------
    ! Pulsation (radians/second):
    real(wp) :: omega
    ! Amplitude of a sinusoid:
    real(wp) :: Amp
    ! Loop counters:
    integer  :: i, j

    ! Useful for debugging and setting the envelope parameters:
    call write_amplitude_envelope()

    ! Initializing the components, separated by octaves:
    if (upward) then
        f = [(fmin * 2**(j-1), j = 1, cmax)]
    else
        f = [(fmax / 2**(j-1), j = 1, cmax)]
    end if
    print *, "Frequencies:", f
    print *, "Log Central frequency:", muf

    print *, "Pitch multiplication factor by time step:", increase

    ! We create a new WAV file, and define the number of tracks and its duration:
    if (upward) then
        call demo%create_WAV_file('shepard_risset_glissando_upward.wav', tracks=1, duration=length)
    else
        call demo%create_WAV_file('shepard_risset_glissando_downward.wav', tracks=1, duration=length)
    end if
    print *, "**** Creating " // demo%get_name() // " ****"

    associate(tape => demo%tape_recorder)

    ! Initializing the proper time of each component:
    do j = 1, cmax
        t(j) = (j-1) * d
    end do

    do i = 0, nint(length*RATE)-1
        ! Computing and adding each component on the track:
        do j = 1, cmax
            ! Amplitude of the signal (gaussian distribution):
            Amp = amplitude(f(j))

            ! Note that omega is not a constant: omega(t)
            ! That is the reason for using a different time for each component.
            omega = 2*PI*f(j)
            tape%left(1, i)  = tape%left(1, i) + Amp * sin(omega*t(j))
        end do

        ! Incrementing all proper times:
        t = t + dt
        ! Pitch variation of each component for next step:
        if (upward) then
            f = f * increase
        else
            f = f / increase
        end if

        ! Each component must stay between fmin and fmax:
        do j = 1, cmax
            if (f(j) > fmax) then           ! For upward glissando
                ! This component must restart from the bottom of the spectrum:
                f(j) = fmin
                ! Its proper time must be also restarted:
                t(j) = 0
            else if (f(j) < fmin) then      ! For downward glissando
                ! This component must restart from the top of the spectrum:
                f(j) = fmax
                ! Its proper time must be also restarted:
                t(j) = 0
            end if
        end do
    end do

    tape%right = tape%left

    call apply_fade_in( tape, track=1, t1=0._wp,    t2=1._wp)
    call apply_fade_out(tape, track=1, t1=length-1, t2=length)

    end associate

    ! All tracks will be mixed on track 0.
    ! Needed even if there is only one track!
    call demo%mix_tracks()
    call demo%close_WAV_file()

    print *,"You can now play the file ", demo%get_name()

contains

    !> Returns an amplitude rising from 0 to 1, from f1 to f2. And 0 outside.
    pure real(wp) function linear1(freq, f1, f2)
        real(wp), intent(in) :: freq, f1, f2

        if ((f1 <= freq).and.(freq <= f2)) then
            linear1 = (freq - f1) / (f2 - f1)
        else
            linear1 = 0
        end if
    end function

    !> Returns an amplitude falling from 1 to 0, from f1 to f2. And 0 outside.
    pure real(wp) function linear2(freq, f1, f2)
        real(wp), intent(in) :: freq, f1, f2

        if ((f1 <= freq).and.(freq <= f2)) then
            linear2 = (freq - f2) / (f1 - f2)
        else
            linear2 = 0
        end if
    end function

    !> Envelope of the glissando. A gaussian, plus linear sections at
    !> the extremities, to reach the 0 level.
    pure real(wp) function amplitude(freq)
        real(wp), intent(in) :: freq
        real(wp) :: Amp

        if (freq <= 2*fmin) then
            Amp = linear1(freq, fmin, 2*fmin)
        else if (freq >= fmax/2) then
            Amp = linear2(freq, fmax/2, fmax)
        else
            Amp = 1
        end if

        amplitude = Amp * (1/(sqrt(2*PI)*sigma)) * exp(-(log10(freq) - muf)**2 / (2 * sigma**2))
    end function

    !> Useful for debugging and setting the envelope parameters:
    subroutine write_amplitude_envelope()
        real(wp) ::freq
        integer  :: u
        integer, parameter  :: points = 500
        real(wp), parameter :: increase = 2**(+10._wp/points)

        print *, muf, " muf = ", 10**muf

        open(newunit=u, file="glissando_envelope.txt", status='replace', action='write')
        freq = 20._wp
        do i = 1, points
            freq = freq * increase
            write(u, *) freq, amplitude(freq)
        end do
        close(u)
    end subroutine

end program shepard_risset_glissando