{"id":22025143,"url":"https://github.com/clementcaton/algosup_2022_project_3_a","last_synced_at":"2025-05-07T09:34:17.325Z","repository":{"id":44531454,"uuid":"444476101","full_name":"ClementCaton/ALGOSUP_2022_Project_3_A","owner":"ClementCaton","description":"A library that could be used by a programmer/artist to create, modify, listen to and save sounds usable for musical purposes as well as do the same to an entire list of said sounds.","archived":false,"fork":false,"pushed_at":"2022-02-10T15:03:40.000Z","size":6266,"stargazers_count":9,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-31T08:43:26.782Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"F#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ClementCaton.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-01-04T15:48:40.000Z","updated_at":"2023-09-07T19:00:15.000Z","dependencies_parsed_at":"2022-09-13T06:03:07.311Z","dependency_job_id":null,"html_url":"https://github.com/ClementCaton/ALGOSUP_2022_Project_3_A","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClementCaton%2FALGOSUP_2022_Project_3_A","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClementCaton%2FALGOSUP_2022_Project_3_A/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClementCaton%2FALGOSUP_2022_Project_3_A/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClementCaton%2FALGOSUP_2022_Project_3_A/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ClementCaton","download_url":"https://codeload.github.com/ClementCaton/ALGOSUP_2022_Project_3_A/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252851615,"owners_count":21814185,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-30T07:14:18.621Z","updated_at":"2025-05-07T09:34:17.302Z","avatar_url":"https://github.com/ClementCaton.png","language":"F#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ALGOSUP_2022_Project_3_A | Sound Synthesizer\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong id=\"table_of_contents\"\u003eTable of Contents\u003c/strong\u003e\u003c/summary\u003e\n\n- [ALGOSUP_2022_Project_3_A | Sound Synthesizer](#algosup_2022_project_3_a--sound-synthesizer)\n  - [Project](#project)\n  - [Project members](#project-members)\n- [Project documentation](#project-documentation)\n  - [Features in development](#features-in-development)\n    - [MP3 development](#mp3-development)\n  - [Getting Started](#getting-started)\n    - [Prerequisites](#prerequisites)\n    - [Download](#download)\n      - [.Net CLI](#net-cli)\n  - [**Basic structure**](#basic-structure)\n  - [Modifying and printing default values](#modifying-and-printing-default-values)\n  - [Reading files](#reading-files)\n    - [Reading wav files](#reading-wav-files)\n  - [Writing to files / Saving](#writing-to-files--saving)\n    - [Writing wav files](#writing-wav-files)\n  - [Playing music](#playing-music)\n  - [Dealing with stereo](#dealing-with-stereo)\n  - [Creating audio data](#creating-audio-data)\n    - [Creating audio data with an envelope](#creating-audio-data-with-an-envelope)\n    - [Creating audio data with a custom envelope](#creating-audio-data-with-a-custom-envelope)\n    - [Creating audio data with a chord](#creating-audio-data-with-a-chord)\n  - [Finding frequencies from notes and octaves](#finding-frequencies-from-notes-and-octaves)\n    - [Finding notes with a custom default frequency](#finding-notes-with-a-custom-default-frequency)\n  - [Creating silence](#creating-silence)\n  - [Cutting audio](#cutting-audio)\n  - [Superposing audio data](#superposing-audio-data)\n    - [Superposing audio with a predefined ratio](#superposing-audio-with-a-predefined-ratio)\n    - [Superposing audio with a custom ratio](#superposing-audio-with-a-custom-ratio)\n    - [Superposing audio without ratios](#superposing-audio-without-ratios)\n  - [Composing](#composing)\n  - [Preview](#preview)\n  - [Frequency analysis](#frequency-analysis)\n  - [Filters](#filters)\n    - [Currently accessible Filters](#currently-accessible-filters)\n    - [Apply multiple filters at once](#apply-multiple-filters-at-once)\n    - [Changing amplitude](#changing-amplitude)\n    - [Custom repeater filter](#custom-repeater-filter)\n    - [Echo](#echo)\n    - [Reverb](#reverb)\n    - [Flanger](#flanger)\n    - [Envelope](#envelope)\n    - [Custom envelope](#custom-envelope)\n    - [Low frequency oscillation](#low-frequency-oscillation)\n      - [AM](#am)\n      - [FM](#fm)\n    - [LowPass / HighPass / BandPass / RejectBand filters](#lowpass--highpass--bandpass--rejectband-filters)\n- [Footnotes](#footnotes)\n  - [Musical notes](#musical-notes)\n  - [Wave functions[^3]](#wave-functions3)\n  - [Duration of elements](#duration-of-elements)\n  - [Unit Test](#unit-test)\n  - [See Also](#see-also)\n  - [**Definitions**](#definitions)\n\n\u003c/details\u003e\n\n## Project\n\nThe project was proposed by [*Algosup*](https://www.algosup.com/index.html) and [*Robert Pickering*](https://github.com/robertpi). Its objective is to create a Sound Synthesizer capable of opening, modifying, createing and saveing sounds using in F#.\n\n## Project members\n\n[*Ivan Molnar*](https://github.com/ivan-molnar) \u003cbr\u003e\n[*Clement Caton*](https://github.com/ClementCaton) \u003cbr\u003e\n[*Louis de Choulot*](https://github.com/Louis-de-Lavenne-de-Choulot) \u003cbr\u003e\n[*Théo Diancourt*](https://github.com/TheoDct) \u003cbr\u003e\n[*Mathieu Chaput*](https://github.com/Chaput-Mathieu) \u003cbr\u003e\n[*Léo Chartier*](https://github.com/leo-chartier)\n\n# Project documentation\n\n## Features in development\n\n### MP3 development\n\nMP3 features are still in developpement. We are supposed to be able to compress and decompress MP3 files. It is already possible look at audio data in mp3 files and check the progress inside the [mp3-compression branch](https://github.com/ClementCaton/ALGOSUP_2022_Project_3_A/tree/compression-mp3)\n\n## Getting Started\n\n### Prerequisites\n\nDownload .NET 6.0 or newer\n\n### Download\n\nYou can simply download our latest build through the NuGet platform with this command : \n\n\n#### .Net CLI\n\n``dotnet add package Synthesizer``\n\n## **Basic structure**\n\nTo interact with the library you'll have to mainly interact with the ``Synth`` object.\nThis object functions as a sort of API towards the rest of the library.\n\nThe synthesizer can be initialysed as follows :\n```fs\nlet synth = Synth()\n```\n\n## Modifying and printing default values\n\nThe reasoning behind using a ``type`` instead of a ``module`` for our library was to enable default values.\nValues such as the samplerate[^11], the bpm[^4] or the default waveform[^3] are needed everywhere but tend to stay the same.\nThese values can be accesed and modified directly from the object.\n\nExample :\n```fs\nlet synth = Synth()   // Init Synth()\n\nprintfn \"%A\" synth    // Print default values\n\nlet oldSampleRate = synth.sampleRate // Save the current sampleRate into an external variable\n\nsynth.sampleRate \u003c- 50000.  // Change default sampleRate\n\nprint $\"{synth}\"        // Print default values with new default sampleRate\nprint $\"{oldSamplRate}\" // Print old sampleRate\n```\n\nThe above example prints :\n```\nsampleRate: 44100 \nbpm: 90 \ndefault wave type: Sin\n\n44100\n\nsampleRate: 50000 \nbpm: 90 \ndefault wave type: Sin\n```\n\n## Reading files\n\n### Reading wav files\n\nYou can extract data from a wav file in the default ``/Output/`` folder using ``synth.ReadFromWav (fileName:string)``.\n\nYou can open it from your own path using ``synth.readFromWavWithPath (filePath:string)``.\n\nThese functions return a tuple containing the ``soundData:list\u003clist\u003cfloat\u003e\u003e``, ``duration:float``, ``sampleRate:int`` and the ``bitsPerSample:int``.\n\nExample :\n\n```fs\nlet inOutputData, inOutputDuration, inOutputSampleRate, inOutputBPSampleRate = synth.ReadFromWav \"yourFileName.wav\" // get everything from a file in the Output folder\n\nlet fromPathData, _, fromPathSampleRate, _ = synth.readFromWavWithPath \"/yourPath/yourFileName.wav\" // get only the sound data and the sample rate from a predefined path\n```\n\n## Writing to files / Saving\n\n### Writing wav files\n\nYou can save files by writing data into them with the function ``synth.WriteToWav name music``. This function will put files in the folder \"./Output\".\n\nExample :\n\n```fs\nsynth.WriteToWavWithPath \"name.wav\" sound // This will save the sound in the file from the path \"./Output/name.wav\".\n```\n\nYou can also save files by writing data into them with the function ``synth.WriteToWavWithPath path fileName music``. This function will put files in \"path/fileName\".\n\nExample :\n\n```fs\nsynth.WriteToWavWithPath \"./folder/\" \"name.wav\" sound // This will save the sound in the file from the path \"./folder/name.wav\".\n```\n\n## Playing music\n\nYour Os is automatically detected to use either SFML on windows or afplay on Mac, this function does not support Linux yet.\n\nYou can play music from the code ``synth.PlayWav offset data``.\n\nExample :\n\n```fs\nsynth.PlayWav 0. data // This will play the sound in the variable data with an offset of 0 second.\n```\n\nYou can also play music from a file with ``synth.PlayWavFromPath offset (filePath:string)``\n\nExample :\n\n```fs\nsynth.PlayWavFromPath 0. \"./Output/name.wav\" // This will play the sound in the file from the path \"./Output/name.wav\" with an offset of 0 second.\n```\n\nEach sound will be played one by one. For the next sound to be played (or to end the program if there aren't any more sounds) you need to press the enter key.\n\n## Dealing with stereo\n\nWorking with stereo[^10] is rather simple.\u003cbr\u003e\nWhenever data is getting written the input needs to be a list of lists and every time we read data the output will also be a list of list.\n\nEach of the inner lists represent the audio data in a different channel.\n\nFor example if we want to write a .wav file with two channels that would look like this :\n\n```fs\nlet synth = Synth() // Init\nlet channel1 = synth.SoundWithEnveloppe 440. (Seconds 2.) Sin 0.5 0.5 0.5 0.5 0.5  // First audio\nlet channel2 = synth.SoundWithEnveloppe 440. (Seconds 2.) Sin 0.5 0.2 0.3 0.4 0.5  // Second audio\n\nsynth.WriteToWav \"stereo.wav\" [channel1; channel2]  // Writing file with two channels\n```\n\nThis will create the following audio file :\n![Stereo](Reports/Files/stereo.png)\n\n## Creating audio data\n\nYou can create some basic audio using ``synth.Sound (frequency:float) (duration:Duration) (waveType:BaseWaves)``\n\nExample :\n\n```fs\nlet newSound = synth.Sound 440. (Seconds 1.) Sin    // Create a 1 second sinusoidal wave with a frequency of 440Hz.\nlet newSound2 synth.Sound (synth.getNoteFreq 3 Note.F) Half Triangular // Create a triangular F3 half note.\n```\n\nAlternatively, it is possible to directly create a note with the ``synth.Note (duration:Duration) (mNote:Note) (octave:int)``.\n\nExample :\n\n```fs\nlet newNote = synth.Note Quarter Note.D 5 // Create a D5 quarter note.\n```\n\n### Creating audio data with an envelope\n\nIn order to create a sound with an envelope you need to use ``synth.SoundWithEnveloppe (frequency:float) (duration:Duration) (waveType:BaseWaves) (sustain:float) (attack:float) (hold:float) (decay:float) (release:float)``.\n\nWe are using a basic AHDSR[^6] envelope[^7] :\n\n![After](Reports/Files/envelope.png)\n\nExample :\n\n```fs\nlet synth = Synth()\nlet sound = synth.SoundWithEnveloppe 440. (Seconds 3.) Sin 0.5 0.5 0.5 0.5 0.5  // Create sound with envelope\n```\n\nThe above example creates the following sound :\n![After](Reports/Files/createWithEnv.png)\n\n\u003csup\u003e* Please note : when creating a new sound with this method, the release adds data at the end of the normal data.\u003c/sup\u003e\n\n### Creating audio data with a custom envelope\n\nIts possible to create sounds with more esoteric envelopes using the ``Synth.SoundWithCustomEnveloppe (frequency:float) (duration:Duration) (waveType:BaseWaves) (dataPoints: List\u003cfloat * float\u003e)`` function.\n\nThe ``dataPoints`` variable refers to a list of coordinates for which the envelope will \"nudge\" the sound towards.\nIt contains a list of tuples, each tuple represents a point in time first and an amplitude second.\n\nFor the shake of an example, lets create a simple envelope that only rises up from 0 to 1 and than back again to 0 :\n\nFirst of all, a simple sound without an envelope so we'll have a point of reference :\n\n\n```fs\nlet synth = Synth() // Init\nlet basicSound = synth.Note (Seconds 1) Note.A 4\n\nsynth.WriteToWav \"basic.wav\" [basicSound]\n```\n\nThis, of course, leaves us with a straight blob of a sinusoidal sound wave with a duration of 1 second :\n![Simple sin wave](Reports/Files/customEnv-0.PNG)\n\nNow if we do the same thing but with our envelope that would look like this:\n\n```fs\nlet synth = Synth() // Init\nlet custEnvSound = synth.SoundWithCustomEnveloppe (synth.GetNoteFreq Note.A 4) (Seconds 1) Sin [(0., 0.); (0.5, 1.); (1., 0.)]\n\nsynth.WriteToWav \"custEnvSound.wav\" [custEnvSound]\n```\n\nThe output already looks a bit more interresting :\n\n![Custom envelope](Reports/Files/customEnv-1.PNG)\n\nA better way of doing this would be to outright create a function for the new envelope :\n\n```fs\nlet synth = Synth() // Init\n\nlet exampleCustomEnvelope (note:Note) (octave:int) (duration:float) =\n    synth.SoundWithCustomEnveloppe (synth.GetNoteFreq note octave) (Seconds duration) Sin [(0., 0.); ((duration/2.), 1.); (duration, 0.)]\n\nlet custEnvSound1 = exampleCustomEnvelope Note.A 4 1.\nlet custEnvSound2 = exampleCustomEnvelope Note.B 6 2.\nlet custEnvSound3 = exampleCustomEnvelope Note.D 3 3.\n\nsynth.WriteToWav \"custEnvSound1.wav\" [custEnvSound1]\nsynth.WriteToWav \"custEnvSound2.wav\" [custEnvSound2]\nsynth.WriteToWav \"custEnvSound3.wav\" [custEnvSound3]\n```\n\nThis way the new, completely personalized, envelope can easily be applied to a large number of notes.\n![Custom envelope final](Reports/Files/customEnv-2.PNG)\n\n### Creating audio data with a chord\n\nA chord is a superposition of multiple notes, generally forming a nice sound in the ear.\n\nThere are currently only two types/qualities of chord possible: `Minor` and `Major`.\n\nWhile a minor chord has, going upward in frequencies, a spacing of 3 then 4 and sounds \"sad\",\na major chord has spacing of 4 then 3 and sound \"happy\".\n\nFor example, to create a `Am3`, you may use\n\n```fs\nlet Am3 = synth.Chord Quarter Note.A Minor 3\n```\n\nwhich is the equivalent of this\n\n```fs\nlet Am3 = synth.Add [\n    synth.Note Quarter Note.A 3\n    synth.Note Quarter Note.C 4\n    synth.Note Quarter Note.E 4\n]\n```\n\n\u003csup\u003esee the `Add` definition [thereafter](#superposing-audio-with-a-predefined-ratio)\u003c/sup\u003e\n\n## Finding frequencies from notes and octaves\n\nA more simplified way to find the sound you are looking for is through musical octaves[^1] and notes[^2].\nTo call on this form of notation you'll have to use the ``synth.GetNoteFreq (note:Note) (octave:int)`` function to get the right frequency.\n\nExample :\n\n```fs\nlet Note = synth.GetNoteFreq Note.C 4 // This returns the frequency of the C4 note\n```\n\nAlternatively, you could directly create a SinWave using the ``synth.Note (duration:Duration) (note:Note) (octave:int)``.\n\nExample :\n\n```fs\nlet Note = synth.Note Half Note.C 4 // This returns the frequency a half duration of the C4 note\n```\n\n### Finding notes with a custom default frequency\n\nIn most cases, the frequency of a note is calculated from a default frequency (mostly, 440Hz for the A4 note).\nHowever, in some cases, you might need to find a note from a different starting frequency.\nThis can be done using the ``synth.GetNoteFreqOffset (note:Note) (octave:int) (aFourFreq:Float)``\n\nExample :\n\n```fs\nlet Note = synth.GetNoteFreqOffset Note.C 4 444. // This returns the frequency of the C4 note calculated from the starting point 444Hz at the A4 note\n```\n\n## Creating silence\n\nCreating silence is as simple as calling the ``synth.Silence (duration:Duration)`` function.\n\n```fs\nlet Silence = synth.Silence (Seconds 2) // Returns 2 seconds of silence\n```\n\n## Cutting audio\n\nCutting audio is simple. You can use the following functions\n\n- ``synth.CutStart (time:float) (data:List\u003cfloat\u003e)`` : Cuts the start of the audio data returning the end part\n  \n- ``synth.CutEnd (time:float) (data:List\u003cfloat\u003e)`` : Cuts the end of the audio data returning the first part\n  \n- ``synth.CutMiddle (timeStart:float) (timeEnd:float) (data:List\u003cfloat\u003e)`` : Cuts out the middle of the audio data and returns the edges merged together\n  \n- ``synth.CutEdge (timeStart:float) (timeEnd:float) (data:List\u003cfloat\u003e)`` : Cuts of both ends of the audio data and returns the middle part\n\nExample :\n\n```fs\nlet a = synth.Note (Seconds 1) Note.A 4\nlet b = synth.Note (Seconds 1) Note.B 4\nlet c = synth.Note (Seconds 1) Note.C 4\nlet d = synth.Note (Seconds 1) Note.D 4\n\nlet full = synth.Compose [a; b; c; d;]        // Complete sound, takes 4 second and plays 4 different notes\n\nlet lastThree = synth.CutStart 1. full   // Cuts first note, leaving last 3\nlet firstThree = synth.CutEnd 1. full    // Cuts last note, leaving first 3\nlet edges = synth.CutMiddle 1. 1. full   // Cuts out the 2 middle notes, leaving the first and the last ones\nlet second = synth.CutMiddle 1. 2. full  // Cuts the first and the last 2 notes, leaving the second one\n```\n\n## Superposing audio data\n\nYou can superpose different sounds together to get a 3rd that is the result of the operation. You have different functions to superpose sounds to help you to easily get what you want.\n\n### Superposing audio with a predefined ratio\n\nThere is the function ``synth.Add sounds``. The variable sounds is a ``List\u003cList\u003cfloat\u003e\u003e`` containing the list of sounds that will be superposed together. This function will superpose the sounds together and average the values depending on the number of waves superposed.\n\nExample :\n\n```fs\nlet sound1 = synth.Compose [synth.Note Eighth Note.C 5]\nlet sound2 = synth.Compose [synth.Note Eighth Note.B 8]\nlet sound3 = synth.Compose [synth.Note Eighth Note.A 1]\n\nlet added = synth.Add [sound1;sound2;sound3]\n```\n\n### Superposing audio with a custom ratio\n\nThere is the function ``Utility.AddFactor (map:List\u003cTuple\u003cList\u003cfloat\u003e, float\u003e\u003e)``. The variable map contains a list of tuple containing the sounds the user wants to superpose together and the ratio for each of the sound.\n\nExample :\n\n```fs\nlet sound1 = synth.Compose [synth.Note Eighth Note.C 5]\nlet sound2 = synth.Compose [synth.Note Eighth Note.B 8]\nlet sound3 = synth.Compose [synth.Note Eighth Note.A 1]\n\nlet added = Utility.AddFactor [(sound1,0.2);(sound2,0.5);(sound3,0.3)]\n```\n\nIt is recommended that the total ratios the user uses is equal to 1. Otherwise the user can use the function ``Utility.Maximize data``. This function will take the data of a sound and modify it so that the amplitude goes from -1 to 1.\n\n### Superposing audio without ratios\n\nThere is the function ``AddSimple (sounds:list\u003clist\u003cfloat\u003e\u003e)``. This function will return the highest absolute value of the data.\n\nExample :\n\n```fs\nlet sound1 = synth.Compose [synth.Note Eighth Note.C 5]\nlet sound2 = synth.Compose [synth.Note Eighth Note.B 8]\n\nlet added = Utility.AddSimple [sound1;sound2]\n```\n\n## Composing\n\nOne thing you have to be aware of is the ``CutCorners`` function.\nWhen we first created the compose function we encountered a strange, small chaotic sound between each end every note.\nThis sound was caused by the notes which ending was a not-zero amplitude.\n\nThe solution was to add in a filter that gradually lowers the amplitude of the notes start and end to 0.\n\n|          Before cutCorner             |          After cutCorner            |\n|:------------------------------------:|:-----------------------------------:|\n| ![Before](Reports/Files/cut_b.png)  | ![After](Reports/Files/cut_a.png)  |\n\u003csup\u003e* for the sake of the example, the filter has been exaggerated\u003c/sup\u003e\n\nTherefore; the ``synth.Compose (sounds:List\u003cfloat\u003e)`` function has a default cutCorner value of 100 (this means it cuts away from the first and last 100 bytes from each note).\n\nExample :\n\n```fs\nlet C4 = synth.Note Half Note.C 4   // init\nlet D4 = synth.Note Half Note.D 4   //\nlet Silence = synth.Silence Quarter //\nlet B5 = synth.Note Half Note.B 5   //\n\nlet Music = synth.Compose [ C4; C4; D4; Silence; B5 ] // Returns a single, large sound composed of the smaller sounds given to it\n```\n\nIn certain cases, one might need to set a custom value to the cutCorner function.\nThis can be done with ``synth.ComposeCutCorner (limit:int) (sounds:List\u003cfloat\u003e)``\n\n```fs\nlet Music = synth.ComposeCutCorner 1000 [\n    C4\n    C4\n    D4\n    Silence\n    B5\n]\n```\n\nAlternatively, one might want to compose without the cutCorners filter.\nThis can be done either by giving it a 0 value or by using the ``synth.ComposeCutCorner (corner:int) (sounds:List\u003cfloat\u003e)`` function.\n\nWith zero value :\n\n```fs\nlet Music = synth.ComposeCutCorner 0 [ C4; C4; D4; Silence; B5 ]\n```\n\nOr with ``synth.ComposeNoCutCorner (sounds:List\u003cfloat\u003e)`` :\n\n```fs\nlet Music = synth.ComposeNoCutCorner [ C4;  C4; D4; Silence; B5 ]\n```\n\nThese two are equivalents.\n\n## Preview\n\nIt is possible to create a preview of an audio loaded into the filter using the ``synth.Preview (title:string) (sound:List\u003cfloat\u003e)`` function with Xplot.Plotly[^9].\n\nExample :\n\n```fs\nlet basic = synth.Note Whole Note.A 2       // Creating a basic note\nlet cut = Utility.CutCorners 5000 basic     // Making it look a bit more interesting\n\nsynth.Preview \"Example\" cut |\u003e ignore       // Launch preview\n```\n\nThe above example automatically opens the browser with the following image :\n\n![Preview](Reports/Files/preview.png)\n\nTools to zoom in/zoom out are also present on the page.\n\n## Frequency analysis\n\nIt's possible to do frequency analysis by using a Fourier transform on an audio file using :\n``Synth.Fourier (data:List\u003cfloat\u003e)``\n\nExample :\n\n```fs\nlet A345 = synth.Add [\n    synth.Note Whole Note.A 3\n    synth.Note Whole Note.A 4\n    synth.Note Whole Note.A 5\n]\n\nlet analysis = synth.Fourier A345\nsynth.PreviewMap \"Analysis of A3, A4, A5\" analysis\n```\n\nThe above example automatically opens the browser with the following graph :\n\n![Preview](Reports/Files/Frequency-Analysis.png)\n\n\u003e Note : Tools to zoom and move are present on the page.\n\nTo extract the main harmonics from the analysis, use `FrequencyAnalyser.LocalMaxValuesIndices (threshold: float) (map: Map\u003cfloat, float\u003e)`\n\nFor example, with the previous code, running\n\n```fs\nlet frequencies = FrequencyAnalysis.LocalMaxValuesIndices 0.2 analysis\nprintfn \"Frequencies: %A\" frequencies\n```\n\nwill output `Frequencies: [220.0457771; 440.0915541; 879.8466468]`\n\n## Filters\n\n### Currently accessible Filters\n\nTo complement your music, its possible to add filters to your audio data :\n\n- Amplitude changer : Changes the amplitude of a given sound.\n  \n- Echo : Repeats and periodically scales down the sound, creating an echo like effect.\n\n- Reverb : Repeats the sound similarly to the echo effect, but does it so before the sound could finish playing, creating a more vibrant sound.\n\n- Flanger : Adds a sweeping sound effect to the audio.\n\n- Envelope : Modifies the way the amplitude of the sound changes over time.\n\n- LFO[^8] AM : Amplitude modulation using a low frequency oscillator.\n\n- LFO FM : Frequency modulation using a low frequency oscillator.\n\n- Low Pass : Cuts off frequencies above a given threshold.\n\n- High Pass : Cuts off frequencies under a given threshold.\n\n- BandPass/RejectBand : Cuts off frequencies both above and under the given thresholds and inverse.\n  \n### Apply multiple filters at once\n\nYou can use this function to apply multiple filters at once like so :\n\n```fs\nlet MusicWithFilters = synth.ApplyFilters [\n    synth.ChangeAmplitude 0.5\n    synth.LowPass 400.\n    synth.Echo 4 0.7 1.5] music\n```\n\n### Changing amplitude\n\nTo change the amplitude of a sound, use the ``Synth.ChangeAmplitude (amplitude:float)`` filter like so :\n\n```fs\nlet MusicWithAmplitude = synth.ChangeAmplitude 0.5 Music\n```\n\n### Custom repeater filter\n\nThe repeater filter does exactly what it says on the tin.\n\n\nIt repeats the input data with an offset and add it to the original sound.\n\nThis filter is the basis on which we built the Reverb and Echo filters.\n\nThe function looks like this : ``Synth.Repeater (nbEcho:int) (decay:float) (delay:float) (dryData:List\u003cfloat\u003e)``\n\nThe variables inputted are :\n\n- nbEcho : The number of times the original sound gets repeated.\n- decay : Each time the sound is repeated we adjust the amplitude of the sound using this value\n- delay : The offset added to the echo (multiplies accordingly to the echo ex : echo 1 will have 1x this value, echo 2 will have 2x this value, etc..)\n- dryData : The original sound\n\nExample :\n\n```fs\nlet basicSound = synth.SoundWithEnveloppe 440. (Seconds 3.) Sin 0.5 0.5 0.5 0.5 0.5 // Creating a basic sound with an envelope to make it interesting\n\nlet repeated1 = synth.Repeater 5 0.6 2.5 basicSound\nlet repeated2 = synth.Repeater 5 0.9 4. basicSound\n```\n\nThe above examples give the following outputs :\n![Repeater examples](Reports/Files/repeater.png)\n\n### Echo\n\nThe echo filter repeats the same sound with a delay between delays and continuously weakens the the new sounds creating an echo effect.\n\n``synth.Echo (nbEcho:int) (decay:float) (delay:float) (dryData:List\u003cfloat\u003e)``\n\nIn this case, the delay is the time period between two echos, and NOT the delay from the start of the sound.\n\nExample :\n\n```fs\nlet basicSound = synth.SoundWithEnveloppe 440. (Seconds 1.) Sin 0.5 0.2 0.2 0.2 0.2 // Creating a basic sound with an envelope to make it interesting\n\nlet echo = synth.Echo 3 0.6 0.25 basicSound\n```\n\nThe above examples give the following outputs :\n![Repeater examples](Reports/Files/echo.png)\n\n### Reverb\n\nSimilarly to echo, reverb repeats and play the same sound with a delay.\nExcept, reverb does so with a delay that is shorter than the original sound.\n\n``Synth.Reverb (delayRatio:float) (minAmpRatio:float) (decay:float) (dryData:List\u003cfloat\u003e)``\n\nInstead of a fixed time, we are using ratio between the delay and the length of the sound.\nThis way, we can just simply input a value between 0 and 1, in which the filter gets more and more pronounce towards 1 instead of needing to pay attention to the length of the sound.\n\nExample :\n\n```fs\nlet basicSound = synth.SoundWithEnveloppe 440. (Seconds 1.) Sin 0.5 0.2 0.2 0.2 0.2 // Creating a basic sound with an envelope to make it interesting\n\nlet reverb = synth.Reverb 0.4 0.3 0.8 basicSound\n```\n\nThe above examples give the following outputs :\n![Repeater examples](Reports/Files/reverb.png)\n\n### Flanger\n\nThe flange filter is used to add kind of sweeping sound to the audio.\n``Synth.Flanger (delay:float) (speed:float) (dryData:List\u003cfloat\u003e)``\n\nExample :\n\n```fs\n    let synth = Synth() // Init\n    let basicSound = synth.SoundWithEnveloppe 440. (Seconds 1.) Sin 0.5 0.2 0.2 0.2 0.2 // Creating a basic sound with an envelope to make it interesting\n    let flanger = synth.Flanger 20. 0.4 basicSound //Adding filter\n    \n    synth.WriteToWav \"basic.wav\" [basicSound]\n    synth.WriteToWav \"flanger.wav\" [flanger]\n```\n\nThe results are :\n\n![Flanger](Reports/Files/flanger.png)\n\n### Envelope\n\nJust like when we create a sound, we can make an envelope to make the amplitude of a given audio data follow certain patterns.\n\nWe are still using a basic AHDSR envelope :\n\n![Envelope explanation](Reports/Files/envelope.png)\n\n``Synth.Envelope (sustain:float) (attack:float) (hold:float) (decay:float) (release) (data:List\u003cfloat\u003e)``\n\nExample :\n\n```fs\nlet synth = Synth()\nlet basic = synth.Note (Seconds 3.) Note.A 4\nlet env = synth.Envelope 0.5 0.5 0.5 0.5 0.5 basic\n\nsynth.WriteToWav \"basic.wav\" [basic]\nsynth.WriteToWav \"env.wav\" [env]\n```\n\nThe above example creates the following sound :\n![After](Reports/Files/env.png)\n\n\u003csup\u003e* Please note : when adding an envelope on top of an already existing sound using this method, the release is accounted into the already existing data and the length of the data does not change.\u003c/sup\u003e\n\n### Custom envelope\n\n\nJust like on creation, it is rather simple to insert a custom pattern into the anvelope.\nThis is done using the  ``Synth.CustomEnvelope (dataPoints0: List\u003cfloat * float\u003e) (data:List\u003cfloat\u003e)`` function.\n\nFor example, to create a simple filter that starts at 0 then rises to the maximum amplitude at the middle of the sound, then falls back to 0bn :\n\n```fs\nlet synth = Synth() // Init\n\nlet exampleCustomEnvelope (data:List\u003cfloat\u003e) =\n    Filter.CustomEnvelope [(0., 0.); ((float data.Length / synth.sampleRate / 2.), 1.); ((float data.Length / synth.sampleRate), 0.)] data\n\nlet custEnvSound1 = exampleCustomEnvelope (synth.Note (Seconds 1) Note.A 4)\nlet custEnvSound2 = exampleCustomEnvelope (synth.Note (Seconds 2) Note.B 4)\nlet custEnvSound3 = exampleCustomEnvelope (synth.Note (Seconds 3) Note.C 4)\n\nsynth.WriteToWav \"custEnvSound1.wav\" [custEnvSound1]\nsynth.WriteToWav \"custEnvSound2.wav\" [custEnvSound2]\nsynth.WriteToWav \"custEnvSound3.wav\" [custEnvSound3]\n```\n\nThis way the new, completely personalized, envelope can easily be applied to a large number of notes.\n![Custom envelope final](Reports/Files/custEnv.png)\n\n### Low frequency oscillation\n\n#### AM\n\nThe Amplitude Modulation using a Low Frequency Oscillator (LFO AM) also called tremolo changes, as the name, implies the frequency of a sound based on a small frequency.\nThis allows for a kind of wobble effect, alternating the amplitude between its maximum in a sinusoidal fashion.\n\nThe function takes not only the modulating frequency as parameter but also the new lower and upper bounds (usually -1. and 1.) and the music's sample rate.\n\nHere is an usage example :\n\n```fs\nlet basic = synth.Note (Seconds 10.) Note.A 4\nlet sound = synth.LFO_AM 60. -1. 1. basic\n```\n\n![LFO AM Example](Reports/Files/LFO_AM.png)\n\n#### FM\n\nAs the name implies, Frequency modulation alternatively increases and decreases the frequency of the input data.\n\n``Synth.LFO_FM (modWave:List\u003cfloat\u003e) (multiplicator:float) (data:List\u003cfloat\u003e)``\n\nThe ``modWave`` stands for an input wave which the function will follow to modulate the input data.\nThe ``multiplier`` is there to help control how strong the effect of the filter is.\n\nExample :\n\n```fs\nlet synth = Synth() // Init\n\nlet basic = synth.Note (Seconds 2.) Note.A 4      // Creating a basic note\nlet modWave = synth.Sound 100. (Seconds 2.) Sin   // Creating a modWave of 100Hz\nlet fm = synth.LFO_FM modWave 2. basic           // Applying the LFO FM filter\n\nsynth.WriteToWav \"basic.wav\" [basic]\nsynth.WriteToWav \"modWave.wav\" [modWave]\nsynth.WriteToWav \"fm.wav\" [fm]\n```\nThe output is :\n\n![LFO FM](Reports/Files/lfo_fm.png)\n\n### LowPass / HighPass / BandPass / RejectBand filters\n\nThese are 4 filters applicable to the audio data.\n\nThey are used to cut off frequencies above a given threshold, below a given threshold, both above and below a given threshold, or both above and below a given threshold then inversed.\n\nThey are used like so :\n\n```fs\n  let musicLowPass = synth.LowPass cutOffFreq Music\n```\n\n```fs\n  let musicHighPass = synth.HighPass cutOffFreq Music\n```\n\n```fs\n  let musicBandPass = synth.BandPass lowCutOffFreq highCutOffFreq Music\n```\n\n```fs\n  let musicRejectBand = synth.RejectBand lowCutOffFreq highCutOffFreq Music\n```\n\n# Footnotes\n\n## Musical notes\n\nThe musical notes available are :\n\u003e ``C``,  ``Cs / Db``, ``D``, ``Ds / Eb``, ``E``, ``F``, ``Fs / Gb``, ``G``, ``Gs / Ab``, ``A``, ``As / Bb``, ``B``\n\n## Wave functions[^3]\n\nThe wave types available are :\n\u003e ``Sin``, ``Square``, ``Triangular``, ``Saw``, ``Silence``, ``CustomInstrument``\n\n- The ``CustomInstrument`` value has a value of ``(float -\u003e float -\u003e float -\u003e float -\u003e float -\u003e float)``. This is because the wave functions need to be written as :\n\n```fs\nlet WaveFunc (frequency:float) (amplitude:float) (verticalShift:float) (phaseShift:float) (timeLength:float) \n```\n\n## Duration of elements\n\nThe note durations[^5] available are :\n\u003e ``Whole``, ``Half``, ``Quarter``, ``Eighth``, ``Sixteenth``, ``Custom``, ``Seconds``\n\n- The Seconds value takes a float as argument.\n- The Custom value takes a float as its argument. This translates using the formula ``value *4.* 60. / bpm``.\n- The tempo of the music can be changed by changing the value ``synth.SetBpm`` (default 90).\n\n## Unit Test\n\nThe tests can be found in the Synthesizer.Test project. To run them you'll have to be located in the project folder and run the dotnet test command.\n\n## See Also\n\nInfo on [**.mp3 files**](https://github.com/ClementCaton/ALGOSUP_2022_Project_3_A/blob/main/Informations/INFO%20mp3.md)\u003cbr\u003e\nInfo on [**.Wav files**](https://github.com/ClementCaton/ALGOSUP_2022_Project_3_A/blob/main/Informations/INFO.md)\u003cbr\u003e\nLink to our [**Trello**](https://trello.com/b/itooTuBY/algosup2022project3a)\u003cbr\u003e\nLink to our [**Functional Specifications**](https://github.com/ClementCaton/ALGOSUP_2022_Project_3_A/blob/main/Reports/Functional%20specification.md)\u003cbr\u003e\nLink to our [**Technical Specifications**](https://github.com/ClementCaton/ALGOSUP_2022_Project_3_A/blob/main/Reports/Technical%20specification.md)\u003cbr\u003e\nLink to our [**Software Architecture Design Choices**](https://github.com/ClementCaton/ALGOSUP_2022_Project_3_A/blob/main/Reports/Software%20architecture%20design%20choices.md)\n\n## **Definitions**\n\n[^1]: [Octave](https://en.wikipedia.org/wiki/Octave) : A series of eight notes occupying the interval between (and including) two notes, one having twice or half the frequency of vibration of the other.\n\n[^2]: [Note](https://en.wikipedia.org/wiki/Musical_note) : A note is a symbol denoting a musical sound.\n\n[^3]: [Basic Waves functions](https://www.musictheory.net/lessons/11) :\u003cbr\u003eA wave function is a mathematical function which can create a wave of a predefined pattern frequency, amplitude, etc.. In the scope of this project we are using the four basic waveforms: Sinusoidal, Square, Triangular and Saw.\u003cbr\u003eThe four basic waves are -\u003e\u003cbr\u003e\u003cbr\u003eThe sin wave = the simplest wave with a formula of *sin(2 π  frequency / sampleRate)* \u003cbr\u003e\u003cbr\u003eThe square wave = a wave made with an sgn of a sinwave with a formula of *sgn(sinwave)*\u003cbr\u003e\u003cbr\u003eThe saw wave = has a form close to a triangle, it has a right angle at the end of its decreasing part *2(t/p - [1/2+t/p]*\u003cbr\u003e\u003cbr\u003eThe triangle wave = this wave has the most complicated formula *period/π arcsin[sin(π x)]*\n\n[^4]: [beat](https://en.wikipedia.org/wiki/Beat_(music)) : Beats is a sort of rythm; 2 beat per second is like a tempo, it will happen twice in a second\n\n[^5]: Musical durations : \u003cbr\u003e\nMusical duration is measured in beats[^4] \u003cbr\u003e\n``Whole last 4 beats``\u003cbr\u003e ``Half last 2 beats``\u003cbr\u003e ``Quarter last 1 beat``\u003cbr\u003e ``Eighth last 1/2 beat``\u003cbr\u003e ``Sixteenth last 1/4 beat``\u003cbr\u003e ``Custom depends of the number of beat``\u003cbr\u003e ``Seconds last the number of beats in the number of seconds written``\n\n[^6]: AHDSR : \u003cbr\u003eAn Envelope parameters -\u003e\u003cbr\u003e Attack is the time taken for initial run-up of level from nil to peak, beginning when the key is pressed.\u003cbr\u003e\u003cbr\u003eHold time allows you to adjust the time that the peak amplitude level is held before the decay stage of the envelope begins.\u003cbr\u003e\u003cbr\u003e\nDecay is the time taken for the subsequent run down from the attack level to the designated sustain level.\u003cbr\u003e\u003cbr\u003e\nSustain is the level during the main sequence of the sound's duration, until the key is released.\u003cbr\u003e\u003cbr\u003e\nRelease is the time taken for the level to decay from the sustain level to zero after the key is released\n\n[^7]: [Envelope](https://theproaudiofiles.com/synthesis-101-envelope-parameters-uses/) : The envelope is the way a sound change over time. It usually enables you to control the wave forms with the AHDSR parameters.\n\n[^8]: [LFO / Low frequency oscillator](https://en.wikipedia.org/wiki/Low-frequency_oscillation) : Is an oscillator performing under 20Hz to create audio effects such as vibrato and phasing.\n\n[^9]: [Xplot.Plotly](https://fslab.org/XPlot/plotly.html) : Is a nuget which allowsto view data in the form of graphs\n\n[^10]: [Stereo](https://en.wikipedia.org/wiki/Stereophonic_sound) : Stereo in opposition to mono, is a sound using multiple audio source (usually 2) to recreate a multi-directional / 3D-sound.\n\n[^11]: [Sample rate](https://www.vocitec.com/docs-tools/blog/sampling-rates-sample-depths-and-bit-rates-basic-audio-concepts#:~:text=The%20sampling%20rate%20refers%20to,a%20specific%20point%20in%20time.) : The sampling rate refers to the number of samples of audio recorded every second. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclementcaton%2Falgosup_2022_project_3_a","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclementcaton%2Falgosup_2022_project_3_a","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclementcaton%2Falgosup_2022_project_3_a/lists"}