One of the goals of the CS211 final project is for you to use the knowledge you have learned this semester about MATLAB programming to create a non-trivial software system. As you develop your sound editor, you will need to refer back to previous lessons and previous lab assignments. The information described below covers MATLAB commands and digital sound concepts that we have not covered previously this semester. You will need to use the MATLAB help system extensively to research more details about the commands listed below. If you are confused about something after researching a MATLAB command, please ask your instructor for additional help.
Each sound editor project will hopefully be unique. The information below is provided as generic help to all students. You are not required to use all of the commands listed below. Use only ones appropriate for your sound editor implementation.
Digital Sound (a short primer)
A digital sound wave is a sampling of a real-world, analog sound wave over time. A sound wave is digitized by sampling the magnitude of the sound wave at discrete intervals. If we sample the magnitude too many times per second, we end up with redundant information and enormous amounts of data. If we sample the magnitude too few times per second, we cannot recreate the sound wave accurately from the digital data and this results in poor output sound quality.
Most music is sampled at 44,100 Hz (Hertz - cycles per second).
Data
MATLAB can read and write digital sound data in WAV file formats. (If you would like to edit a sound file that is in a different file format, use the http://www.goldwave.com/ sound editor to convert it to a WAV file by loading the file and then using Save As... .)
It is very important that you use small sound files for your initial software testing. After your software works correctly on small test cases, you will need to test it on larger sound files, but do this only after it works on small files.
The file Test_wav_files.zip contains some good, test case, sound files.
MATLAB
commandDescription wavread() [Magnitudes, Sample_rate, Number_of_bits_per_sample] = wavread(File_name); Reads a wav file into memory and returns three values:
- An array that contains the digital wave form, i.e., the magnitude of the sound wave over time. Each magnitude value is in the range [-1, 1]. If the sound file contains stereo waves, the array contains two columns, one column of data for each output speaker.
- A scalar value that specifies the digitize rate of the sound, i.e., the number of samples per second.
- A scalar value that specifies the number of bits used to represent each magnitude value. (This can be ignored because MATLAB always converts the magnitude values to percentages in the range [-1, +1].
wavwrite() wavwrite(Magnitudes, Sample_rate, Number_of_bits_per_sample, File_name) Creates a wav file on your hard drive. It is recommended that you use the same values for Sample_rate and Number_of_bits_per_sample that were read from the original input file. (Hopefully the Magnitudes array will be different -- that's why you are writing your sound editor, to modify the sound data.)
Playing sounds
MATLAB has an "audio player" object that will play your sound data. To use it, do the following.
- Create an instance of an "audio player" and remember its "handle".
- Using this "handle," you can start and stop the playback of your sound data. You can also have a callback function be called when the playing begins, when it ends, and at set intervals in-between.
There are many ways to implement the audio playback. You may not need all of the commands listed below.
MATLAB
commandDescription audioplayer() Audio_player_handle = audioplayer(Magnitudes, Sample_rate, Number_of_bits_per_sample); The three input arguments are the same values returned by wavread().
play() play(Audio_player_handle, Start_magnitudes_array_index) Plays the sound data starting with the sample at the specified index. For example, if your sound array contains 500,000 values, and you want to start the sound playing at the 20,000 value, send 20,000 as the second argument.
stop() stop(Audio_player_handle) Stops the sound from playing.
pause() pause(Audio_player_handle) Pauses the sound from playing. It will resume from this current place if a resume() command is issued.
resume() resume(Audio_player_handle) Resumes the playing of the sound.
isplaying() isplaying(Audio_player_handle) Returns true if the player is currently playing a sound; returns false otherwise.
disp() disp(Audio_player_handle) Displays information about the audio player object.
get() get(Audio_player_handle, 'PropertyName') Gets a property value of an audio player object. The most useful property you might want to "get" is the current position of the "play head" (i.e., which sample is currently being played). For example,
Current_index_into_magnitudes_array = get(Audio_player_handle, 'CurrentSample');
set() set(Audio_player_handle, 'PropertyName', PropertyValue) Sets a property value of an audio player object. Some of the properties you may want to "set" include:
set(Audio_player_handle, 'TimerPeriod', Seconds_per_play_head_update); set(Audio_player_handle, 'TimerFcn', {@Move_play_head, handles} ); set(Audio_player_handle, 'StopFcn', {@Audio_has_stopped, handles} ); set(Audio_player_handle, 'StartFcn', {@Audio_is_starting, handles} );The last three set commands are setting callback functions (notice the cell array as the value argument). The @ sign in front of the names means "handle of." The handles variable is the handles structure that is maintained and sent to all GUI callback functions.
If you set the TimerFcn property, the specified function will be called at the TimerPeriod interval you specify. For example, if you set the TimerPeriod property value to 0.1, the callback function will be called once every 1/10th a second. You can use this to update the position of the "play head" as the sound plays.
If you set the StartFcn property, the specified function will be called once before the sound starts playing.
If you set the StopFcn property, the specified function will be called once after the sound stops playing.
If you set a callback function property, you must write the callback function. It must have 3 arguments, just like your other GUI callback functions. For example:
function Move_play_head(obj, event, handles) ...IMPORTANT: If your audio player callback functions produce any graphical output, you must set the HandleVisibility property to 'on' for all figure and all axes graphic objects. Theoretically you can set these values using the guide tool, or you can set them in your *_OpeningFcn() function. Setting them in the *_OpeningFcn() function produced more consistent behavior for Dr. Brown's sound editor.
User Interactions
To capture the pressing and releasing of mouse buttons and the movement of your mouse, you must set callback functions for your GUI figure window. It is best to do this in your *_OpeningFcn() function. The following commands register callback functions for mouse events. (Change the variable names appropriately for your program.)
set(handles.Main_figure_handle, 'WindowButtonDownFcn', {@Window_button_pressed, handles} ); set(handles.Main_figure_handle, 'WindowButtonMotionFcn', {@Window_button_motion, handles} ); set(handles.Main_figure_handle, 'WindowButtonUpFcn', {@Window_button_released, handles} );For each callback function that you set, you have to write a callback function to process the corresponding event. For example, given the commands above, you would implement the following function:
function Window_button_pressed(hObject, eventdata, handles) ...When a mouse event callback function is called, you can use appropriate get() function calls to get information about the event. Some possible useful information includes:
Location = get(Axes_handle, 'CurrentPoint');
Location = get(Figure_handle, 'CurrentPoint');
MouseButtonType = get(Figure_handle, 'SelectionType');
Key = get(Figure_handle, 'CurrentCharacter');Currently_selected_object = gco();
IMPORTANT: If you get the location of the cursor using the figure handle, you will get the cursor location relative to the entire window. If you get the cursor location using an axes handle, you get the cursor location relative to the axes.
Useful Equations
The following equations might be helpful for most implementations. Check the math to make sure you understand what the equations are doing. (Try some example numbers and see if you get expected results.) Basically you need to be able to convert between time (in seconds) and array indexes.
Assuming the following variables:
Magnitudes - an array containing a digital sound wave
Sample_rate - a scalar that is the number of samples per second in the sound
Sample_time - a scalar that is some time, in seconds, between 0 and the end of the sound
Sample_index - a scalar that is some index position in the Magnitudes array
Total_play_time = length(Magnitudes) / Sample_rate;
Sample_index = round((Sample_time / Total_play_time) * length(Magnitudes));Sample_time = (Sample_index - 1) / Sample_rate;
Efficiency Concerns
Digital sound data requires large amounts of data, even for small sounds. For example, a 12 second sound, sampled at 44,100 Hz, contains over 1/2 million values. MATLAB can plot these large arrays, but not very quickly. If you want to plot a sound wave quickly, consider plotting only intermediate values in the sound array. FYI: a Dell D600 computer, can display approximately 10,000 sample values fairly quickly. Cadets with older computers may need to plot even fewer values.