• Non ci sono risultati.

3.2 “Back to the Future”

Listato 3.8: Implementazione dellʼoscillatore digitale fornita da pCM+

Come si può facilmente osservare, l’operatore pcm::Oscillator::operator() implementa lo pseu-

do-codice nel listato 1.1.

Il parametro del template pcm::Oscillator è un modello del concetto di Wavetable presentato nel seguente paragrafo.

3.9.7. Wavetable

pCM+ introduce un concetto anche per le wavetable (vedi paragrafo 1.2.1). Tale concetto è utiliz- zato nei parametri (di template) di tutti gli Element che fanno uso di wavetable, tra cui pcm::Oscillator (vedi paragrafo 3.9.6.2), pcm::LinearInterpolatingOscillator (che implementa l’oscillatore digitale interpolante, con interpolazione lineare), e pcm::TableAccessor (che imple- menta l’opcode tablei di Csound).

Una Wavetable è un Singleton (le pseudo-signature del concetto di Singleton sono le stesse esposte dalla classe template Loki::SingletonHolder, della libreria Loki). Oltre alle pseudo-signature di

Tabella 3.6: Pseudo-signatures del concetto di Wavetable.

Pseudo-Signature Semantica

Wave::size La dimensione della wavetable.

Wave::sample_rate La frequenza di campionamento della wavetable.

Wave::Wave( ) Costruttore di default.

const pcm::Sample &Wave::operator[]

( unsigned long idx ) const Operatore di indicizzazione (con indice intero). pcm::Sample operator[]

( double idx ) const

Operatore di indicizzazione (con indice in virgola mobile).

Come supporto alla definizione di modelli di Wavetable, pCM+ fornisce la classe template pcm::Wavetable (definita nel file header <pcm+/Wavetable.h>). Il primo parametro che l’utente deve fornire deve modellare il concetto di WaveInitializer, le cui pseudo-signature sono quelle in tabella 3.6, in cui WInit ne è un modello.

Tabella 3.7: Pseudo-signatures del concetto di WaveInitializer.

Pseudo-Signature Semantica

static void WInit::Init

( pcm::Sample *table, std::size_t size )

Inizializza table con una forma d’onda di size campioni.

Gli altri due parametri del template pcm::Wavetable sono opzionali, e determinano la dimensione e la frequenza di campionamento della wavetable (il valore di default di entrambi i parametri è pari a 44100).

3.9.8. Instrument::GetData< i >

In questo sotto-paragrafo è approfondita la semantica e le modalità di implementazione della pseu- do-signature GetData<i>() del concetto di Instrument (vedi paragrafo 3.9.5).

Dal paragrafo 3.9.6, sappiamo che tale signature è chiamata da pCM+ quando un oggetto Stream attraversa un oggetto Element. Il parametro di template i identifica la posizione dell’Element nella

pipeline logica dell’Instrument a cui l’oggetto Stream appartiene. Per chiarezza, riportiamo di se- guito la pseudo-signature Stream::GetData<i>() (Stream modello di Stream):

template< unsigned int i > void Stream::GetData( void *output, void *input )

Sia Instr il modello di Instrument a cui Stream appartiene (ovvero tale che i due tipi Instr::StreamType e Stream coincidono). Nonostante siano passati come void*, i due argomenti output e input hanno tipo Elem::OutputType, Elem::InputType, rispettivamente, dove Elem è il tipo dell’i-esimo Element nella Forward Sequence Instr::PipelineType. pCM+ garantisce pertanto che su tali argomenti possa essere svolto con successo uno static_cast su tali tipi, rispettivamente. Una volta che i parametri in ingresso sono stati convertiti correttamente, Stream::GetData<i>() in- serisce, per mezzo della signature statica Elem::SetOutputAt<i>()/Elem::SetInputAt<i>() nei loro campi i dati e i puntatori ai dati su cui Elem::operator() eseguirà il processamento audio.

Per agevolare l’utente nella definizione delle specializzazioni di Stream::GetData<i>(), nel file header <pcm+/UtilityInstrumentMacros.h> pCM+ definisce alcune macro di utilità.

Le macro PCM_CAST_OUTPUT, e PCM_CAST_INPUT, destinate agli argomenti output, input, rispettiva- mente, creano un riferimento a *output, *input, rispettivamente, dopo averne svolto il cast sopra citato. I parametri di tali macro sono l’indice i (il parametro di Stream::GetData<i>()), il nome da attribuire al riferimento creato, ed il nome attribuito al parametro a cui la macro è destinata (output per PCM_CAST_OUTPUT, input per PCM_CAST_INPUT).

Per accedere agevolmente ai campi delle tuple ottenute, pCM+ introduce le macro PCM_SET_OUT- PUT_FIELD, PCM_SET_INPUT_FIELD. L’esempio seguente mostra come utilizzare tali macro.

3.9.8.1. Esempio

Nel listato 3.9 sono mostrate le specializzazioni della signature GetData<i>() dell’Instrument Sim- pleChowningFMInstr definito nel listato 3.6.

#include "SimpleChowningFMInstr.h"

///////////////////////////////////////////////////// /// Stream::GetData< i >( ) SPETIALIZATIONS:

/////////////////////////////////////////////////////

template< >

void SimpleChowningFMInstr::StreamType::GetData< 0 >( void *output, void *input )

// Siamo sul primo element della pipeline (Modulation Index Envelope.)

{

// Recupera i dati in ingresso/uscita su cui operare.

// (I seguenti cast sono garantiti dal supporto PCM+).

PCM_CAST_OUTPUT( 0, out_ref, output ); PCM_CAST_INPUT( 0, in_ref, input );

// Just like that:

// typedef TYPE_AT( PipelineType, i ) ElementType;

// typename ElementType::OutputType &out_ref = *static_cast< typename ElementType::OutputType * >(

output );

// typename ElementType::InputType &in_ref = *static_cast< typename ElementType::InputType * >( input );

// Set input:

PCM_SET_INPUT_FIELD( 0, in_ref, instr_->GetModEnvAttackTime( ) ); PCM_SET_INPUT_FIELD( 1, in_ref, instr_->GetModEnvAttackAmpl( ) ); PCM_SET_INPUT_FIELD( 2, in_ref, instr_->GetModEnvDecayTime( ) ); PCM_SET_INPUT_FIELD( 3, in_ref, instr_->GetModEnvSustainAmpl( ) ); PCM_SET_INPUT_FIELD( 4, in_ref, instr_->GetModEnvReleaseTime( ) ); PCM_SET_INPUT_FIELD( 5, in_ref, instr_->GetModEnvReleaseTime( ) ); PCM_SET_INPUT_FIELD( 6, in_ref, note_on_ );

// Set output:

PCM_SET_OUTPUT_FIELD( 0, out_ref, deviation_ ); }

template< >

void SimpleChowningFMInstr::StreamType::GetData< 1 >( void *output, void *input )

// Siamo sul secondo element della pipeline (OSC1.)

{

// Recupera i dati in ingresso/uscita su cui operare.

// (I seguenti cast sono garantiti dal supporto PCM+).

PCM_CAST_OUTPUT( 1, out_ref, output ); PCM_CAST_INPUT( 1, in_ref, input );

// Set input:

PCM_SET_INPUT_FIELD( 0, in_ref, deviation_ * instr_->GetModulationFreq( ) ); PCM_SET_INPUT_FIELD( 1, in_ref, instr_->GetModulationFreq( ) );

// Set output:

PCM_SET_OUTPUT_FIELD( 0, out_ref, modulation_sign_ ); }

template< >

void SimpleChowningFMInstr::StreamType::GetData< 2 >( void *output, void *input )

// Siamo sul terzo element della pipeline (Carried Envelope.)

{

// Recupera i dati in ingresso/uscita su cui operare.

// (I seguenti cast sono garantiti dal supporto PCM+).

PCM_CAST_OUTPUT( 2, out_ref, output ); PCM_CAST_INPUT( 2, in_ref, input );

// Set input:

PCM_SET_INPUT_FIELD( 0, in_ref, instr_->GetCarEnvAttackTime( ) ); PCM_SET_INPUT_FIELD( 1, in_ref, instr_->GetCarEnvAttackAmpl( ) ); PCM_SET_INPUT_FIELD( 2, in_ref, instr_->GetCarEnvDecayTime( ) ); PCM_SET_INPUT_FIELD( 3, in_ref, instr_->GetCarEnvSustainAmpl( ) ); PCM_SET_INPUT_FIELD( 4, in_ref, instr_->GetCarEnvReleaseTime( ) ); PCM_SET_INPUT_FIELD( 5, in_ref, instr_->GetCarEnvReleaseTime( ) ); PCM_SET_INPUT_FIELD( 6, in_ref, note_on_ );

// Set output:

PCM_SET_OUTPUT_FIELD( 0, out_ref, amp_sign_ ); }

template< >

void SimpleChowningFMInstr::StreamType::GetData< 3 >( void *output, void *input )

// Siamo sul quarto element della pipeline (OSC2.)

{

// Recupera i dati in ingresso/uscita su cui operare.

// (I seguenti cast sono garantiti dal supporto PCM+).

PCM_CAST_OUTPUT( 3, out_ref, output ); PCM_CAST_INPUT( 3, in_ref, input );

// Set input:

PCM_SET_INPUT_FIELD( 0, in_ref, amp_sign_ );

PCM_SET_INPUT_FIELD( 1, in_ref, modulation_sign_ + carried_freq_ );

// Set output:

PCM_SET_OUTPUT_FIELD( 0, out_ref, out_sign_ ); }

Come si può verificare da una lettura attenta del codice, queste specializzazioni di GetData imple- mentano complessivamente la patch rappresentata in figura 1.3.

3.9.9. Stream Converter

Nel paragrafo 3.9.4 avevamo chiamato stream converter le specializzazioni del metodo template template< unsigned int i > ConvertStream() dei modelli di Orchestra. Di seguito ne riportiamo la signature, dove Orc è un modello di Orchestra:

template< unsigned int i > void Orc::ConvertStream( const void *src_stream, void *dest_stream ) const

Il k-esimo stream converter (cioè la specializzazione che fissa il parametro i al valore k) ha la re- sponsabilità di convertire lo Stream in uscita dalla k-1-esima pipeline logica nello Stream in ingres- so alla k-esima pipeline logica.

In altre parole, gli stream converter realizzano a livello di orchestra ciò che GetData() svolge a li- vello di instrument: implementano cioè la patch definita dall’orchestra tra gli instrument interni al- l’orchestra stesso.

Il seguente esempio mostra le modalità di implementazione degli stream converter per mezzo delle macro fornite nel file header <pcm+/UtilityOrchestraMacros.h> e dell’Instrument pcm::OutInstr, fornito da pCM+ nel file header <pcm+/OutInstr.h>, per scrivere i segnali generati e ricevuti al fon- do della pipeline fisica nei buffer audio in uscita.

3.9.9.1. Esempio

#include "LeadOrchestra.h"

// SPECIALIZZAZIONE DI StateType::AddInstr() PER L'INSTR SimpleChowningFMInstr.

template<>

void LeadOrchestra::StateType::AddInstr< 1 >( PCM_INSTR_PTR_TYPE( 1 ) instr )

{

PCM_INSTR_PTR_AT( instrs_tuple_, 1 ) = instr;

// L'inviluppo di modulazione di frequenza e' fissato (non modificabile).

PCM_INSTR_PTR_AT( instrs_tuple_, 1 )->SetModEnvAttackTime( .25l ); PCM_INSTR_PTR_AT( instrs_tuple_, 1 )->SetModEnvAttackAmpl( 1l ); PCM_INSTR_PTR_AT( instrs_tuple_, 1 )->SetModEnvDecayTime( .5l ); PCM_INSTR_PTR_AT( instrs_tuple_, 1 )->SetModEnvSustainAmpl( .75l ); PCM_INSTR_PTR_AT( instrs_tuple_, 1 )->SetModEnvReleaseTime( .5l ); PCM_INSTR_PTR_AT( instrs_tuple_, 1 )->SetModEnvReleaseAmpl( 0l ); }

// MODALITA' DI IMPLEMENTAZIONE DEGLI STREAM CONVERTER:

// Siano X, Y due instrument consecutivi nella pipeline di un orchestra O, // e x, y gli stream in ingresso, uscita, risp., alla funzione di conversione F // degli stream tra X e Y.

//

// F "legge" su x per mezzo della sua interfaccia. //

// F "scrive" su y per mezzo della sua interfaccia. // STREAM CONVERTERS SPETIALIZATIONS:

/// N.B.: Il primo strem-converter in realta' non deve convertire nulla, ma generare opportunamente // il primo stream della pipeline.

template<> void LeadOrchestra::ConvertStream< 0 >( const void *, void *dest_stream ) const

{

// N.B.: il supporto PCM garantisce che il seguente cast vada a buon fine.

PCM_CAST_DEST_STREAM( 0, dest_stream_ref, dest_stream );

// Modifica dest_s rispetto allo stato di this.

// In particolare rispetto a on_, freq_ e ampl_.

dest_stream_ref.SetNoteOn( on_ );

dest_stream_ref.SetFreq( GetFreq( ) );

dest_stream_ref.SetAmp( GetAmpl( ) );

}

template<> void LeadOrchestra::ConvertStream< 1 >( const void *src_stream, void *dest_stream ) const

{

// N.B.: il supporto PCM garantisce che i seguenti cast vadano a buon fine.

PCM_CAST_SRC_STREAM( 0, src_stream_ref, src_stream ); PCM_CAST_DEST_STREAM( 1, dest_stream_ref, dest_stream );

// Scrivi in buff_ il campione calcolato dal primo instrument.

buff_[ w_index_ ] = *src_stream_ref.GetOutput( );

w_index_ = ( w_index_ + 1 ) % max_num_toks_;

// Modifica dest_s rispetto allo stato di this.

// In particolare rispetto a on_, freq_ e ampl_.

dest_stream_ref.SetNoteOn( on_ );

dest_stream_ref.SetFreq( GetFreq( ) );

dest_stream_ref.SetAmp( GetAmpl( ) );

}

/// N.B.: Lo stream di OutInstr e' acceduto per mezzo dell'operator [], il cui indice si // riferisce al numero del CANALE a cui attribuire il campione assegnato. Per esempio: // l'istruzione 'out_s[ 1 ] = my_sample;' assegna il campione my_sample al secondo // canale in uscita (la base dell'indicizzazione e' 0).

template<> void LeadOrchestra::ConvertStream< 2 >( const void *src_stream, void *out_stream ) const

{

// N.B.: il supporto PCM garantisce che i seguenti cast vadano a buon fine.

PCM_CAST_SRC_STREAM( 1, src_stream_ref, src_stream ); PCM_CAST_DEST_STREAM( 2, out_s, out_stream );

pcm::Sample wt_sample = buff_[ r_index_ ];

r_index_ = ( r_index_ + 1 ) % max_num_toks_;

pcm::Sample fm_sample = *src_stream_ref.GetOutput( );

for ( unsigned int i = 0; i != pcm::num_channels; ++i ) {

out_s[ i ] = ( i % 2 ) ? fm_sample : wt_sample; }

}