this is the seventh and last section of the {uxn tutorial}! here we talk about the devices in the varvara computer that we haven't covered yet: audio, file, and datetime.
this should be a light and calm end of our journey, as it has to do less with programming logic and more with the input and output conventions in these devices.
let's begin!
# the file device
the file device in the varvara computer allows us to read and write to external files.
* the success short stores the length of the data that was successfully read or written, or zero if there was an error
* the name short is for the memory address where the filename (null-terminated, i.e. with a 00) is stored
* the length short is the amount of bytes to read or write: don't forget that the program memory is ffff plus 1 bytes long, and that the program itself is stored there!
* setting the append byte to 01 makes write append data to the end of the file. when the append byte has the the default value, 00, write overwrites the contents from the start
* the path of the file, written as a labelled string of text in program memory and terminated by a 00 - this path would be relative to the location where uxnemu is ran.
* the amount of bytes that we want to read from the file: it's okay if this number is not equal to the size of the file; it can be smaller or even greater.
* the label for a reserved section of program memory where read data will be stored
and that's it!
we can use a structure like the following, where the filename and reserved memory are under a label, and the load-file subroutine under another one:
```
@load-file ( -- )
;file/name .File/name DEO2 ( set address of file path )
#00ff .File/length DEO2 ( will attempt to read 255 bytes )
note that for the filename we are using the raw string rune (") that allows us to write several characters in program memory until a whitespace is found.
in this example we are writing a character to the console according to the success short being zero or not, but we could decide to take any action that we consider appropriate.
also, in this example we are not really concerned with how many bytes were actually read: keep in mind that this information is stored in File/success until another read or write happens!
it's important to remember that, as always in this context, we are dealing with raw bytes.
not only we can choose to treat these bytes as text characters, but also we can choose to use them as sprites, coordinates, dimensions, colors, etc!
the only differences, beside the use of File/write instead of File/read, are the file length and the comparison for the success short: in this case we know for sure how many bytes should have been written.
programs for the varvara computer written by 100r tend to have the ability to read a "theme" file that contains six bytes corresponding to the three shorts for the system colors.
similar to how in the screen device we can draw by pointing to addresses with sprite data, in the audio devices we will be able to play sounds by pointing to addresses with audio data ("samples").
stretching the analogy: similar to how we can draw sprites in different positions on the screen, we can play our samples at different rates, volume, and envelopes.
in the context of varvara, we can understand them as multiple unsigned bytes (u8) that correspond to amplitudes of the sound wave that compose the sample.
a "playhead" visits each of these numbers during a specific time, and uses them to set the amplitude of the sound wave.
similar to how we have dealt with sprites, and similar to the file device discussed above, in order to set a sample in the audio device we just have to write its address and its length:
the pitch byte makes the sample start playing whenever we write to it, similar to how the sprite byte performs the drawing of the sprite when we write to it.
normally we will want to loop the sample in order to generate a tone based on it. only when the sample is long enough it will make sense to not loop it and play it once.
the volume byte is divided in two nibbles: the high nibble corresponds to the volume of the left channel, and the low nibble corresponds to the volume of the right channel.
therefore, each channel has 16 possible levels: 0 is the minimum, and f the maximum.
the following would set the maximum volume in the device:
```
#ff .Audio0/volume DEO ( set maximum volume in left and right )
ADSR stands for attack, decay, sustain, and release. it is the name of a common "envelope" that modulates the amplitude of a sound from beginning to end.
in the varvara computer, the ADSR components work as follows:
* the attack section is the time that it takes to bring the amplitude of the playing sound from 0 to 100%.
* then, the decay section is the time that it takes to bring the amplitude from 100% to 50%
* then, during the sustain section the amplitude is kept at 50%
* and finally, in the release section the amplitude goes from 50% to 0%.
the following program has now the five components we need in order to play a sound: a sample address, its length, the adsr durations, the volume, and its pitch!
i invite you to experiment modifying the ADSR values: how does the sound change when there's only one of them? or when all of them are small numbers? or with different combinations of durations?
once we have set up our audio device with a sample, length, ADSR envelope and volume, we could play it again and again by (re)writing a pitch at a different moment; the other parameters can be left untouched.
what if you implement playing different pitches by pressing different keys on the keyboard? you could use our previous examples, but writing a pitch to the device instead of e.g. incrementing a coordinate :)
or what about complementing our pong program from {uxn tutorial day 6} with sound effects, having the device playing a note whenever there's a bounce of the ball?
or what if you use the screen vector to time the repetitive playing of a note? or what about you have it play a melody by following a sequence of notes? could this sequence come from a text file? :)
## playback information
the audio device provides us with two ways of checking during runtime the state of the playback:
* the position short
* the output byte
when we read the position short, we get the current position of the "playhead" in the sample, starting from 0 (i.e. the playhead is at the beginning of the sample) and ending at the sample length minus one.
the output byte allows us to read the amplitude of the envelope. it returns 0 when the sample is not playing, so it can be used as a way of knowing that the playback has ended.
the idea of having four audio devices is that we can have all of them playing at once, and each one can have a different sample, ADSR envelope, volume, and pitch.
this gives us many more possibilities:
maybe in a game there could be a melody playing in the background along with incidental sounds related to the gameplay?
maybe you can build a sequencer where you can control the four devices as different tracks?