diff --git a/.travis.yml b/.travis.yml index bb5a4e0..2c028bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,15 +3,14 @@ python: - '3.4' branches: only: - - horizon - - master + - anchor-sound env: global: - - GH_REF: github.com/brownplt/pyret-docs.git + - GH_REF: github.com/h454482/pyret-docs.git - RACKET_DIR: "~/racket" - - RACKET_VERSION: '7.1' - - secure: tkOgKmY5brk6wSTluhgKaFiUiVVXnf1HefE1v56LOUlNHcieMLjRAgXc5LsdezI5ymtf59tZBVAdv7zWG+2Co/nMgIVE20FLpT2UeZCyBu+669lDPZG5zJcsq3Yp52EyE3pSzBRc5H5c9Ni3AZWunV/OxV8xUn3TMkTz9ian/j1AiyDK6z/s5TyFVuRITUOriyuE84e5HJvSYdCjMH2jZE3vCp+lBO9xK/+7IAyoXM7LBFIxz2qBbfzeGbDUzMiFujJZ0/cJu8Qfjlz/AEzW13BWhzhrI9FWUaawKJX1xU6gv9uhRgmsQqQjOMe2CdDuUiUoFP11WjbSnSsN7/B3oklqSiHNHFnOCbxYqunpEpI4EOltL0SpEFHZYl5JgtsLJTbqZvAJ0AaT88F8HBJEFthxyz6MhbBZ9CewOpFY50qY7yvsLr6JEEzCEnTk2EtIv6r2Ux5Xad+K2AXfpEPR1VvfRl6J4U7JGKHHspNVJmpdZSnJdYTzS2siHknV4LQ5Zrl1uwhTjTc4H6bshDKQfcXFkkc4edV78jrJiPDP5hpPVaqLH5MTLV0mhsrSowHPaOyL6y3y+/mrUFG7F0Lha9qdtT4k8PvD47ldPxBCaFOh85MC+w+p65WFqf2rwFMmZNA9sUTltFvU5OMtBPfTMpCrhgnBhf62UrLm6z2jJZY= - - secure: LNFPDErTqBbgv3XgDI+X9q9Clb/BCFeuUcHjpzP5w5u4y2hpe3TnOkrLfI/FNNYqL2wczqA9cyQ6tDsczFatxz936heSU087uimPCEHKYuUk4jCZ3vM1iIH+MeiAEnaSDXmHOS0iSxvX86BU5AYULlRdpCNqU/12dzJZdARYhIGX+7LNUCUf2/D9MAbL85OjGjzR6ZEaJ5TdvL7IPKejsABiyU/TEAD6PNIgPaL4MRv+gqhedOak7nhFHUfJFUOVQ/GFE9np46HWW19aGIJSktO7RRCsDESUBte19qdQz+pABgn/mR3zVYsn0x7bd9VhpGHt5yeEpAhm82GB5MdkLVYCap8SgGHzl6IAXyToBTmC6wDxOQ/ivpWnkDum1W2rEuY/HrT1235Z7ea5YZVajg5uxAcnkpYqkOyWrLMm7yjXY6wdNQ9F1YKyx6GOxpZj/5opib3aF0hxIRF5nHD2ROZVgLeBM6PKliES1/X5GutBQCh3PsXwGd1G40YtUPXghMwxjYiDmwkM05fOnrUqwfobz98yrDm/6Q7KUEqUaUcV/RshLYcgXrOgSv9YSDykJ7BkvFhnDTOiHPVc4gg4GFHjZI0T8cCziyeroo3/pfR/Oe1kIXyuCzpP4kbY48JGE7GEvC7vT/dwOQ4ERt4XfzD2jlBaubqI6Boy95+VrKo= + - RACKET_VERSION: '7.4' + - secure: YeIStlvyzUSqDIOgx2quYuEFk2Ves/XL1fa+x/NT4a2q92WX/DJxBv6Nn5zuwWviXFfEwdi3r9T5DcRhkWmQE8ogHmX9ria8qBvIi0pkPyR6bpUVLcqhxssjrzUcusEk5cboOyzJHimrKxyoXV0iJmUVqYhOEwISQxMRV/aW/lsegvp5n5KU4R+n2tYsS8e8Y3vLloFiL3QU4mpez6rQzpM6ZKLDwDPWfjxSAAz/hCdEpp7InitFFbpH/npTOBJI2UbKc/W56umGPVUrIgU8x1C3MF8sB/35wq3hg3TK02axDGW2HNoY7P4Peaqt2nkkrnyEgV2HyjyD4m778Aj9qThvJL1jVtERmYDIBNdzagF8YM3yJ6EA2WxLTS2ERO+T1RYgwDixucQARl5bp8SspWy/vM+dOQGUVVKyF9CSs8/Pd/07Fx84L43ELCgGAjMyEhE8hbChOHbcLnOuU1n++Ae5u/0ytSSkou1Tb1QEo5MSoYi1C9ScI3eH90Ff5GRg3THmmCPkwTui2Ou8wp3oLc6SuULDH6U/lD0+6wFu8FpGIYl1Dk0lfQo3JnW0eQoHslE0uW0AFUHRQPujws8JM8hEmARZDyjVSOCADUkHzN+28seguX5mwyNlbwgcvAgSZDuL4XYJrPeEUvpVMVxGCjdZlcmCw8XeKgYTmngxlTw= + - secure: rHmNPLTMeRyfAt8gk7HYY2PFA++hET1RYTr2AGNUOPh7v9tCe8yeukabP9xGB9p202HMmfa9eybmQ5NsOMCXgbWTYrixjpWJFGqTRCvj7IobZJt2jwX+eK4dWQMur9FG2BPd/qA9Pw1W/xeOyF50zBgHAzWAwACUP2o2E+fIapu8bT9ItZtccjjCHeEMJ216UfJdpf86K+SkZyVz97agTLeQ6Rph6OKVAQMCTxzn89zhyM4fFU0FJbVI0KqAMr5hbjLqUsfOHL/6yq2bTtrllgKa16gr+utpFtYnmmoLU5rmYgOhaY+hgoNWwWo4+iEzNDpRlWBsVxbPQWj1YnpzdFKueApWjkQaddHj990M7Ts/gTmhbHE28nKbCVVpZCPlTY6Mb+Co0BeFW5FwM+9Tnj3EAUHHRdXj0oFPylw39sFHBRjeqn1eJx8oX0M1qdKeuF83KRyQI+sRNAPoO+XPy6Ig6UI49gFQoXBtiAWZeaMPVWPA77XGloJxUP+D7pZe1Qov3gQJrATG7LycIA9hwB6hOiINuloYW6YHzFUqPHKqtCp8ZOztRYghFfOGBQJpgagxKj/nmKIWdGsLxcBlArceWO8HgjwcpHhek9NQdnYdcqLzZnFCBWjUrUaYjj1EFYjJMvtbT9oM8nZmQxsPnRQMTo4dbJ0gj/N93fnvSm4= before_install: - git clone https://github.com/greghendershott/travis-racket.git - cat travis-racket/install-racket.sh | bash @@ -20,25 +19,15 @@ script: - npm install - make deploy: - - provider: s3 - bucket: pyret-docs - local_dir: build/docs - upload-dir: docs/latest - skip_cleanup: true - on: - branch: master - secret_access_key: - secure: LzAA8MSvkPCydPU/Kobw4lPLiGbNicqFd9q7S1PkgOtjb9AdA+NBC1kZRf7wunV3b72UHmD7mIHRJESEqWPk4NdFuQM4tJOSIKsDjnF3RuPXO1BpmLMP5/2XUVyv8v1tPo/IgIVnRGGDc5ylUFKbvBkGP7CqWmtF15WyxsX6LFzDVEBIC79wq3TXNBfUHOVYOuiBSzJSzdwFSTABEBqdHHwqeDnFDxy9aeVogbF6FoofaMUsuN46FBZ4EHDm+QhedWaHQ1sq2Waf+tkJtYIdZk+CdafxyQA3uuBTLyztuH6gNCDeJgvYyNGXpYphGyB4nGOIuH4E6V31t9FyF53Uz16oOgidfvKmYyw+m06PYuoOnzs35Vcf+Kp0/zIuxt7BpxMx80cbmm/kpdxrQKGgGCLP3VcwJ07IPMW6YkmsFg3Ob2vAWvcBQdErj586OOoGiTi4cRZzsPeQ9AfAnD6Dq+NhguG88kV+ntRisDZ60A+zGxLsFtqGn87s3W3ieVSrgv1P2gihwYasCm0B5jTeV7AnsKVRExnDedReJORIOQfc33sToWEVQ+cGZ1e2QyasuJtaVg2qza1SB4JZ2psjhkvhWfYCBDdODeukWJ/KGUxOxYupc+nEQkg6VRtKey6oF5MDkFGL9cOSzDrX3AGhyPFq6mmFJSuNshJOncZDliE= - access_key_id: - secure: FO6FefC+VV5xqRckWkfm4BAAO+fi91xCMCG2U/cKpP/LSkrN5PBqSeSymy2II0ga9hhzu/gL3zJFSivwufCr/cQdozKyqqI9kFE3qRyfIcydfQQt3f46l6bXPpiw3n6MAI03TBYzV2M7Vlj/Y2KBYv5cpY7fUn2kDu0eHKr8GunSysltM39VpTYjWReup5C7fuyAhgJvQHnPaxWr+QTgNcXERge4IVFON+opaMadrz2ECVTvwIaC/7N4VH1xu/WB1objwyH7lfElxI21rM5e8zeyQpBVGaAmdkY97uQ8Jsx7Zzvux6DRK/9ga+O4xZSsJoTNHBf3mLtzy1lhO5eO1aJlPH6JiEG0Mb144/o9ToH1RDC8Wc/7tEDGrOq4m2z5o3NGLf9adFv6EEzjyw8QouGCkj4OgQOptmLGBJ2/ZUmNfcDeVGtdm7d9A565ElRgvc8dbWRkika982JVBwuR/2FjGwkmHAvxYDMfJ77tEninhGptpgXR1rZxt5vrVOWnQdle2iu6Tco17OvLSwidNr7B7ejq/SQBWYBGpoyZlQGF3Xp95hNEMF76aKhFajS519mRHFzTqUsBGuHkjbHVvYx+rkUaP+Zsx1n2h2Uv/A2KHHGzpLBPyM1e8zKVluHYmjh7XFdX/af+n7jmL29R9yFVlD41PjKAdcdo7u7bJw4= - - provider: s3 - bucket: pyret-docs - local_dir: build/docs - upload-dir: docs/horizon - skip_cleanup: true - on: - branch: horizon - secret_access_key: - secure: LzAA8MSvkPCydPU/Kobw4lPLiGbNicqFd9q7S1PkgOtjb9AdA+NBC1kZRf7wunV3b72UHmD7mIHRJESEqWPk4NdFuQM4tJOSIKsDjnF3RuPXO1BpmLMP5/2XUVyv8v1tPo/IgIVnRGGDc5ylUFKbvBkGP7CqWmtF15WyxsX6LFzDVEBIC79wq3TXNBfUHOVYOuiBSzJSzdwFSTABEBqdHHwqeDnFDxy9aeVogbF6FoofaMUsuN46FBZ4EHDm+QhedWaHQ1sq2Waf+tkJtYIdZk+CdafxyQA3uuBTLyztuH6gNCDeJgvYyNGXpYphGyB4nGOIuH4E6V31t9FyF53Uz16oOgidfvKmYyw+m06PYuoOnzs35Vcf+Kp0/zIuxt7BpxMx80cbmm/kpdxrQKGgGCLP3VcwJ07IPMW6YkmsFg3Ob2vAWvcBQdErj586OOoGiTi4cRZzsPeQ9AfAnD6Dq+NhguG88kV+ntRisDZ60A+zGxLsFtqGn87s3W3ieVSrgv1P2gihwYasCm0B5jTeV7AnsKVRExnDedReJORIOQfc33sToWEVQ+cGZ1e2QyasuJtaVg2qza1SB4JZ2psjhkvhWfYCBDdODeukWJ/KGUxOxYupc+nEQkg6VRtKey6oF5MDkFGL9cOSzDrX3AGhyPFq6mmFJSuNshJOncZDliE= - access_key_id: - secure: FO6FefC+VV5xqRckWkfm4BAAO+fi91xCMCG2U/cKpP/LSkrN5PBqSeSymy2II0ga9hhzu/gL3zJFSivwufCr/cQdozKyqqI9kFE3qRyfIcydfQQt3f46l6bXPpiw3n6MAI03TBYzV2M7Vlj/Y2KBYv5cpY7fUn2kDu0eHKr8GunSysltM39VpTYjWReup5C7fuyAhgJvQHnPaxWr+QTgNcXERge4IVFON+opaMadrz2ECVTvwIaC/7N4VH1xu/WB1objwyH7lfElxI21rM5e8zeyQpBVGaAmdkY97uQ8Jsx7Zzvux6DRK/9ga+O4xZSsJoTNHBf3mLtzy1lhO5eO1aJlPH6JiEG0Mb144/o9ToH1RDC8Wc/7tEDGrOq4m2z5o3NGLf9adFv6EEzjyw8QouGCkj4OgQOptmLGBJ2/ZUmNfcDeVGtdm7d9A565ElRgvc8dbWRkika982JVBwuR/2FjGwkmHAvxYDMfJ77tEninhGptpgXR1rZxt5vrVOWnQdle2iu6Tco17OvLSwidNr7B7ejq/SQBWYBGpoyZlQGF3Xp95hNEMF76aKhFajS519mRHFzTqUsBGuHkjbHVvYx+rkUaP+Zsx1n2h2Uv/A2KHHGzpLBPyM1e8zKVluHYmjh7XFdX/af+n7jmL29R9yFVlD41PjKAdcdo7u7bJw4= +- provider: s3 + acl: public_read + bucket: pyret-docs-sound + local_dir: build/docs + upload-dir: docs + skip_cleanup: true + on: + branch: anchor-sound + secret_access_key: + secure: UaWXrI496sOqBvLbwf+Xibs3yLwXxIcXAyG3dRkwufIUYdF1s71NxN62ZBrgaaFOoaa1tmgZ5EwLvsYB1vSu8nB69sIfAzJv1dJF48QXUISWAvoTy7hbUyK9eHjf3XkLFJgxNDI5ENGZ4r7dY4gbU5fxhwPVomIf87qOoVF05R6259+w6oqWIFdUX52bIB4VA+GyLCUeFWIpUiovx1p3KjFA5JzAzhzybTJuv7AGdpLOFEG9jY23BAjYumxECuv56f9pz75gQNJG0dKEcjD29wWAVisRBIweroGIFpnG/GbUhIobu0OYei/+baw6dLH3VXMfDbUtHqAdMWUHTtujAdn1ckEerHE5nWdt3lvI8ZxX9pITAfzBhaKnX9qJZAXYqoYD17OQ7htFBorBE4ySot5vdEjsU82yoHjGOayFCWfMUpOANn1MqdNs8FYamInBuu1DcODGndyLR1ao+5B3nOGnhKyvGteS/jUN9TE7/csKGC+WLkohBdlg4caRApWdqGz7x6V0Xa78A8bRZKab/8G7voxp5qHetmVaD71Xsl+ehvO2aTkn/ECQhKYMd15Ag0FrEHjHUYzgyYcEO+1M52UE/FnFCjhzLA2aDgZ7vZeUbypGb3G+adhedWsds/0UTq5yQguiX+GKPi3e3ayKR/US/yJ1c0LqpXCVgUR3iPM= + access_key_id: + secure: j572fyx8e5C/iVmv72pQYP/8yBgvDn1/dUUTV5ENw26/zO36yTSsY75KIDybCPctYmb8ap4TTfgSMIjNgWz9KlJIQaAEsgu5mNC5O5R6DEc32/HBsOb9XPcV9rlQwB/CL6q9AITkNvsVxDnWQ0fwoUafYUxAWBNCCEwHdfkZlc2elazhgRIrHLpyhr1SxphxUGzdSmzvwdIUfWs/yDwJGPPrUCuQCUvleL1xMNlNMM0eOohMbk/svZTwNoMAAMPDI9RiE5oURrfKlJynz7yaKEKdKNCGF4g1Z5AbYfcIWx6WlxcxGa957ivYxgPHxunY206HzqqYi4Bir+q2YOg5HeNBmjYlhKBnrj4iWUZiYo+m1i3Vw3xR/FG5cU3RL34i0aDqPHHMIaKGgPmIH9ZXnt7Ncbql9Emgo875SwtLpAycI+Nf91GKMl+R1IRsAr8YvR9uTUQ/pTZi0N49Mw5d4UudhDt/AxPYKrXLh8qrsZYXq7nmLCGFBGA+iy1OOnUJxYQZ0o65MQyS8Qx93hSiwGiLjk/UMAzOSZ/MBE0+/iA0anfwJzs+JOoApYn+P3SksfCA0tsC1ikmxzVdf/nRTMyKMN1vmP2d+XLrnw8FWH35WVGCcphh55ctht465ufstvM+C2TgIDFhsmAmSuaqM7HWRl3hxYdVXX3Xhz7UCoc= \ No newline at end of file diff --git a/src/Pyret-Tutorial/jingleBells.scrbl b/src/Pyret-Tutorial/jingleBells.scrbl new file mode 100755 index 0000000..76e1209 --- /dev/null +++ b/src/Pyret-Tutorial/jingleBells.scrbl @@ -0,0 +1,129 @@ +#lang scribble/base + +@(define (version . t) + (apply section "Version: " t)) + +@require{lib.rkt} + +@title{Tutorial: Your First Song - Jingle Bells} + +@(table-of-contents) + +@section{Introduction} + +In this tutorial we're going to write a program that constructs and plays the song - +Jingle Bells on Pyret. The tutorial is built to facilitate the simple construction of a +single line, but it'll have all the elements you need to build much sounds of your own. + +@section{About the Pyret Sound Library} + +The Pyret Sound Library is responsible for the creation and manipulation of various types of sounds. + + A sound file can be visualized as a set of channels, each corresponding to a sound wave. Depending on the + construction and framework, these channels serve varying purposes. In some cases, they may refer to the + left and right stereo sound stream. In others, each channel may correspond to a different instrument. In + many cases, a set of channels may contain very similar sounds that have been overlaid together to improve + quality. This library is supported by an underlying infrastructure of the WebAudioAPI. Therefore, it is + equipped to handle only .wav files for uploads and downloads. + + A sound wave can be represented in a numeric form as a set of amplitudes whose values can range from -1 + to +1. As a consequence, each sound in this library is characterized by a two-dimensional float array, + where each row corresponds to the sound wave in a given channel. + + The sound library is equipped with a widget that possesses media-player capabilities. Any object of the + type 'sound', will automatically invoke the widget. + +@section{Preliminaries} + +To begin with, we should inform Pyret that we plan to make use of the +sound library. Additionally we require the List and Global libraries. +@pydisp{ +import global as G +import sound as S +import list as L +} +This tells Pyret to load to these three libraries and bind the results +to the corresponding names, @pyin{G}, @pyin{S} and @pyin{L}. Thus, all sound +operations are obtained from @pyin{S} and list operations from +@pyin{L}. + +@section{Constructing the sound} +In this case, each note in the song can be visualized as pressing an octave key on a piano. +Once we have the individual notes, we can then append them together to construct the sound. +However, one would have to be cognizant of the pauses in the song, and insert appropriate +gaps of silence in between notes. One we have the song, we could further add in additional +instrument backgrounds using overlaying, and also fad the final output towards the end, +like a typical song. + +@subsection{Creating notes} + +We will use the get-note funstion of the Sound library to create a note. This constructs +a sound note of an octave of the given key, such as A4, C8 etc, with a tangible silence in the end. +Hence, it works like a typical press of a piano key. Concatenation of several notes will result in a melody akin to playing notes on a piano. +Consider the following code that creates a note of keys A3, A1 and C3: + +@pydisp{ +a = S.get-note("A3", 1, 1.25) +b = S.get-note("A1", 1, 1.25) +c = S.get-note("C3", 1, 1.25) +} + +@subsection{Concatenating notes} + +Now we're ready to draw append these notes together, to create a line of music. For this, +we will use the concatList function of the Sound library. This function concatenates different +sounds together as one long sound. The function requires a list of sound objects in the order +in which they need to be concatenated. It then returns the result as a new sound. +Hence, we need to construct a list of the above notes in the right order to create our first line. +@pydisp{ +l1 = [G.raw-array: a,a,a,b,a,a,a] + +l2 = [G.raw-array: c,c,c,b,c,c,c] + +r = S.concat-list(l1) + +q = S.concat-list(l2) +} + +@subsection{Overlaying two sounds} +The reason for the creation of two sounds for the same line is to add a layer dimensionality, +which makes the sound more profound and interesting. We can combine these two sounds to play a +single line by overlaying them using the overlay function. Overlay places one sound over another, +and is the equivalent of an addition operation between the amplitudes of a set of sounds. +The Overlay function requires two sound objects that need to be overlayed, and returns the +result as a new sound. +@pydisp{ +p = S.overlay(r,q) +} + +@exercise{ +Are you happy with the resulting sound? Do you find anything amiss? If so, what can we do to +modify the sound, so that it sounds similar to the original? +} + +@subsection{Increasing the Tempo} +We can increase the tempo of a sound to our required speed using the adjust-playback-speed function in +the Sound library. This function Constructs a sound that plays at the new rate given by the playback +speed, such as 2X, 3X etc. It takes a sound object and an integer number that specifies the factor by +which the sound needs to be sped up or slowed down. It then returns a resultant new sound object. +@pydisp{ +t = S.adjust-playback-speed(p, 8) +} + +That's it! We've created our very first line of music using Pyret's sound library! + +@exercise{ +Now that we have constructed our very first line, can you think about how to similarly construct the +entire song? You can use the following resource for note reference - +@show-url{https://www.streetdirectory.com/travel_guide/31508/music/learn_to_play_jingle_bells_without_sheet_music.html} +} + +@subsection{Fading a sound} + +Have you ever noticed how the ending of a song has a gradual fade? You can implement that functionality +to your very own rendition of Jingle Bells that you have constructed above. We use the fade-out function from +the Pyret Sound library for this purpose. The function progressively fades / softens a given sound towards the end +and returns the result as a new sound. +@pydisp{ +m = S.fade-out(t) +} \ No newline at end of file diff --git a/src/builtin/concated.PNG b/src/builtin/concated.PNG new file mode 100755 index 0000000..c62f4ac Binary files /dev/null and b/src/builtin/concated.PNG differ diff --git a/src/builtin/fadein.png b/src/builtin/fadein.png new file mode 100644 index 0000000..355e504 Binary files /dev/null and b/src/builtin/fadein.png differ diff --git a/src/builtin/fadeinindex.png b/src/builtin/fadeinindex.png new file mode 100644 index 0000000..1f1e3c5 Binary files /dev/null and b/src/builtin/fadeinindex.png differ diff --git a/src/builtin/fadeintime.png b/src/builtin/fadeintime.png new file mode 100644 index 0000000..f507176 Binary files /dev/null and b/src/builtin/fadeintime.png differ diff --git a/src/builtin/fadeout.PNG b/src/builtin/fadeout.PNG new file mode 100755 index 0000000..541fdda Binary files /dev/null and b/src/builtin/fadeout.PNG differ diff --git a/src/builtin/fadeoutindex.png b/src/builtin/fadeoutindex.png new file mode 100644 index 0000000..e5af673 Binary files /dev/null and b/src/builtin/fadeoutindex.png differ diff --git a/src/builtin/fadeouttime.png b/src/builtin/fadeouttime.png new file mode 100644 index 0000000..646f522 Binary files /dev/null and b/src/builtin/fadeouttime.png differ diff --git a/src/builtin/getnote.PNG b/src/builtin/getnote.PNG new file mode 100755 index 0000000..515b0b8 Binary files /dev/null and b/src/builtin/getnote.PNG differ diff --git a/src/builtin/multiarray.png b/src/builtin/multiarray.png new file mode 100644 index 0000000..03a4eb9 Binary files /dev/null and b/src/builtin/multiarray.png differ diff --git a/src/builtin/multichannel.png b/src/builtin/multichannel.png new file mode 100644 index 0000000..8754945 Binary files /dev/null and b/src/builtin/multichannel.png differ diff --git a/src/builtin/overlayed.PNG b/src/builtin/overlayed.PNG new file mode 100755 index 0000000..51560aa Binary files /dev/null and b/src/builtin/overlayed.PNG differ diff --git a/src/builtin/overlayone.PNG b/src/builtin/overlayone.PNG new file mode 100755 index 0000000..194c923 Binary files /dev/null and b/src/builtin/overlayone.PNG differ diff --git a/src/builtin/overlaytwo.PNG b/src/builtin/overlaytwo.PNG new file mode 100755 index 0000000..4c21984 Binary files /dev/null and b/src/builtin/overlaytwo.PNG differ diff --git a/src/builtin/soundwave.PNG b/src/builtin/soundwave.PNG new file mode 100755 index 0000000..213bd51 Binary files /dev/null and b/src/builtin/soundwave.PNG differ diff --git a/src/builtin/urlsound.png b/src/builtin/urlsound.png new file mode 100644 index 0000000..9a0c7c2 Binary files /dev/null and b/src/builtin/urlsound.png differ diff --git a/src/getting-started.scrbl b/src/getting-started.scrbl index 6e6a8c3..aeb8aeb 100644 --- a/src/getting-started.scrbl +++ b/src/getting-started.scrbl @@ -19,3 +19,4 @@ that you can follow to build up a simple animated game: @include-section["tour.scrbl"] @include-section["Pyret-Tutorial/lander.scrbl"] +@include-section["Pyret-Tutorial/jingleBells.scrbl"] diff --git a/src/lang/spies.scrbl b/src/lang/spies.scrbl index 5924201..6cf6d94 100644 --- a/src/lang/spies.scrbl +++ b/src/lang/spies.scrbl @@ -1,6 +1,7 @@ #lang scribble/base @(require + scribble/core racket/list racket/file (only-in racket/string string-join) @@ -9,6 +10,17 @@ "../../scribble-api.rkt" "../../ragged.rkt") +@(define (spy-index-tag opname) + (define tag (make-generated-tag)) + (define index-tags (list (pyret opname) "spies")) + (make-index-element #f + (list (make-target-element #f '() `(idx ,tag))) + `(idx ,tag) + (cons opname (list "spies")) + index-tags + #f)) + +@(spy-index-tag "spy") @title[#:tag "s:spies"]{Spies} Spies are used for convenient display of values for print-style debugging. See diff --git a/src/libraries.scrbl b/src/libraries.scrbl index 88e5ef3..ace8124 100644 --- a/src/libraries.scrbl +++ b/src/libraries.scrbl @@ -29,6 +29,7 @@ This section contains information on libraries that come with Pyret. @include-section["trove/image-structs.js.rkt"] @include-section["trove/image.js.rkt"] +@include-section["trove/sound.js.rkt"] @include-section["trove/world.js.rkt"] @include-section["trove/gdrive-sheets.scrbl"] @include-section["trove/data-source.scrbl"] diff --git a/src/trove/image.js.rkt b/src/trove/image.js.rkt index 04996f0..01c82b9 100644 --- a/src/trove/image.js.rkt +++ b/src/trove/image.js.rkt @@ -721,14 +721,17 @@ by @pyret{dx} pixels, and then down by @pyret{dy} pixels. } @repl-examples[ - `(@{overlay-xy(0, 0, - square(30, "solid", "bisque"), square(50, "solid", "dark-green"))} + `(@{overlay-xy(square(30, "solid", "bisque"), + 0, 0, + square(50, "solid", "dark-green"))} ,(overlay/xy (square 30 "solid" "bisque") 0 0 (square 50 "solid" "darkgreen"))) - `(@{overlay-xy(30, 20, # Move green square right 30 and down 20 - square(30, "solid", "bisque"), square(50, "solid", "dark-green"))} + `(@{overlay-xy(square(30, "solid", "bisque"), + 30, 20, # Move green square right 30 and down 20 + square(50, "solid", "dark-green"))} ,(overlay/xy (square 30 "solid" "bisque") 30 20 (square 50 "solid" "darkgreen"))) - `(@{overlay-xy(-10, -20, # Move green square left 10 and up 20 - square(30, "solid", "bisque"), square(50, "solid", "dark-green"))} + `(@{overlay-xy(square(30, "solid", "bisque"), + -10, -20, # Move green square left 10 and up 20 + square(50, "solid", "dark-green"))} ,(overlay/xy (square 30 "solid" "bisque") -10 -20 (square 50 "solid" "darkgreen"))) ] @function[ diff --git a/src/trove/sound.js.rkt b/src/trove/sound.js.rkt new file mode 100644 index 0000000..6899b55 --- /dev/null +++ b/src/trove/sound.js.rkt @@ -0,0 +1,572 @@ +#lang scribble/base +@(require "../../scribble-api.rkt" + "../abbrevs.rkt" + (prefix-in html: "../../manual-html.rkt") + 2htdp/image + scribble/manual) + +@(append-gen-docs +'(module "sound" + (path "build/phase1/trove/sound.js") + (fun-spec (name "make-sound") (arity 2)) + (fun-spec (name "make-multi-channel-sound") (arity 2)) + (fun-spec (name "get-sound-from-url") (arity 1)) + (fun-spec (name "get-multi-channel-data-arrays") (arity 1)) + (fun-spec (name "get-channel-data-array") (arity 2)) + (fun-spec (name "sound-duration") (arity 1)) + (fun-spec (name "sound-sample-rate") (arity 1)) + (fun-spec (name "sound-num-channels") (arity 1)) + (fun-spec (name "sound-num-samples") (arity 1)) + (fun-spec (name "is-sound") (arity 1)) + (fun-spec (name "sounds-equal") (arity 2)) + (fun-spec (name "normalize-sound") (arity 1)) + (fun-spec (name "overlay") (arity 2)) + (fun-spec (name "concat") (arity 2)) + (fun-spec (name "overlay-list") (arity 1)) + (fun-spec (name "concat-list") (arity 1)) + (fun-spec (name "adjust-playback-speed") (arity 2)) + (fun-spec (name "set-sample-rate") (arity 2)) + (fun-spec (name "crop-by-time") (arity 3)) + (fun-spec (name "crop-by-index") (arity 3)) + (fun-spec (name "get-cosine-wave") (arity 1)) + (fun-spec (name "get-sine-wave") (arity 1)) + (fun-spec (name "get-tone") (arity 2)) + (fun-spec (name "get-note") (arity 3)) + (fun-spec (name "fade-in") (arity 1)) + (fun-spec (name "fade-in-by-index") (arity 2)) + (fun-spec (name "fade-in-by-time") (arity 2)) + (fun-spec (name "fade-out") (arity 1)) + (fun-spec (name "fade-out-by-index") (arity 2)) + (fun-spec (name "fade-out-by-time") (arity 2)) + ; (fun-spec (name "remove-vocals") (arity 1)) + (data-spec (name "Sound") (variants) (shared)) +)) +@(define Sound (a-id "Sound" (xref "sound" "Sound"))) +@(define AudioBuffer (a-id "AudioBuffer" (xref "audiobuffer" "AudioBuffer"))) + +@docmodule["sound"]{ + + The functions in this module are responsible for the creation and manipulation of various types of sounds. + + A sound file can be visualized as a set of channels, each corresponding to a sound wave. Depending on the + construction and framework, these channels serve varying purposes. In some cases, they may refer to the + left and right stereo sound stream. In others, each channel may correspond to a different instrument. In + many cases, a set of channels may contain very similar sounds that have been overlaid together to improve + quality. This library is supported by an underlying infrastructure of the WebAudioAPI. Therefore, it is + equipped to handle only .wav files for uploads and downloads. + + A sound wave can be represented in a numeric form as a set of amplitudes whose values can range from -1 + to +1. As a consequence, each sound in this library is characterized by a two-dimensional float array, + where each row corresponds to the sound wave in a given channel. + + The sound library is equipped with a widget that possesses media-player capabilities. Any object of the + type 'sound', will automatically invoke the widget. + + @section[#:tag "sound_DataTypes"]{Data Types} + @type-spec["Sound" (list)]{ + + This data type is the standard representation in this library for a sound. + Imitating a real-world sound, this object is characterized by the two-dimensional + float array of amplitudes, the sample rate, the duration, and the number of channels. + + The sample rate of a sound needs to be within the permissible range of 3000 to 384000. + + The duration is mentioned in terms of seconds. + + The number of channels is an integer. + + 'Sound' is the return type of many of the functions in this module; it + includes simple sounds, and also combinations or transformations of + existing sounds, like overlays, concatenations, and scaling. + } + + @section{Basic Sounds} + @function[ + "make-sound" + #:contract (a-arrow N (RA-of N) Sound) + #:return Sound + #:args (list '("sample_rate" "") + '("data_array" "") )]{ + Primary function to construct a sound from scratch using the given sample rate and data array. + + The sample rate of a sound needs to be within the permissible range of 3000 to 384000. + + The data array is a 1 - Dimensional float array, where the elements + correspond to the amplitudes of the sound wave present in that channel. + + } + + @repl-examples[ + `(@{soundA = S.make-sound(3000, [G.raw-array: 0.1, 0.2, 0.3, 0.1])}, @image[#:scale 0.5 "src/builtin/soundwave.PNG"]) + ] + + @function[ + "make-multi-channel-sound" + #:contract (a-arrow N (RA-of (RA-of N)) Sound) + #:return Sound + #:args (list '("sample_rate" "") + '("data_array" "") )]{ + Primary function to construct a sound from scratch using the given sample rate and data array. + + The sample rate of a sound needs to be within the permissible range of 3000 to 384000. + + The data array is a 2 - Dimensional float array, where the elements of each row of this array + corresponds to the amplitudes of the sound wave present in that channel. + } + + @repl-examples[ + `(@{soundB = S.make-multi-channel-sound(3000, [G.raw-array: + [G.raw-array: 0.1, 0.2, 0.3, 0.1], [G.raw-array: -0.2, -0.3, -0.1, 0.5]])}, + @image[#:scale 0.35 "src/builtin/multichannel.png"]) + ] + @function[ + "get-sound-from-url" + #:contract (a-arrow S Sound) + #:return Sound + #:args (list '("path" ""))]{ + Constructs a sound object from a given URL resource that points to a .wav audio file. + The URL is passed as a string parameter. + + } + @repl-examples[ + `(@{urlSound = + S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav")}, + @image[#:scale 0.35 "src/builtin/urlsound.png"]) + ] + + @section{Sound Properties} + @function[ + "get-multi-channel-data-arrays" + #:contract (a-arrow Sound (RA-of (RA-of N))) + #:return (RA-of N) + #:args (list '("sound" ""))]{ + Returns the two-dimensional / single-dimensional float array of the given sound. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + numArray = S.get-multi-channel-data-arrays(urlSound)} + + + @function[ + "get-channel-data-array" + #:contract (a-arrow Sound N (RA-of N) ) + #:return (RA-of N) + #:args (list '("sound" "") + '("channel" ""))]{ + Returns the single-dimensional float array of the given channel of a given sound. + + } + @examples{ + urlSound = + S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + numArray = S.get-channel-data-array(urlSound) + } + + @function[ + "sound-duration" + #:contract (a-arrow Sound N) + #:return N + #:args (list '("sound" ""))]{ + Returns the duration of a given sound in seconds. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + duration = S.sound-duration(urlSound) + } + @function[ + "sound-sample-rate" + #:contract (a-arrow Sound N) + #:return N + #:args (list '("sound" ""))]{ + Returns the sample rate (number of samples per second) of a given sound, which is necessarily the frequency of a sound in Hz. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + sampleRate = S.sound-sample-rate(urlSound) + } + @function[ + "set-sample-rate" + #:contract (a-arrow Sound N Sound) + #:return Sound + #:args (list '("sound" "") + '("sampleRate" ""))]{ + Sets / Updates the sample rate (number of samples per second) of a given sound, + and returns the new sound. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + newSound = S.set-sample-rate(urlSound, 10000) + } + @function[ + "sound-num-channels" + #:contract (a-arrow Sound N) + #:return N + #:args (list '("sound" ""))]{ + Returns the number of channels present in a given sound. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + numChannels = S.sound-num-channels(urlSound) + } + @function[ + "sound-num-samples" + #:contract (a-arrow Sound N) + #:return N + #:args (list '("sound" ""))]{ + Returns the number of samples or the size of the float array of the very first channel present in a given sound. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + numSamples = S.sound-num-samples(urlSound) + } + @function[ + "is-sound" + #:contract (a-arrow A B) + #:return B + #:args (list '("thing" ""))]{ + Validates if a given object is of a valid sound type. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + val = S.is-sound(urlSound) + } + @function[ + "sounds-equal" + #:contract (a-arrow Sound Sound B) + #:return B + #:args (list '("sound1" "") + '("sound2" ""))]{ + Specifies if two given sound objects are equal. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + urlSound2 = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + val = S.sounds-equal(urlSound, urlSound2) + } + @function[ + "normalize-sound" + #:contract (a-arrow Sound Sound) + #:return Sound + #:args (list '("sound" ""))]{ + Normalization is the process of transforming / scaling the amplitudes of a sound to + fall between the range of -1 to +1. + + Please note that the array returned by a sound that is not normalized will have amplitude values + that are outside the range of -1 to +1. However, while playing the sound, the internal + sound API automatically clamps the values that fall out of this range, in order to play it as a valid sound. + One can observe these non-compliant points being plotted as red dots on the widget. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + normSound = S.normalize-sound(urlSound) + } + + @section{Overlaying Sounds} + + @function[ + "overlay" + #:contract (a-arrow Sound Sound Sound) + #:return Sound + #:args (list '("sound1" "") + '("sound2" ""))]{ + Places one sound over another, and is the equivalent of an addition operation + between the amplitudes of a set of sounds. The function requires two + sound objects that need to be overlayed, and returns the result as a new sound. + + Given below are two individual sound objects, and the sound resulting from an + overlay of these two sounds. A practical application of this function is when we want to + overlay the sounds being played by different instruments for one one song. + + } + @repl-examples[ + `(@{soundA = S.get-note("A3", 0.5, 0.25)}, + @image[#:scale 0.5 "src/builtin/overlayone.PNG"]) + `(@{soundB = S.get-note("A1", 0.5, 0.25)}, + @image[#:scale 0.5 "src/builtin/overlaytwo.PNG"]) + `(@{soundC = S.overlay(soundA, soundB)}, + @image[#:scale 0.5 "src/builtin/overlayed.PNG"]) + ] + + @function[ + "overlay-list" + #:contract (a-arrow (RA-of Sound) Sound) + #:return Sound + #:args (list '("samples" ""))]{ + Places one sound over another, and is the equivalent of an addition operation + between the amplitudes of a set of sounds. The function requires a + list of sound objects that need to be overlayed, and returns the result as a new sound. + + Given below are two individual sound objects, and the sound resulting from an + overlay of these two sounds. A practical application of this function is when we want to + overlay the sounds being played by different instruments for one one song. + + } + @examples{ + soundA = S.get-note("A3", 0.5, 0.25) + soundB = S.get-note("A1", 0.5, 0.25) + soundList = [G.raw-array: soundA, soundB] + soundC = S.overlay(soundList) + } + @repl-examples[ + `(@{soundA = S.get-note("A3", 0.5, 0.25)}, + @image[#:scale 0.5 "src/builtin/overlayone.PNG"]) + `(@{soundB = S.get-note("A1", 0.5, 0.25)}, + @image[#:scale 0.5 "src/builtin/overlaytwo.PNG"]) + `(@{soundList = [G.raw-array: soundA, soundB] + soundC = S.overlay(soundList)}, + @image[#:scale 0.5 "src/builtin/overlayed.PNG"]) + ] + + @function[ + "concat" + #:contract (a-arrow Sound Sound Sound) + #:return Sound + #:args (list '("sound1" "") + '("sound2" ""))]{ + Concatenates different sounds together as one long sound. The function requires a + list of sound objects in the order in which they need to be concatenated. It then + returns the result as a new sound. + + Given below are two individual sound objects, and the sound resulting from a + concatenation of these two sounds. + + } + @repl-examples[ + `(@{soundA = S.get-note("A3", 0.5, 0.25)}, + @image[#:scale 0.5 "src/builtin/overlayone.PNG"]) + `(@{soundB = S.get-note("A1", 0.5, 0.25)}, + @image[#:scale 0.5 "src/builtin/overlaytwo.PNG"]) + `(@{soundC = S.concat(soundA, soundB)}, + @image[#:scale 0.5 "src/builtin/concated.PNG"]) + ] + + + @section{Concatenating Sounds} + @function[ + "concat-list" + #:contract (a-arrow (RA-of Sound) Sound) + #:return Sound + #:args (list '("samples" ""))]{ + Concatenates different sounds together as one long sound. The function requires a + list of sound objects in the order in which they need to be concatenated. It then + returns the result as a new sound. + + Given below are two individual sound objects, and the sound resulting from a + concatenation of these two sounds. + + } + @repl-examples[ + `(@{soundA = S.get-note("A3", 0.5, 0.25)}, + @image[#:scale 0.5 "src/builtin/overlayone.PNG"]) + `(@{soundB = S.get-note("A1", 0.5, 0.25)}, + @image[#:scale 0.5 "src/builtin/overlaytwo.PNG"]) + `(@{soundList = [G.raw-array: soundA, soundB] + soundC = S.concat(soundList)}, + @image[#:scale 0.5 "src/builtin/concated.PNG"]) + ] + + + @section{Scaling, and Cropping Sounds} + @function[ + "adjust-playback-speed" + #:contract (a-arrow Sound N Sound) + #:return Sound + #:args (list '("sample" "") + '("rate" ""))]{ + Constructs a sound that plays at the new rate given by the playback speed, such as 2X, 3X etc. + It takes a sound object and an integer number that specifies the factor by which the sound needs + to be sped up or slowed down. It then returns a resultant new sound object. + + } + + @examples{ + soundA = S.get-note("A3", 0.5, 0.25) + soundC = S.adjust-playback-speed(soundA, 3) + } + + + @function[ + "crop-by-time" + #:contract (a-arrow Sound N N Sound) + #:return Sound + #:args (list '("sample" "") + '("start" "") + '("end" ""))]{ + Crops a sound to a new sound based on the given start and end time. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + soundC = S.crop-by-time(urlSound, 1, 2) + } + @function[ + "crop-by-index" + #:contract (a-arrow Sound N N Sound) + #:return Sound + #:args (list '("sample" "") + '("start" "") + '("end" ""))]{ + Crops a sound to a new sound based on the given start and end index for the float array. + + } + @examples{ + urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + soundC = S.crop-by-index(urlSound, 20, 100) + } + @section{Starter Sound Waves and Tones} + @function[ + "get-cosine-wave" + #:contract (a-arrow N Sound) + #:return Sound + #:args (list '("duration" ""))]{ + Constructs a default cosine wave of the given duration with frequency of 440Hz. + + } + @examples{ + soundC = S.get-cosine-wave() + } + @function[ + "get-sine-wave" + #:contract (a-arrow N Sound) + #:return Sound + #:args (list '("duration" ""))]{ + Constructs a default sine wave of the given duration with frequency of 440Hz. + + } + @examples{ + soundC = S.get-sine-wave() + } + @function[ + "get-tone" + #:contract (a-arrow S N Sound) + #:return Sound + #:args (list '("key" "") + '("duration" ""))]{ + Constructs a sound tone of the given duration of an octave of the given key, such as A4, C8 etc. + Concatenation of several tones will result in one long set of beeps with no pauses in between. + + } + + @repl-examples[ + `(@{soundC = S.get-tone("A4", 0.5)}, + @image[#:scale 0.5 "src/builtin/overlayone.PNG"]) + ] + + @function[ + "get-note" + #:contract (a-arrow S N N Sound) + #:return Sound + #:args (list '("key" "") + '("durationOn" "") + '("durationOff" ""))]{ + Constructs a sound note of the given durationOn of an octave of the given key, such as A4, C8 etc, with a + tangible silence of the given durationOff in the end. Hence, it works like a typical press of a piano key. + Concatenation of several notes will result in a melody akin to playing notes on a piano. + + } + + @repl-examples[ + `(@{soundC = S.get-note("A4", 0.5, 0.25)}, + @image[#:scale 0.5 "src/builtin/getnote.PNG"]) + ] + + @section{Fading Sounds} + @function[ + "fade-in" + #:contract (a-arrow Sound Sound) + #:return Sound + #:args (list '("sound" ""))]{ + Progressively fades in a given sound from the beginning towards the end and returns the result as a new sound. + + } + + @repl-examples[ + `(@{urlSound = S.get-tone("C3", 5) + soundC = S.fade-in(urlSound)}, + @image[#:scale 0.35 "src/builtin/fadein.png"]) + ] + + @function[ + "fade-in-by-index" + #:contract (a-arrow Sound N Sound) + #:return Sound + #:args (list '("sound" "") + '("end" ""))]{ + Progressively fades in a given sound from the beginning towards the end index and returns the result as a new sound. + + } + + @repl-examples[ + `(@{urlSound = S.get-tone("C3", 5) + soundC = S.fade-in-by-index(urlSound, 50000)}, + @image[#:scale 0.35 "src/builtin/fadeinindex.png"]) + ] + @function[ + "fade-in-by-time" + #:contract (a-arrow Sound N Sound) + #:return Sound + #:args (list '("sound" "") + '("end" ""))]{ + Progressively fades in a given sound from the beginning towards the end time and returns the result as a new sound. + + } + + @repl-examples[ + `(@{urlSound = S.get-tone("C3", 5) + soundC = S.fade-in-by-time(urlSound, 3)}, + @image[#:scale 0.35 "src/builtin/fadeintime.png"]) + ] + + @function[ + "fade-out" + #:contract (a-arrow Sound Sound) + #:return Sound + #:args (list '("sound" ""))]{ + Progressively fades out a given sound towards the end and returns the result as a new sound. + + } + + @repl-examples[ + `(@{urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + soundC = S.fade-out(urlSound)}, + @image[#:scale 0.5 "src/builtin/fadeout.PNG"]) + ] + @function[ + "fade-out-by-index" + #:contract (a-arrow Sound N Sound) + #:return Sound + #:args (list '("sound" "") + '("start" ""))]{ + Progressively fades out a given sound from the start index towards the end and returns the result as a new sound. + + } + + @repl-examples[ + `(@{urlSound = S.get-tone("C3", 5) + soundC = S.fade-out-by-index(urlSound, 50000)}, + @image[#:scale 0.35 "src/builtin/fadeoutindex.png"])] + + @function[ + "fade-out-by-time" + #:contract (a-arrow Sound N Sound) + #:return Sound + #:args (list '("sound" "") + '("start" ""))]{ + Progressively fades in a given sound from the start time towards the end and returns the result as a new sound. + + } + + @repl-examples[ + `(@{urlSound = S.get-sound-from-url("http://bbcsfx.acropolis.org.uk/assets/07075055.wav") + soundC = S.fade-out-by-time(urlSound, 3)}, @image[#:scale 0.35 "src/builtin/fadeouttime.png"])] + +} \ No newline at end of file