tape_recorder_class.f90 Source File


This file depends on

sourcefile~~tape_recorder_class.f90~~EfferentGraph sourcefile~tape_recorder_class.f90 tape_recorder_class.f90 sourcefile~forsynth.f90 forsynth.f90 sourcefile~tape_recorder_class.f90->sourcefile~forsynth.f90

Files dependent on this one

sourcefile~~tape_recorder_class.f90~~AfferentGraph sourcefile~tape_recorder_class.f90 tape_recorder_class.f90 sourcefile~audio_effects.f90 audio_effects.f90 sourcefile~audio_effects.f90->sourcefile~tape_recorder_class.f90 sourcefile~envelopes.f90 envelopes.f90 sourcefile~envelopes.f90->sourcefile~tape_recorder_class.f90 sourcefile~misc_sounds.f90 misc_sounds.f90 sourcefile~misc_sounds.f90->sourcefile~tape_recorder_class.f90 sourcefile~music.f90 music.f90 sourcefile~misc_sounds.f90->sourcefile~music.f90 sourcefile~wav_file_class.f90 wav_file_class.f90 sourcefile~misc_sounds.f90->sourcefile~wav_file_class.f90 sourcefile~music.f90->sourcefile~tape_recorder_class.f90 sourcefile~music.f90->sourcefile~envelopes.f90 sourcefile~signals.f90 signals.f90 sourcefile~music.f90->sourcefile~signals.f90 sourcefile~signals.f90->sourcefile~tape_recorder_class.f90 sourcefile~signals.f90->sourcefile~envelopes.f90 sourcefile~wav_file_class.f90->sourcefile~tape_recorder_class.f90 sourcefile~all_signals.f90 all_signals.f90 sourcefile~all_signals.f90->sourcefile~envelopes.f90 sourcefile~all_signals.f90->sourcefile~music.f90 sourcefile~all_signals.f90->sourcefile~signals.f90 sourcefile~all_signals.f90->sourcefile~wav_file_class.f90 sourcefile~arpeggios.f90 arpeggios.f90 sourcefile~arpeggios.f90->sourcefile~envelopes.f90 sourcefile~arpeggios.f90->sourcefile~music.f90 sourcefile~arpeggios.f90->sourcefile~signals.f90 sourcefile~arpeggios.f90->sourcefile~wav_file_class.f90 sourcefile~blues.f90 blues.f90 sourcefile~blues.f90->sourcefile~audio_effects.f90 sourcefile~blues.f90->sourcefile~music.f90 sourcefile~blues.f90->sourcefile~signals.f90 sourcefile~blues.f90->sourcefile~wav_file_class.f90 sourcefile~chords_and_melody.f90 chords_and_melody.f90 sourcefile~chords_and_melody.f90->sourcefile~audio_effects.f90 sourcefile~chords_and_melody.f90->sourcefile~envelopes.f90 sourcefile~chords_and_melody.f90->sourcefile~music.f90 sourcefile~chords_and_melody.f90->sourcefile~signals.f90 sourcefile~chords_and_melody.f90->sourcefile~wav_file_class.f90 sourcefile~demo_effects.f90 demo_effects.f90 sourcefile~demo_effects.f90->sourcefile~audio_effects.f90 sourcefile~demo_effects.f90->sourcefile~envelopes.f90 sourcefile~demo_effects.f90->sourcefile~music.f90 sourcefile~demo_effects.f90->sourcefile~wav_file_class.f90 sourcefile~doppler_effect.f90 doppler_effect.f90 sourcefile~doppler_effect.f90->sourcefile~wav_file_class.f90 sourcefile~drone_music.f90 drone_music.f90 sourcefile~drone_music.f90->sourcefile~envelopes.f90 sourcefile~drone_music.f90->sourcefile~music.f90 sourcefile~drone_music.f90->sourcefile~wav_file_class.f90 sourcefile~drum_machine.f90 drum_machine.f90 sourcefile~drum_machine.f90->sourcefile~signals.f90 sourcefile~drum_machine.f90->sourcefile~wav_file_class.f90 sourcefile~shepard_risset_glissando.f90 shepard_risset_glissando.f90 sourcefile~shepard_risset_glissando.f90->sourcefile~envelopes.f90 sourcefile~shepard_risset_glissando.f90->sourcefile~wav_file_class.f90 sourcefile~shepard_scale.f90 shepard_scale.f90 sourcefile~shepard_scale.f90->sourcefile~audio_effects.f90 sourcefile~shepard_scale.f90->sourcefile~wav_file_class.f90

Source Code

! Forsynth: a multitracks stereo sound synthesis project
! License GPL-3.0-or-later
! Vincent Magnin
! Last modifications: 2024-05-31

!> This is the basic class, representing a numeric tape recorder with audio tracks.
module tape_recorder_class
    use forsynth, only: wp, RATE

    implicit none

    type tape_recorder
        !> Number of audio tracks (excluding track 0 reserved for the final mix):
        integer  :: tracks
        !> Duration in seconds:
        real(wp) :: duration
        !> Number of samples:
        integer  :: samples
        !> Two arrays stocking the stereo tracks:
        real(wp), dimension(:, :), allocatable :: left, right
    contains
        procedure :: new
        procedure :: clear_tracks
        procedure :: mix_tracks
        procedure :: copy_section
        procedure :: finalize
        final     :: auto_finalize
    end type tape_recorder

    public :: tape_recorder

contains

    subroutine new(self, tracks, duration)
        class(tape_recorder), intent(inout)  :: self
        ! Track 0 excluded:
        integer, intent(in)  :: tracks
        real(wp), intent(in) :: duration

        self%duration = duration
        self%tracks = tracks

        self%samples = nint(duration * RATE)

        allocate(self%left (0:tracks, 0:self%samples))
        allocate(self%right(0:tracks, 0:self%samples))

        call self%clear_tracks()
    end subroutine

    !> Erase all tracks on all the channels of the tape.
    subroutine clear_tracks(self)
        class(tape_recorder), intent(inout)  :: self

        self%left  = 0.0_wp
        self%right = 0.0_wp
    end subroutine


    !> Tracks 1 to tracks-1 are mixed on track 0.
    subroutine mix_tracks(self, levels, pan)
        class(tape_recorder), intent(inout)  :: self
        real(wp), dimension(1:self%tracks), intent(in), optional :: levels
        real(wp), dimension(1:self%tracks), intent(in), optional :: pan
        real(wp), dimension(1:self%tracks) :: pano
        real(wp) :: panL, panR
        integer  :: track

        ! As the track 0 can be used as an auxiliary track by some routines,
        ! it is important to clear it before the final mixing:
        self%left( 0, :) = 0.0_wp
        self%right(0, :) = 0.0_wp

        ! Pan is centered on 0 and -1 < pan < +1
        ! Default is 0:
        if (.not.present(pan)) then
            pano = 0._wp
        else
            ! Each element of pan(:) must be in [-1 ; +1]
            pano = max(-1._wp, min(+1._wp, pan))
        end if

        do track = 1, self%tracks
            ! Panoramic factors:
            if (pano(track) > 0._wp) then
                panL = 1._wp - pano(track)
                panR = 1._wp
            else if (pano(track) < 0._wp) then
                panL = 1._wp
                panR = 1._wp + pano(track)
            else
                panL = 1._wp
                panR = 1._wp
            end if

            if (.not.present(levels)) then
                self%left( 0, :) = self%left( 0, :) + panL * self%left( track, :)
                self%right(0, :) = self%right(0, :) + panR * self%right(track, :)
            else
                self%left( 0, :) = self%left( 0, :) + panL * levels(track) * self%left( track, :)
                self%right(0, :) = self%right(0, :) + panR * levels(track) * self%right(track, :)
            end if
        end do
    end subroutine


    !> Copy section t1...t2 at t3, either on the same track or another one.
    !> The content already present at t3 is overwritten.
    !> The code suppose that t1 < t2 < t3.
    subroutine copy_section(self, from_track, to_track, t1, t2, t3)
        class(tape_recorder), intent(inout)  :: self
        integer, intent(in)  :: from_track, to_track
        real(wp), intent(in) :: t1, t2, t3
        integer :: i, i1, i3

        i1 = nint(t1*RATE)
        do i = i1, nint(t2*RATE)-1
            ! The position of the sample receiving the copy:
            i3 = nint(t3*RATE) + (i-i1)
            ! To avoid pasting beyond the end of the track:
            if (i3 <= self%samples) then
                self%left( to_track, i3) = self%left( from_track, i)
                self%right(to_track, i3) = self%right(from_track, i)
            else
                exit
            end if
        end do
    end subroutine

    !> Called by the close_WAV_file() method.
    subroutine finalize(self)
        class(tape_recorder), intent(inout)  :: self

        deallocate(self%left)
        deallocate(self%right)
    end subroutine

    !> An automatic finalizer, by security.
    subroutine auto_finalize(self)
        type(tape_recorder), intent(inout)  :: self

        deallocate(self%left)
        deallocate(self%right)
    end subroutine

end module tape_recorder_class