diff --git a/ChangeLog b/ChangeLog index a86013f9f8..25ded2ec4f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,54 @@ +2007-01-20 Javier Serrano Polo + + * plugins/singerbot/artwork.png: + * plugins/singerbot/logo.png: + * plugins/singerbot/Makefile.am: + * plugins/singerbot/singerbot.h: + * plugins/singerbot/singerbot.cpp: + initial release, SingerBot (experimental) instrument plugin + + * configure.in: + * include/note_play_handle.h: + * plugins/Makefile.am: + * src/core/arp_and_chords_tab_widget.cpp: + * src/core/note_play_handle.cpp: + * src/tracks/instrument_track.cpp: + added singerbot support + + * include/sample_buffer.h: + deleteResampling methods made static + + * plugins/audio_file_processor/audio_file_processor.cpp: + * plugins/organic/organic.cpp: + * plugins/triple_oscillator/triple_oscillator.cpp: + * src/core/note_play_handle.cpp: + * src/tracks/instrument_track.cpp: + call deleteNotePluginData() only if there is plugin data, fixes + unprotected instruments + + * plugins/vestige/vestige.cpp: + trigger deleteNotePluginData() + + * src/tracks/instrument_track.cpp: + avoid double unlock + + * configure.in: + fixed configuring with --with-stk + + * acinclude.m4: + moved QT_TRANSLATIONS_DIR to config.h + + * plugins/flp_import/flp_import.cpp: + * plugins/flp_import/unrtf/word.c: + optimized unsafe loops + + * src/lib/sample_buffer.cpp: + * src/widgets/project_notes.cpp: + minor cosmetic changes + + * include/shared_object.h: + not really thread-safe, will be removed + 2007-01-02 Tobias Doerffel * src/core/note.cpp: diff --git a/acinclude.m4 b/acinclude.m4 index 0bd9e26f99..2408654a70 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -61,6 +61,7 @@ esac AC_MSG_RESULT([$QT_VER ($QT_MAJOR)]) # Search for available Qt translations +AH_TEMPLATE(QT_TRANSLATIONS_DIR, [Define to Qt translations directory]) AC_MSG_CHECKING([Qt translations]) case "${QT_VER}" in 3*) @@ -77,6 +78,8 @@ for i in $QT_TRANSLATIONS_SEARCH ; do done if test x"$QT_TRANSLATIONS" = x ; then AC_MSG_WARN([*** not found! You may want to install a Qt i18n package]) +else + AC_DEFINE_UNQUOTED(QT_TRANSLATIONS_DIR, "$QT_TRANSLATIONS") fi AC_MSG_RESULT([$QT_TRANSLATIONS]) @@ -273,10 +276,6 @@ if test x"$QT_IS_MT" = "xyes" ; then QT_LIBS="$QT_LIBS" fi -if test x"$QT_TRANSLATIONS" != x ; then - QT_CXXFLAGS="$QT_CXXFLAGS -DQT_TRANSLATIONS_DIR='\"$QT_TRANSLATIONS\"'" -fi - QT_LDADD="-L$QTDIR/lib $QT_LIBS" #if test x$QT_IS_STATIC = xyes ; then diff --git a/configure.in b/configure.in index dcbb0c2559..a717cd9f34 100644 --- a/configure.in +++ b/configure.in @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) -AC_INIT(lmms, 0.2.1-svn20061228, lmms-devel/at/lists/dot/sf/dot/net) -AM_INIT_AUTOMAKE(lmms, 0.2.1-svn20061228) +AC_INIT(lmms, 0.2.1-svn20070120, lmms-devel/at/lists/dot/sf/dot/net) +AM_INIT_AUTOMAKE(lmms, 0.2.1-svn20070120) AM_CONFIG_HEADER(config.h) @@ -264,8 +264,8 @@ AM_CONDITIONAL(LADSPA_SUPPORT, test "$build_linux" = "true" ) # check for STK AC_LANG_PUSH(C++) AC_ARG_WITH(stk, - AS_HELP_STRING([--without-stk], [disable support for STK plugins]), - [ with_stk=no ], [ with_stk=yes ]) + AS_HELP_STRING([--without-stk], [disable support for STK plugins]), , + [ with_stk=yes ]) AH_TEMPLATE(HAVE_STK_H, [Define to 1 if you have the header file.]) if test "x$with_stk" = "xyes" ; then AC_CHECK_HEADER([stk/Stk.h], HAVE_STK_H="true") @@ -281,6 +281,29 @@ AM_CONDITIONAL(STK_SUPPORT, test ! -z "$HAVE_STK_H") AC_LANG_POP(C++) +# check for Festival +AC_LANG_PUSH(C++) +AC_ARG_WITH(singerbot, + AS_HELP_STRING([--without-singerbot], + [disable support for SingerBot plugin]), , + [ with_singerbot=yes ]) +AH_TEMPLATE(SINGERBOT_SUPPORT, + [Define to 1 to enable SingerBot plugin support.]) +if test "x$with_singerbot" = "xyes" ; then + ORIG_CPPFLAGS=$CPPFLAGS + CPPFLAGS+=" -I/usr/include/festival -I/usr/include/speech_tools" + CPPFLAGS+=" -Wno-non-template-friend" + AC_CHECK_HEADER([festival.h], FESTIVAL_SUPPORT="true") + AC_CHECK_LIB([Festival], [main], , FESTIVAL_SUPPORT="") + CPPFLAGS=$ORIG_CPPFLAGS +fi +if test ! -z "$FESTIVAL_SUPPORT" ; then + AC_DEFINE(SINGERBOT_SUPPORT) +fi +AM_CONDITIONAL(SINGERBOT_SUPPORT, test ! -z "$FESTIVAL_SUPPORT") +AC_LANG_POP(C++) + + # check for vorbis-lib AC_ARG_WITH(vorbis, AS_HELP_STRING([--without-vorbis], @@ -565,6 +588,7 @@ AC_CONFIG_FILES([Makefile plugins/midi_import/Makefile plugins/organic/Makefile plugins/plucked_string_synth/Makefile + plugins/singerbot/Makefile plugins/stk/Makefile plugins/stk/mallets/Makefile plugins/triple_oscillator/Makefile @@ -756,6 +780,27 @@ else fi +if test -z "$FESTIVAL_SUPPORT" ; then + if test "x$with_singerbot" = "xyes" ; then + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You don't seem to have Festival development files." + echo " = The SingerBot instrument plugin will be ignored." + echo " = Before enabling this plugin, bear in mind that it is still experimental." + echo " =" + echo " = To remove this warning, please pass" + echo " = " + echo " = --without-singerbot" + echo " =" + with_warnings="true" + fi +else + PLUGINS_TO_BUILD="$PLUGINS_TO_BUILD\n\t\* SingerBot instrument plugin" +fi + + if test -z "$HAVE_SAMPLERATE_H" ; then echo " ========================" echo " === LMMS - WARNING =======================================================" diff --git a/include/note_play_handle.h b/include/note_play_handle.h index 3b69ddb510..842bc8f264 100644 --- a/include/note_play_handle.h +++ b/include/note_play_handle.h @@ -2,7 +2,7 @@ * note_play_handle.h - declaration of class notePlayHandle which is needed * by LMMS-Play-Engine * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -207,6 +207,18 @@ public: m_instrumentTrack->m_instrument->waitForWorkerThread(); } +#if SINGERBOT_SUPPORT + int patternIndex( void ) + { + return( m_patternIndex ); + } + + void setPatternIndex( int _i ) + { + m_patternIndex = _i; + } +#endif + private: instrumentTrack * m_instrumentTrack; // needed for calling @@ -232,6 +244,9 @@ private: // sub-note) bool m_muted; // indicates whether note is muted bbTrack * m_bbTrack; // related BB track +#if SINGERBOT_SUPPORT + int m_patternIndex; // position among relevant notes +#endif private slots: diff --git a/include/sample_buffer.h b/include/sample_buffer.h index 007c890672..5af02eff72 100644 --- a/include/sample_buffer.h +++ b/include/sample_buffer.h @@ -1,7 +1,7 @@ /* * sample_buffer.h - container-class sampleBuffer * - * Copyright (c) 2005-2006 Tobias Doerffel + * Copyright (c) 2005-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -130,7 +130,7 @@ public: return( m_data ); } - void FASTCALL deleteResamplingData( void * * _ptr ); + static void FASTCALL deleteResamplingData( void * * _ptr ); QString openAudioFile( void ) const; @@ -232,7 +232,7 @@ private: void initResampling( void ); void quitResampling( void ); SRC_STATE * createResamplingContext( void ); - void FASTCALL destroyResamplingContext( SRC_STATE * _context ); + static void FASTCALL destroyResamplingContext( SRC_STATE * _context ); SRC_DATA m_srcData; SRC_STATE * m_srcState; diff --git a/include/shared_object.h b/include/shared_object.h index 2a9eeeb034..d3c27c70c3 100644 --- a/include/shared_object.h +++ b/include/shared_object.h @@ -50,6 +50,7 @@ public: template static T * ref( T * _object ) { + // TODO: Use QShared _object->m_reference_mutex.lock(); ++_object->m_reference_count; _object->m_reference_mutex.unlock(); diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 9d45b82850..8cbb90780a 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -10,6 +10,10 @@ if STK_SUPPORT STK_DIR=stk endif +if SINGERBOT_SUPPORT +SINGERBOT_DIR=singerbot +endif + SUBDIRS = \ audio_file_processor \ bass_booster \ @@ -21,6 +25,7 @@ SUBDIRS = \ midi_import \ organic \ plucked_string_synth \ + $(SINGERBOT_DIR) \ $(STK_DIR) \ triple_oscillator \ $(VST_DIRS) \ diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 8c5fb43a44..1a36fce673 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -1,7 +1,7 @@ /* * audio_file_processor.cpp - instrument for using audio-files * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -415,10 +415,7 @@ void audioFileProcessor::playNote( notePlayHandle * _n, bool ) void audioFileProcessor::deleteNotePluginData( notePlayHandle * _n ) { - if( _n->m_pluginData != NULL ) - { - m_sampleBuffer.deleteResamplingData( &_n->m_pluginData ); - } + m_sampleBuffer.deleteResamplingData( &_n->m_pluginData ); } diff --git a/plugins/flp_import/flp_import.cpp b/plugins/flp_import/flp_import.cpp index 12c7b773a9..bef4fb4e56 100644 --- a/plugins/flp_import/flp_import.cpp +++ b/plugins/flp_import/flp_import.cpp @@ -1,7 +1,7 @@ /* * flp_import.cpp - support for importing FLP-files * - * Copyright (c) 2006 Tobias Doerffel + * Copyright (c) 2006-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -739,7 +739,8 @@ bool flpImport::tryImport( trackContainer * _tc ) { //dump_mem( text, text_len ); const int bpn = 20; - for( Uint32 i = 0; i*bpn < text_len; ++i ) + Uint32 imax = ( text_len + bpn - 1 ) / bpn; + for( Uint32 i = 0; i < imax; ++i ) { int ch = *( text + i*bpn + 6 ); int pos = *( (int *)( text + i*bpn ) ); diff --git a/plugins/flp_import/unrtf/word.c b/plugins/flp_import/unrtf/word.c index 989a92b975..3dd27d41aa 100644 --- a/plugins/flp_import/unrtf/word.c +++ b/plugins/flp_import/unrtf/word.c @@ -45,6 +45,7 @@ * 22 Sep 01, tuorfa@yahoo.com: moved word_dump to here from parse.c * 22 Sep 01, tuorfa@yahoo.com: added function-level comment blocks * 29 Mar 05, daved@physiol.usyd.edu.au: changes requested by ZT Smith + * 11 Jan 07, jasp00@users.sourceforge.net: optimized unsafe loop *--------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H @@ -164,10 +165,10 @@ void word_free (Word *w) { static void print_indentation (int level) { - int i; - if (level) { - for (i=0;i> 1) + (level & 1); + while (level-- > 0) printf (". "); } else { printf ("\n-----------------------------------------------------------------------\n\n"); diff --git a/plugins/organic/organic.cpp b/plugins/organic/organic.cpp index f9639a5b48..a81ada5aee 100644 --- a/plugins/organic/organic.cpp +++ b/plugins/organic/organic.cpp @@ -1,7 +1,7 @@ /* * organic.cpp - additive synthesizer for organ-like sounds * - * Copyright (c) 2006 Andreas Brandmaier + * Copyright (c) 2006-2007 Andreas Brandmaier * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -402,10 +402,6 @@ void organicInstrument::playNote( notePlayHandle * _n, bool ) void organicInstrument::deleteNotePluginData( notePlayHandle * _n ) { - if( _n->m_pluginData == NULL ) - { - return; - } delete static_cast( static_cast( _n->m_pluginData )->oscLeft ); delete static_cast( static_cast( diff --git a/plugins/singerbot/Makefile.am b/plugins/singerbot/Makefile.am new file mode 100644 index 0000000000..85c01546c0 --- /dev/null +++ b/plugins/singerbot/Makefile.am @@ -0,0 +1,37 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + + +INCLUDES = -I/usr/include/festival -I/usr/include/speech_tools \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I. + + +CXXFLAGS += -Wno-non-template-friend + +AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="singerbot" + + +%.moc: ./%.h + $(MOC) -o $@ $< + + +MOC_FILES = ./singerbot.moc + +BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h +EMBEDDED_RESOURCES = $(wildcard *png) + +./embedded_resources.h: $(EMBEDDED_RESOURCES) + $(BIN2RES) $(EMBEDDED_RESOURCES) > $@ + +EXTRA_DIST = $(EMBEDDED_RESOURCES) + + +CLEANFILES = $(MOC_FILES) ./embedded_resources.h + + + +pkglib_LTLIBRARIES = libsingerbot.la + +libsingerbot_la_SOURCES = singerbot.cpp singerbot.h +libsingerbot_la_LDFLAGS = -lFestival -lestools -lestbase -lesd -lncurses + +$(libsingerbot_la_SOURCES): ./embedded_resources.h diff --git a/plugins/singerbot/artwork.png b/plugins/singerbot/artwork.png new file mode 100644 index 0000000000..d912062311 Binary files /dev/null and b/plugins/singerbot/artwork.png differ diff --git a/plugins/singerbot/logo.png b/plugins/singerbot/logo.png new file mode 100644 index 0000000000..0cb2a1150b Binary files /dev/null and b/plugins/singerbot/logo.png differ diff --git a/plugins/singerbot/singerbot.cpp b/plugins/singerbot/singerbot.cpp new file mode 100644 index 0000000000..59907eecca --- /dev/null +++ b/plugins/singerbot/singerbot.cpp @@ -0,0 +1,446 @@ +/* + * singerbot.cpp - a singing bot instrument plugin + * + * Copyright (c) 2007 Javier Serrano Polo + * + * 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. + * + */ + + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + +#include "singerbot.h" +#include "buffer_allocator.h" +#include "instrument_track.h" +#include "note_play_handle.h" +#include "pattern.h" +#include "sample_buffer.h" +#include "song_editor.h" + +#undef SINGLE_SOURCE_COMPILE +#include "embed.cpp" + +#undef HAVE_CONFIG_H +#include + + + + +extern "C" +{ + +plugin::descriptor singerbot_plugin_descriptor = +{ + STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), + "SingerBot", + QT_TRANSLATE_NOOP( "pluginBrowser", + "Singer bot to add some basic vocals" ), + "Javier Serrano Polo ", + 0x0100, + plugin::Instrument, + new QPixmap( PLUGIN_NAME::getIconPixmap( "logo" ) ), + NULL +} ; + + +// neccessary for getting instance out of shared lib +plugin * lmms_plugin_main( void * _data ) +{ + return( new singerBot( static_cast( _data ) ) ); +} + +} + + + + +singerBot::synThread * singerBot::s_thread = NULL; + + + + +singerBot::singerBot( instrumentTrack * _track ) : + instrument( _track, &singerbot_plugin_descriptor ) +{ + if( !s_thread ) + { + s_thread = new synThread(); + s_thread->start(); + } + + setPaletteBackgroundPixmap( PLUGIN_NAME::getIconPixmap( "artwork" ) ); + + QVBoxLayout * vbox = new QVBoxLayout( this ); + vbox->setMargin( 10 ); + vbox->setSpacing( 0 ); + vbox->addSpacing( 45 ); + + m_lyrics = new QTextEdit( this ); +#ifdef QT4 + m_lyrics->setAutoFillBackground( TRUE ); + QPalette pal; + pal.setColor( m_lyrics->backgroundRole(), QColor( 64, 64, 64 ) ); + m_lyrics->setPalette( pal ); +#else + m_lyrics->setTextFormat( PlainText ); + m_lyrics->setPaletteBackgroundColor( QColor( 64, 64, 64 ) ); +#endif + m_lyrics->setText( "Hello, world!" ); + + connect( m_lyrics, SIGNAL( textChanged( void ) ), + this, SLOT( lyricsChanged( void ) ) ); + + vbox->addWidget( m_lyrics ); + + updateWords(); +} + + + + +singerBot::~singerBot() +{ +} + + + + +void singerBot::playNote( notePlayHandle * _n, bool ) +{ + const Uint32 frames = eng()->getMixer()->framesPerAudioBuffer(); + sampleFrame * buf = bufferAllocator::alloc( frames ); + + if( !_n->m_pluginData ) + { + createWave( _n ); + } + handle_data * hdata = (handle_data *)_n->m_pluginData; + + sampleBuffer * sample_buffer = hdata->remaining_frames ? + readWave( hdata ) : new sampleBuffer( NULL, 0, eng() ); + + if( sample_buffer->play( buf, 0, frames, BASE_FREQ, + FALSE, //loop + &hdata->resampling_data ) ) + { + getInstrumentTrack()->processAudioBuffer( buf, frames, _n ); + } + sharedObject::unref( sample_buffer ); + bufferAllocator::free( buf ); +} + + + + +void singerBot::deleteNotePluginData( notePlayHandle * _n ) +{ + handle_data * hdata = (handle_data *)_n->m_pluginData; + if( hdata->resampling_data ) + { + sampleBuffer::deleteResamplingData( &hdata->resampling_data ); + } + if( hdata->wave ) + { + delete hdata->wave; + } + delete hdata; +} + + + + +void singerBot::saveSettings( QDomDocument & _doc, QDomElement & _this ) +{ + QDomElement element = _doc.createElement( "lyrics" ); + _this.appendChild( element ); +#ifdef QT4 + QDomCDATASection ds = _doc.createCDATASection( + m_lyrics->toPlainText() ); +#else + QDomCDATASection ds = _doc.createCDATASection( m_lyrics->text() ); +#endif + element.appendChild( ds ); +} + + + + +void singerBot::loadSettings( const QDomElement & _this ) +{ +#ifdef QT4 + m_lyrics->setPlainText( + _this.namedItem( "lyrics" ).toElement().text() ); +#else + m_lyrics->setText( _this.namedItem( "lyrics" ).toElement().text() ); +#endif +} + + + + +QString singerBot::nodeName( void ) const +{ + return( singerbot_plugin_descriptor.name ); +} + + + + +void singerBot::lyricsChanged( void ) +{ + m_words_dirty = TRUE; +} + + + + +void singerBot::updateWords( void ) +{ + m_words = QStringList::split( ' ', + m_lyrics->text().simplifyWhiteSpace().lower() ); + m_words_dirty = FALSE; +} + + + + +void singerBot::createWave( notePlayHandle * _n ) +{ + handle_data * hdata = new handle_data; + _n->m_pluginData = hdata; + hdata->resampling_data = NULL; + hdata->wave = NULL; + hdata->remaining_frames = 0; + + if( m_words_dirty ) + { + updateWords(); + } + if( m_words.empty() ) + { + return; + } + + hdata->frequency = getInstrumentTrack()->frequency( _n ) + / ( eng()->getMixer()->sampleRate() + / DEFAULT_SAMPLE_RATE ); + hdata->duration = _n->length() > 0 ? + _n->length() * 60.0f * BEATS_PER_TACT + / 64.0f / eng()->getSongEditor()->getTempo() : + 0; + int word_index = _n->patternIndex() % m_words.size(); + hdata->text = m_words[word_index].ascii(); + + s_thread->set_data( hdata ); + s_thread->unlock_synth(); + s_thread->lock_handle(); + + if( !hdata->wave ) + { + return; + } + hdata->wave->resample( eng()->getMixer()->sampleRate() ); + hdata->remaining_frames = hdata->wave->num_samples(); +} + + + + +sampleBuffer * singerBot::readWave( handle_data * _hdata ) +{ + f_cnt_t buffer_size = eng()->getMixer()->framesPerAudioBuffer(); + f_cnt_t frames = tMin( buffer_size, _hdata->remaining_frames ); + f_cnt_t offset = _hdata->wave->num_samples() - _hdata->remaining_frames; + + const float fac = 1.0f / OUTPUT_SAMPLE_MULTIPLIER; + sampleFrame * data = new sampleFrame[frames]; + + for( f_cnt_t frame = 0; frame < frames; ++frame ) + { + sample_t wave_sample = _hdata->wave->a( offset + frame ) * fac; + for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + data[frame][chnl] = wave_sample; + } + } + + sampleBuffer * buffer = new sampleBuffer( data, frames, eng() ); + _hdata->remaining_frames -= frames; + delete[] data; + return( buffer ); +} + + + + + + + + +singerBot::synThread::synThread( void ) : + m_handle_semaphore( 1 ), + m_synth_semaphore( 1 ) +{ + m_handle_semaphore += m_handle_semaphore.total(); + m_synth_semaphore += m_synth_semaphore.total(); +} + + + + +singerBot::synThread::~synThread() +{ + m_handle_semaphore -= m_handle_semaphore.total(); + m_synth_semaphore -= m_synth_semaphore.total(); +} + + + + +void singerBot::synThread::run( void ) +{ + const int load_init_files = 1; + festival_initialize( load_init_files, FESTIVAL_HEAP_SIZE ); + + festival_eval_command( + "(define get_segment" + " (lambda (utt) (begin" + " (Initialize utt)" + " (Text utt)" + " (Token_POS utt)" + " (Token utt)" + " (POS utt)" + " (Phrasify utt)" + " (Word utt)" + " ))" + ")" ); + + festival_eval_command( + "(Parameter.set 'Int_Method 'DuffInt)" ); + festival_eval_command( + "(Parameter.set 'Int_Target_Method Int_Targets_Default)" ); + + for( ; ; ) + { + m_synth_semaphore++; + text_to_wave(); + m_handle_semaphore--; + } +} + + + + +void singerBot::synThread::text_to_wave( void ) +{ + char command[80]; + sprintf( command, + "(set! duffint_params '((start %f) (end %f)))", + m_data->frequency, m_data->frequency ); + festival_eval_command( command ); + festival_eval_command( + "(Parameter.set 'Duration_Stretch 1)" ); + + sprintf( command, + "(set! total_time (parse-number %f))", m_data->duration ); + festival_eval_command( command ); + festival_eval_command( + "(set! word " + quote_string( m_data->text, "\"", "\\", 1 ) + + ")" ); + festival_eval_command( + "(set! my_utt (eval (list 'Utterance 'Text word)))" ); + festival_eval_command( + " (get_segment my_utt)" ); + festival_eval_command( + "(if (equal? (length (utt.relation.leafs my_utt 'Segment)) 1)" + " (begin (set! my_utt (eval " + "(list 'Utterance 'Text (string-append word \" \" word))))" + " (get_segment my_utt)" + "))" ); + festival_eval_command( + " (Pauses my_utt)" ); + festival_eval_command( + " (item.delete (utt.relation.first my_utt 'Segment))" ); + festival_eval_command( + " (item.delete (utt.relation.last my_utt 'Segment))" ); + festival_eval_command( + " (Intonation my_utt)" ); + festival_eval_command( + " (PostLex my_utt)" ); + festival_eval_command( + " (Duration my_utt)" ); + festival_eval_command( + "(if (not (equal? total_time 0)) (begin" + "(set! utt_time" + " (item.feat (utt.relation.last my_utt 'Segment) 'end))" + "(Parameter.set 'Duration_Stretch (/ total_time utt_time))" + "(Duration my_utt)" + "))" ); + festival_eval_command( + " (Int_Targets my_utt)" ); + if( festival_eval_command( + " (Wave_Synth my_utt)" ) ) + { + m_data->wave = get_wave( "my_utt" ); + } + else + { + // Unsupported frequency + m_data->wave = NULL; + } +} + + + + +EST_Wave * singerBot::synThread::get_wave( const char * _name ) +{ + LISP lutt = siod_get_lval( _name, NULL ); + if( !utterance_p( lutt ) ) + { + return( NULL ); + } + + EST_Relation *r = utterance( lutt )->relation( "Wave" ); + + //TODO: This check is useless. The error is fatal. + if ( !r || !r->head() ) + { + return( NULL ); + } + + return( new EST_Wave( *wave( r->head()->f( "wave" ) ) ) ); +} + + + + +#include "singerbot.moc" diff --git a/plugins/singerbot/singerbot.h b/plugins/singerbot/singerbot.h new file mode 100644 index 0000000000..066a4aa779 --- /dev/null +++ b/plugins/singerbot/singerbot.h @@ -0,0 +1,137 @@ +/* + * singerbot.h - declaration of class singerBot, a singing bot instrument plugin + * + * Copyright (c) 2007 Javier Serrano Polo + * + * 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. + * + */ + + +#ifndef _SINGERBOT_H +#define _SINGERBOT_H + + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "instrument.h" + + +class EST_Wave; +class QTextEdit; +class sampleBuffer; +class EST_String; + + +class singerBot : public instrument +{ + Q_OBJECT +public: + singerBot( instrumentTrack * _track ); + virtual ~singerBot(); + + virtual void FASTCALL playNote( notePlayHandle * _n, + bool _try_parallelizing ); + virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n ); + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _this ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + virtual QString nodeName( void ) const; + + +public slots: + void lyricsChanged( void ); + + +private: + typedef struct + { + void * resampling_data; + EST_Wave * wave; + int remaining_frames; + float frequency; + float duration; + const char * text; + } handle_data; + + + class synThread : public QThread + { + public: + synThread( void ); + virtual ~synThread(); + + void set_data( handle_data * _hdata ) + { + m_data = _hdata; + } + + void unlock_synth( void ) + { + m_synth_semaphore--; + } + void lock_handle( void ) + { + m_handle_semaphore++; + } + + + protected: + virtual void run( void ); + + + private: + QSemaphore m_handle_semaphore; + QSemaphore m_synth_semaphore; + + handle_data * m_data; + + void text_to_wave( void ); + EST_Wave * get_wave( const char * _name ); + + } ; + + + static synThread * s_thread; + + QTextEdit * m_lyrics; + QStringList m_words; + bool m_words_dirty; + + void createWave( notePlayHandle * _n ); + sampleBuffer * readWave( handle_data * _hdata ); + void updateWords( void ); + +} ; + + + + +#endif diff --git a/plugins/triple_oscillator/triple_oscillator.cpp b/plugins/triple_oscillator/triple_oscillator.cpp index 429fb88a66..0784dae6a9 100644 --- a/plugins/triple_oscillator/triple_oscillator.cpp +++ b/plugins/triple_oscillator/triple_oscillator.cpp @@ -1,7 +1,7 @@ /* * triple_oscillator.cpp - powerful instrument with three oscillators * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -769,10 +769,6 @@ void tripleOscillator::playNote( notePlayHandle * _n, bool ) void tripleOscillator::deleteNotePluginData( notePlayHandle * _n ) { - if( _n->m_pluginData == NULL ) - { - return; - } delete static_cast( static_cast( _n->m_pluginData )->oscLeft ); delete static_cast( static_cast( diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index b42a376300..199b57bed3 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -1,7 +1,7 @@ /* * vestige.cpp - instrument-plugin for hosting VST-plugins * - * Copyright (c) 2005-2006 Tobias Doerffel + * Copyright (c) 2005-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -354,6 +354,8 @@ void vestigeInstrument::playNote( notePlayHandle * _n, bool ) } m_plugin->enqueueMidiEvent( midiEvent( NOTE_ON, 0, k, _n->getVolume() ), _n->framesAhead() ); + // notify when the handle stops, call to deleteNotePluginData + _n->m_pluginData = _n; } m_pluginMutex.unlock(); } diff --git a/src/core/arp_and_chords_tab_widget.cpp b/src/core/arp_and_chords_tab_widget.cpp index 6b846d75a6..30b88f1020 100644 --- a/src/core/arp_and_chords_tab_widget.cpp +++ b/src/core/arp_and_chords_tab_widget.cpp @@ -4,7 +4,7 @@ * arp_and_chords_tab_widget.cpp - widget for use in arp/chord-tab of * instrument-track-window * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -461,7 +461,7 @@ void arpAndChordsTabWidget::processNote( notePlayHandle * _n ) break; } // create copy of base-note - note note_copy( NULL, 0, 0, + note note_copy( NULL, _n->length(), 0, (tones)( sub_note_key % NOTES_PER_OCTAVE ), (octaves)( sub_note_key / @@ -477,6 +477,10 @@ void arpAndChordsTabWidget::processNote( notePlayHandle * _n ) _n->framesAhead(), _n->frames(), note_copy ); note_play_handle->setBBTrackFrom( _n ); +#if SINGERBOT_SUPPORT + note_play_handle->setPatternIndex( + _n->patternIndex() ); +#endif // add sub-note to base-note, now all stuff is // done by notePlayHandle::play_note() _n->addSubNote( note_play_handle ); @@ -640,6 +644,9 @@ void arpAndChordsTabWidget::processNote( notePlayHandle * _n ) new_note, TRUE ); note_play_handle->setBBTrackFrom( _n ); +#if SINGERBOT_SUPPORT + note_play_handle->setPatternIndex( _n->patternIndex() ); +#endif // add sub-note to base-note - now all stuff is done by // notePlayHandle::playNote() diff --git a/src/core/note_play_handle.cpp b/src/core/note_play_handle.cpp index a1768cbfe4..c6f92acaf3 100644 --- a/src/core/note_play_handle.cpp +++ b/src/core/note_play_handle.cpp @@ -4,7 +4,7 @@ * note_play_handle.cpp - implementation of class notePlayHandle, part of * play-engine * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -59,6 +59,9 @@ notePlayHandle::notePlayHandle( instrumentTrack * _it, m_arpNote( _arp_note ), m_muted( FALSE ), m_bbTrack( NULL ) +#if SINGERBOT_SUPPORT + , m_patternIndex( 0 ) +#endif { // if the instrument is monophonic we do not allow other note-play- // handles to exist for this track and therefore remove them @@ -117,7 +120,7 @@ notePlayHandle::~notePlayHandle() noteOff( 0 ); } - if( m_instrumentTrack != NULL ) + if( m_instrumentTrack != NULL && m_pluginData != NULL ) { m_instrumentTrack->deleteNotePluginData( this ); } @@ -252,7 +255,10 @@ void notePlayHandle::checkValidity( void ) { noteOff( 0 ); } - m_instrumentTrack->deleteNotePluginData( this ); + if( m_pluginData ) + { + m_instrumentTrack->deleteNotePluginData( this ); + } m_instrumentTrack = NULL; } // sub-notes might not be registered at mixer (for example arpeggio- diff --git a/src/lib/sample_buffer.cpp b/src/lib/sample_buffer.cpp index 71b9c87061..9d6d636448 100644 --- a/src/lib/sample_buffer.cpp +++ b/src/lib/sample_buffer.cpp @@ -397,7 +397,7 @@ f_cnt_t sampleBuffer::decodeSampleSDL( const char * _f, { AUDIO_S16SYS, _channels, - _samplerate, + _samplerate } ; f_cnt_t frames = 0; diff --git a/src/tracks/instrument_track.cpp b/src/tracks/instrument_track.cpp index 3e96a9f25b..e4da18206a 100644 --- a/src/tracks/instrument_track.cpp +++ b/src/tracks/instrument_track.cpp @@ -4,7 +4,7 @@ * instrument_track.cpp - implementation of instrument-track-class * (window + data-structures) * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -664,7 +664,9 @@ void instrumentTrack::processInEvent( const midiEvent & _me, { m_notesMutex.lock(); m_notes[_me.key()] = nph; + m_notesMutex.unlock(); } + return; } break; } @@ -777,7 +779,7 @@ QString instrumentTrack::instrumentName( void ) const void instrumentTrack::deleteNotePluginData( notePlayHandle * _n ) { - if( m_instrument != NULL ) + if( m_instrument != NULL && _n->m_pluginData != NULL ) { m_instrument->deleteNotePluginData( _n ); } @@ -996,6 +998,9 @@ bool FASTCALL instrumentTrack::play( const midiTime & _start, noteVector & notes = p->notes(); // ...and set our index to zero noteVector::iterator it = notes.begin(); +#if SINGERBOT_SUPPORT + int note_idx = 0; +#endif // very effective algorithm for playing notes that are // posated within the current sample-frame @@ -1006,6 +1011,12 @@ bool FASTCALL instrumentTrack::play( const midiTime & _start, // skip notes which are posated before start-tact while( it != notes.end() && ( *it )->pos() < cur_start ) { +#if SINGERBOT_SUPPORT + if( ( *it )->length() != 0 ) + { + ++note_idx; + } +#endif ++it; } } @@ -1045,6 +1056,9 @@ bool FASTCALL instrumentTrack::play( const midiTime & _start, note_frames, *cur_note ); note_play_handle->setBBTrack( bb_track ); +#if SINGERBOT_SUPPORT + note_play_handle->setPatternIndex( note_idx ); +#endif note_play_handle->play( FALSE ); // could we play all within current number of // frames per audio-buffer? @@ -1061,6 +1075,9 @@ bool FASTCALL instrumentTrack::play( const midiTime & _start, delete note_play_handle; } played_a_note = TRUE; +#if SINGERBOT_SUPPORT + ++note_idx; +#endif } ++it; } diff --git a/src/widgets/project_notes.cpp b/src/widgets/project_notes.cpp index 0fa2b96482..70023a7020 100644 --- a/src/widgets/project_notes.cpp +++ b/src/widgets/project_notes.cpp @@ -73,7 +73,7 @@ -projectNotes::projectNotes( engine * _engine) : +projectNotes::projectNotes( engine * _engine ) : QMainWindow( _engine->getMainWindow()->workspace() #ifndef QT4 , 0, Qt::WStyle_Title