%% Write and Read Binary Files
% Create a binary file with a custom header using the |dsp.BinaryFileWriter| 
% System object. Write data to this file. Read the header and data using the 
% |dsp.BinaryFileReader| System object.
%% Write the Data
% Specify the file header as a structure with the following fields:
%
% * DataType set to |double|.
% * Complexity set to |false|.
% * FrameSize (number of rows in the data matrix) set to 150.
% * NumChannels (number of columns in the data matrix) set to 1.
%
% Create a |dsp.BinaryFileWriter| object using this header. The object 
% writes the header first, followed by the data, to |ex_file.bin|. The data 
% is a noisy sine wave signal. View the data in a time scope.

L = 150;
header = struct('DataType','double','Complexity',false,'FrameSize',L,'NumChannels',1);
writer = dsp.BinaryFileWriter('ex_file.bin','HeaderStructure',header);

sine = dsp.SineWave('SamplesPerFrame',L);
scopewriter = timescope(1,'YLimits',[-1.5 1.5],'SampleRate',...
    sine.SampleRate,'TimeSpanSource','Property','TimeSpan',1);

for i = 1:1000
    data = sine() + 0.01*randn(L,1);
    writer(data);
    scopewriter(data)
end
%% 
% Release the writer so that the reader can access the data from this file.
%%
release(writer);
%% Read the Data
% Read the data from the binary file, |ex_file.bin|, using the |dsp.BinaryFileReader|
% object. The file contains the header data followed by the actual data.
% The object reads the binary data until the end of file is reached.
% Specify the header to the reader using the |HeaderStructure| property of the reader object.
%
% If the exact header is not known on the reader side, you must at least 
% specify the prototype of the header. That is, the number of fields, and the 
% data type, size, and complexity of each field in the prototype must match 
% with the header data written to the binary file. When the |readHeader| 
% function reads the data from the binary file, the function extracts the 
% header information based on how the fields are specified in the header prototype.
% For example, a header field set to |'double'| on the writer side can be 
% specified as any string of 6 characters on the reader side. 
% The |readHeader| function reads this field as a string of 6 characters 
% from the binary file, which matches with |'double'|.
%
%%
headerPrototype = struct('DataType','datype','Complexity',false,'FrameSize',1,'NumChannels',10);
reader = dsp.BinaryFileReader(...
    'ex_file.bin',...
    'HeaderStructure',headerPrototype);
headerReader = readHeader(reader)

%%
% The header data extracted by the |readHeader| function is assigned to the 
% corresponding properties of the |reader| object.
reader.IsDataComplex = headerReader.Complexity;
reader.DataType = headerReader.DataType;
reader.NumChannels = headerReader.NumChannels;
reader.SamplesPerFrame = headerReader.FrameSize;

%%
% Initialize a scope on the reader side to view the extracted binary file
% data.
scopereader = timescope(1,'YLimits',[-1.5 1.5],'SampleRate',...
    sine.SampleRate,'TimeSpanSource','Property','TimeSpan',1);

%%
% The data is read into a single channel (column) containing multiple frames, where 
% each frame has 150 samples. View the data in a time scope.
while ~isDone(reader)
    out = reader();
    scopereader(out)
end
release(reader);
release(scopereader);

%% 
% Set the reader to read data in frames of size 300. Verify that the data 
% read matches the data written to the file.
reader.SamplesPerFrame = 300;
while ~isDone(reader)
    out = reader();
    scopereader(out)
end
release(reader);
%% 
% Even when the reader reads data with a different frame size, the output 
% in both time scopes matches exactly.

%% 
% Copyright 2012 The MathWorks, Inc.