📄 sdl_audio.cpp
字号:
/* * Copyright (C) 2005-2007 gulikoza * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* $Id$ */#include "main.h"#include "video.h"#include "timer.h"#include <math.h>#include "samplerate.h"//#define DEBUG#include "log.h"#define MODULE "sdl_audio"#define SAMPLES 2048void audio_callback(void *userdata, Uint8 *stream, int len);void init_audio(void){ /* Allocate decoding buffer */ sdl.audio = (Audioinfo*)malloc(sizeof(Audioinfo)); SDL_memset(sdl.audio, 0, sizeof(Audioinfo)); // Should hold about 2.5 sec sdl.audio->data = (float*)_aligned_malloc(sizeof(char)*BUFFERSIZE, 64); sdl.audio->data_out = (float*)_aligned_malloc(sizeof(float)*2*SAMPLES, 64); sdl.audio->audio_change = SDL_CreateSemaphore(0); sdl.audio->volume = 1.0f; sdl.audio->prevVolume = 1.0f; sdl.audio->src_state = src_new(SRC_LINEAR, 2, NULL); sdl_bar.SetVolume(lrintf(sdl.audio->volume*100), 0);}void reinit_audio(unsigned int freq){ if(!sdl.audio) return; if(sdl.audio->spec == NULL) { /* Open the audio device */ SDL_AudioSpec desired; /* Allocate space for the obtained SDL_AudioSpec */ sdl.audio->spec = (SDL_AudioSpec*)malloc(sizeof(SDL_AudioSpec)); /* Sampling frequency */ desired.freq = freq; /* 16-bit signed audio */ desired.format = AUDIO_S16SYS; /* Stereo */ desired.channels = 2; /* Large audio buffer reduces risk of dropouts but increases response time */ desired.samples = SAMPLES; /* Our callback function */ desired.callback = audio_callback; desired.userdata = NULL; /* Open the audio device */ if(SDL_OpenAudio(&desired, sdl.audio->spec) < 0) { ERROR_MSG("Couldn't open audio: %s", SDL_GetError()); SDL_SemPost(sdl.audio->audio_change); return; } ERROR_MSG("Opened audio device at %d Hz, %s", sdl.audio->spec->freq, sdl.audio->spec->channels == 2 ? "stereo":"mono"); } SDL_LockAudio(); sdl.audio->pts = 0.0; sdl.audio->w_pos = sdl.audio->r_pos = 0; sdl.audio->ptsstart = 0; sdl.audio->size = 0; sdl.audio->ratio = ((double)sdl.audio->spec->freq)/freq; src_reset(sdl.audio->src_state); LOG_MSG("Resample ratio set to %lf (source freq: %d)", sdl.audio->ratio, freq); SDL_UnlockAudio(); SDL_PauseAudio(0); SDL_SemPost(sdl.audio->audio_change);}void close_audio(const bool clear){ if(sdl.audio) { if(sdl.audio->spec) { SDL_CloseAudio(); free(sdl.audio->spec); sdl.audio->spec = NULL; } if(clear) { src_delete(sdl.audio->src_state); _aligned_free(sdl.audio->data); _aligned_free(sdl.audio->data_out); SDL_DestroySemaphore(sdl.audio->audio_change); free(sdl.audio); sdl.audio = NULL; } }}void audio_callback(void *userdata, Uint8 *stream, int len){ int freq = sdl.audio->spec->freq; unsigned int buf_pts = (unsigned int)lrint(sdl.audio->pts); if(sdl.audio->r_pos == 0) { if(sdl.audio->ptsstart == 0) { for(buf_pts = 0; buf_pts < (unsigned int)len; buf_pts++) *stream++ = 0x0; LOG_MSG("No audio PTS, nothing to play"); return; } LOG_MSG("Setting audio PTS to %u", sdl.audio->ptsstart); // Calibrate PTS each buffer start if(sdl.audio->ptsstart >= buf_pts) { sdl.audio->pts = (double)sdl.audio->ptsstart; buf_pts = sdl.audio->ptsstart; } else { ERROR_MSG("%u is smaller than %.2f, missing timestamp?", sdl.audio->ptsstart, sdl.audio->pts); } } // Get audio delay (in samples) int delay = sdl.audio->delay; unsigned int pts = timer->GetPTS(); if(delay > 0) { LOG_MSG("Audio buffer delay is %d samples, %d ms", delay, delay*1000/freq); delay = delay*1000/freq; } else {#if (SDL_IS_PATCHED) sdl.audio->delay = SDL_AudioDelay(); LOG_MSG("Failed to get buffer delay");#endif delay = 0; } LOG_MSG("Requested %d bytes (%d ms, %d samples), current PTS: %u, should play at PTS %u", len, len*1000/(freq<<2), (len>>1), pts, pts+delay); pts += delay; delay = 0; // Mix as much data as possible double ratio = sdl.audio->ratio; // Current audio pts is sdl.audio->pts if(pts+5 < buf_pts) { // Audio buffer position is more than 1/2 call ahead if(buf_pts-pts > (unsigned int)(len*1000)/(freq<<3)) { LOG_MSG("Playback is %u ms ahead (buffer PTS: %.2f), dropping entire frame", buf_pts-pts, sdl.audio->pts); for(buf_pts = 0; buf_pts < (unsigned int)len; buf_pts++) *stream++ = 0x0; return; } // Mix fewer samples, increase samplerate delay = (buf_pts-pts); delay *= (-freq)/(32*1000); LOG_MSG("Playback is %u ms ahead, mixing %d samples less", buf_pts-pts, -delay); } else if(pts > buf_pts+5) { // Audio buffer position is behind // Try to mix additional samples, lower samplerate delay = ((pts-buf_pts)*freq)/(32*1000); LOG_MSG("Playback is %u ms behind, mixing %d more samples", pts-buf_pts, delay); } LOG_MSG("Audio buffer PTS: %.2f, %d ms jitter", sdl.audio->pts, (int)buf_pts - (int)pts); // Calculate samplerate ratio if(delay != 0) ratio = ratio * ((double)(len>>2)/((len>>2)+delay)); LOG_MSG("Frequency resample ratio: %lf, frames needed: %d", ratio, len>>2); SRC_DATA src_data; src_data.end_of_input = 0; src_data.src_ratio = ratio; // input_frames = floats for each channel src_data.input_frames = (sdl.audio->size-sdl.audio->r_pos)>>1; src_data.data_in = sdl.audio->data+sdl.audio->r_pos; src_data.output_frames = len>>2; src_data.data_out = sdl.audio->data_out; src_data.input_frames_used = 0; delay = 0;write_audio: if((buf_pts=src_process(sdl.audio->src_state, &src_data))) { ERROR_MSG("Error SRC returned %s", src_strerror(buf_pts)); return; } delay += src_data.output_frames_gen; src_data.input_frames -= src_data.input_frames_used; sdl.audio->r_pos += src_data.input_frames_used<<1; src_data.output_frames -= src_data.output_frames_gen; LOG_MSG("%d frames used, %d frames generated (%d available)", src_data.input_frames_used, src_data.output_frames_gen, src_data.input_frames); // Advance PTS sdl.audio->pts += (double)(src_data.output_frames_gen*1000.0)/(freq*ratio); LOG_MSG("Audio played, PTS advance: %.2f, new PTS: %.2f", (double)(src_data.output_frames_gen*1000.0)/(freq*ratio), sdl.audio->pts); if(src_data.output_frames) { LOG_MSG("%d samples missing", src_data.output_frames); if(src_data.input_frames == 0) { if(sdl.audio->w_pos < sdl.audio->size) { sdl.audio->r_pos = 0; sdl.audio->size = sdl.audio->w_pos; src_data.data_in = sdl.audio->data; src_data.input_frames = sdl.audio->size>>1; src_data.data_out += src_data.output_frames_gen<<1; LOG_MSG("Syncing buffer pts %.2f to %u (%.2f)", sdl.audio->pts, sdl.audio->ptsstart, sdl.audio->ptsstart-sdl.audio->pts); sdl.audio->pts = sdl.audio->ptsstart; } if(src_data.input_frames != 0) goto write_audio; ERROR_MSG("Audio buffer underrun!"); } } // Adjust volume, convert to short and write to output buffer ratio = (8.0 * 0x10000000) * sdl.audio->volume; signed short * oup = (signed short*)stream; float * inp = sdl.audio->data_out; // Data is stereo delay = delay<<1; while(delay--) { float scaled_value = (*inp++) * ratio; if(scaled_value > (1.0 * 0x7FFFFFFE)) { *oup++ = 32767; continue; } if(scaled_value < (-8.0 * 0xFFFFFFF)) { *oup++ = -32768; continue; } *oup++ = (lrintf(scaled_value) >> 16); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -