蓝桥杯 ADV-227 算法提高 11-1实现strcmp函数

自定义strcmp函数
本文介绍了一个自定义的字符串比较函数myStrcmp,该函数能够按照ASCII顺序比较两个字符串的大小,并返回相应的比较结果。文章提供了完整的C语言实现代码示例。
问题描述
  自己实现一个比较字符串大小的函数,也即实现strcmp函数。函数:int myStrcmp(char *s1,char *s2) 按照ASCII顺序比较字符串s1与s2。若s1与s2相等返回0,s1>s2返回1,s1<s2返回-1。具体来说,两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止(注意'\0'值为0,小于任意ASCII字符)。如:
  "A"<"B"
  "a">"A"
  "computer">"compare"
  "hello"<"helloworld"
样例输出
数据规模和约定

  字符串长度<100。

#include<stdio.h>
int main()
{
	int myStrcmp(char *p1,char *p2);
	char str1[100],str2[100],*p1,*p2;
	int x;
	gets(str1);
	gets(str2);
	p1=&str1[0];
	p2=&str2[0];
	x=myStrcmp(p1,p2);
	printf("%d\n",x);
	return 0;
}
int myStrcmp(char *p1,char *p2)
{
	int z=0;
	while(*(p1+z)==*(p2+z))
	{
		if(*(p1+z++)=='\0')
		{
			return 0;
		}
	}
	if(*(p1+z)>*(p2+z))
	{
		return 1;
	}
	if(*(p1+z)<*(p2+z))
	{
		return -1;
	}
}


function varargout = advisor(varargin) % advisor Application M-file for advisor.fig % FIG = advisor launch advisor GUI. % advisor('callback_name', ...) invoke the named callback. % % ADVISOR Advanced Vehicle Simulator % % This function opens the first graphical user interface (GUI) into % the program ADVISOR. % % The ADVISOR program is designed to assist in testing different % vehicle configurations using standard and custom driving cycles or % test procedures. % % For help in running ADVISOR please use the help buttons found on % each user interface which links to the documentation pages. If you still % need help or have helpful suggestions for improvement please email: % advisor@nrel.gov % % Last Modified by GUIDE v2.0 21-Mar-2002 15:12:42 if nargin == 0 % LAUNCH GUI global vinf %----------------------------------------------------------- %if ADVISOR is running then don't start another one, just %bring the current ADVISOR figure to the top figure_tags={'advisor_figure';'input_figure';'execute_figure';'results_figure';... 'rw_results_figure';'parametric_results_figure';'j1711_results_figure';... 'city_hwy_results_figure';'ftp_results_figure'}; for i=1:size(figure_tags) if ~isempty(findobj('tag',figure_tags{i})) figure(findobj('tag',figure_tags{i})); return end end %------------------------------------------------------------- advisor('set_path') %-------------------------------------------------- %clear the workspace evalin('base','clear'); evalin('base','global vinf') %set units to 'us' if not defined if ~exist('vinf.units') vinf.units='us'; end hwait=waitbar(0,'loading startup figure'); try waitbar(.5) end %add some sound if user's system can play sound try load list_optionlist.mat; %loads list_optionlist and list_def if findstr(lower(list_def),'truck') [advisor_sound,fs]=wavread('truckwelcome.wav'); else [advisor_sound,fs]=wavread('advisor.wav'); end sound(advisor_sound,fs) end %--------------------------------------------------- fig = openfig(mfilename,'new'); %'reuse' wasn't working when SOC figure was up %fig = openfig(mfilename,'reuse'); % Generate a structure of handles to pass to callbacks, and store it. handles = guihandles(fig); guidata(fig, handles); if nargout > 0 varargout{1} = fig; end %------------------------------------------------------- %adjust the figure appropriately [ver,date]=advisor_ver('info'); set(findobj('tag','advisor_figure'),'Name',[ver ' ' date]) %set the units radio buttons appropriately set(findobj('tag','metric_radiobutton'),'value',strcmp(vinf.units,'metric')) set(findobj('tag','us_radiobutton'),'value',strcmp(vinf.units,'us')) %set the optionlist popupmenu advisor('set_optionlist_popup') %set everything normalized and set the figure size and center it h=findobj('type','uicontrol'); g=findobj('type','axes'); set([h;g],'units','normalized') screensize=get(0,'screensize'); if screensize(3)<1024 uiwait(warndlg({'ADVISOR does not work well at resolutions lower than 1024x768. Please increase resolution or proceed with caution!'},... 'ADVISOR: Low Resolution Warning','modal')) end %test for large fonts and give warning if they are large. figh=figure('visible','off'); test_string='test string'; handle_text=text(0,0,test_string); set(handle_text,'units','pixels') extent=get(handle_text,'extent');% 0 -9 60 18 close(figh); if extent(3)>61 uiwait(warndlg({'ADVISOR does not work well with large fonts. Please change your display settings to small fonts or proceed with caution!'},... 'ADVISOR: Low Resolution Warning','modal')) end im_width=640;%this is the width of the image im_height=474; left=screensize(3)/2-im_width/2; bottom=screensize(4)/2-im_height/2; set(gcf,'position',[left bottom im_width im_height]);% set(gcf,'resize','off') %set the figure back on after everything is drawn set(gcf,'visible','on'); advisor_ver waitbar(1) close(hwait); %close the waitbar adv_dir=strrep(which('advisor'),'\advisor.m',''); imagedata = imread([adv_dir '\gui_graphics\Splash_Screen_car.jpg']); h=image(imagedata); set(h,'ButtonDownFcn','advisor(''play_movie'')'); advisor('play_movie') elseif ischar(varargin{1}) % INVOKE NAMED SUBFUNCTION OR CALLBACK try if (nargout) [varargout{1:nargout}] = feval(varargin{:}); % FEVAL switchyard else feval(varargin{:}); % FEVAL switchyard end catch disp(lasterr); end end %| ABOUT CALLBACKS: %| GUIDE automatically appends subfunction prototypes to this file, and %| sets objects' callback properties to call them through the FEVAL %| switchyard above. This comment describes that mechanism. %| %| Each callback subfunction declaration has the following form: %| <SUBFUNCTION_NAME>(H, EVENTDATA, HANDLES, VARARGIN) %| %| The subfunction name is composed using the object's Tag and the %| callback type separated by '_', e.g. 'slider2_Callback', %| 'figure1_CloseRequestFcn', 'axis1_ButtondownFcn'. %| %| H is the callback object's handle (obtained using GCBO). %| %| EVENTDATA is empty, but reserved for future use. %| %| HANDLES is a structure containing handles of components in GUI using %| tags as fieldnames, e.g. handles.figure1, handles.slider2. This %| structure is created at GUI startup using GUIHANDLES and stored in %| the figure's application data using GUIDATA. A copy of the structure %| is passed to each callback. You can store additional information in %| this structure at GUI startup, and you can change the structure %| during callbacks. Call guidata(h, handles) after changing your %| copy to replace the stored original so that subsequent callbacks see %| the updates. Type "help guihandles" and "help guidata" for more %| information. %| %| VARARGIN contains any extra arguments you have passed to the %| callback. Specify the extra arguments by editing the callback %| property in the inspector. By default, GUIDE sets the property to: %| <MFILENAME>('<SUBFUNCTION_NAME>', gcbo, [], guidata(gcbo)) %| Add any extra arguments after the last argument, before the final %| closing parenthesis. % -------------------------------------------------------------------- function varargout = Copyright_button_Callback(h, eventdata, handles, varargin) copyright % -------------------------------------------------------------------- function varargout = Start_button_Callback(h, eventdata, handles, varargin) global vinf close(gcbf); evalin('base','global vinf'); gui_input_open('defaults'); % -------------------------------------------------------------------- function varargout = edit_optionlist() edit_profiles % -------------------------------------------------------------------- function varargout = Profile_Popup_Callback(h, eventdata, handles, varargin) current_selection_str=gui_current_str('Profile_Popup'); if strcmp(current_selection_str,'edit list') advisor('edit_optionlist') return end global vinf vinf.optionlist.name=current_selection_str; load list_optionlist.mat list_def=current_selection_str; p=which('list_optionlist.mat'); save(p,'list_def','list_optionlist'); %--------------------------------------------------------------------- function varargout = set_optionlist_popup load list_optionlist.mat; %loads list_optionlist and list_def set(findobj('tag','Profile_Popup'),'string',list_optionlist) index=strmatch(list_def, list_optionlist, 'exact'); set(findobj('tag','Profile_Popup'),'value',index); global vinf vinf.optionlist.name=list_def; % -------------------------------------------------------------------- function varargout = Help_button_Callback(h, eventdata, handles, varargin) web(['file:\\\' which('advisor_doc.htm')],'-browser'); % -------------------------------------------------------------------- function varargout = Exit_button_Callback(h, eventdata, handles, varargin) close(gcbf) % -------------------------------------------------------------------- function varargout = list_popup_Callback(h, eventdata, handles, varargin) %---------------------------------------------------- function set_path() %------------------Add ADVISOR directories to the MATLAB path------------- % Add the appropriate paths based on where advisor.m is found with the which command (This % defines the main ADVISOR directory) % % NOTE: paths will only be added with the addpath command if they don't already exist. % If new directories were added to the path (using addpath), a 1 will be returned % otherwise a 0 will be returned. % % SetAdvisorPath can accept a comma separated list of arguments. The arguments should be % the name of an existing directory. Non-existing directories will not be created. % path_added=SetAdvisorPath; % external function that contains necessary path information %find out if user wants to save the path changes. if path_added==1 ButtonName=questdlg('The Matlab path has been updated with ADVISOR directories for this Matlab session. Would you like to save the new path for future Matlab sessions? Note: Using VisualDoc optimization requires the path to be saved. ', ... 'ADVISOR path setting', ... 'Yes','No','Yes'); switch ButtonName, case 'Yes', result=path2rc; %path2rc saves the current path to pathdef.m (Both are MATLAB files-- not ADVISOR files) if result==0 disp('ADVISOR Directories were successfully added to the Matlab Path. See Path browser for path information.'); else disp('Not successful. ADVISOR directories were not successfully saved in the Matlab Path.'); end case 'No', end % switch end; %if path_added=1; % -------------------------------------------------------------------- function varargout = Load_Results_Callback(h, eventdata, handles, varargin) global vinf if ~exist('varargin') % no user-specified filename provided [name, path]=uigetfile('*.mat'); %user input for mat name of saved simulation elseif isempty(varargin) [name, path]=uigetfile('*.mat'); %user input for mat name of saved simulation else % user-specified filename provided path_name=varargin{1}; % break argument into the path and filename parts idx=strfind(path_name,'\'); if isempty(idx) idx=strfind(path_name,'/'); end if isempty(idx) name=path_name; path=''; else name=path_name((max(idx)+1):end); path=path_name(1:max(idx)); end end %if no file is selected(ie. figure is cancelled) %then exit(or return) out of this function if name==0 return end evalin('base','clear all') evalin('base',['load ''', path, name,'''']); close(gcbf); global vinf; if strcmp(vinf.parametric.run,'off') ResultsFig; else parametric_gui; end %--------------------------------------------------------------------- function metric_radiobutton_Callback(h, eventdata, handles, varargin) global vinf vinf.units='metric'; set(findobj('tag','us_radiobutton'),'value',0); set(findobj('tag','metric_radiobutton'),'value',1); %--------------------------------------------------------------------- function us_radiobutton_Callback(h, eventdata, handles, varargin) global vinf vinf.units='us'; set(findobj('tag','metric_radiobutton'),'value',0); set(findobj('tag','us_radiobutton'),'value',1); %--------------------------------------------------------------------- function play_movie() try adv_dir=strrep(which('advisor'),'\advisor.m',''); imagenames={'car2truck001.gif'; 'car2truck002.gif'; 'car2truck003.gif'; 'car2truck004.gif'; 'car2truck005.gif'; 'car2truck006.gif'; 'car2truck007.gif'; 'car2truck008.gif'; 'car2truck009.gif'; 'car2truck010.gif'; 'car2truck011.gif'; 'car2truck012.gif'; 'car2truck013.gif'; 'car2truck014.gif'; 'car2truck015.gif'}; for i=1:length(imagenames) [X,map]=imread([adv_dir '\gui_graphics\' imagenames{i}]); M(i)=im2frame(X,map); end loc=[36 206 0 0]; N=[-2 1 1 1 1 1 1 1:15 15 15 15 15 15 15 15]; movie(M,N,8,loc) end function LoadVehicle(filename) % allows user to start ADVISOR with a specific saved vehicle other than the default if ~exist('filename') filename='PARALLEL_defaults_in'; end if evalin('base',['~exist(''',filename,'.m'')']) disp(['File ',filename,'.m not found.']) else global vinf vinf.units='us'; gui_input_open(filename) end return function LoadResults(filename) % allows user to start ADVISOR with a specific results file from the command line if exist('filename') if evalin('base',['~exist(''',filename,'.mat'')']) disp(['File ',filename,'.mat not found.']) else advisor('Load_Results_Callback',[],[],[],filename) end end return % Revision History % 041802:tm added call to SetAdvisorPath external function and removed common code from this file % 041802:tm added statements to the Load_Results_Callback function to allow the user to specify a % results file to load from the command line % 041802:tm added function to allow user to load a specific vehicle from the command line % 041802:tm added function to allow user to load a specific results file from the command line 怎么修改可以让advisor2002适配MATLAB2024a
最新发布
08-09
function audio_pitch_correction % 创建主GUI界面 fig = uifigure('Name', '音频音准矫正系统', 'Position', [100 100 900 700]); % 创建音频选择区域 uilabel(fig, 'Position', [50 680 300 20], 'Text', '待矫正音频来源:', 'FontWeight', 'bold'); % 创建录音选项按钮组 source_btn_group = uibuttongroup(fig, 'Position', [50 630 300 40], 'Title', ''); uibutton(source_btn_group, 'Position', [10 10 130 30], 'Text', '导入音频文件', ... 'ButtonPushedFcn', @(btn,event) select_audio(fig, 'source')); uibutton(source_btn_group, 'Position', [160 10 130 30], 'Text', '录制音频', ... 'ButtonPushedFcn', @(btn,event) record_audio(fig)); % 创建参考音频选择按钮 uilabel(fig, 'Position', [400 680 300 20], 'Text', '参考音频来源:', 'FontWeight', 'bold'); uibutton(fig, 'Position', [400 630 150 30], 'Text', '导入参考音频', ... 'ButtonPushedFcn', @(btn,event) select_audio(fig, 'reference')); % 创建处理按钮 process_btn = uibutton(fig, 'Position', [600 630 150 30], ... 'Text', '开始矫正', 'Enable', 'off', ... 'ButtonPushedFcn', @(btn,event) process_audio(fig)); % 创建播放和保存按钮 uibutton(fig, 'Position', [50 580 150 30], 'Text', '播放原始音频', ... 'ButtonPushedFcn', @(btn,event) play_audio(fig, 'source')); uibutton(fig, 'Position', [250 580 150 30], 'Text', '播放矫正音频', ... 'ButtonPushedFcn', @(btn,event) play_audio(fig, 'corrected')); uibutton(fig, 'Position', [450 580 150 30], 'Text', '保存矫正音频', ... 'ButtonPushedFcn', @(btn,event) save_audio(fig)); % 创建录音状态显示 recording_label = uilabel(fig, 'Position', [650 580 200 30], ... 'Text', '准备录音', 'FontColor', [0 0.5 0]); % 创建波形显示区域 ax_source = uiaxes(fig, 'Position', [50 350 800 150]); title(ax_source, '待矫正音频波形'); ax_reference = uiaxes(fig, 'Position', [50 180 800 150]); title(ax_reference, '参考音频波形'); ax_corrected = uiaxes(fig, 'Position', [50 10 800 150]); title(ax_corrected, '矫正后音频波形'); % 存储据 fig.UserData.source_audio = []; fig.UserData.reference_audio = []; fig.UserData.corrected_audio = []; fig.UserData.fs = 44100; % 默认采样率 fig.UserData.process_btn = process_btn; fig.UserData.axes = struct('source', ax_source, 'reference', ax_reference, 'corrected', ax_corrected); fig.UserData.recording_label = recording_label; fig.UserData.recorder = []; % 录音器对象 fig.UserData.timer = []; % 计时器对象 end function select_audio(fig, audio_type) [file, path] = uigetfile({'*.wav;*.mp3;*.ogg;*.flac', ... '音频文件 (*.wav,*.mp3,*.ogg,*.flac)'}); if isequal(file, 0) return; end filename = fullfile(path, file); [audio, fs] = audioread(filename); % 处理立体声:转换为单声道 if size(audio, 2) > 1 audio = mean(audio, 2); end % 截取前20秒 max_samples = min(20*fs, length(audio)); audio = audio(1:max_samples); % 存储据 fig.UserData.([audio_type '_audio']) = audio; fig.UserData.fs = fs; % 更新波形显示 ax = fig.UserData.axes.(audio_type); plot(ax, (1:length(audio))/fs, audio); xlabel(ax, '时间 (s)'); ylabel(ax, '幅度'); % 启用处理按钮 if ~isempty(fig.UserData.source_audio) && ~isempty(fig.UserData.reference_audio) fig.UserData.process_btn.Enable = 'on'; end end function record_audio(fig) % 创建录音界面 record_fig = uifigure('Name', '音频录制', 'Position', [300 300 400 200]); % 录音时长设置 uilabel(record_fig, 'Position', [50 150 100 20], 'Text', '录音时长 (秒):'); duration_edit = uieditfield(record_fig, 'numeric', ... 'Position', [160 150 100 20], 'Value', 5, 'Limits', [1 30]); % 采样率设置 uilabel(record_fig, 'Position', [50 120 100 20], 'Text', '采样率:'); fs_dropdown = uidropdown(record_fig, ... 'Position', [160 120 100 20], ... 'Items', {'8000', '16000', '44100', '48000'}, ... 'Value', '44100'); % 控制按钮 record_btn = uibutton(record_fig, 'Position', [50 70 100 30], ... 'Text', '开始录音', ... 'ButtonPushedFcn', @(btn,event) start_recording(fig, duration_edit.Value, str2double(fs_dropdown.Value))); uibutton(record_fig, 'Position', [160 70 100 30], ... 'Text', '停止录音', ... 'ButtonPushedFcn', @(btn,event) stop_recording(fig)); uibutton(record_fig, 'Position', [270 70 100 30], ... 'Text', '关闭', ... 'ButtonPushedFcn', @(btn,event) close(record_fig)); end function start_recording(fig, duration, fs) % 更新状态 fig.UserData.recording_label.Text = '录音中...'; fig.UserData.recording_label.FontColor = [1 0 0]; drawnow; % 创建录音器对象 recorder = audiorecorder(fs, 16, 1); % 16-bit, 单声道 % 设置录音时长 fig.UserData.recorder = recorder; fig.UserData.fs = fs; % 开始录音 record(recorder, duration); % 创建计时器显示剩余时间 t = timer('ExecutionMode', 'fixedRate', 'Period', 1, ... 'TasksToExecute', duration, ... 'TimerFcn', @(t,~) update_recording_timer(fig, t, duration)); start(t); % 存储计时器 fig.UserData.timer = t; end function update_recording_timer(fig, t, total_duration) elapsed = t.TasksExecuted; remaining = total_duration - elapsed; fig.UserData.recording_label.Text = sprintf('录音中: %d秒', remaining); % 录音结束时自动停止 if remaining <= 0 stop_recording(fig); end end function stop_recording(fig) if ~isempty(fig.UserData.recorder) && isrecording(fig.UserData.recorder) stop(fig.UserData.recorder); end % 停止计时器 if ~isempty(fig.UserData.timer) && isvalid(fig.UserData.timer) stop(fig.UserData.timer); delete(fig.UserData.timer); fig.UserData.timer = []; end % 获取录音据 audio = getaudiodata(fig.UserData.recorder); fs = fig.UserData.fs; % 更新状态 fig.UserData.recording_label.Text = '录音完成!'; fig.UserData.recording_label.FontColor = [0 0.5 0]; % 存储为待矫正音频 fig.UserData.source_audio = audio; % 更新波形显示 ax = fig.UserData.axes.source; plot(ax, (1:length(audio))/fs, audio); title(ax, '录制音频波形'); xlabel(ax, '时间 (s)'); ylabel(ax, '幅度'); % 启用处理按钮 if ~isempty(fig.UserData.reference_audio) fig.UserData.process_btn.Enable = 'on'; end end function process_audio(fig) source = fig.UserData.source_audio; reference = fig.UserData.reference_audio; fs = fig.UserData.fs; % 确保主图窗存在 if ~isvalid(fig) errordlg('主窗口已关闭,无法处理音频!', '处理错误'); return; end % 创建处理进度对话框 h = uiprogressdlg(fig, 'Title', '处理中', 'Message', '音频对齐...', 'Indeterminate', 'on'); % 步骤1:音频对齐 try [aligned_source, aligned_ref] = improved_align_audio(source, reference, fs); catch ME close(h); errordlg(['音频对齐失败: ' ME.message], '处理错误'); return; end % 步骤2:基频提取 h.Message = '提取音高...'; try [f0_source, time_source] = extract_pitch(aligned_source, fs); [f0_ref, time_ref] = extract_pitch(aligned_ref, fs); catch ME close(h); errordlg(['音高提取失败: ' ME.message], '处理错误'); return; end % 步骤3:音调矫正 h.Message = '矫正音调...'; try [corrected, f0_corrected] = correct_pitch(fig, aligned_source, fs, f0_source, f0_ref, time_source, time_ref); catch ME close(h); errordlg(['音高校正失败: ' ME.message], '处理错误'); return; end % 关闭进度对话框 close(h); % === 关键修复 1: 存储矫正结果 === fig.UserData.corrected_audio = corrected; % === 关键修复 2: 更新播放按钮状态 === play_btn = findobj(fig, 'Text', '播放矫正音频'); if ~isempty(play_btn) play_btn.Enable = 'on'; end % 保存结果并更新显示 % 更新原始音频波形图(添加音高曲线) ax_src = fig.UserData.axes.source; cla(ax_src); yyaxis(ax_src, 'left'); plot(ax_src, (1:length(aligned_source))/fs, aligned_source, 'b'); ylabel(ax_src, '幅度'); yyaxis(ax_src, 'right'); plot(ax_src, time_source, f0_source, 'r', 'LineWidth', 1.5); ylabel(ax_src, '频率 (Hz)'); title(ax_src, '原始音频波形与音高'); grid(ax_src, 'on'); % 更新参考音频波形图(添加音高曲线) ax_ref = fig.UserData.axes.reference; cla(ax_ref); yyaxis(ax_ref, 'left'); plot(ax_ref, (1:length(aligned_ref))/fs, aligned_ref, 'g'); ylabel(ax_ref, '幅度'); yyaxis(ax_ref, 'right'); plot(ax_ref, time_ref, f0_ref, 'm', 'LineWidth', 1.5); ylabel(ax_ref, '频率 (Hz)'); title(ax_ref, '参考音频波形与音高'); grid(ax_ref, 'on'); % 更新矫正后音频波形图(添加音高曲线) ax_corr = fig.UserData.axes.corrected; cla(ax_corr); yyaxis(ax_corr, 'left'); plot(ax_corr, (1:length(corrected))/fs, corrected, 'Color', [0.5 0 0.5]); ylabel(ax_corr, '幅度'); yyaxis(ax_corr, 'right'); plot(ax_corr, time_source, f0_corrected, 'Color', [1 0.5 0], 'LineWidth', 2); ylabel(ax_corr, '频率 (Hz)'); title(ax_corr, '矫正后音频波形与音高'); grid(ax_corr, 'on'); % 绘制综合音高对比图 % 修改后的调用:添加音频波形参 plot_pitch_comparison(time_source, f0_source, time_ref, f0_ref, f0_corrected,... aligned_source, aligned_ref, corrected, fs); fprintf('原始音高平均: %.1f Hz\n', mean(f0_source(f0_source>0))); fprintf('参考音高平均: %.1f Hz\n', mean(f0_ref(f0_ref>0))); fprintf('矫正后音高平均: %.1f Hz\n', mean(f0_corrected(f0_corrected>0))); end function [aligned_src, aligned_ref] = improved_align_audio(src, ref, fs) % 改进的音频对齐方法:使用频谱互相关 win_size = round(0.1 * fs); % 100ms窗口 hop_size = round(0.05 * fs); % 50ms跳跃 % 计算源音频的频谱图 [S_src, ~, t_src] = spectrogram(src, win_size, win_size-hop_size, win_size, fs); % 计算参考音频的频谱图 [S_ref, ~, t_ref] = spectrogram(ref, win_size, win_size-hop_size, win_size, fs); % 计算互相关 n_frames = min(length(t_src), length(t_ref)); corr_vals = zeros(1, n_frames); for i = 1:n_frames spec_src = abs(S_src(:, i)); spec_ref = abs(S_ref(:, i)); corr_vals(i) = dot(spec_src, spec_ref) / (norm(spec_src) * norm(spec_ref)); end % 找到最大相关帧 [~, max_idx] = max(corr_vals); time_diff = t_src(max_idx) - t_ref(max_idx); sample_diff = round(time_diff * fs); % 对齐音频 if sample_diff > 0 aligned_src = src(1:end-sample_diff); aligned_ref = ref(sample_diff+1:end); else aligned_src = src(-sample_diff+1:end); aligned_ref = ref(1:end+sample_diff); end % 确保等长 min_len = min(length(aligned_src), length(aligned_ref)); aligned_src = aligned_src(1:min_len); aligned_ref = aligned_ref(1:min_len); end function mfcc = mfcc_feature(audio, fs, frame_size, hop_size) % 参验证 if nargin < 4 hop_size = round(frame_size/2); % 默认50%重叠 end % 预处理:预加重 audio = filter([1 -0.97], 1, audio); % 分帧处理 frames = buffer(audio, frame_size, frame_size - hop_size, 'nodelay'); num_frames = size(frames, 2); % 加窗(汉明窗) window = hamming(frame_size); windowed_frames = frames .* repmat(window, 1, num_frames); % 计算功率谱 nfft = 2^nextpow2(frame_size); mag_frames = abs(fft(windowed_frames, nfft)); power_frames = (mag_frames(1:nfft/2+1, :)).^2; % 设计梅尔滤波器组 num_filters = 26; % 滤波器量 mel_min = 0; % 最小Mel频率 mel_max = 2595 * log10(1 + (fs/2)/700); % 最大Mel频率 % 创建等间隔的Mel频率点 mel_points = linspace(mel_min, mel_max, num_filters + 2); % 将Mel频率转换为线性频率 hz_points = 700 * (10.^(mel_points/2595) - 1); % 转换为FFT bin索引 bin_indices = floor((nfft+1) * hz_points / fs); % 创建梅尔滤波器组 filter_bank = zeros(num_filters, nfft/2+1); for m = 2:num_filters+1 left = bin_indices(m-1); center = bin_indices(m); right = bin_indices(m+1); % 左侧斜坡 for k = left:center-1 filter_bank(m-1, k+1) = (k - left) / (center - left); end % 右侧斜坡 for k = center:right filter_bank(m-1, k+1) = (right - k) / (right - center); end end % 应用梅尔滤波器组 mel_spectrum = filter_bank * power_frames; % 取对 log_mel = log(mel_spectrum + eps); % 计算DCT得到MFCC系 mfcc = dct(log_mel); % 保留前13个系(含能量系) mfcc = mfcc(1:13, :); % 可选:添加能量特征 energy = log(sum(power_frames) + eps); mfcc(1, :) = energy; % 替换第0阶MFCC为对能量 % 应用倒谱均值归一化 (CMN) mfcc = mfcc - mean(mfcc, 2); end function [f0, time] = extract_pitch(audio, fs) % 参设置 frame_size = round(0.05 * fs); % 50ms帧 hop_size = round(0.025 * fs); % 25ms跳跃 n_frames = floor((length(audio) - frame_size) / hop_size) + 1; f0_min = 80; % 最低基频(Hz) f0_max = 1000; % 最高基频(Hz) tau_min = round(fs/f0_max); tau_max = round(fs/f0_min); f0 = zeros(1, n_frames); time = zeros(1, n_frames); % 预处理:带通滤波去除噪声 [b, a] = butter(4, [80, 1000]/(fs/2), 'bandpass'); audio = filtfilt(b, a, audio); for i = 1:n_frames start_idx = (i-1)*hop_size + 1; end_idx = min(start_idx + frame_size - 1, length(audio)); frame = audio(start_idx:end_idx); % 改进的YIN算法实现 diff = zeros(1, tau_max); for tau = 0:tau_max-1 for j = 1:length(frame)-tau diff(tau+1) = diff(tau+1) + (frame(j) - frame(j+tau))^2; end end % 累积均值归一化差分函数 (CMND) cmnd = diff; cmnd(1) = 1; % 避免除以零 for tau = 2:tau_max cmnd(tau) = diff(tau) / ((1/(tau)) * sum(diff(1:tau))); end % 寻找最小值 (考虑阈值) [min_val, min_idx] = min(cmnd(tau_min:tau_max)); tau = min_idx + tau_min - 1; % 二次插值提高精度 if tau > 1 && tau < tau_max cmnd_tau = cmnd(tau); cmnd_prev = cmnd(tau-1); cmnd_next = cmnd(tau+1); if min_val < 0.1 % 有效峰值阈值 delta = 0.5 * (cmnd_next - cmnd_prev) / ... (2*cmnd_tau - cmnd_prev - cmnd_next); tau = tau + delta; end end % 计算基频 f0(i) = fs / tau; time(i) = (start_idx + frame_size/2) / fs; end % 后处理:中值滤波和插值 valid_idx = f0 > f0_min & f0 < f0_max; f0(~valid_idx) = NaN; f0 = fillmissing(f0, 'movmedian', 5); % 5点移动中值 f0 = fillmissing(f0, 'linear'); % 线性插值填充 end function [corrected, f0_corrected] = correct_pitch(fig, audio, fs, f0_src, f0_ref, time_src, time_ref) % 创建进度条 h = uiprogressdlg(fig, 'Title', '处理中', 'Message', '音高校正...'); frame_len = round(0.05 * fs); % 50ms帧长 hop_size = round(0.025 * fs); % 25ms跳跃 n_frames = floor((length(audio)-frame_len)/hop_size) + 1; corrected = zeros(size(audio)); f0_corrected = zeros(1, n_frames); % 创建参考音高插值函数 valid_ref = f0_ref > 0; if any(valid_ref) ref_interp = @(t) interp1(time_ref(valid_ref), f0_ref(valid_ref), t, 'linear', 'extrap'); else ref_interp = @(t) 0; end for i = 1:n_frames % 计算当前帧位置 start_idx = (i-1)*hop_size + 1; end_idx = start_idx + frame_len - 1; frame = audio(start_idx:end_idx); % 查找当前帧对应的目标音高 t_frame = (start_idx + frame_len/2) / fs; target_f0 = ref_interp(t_frame); if f0_src(i) > 0 && target_f0 > 0 % 使用对比例(音乐音高是几何级) semitone_diff = 12 * log2(target_f0 / f0_src(i)); % 限制最大校正范围(±12半音) semitone_diff = max(-12, min(12, semitone_diff)); % 转换为频率比例 target_ratio = 2^(semitone_diff/12); % 使用相位声码器 corrected_frame = phase_vocoder(frame, target_ratio, fs); f0_corrected(i) = target_f0; else corrected_frame = frame; f0_corrected(i) = f0_src(i); end % 重叠相加 frame_end_idx = start_idx + length(corrected_frame) - 1; if frame_end_idx <= length(corrected) corrected(start_idx:frame_end_idx) = ... corrected(start_idx:frame_end_idx) + corrected_frame .* hamming(length(corrected_frame)); end % 更新进度条 h.Value = i/n_frames; h.Message = sprintf('处理进度: %d/%d 帧 (%.1f%%)', i, n_frames, i/n_frames*100); end % === 关键修复 3: 据格式处理 === corrected = real(corrected); % 确保实 max_amp = max(abs(corrected)); if max_amp > 0 corrected = corrected / max_amp; else corrected = zeros(size(corrected)); % 处理全零情况 end if ~isa(corrected, 'double') corrected = double(corrected); end % 归一化防止削波 max_amp = max(abs(corrected)); if max_amp > 0 corrected = corrected / max_amp; end close(h); end function y = phase_vocoder(x, ratio, fs) % 改进的相位声码器实现 n = 2048; % FFT点 hop_in = round(n/4); hop_out = round(hop_in * ratio); % 初始化 w = hann(n, 'periodic'); X = stft(x, 'Window', w, 'OverlapLength', n-hop_in, 'FFTLength', n); % 相位处理 Y = phase_vocoder_process(X, hop_in, hop_out); % 重建信号 y = istft(Y, 'Window', w, 'OverlapLength', n-hop_out, 'FFTLength', n, ... 'ConjugateSymmetric', true); end function Y = phase_vocoder_process(X, hop_in, hop_out) Y = zeros(size(X)); if isempty(X), return; end phase_adv = angle(X(:,1)); Y(:,1) = X(:,1); % 保留第一帧 for i = 2:size(X,2) mag = abs(X(:,i)); phase = angle(X(:,i)); % 计算相位增量 (考虑2π周期性) delta_phase = phase - phase_adv; delta_phase = delta_phase - 2*pi*round(delta_phase/(2*pi)); % 计算瞬时频率 (考虑采样率) inst_freq = (delta_phase + 2*pi*(0:size(X,1)-1)'/size(X,1)) / hop_in; % 累积相位 adjusted_phase = phase_adv + inst_freq * hop_out; % 合成新帧 Y(:,i) = mag .* exp(1j * adjusted_phase); % 更新相位 phase_adv = adjusted_phase; end end function plot_pitch_comparison(time_src, f0_src, time_ref, f0_ref, f0_corrected, src_wave, ref_wave, corr_wave, fs) % 确保所有序列长度一致 min_length = min([length(time_src), length(time_ref), length(f0_corrected)]); time_src = time_src(1:min_length); f0_src = f0_src(1:min_length); time_ref = time_ref(1:min_length); f0_ref = f0_ref(1:min_length); f0_corrected = f0_corrected(1:min_length); % 创建综合音高对比图(包含波形和音高) pitch_fig = figure('Name', '音频波形与音高分析', 'Position', [100 100 900 800]); % 原始音频波形 + 音高 subplot(3,1,1); time_wave_src = (1:length(src_wave)) / fs; yyaxis left; plot(time_wave_src, src_wave, 'Color', [0.7 0.7 1], 'LineWidth', 0.5); ylabel('幅度'); ylim([-1.1 1.1]); % 固定幅度范围 yyaxis right; plot(time_src, f0_src, 'b', 'LineWidth', 1.5); hold on; plot(time_ref, f0_ref, 'r--', 'LineWidth', 1.5); hold off; title('原始音频波形与音高'); xlabel('时间 (s)'); ylabel('频率 (Hz)'); legend('原始波形', '原始音高', '参考音高', 'Location', 'best'); grid on; % 参考音频波形 + 音高 subplot(3,1,2); time_wave_ref = (1:length(ref_wave)) / fs; yyaxis left; plot(time_wave_ref, ref_wave, 'Color', [1 0.7 0.7], 'LineWidth', 0.5); ylabel('幅度'); ylim([-1.1 1.1]); % 固定幅度范围 yyaxis right; plot(time_ref, f0_ref, 'r', 'LineWidth', 1.5); title('参考音频波形与音高'); xlabel('时间 (s)'); ylabel('频率 (Hz)'); legend('参考波形', '参考音高', 'Location', 'best'); grid on; % 矫正后音频波形 + 音高 subplot(3,1,3); time_wave_corr = (1:length(corr_wave)) / fs; yyaxis left; plot(time_wave_corr, corr_wave, 'Color', [0.7 1 0.7], 'LineWidth', 0.5); ylabel('幅度'); ylim([-1.1 1.1]); % 固定幅度范围 yyaxis right; plot(time_src, f0_src, 'b:', 'LineWidth', 1); hold on; plot(time_ref, f0_ref, 'r--', 'LineWidth', 1); plot(time_src, f0_corrected, 'g', 'LineWidth', 2); hold off; title('矫正后音频波形与音高'); xlabel('时间 (s)'); ylabel('频率 (Hz)'); legend('矫正波形', '原始音高', '参考音高', '矫正音高', 'Location', 'best'); grid on; % 添加音高误差分析 valid_idx = (f0_src > 0) & (f0_ref > 0) & (f0_corrected > 0); if any(valid_idx) src_error = mean(abs(f0_src(valid_idx) - f0_ref(valid_idx))); corr_error = mean(abs(f0_corrected(valid_idx) - f0_ref(valid_idx))); annotation(pitch_fig, 'textbox', [0.15 0.05 0.7 0.05], ... 'String', sprintf('原始音高平均误差: %.2f Hz | 矫正后音高平均误差: %.2f Hz | 改进: %.1f%%', ... src_error, corr_error, (src_error - corr_error)/src_error*100), ... 'FitBoxToText', 'on', 'BackgroundColor', [0.9 0.9 0.9], ... 'FontSize', 12, 'HorizontalAlignment', 'center'); end end function play_audio(fig, audio_type) if ~isvalid(fig) errordlg('主窗口无效!', '播放错误'); return; end switch audio_type case 'source' audio = fig.UserData.source_audio; title_text = '播放原始音频'; if isempty(audio) errordlg('未找到原始音频据!', '播放错误'); return; end case 'corrected' audio = fig.UserData.corrected_audio; title_text = '播放矫正音频'; if isempty(audio) errordlg('请先完成音高校正!', '播放错误'); return; end otherwise return; end fs = fig.UserData.fs; player = audioplayer(audio, fs); % 创建播放控制界面 play_fig = uifigure('Name', title_text, 'Position', [500 500 300 150]); % 播放进度条 ax = uiaxes(play_fig, 'Position', [50 100 200 20]); hold(ax, 'on'); prog_line = plot(ax, [0 0], [0 1], 'b', 'LineWidth', 2); % 垂直范围[0,1] hold(ax, 'off'); xlim(ax, [0 1]); ylim(ax, [0 1]); set(ax, 'XTick', [], 'YTick', []); % 播放时间显示 time_label = uilabel(play_fig, 'Position', [50 80 200 20], ... 'Text', '00:00 / 00:00', 'HorizontalAlignment', 'center'); % 控制按钮 uibutton(play_fig, 'Position', [50 30 60 30], 'Text', '播放', ... 'ButtonPushedFcn', @(btn,event) play(player)); uibutton(play_fig, 'Position', [120 30 60 30], 'Text', '暂停', ... 'ButtonPushedFcn', @(btn,event) pause(player)); uibutton(play_fig, 'Position', [190 30 60 30], 'Text', '停止', ... 'ButtonPushedFcn', @(btn,event) stop(player)); % 总时长计算 total_time = length(audio)/fs; mins = floor(total_time/60); secs = round(total_time - mins*60); total_str = sprintf('%02d:%02d', mins, secs); % 更新播放进度回调 player.TimerFcn = {@update_playback, play_fig, time_label, total_str, prog_line, length(audio)}; player.TimerPeriod = 0.1; % 更新频率(秒) player.StopFcn = @(src,event) stop_playback(src, event, play_fig); end function stop_playback(src, ~, fig) stop(src); if isvalid(fig) close(fig); end end function save_audio(fig) if ~isvalid(fig) || isempty(fig.UserData.corrected_audio) errordlg('无有效音频据可保存!', '保存错误'); return; end [file, path] = uiputfile('*.wav', '保存矫正音频'); if isequal(file, 0), return; end audiowrite(fullfile(path, file), fig.UserData.corrected_audio, fig.UserData.fs); msgbox('音频保存成功!', '完成'); end 这是全部的代码,现在的问题是,校准的音高没有保存到据中,音频音高改变应该对应着波形、频频率的改变,当生成的音频保存后再次导入后发现音高没有被保存下来
06-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值