function f = sound_create(type, p, opt)

% sound_create - create XBAT sound structure
% ------------------------------------------
%
% opt = sound_create
%
%   f = sound_create(type, in, opt)
%
% Input:
% ------
%  type - sound type
%  in - sound source input
%  opt - sound create options
%
% Output:
% -------
%  f - sound structure

%--------------------------------
% Author: Harold Figueroa
%--------------------------------
% $Revision: 6176 $
% $Date: 2006-08-15 19:41:57 -0400 (Tue, 15 Aug 2006) $
%--------------------------------

% NOTE: eventually we could point to a file that configures recording

%-------------------------------------------------------------------
% HANDLE INPUT
%-------------------------------------------------------------------

%--
% set and possibly return default options
%--

% NOTE: at the moment only attribute search is an options

if nargin < 3

	opt.attributes = 1;
	
	if nargin < 1
		f = opt; return;
	end
	
end

%--
% output empty sound on empty type
%--

if nargin && isempty(type)
	f = sound_create_in; return;
end

%--
% check and normalize sound type
%--

[ignore, type] = is_sound_type(type);

if isempty(type)
	error('Unrecognized sound type.');
end

%--
% get sound source info through dialog
%--

if nargin < 2

	switch type
		
		%--
		% get sound files
		%--
		
		case {'file', 'file stream'}
			
			%--
			% get sound files
			%--

			[fn, pn] = uiget_sound_files(type);

			% NOTE: return empty on cancel

			if isempty(pn)
				f = []; return;
			end

			%--
			% pack output into file path strings
			%--

			% NOTE: the cell output condition only happens for type 'file'

			if iscell(fn)
				for k = 1:length(fn)
					p{k} = [pn, fn{k}];
				end
			else
				p = [pn, fn];
			end
		
			
		case 'stack'
			
			%--
			% there are no files, "p" is an array of sounds
			%--
			
			% NOTE: this is where you could present a dialog to pick sounds
			% from a library or something...
			
			p = [];
			
		%--
		% configure recording
		%--
		
		case 'recording'
		
			% TODO: consider how this value could be used for recording
			
			% NOTE: the configuration values could be obtained from a file

			p = [];
						
			result = recording_dialog;

			recording = result.values;

			if isempty(recording)
				f = []; return;
			end

			% NOTE: append random identifier for input object
			
			recording.id = ['XBAT_ANALOG_INPUT_', int2str(1000000*rand)];
			
		case 'variable'	
			
			p = get_workspace_dialog();
		
			
	end

end

%--
% handle creation of multiple file sounds recursively
%--

if iscellstr(p) && strcmp(type, 'file')
	
	opt.attributes = 0; % NOTE: disable searching for attributes for efficiency
	
	%--
	% try to generate a sound for each file
	%--
	
	f = [];
	
	for k = 1:length(p)

		try
			if isempty(f)
				f = sound_create('file', p{k}, opt);
			else
				f(end + 1) = sound_create('file', p{k}, opt);
			end
		catch
			disp(['WARNING: Unable to create sound from file ''', p{k}, '''.']);			
		end

	end

	return;

end

%-------------------------------------------------------------------
% CREATE SOUND
%-------------------------------------------------------------------

%--
% create workspace variable struct
%--

if strcmpi(type, 'variable')
	
	name = p; p = [];
	
	p.name = name; p.value = evalin('base', name);
	
end

%--
% parse input location to match uigetfile output
%--

if ~isempty(p) && ischar(p)
	
	[p, f1, f2] = fileparts(p);

	p = [p, filesep];

	if strcmpi(type, 'File')
		fn = [f1, f2];
	end
	
end

%--
% create sound structure depending on type
%--

switch lower(type)

	%--------------------------------
	% FILE
	%--------------------------------
	
	case 'file'

		%--
		% get sound file info and sound attributes
		%--
					
		info = get_file_info(p, fn);

		attr = get_attributes(p, opt.attributes);

		%--
		% create and fill sound
		%--

		f = sound_create_in( ...
			'type',			type, ...
			'path',			p, ...
			'file',			fn, ...
			'info',			info, ...
			'samplerate',	info.samplerate, ...
			'samples',		info.samples, ...
			'channels',		info.channels, ...
			'samplesize',	info.samplesize, ...
			'format',		info.format ...
		);
	
		f = sound_attribute_update(f, 'create');

	%--------------------------------
	% FILE STREAM
	%--------------------------------

	% NOTE: alphabetical order of file name indicates stream position
	
	% NOTE: sound files in directory should have consistent extensions
	
	case ('file stream')

		%--
		% get sound files
		%--
		
		[f, ext] = get_format_files(p);

		if isempty(ext)
			error('No sound files were found in directory.');
		end
		
		f = f.(ext{1}); % NOTE: select the first extension encountered

		f = flipud(f); % NOTE: reverse order increases alphabetically
				
		%--
		% get sound file info and sound attributes
		%--

		[info, pal] = get_file_info(p, f);
		
		attr = get_attributes(p, opt.attributes);
		
		%--
		% store info for MP3 files
		%--
		
		% TODO: normalize stored info
		
% 		if strcmpi(ext{1}, 'mp3')
% 			info_stored = info;
% 		else
% 			info_stored.bytes = sum([info.bytes]);
% 		end
		
		info_stored.bytes = sum([info.bytes]);
		
		%--
		% create and fill sound
		%--

		samples = cell2mat({info.samples}');
	
		f = sound_create_in( ...
			'type',			type, ...
			'path',			p, ...
			'file',			f, ...
			'info',			info_stored, ...
			'samplerate',	info(1).samplerate, ...
			'samples',		samples, ...
			'channels',		info(1).channels, ...
			'samplesize',	info(1).samplesize, ...
			'format',		info(1).format ...
		);
	
		f = sound_attribute_update(f, 'create');
			
		%--
		% close waitbar
		%--

		delete(pal);
		
	%--------------------------------------------------------
	% STACK
	%--------------------------------------------------------
	
	case ('stack')
		
		sounds = p;
		
		if isempty(sounds)
			f = sound_create_in();
			return;
		end
		
		%--
		% sample rate is maximum sample rate of sounds
		%--
		
		samplerate = max([sounds.samplerate]);
		
		for k = 1:length(sounds)
			
			if (sounds(k).samplerate ~= samplerate)
				sounds(k).output.rate = samplerate;
			end
			
		end
				
		% NOTE: this one will need to take advantage of shifts when we
		% implement them
		
		duration = max([sounds.duration]);
		
		samples = duration * samplerate;
		
		channels = length(sounds);	
		
		f = sound_create_in( ...
			'type', type, ...
			'path', [], ...
			'file', [], ...
			'info', sounds, ...
			'samplerate', samplerate, ...
			'samples', samples, ...
			'duration', duration, ...
			'channels', channels ...
		);
				
	%--------------------------------------------------------
	% recording
	%--------------------------------------------------------
	
	% TODO: allow logging to DAQ file or convert this to standard audio formats

	case ('recording')
				
		%--
		% get sound source info and sound attributes
		%--
		
		[info, format] = get_recording_info(recording); % NOTE: this may become a separate function
		
		attr = get_attributes(info.path, opt.attributes); % NOTE: nothing here now, this could change
		
		%--
		% create and fill sound
		%--
		
		% TODO: figure out samplesize
		
		f = sound_create_in( ...
			'type',			info.type, ...
			'path',			info.path, ...
			'file',			info.file, ...
			'input',		recording, ...
			'samplerate',	info.samplerate, ...
			'samples',		info.samples, ...
			'channels',		info.channels, ...
			'format',		format.name, ...
			'realtime',		attr.realtime, ...
			'calibration',	attr.calibration, ...
			'geometry',		attr.geometry, ...
			'speed',		attr.speed ...
		);
	
	%-----------------------------------------------------------
	% variable
	%-----------------------------------------------------------
	
	case ('variable')
		
		[r, c] = size(p.value);
		
		channels = min(r, c); samples = max(r, c);	
		
		if samples < 1000
			error('Could not create sound for the specified variable.')
		end
		
		f = sound_create_in( ...
			'type', type, ...
			'path', 'base', ...
			'file', p.name, ...
			'samplerate', 8000, ...
			'samples', samples, ...
			'channels', channels ...	
		);
		
	
end

%--------------------------------
% DISPLAY PERSISTENCE FIELDS
%--------------------------------

% TODO: setup default computation and presets for some of these fields

%--
% page view settings field
%--

% f.view = view_create;

%--
% spectrogram settings field
%--

% f.specgram = specgram_parameter;


%--------------------------------------------------
% SOUND_CREATE_IN
%--------------------------------------------------

function sound = sound_create_in(varargin)

%--------------------------------
% Author: Harold Figueroa
%--------------------------------
% $Revision: 6176 $
% $Date: 2006-08-15 19:41:57 -0400 (Tue, 15 Aug 2006) $
%--------------------------------

% TODO: reconsider field ordering

%------------------------
% CREATE STRUCT
%------------------------

%--
% source fields
%--

sound.type = [];

sound.format = [];

sound.path = [];

sound.file = [];

sound.info = [];

% NOTE: these refer to how samples come into and are read from the sound

sound.input = []; % NOTE: contents defined in recording_dialog.m

sound.output = sound_file_read; 

%--
% time fields
%--

sound.samplerate = [];

sound.samples = [];	

sound.cumulative = [];

sound.duration = [];		

sound.realtime = [];

sound.time_stamp = [];

%--
% channel fields
%--

sound.channels = [];

% NOTE: these are channel related sound attributes

sound.calibration = [];	

sound.geometry = [];

sound.speed = [];

%--
% format fields
%--

sound.samplesize = [];

%--
% display
%--

% NOTE: these help with persistence of displays

sound.view = view_create;

sound.specgram = specgram_parameter;

%--
% admin and metadata fields
%--

sound.created = now;

sound.modified = [];	

sound.notes = [];

sound.tags = [];

sound.userdata = [];

%------------------------
% FILL STRUCT
%------------------------

% NOTE: this code is closer to a proper constructor

if length(varargin)
	
	%--
	% set fields directly
	%--
	
	sound = parse_inputs(sound, varargin{:});
	
	%--
	% set certain fields as computations
	%--
	
	if isempty(sound.cumulative)
		sound.cumulative = cumsum(sound.samples);
	end
	
	if isempty(sound.duration)
		sound.duration = sound.cumulative(end) / sound.samplerate;
	end
	
	%--
	% set default values for some fields
	%--
	
% 	if (isempty(sound.geometry))
% 		sound.geometry = default_geometry(sound.channels);
% 	end
% 
% 	if (isempty(sound.calibration))
% 		sound.calibration = zeros(sound.channels,1);
% 	end

	%--
	% check some fields for consistency
	%--
	
	% TODO: write some value tests here
	
end


%--------------------------------------------------
% GET_FILE_INFO
%--------------------------------------------------

function [info, pal] = get_file_info(p, f)

% get_file_info - get sound file info and check for consistency
% -------------------------------------------------------------
%
% [info, pal] = get_file_info(p, f)
%
% Input:
% ------
%  p - path to files
%  f - files
%
% Output:
% -------
%  info - sound file info
%  pal - palette handle

%--------------------------------
% Author: Harold Figueroa
%--------------------------------
% $Revision: 6176 $
% $Date: 2006-08-15 19:41:57 -0400 (Tue, 15 Aug 2006) $
%--------------------------------

%---------------------------
% FILE
%---------------------------

if ischar(f)
	info = sound_file_info([p, f]); pal = []; return;
end

%---------------------------
% FILE LIST
%---------------------------

%--
% set test fields and error prefix
%--

test_field = { ...
	'samplerate', ...
	'channels', ...
	'samplesize', ...
	'format' ...
};

str = ['Files in ''' p ''' do not have matching '];

%--
% create waitbar
%--

pal = sound_create_wait;

listbox = findobj(pal, 'tag', 'Files', 'style', 'listbox');

%--
% get sound file info incrementally
%--

N = length(f);

for k = 1:N

	%--
	% update waitbar
	%--

	waitbar_update(pal,'PROGRESS', ...
		'value', k/N, 'message', ['Adding file ''' f{k} ''''] ...
	);

	%--
	% get sound file info
	%--

	info(k) = sound_file_info([p, f{k}]);

	%--
	% get base file properties
	%--

	if (k == 1)

		base = info;
		
	%--
	% compare later sound files with first file
	%--

	else

		for j = 1:length(test_field)
			
			if (~isequal(base.(test_field{j}),info(k).(test_field{j})))
				close(pal);
				error([str, test_field{j}, '.']);
			end
			
		end

	end

	%--
	% update file list display on waitbar
	%--

	k1 = max(k - 20,1); 
	
	txt = f(k1:k); 
	
	val = length(txt);

	set(listbox, ...
		'string',txt, ...
		'value',val ...
	);

end


%------------------------------------------------------------------------
% SOUND_CREATE_WAIT
%------------------------------------------------------------------------

function h = sound_create_wait(name)

% sound_create_wait - create sound creation waitbar
% -------------------------------------------------
%
% h = sound_create_wait(name)
%
% Input:
% ------
%  sound - sound structure

%--------------------------------
% Author: Harold Figueroa
%--------------------------------
% $Revision: 6176 $
% $Date: 2006-08-15 19:41:57 -0400 (Tue, 15 Aug 2006) $
%--------------------------------

% NOTE: this is only used while creating multiple file sound structures

%-----------------------------
% WAITBAR CONTROLS
%-----------------------------

%--
% progress waitbar
%--

control = control_create( ...
	'name','PROGRESS', ...
	'alias','Create Sound ...', ...
	'style','waitbar', ...
	'confirm',1, ...
	'lines',1.1, ...
	'space',2 ...
);

control(end + 1) = control_create( ...
	'style','separator', ...
	'type','header', ...
	'string','Details' ...
);

control(end + 1) = control_create( ...
	'name','Files', ...
	'alias','Files', ...
	'lines',6, ...
	'space',0, ...
	'confirm',0, ...
	'style','listbox' ...
);

%-----------------------------
% CREATE WAITBAR
%-----------------------------

% NOTE: the title does not match the function, 'get folder sounds' and 'add sounds' 

name = ['Create Sound ...'];

%--
% check for possible parent
%--

opt = waitbar_group; opt.show_after = 1;

par = get_xbat_figs('type','waitbar');

if (numel(par) == 1)
	h = waitbar_group(name,control,par,'bottom',opt); return;
end

h = waitbar_group(name,control,[],[],opt);


%------------------------------------------------------------------------
% GET_RECORDING_INFO
%------------------------------------------------------------------------

function [info,format] = get_recording_info(recording)

% get_recording_info - get recording source info
% --------------------------------------------
%
% [info,format] = get_recording_info(recording)
%
% Input:
% ------
%  recording - recording configuration structure
%
% Output:
% -------
%  info - recording info
%  format - file format of output files, this is a supported format

% NOTE: this function computes standard sound file header info as well as filenames

%--
% output file info
%--

% TODO: we may record to a single file as well

info.type = 'file stream';

info.path = create_dir([recording.path, filesep, recording.name, filesep]);

if (isempty(info.path))
	error('Unable to create recording output file directory.');
end

%--
% rate and channels info
%--

info.samplerate = recording.samplerate;

info.channels = length(recording.channels);

if (isempty(info.channels))
	error('No channels selected.');
end

%--
% samples info 
%--

% NOTE: we need to compute number of files and file durations

file_duration = time_to_seconds(recording.file_length{1});

[N,last_duration] = div_rem(recording.duration,file_duration);

file_durations = file_duration * ones(N,1);

if (last_duration)
	file_durations(end + 1) = last_duration;
end

%----------------------

info.samples = file_durations * recording.samplerate;	

%--
% get file extension from format
%--

format = get_formats([],'name',recording.format{1});

if (isempty(format))
	error('Unable to find requested output file format.');
end

ext = format(1).ext{1};

%--
% file names
%--

% NOTE: file names are based on sound name and time stamps

name = [strrep(recording.name,' ','_'), '_'];

time_stamps = strrep(sec_to_clock(cumsum([0; file_durations(1:end-1)])),':','');

info.file = strcat(name,time_stamps);
	
% NOTE: remove decimal positions produced by sec_to_clock

for k = 1:length(info.file)
	info.file{k}(end - 2:end) = [];
end

info.file = strcat(info.file,['.', ext]);

info.file = info.file(:);
