Update NTK.
[nondaw.git] / sequencer / src / sequence.C
blobc6a0c386036563efbe7324037cbc981723e90f63
2 /*******************************************************************************/
3 /* Copyright (C) 2008 Jonathan Moore Liles                                     */
4 /*                                                                             */
5 /* This program is free software; you can redistribute it and/or modify it     */
6 /* under the terms of the GNU General Public License as published by the       */
7 /* Free Software Foundation; either version 2 of the License, or (at your      */
8 /* option) any later version.                                                  */
9 /*                                                                             */
10 /* This program is distributed in the hope that it will be useful, but WITHOUT */
11 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       */
12 /* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for   */
13 /* more details.                                                               */
14 /*                                                                             */
15 /* You should have received a copy of the GNU General Public License along     */
16 /* with This program; see the file COPYING.  If not,write to the Free Software */
17 /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18 /*******************************************************************************/
20 #include "sequence.H"
21 #include "phrase.H"
22 #include "pattern.H"
23 #include "smf.H"
25 #include "non.H"
27 #include <errno.h>
29 /* #include <string> */
31 /* using std::string; */
33 sequence::sequence ( void )
35     _rd = new data;
36     _name = _notes = NULL;
38     _index = 0;
39     _playing = 0;
43 void
44 sequence::lock ( void )
46     // create a copy of the lock-free data.
47     _rw = new data;
49     data *d = const_cast< data *> (_rd);
51     _rw->phrases = d->phrases;
52     _rw->num     = d->num;
55 void
56 sequence::unlock ( void )
58     _history.push_back( const_cast<data *>( _rd ) );
60     if ( _history.size() > MAX_UNDO + 1 )
61     {
62         data *d = _history.front();
64         if ( d == _rw || d == _rd )
65             ASSERTION( "something bad has happend." );
67         delete d;
69         _history.pop_front();
70     }
72     // swap the copy back in (atomically).
73     _rd = _rw;
75     _rw = NULL;
77     song.set_dirty();
80 void
81 sequence::insert ( unsigned int n, int pn )
83     lock();
85 /*     if ( n > _rw->phrases.size() ) */
86 /*         _rw->phrases.resize( n + 10 ); */
88 //    MESSAGE( "inserting %d at %d", pn, n );
90     _rw->phrases.insert( _find( n ), pn );
91     _rw->num++;
93     unlock();
96 vector <int>::iterator
97 sequence::_find ( int n )
99     // boy I hate C++/STL.. So lame.
100     int i = 0;
101     for ( vector <int>::iterator e = _rw->phrases.begin(); e != _rw->phrases.end(); e++ )
102     {
103         if ( i == n )
104             return e;
105         i++;
106     }
108     return _rw->phrases.end();
111 void
112 sequence::remove ( int n )
114     lock();
116     _rw->phrases.erase( _find( n ) );
117     _rw->num--;
119     unlock();
122 /** return the number of phrases in this sequence */
124 sequence::phrases ( void ) const
126     return _rd->num;
129 void
130 sequence::_swap ( int n1, int n2 )
132     int x = _rw->phrases[ n1 ];
133     _rw->phrases[ n1 ] = _rw->phrases[ n2 ];
134     _rw->phrases[ n2 ] = x;
137 void
138 sequence::move ( int n, int dir )
140     lock();
142     switch ( dir )
143     {
144         case UP:
145         {
146             if ( n - 1 >= 0 )
147                 _swap( n - 1, n );
148             break;
149         }
150         case DOWN:
151         {
152             if ( n + 1 < _rw->num )
153                 _swap( n + 1, n );
154             break;
155         }
157     }
159     unlock();
162 /* Render sequence to a string.. suitable for display in the UI */
163 char *
164 sequence::dump ( void )
166     char *s = (char *)malloc( 256 );
167     s[0] = '\0';
168     size_t siz = 256;
170     int start = 1;
171     for ( int i = 0; i < _rd->num; i++ )
172     {
173         const int len = 256;
175         char line[len];
177         int x = _rd->phrases[ i ];
179         phrase *p = phrase::phrase_by_number( x );
181         if ( ! p )
182             return NULL;
184         snprintf( line, len, "%d\t%d\t%s\n", start, p->number(), p->name() );
186         start += p->bars();
188         s = (char *)realloc( s, siz += strlen( line ) + 1 );
190         strcat( s, line );
191     }
192     return s;
196 void
197 sequence::play ( tick_t start, tick_t end ) const
199     // keep our own copy.
200     data *d = _rd;
202     tick_t offset = 0;
203     for ( int i = 0; i < d->num; i++ )
204     {
205         phrase *p = phrase::phrase_by_number( d->phrases[ i ] );
206         if ( p )
207         {
208             tick_t pstart = offset;
209             tick_t pend = offset + p->length();
211             // this phrase seems to be current.
212             if ( pend > start && pstart <= end )
213             {
214                 p->trigger( pstart, pend );
215           
216                 _playing = p->number();
218                 _index = start;
219                 
220                 p->play( start, end );
221             }
223             offset = pend;
224         }
225         else
226             WARNING( "programming error: no such phrase." );
227     }
230 /** return the number of the currently playing phrase, or 0 if none. */
232 sequence::playing ( void ) const
234     return _playing;
237 /** return the location of the playhead for this sequence */
238 tick_t
239 sequence::index ( void ) const
241     return _index;
244 /** return the total length of the sequence in ticks */
245 tick_t
246 sequence::length ( void ) const
248     tick_t l = 0;
250     for ( int i = 0; i < _rd->num; i++ )
251     {
252         phrase *p = phrase::phrase_by_number( _rd->phrases[ i ] );
254         if ( ! p )
255             break;
257         l += p->length();
258     }
260     return l;
263 /** return to a blank slate */
264 void
265 sequence::reset ( void )
267 //    MESSAGE( "reseting" );
269     lock();
271     _rw->num = 0;
273     phrase::reset();
274     pattern::reset();
276     unlock();
279 /** load entire sequence from file, replacing everything */
280 bool
281 sequence::load ( const char *name )
283     smf f;
285     if ( ! f.open( name, smf::READ ) )
286     {
287         WARNING( "error opening file: %s", strerror( errno ) );
288         return false;
289     }
291     f.read_header();
293     if ( f.format() != 2 )
294     {
295         WARNING( "not a Non song file" );
296         return false;
297     }
299     f.next_track();
301     DMESSAGE( "reading song info" );
303     /* read song info */
304     int mode = PATTERN;
305     int phrases = 0;
306     int patterns = 0;
307     char *sname = NULL;
308     char *notes = NULL;
310     if ( ! f.read_song_info( &mode, &phrases, &patterns, &sname, &notes ) )
311     {
312         WARNING( "not a Non song file" );
313         return false;
314     }
316     song.play_mode = (play_mode_e)mode;
318     if ( sname )
319         this->name( sname );
321     if ( notes )
322         this->notes( notes );
324     /* tear it down */
325     reset();
327     DMESSAGE( "reading playlist" );
329 //    f.read_playlist( this );
331     lock();
333     char *s;
334     while ( (s = f.read_cue_point() ) )
335     {
336         int n;
338         sscanf( s, "%d:", &n );
340         _rw->phrases.insert( _find( _rw->num++ ), n );
341     }
343     /* read playlist */
345     DMESSAGE( "reading phrases" );
347     while ( phrases-- && f.next_track() )
348     {
349         phrase *p = new phrase;
351         p->load( &f );
352     }
354     DMESSAGE( "reading patterns" );
356     while ( patterns-- && f.next_track() )
357     {
358         pattern *p = new pattern;
360         p->load( &f );
361     }
363     unlock();
365     signal_new_song();
367     return true;
370 /** save entire sequence to file */
371 void
372 sequence::save ( const char *name ) const
374     smf f;
376     /* open for writing */
377     f.open( name, smf::WRITE );
379     f.write_header( 2 );
381     DMESSAGE( "saving playlist" );
383     f.open_track( NULL, -1 );
385     DMESSAGE( "saving song info" );
387     f.write_song_info( song.play_mode, phrase::phrases(), pattern::patterns(), this->name(), notes() );
389     for ( int i = 0; i < _rd->num; ++i )
390     {
391         char pat[256];
393         phrase *p = phrase::phrase_by_number( _rd->phrases[ i ] );
395         snprintf( pat, 256, "%d: %s", p->number(), p->name() );
397         f.write_meta_event( smf::CUEPOINT, pat );
398     }
400     f.close_track( 0 );
402     DMESSAGE( "saving phrases" );
404     for ( int i = 0; i < phrase::phrases(); i++ )
405     {
406         phrase *p = phrase::phrase_by_number( i + 1 );
408         p->dump( &f );
409     }
411     DMESSAGE( "saving patterns" );
413     for ( int i = 0; i < pattern::patterns(); i++ )
414     {
415         pattern *p = pattern::pattern_by_number( i + 1 );
417         p->dump( &f );
418     }
422 /*************/
423 /* Accessors */
424 /*************/
426 char *
427 sequence::name ( void ) const
429     return _name;
432 void
433 sequence::name ( const char *s )
435     if ( _name ) free( _name );
437     _name = strdup( s );
439     song.set_dirty();
442 char *
443 sequence::notes ( void ) const
445     return _notes;
448 void
449 sequence::notes ( const char *s )
451     if ( _notes ) free( _notes );
453     _notes = strdup( s );
455     song.set_dirty();