added basic support for recording sound into sample tracks

git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@1508 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
Tobias Doerffel
2008-08-27 13:52:21 +00:00
parent d1283bc54e
commit 1410f2bc92
12 changed files with 509 additions and 12 deletions

View File

@@ -38,7 +38,8 @@ audioDevice::audioDevice( const ch_cnt_t _channels, mixer * _mixer ) :
m_sampleRate( _mixer->processingSampleRate() ),
m_channels( _channels ),
m_mixer( _mixer ),
m_buffer( new surroundSampleFrame[getMixer()->framesPerPeriod()] )
m_buffer( new surroundSampleFrame[getMixer()->framesPerPeriod()] ),
m_supportsCapture( false )
{
int error;
if( ( m_srcState = src_new(

View File

@@ -79,6 +79,12 @@ void engine::init( const bool _has_gui )
s_fxMixer = new fxMixer;
s_bbTrackContainer = new bbTrackContainer;
s_ladspaManager = new ladspa2LMMS;
s_projectJournal->setJournalling( TRUE );
s_mixer->initDevices();
if( s_hasGUI )
{
s_mainWindow = new mainWindow;
@@ -89,15 +95,7 @@ void engine::init( const bool _has_gui )
s_bbEditor = new bbEditor( s_bbTrackContainer );
s_pianoRoll = new pianoRoll;
s_automationEditor = new automationEditor;
}
s_ladspaManager = new ladspa2LMMS;
s_projectJournal->setJournalling( TRUE );
s_mixer->initDevices();
if( s_hasGUI )
{
s_mainWindow->finalize();
}

View File

@@ -54,6 +54,7 @@
#include "audio_alsa.h"
#include "audio_jack.h"
#include "audio_oss.h"
#include "audio_portaudio.h"
#include "audio_pulseaudio.h"
#include "audio_sdl.h"
#include "audio_dummy.h"
@@ -287,8 +288,18 @@ mixer::mixer( void ) :
m_masterGain( 1.0f ),
m_audioDev( NULL ),
m_oldAudioDev( NULL ),
m_globalMutex( QMutex::Recursive )
m_globalMutex( QMutex::Recursive ),
m_inputBufferRead( 0 ),
m_inputBufferWrite( 1 )
{
for( int i = 0; i < 2; ++i )
{
m_inputBufferFrames[i] = 0;
m_inputBufferSize[i] = DEFAULT_BUFFER_SIZE * 100;
m_inputBuffer[i] = new sampleFrame[ DEFAULT_BUFFER_SIZE * 100 ];
clearAudioBuffer( m_inputBuffer[i], m_inputBufferSize[i] );
}
if( configManager::inst()->value( "mixer", "framesperaudiobuffer"
).toInt() >= 32 )
{
@@ -453,6 +464,15 @@ sample_rate_t mixer::outputSampleRate( void ) const
sample_rate_t mixer::inputSampleRate( void ) const
{
return( m_audioDev != NULL ? m_audioDev->sampleRate() :
baseSampleRate() );
}
sample_rate_t mixer::processingSampleRate( void ) const
{
return( outputSampleRate() * m_qualitySettings.sampleRateMultiplier() );
@@ -470,6 +490,36 @@ bool mixer::criticalXRuns( void ) const
void mixer::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames )
{
lockInputFrames();
f_cnt_t frames = m_inputBufferFrames[ m_inputBufferWrite ];
int size = m_inputBufferSize[ m_inputBufferWrite ];
sampleFrame * buf = m_inputBuffer[ m_inputBufferWrite ];
if( frames + _frames > size )
{
size = tMax( size * 2, frames + _frames );
sampleFrame * ab = new sampleFrame[ size ];
memcpy( ab, buf, frames * sizeof( sampleFrame ) );
delete [] buf;
m_inputBufferSize[ m_inputBufferWrite ] = size;
m_inputBuffer[ m_inputBufferWrite ] = ab;
buf = ab;
}
memcpy( &buf[ frames ], _ab, _frames * sizeof( sampleFrame ) );
m_inputBufferFrames[ m_inputBufferWrite ] += _frames;
unlockInputFrames();
}
const surroundSampleFrame * mixer::renderNextBuffer( void )
{
microTimer timer;
@@ -486,6 +536,16 @@ const surroundSampleFrame * mixer::renderNextBuffer( void )
last_metro_pos = p;
}
lockInputFrames();
// swap buffer
m_inputBufferWrite++;
m_inputBufferWrite %= 2;
m_inputBufferRead++;
m_inputBufferRead %= 2;
// clear new write buffer
m_inputBufferFrames[ m_inputBufferWrite ] = 0;
unlockInputFrames();
// now we have to make sure no other thread does anything bad
// while we're acting...
lock();
@@ -929,6 +989,20 @@ audioDevice * mixer::tryAudioDevices( void )
#endif
#ifdef LMMS_HAVE_PORTAUDIO
if( dev_name == audioPortAudio::name() || dev_name == "" )
{
dev = new audioPortAudio( success_ful, this );
if( success_ful )
{
m_audioDevName = audioPortAudio::name();
return( dev );
}
delete dev;
}
#endif
#ifdef LMMS_HAVE_PULSEAUDIO
if( dev_name == audioPulseAudio::name() || dev_name == "" )
{

View File

@@ -0,0 +1,158 @@
#ifndef SINGLE_SOURCE_COMPILE
/*
* sample_record_handle.cpp - implementation of class sampleRecordHandle
*
* Copyright (c) 2008 Csaba Hruska <csaba.hruska/at/gmail.com>
*
* 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_record_handle.h"
#include "bb_track.h"
#include "engine.h"
#include "instrument_track.h"
#include "pattern.h"
#include "sample_buffer.h"
#include "sample_track.h"
sampleRecordHandle::sampleRecordHandle( sampleTCO * _tco ) :
playHandle( SamplePlayHandle ),
m_track( _tco->getTrack() ),
m_bbTrack( NULL ),
m_tco( _tco ),
m_framesRecorded( 0 ),
m_minLength( _tco->length() )
{
}
sampleRecordHandle::~sampleRecordHandle()
{
if( !m_buffers.empty() )
{
sampleBuffer * sb;
createSampleBuffer( &sb );
m_tco->setSampleBuffer( sb );
}
while( !m_buffers.empty() )
{
delete[] m_buffers.front().first;
m_buffers.erase( m_buffers.begin() );
}
m_tco->setRecord( false );
}
void sampleRecordHandle::play( bool /* _try_parallelizing */,
sampleFrame * _working_buffer )
{
const sampleFrame * recbuf = engine::getMixer()->inputBuffer();
const fpp_t frames = engine::getMixer()->inputBufferFrames();
writeBuffer( recbuf, frames );
m_framesRecorded += frames;
midiTime len = m_framesRecorded / engine::framesPerTick();
if( len > m_minLength )
{
// m_tco->changeLength( len );
m_minLength = len;
}
}
bool sampleRecordHandle::done( void ) const
{
return( FALSE );
}
bool sampleRecordHandle::isFromTrack( const track * _track ) const
{
return( m_track == _track || m_bbTrack == _track );
}
f_cnt_t sampleRecordHandle::framesRecorded( void ) const
{
return( m_framesRecorded );
}
void sampleRecordHandle::createSampleBuffer( sampleBuffer * * _sample_buf )
{
const f_cnt_t frames = framesRecorded();
// create buffer to store all recorded buffers in
sampleFrame * data = new sampleFrame[frames];
// make sure buffer is cleaned up properly at the end...
sampleFrame * data_ptr = data;
#ifdef LMMS_DEBUG
assert( data != NULL );
#endif
// now copy all buffers into big buffer
for( bufferList::const_iterator it = m_buffers.begin();
it != m_buffers.end(); ++it )
{
memcpy( data_ptr, ( *it ).first, ( *it ).second *
sizeof( sampleFrame ) );
data_ptr += ( *it ).second;
}
// create according sample-buffer out of big buffer
*_sample_buf = new sampleBuffer( data, frames );
( *_sample_buf )->setSampleRate( engine::getMixer()->inputSampleRate() );
delete[] data;
}
void sampleRecordHandle::writeBuffer( const sampleFrame * _ab,
const fpp_t _frames )
{
sampleFrame * buf = new sampleFrame[_frames];
for( fpp_t frame = 0; frame < _frames; ++frame )
{
for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl )
{
buf[frame][chnl] = _ab[frame][chnl];
}
}
m_buffers.push_back( qMakePair( buf, _frames ) );
}
#endif

View File

@@ -49,6 +49,7 @@
#include "tool_button.h"
#include "tooltip.h"
#include "visualization_widget.h"
#include "audio_device.h"
@@ -222,6 +223,21 @@ songEditor::songEditor( song * _song, songEditor * & _engine_ptr ) :
tr( "Play song (Space)" ),
this, SLOT( play() ), m_toolBar );
m_recordButton = new toolButton( embed::getIconPixmap( "record" ),
tr( "Record samples from Audio-device" ),
this, SLOT( record() ), m_toolBar );
m_recordAccompanyButton = new toolButton(
embed::getIconPixmap( "record_accompany" ),
tr( "Record samples from Audio-device while playing song or BB track" ),
this, SLOT( recordAccompany() ), m_toolBar );
// disable record buttons if capturing is not supported
if( !engine::getMixer()->audioDev()->supportsCapture() )
{
m_recordButton->setDisabled( true );
m_recordAccompanyButton->setDisabled( true );
}
m_stopButton = new toolButton( embed::getIconPixmap( "stop", 24, 24 ),
tr( "Stop song (Space)" ),
this, SLOT( stop() ), m_toolBar );
@@ -305,6 +321,8 @@ songEditor::songEditor( song * _song, songEditor * & _engine_ptr ) :
tb_layout->addSpacing( 5 );
tb_layout->addWidget( m_playButton );
tb_layout->addWidget( m_recordButton );
tb_layout->addWidget( m_recordAccompanyButton );
tb_layout->addWidget( m_stopButton );
tb_layout->addSpacing( 10 );
tb_layout->addWidget( m_addBBTrackButton );
@@ -386,6 +404,22 @@ void songEditor::play( void )
void songEditor::record( void )
{
play();
}
void songEditor::recordAccompany( void )
{
play();
}
void songEditor::stop( void )
{
m_s->stop();

View File

@@ -28,11 +28,13 @@
#include <QtXml/QDomElement>
#include <QtGui/QDropEvent>
#include <QtGui/QMenu>
#include <QtGui/QLayout>
#include <QtGui/QMdiArea>
#include <QtGui/QPainter>
#include <QtGui/QPushButton>
#include "gui_templates.h"
#include "sample_track.h"
#include "song.h"
#include "embed.h"
@@ -40,6 +42,7 @@
#include "tooltip.h"
#include "audio_port.h"
#include "sample_play_handle.h"
#include "sample_record_handle.h"
#include "string_pair_drag.h"
#include "knob.h"
#include "main_window.h"
@@ -89,6 +92,16 @@ const QString & sampleTCO::sampleFile( void ) const
void sampleTCO::setSampleBuffer( sampleBuffer * _sb )
{
sharedObject::unref( m_sampleBuffer );
m_sampleBuffer = _sb;
updateLength();
emit sampleChanged();
}
void sampleTCO::setSampleFile( const QString & _sf )
{
@@ -101,6 +114,15 @@ void sampleTCO::setSampleFile( const QString & _sf )
void sampleTCO::toggleRecord( void )
{
m_recordModel.setValue( !m_recordModel.value() );
emit dataChanged();
}
void sampleTCO::updateLength( bpm_t )
{
changeLength( sampleLength() );
@@ -204,6 +226,37 @@ void sampleTCOView::updateSample( void )
void sampleTCOView::contextMenuEvent( QContextMenuEvent * _cme )
{
QMenu contextMenu( this );
if( fixedTCOs() == FALSE )
{
contextMenu.addAction( embed::getIconPixmap( "cancel" ),
tr( "Delete (middle mousebutton)" ),
this, SLOT( remove() ) );
contextMenu.addSeparator();
contextMenu.addAction( embed::getIconPixmap( "edit_cut" ),
tr( "Cut" ), this, SLOT( cut() ) );
}
contextMenu.addAction( embed::getIconPixmap( "edit_copy" ),
tr( "Copy" ), m_tco, SLOT( copy() ) );
contextMenu.addAction( embed::getIconPixmap( "edit_paste" ),
tr( "Paste" ), m_tco, SLOT( paste() ) );
contextMenu.addSeparator();
contextMenu.addAction( embed::getIconPixmap( "muted" ),
tr( "Mute/unmute (<Ctrl> + middle click)" ),
m_tco, SLOT( toggleMute() ) );
contextMenu.addAction( embed::getIconPixmap( "record" ),
tr( "Set/clear record" ),
m_tco, SLOT( toggleRecord() ) );
constructContextMenu( &contextMenu );
contextMenu.exec( QCursor::pos() );
}
void sampleTCOView::dragEnterEvent( QDragEnterEvent * _dee )
{
if( stringPairDrag::processDragEnterEvent( _dee,
@@ -241,6 +294,23 @@ void sampleTCOView::dropEvent( QDropEvent * _de )
void sampleTCOView::mousePressEvent( QMouseEvent * _me )
{
if( _me->button() == Qt::LeftButton &&
engine::getMainWindow()->isCtrlPressed() &&
engine::getMainWindow()->isShiftPressed() )
{
m_tco->toggleRecord();
}
else
{
trackContentObjectView::mousePressEvent( _me );
}
}
void sampleTCOView::mouseDoubleClickEvent( QMouseEvent * )
{
QString af = m_tco->m_sampleBuffer->openAudioFile();
@@ -298,6 +368,14 @@ void sampleTCOView::paintEvent( QPaintEvent * _pe )
{
p.drawPixmap( 3, 8, embed::getIconPixmap( "muted", 16, 16 ) );
}
if( m_tco->isRecord() )
{
p.setFont( pointSize<6>( p.font() ) );
p.setPen( QColor( 224, 0, 0 ) );
p.drawText( 9, p.fontMetrics().height() - 1, "Rec" );
p.setBrush( QBrush( QColor( 224, 0, 0 ) ) );
p.drawEllipse( 4, 5, 4, 4 );
}
}
@@ -342,8 +420,18 @@ bool sampleTrack::play( const midiTime & _start, const fpp_t _frames,
sampleTCO * st = dynamic_cast<sampleTCO *>( tco );
if( !st->isMuted() )
{
samplePlayHandle * handle = new samplePlayHandle( st );
handle->setVolumeModel( &m_volumeModel );
playHandle * handle;
if( st->isRecord() )
{
sampleRecordHandle * smpHandle = new sampleRecordHandle( st );
handle = smpHandle;
}
else
{
samplePlayHandle * smpHandle = new samplePlayHandle( st );
smpHandle->setVolumeModel( &m_volumeModel );
handle = smpHandle;
}
//TODO: check whether this works
// handle->setBBTrack( _tco_num );
handle->setOffset( _offset );