Files
lmms/src/core/sample_buffer.cpp
Tobias Doerffel 9e4e576de9 * fixed out-of-boundary array access when reversing samples
* optimized loops for loading samples



git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@1793 0778d3d1-df1d-0410-868b-ea421aaaa00d
2008-10-25 10:12:44 +00:00

1208 lines
26 KiB
C++

#ifndef SINGLE_SOURCE_COMPILE
/*
* sample_buffer.cpp - container-class sampleBuffer
*
* Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "sample_buffer.h"
#include "mixer.h"
#include <QtCore/QBuffer>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
#include <QtGui/QPainter>
#include <cstring>
#include <sndfile.h>
#ifdef LMMS_HAVE_OGGVORBIS
#include <vorbis/vorbisfile.h>
#endif
#ifdef LMMS_HAVE_FLAC_STREAM_ENCODER_H
#include <FLAC/stream_encoder.h>
#endif
#ifdef LMMS_HAVE_FLAC_STREAM_DECODER_H
#include <FLAC/stream_decoder.h>
#endif
#include "base64.h"
#include "config_mgr.h"
#include "debug.h"
#include "drumsynth.h"
#include "endian_handling.h"
#include "engine.h"
#include "interpolation.h"
#include "templates.h"
sampleBuffer::sampleBuffer( const QString & _audio_file,
bool _is_base64_data ) :
m_audioFile( ( _is_base64_data == TRUE ) ? "" : _audio_file ),
m_origData( NULL ),
m_origFrames( 0 ),
m_data( NULL ),
m_frames( 0 ),
m_startFrame( 0 ),
m_endFrame( 0 ),
m_loopStartFrame( 0 ),
m_loopEndFrame( 0 ),
m_amplification( 1.0f ),
m_reversed( FALSE ),
m_frequency( BaseFreq ),
m_sampleRate( engine::getMixer()->baseSampleRate() )
{
if( _is_base64_data == TRUE )
{
loadFromBase64( _audio_file );
}
update();
}
sampleBuffer::sampleBuffer( const sampleFrame * _data, const f_cnt_t _frames ) :
m_audioFile( "" ),
m_origData( NULL ),
m_origFrames( 0 ),
m_data( NULL ),
m_frames( 0 ),
m_startFrame( 0 ),
m_endFrame( 0 ),
m_loopStartFrame( 0 ),
m_loopEndFrame( 0 ),
m_amplification( 1.0f ),
m_reversed( FALSE ),
m_frequency( BaseFreq ),
m_sampleRate( engine::getMixer()->baseSampleRate() )
{
if( _frames > 0 )
{
m_origData = new sampleFrame[_frames];
memcpy( m_origData, _data, _frames * BYTES_PER_FRAME );
m_origFrames = _frames;
}
update();
}
sampleBuffer::sampleBuffer( const f_cnt_t _frames ) :
m_audioFile( "" ),
m_origData( NULL ),
m_origFrames( 0 ),
m_data( NULL ),
m_frames( 0 ),
m_startFrame( 0 ),
m_endFrame( 0 ),
m_loopStartFrame( 0 ),
m_loopEndFrame( 0 ),
m_amplification( 1.0f ),
m_reversed( FALSE ),
m_frequency( BaseFreq ),
m_sampleRate( engine::getMixer()->baseSampleRate() )
{
if( _frames > 0 )
{
m_origData = new sampleFrame[_frames];
memset( m_origData, 0, _frames * BYTES_PER_FRAME );
m_origFrames = _frames;
}
update();
}
sampleBuffer::~sampleBuffer()
{
delete[] m_origData;
delete[] m_data;
}
void sampleBuffer::update( bool _keep_settings )
{
const bool lock = ( m_data != NULL );
if( lock )
{
engine::getMixer()->lock();
delete[] m_data;
}
if( m_audioFile == "" && m_origData != NULL && m_origFrames > 0 )
{
// TODO: reverse- and amplification-property is not covered
// by following code...
m_data = new sampleFrame[m_origFrames];
memcpy( m_data, m_origData, m_origFrames * BYTES_PER_FRAME );
if( _keep_settings == FALSE )
{
m_frames = m_origFrames;
m_loopStartFrame = m_startFrame = 0;
m_loopEndFrame = m_endFrame = m_frames;
}
}
else if( m_audioFile != "" )
{
QString file = tryToMakeAbsolute( m_audioFile );
char * f = qstrdup( file.toAscii().constData() );
int_sample_t * buf = NULL;
ch_cnt_t channels = DEFAULT_CHANNELS;
sample_rate_t samplerate = engine::getMixer()->baseSampleRate();
m_frames = 0;
if( m_frames == 0 )
{
m_frames = decodeSampleSF( f, buf, channels,
samplerate );
}
#ifdef LMMS_HAVE_OGGVORBIS
if( m_frames == 0 )
{
m_frames = decodeSampleOGGVorbis( f, buf, channels,
samplerate );
}
#endif
if( m_frames == 0 )
{
m_frames = decodeSampleDS( f, buf, channels,
samplerate );
}
delete[] f;
if( m_frames > 0 && buf != NULL )
{
// following code transforms int-samples into
// float-samples and does amplifying & reversing
const float fac = m_amplification /
OUTPUT_SAMPLE_MULTIPLIER;
m_data = new sampleFrame[m_frames];
const int ch = ( channels > 1 ) ? 1 : 0;
// if reversing is on, we also reverse when
// scaling
if( m_reversed )
{
int idx = ( m_frames - 1 ) * channels;
for( f_cnt_t frame = 0; frame < m_frames;
++frame )
{
m_data[frame][0] = buf[idx+0] * fac;
m_data[frame][1] = buf[idx+ch] * fac;
idx -= channels;
}
}
else
{
int idx = 0;
for( f_cnt_t frame = 0; frame < m_frames;
++frame )
{
m_data[frame][0] = buf[idx+0] * fac;
m_data[frame][1] = buf[idx+ch] * fac;
idx += channels;
}
}
delete[] buf;
normalizeSampleRate( samplerate, _keep_settings );
}
else
{
// sample couldn't be decoded, create buffer containing
// one sample-frame
m_data = new sampleFrame[1];
memset( m_data, 0, sizeof( *m_data ) );
m_frames = 1;
m_loopStartFrame = m_startFrame = 0;
m_loopEndFrame = m_endFrame = 1;
}
}
else
{
// neither an audio-file nor a buffer to copy from, so create
// buffer containing one sample-frame
m_data = new sampleFrame[1];
memset( m_data, 0, sizeof( *m_data ) );
m_frames = 1;
m_loopStartFrame = m_startFrame = 0;
m_loopEndFrame = m_endFrame = 1;
}
if( lock )
{
engine::getMixer()->unlock();
}
emit sampleUpdated();
}
void sampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr,
bool _keep_settings )
{
// do samplerate-conversion to our default-samplerate
if( _src_sr != engine::getMixer()->baseSampleRate() )
{
sampleBuffer * resampled = resample( this, _src_sr,
engine::getMixer()->baseSampleRate() );
delete[] m_data;
m_frames = resampled->frames();
m_data = new sampleFrame[m_frames];
memcpy( m_data, resampled->data(), m_frames *
sizeof( sampleFrame ) );
delete resampled;
}
if( _keep_settings == FALSE )
{
// update frame-variables
m_loopStartFrame = m_startFrame = 0;
m_loopEndFrame = m_endFrame = m_frames;
}
}
f_cnt_t sampleBuffer::decodeSampleSF( const char * _f,
int_sample_t * & _buf,
ch_cnt_t & _channels,
sample_rate_t & _samplerate )
{
SNDFILE * snd_file;
SF_INFO sf_info;
f_cnt_t frames = 0;
if( ( snd_file = sf_open( _f, SFM_READ, &sf_info ) ) != NULL )
{
frames = sf_info.frames;
_buf = new int_sample_t[sf_info.channels * frames];
if( sf_read_short( snd_file, _buf, sf_info.channels * frames )
< sf_info.channels * frames )
{
#ifdef DEBUG_LMMS
printf( "sampleBuffer::decodeSampleSF(): could not read"
" sample %s: %s\n", _f, sf_strerror( NULL ) );
#endif
}
_channels = sf_info.channels;
_samplerate = sf_info.samplerate;
sf_close( snd_file );
}
else
{
#ifdef DEBUG_LMMS
printf( "sampleBuffer::decodeSampleSF(): could not load "
"sample %s: %s\n", _f, sf_strerror( NULL ) );
#endif
}
return( frames );
}
#ifdef LMMS_HAVE_OGGVORBIS
// callback-functions for reading ogg-file
size_t qfileReadCallback( void * _ptr, size_t _size, size_t _n, void * _udata )
{
return( static_cast<QFile *>( _udata )->read( (char*) _ptr,
_size * _n ) );
}
int qfileSeekCallback( void * _udata, ogg_int64_t _offset, int _whence )
{
QFile * f = static_cast<QFile *>( _udata );
if( _whence == SEEK_CUR )
{
f->seek( f->pos() + _offset );
}
else if( _whence == SEEK_END )
{
f->seek( f->size() + _offset );
}
else
{
f->seek( _offset );
}
return( 0 );
}
int qfileCloseCallback( void * _udata )
{
delete static_cast<QFile *>( _udata );
return( 0 );
}
long qfileTellCallback( void * _udata )
{
return( static_cast<QFile *>( _udata )->pos() );
}
f_cnt_t sampleBuffer::decodeSampleOGGVorbis( const char * _f,
int_sample_t * & _buf,
ch_cnt_t & _channels,
sample_rate_t & _samplerate )
{
static ov_callbacks callbacks =
{
qfileReadCallback,
qfileSeekCallback,
qfileCloseCallback,
qfileTellCallback
} ;
OggVorbis_File vf;
f_cnt_t frames = 0;
QFile * f = new QFile( _f );
if( f->open( QFile::ReadOnly ) == FALSE )
{
delete f;
return( 0 );
}
int err = ov_open_callbacks( f, &vf, NULL, 0, callbacks );
if( err < 0 )
{
switch( err )
{
case OV_EREAD:
printf( "sampleBuffer::decodeSampleOGGVorbis():"
" media read error\n" );
break;
case OV_ENOTVORBIS:
/* printf( "sampleBuffer::decodeSampleOGGVorbis():"
" not an Ogg Vorbis file\n" );*/
break;
case OV_EVERSION:
printf( "sampleBuffer::decodeSampleOGGVorbis():"
" vorbis version mismatch\n" );
break;
case OV_EBADHEADER:
printf( "sampleBuffer::decodeSampleOGGVorbis():"
" invalid Vorbis bitstream header\n" );
break;
case OV_EFAULT:
printf( "sampleBuffer::decodeSampleOgg(): "
"internal logic fault\n" );
break;
}
delete f;
return( 0 );
}
ov_pcm_seek( &vf, 0 );
_channels = ov_info( &vf, -1 )->channels;
_samplerate = ov_info( &vf, -1 )->rate;
ogg_int64_t total = ov_pcm_total( &vf, -1 );
_buf = new int_sample_t[total * _channels];
int bitstream = 0;
long bytes_read = 0;
do
{
bytes_read = ov_read( &vf, (char *) &_buf[frames * _channels],
( total - frames ) * _channels *
BYTES_PER_INT_SAMPLE,
isLittleEndian() ? 0 : 1,
BYTES_PER_INT_SAMPLE, 1, &bitstream );
if( bytes_read < 0 )
{
break;
}
frames += bytes_read / ( _channels * BYTES_PER_INT_SAMPLE );
}
while( bytes_read != 0 && bitstream == 0 );
ov_clear( &vf );
return( frames );
}
#endif
f_cnt_t sampleBuffer::decodeSampleDS( const char * _f,
int_sample_t * & _buf,
ch_cnt_t & _channels,
sample_rate_t & _samplerate )
{
DrumSynth ds;
return( ds.GetDSFileSamples( _f, _buf, _channels ) );
}
bool sampleBuffer::play( sampleFrame * _ab, handleState * _state,
const fpp_t _frames,
const float _freq,
const bool _looped )
{
QMutexLocker ml( &m_varLock );
engine::getMixer()->clearAudioBuffer( _ab, _frames );
if( m_endFrame == 0 || _frames == 0 )
{
return( FALSE );
}
const double freq_factor = (double) _freq / (double) m_frequency *
m_sampleRate / engine::getMixer()->processingSampleRate();
// calculate how many frames we have in requested pitch
const f_cnt_t total_frames_for_current_pitch = static_cast<f_cnt_t>( (
m_endFrame - m_startFrame ) /
freq_factor );
if( total_frames_for_current_pitch == 0 )
{
return( FALSE );
}
// this holds the number of the first frame to play
f_cnt_t play_frame = _state->m_frameIndex;
if( play_frame < m_startFrame )
{
play_frame = m_startFrame;
}
// this holds the number of remaining frames in current loop
f_cnt_t frames_for_loop;
if( _looped )
{
play_frame = getLoopedIndex( play_frame );
frames_for_loop = static_cast<f_cnt_t>(
( m_loopEndFrame - play_frame ) /
freq_factor );
}
else
{
if( play_frame >= m_endFrame )
{
return( FALSE );
}
frames_for_loop = static_cast<f_cnt_t>(
( m_endFrame - play_frame ) /
freq_factor );
if( frames_for_loop == 0 )
{
return( FALSE );
}
}
sampleFrame * tmp = NULL;
// check whether we have to change pitch...
if( freq_factor != 1.0 || _state->m_varyingPitch )
{
SRC_DATA src_data;
// Generate output
const f_cnt_t margin = 64;
f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor )
+ margin;
src_data.data_in = getSampleFragment( play_frame,
fragment_size, _looped, &tmp )[0];
src_data.data_out = _ab[0];
src_data.input_frames = fragment_size;
src_data.output_frames = _frames;
src_data.src_ratio = 1.0 / freq_factor;
src_data.end_of_input = _state->m_eof;
int error = src_process( _state->m_resamplingData,
&src_data );
if( error )
{
printf( "sampleBuffer: error while resampling: %s\n",
src_strerror( error ) );
}
if( src_data.output_frames_gen > _frames )
{
printf( "sampleBuffer: not enough frames: %ld / %d\n",
src_data.output_frames_gen, _frames );
}
_state->m_eof = src_data.end_of_input;
// Advance
play_frame += src_data.input_frames_used;
if( _looped )
{
play_frame = getLoopedIndex( play_frame );
}
}
else
{
// we don't have to pitch, so we just copy the sample-data
// as is into pitched-copy-buffer
// Generate output
memcpy( _ab,
getSampleFragment( play_frame, _frames, _looped, &tmp ),
_frames * BYTES_PER_FRAME );
// Advance
play_frame += _frames;
if( _looped )
{
play_frame = getLoopedIndex( play_frame );
}
}
delete[] tmp;
_state->m_frameIndex = play_frame;
return( TRUE );
}
sampleFrame * sampleBuffer::getSampleFragment( f_cnt_t _start,
f_cnt_t _frames, bool _looped, sampleFrame * * _tmp ) const
{
if( _looped )
{
if( _start + _frames <= m_loopEndFrame )
{
return( m_data + _start );
}
}
else
{
if( _start + _frames <= m_endFrame )
{
return( m_data + _start );
}
}
*_tmp = new sampleFrame[_frames];
if( _looped )
{
f_cnt_t copied = m_loopEndFrame - _start;
memcpy( *_tmp, m_data + _start, copied * BYTES_PER_FRAME );
f_cnt_t loop_frames = m_loopEndFrame - m_loopStartFrame;
while( _frames - copied > 0 )
{
f_cnt_t todo = qMin( _frames - copied, loop_frames );
memcpy( *_tmp + copied, m_data + m_loopStartFrame,
todo * BYTES_PER_FRAME );
copied += todo;
}
}
else
{
f_cnt_t available = m_endFrame - _start;
memcpy( *_tmp, m_data + _start, available * BYTES_PER_FRAME );
memset( *_tmp + available, 0, ( _frames - available ) *
BYTES_PER_FRAME );
}
return( *_tmp );
}
f_cnt_t sampleBuffer::getLoopedIndex( f_cnt_t _index ) const
{
if( _index < m_loopEndFrame )
{
return( _index );
}
return( m_loopStartFrame + ( _index - m_loopStartFrame )
% ( m_loopEndFrame - m_loopStartFrame ) );
}
void sampleBuffer::visualize( QPainter & _p, const QRect & _dr,
const QRect & _clip )
{
// _p.setClipRect( _clip );
// _p.setPen( QColor( 0x22, 0xFF, 0x44 ) );
//_p.setPen( QColor( 64, 224, 160 ) );
const int w = _dr.width();
const int h = _dr.height();
const int yb = h / 2 + _dr.y();
const float y_space = h*0.25f;
if( m_frames < 60000 )
{
_p.setRenderHint( QPainter::Antialiasing );
QColor c = _p.pen().color();
_p.setPen( QPen( c, 0.7 ) );
}
const int fpp = tLimit<int>( m_frames / w, 1, 20 );
QPoint * l = new QPoint[m_frames / fpp + 1];
int n = 0;
const int xb = _dr.x();
for( int frame = 0; frame < m_frames; frame += fpp )
{
l[n] = QPoint( xb + ( frame * w / m_frames ),
(int)( yb - ( ( m_data[frame][0]+m_data[frame][1] ) *
y_space ) ) );
++n;
}
_p.drawPolyline( l, m_frames / fpp );
delete[] l;
}
QString sampleBuffer::openAudioFile( void ) const
{
QFileDialog ofd( NULL, tr( "Open audio file" ) );
QString dir;
if( m_audioFile != "" )
{
QString f = m_audioFile;
if( QFileInfo( f ).isRelative() )
{
f = configManager::inst()->userSamplesDir() + f;
if( QFileInfo( f ).exists() == FALSE )
{
f = configManager::inst()->factorySamplesDir() +
m_audioFile;
}
}
dir = QFileInfo( f ).absolutePath();
}
else
{
dir = configManager::inst()->userSamplesDir();
}
// change dir to position of previously opened file
ofd.setDirectory( dir );
ofd.setFileMode( QFileDialog::ExistingFiles );
// set filters
QStringList types;
types << tr( "All Audio-Files (*.wav *.ogg *.ds *.flac *.spx *.voc "
"*.aif *.aiff *.au *.raw *.mp3)" )
<< tr( "Wave-Files (*.wav)" )
<< tr( "OGG-Files (*.ogg)" )
<< tr( "DrumSynth-Files (*.ds)" )
<< tr( "FLAC-Files (*.flac)" )
<< tr( "SPEEX-Files (*.spx)" )
<< tr( "MP3-Files (*.mp3)" )
//<< tr( "MIDI-Files (*.mid)" )
<< tr( "VOC-Files (*.voc)" )
<< tr( "AIFF-Files (*.aif *.aiff)" )
<< tr( "AU-Files (*.au)" )
<< tr( "RAW-Files (*.raw)" )
//<< tr( "MOD-Files (*.mod)" )
;
ofd.setFilters( types );
if( m_audioFile != "" )
{
// select previously opened file
ofd.selectFile( QFileInfo( m_audioFile ).fileName() );
}
if( ofd.exec () == QDialog::Accepted )
{
if( ofd.selectedFiles().isEmpty() )
{
return( "" );
}
return( tryToMakeRelative( ofd.selectedFiles()[0] ) );
}
return( "" );
}
#undef LMMS_HAVE_FLAC_STREAM_ENCODER_H /* not yet... */
#undef LMMS_HAVE_FLAC_STREAM_DECODER_H
#ifdef LMMS_HAVE_FLAC_STREAM_ENCODER_H
FLAC__StreamEncoderWriteStatus flacStreamEncoderWriteCallback(
const FLAC__StreamEncoder *
/*_encoder*/,
const FLAC__byte _buffer[],
unsigned int/* _samples*/,
unsigned int _bytes,
unsigned int/* _current_frame*/,
void * _client_data )
{
/* if( _bytes == 0 )
{
return( FLAC__STREAM_ENCODER_WRITE_STATUS_OK );
}*/
return( ( static_cast<QBuffer *>( _client_data )->write(
(const char *) _buffer, _bytes ) ==
(int) _bytes ) ?
FLAC__STREAM_ENCODER_WRITE_STATUS_OK :
FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR );
}
void flacStreamEncoderMetadataCallback( const FLAC__StreamEncoder *,
const FLAC__StreamMetadata * _metadata,
void * _client_data )
{
QBuffer * b = static_cast<QBuffer *>( _client_data );
b->seek( 0 );
b->write( (const char *) _metadata, sizeof( *_metadata ) );
}
#endif
QString & sampleBuffer::toBase64( QString & _dst ) const
{
#ifdef LMMS_HAVE_FLAC_STREAM_ENCODER_H
const f_cnt_t FRAMES_PER_BUF = 1152;
FLAC__StreamEncoder * flac_enc = FLAC__stream_encoder_new();
FLAC__stream_encoder_set_channels( flac_enc, DEFAULT_CHANNELS );
FLAC__stream_encoder_set_blocksize( flac_enc, FRAMES_PER_BUF );
/* FLAC__stream_encoder_set_do_exhaustive_model_search( flac_enc, TRUE );
FLAC__stream_encoder_set_do_mid_side_stereo( flac_enc, TRUE );*/
FLAC__stream_encoder_set_sample_rate( flac_enc,
engine::getMixer()->sampleRate() );
QBuffer ba_writer;
ba_writer.open( QBuffer::WriteOnly );
FLAC__stream_encoder_set_write_callback( flac_enc,
flacStreamEncoderWriteCallback );
FLAC__stream_encoder_set_metadata_callback( flac_enc,
flacStreamEncoderMetadataCallback );
FLAC__stream_encoder_set_client_data( flac_enc, &ba_writer );
if( FLAC__stream_encoder_init( flac_enc ) != FLAC__STREAM_ENCODER_OK )
{
printf( "error within FLAC__stream_encoder_init()!\n" );
}
f_cnt_t frame_cnt = 0;
while( frame_cnt < m_frames )
{
f_cnt_t remaining = qMin<f_cnt_t>( FRAMES_PER_BUF,
m_frames - frame_cnt );
FLAC__int32 buf[FRAMES_PER_BUF * DEFAULT_CHANNELS];
for( f_cnt_t f = 0; f < remaining; ++f )
{
for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
{
buf[f*DEFAULT_CHANNELS+ch] = (FLAC__int32)(
mixer::clip( m_data[f+frame_cnt][ch] ) *
OUTPUT_SAMPLE_MULTIPLIER );
}
}
FLAC__stream_encoder_process_interleaved( flac_enc, buf,
remaining );
frame_cnt += remaining;
}
FLAC__stream_encoder_finish( flac_enc );
FLAC__stream_encoder_delete( flac_enc );
printf("%d %d\n", frame_cnt, (int)ba_writer.size() );
ba_writer.close();
base64::encode( ba_writer.buffer().data(), ba_writer.buffer().size(),
_dst );
#else /* LMMS_HAVE_FLAC_STREAM_ENCODER_H */
base64::encode( (const char *) m_data,
m_frames * sizeof( sampleFrame ), _dst );
#endif /* LMMS_HAVE_FLAC_STREAM_ENCODER_H */
return( _dst );
}
sampleBuffer * sampleBuffer::resample( sampleFrame * _data,
const f_cnt_t _frames,
const sample_rate_t _src_sr,
const sample_rate_t _dst_sr )
{
const f_cnt_t dst_frames = static_cast<f_cnt_t>( _frames /
(float) _src_sr * (float) _dst_sr );
sampleBuffer * dst_sb = new sampleBuffer( dst_frames );
sampleFrame * dst_buf = dst_sb->m_origData;
// yeah, libsamplerate, let's rock with sinc-interpolation!
int error;
SRC_STATE * state;
if( ( state = src_new( SRC_SINC_MEDIUM_QUALITY,
DEFAULT_CHANNELS, &error ) ) != NULL )
{
SRC_DATA src_data;
src_data.end_of_input = 0;
src_data.data_in = _data[0];
src_data.data_out = dst_buf[0];
src_data.input_frames = _frames;
src_data.output_frames = dst_frames;
src_data.src_ratio = (double) _dst_sr / _src_sr;
if( ( error = src_process( state, &src_data ) ) )
{
printf( "sampleBuffer: error while resampling: %s\n",
src_strerror( error ) );
}
src_delete( state );
}
else
{
printf( "Error: src_new() failed in sample_buffer.cpp!\n" );
}
dst_sb->update();
return( dst_sb );
}
void sampleBuffer::setAudioFile( const QString & _audio_file )
{
m_audioFile = tryToMakeRelative( _audio_file );
update();
}
#ifdef LMMS_HAVE_FLAC_STREAM_DECODER_H
struct flacStreamDecoderClientData
{
QBuffer * read_buffer;
QBuffer * write_buffer;
} ;
FLAC__StreamDecoderReadStatus flacStreamDecoderReadCallback(
const FLAC__StreamDecoder *
/*_decoder*/,
FLAC__byte * _buffer,
unsigned int * _bytes,
void * _client_data )
{
int res = static_cast<flacStreamDecoderClientData *>(
_client_data )->read_buffer->read(
(char *) _buffer, *_bytes );
if( res > 0 )
{
*_bytes = res;
return( FLAC__STREAM_DECODER_READ_STATUS_CONTINUE );
}
*_bytes = 0;
return( FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM );
}
FLAC__StreamDecoderWriteStatus flacStreamDecoderWriteCallback(
const FLAC__StreamDecoder *
/*_decoder*/,
const FLAC__Frame * _frame,
const FLAC__int32 * const _buffer[],
void * _client_data )
{
if( _frame->header.channels != 2 )
{
printf( "channels != 2 in "
"flacStreamDecoderWriteCallback()\n" );
return( FLAC__STREAM_DECODER_WRITE_STATUS_ABORT );
}
if( _frame->header.bits_per_sample != 16 )
{
printf( "bits_per_sample != 16 in "
"flacStreamDecoderWriteCallback()\n" );
return( FLAC__STREAM_DECODER_WRITE_STATUS_ABORT );
}
const f_cnt_t frames = _frame->header.blocksize;
for( f_cnt_t frame = 0; frame < frames; ++frame )
{
sampleFrame sframe = { _buffer[0][frame] /
OUTPUT_SAMPLE_MULTIPLIER,
_buffer[1][frame] /
OUTPUT_SAMPLE_MULTIPLIER
} ;
static_cast<flacStreamDecoderClientData *>(
_client_data )->write_buffer->write(
(const char *) sframe, sizeof( sframe ) );
}
return( FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE );
}
void flacStreamDecoderMetadataCallback( const FLAC__StreamDecoder *,
const FLAC__StreamMetadata *,
void * /*_client_data*/ )
{
printf("stream decoder metadata callback\n");
/* QBuffer * b = static_cast<QBuffer *>( _client_data );
b->seek( 0 );
b->write( (const char *) _metadata, sizeof( *_metadata ) );*/
}
void flacStreamDecoderErrorCallback( const FLAC__StreamDecoder *,
FLAC__StreamDecoderErrorStatus _status,
void * /*_client_data*/ )
{
printf("error callback! %d\n", _status);
// what to do now??
}
#endif
void sampleBuffer::loadFromBase64( const QString & _data )
{
char * dst = NULL;
int dsize = 0;
base64::decode( _data, &dst, &dsize );
#ifdef LMMS_HAVE_FLAC_STREAM_DECODER_H
QByteArray orig_data = QByteArray::fromRawData( dst, dsize );
QBuffer ba_reader( &orig_data );
ba_reader.open( QBuffer::ReadOnly );
QBuffer ba_writer;
ba_writer.open( QBuffer::WriteOnly );
flacStreamDecoderClientData cdata = { &ba_reader, &ba_writer } ;
FLAC__StreamDecoder * flac_dec = FLAC__stream_decoder_new();
FLAC__stream_decoder_set_read_callback( flac_dec,
flacStreamDecoderReadCallback );
FLAC__stream_decoder_set_write_callback( flac_dec,
flacStreamDecoderWriteCallback );
FLAC__stream_decoder_set_error_callback( flac_dec,
flacStreamDecoderErrorCallback );
FLAC__stream_decoder_set_metadata_callback( flac_dec,
flacStreamDecoderMetadataCallback );
FLAC__stream_decoder_set_client_data( flac_dec, &cdata );
FLAC__stream_decoder_init( flac_dec );
FLAC__stream_decoder_process_until_end_of_stream( flac_dec );
FLAC__stream_decoder_finish( flac_dec );
FLAC__stream_decoder_delete( flac_dec );
ba_reader.close();
orig_data = ba_writer.buffer();
printf("%d\n", (int) orig_data.size() );
m_origFrames = orig_data.size() / sizeof( sampleFrame );
delete[] m_origData;
m_origData = new sampleFrame[m_origFrames];
memcpy( m_origData, orig_data.data(), orig_data.size() );
#else /* LMMS_HAVE_FLAC_STREAM_DECODER_H */
m_origFrames = dsize / sizeof( sampleFrame );
delete[] m_origData;
m_origData = new sampleFrame[m_origFrames];
memcpy( m_origData, dst, dsize );
#endif
delete[] dst;
m_audioFile = "";
update();
}
void sampleBuffer::setStartFrame( const f_cnt_t _s )
{
m_varLock.lock();
m_loopStartFrame = m_startFrame = _s;
m_varLock.unlock();
}
void sampleBuffer::setEndFrame( const f_cnt_t _e )
{
m_varLock.lock();
m_loopEndFrame = m_endFrame = _e;
m_varLock.unlock();
}
void sampleBuffer::setAmplification( float _a )
{
m_amplification = _a;
update( TRUE );
}
void sampleBuffer::setReversed( bool _on )
{
m_reversed = _on;
update( TRUE );
}
QString sampleBuffer::tryToMakeRelative( const QString & _file )
{
if( QFileInfo( _file ).isRelative() == FALSE )
{
QString f = QString( _file ).replace( QDir::separator(), '/' );
QString fsd = configManager::inst()->factorySamplesDir();
QString usd = configManager::inst()->userSamplesDir();
fsd.replace( QDir::separator(), '/' );
usd.replace( QDir::separator(), '/' );
if( f.startsWith( fsd ) )
{
return( QString( f ).mid( fsd.length() ) );
}
else if( f.startsWith( usd ) )
{
return( QString( f ).mid( usd.length() ) );
}
}
return( _file );
}
QString sampleBuffer::tryToMakeAbsolute( const QString & _file )
{
if( QFileInfo( _file ).isAbsolute() )
{
return( _file );
}
QString f = configManager::inst()->userSamplesDir() + _file;
if( QFileInfo( f ).exists() )
{
return( f );
}
return( configManager::inst()->factorySamplesDir() + _file );
}
sampleBuffer::handleState::handleState( bool _varying_pitch ) :
m_frameIndex( 0 ),
m_varyingPitch( _varying_pitch ),
m_eof( 0 )
{
int error;
if( ( m_resamplingData = src_new(/*
( engine::getMixer()->highQuality() == TRUE ) ?
SRC_SINC_FASTEST :*/
SRC_LINEAR,
DEFAULT_CHANNELS, &error ) ) == NULL )
{
printf( "Error: src_new() failed in sample_buffer.cpp!\n" );
}
}
sampleBuffer::handleState::~handleState()
{
src_delete( m_resamplingData );
}
#include "moc_sample_buffer.cxx"
#endif