Audio Streaming

A4: Audio Streaming  
 1/16  
A4: Audio Streaming  
Due Sunday by 11:59p.m.  
Points 200  
Available after Mar 13 at 12a.m.  
Last updated Apr. 2  
WARNING: Please define any additional macros or functions using libas.h (.c), we may need to  
override your particular as_*.h files for the automated testing, thank you!  
Table of Contents  
1. Introduction  
1.1. Background  
2. Basic Outline  
2.1. Setup  
3. Implementation  
3.1. Passive Application Architecture  
3.2. Active Application Architecture  
3.3. Request Format  
3.4. Dynamic buffering  
4. Tasks  
4.1 Server Tasks  
4.2 Client Tasks  
5. Grading scheme  
6. Software you’ll probably need and some tips about how to proceed  
6.1. Debugging the server or the client separately  
6.2. Ensuring files received by the client match the server  
6.3. Playing audio data (and how to install mpv)  
6.3.1. Listening in the labs  
6.3.2. Using real files  
7. Updates and clarifications (check back here as needed)  
8. Concluding remarks  
1. Introduction  
In this assignment, we will endeavor to develop a system of interconnected applications in one of the  
most often-used and fundamental configurations: a server and accompanying client(s) connected  
through sockets. This particular server will serve a small music (media file) library such that compatible  
clients will be able stream the files on the server’s library, save the files locally, or both.  
 A4: Audio Streaming  
 2/16  
In essence, streaming means that files can be requested from the server, and they are sent  
progressively and intermittently in chunks. Your implementation will be able to support many different  
media file types, including wav , mp3 , ogg , flac , etc.. The clients will use a dynamic buffer to receive  
as many packets as are available, as they become available, before redirecting them to their appropriate  
destination.  
1.1. Background  
For this assignment, multiple clients will be able to simultaneously stream available audio files from the  
server. The client and the server will be using sockets to communicate with each other while transferring  
text or binary data.  
Roughly speaking, the server is responsible for 2 things:  
1. Scanning and maintaining a “music library” –写Audio Streaming which for this assignment, will simply be maintaining a  
list of files with correct extensions from a specified library directory (default: library/ ). See Section  
6.3.2 to download a sample library that you may use to complete the assignment.  
2. Listen for clients and handle clients in their own child process. The server will wait and retrieve the  
exit status of all children under normal operation. Part of handling, is receiving and responding to  
requests.  
The client, on the other hand, will be responsible for:  
1. Making requests and receiving responses  
2. Managing an accompanying media player process named mpv (https://mpv.io/) , which is a free,  
open source, and cross-platform media player. For installation information, please see Section 6.3  
(https://q.utoronto.ca/#org2f62d44) .  
2. Basic Outline  
The majority of the design is provided to you in the starter code, and you are tasked with completing:  
1. Server side processing of received requests and associated responses, for:  
1. Listing all files  
2. Streaming particular files  
3. Setting up the server to listen on the specified port number  
2. Client-side sending of requests and receiving responses, for:  
1. Creating requests to list or stream files  
2. Receiving files with dynamic buffering  
3. Storing files as they are received  
4. (Separately from local storing) Piping files to an accompanying audio (media) player process  
The challenge in this assignment is ensuring that no bytes are lost, all bytes are transferred and received  
in the right order, and this is done without memory errors, as well as feeling comfortable working with an  
 A4: Audio Streaming  
 3/16  
already developed, and somewhat larger codebase. Throughout the starter code there are TODOs that  
must be completed. The functions and structs defined in the three header files as_client.h as_server.h  
and libas.h should not change, but you may add to these headers as you need, and change the values  
of different macros.  
2.1. Setup  
Follow the instructions carefully, so that we receive your work correctly.  
Your first step should be to log into MarkUs (https://markus.teach.cs.toronto.edu/2024-01/courses/16/) and  
navigate to the A4 assignment. Then, at the bottom of the assignment page, click on the button labelled  
"Add Starter Files to Repository".  
Then, open a terminal on your computer and do a git pull in your existing CSC209 MarkUs repository.  
Inside the repository, you should see a new folder name A4 containing the starter files for this  
assignment.  
Starter Files  
as_server.c: contains code that implements the audio streaming server. You should modify this  
file to complete the missing server features.  
as_server.h: the header file for functions and structs that are defined and used in as_server.c. Do  
not change.  
as_client.c: contains code that implements the audio streaming client. You should modify this file  
to complete the missing client features.  
as_client.h: the header file for functions that are defined in as_client.c. Do not change.  
libas.c: contains common code that are used by both the server and client. Do not change.  
libas.h: contains the function prototypes and structs that are used by both the server and client. Do  
not change.  
stream_debugger.c: a simple debugging program that can help you with debugging your streaming  
service. Do not change.  
Makefile: a script that is used to simplify compilation of your program. Do not change.  
Compilation  
You should use the following command in your A4 folder to compile your source code:  
make  
If your submission does not compile, you will receive a mark of 0. If the compilation displays warning  
messages, you will lose 10% of the total mark. It is your responsibility to ensure that your submission  
works on teach.cs.  
 A4: Audio Streaming  
 4/16  
Note that the Makefile will generate a file named port.mk which randomly assigns a default port number  
for you. This is to minimize the chance where you and another classmate accidentally use the same port  
number on teach.cs and negatively affect each other's work, e.g., if someone else is using your port  
number, you won't be able to start the server.  
After compilation, you can simply run the server as follows:  
./as_server  
Once the server is up and running, you can run the client to connect to the server:  
./as_client  
Note: while the starter code compiles, you will not be able to start the server until you complete the  
following:  
1. See Section 6.3.2 to set up your media library. Note: your server code does not need to handle an  
empty library, so you need to make sure your library has at least one valid audio file.  
2. The initialize_server_socket function.  
If at any point, you are tired of the excessive debug output, you may re-compile your code in release  
mode.  
make clean  
make release  
Remember that debugger symbols are not available in release mode, so you will not be able to gdb your  
program. Note that you will also need to run make clean before running make again to go back to debug  
mode.  
3. Implementation  
A passive process is on a machine known as the “server”, somewhere on the network. This process is  
bound to a particular port accompanying the network address for the machine.  
3.1. Passive Application Architecture  
The passive process’s main/original process sets up a socket for incoming connections and uses  
select to manage whether clients are connecting, or if it locally receives 'q' on stdin , implying the  
application should shut down. This is managed through the run_server function, which also re-scans the  
library after a certain amount of iterations of it’s main loop. It also passively checks for the termination of  
any existing child processes.  
Each new connection is managed in a child process by (immediately calling) handle_client , which  
receives the current layout of the library for the duration of the connection. It is responsible for receiving  
 A4: Audio Streaming  
 5/16  
and responding to requests from the client.  
Any requests to stream a file are written to the connected client in chunks of a maximum size:  
STREAM_CHUNK_SIZE .  
3.2. Active Application Architecture  
The “client” application is a single active process anywhere on the same network as the “server”,  
including the same machine. It features a small shell; see the client_shell function, featuring the  
following commands:  
1. list – to list the available files in the server’s library  
2. get <index> – to save a particular file to client’s library directory  
3. stream <index> – to stream a particular file without saving it  
4. stream+ <index> – to stream a particular file and save it to the client’s library  
5. help – to list the available commands  
6. quit – to quit the application  
These boil down to two types of requests:  
1. listing the available files.  
2. streaming a particular file.  
The client will receive the library list and then be able to request a file to be streamed by it’s library index.  
Commands 2-4 ultimately leverage stream_and_get_request with different arguments. This function will  
also use select to accomplish interleaved and non-blocking: reading from the socket connection, writing  
to the file-system and playing audio through the audio player. See Section 6.3, for more information on  
how to stream the received files. The client must successfully wait for this process to terminate as  
appropriate.  
3.3. Request Format  
List Request  
To send a list request to the server, the client would send the following text data:  
"LIST\r\n"  
The server will respond with a list of files in the format index:filename, one file per line, in text format.  
The list is returned as a single string with each file starting with an integer corresponding to it's index in  
the library, in reverse order. A colon separates the index and the file's name. Each entry is separated by  
a network newline "\r\n" (2 chars). For example, if the library contains the files "file1.wav",  
"artist/file2.wav", and "artist/album/file3.wav" in this order, the data sent to the client will be the following  
characters:  
 A4: Audio Streaming  
 6/16  
For example:  
"2:artist/album/file3.wav\r\n1:artist/file2.wav\r\n0:file1.wav\r\n"  
Stream Request  
To send a stream request to the server, the client would send a mixture of text and binary data. The text  
data is exactly the following:  
"STREAM\r\n"  
and a 4-byte binary data should follow, that represents the requested file index. For example, to stream  
file index 3, a full packet will look like this in hex:  
5354 5245 414d 0d0a 0000 0003  
S T R E A M \r\n |-- 3* -|  
*3 is in 32-bit big endian byte order.  
Note that the file index needs to be in network byte order, i.e., big endian byte order.  
If the file index exists, the server will respond by first sending the size of the file in network byte order,  
then sending a series of data chunks in the following format:  
[-- size of file --][ ----- data chunk 1 -----][ ----- data chunk 2 -----][ -- last data chunk -- ]  
<---- 4 bytes -----><------ 1024 bytes -------><------ 1024 bytes -------><-- up to 1024 bytes --->  
Note that the last chunk of data is can be less than 1024 bytes. For example, suppose the you send a  
text file with the content "hello world" using this protocol, the server response will look like this in hex:  
0000 000b 6865 6c6c 6f20 776f 726c 64  
|- 11 --| h e l l o _ w o r l d  
Hint: 0x0000000b is the decimal 11 in big endian, which is the size of the file.  
3.4. Dynamic buffering (client-side)  
First thing that you’ll need to take a look at is the realloc function documentation. Have a quick read of  
man realloc .  
The intended dynamic buffer is as follows, and shall receive full marks if successfully accomplished.  
Marks for alternative solutions will be subject to TA discretion. The simplest of which is a fixed size array  
on the stack (or the heap*), and this will receive 1/3 of the buffering grade, even if everything else is  
perfect. The keyword here is dynamic, so the realloc function will probably be your friend. Any solution  
that does not implement dynamically changing buffer sizes on the client can achieve a maximum grade,  
of at most, 90% on this assignment overall (only a third of the 15% awarded for dynamic buffering,  
because technically any non-static buffer is kind of dynamic).  
 A4: Audio Streaming  
 7/16  
The intended/aspirational implementation features two buffers: one to read directly from the socket --  
which will be of a fixed size ( NETWORK_PRE_DYNAMIC_BUFF_SIZE ) allowing at most this many bytes to be read  
from the socket each time select indicates it is ready. Then, a second and dynamic buffer to hold all the  
bytes that have been received by the client, but not yet been written to (both) the audio process pipe and  
library file (as needed). This includes what was read into the first buffer, and any bytes that you have no  
managed to write to the other targets yet. If the byte hasn’t been written out to each fd (or one of them if  
not doing stream+) yet, it should be held in this second buffer that dynamically grows, so that the first  
can read from the network again. All bytes from the first fixed buffer move through the second dynamic  
one.  
The first bytes of the dynamic buffer shall contain the next bytes that need to be written. So, upon  
reading the first, say, four bytes from the server, in your first fixed buffer, you then make your dynamic  
buffer 4 bytes in size and move those bytes into the second buffer in the order they were received.  
As more bytes are received from the network, your second buffer should expand to contain the additional  
bytes. “Simultaneously”, you should be writing to one or both of the audio and file destinations, starting  
from the beginning of the buffer. It is sufficient to simply try to write the entire contents of the buffer to  
either stream when they are ready for writing. But, this doesn't guarantee that they well all be, in fact,  
written. Use the return value of write to get the number of bytes actually written, to update which byte  
each stream (audio or file) will need next for an unbroken stream of data.  
Once you’ve finished writing data out of the second buffer, you can look to see how many of the first  
bytes in the buffer are no longer needed, as in, ones that have already been written to both (for stream+ ,  
otherwise it is either) the audio and file streams. You should then resize your buffer so that it no longer is  
taking up memory for those bytes. So if your dynamic buffer had 100 bytes in it, and you wrote 20 bytes  
to the audio process and 15 bytes to the local library file, you can remove 15 bytes from the buffer,  
making a new buffer of 85 bytes in length. Somehow, the audio process must still start from the correct  
byte offset in this example, so it will be "ahead" of the local file.  
Note that the server should be sending the file’s bytes starting with the first byte in the file, and  
ending with the last, even though network-byte-order is big-endian. Endianess pertains to  
contiguous data-types, not files or streams of bytes.  
How do you accomplish this “simultaneously”? Well we will use select , and read from the network if  
select indicates that the connected socket has bytes to be read, and write to either file descriptor if it  
indicates that either file descriptor can be written to. Then use select again in a loop.  
*Allocated once and free'd once. Allocating and freeing multiple times for one file transfer/stream will be  
considered dynamic, but only the described solution is guaranteed full marks. Any other dynamic  
solutions will receive at least 50% (7.5/15) and up to 100% (15/15) for this portion of the work.  
4. Tasks  
 A4: Audio Streaming  
 8/16  
For each function listed below, there are descriptions of the expected functionality above the function  
signature in the header files, or above the function body for helper functions. You should consult them for  
more information on how to implement each function. Hint: there are some helper functions that have  
already been implemented for you to simplify some tasks. Please take some time to understand how  
they work.  
For each function, there is a list of errors that you must handle. If any of the listed error occurred,  
print a relevant error message (not tested but the TA will look at it) then return -1 (tested). If you  
have temporarily-allocated resources (e.g., heap memory, file descriptors), you need to free/close  
them before returning.  
General set of errors you must always handle for all functions:  
Reading or writing to socket failed.  
Memory allocation, e.g., malloc , failed.  
4.1. Server Tasks  
For the server, you are responsible for implementing the following functions:  
initialize_server_socket  
int initialize_server_socket(int port)  
The purpose of this function is to initialize a server socket up to the point of listening for connections. You  
should use the existing helper functions to complete this task. On success, return the file descriptor used  
for listening to connections.  
Hints:  
You should be calling init_server_addr and set_up_server_socket to complete this function. Take  
some time to learn what they do.  
For set_up_server_socket , use MAX_PENDING for the num_queue argument.  
list_request_response  
int list_request_response(const ClientSocket * client, Library *library)  
The purpose of this function is to respond to the client request for listing the files in your media library.  
See Section 3.3 for more information. You should take a look at the Library struct definition in libas.h ,  
and understand the purpose and format of each member variable.  
Advices:  
Take a look at _free_library in libas.c and make sure you understand the data structure. Then,  
take a look at scan_library in as_server.c and make sure you understand how the object is  
initialized.  
Use the write_precisely function to simplify writing to socket.  
You may assume filenames do not include newline characters.  
 A4: Audio Streaming  
 9/16  
You may assume the media library is never empty.  
Hence, you need to ensure there is at least one audio file in your library folder, otherwise the  
server will not send any response and the client will hang.  
stream_request_response  
int stream_request_response(const ClientSocket * client, Library *library, uint8_t  
*post_req, int num_pr_bytes)  
The purpose of this function is to respond to the client request for streaming a particular file.  
The file is streamed in chunks of maximum STREAM_CHUNK_SIZE bytes. The client will be able to request a  
specific file by its index in the library. Please see Section 3.3 for more information.  
It is important to understand the post_req and num_pr_bytes parameters, because there is no guarantee  
whether the file_index data from the client is included in the request packet, or would come afterwards.  
This could happen:  
# only 1 byte out of the 4 bytes for file_index came with the stream request  
"STREAM\r\n" + 0x00  
The post_req parameter is a pointer to the end of the stream request data (after the \r\n ), and  
num_pr_bytes tells you how many bytes of data are available in post_req . In the above example,  
num_pr_bytes would be 1.  
To successfully complete this function, you must look at num_pr_bytes and determine how many more  
bytes you need to read from client_socket . If num_pr_bytes is 4, then you don't need read anymore.  
After you have the 4-bytes of data, you need to convert it from network byte order back to native byte  
order so that the value is correct. Hint: use ntohl .  
Errror Handling:  
If any of the following error occurred, print a relevant error message (not tested but the TA will look at it)  
then return -1 (tested). If you have temporarily-allocated heap memory, you need to free it before  
returning.  
The file index does not correspond to a valid media file, i.e., index out of range. (unfortunately, this  
will cause the client to hang, but it indicates that you sent an invalid request).  
Requested file does not exist (this can happen if someone deleted the file and your server hasn't  
realized it yet).  
num_pr_bytes is greater than 4 (think time: why is this an error?)  
4.2. Client Tasks  
For the client, you are responsible for implementing the following functions:  
get_library_dir_permission  
 A4: Audio Streaming  
 10/16  
int get_library_dir_permission(const char *library_dir, mode_t * perpt)  
Given the name of the library folder library_dir , return its permission through the output parameter  
perpt . If library_dir does not exist, create it with the permission 0700 . On success, return 0. You may  
assume perpt is never NULL .  
Hint: you will need to use the stat system call and mkdir system system call.  
Error Handling:  
Any of the system calls you made failed.  
Except in the case where the first time you call stat , the library folder does not exist.  
list_request  
int list_request(int sockfd, Library *library)  
Send a list request (please see Section 3.3) to the server and store the list of available audio files in the  
library object. Upon success, print the list of audio files by their file_index in ascending order, and return  
the number of files available in the library, e.g.:  
0: flac/teenage-ant-marching-band.flac  
1: m4a/atmospheric-eerie-song.m4a  
2: m4a/night-sessions.m4a  
3: mp3/dark-evil-piano.mp3  
4: mp3/encryption.mp3  
5: mp3/im-your-dj.mp3  
6: ogg/permsound.ogg  
7: ogg/rainbow-disco-bears.ogg  
8: wav/ac-guitar.wav  
9: wav/afx-study.wav  
10: wav/cha-cha-ender.wav  
11: wav/magic-harp.wav  
12: wav/wedidit.wav  
13: wav/weird-synth.wav  
Use the following format specifier to print each line to standard output: "%d: %s\n" .  
Advices:  
Take a look at _free_library in libas.c and make sure you understand what is heap-allocated and  
what isn't. Then, take a look at scan_library in as_server.c and make sure you understand how the  
object is initialized. You can re-use some of the logic to implement this function, e.g., instead of  
reading from the file system, you are reading and parsing the response packet from the server.  
Most of the response packet parsing is done for you in the function get_next_filename . Learn how to  
use the function.  
Error Handling:  
get_next_filename failed.  
start_audio_process  
 A4: Audio Streaming  
 11/16  
int start_audio_player_process(int *audio_out_fd)  
Create the audio player process, and redirect standard input to it. Use the AUDIO_PLAYER and  
AUDIO_PLAYER_ARGS macro constants for setting up arguments to exec .  
Notes:  
Because the audio player process sometimes takes a while to boot up, you should sleep for  
AUDIO_PLAYER_BOOT_DELAY seconds.  
If you are using WSL, the executable name will be "mpv.exe" instead of just "mpv". You can deal with  
this discrepancy by temporarily changing the macro AUDIO_PLAYER (defined in as_client.h ) from  
"mpv" to "mpv.exe". However, do not check this change in.  
Error Handling:  
Any of the system calls you made in the parent process failed.  
send_and_process_stream_request  
int send_and_process_stream_request(int sockfd, uint32_t file_index, int audio_out_fd, int  
file_dest_fd)  
This is the meat of this assignment. The purpose of this function is to simultaneously receive data from  
the server, save data to a file, and steam data to the audio process. You are required to use select to  
accomplish this feat. Failure to use select will result in 0 for this function.  
Please see Section 3.3 for information on how to send a stream request packet, and what the server  
response looks like.  
Before returning from this function (failure or success), you must close the two file descriptors,  
audio_out_fd and int file_dest_fd .  
Advices:  
Because an audio file can be extremely large (e.g., 4GB), it would be infeasible to preallocate a  
buffer for the entire incoming file. Therefore, you need create a dynamic buffer for receiving data and  
writing data to one or both file descriptors. Please see Section 3.4 for more information on how to  
implement this. However, you may create the entire buffer in memory just as a intermediate step to  
get things working, before attempting dynamic buffering.  
Use the timeout macro constants for setting up select 's timeout argument: SELECT_TIMEOUT_SEC and  
SELECT_TIMEOUT_USEC .  
Error Handling:  
Both file descriptors, audio_out_fd and int file_dest_fd , are invalid, i.e., -1.  
If you receive more data than what the specified file size is, print an error message (not auto-tested,  
but we will look at your code) and disregard the extra bytes (do not return -1).  
 A4: Audio Streaming  
 12/16  
5. Grading scheme  
The following is a breakdown of the grading scheme for this assignment:  
10% style  
30% memory management (valgrind testing)  
15% server/client each  
20% server functionality  
25% client functionality (independent of dynamic buffer)  
15% Implementation (TA inspection) of client-side buffering  
Max 5% if not dynamic  
6. Software you’ll probably need and some tips about  
how to proceed  
6.1. Debugging the server or the client separately  
The basic utility known as netcat or nc (see man nc ) is ideal for sending or receiving raw text  
(amongst other things with some work). You can use nc to setup a dummy server that you can view  
requests with, and type back responses to your client (locally on your machine, bound to port 3456) like  
this:  
nc -v -C -l -p 3456  
This sets up verbose printing, network newlines to be sent everytime you hit enter ( \r\n not just \n ,  
notice that our requests make use of this), and listening – essentially a passive network process.  
You can configure an active netcat process as follows, more commonly referred to as a client to connect  
to this server with:  
nc -v -C localhost 3456  
Try running both, in seperate terminals, side-by-side and start entering data into either side. Notice how it  
pops up in the other terminal. Once you get your client or server implementations working, you can swap  
out one of these netcat programs for your solution, e.g. connect your as_client to an nc server.  
Note, you can also run the server on teach.cs, and then connect by replacing localhost with  
teach.cs.utoronto.ca, then you can run the server after ssh'ing into teach.cs, and the client locally or on  
teach.cs, and connect to the server that is running on teach.cs!  
I recommend implementing the server first, since a lot of heavy lifting has already been done for you in  
this application. Then you can try running something like this before implementing the client, to see that it  
is possible:  
 A4: Audio Streaming  
 13/16  
echo -e "STREAM\r\n\x00\x00\x00\x02" | nc -C <server-address> <port> | dd bs=1 skip=4 | tee filenam  
e.wav | mpv -  
This should allow you to retrieve the file with index 2 (notice the last of 4 hex bytes is 0x02 in the echo  
message) from the server, skip the first 4 bytes of response (the file’s size as an unsigned int sent by the  
server), save it to filename.wav and play it through mpv . This solution may not produce a perfect  
filename.wav depending on how you kill the line of commands. But, in effect, the client’s most difficult  
task can be accomplished through this line of shell script with fairly standard programs. Nonetheless we  
are going to do the dynamic buffering as an exercise and a demonstration of how one might buffer  
media. The portion of \x00\x00\x00\0x02 above, indicates 4 hexadecimal numbers. Network byte order  
is big-endian, so the above is the number two, expressed as a 32-bit big-endian integer.  
6.2. Ensuring files received by the client match the server  
The command line utility diff will report when two files differ, so use this to check that the received file  
is the same as the one on the server. If they are identical, it will print nothing! Alternatively cmp also  
provides good output. These are sufficient tools, but are hard to use for binary files. Remember that you  
can send and recieve (though not actually play anything through mpv) any file data you like, as long as  
the extension is compatible (see libas.h). This means you can debug with files containing only ASCII text  
in them, and use diff tools like diff or cmp, or diff within your IDE to see where things went wrong.  
While you are working on receiving files, changing the size of buffers, or more specifically, the number of  
bytes you read or write at once, will help debug the process of sending and receiving data. Stepping  
through your code with smaller buffers and test files will really help.  
As a starting point, it may be helpful to create a dummy file in text format to test that file transfer is  
working. You will need to rename the file to have the extension .wav , otherwise the server will not pick it  
up. You should use the get command on the client to attempt transferring the file.  
6.3. Playing audio data  
To actually hear the output of the audio files (video files were not tested, I suspect you might need to  
enlarge some of the buffers for performance reasons, but it should probably work too, see  
SUPPORTED_FILE_EXTS and let me know on Piazza if you looked into it), you’ll be piping data into software  
that can decode any compression used to store the file and then relay the correct data to the audio driver  
system. The driver system is not consistent between Linux, Mac and Windows, so we will use the mpv  
(https://mpv.io/) project to do all this work in a cross-platform way, and allow us to experiment using  
files with arbitrary compressions/encodings/extensions (not all of which are compatible with streaming,  
but .mp3 and .mp4 containers should represent stuff that will work if you get this far). Note that you can  
use mpv's controls when you've exec'd the process (try using the arrow keys while music is playing).  
tldr: A good first step overall is installing mpv . You can use mpv to play media files directly through  
stdin by giving it the argument - , as in mpv -  
 A4: Audio Streaming  
 14/16  
On the teach.cs machines, the basic command aplay is sufficient to process .wav files, and is a good  
starting place if you need to limit output, but you should have access to mpv on the teach.cs machines  
as well, and this will give you some consistency across platforms. Install mpv on your local machine,  
make sure it is accessible “via command line” (i.e. it’s on your PATH ) and then switch to the mpv -specific  
macros in as_client.h .  
Most linux package managers provide mpv e.g. sudo apt install mpv but note if you're using the  
windows linux subsystem (WSL), you're probably better off installing mpv outside of the linux subsystem  
and linking to the executable (the application called mpv in the installation) on a correct path to be  
accessible to a bash terminal in WSL. It will take some digging to figure out. This github repo  
(https://github.com/jnozsc/mpv-nightly-build) hosts mac builds, and is linked to from mpv's main  
installation page. You'll still need to be able to access mpv from your shell's path.  
While this will be great when everything is working, we’ve also included the code for something called  
stream_debugger , which will help you debug what you are sending to the external process and when.  
6.3.1. Listening in the labs  
Please avoid disturbing others with playing any audio out of the speakers in the lab. There are  
headphone jacks on the lab machines, on the front of the desktop box (top jack is only microphone,  
bottom is headphones with optionally connected mic, use this with standard headphones):  
As well as an audio out (green; largely equivalent to the headphones, but perhaps slightly different  
voltages for additional amplification) on the back of the desktop box:  
 A4: Audio Streaming  
 15/16  
6.3.2. Using real files  
We have made a small library of free sounds for your testing (downloaded from https://freesound.org/  
(https://freesound.org/) ). You can get a zipped copy here  
(https://q.utoronto.ca/courses/337029/files/31062827?wrap=1)  
(https://q.utoronto.ca/courses/337029/files/31062827/download?download_frd=1) . You should unzip this file  
in the A4 folder. Make sure the library folder is created after unzipping. WARNING: do not check in  
the audio files into your repository. We have add a .gitignore file to make sure you do not  
accidentally make this mistake.  
If you are working on the teach.cs machine, you can also make a copy of the audio library from our  
course directory:  
# you should be in your A4 folder  
cp -R /u/csc209h/winter/pub/a4/library .  
You can also check out this article (https://creativecommons.org/about/arts-culture/arts-culture?resources/legalmusicforremixing/) for a bunch of free choices to add to your library, ccMixter had some  
fun things.  
7. Updates and clarifications (check back here as  
needed)  
Mar 20, 5:31pm PST (jsun): updated starter code and solution to be more const-correct. Removed  
the need to implement get_file_request , stream_request , and stream_and_get_request for the starter  
code.  
Mar 24, 7:47pm PST (jsun): updated handout to clarify that you may assume the server's media  
library is never empty.  
 A4: Audio Streaming  
 16/16  
Apr 1, 12:45pm EST (dee): Warning pertaining to avoiding adding to as_client.h and as_server.h.  
Use libas.h and libas.c for any macros or additional functions that need to be accessible across  
multiple .c files.  
Apr 2, 1:28pm EST (dee): There was some concern that the above meant that you could not change  
macro values, particularly AUDIO_PLAYER and/or AUDIO_PLAYER_ARGS in as_client.h. You can and should  
have fun changing all the macros as you like, but your solution will be tested using the values from  
the starter, as well as some additional values of AUDIO_PLAYER and AUDIO_PLAYER_ARGS to use a  
testing executable for testing the bytes going to the audio player process.  
Apr 2, 1:46pm EST (dee): I've made a variety of references to our dynamic buffer being a circular  
buffer. This is not a conventional use of this term bizzare use of a term I frankly have been otherwise  
familiar with: circular buffers tend to refer to buffers of a fixed size that write over the beginning of a  
buffer when they are filled (and then write in a circle). My usage of the term is based in circles being  
a useful expression of a line with infinite length and multiple entry/exit points and a general  
disposition towards using circular models over more linear dimensions.  
8. Concluding remarks  
This has been one of the most impressive instances of CSC209 in my memory, all things considered.  
This is understood to be a difficult course, and even making it through to the point of reading the end of  
this document should be understood as an accomplishment.  
As you complete this assignment savour your new knowledge and pat yourself on WX:codinghelp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值