gusucode.com > audiovideo工具箱matlab源码程序 > audiovideo/audiowrite.m
function audiowrite(filename,y,Fs,varargin) %AUDIOWRITE write audio files % AUDIOWRITE(FILENAME,Y,FS) writes data Y to an audio % file specified by the file name FILENAME, with a sample rate % of FS Hz. % Stereo data should be specified as a matrix with two columns. % Multi-channel data should be specified as a matrix of N columns. % % The file format to use when writing the file is inferred from % FILENAME's extension. Supported formats are as follows: % % Format File Extension(s) Compression Method % --------------------------------------------------------- % Wave .wav None % MPEG-4 Audio .m4a, .mp4 AAC % FLAC .flac FLAC (Lossless) % Ogg/Vorbis .ogg, .oga Vorbis % % AUDIOWRITE(FILENAME,Y,FS,Name1,Value1,Name2,Value2, ...) % Specifies optional comma-separated pairs of Name,Value arguments, % where Name is the argument name and Value is the corresponding value. % Name must appear inside single quotes (' '). You can specify several % name and value pair arguments in any order as Name1,Value1,...,NameN, % ValueN. Valid Name,Value arguments are as follows: % % 'BitsPerSample' Number of bits per sample to write out to the audio % file. % Only supported for WAVE (.wav) and FLAC(.flac) files. % Valid values are 8,16,24,32, or 64 and vary depending % upon the supported format. % See "Output Data Type" section below for more details. % % 'BitRate' Number of kilobits per second (kbps) used for compressed % audio files. In general, the larger the BitRate The % higher the compressed audio quality. % Only Supported for MPEG-4 Audio (.m4a, .mp4) files. % % 'Title' String representing a title to be written to the file. % % 'Artist' String representing the artist or author to be written % to the file. % % 'Comment' String representing a comment to be written to the % file. % % Input Data Ranges % Y is specified as an m-by-n matrix, where m is the % number of audio samples to write and n is the number of audio channels % to write. If either m or n is 1, then audiowrite assumes that % dimension specifies the number of audio channels, and the other % dimension specifies the number of audio samples. % % The valid range for the data in Y depends on the data type of Y. % Supported data types are as follows: % % Data Type of Y Valid Range for Y % ----------------------------------- % uint8 0 <= Y <= 255 % int16 -32768 <= Y <= +32767 % int32 -2^32 <= Y <= 2^32-1 % single -1.0 <= Y <= +1.0 % double -1.0 <= Y <= +1.0 % % Data beyond the valid range is clipped. % % If Y is single or double, then audio data in Y should be normalized % to values in the range -1.0 and 1.0, inclusive. % % Output Data Type % The native data type written to the audio file is determined by % the file format, the data type of Y, and the specified output % BitsPerSample. % % File Formats Data Type of Y Output BitsPerSample Output Data Type % ----------------------------------------------------------------------- % WAVE (.wav) uint8,int16,int32 8 uint8 % single,double 16 int16 % 24 int32 % --------------------------------------------------------- % uint8,int16,int32 32 int32 % --------------------------------------------------------- % single,double 32 single % --------------------------------------------------------- % single,double 64 double % ----------------------------------------------------------------------- % FLAC (.flac) uint8,int16,int32, 8 int8 % single,double 16 int16 % 24 int32 % ----------------------------------------------------------------------- % MPEG-4 uint8,int16,int32, N/A single % (.m4a,.mp4), single,double % OGG(.ogg) % ----------------------------------------------------------------------- % % See also AUDIOINFO, AUDIOREAD % Copyright 2012-2014 The MathWorks, Inc. narginchk(3,13); % Parse inputs: props = parseInputs(filename, y, Fs, varargin); [props.filename, fileExisted] = validateFilename( props.filename ); % y can only be 1D or 2D if ndims(y) > 2 %#ok<ISMAT> error(message('MATLAB:audiovideo:audiowrite:invalidDimensions')); end % If input is a vector, force it to be a column: if size(y,1)==1, y = y(:); end [~, props.Channels] = size(y); import multimedia.internal.audio.file.PluginManager; try writePlugin = PluginManager.getInstance.getPluginForWrite(props.filename); catch exception % The exception has been fully formed. Only the prefix has to be % replaced. exception = PluginManager.replacePluginExceptionPrefix(exception, 'MATLAB:audiovideo:audiowrite'); throw(exception); end import multimedia.internal.audio.file.FrameSize; try options.Filename = props.filename; % Create Channel object channel = asyncio.Channel( ... writePlugin,... PluginManager.getInstance.MLConverter, ... options, [0, 0]); % The channel exposes settable custom properties depending upon the % file type. Validate that the input properties are valid for this % type of file. props = validateProperty('BitRate',props,'BitRate',channel,128); props = validateProperty('BitsPerSample',props,'FileDataType',channel,16); props = validateProperty('Quality',props,'Quality',channel,75); % create and configure a transform filter to convert % the incoming data into a form that the channel's device plugin % will expect. channel.OutputStream.addFilter( ... PluginManager.getInstance.TransformFilter, ... []); options.FilterOutputDataType = filterOutputDataTypeFromFile(... props.filename, ... class(y), ... channel.DataType); options.FilterTransformType = 'InterleaveTranspose'; options.SampleRate = double(props.Fs); options.NumberOfChannels = double(props.Channels); options.BitRate = double(props.BitRate) * 1000; % convert to Bits-Per-Second options.Quality = double(props.Quality) / 100; % value must be between 0 and 1 options.FileDataType = fileDataTypeFromBitsPerSample(... props.BitsPerSample,... class(y),... props.filename); options.Comment = props.Comment; options.Artist = props.Artist; options.Title = props.Title; % Just prior to writing y to the input range if needed y = clipInputData(y, options.FileDataType); channel.open(options); channel.OutputStream.write(y, double(FrameSize.Optimal)); % Warn about writing Mp4 metadata on Mac warnIfMp4Metadata(props); channel.close(); catch exception try channel.close(); % Close the channel catch ME handleException(ME, props, fileExisted); end handleException(exception, props, fileExisted); end end function props = parseInputs(filename, y, Fs, pvpairs) p = inputParser; p.addRequired('filename',@(x)validateattributes(x,{'char'},{'nonempty'})); p.addRequired('y',... @(x)validateattributes( x, ... {'uint8','int16','int32','single','double'},{'nonempty'})); p.addRequired('Fs',@(x)validateattributes(x,{'numeric'},... {'nonempty','positive','integer','nonnan'})); p.addParamValue('BitsPerSample',[],@(x)validateattributes(x,{'numeric'},... {'nonempty','positive','integer'})); p.addParamValue('BitRate',[],@(x)validateattributes(x,{'numeric'},... {'nonempty','positive','integer'})); p.addParamValue('Quality',[],@(x)validateattributes(x,{'numeric'},... {'nonempty','integer','>=',0,'<=',100})); p.addParamValue('Title',[],@(x)validateattributes(x,{'char'},{})); p.addParamValue('Artist',[],@(x)validateattributes(x,{'char'},{})); p.addParamValue('Comment',[],@(x)validateattributes(x,{'char'},{})); p.CaseSensitive = false; p.KeepUnmatched = true; p.FunctionName='audiowrite'; parse(p,filename, y, Fs, pvpairs{:}); % Partially Match 'unmatched' properties if ~isempty(p.Unmatched) validParams = { ... 'BitsPerSample',... 'BitRate',... 'Title',... 'Artist',... 'Comment'}; props = p.Results; unmatchedParams = fieldnames(p.Unmatched); for ii=1:length(unmatchedParams) matchedParam = validatestring(unmatchedParams{ii}, validParams,'audiowrite'); props.(matchedParam) = p.Unmatched.(unmatchedParams{ii}); end end end function dataType = fileDataTypeFromBitsPerSample(bitsPerSample, inputDataType, filename) validBitsPerSample = [8 16 24 32 64]; validDataTypes = {'uint8' 'int16' 'int24' 'single' 'double'}; % Flac files only support 8, 16, and 24 bit filepath = audiovideo.internal.FilePath(filename); if strcmpi(filepath.Extension, 'flac') validBitsPerSample = validBitsPerSample(1:3); validDataTypes = validDataTypes(1:3); end if ~ismember(bitsPerSample,validBitsPerSample) values = sprintf('%d,',validBitsPerSample); values = values(1:end-1); % remove trailing ',' error(message(... 'MATLAB:audiovideo:audiowrite:invalidBitsPerSample',... sprintf('%d',bitsPerSample), values)); end dataType = validDataTypes{bitsPerSample == validBitsPerSample}; if bitsPerSample == 32 && ismember(inputDataType,{'uint8','int16','int32'}) % If bitsPerSample is 32, Integer input data should be % int32 instead of single. dataType = 'int32'; end end function dataType = filterOutputDataTypeFromFile(filename, inputDataType, channelDataType) % For most files, just use the underlying Channel's device plugin DataType dataType = channelDataType; % For wav and flac files, specify the datatype based upon the % input data to preserve the incoming data and avoid % conversions from inputDataType to channelDataType filepath = audiovideo.internal.FilePath(filename); if any(strcmpi(filepath.Extension, {'wav','flac'})) switch inputDataType case 'uint8' dataType = 'int16'; % wav/flac device needs int16 minimum. case {'int16','int32','single','double'} dataType = inputDataType; otherwise dataType = channelDataType; end end end function [filename, exists] = validateFilename( filename ) filepath = audiovideo.internal.FilePath(filename); if isempty(filepath.Absolute) error(message('MATLAB:audiovideo:audiowrite:folderNotFound', ... filepath.ParentPath)); end % Validate that the filename has the correct extension. if isempty(filepath.Extension) error(message('MATLAB:audiovideo:audiowrite:noFileExtension', ... filename, sprintWriteableFileTypes)); end import multimedia.internal.audio.file.PluginManager; if ~any(strcmpi(filepath.Extension, PluginManager.getInstance.WriteableFileTypes)) error(message('MATLAB:audiovideo:audiowrite:invalidFileExtension',... filename, sprintWriteableFileTypes)); end if ~filepath.Writeable error(message('MATLAB:audiovideo:audiowrite:fileNotWritable', filename)); end filename = filepath.Absolute; exists = filepath.Exists; end function fileTypes = sprintWriteableFileTypes % Print the list of writeable file types to a string import multimedia.internal.audio.file.PluginManager; fileTypes = cellfun(@(x) sprintf('\t.%s\n',x),... PluginManager.getInstance.WriteableFileTypes, ... 'UniformOutput', false); % expand into a character array fileTypes = [fileTypes{:}]; % remove trailing newline ('\n') character. fileTypes = fileTypes(1:end-1); end function warnIfMp4Metadata(props) % Warn when writing Mp4 metadata on Mac, which is currently unsupported if ~ismac % Metadata s return end if isempty(props.Title) && isempty(props.Artist) && isempty(props.Comment) % If no metadata is being written, do not warn return end % Warn only for m4a and mp4 files filepath = audiovideo.internal.FilePath(props.filename); if any(strcmpi(filepath.Extension, {'m4a','mp4'})) warning(message('MATLAB:audiovideo:audiowrite:metadataMp4UnsupportedMac')); end end function props = validateProperty(propName, props,channelProp, channel, defaultValue) % A property (propName) is only valid if its associated % channel property (channelProp) is available on the channel. if ~isempty(props.(propName)) && ~ismember(channelProp, properties(channel)) [~,~,extension] = fileparts(props.filename); error(message(... 'MATLAB:audiovideo:audiowrite:unsupportedParameter',... propName,extension)); end % Property is valid. Set the default value if it has not been % specified by the user if isempty(props.(propName)) props.(propName) = defaultValue; end end function y = clipInputData(y, fileDataType) if ~ismember(class(y),{'double','single'}) return; % data does not need to be clipped end if ismember(fileDataType, {'single','double'}) % do not clip when writing out 'single' or 'double' to the file % to preserve full fidelity of the signal return; end % Data is input as a 'single' or 'double', and being written to an integer % format. Clip the range [-1 +1]. if any(y(:) < -1 | y(:) > +1) warning(message('MATLAB:audiovideo:audiowrite:dataClipped')); end y(y < -1) = -1; y(y > +1) = +1; end function handleException(exception, props, fileExisted) import multimedia.internal.audio.file.PluginManager; newexception = PluginManager.convertPluginException(exception, ... 'MATLAB:audiovideo:audiowrite'); filepath = audiovideo.internal.FilePath(props.filename); if ~fileExisted && filepath.Exists % delete new files on error delete(props.filename); end throwAsCaller(newexception); end