Richie
Richie

Reputation: 366

Optimising a code block into a loop

I am pretty new to C++ and compiled languages in general I have a strong interpreted background so I am trying to get my head around the limitations of not having access to certain things at compile time.

At present I have a large block of code which looks like this:

//New note, note36

MemoryInputStream* input36 = new MemoryInputStream(BinaryData::C1hard1_wav, BinaryData::C1hard1_wavSize, false);
auto reader36 = audioFormatManager.createReaderFor(input36);

BigInteger note36;
note36.setRange(36, 1, true);

addSound(new SamplerSound("note36", *reader36, note36, 36, 0.001, 0.01, 26.0));

delete input36;
delete reader36;

//New note, note37

MemoryInputStream* input37 = new MemoryInputStream(BinaryData::Csharp1hard1_wav, BinaryData::Csharp1hard1_wavSize, false);
auto reader37 = audioFormatManager.createReaderFor(input37);

BigInteger note37;
note37.setRange(37, 1, true);

addSound(new SamplerSound("note37", *reader37, note37, 37, 0.001, 0.01, 26.0));

delete input37;
delete reader37;

This code repeats 48 times in this method and this is not good practice. An example of how I would achieve this in PHP, an interpreted language would look something like this

$noteMap = array(36 => "C1", 37 => "Csharp1");

foreach($noteMap as $midiNumber => $note)
{
    $name = $note . "hard1_wav";
    $size = $note . "hard1_wavSize";

    $input = new MemoryInputStream(BinaryData::$name, BinaryData::$size, false);
    $reader = audioFormatManager->createReaderFor($input);

    //I know this bit is bad PHP, no bigintegers in PHP but I can't think of a way to replicate it for arguments sake
    $note = 0;
    $note->setRange($midiNumber, 1, true);

    addSound(new SamplerSound("note".$midiNumber, $reader36, $note, $midiNumber, 0.001, 0.01, 26.0));
}

This is much more manageable and reusable. Changes I make will not need to be carefully repeated throughout the file and changes can be made quickly. I know that passing variables as function names is not something that be done in compiled languages, but even taking that into account there must be a way to wrap my large code block into a neat loop I just can't find the resource out there explaining it to me.

Upvotes: 2

Views: 215

Answers (3)

Jonesinator
Jonesinator

Reputation: 4216

Assuming BinaryData is a JUCE generated resource file the following algorithm should be equivalent to your PHP version. This doesn't use dynamic variable names, instead it uses the BinaryData::getNamedResource function that should appear in the JUCE generated code.

std::map<int, std::string> note_map{{36, "C1"}, {37, "Csharp1"}};
for ( const auto& note : note_map ) {
    BigInteger midi_bit;
    midi_bit.setBit(note.first);

    int size;
    std::string note_resource = note.second + "hard1_wav";
    auto data = BinaryData::getNamedResource(note_resource.c_str(), size);

    auto input = new MemoryInputStream(data, size, false);
    auto reader = audioFormatManager->createReaderFor(input);

    auto note_name = "note" + std::to_string(note.first);
    addSound(new SamplerSound(note_name.c_str(), *reader, midi_bit,
                              note.first, 0.001, 0.01, 26.0));
}

Another (bad) solution is to use the C Preprocessor to generate the code something like the following:

#define note(num, name)                                                                                               \
    MemoryInputStream* input##num = new MemoryInputStream(BinaryData::name##_wav, BinaryData::name##_wavSize, false); \
    auto reader##num = audioFormatManager.createReaderFor(input##num);                                                \
    BigInteger note##num;                                                                                             \
    note##num.setBit(num);                                                                                            \
    addSound(new SamplerSound("note" #num, *reader##num, note##num, num, 0.001, 0.01, 26.0));                         \
    delete input##num;                                                                                                \
    delete reader##num
note(36, C1);
note(37, Csharp1);

The following code is generated by the preprocessing stage:

MemoryInputStream* input36 = new MemoryInputStream(BinaryData::C1_wav, BinaryData::C1_wavSize, false); auto reader36 = audioFormatManager.createReaderFor(input36); BigInteger note36; note36.setBit(36); addSound(new SamplerSound("note" "36", *reader36, note36, 36, 0.001, 0.01, 26.0)); delete input36; delete reader36;
MemoryInputStream* input37 = new MemoryInputStream(BinaryData::Csharp1_wav, BinaryData::Csharp1_wavSize, false); auto reader37 = audioFormatManager.createReaderFor(input37); BigInteger note37; note37.setBit(37); addSound(new SamplerSound("note" "37", *reader37, note37, 37, 0.001, 0.01, 26.0)); delete input37; delete reader37;

The preprocessor solution should be code equivalent to your current solution, but in general it is not very good practice to generate tons of variables using macros in this way. The runtime solution above should be preferred over using the preprocessor.

Upvotes: 1

blackghost
blackghost

Reputation: 1825

You can do something like this:

typedef void (FuncPtr)(int x);
void C1Func(int x);
void CSharpFunc( int x);

void someFunc() {
    const Input input = new MemoryInputStream(name, size, false);
    const Reader reader = audioFormatManager->createReaderFor(input);

    // note: gxx syntax here, may not be supported in other versions...    
    Note noteMap[48] = { [36]=Note(36,C1Func), [37]=Note(37,CSharpFunc) };
    String name, size;

    for (int i = 0; i < arraySizeOf(noteMap); i++) {
        Note * note = noteMap[i];
        if (!note->isInitialized())
             continue;
        name = note + "hard1_wav";
        size = note + "hard1_wavSize";
        note->setRange(i,1,true);
        std::string name = "note"+i;
        addSound(new SamplerSound(name, reader, note[i], i, 0.001, 0.01, 26.0));
    }
    delete input;
    delete reader;
}

You'll need a Note class with a constructor (Note::Note(int idx,FuncPtr fn)). Also the initialization of the array is specific to gnu, so you might need to use std::map or something equivalent instead, (or define INVALID_NOTE as invalid and add a bunch of INVALID_NOTE,'s in there to mark the other array members as empty).

Upvotes: 1

gsamaras
gsamaras

Reputation: 73366

Don't that, such a waste of resources, where you call new and delete on every Note.

Use a data structure instead, such as an std::vector or an std::map.

Once you fill your data structure you can iterate over its elements.

Upvotes: 0

Related Questions