This patch adds prefetch buffering to the sequencer (see the tracker for additional details: https://projects.blender.org/tracker/?func=detail&aid=7307&group_id=9&atid=127 ) We create seperate render threads (currently one, because of the fact, that sequence rendering modifies global structures...), that render up to the defined userpref value "Prefetch frames" in advance. (Pressing Alt-A will _first_ fill the buffer and then start playing.) Bassam and I did some extensive testing, so it should work. If you don't configure your number of prefetch frames, prefetching is disabled! (Sane defaults... :) Also: if the machine is definitely too slow and runs out of the prefetch area, prefetching is disabled automatically and we are back to good old frame skipping mode. My Dual Athlon is able to handle 4 parallel DV streams at once (sometimes a little bit choppy, but prefetching is never disabled!) I fixed also a long standing bug in the audio code, that made playback run backwards at the beginning...
550 lines
12 KiB
C
550 lines
12 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
|
|
*
|
|
* 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. The Blender
|
|
* Foundation also sells licenses for use in proprietary software under
|
|
* the Blender License. See http://www.blender.org/BL/ for information
|
|
* about this.
|
|
*
|
|
* 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.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): intrr, Peter Schlaile
|
|
*
|
|
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifndef WIN32
|
|
#define __USE_XOPEN /* Needed for swab on linux */
|
|
#include <unistd.h>
|
|
#undef __USE_XOPEN
|
|
#else
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "PIL_time.h"
|
|
|
|
#include "BMF_Api.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_arithb.h"
|
|
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_sound_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_sequence_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_ipo_types.h"
|
|
|
|
#include "BKE_utildefines.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_library.h"
|
|
#include "BKE_blender.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_ipo.h"
|
|
|
|
#include "BIF_gl.h"
|
|
#include "BIF_graphics.h"
|
|
#include "BIF_keyval.h"
|
|
#include "BIF_mainqueue.h"
|
|
#include "BIF_resources.h"
|
|
#include "BIF_screen.h"
|
|
#include "BIF_toolbox.h"
|
|
#include "BIF_mywindow.h"
|
|
#include "BIF_space.h"
|
|
#include "BIF_glutil.h"
|
|
#include "BIF_interface.h"
|
|
|
|
#include "BSE_view.h"
|
|
#include "BSE_seqaudio.h"
|
|
#include "BIF_editsound.h"
|
|
|
|
#include "mydevice.h"
|
|
#include "blendef.h"
|
|
|
|
|
|
void audio_fill(void *mixdown, Uint8 *sstream, int len);
|
|
/* ************ GLOBALS ************* */
|
|
|
|
static int audio_pos;
|
|
static int audio_scrub=0;
|
|
static int audio_playing=0;
|
|
static int audio_initialised=0;
|
|
static int audio_startframe=0;
|
|
/////
|
|
//
|
|
/* local protos ------------------- */
|
|
void audio_mixdown(void);
|
|
|
|
void makewavstring (char *string)
|
|
{
|
|
char txt[64];
|
|
|
|
if (string==0) return;
|
|
|
|
strcpy(string, G.scene->r.pic);
|
|
BLI_convertstringcode(string, G.sce, G.scene->r.cfra);
|
|
|
|
BLI_make_existing_file(string);
|
|
|
|
if (BLI_strcasecmp(string + strlen(string) - 4, ".wav")) {
|
|
sprintf(txt, "%04d_%04d.wav", (G.scene->r.sfra) , (G.scene->r.efra) );
|
|
strcat(string, txt);
|
|
}
|
|
}
|
|
|
|
void audio_mixdown()
|
|
{
|
|
int file, c, totlen, totframe, i, oldcfra;
|
|
char *buf;
|
|
|
|
buf = MEM_mallocN(65536, "audio_mixdown");
|
|
makewavstring(buf);
|
|
|
|
file = open(buf, O_BINARY+O_WRONLY+O_CREAT+O_TRUNC, 0666);
|
|
|
|
if(file == -1)
|
|
{
|
|
error("Can't open output file");
|
|
return;
|
|
}
|
|
|
|
waitcursor(1);
|
|
|
|
printf("Saving: %s ", buf);
|
|
|
|
strcpy(buf, "RIFFlengWAVEfmt fmln01ccRATEbsecBP16dataDLEN");
|
|
totframe = (EFRA - SFRA + 1);
|
|
totlen = (int) ( ((float)totframe / (float)G.scene->r.frs_sec) * (float)G.scene->audio.mixrate * 4.0);
|
|
printf(" totlen %d\n", totlen+36+8);
|
|
|
|
totlen+= 36; /* len is filesize-8 in WAV spec, total header is 44 bytes */
|
|
memcpy(buf+4, &totlen, 4);
|
|
totlen-= 36;
|
|
|
|
buf[16] = 0x10; buf[17] = buf[18] = buf[19] = 0; buf[20] = 1; buf[21] = 0;
|
|
buf[22] = 2; buf[23]= 0;
|
|
memcpy(buf+24, &G.scene->audio.mixrate, 4);
|
|
i = G.scene->audio.mixrate * 4;
|
|
memcpy(buf+28, &i, 4);
|
|
buf[32] = 4; buf[33] = 0; buf[34] = 16; buf[35] = 0;
|
|
i = totlen;
|
|
memcpy(buf+40, &i, 4);
|
|
|
|
if (G.order == B_ENDIAN) {
|
|
/* swap the four ints to little endian */
|
|
|
|
/* length */
|
|
SWITCH_INT(buf[4]);
|
|
|
|
/* audio rate */
|
|
SWITCH_INT(buf[24]);
|
|
|
|
/* audio mixrate * 4 */
|
|
SWITCH_INT(buf[28]);
|
|
|
|
/* length */
|
|
SWITCH_INT(buf[40]);
|
|
}
|
|
|
|
c = write(file, buf, 44);
|
|
|
|
oldcfra = CFRA;
|
|
audiostream_play(SFRA, 0, 1);
|
|
|
|
i= 0;
|
|
while ( totlen > 0 ) {
|
|
totlen -= 64;
|
|
|
|
memset(buf+i, 0, 64);
|
|
|
|
CFRA=(int) ( ((float)(audio_pos-64)/( G.scene->audio.mixrate*4 ))*(float)G.scene->r.frs_sec );
|
|
|
|
audio_fill(buf+i, NULL, 64);
|
|
if (G.order == B_ENDIAN) {
|
|
char tbuf[64];
|
|
memcpy(tbuf, buf+i, 64);
|
|
swab(tbuf, buf+i, 64);
|
|
}
|
|
if (i == (65536-64)) {
|
|
i=0;
|
|
write(file, buf, 65536);
|
|
}
|
|
else i+=64;
|
|
}
|
|
write(file, buf, i);
|
|
|
|
waitcursor(0);
|
|
CFRA = oldcfra;
|
|
close(file);
|
|
MEM_freeN(buf);
|
|
|
|
return;
|
|
}
|
|
|
|
void audiostream_fill(Uint8 *mixdown, int len)
|
|
{
|
|
int oldcfra = CFRA;
|
|
int i;
|
|
|
|
memset(mixdown, 0, len);
|
|
|
|
for (i = 0; i < len; i += 64) {
|
|
CFRA = (int) ( ((float)(audio_pos-64)
|
|
/( G.scene->audio.mixrate*4 ))
|
|
*(float)G.scene->r.frs_sec );
|
|
|
|
audio_fill(mixdown + i, NULL,
|
|
(len - i) > 64 ? 64 : (len - i));
|
|
}
|
|
|
|
CFRA = oldcfra;
|
|
}
|
|
|
|
|
|
static void audio_levels(Uint8 *buf, int len, float db, float facf, float pan)
|
|
{
|
|
int i;
|
|
float facl, facr, fac;
|
|
signed short *sample;
|
|
|
|
if (pan>=0) { facr = 1.0; facl = 1.0-pan; }
|
|
else { facr = pan+1.0; facl = 1.0; }
|
|
|
|
fac = pow(10.0, ((-(db+G.scene->audio.main))/20.0)) / facf;
|
|
facl /= fac;
|
|
facr /= fac;
|
|
|
|
for (i=0; i<len; i+=4) {
|
|
sample = (signed short*)(buf+i);
|
|
sample[0] = (short) ((float)sample[0] * facl);
|
|
sample[1] = (short) ((float)sample[1] * facr);
|
|
}
|
|
}
|
|
|
|
/* convert mono/stereo and sampling rate, alloc a buffer for
|
|
* sound->stream to contain the new sample, and set sound->streamlen
|
|
* accordingly.
|
|
*/
|
|
void audio_makestream(bSound *sound)
|
|
{
|
|
signed short *source, *dest;
|
|
float ratio;
|
|
int i;
|
|
|
|
if ( (!sound)||(sound->stream)||(!sound->sample)||(!G.scene) ) {
|
|
return;
|
|
}
|
|
ratio = (float)G.scene->audio.mixrate / (float)sound->sample->rate;
|
|
sound->streamlen = (int) ( (float)sound->sample->len * ratio * 2.0/((float)sound->sample->channels) );
|
|
sound->stream = malloc((int) ((float)sound->streamlen * 1.05));
|
|
if (sound->sample->rate == G.scene->audio.mixrate) {
|
|
if (sound->sample->channels == 2) {
|
|
memcpy(sound->stream, sound->sample->data, sound->streamlen);
|
|
return;
|
|
} else {
|
|
for (source = (signed short*)(sound->sample->data),
|
|
dest = (signed short*)(sound->stream),
|
|
i=0;
|
|
i<sound->streamlen/4;
|
|
dest += 2, source++, i++) dest[0] = dest[1] = source[0];
|
|
return;
|
|
}
|
|
}
|
|
if (sound->sample->channels == 1) {
|
|
for (dest=(signed short*)(sound->stream), i=0, source=(signed short*)(sound->sample->data);
|
|
i<(sound->streamlen/4); dest+=2, i++)
|
|
dest[0] = dest[1] = source[(int)((float)i/ratio)];
|
|
}
|
|
else if (sound->sample->channels == 2) {
|
|
for (dest=(signed short*)(sound->stream), i=0, source=(signed short*)(sound->sample->data);
|
|
i<(sound->streamlen/2); dest+=2, i+=2) {
|
|
dest[1] = source[(int)((float)i/ratio)];
|
|
dest[0] = source[(int)((float)i/ratio)+1];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void audio_fill_ram_sound(Sequence *seq, void * mixdown,
|
|
Uint8 * sstream, int len)
|
|
{
|
|
Uint8* cvtbuf;
|
|
bSound* sound;
|
|
float facf;
|
|
|
|
sound = seq->sound;
|
|
audio_makestream(sound);
|
|
if ((seq->curpos<sound->streamlen -len) && (seq->curpos>=0) &&
|
|
(seq->startdisp <= CFRA) && ((seq->enddisp) > CFRA))
|
|
{
|
|
if(seq->ipo && seq->ipo->curve.first) {
|
|
do_seq_ipo(seq);
|
|
facf = seq->facf0;
|
|
} else {
|
|
facf = 1.0;
|
|
}
|
|
cvtbuf = malloc(len);
|
|
memcpy(cvtbuf, ((Uint8*)sound->stream)+(seq->curpos & (~3)), len);
|
|
audio_levels(cvtbuf, len, seq->level, facf, seq->pan);
|
|
if (!mixdown) {
|
|
SDL_MixAudio(sstream, cvtbuf, len, SDL_MIX_MAXVOLUME);
|
|
} else {
|
|
SDL_MixAudio((Uint8*)mixdown, cvtbuf, len, SDL_MIX_MAXVOLUME);
|
|
}
|
|
free(cvtbuf);
|
|
}
|
|
seq->curpos += len;
|
|
}
|
|
|
|
static void audio_fill_hd_sound(Sequence *seq,
|
|
void * mixdown, Uint8 * sstream,
|
|
int len)
|
|
{
|
|
Uint8* cvtbuf;
|
|
float facf;
|
|
|
|
if ((seq->curpos >= 0) &&
|
|
(seq->startdisp <= CFRA) && ((seq->enddisp) > CFRA))
|
|
{
|
|
if(seq->ipo && seq->ipo->curve.first) {
|
|
do_seq_ipo(seq);
|
|
facf = seq->facf0;
|
|
} else {
|
|
facf = 1.0;
|
|
}
|
|
cvtbuf = malloc(len);
|
|
|
|
sound_hdaudio_extract(seq->hdaudio, (short*) cvtbuf,
|
|
seq->curpos / 4,
|
|
G.scene->audio.mixrate,
|
|
2,
|
|
len / 4);
|
|
audio_levels(cvtbuf, len, seq->level, facf, seq->pan);
|
|
if (!mixdown) {
|
|
SDL_MixAudio(sstream,
|
|
cvtbuf, len, SDL_MIX_MAXVOLUME);
|
|
} else {
|
|
SDL_MixAudio((Uint8*)mixdown,
|
|
cvtbuf, len, SDL_MIX_MAXVOLUME);
|
|
}
|
|
free(cvtbuf);
|
|
}
|
|
seq->curpos += len;
|
|
}
|
|
|
|
static void audio_fill_seq(Sequence * seq, void * mixdown,
|
|
Uint8 *sstream, int len)
|
|
{
|
|
while(seq) {
|
|
if (seq->type == SEQ_META) {
|
|
audio_fill_seq(seq->seqbase.first,
|
|
mixdown, sstream, len);
|
|
}
|
|
if ( (seq->type == SEQ_RAM_SOUND) &&
|
|
(seq->sound) &&
|
|
(!(seq->flag & SEQ_MUTE))) {
|
|
audio_fill_ram_sound(seq, mixdown, sstream, len);
|
|
}
|
|
if ( (seq->type == SEQ_HD_SOUND) &&
|
|
(!(seq->flag & SEQ_MUTE))) {
|
|
if (!seq->hdaudio) {
|
|
char name[FILE_MAXDIR+FILE_MAXFILE];
|
|
|
|
strncpy(name, seq->strip->dir,
|
|
FILE_MAXDIR-1);
|
|
strncat(name,
|
|
seq->strip->stripdata->name,
|
|
FILE_MAXFILE-1);
|
|
BLI_convertstringcode(name, G.sce,
|
|
G.scene->r.cfra);
|
|
|
|
seq->hdaudio= sound_open_hdaudio(name);
|
|
}
|
|
if (seq->hdaudio) {
|
|
audio_fill_hd_sound(seq, mixdown,
|
|
sstream, len);
|
|
}
|
|
}
|
|
seq = seq->next;
|
|
}
|
|
}
|
|
|
|
void audio_fill(void *mixdown, Uint8 *sstream, int len)
|
|
{
|
|
Editing *ed;
|
|
Sequence *seq;
|
|
|
|
ed = G.scene->ed;
|
|
if((ed) && (!(G.scene->audio.flag & AUDIO_MUTE))) {
|
|
seq = ed->seqbasep->first;
|
|
audio_fill_seq(seq, mixdown, sstream, len);
|
|
}
|
|
|
|
audio_pos += len;
|
|
if (audio_scrub) {
|
|
audio_scrub--;
|
|
if (!audio_scrub) {
|
|
audiostream_stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
static int audio_init(SDL_AudioSpec *desired)
|
|
{
|
|
SDL_AudioSpec *obtained, *hardware_spec;
|
|
|
|
SDL_CloseAudio();
|
|
|
|
obtained = (SDL_AudioSpec*)MEM_mallocN(sizeof(SDL_AudioSpec),
|
|
"SDL_AudioSpec");
|
|
|
|
desired->callback=audio_fill;
|
|
|
|
if ( SDL_OpenAudio(desired, obtained) < 0 ) {
|
|
fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
|
|
if (obtained) MEM_freeN(obtained);
|
|
return 0;
|
|
}
|
|
audio_initialised = 1;
|
|
hardware_spec=obtained;
|
|
|
|
MEM_freeN(obtained);
|
|
|
|
SDL_PauseAudio(0);
|
|
return 1;
|
|
}
|
|
|
|
static int audiostream_play_seq(Sequence * seq, Uint32 startframe)
|
|
{
|
|
char name[FILE_MAXDIR+FILE_MAXFILE];
|
|
int have_sound = 0;
|
|
|
|
while(seq) {
|
|
if (seq->type == SEQ_META) {
|
|
if (audiostream_play_seq(
|
|
seq->seqbase.first, startframe)) {
|
|
have_sound = 1;
|
|
}
|
|
}
|
|
if ((seq->type == SEQ_RAM_SOUND) && (seq->sound)) {
|
|
have_sound = 1;
|
|
seq->curpos = (int)( (((float)((float)startframe
|
|
-(float)seq->start)
|
|
/ (float)G.scene->r.frs_sec)
|
|
* ((float)G.scene->audio.mixrate)
|
|
* 4 ));
|
|
}
|
|
if ((seq->type == SEQ_HD_SOUND)) {
|
|
have_sound = 1;
|
|
if (!seq->hdaudio) {
|
|
strncpy(name, seq->strip->dir, FILE_MAXDIR-1);
|
|
strncat(name, seq->strip->stripdata->name,
|
|
FILE_MAXFILE-1);
|
|
|
|
seq->hdaudio = sound_open_hdaudio(name);
|
|
}
|
|
seq->curpos = (int)( (((float)((float)startframe
|
|
- (float)seq->start)
|
|
/ (float)G.scene->r.frs_sec)
|
|
* ((float)G.scene->audio.mixrate)
|
|
* 4 ));
|
|
}
|
|
seq= seq->next;
|
|
}
|
|
return have_sound;
|
|
}
|
|
|
|
void audiostream_play(Uint32 startframe, Uint32 duration, int mixdown)
|
|
{
|
|
static SDL_AudioSpec desired;
|
|
Editing *ed;
|
|
int have_sound = 0;
|
|
|
|
ed= G.scene->ed;
|
|
if(ed) {
|
|
have_sound =
|
|
audiostream_play_seq(ed->seqbasep->first, startframe);
|
|
}
|
|
|
|
if(have_sound) {
|
|
/* this call used to be in startup */
|
|
sound_init_audio();
|
|
}
|
|
|
|
if (!audio_initialised && !(duration + mixdown)) {
|
|
desired.freq=G.scene->audio.mixrate;
|
|
desired.format=AUDIO_S16SYS;
|
|
desired.channels=2;
|
|
desired.samples=U.mixbufsize;
|
|
desired.userdata=0;
|
|
if (audio_init(&desired)==0) {
|
|
U.mixbufsize = 0; /* no audio */
|
|
}
|
|
}
|
|
|
|
audio_startframe = startframe;
|
|
audio_pos = ( ((int)( (( (double)startframe)
|
|
/(double)G.scene->r.frs_sec)
|
|
*(G.scene->audio.mixrate)*4 )) & (~3) );
|
|
|
|
audio_scrub = duration;
|
|
if (!mixdown) {
|
|
SDL_PauseAudio(0);
|
|
audio_playing++;
|
|
}
|
|
}
|
|
|
|
void audiostream_start(Uint32 frame)
|
|
{
|
|
audiostream_play(frame, 0, 0);
|
|
}
|
|
|
|
void audiostream_scrub(Uint32 frame)
|
|
{
|
|
if (U.mixbufsize) audiostream_play(frame, 4096/U.mixbufsize, 0);
|
|
}
|
|
|
|
void audiostream_stop(void)
|
|
{
|
|
SDL_PauseAudio(1);
|
|
audio_playing=0;
|
|
}
|
|
|
|
int audiostream_pos(void)
|
|
{
|
|
int pos;
|
|
|
|
pos = (int) (((double)(audio_pos-U.mixbufsize)
|
|
/ ( G.scene->audio.mixrate*4 ))
|
|
* (double)G.scene->r.frs_sec );
|
|
|
|
if (pos < audio_startframe) pos = audio_startframe;
|
|
return ( pos );
|
|
}
|
|
|