<commit_before>// Copyright (c) 2009 <NAME> <<EMAIL>>
//
// This file is part of librezin, a free software project.  You can redistribute it and/or modify
// it under the terms of the MIT License.

#include <rezin/snd.hpp>

#include <sfz/sfz.hpp>

using sfz::Bytes;
using sfz::BytesSlice;
using sfz::Exception;
using sfz::Json;
using sfz::JsonDefaultVisitor;
using sfz::ReadSource;
using sfz::StringMap;
using sfz::StringSlice;
using sfz::WriteTarget;
using sfz::format;
using sfz::hex;
using sfz::range;
using sfz::read;
using sfz::write;
using std::make_pair;
using std::map;
using std::vector;

namespace rezin {

namespace {

// Read the header of a format 1 'snd ' resource.
//
// @param [in] in       The ReadSource to read from.
void read_snd_format_1_header(ReadSource in) {
    uint16_t synthesizer_count;
    read(in, synthesizer_count);
    if (synthesizer_count != 1) {
        throw Exception(format("can only handle 1 synthesizer; {0} found", synthesizer_count));
    }

    uint16_t type;
    read(in, type);
    if (type != 5) {
        throw Exception(format("can only handle sampledSynth; {0} found", type));
    }

    uint32_t options;
    read(in, options);
    if ((options & 0x00F0) != options) {
        throw Exception(format("can only handle initMono and initStereo, not 0x{0}", hex(options)));
    }
}

// Read the header of a format 2 'snd ' resource.
//
// @param [in] in       The ReadSource to read from.
void read_snd_format_2_header(ReadSource in) {
    in.shift(2);
}

// Read an individual command from a 'snd ' resource.
//
// @param [in] in       The ReadSource to read from.
// @param [out] command The sound command.
// @param [out] param1  The first parameter to the sound command.
// @param [out] param2  The second parameter to the sound command.
void read_snd_command(ReadSource in, uint16_t& command, uint16_t& param1, uint32_t& param2) {
    read(in, command);
    read(in, param1);
    read(in, param2);
}

// Read the definition for a block of sampled sound data.
//
// Much of the data from the table is discarded: loop_start; loop_end; encoding; and
// base_frequency.  Hopefully this information is not important.
//
// @param [in] in       The ReadSource to read from.
// @param [out] pointer Where to look for the block of sampled sound data.
// @param [out] size    The size of the sampled sound data.
// @param [out] rate    The sample rate of the sound in Hz, e.g. 44100, 22050, 11025.
void read_snd_data_table(ReadSource in, uint32_t& pointer, uint32_t& size, double& rate) {
    uint32_t fixed_sample_rate;
    uint32_t loop_start;
    uint32_t loop_end;
    uint8_t encoding;
    uint8_t base_frequency;

    read(in, pointer);
    read(in, size);
    read(in, fixed_sample_rate);
    rate = fixed_sample_rate / 65536.0;
    read(in, loop_start);
    read(in, loop_end);
    read(in, encoding);
    read(in, base_frequency);
}

// Reads the samples for a sound into a JSON array.
//
// @param [in] in       The ReadSource to read from.
// @param [in] sample_count The number of samples to read.
// @param [out] samples The array to read the samples into.
void read_snd_samples(ReadSource in, uint32_t sample_count, vector<uint8_t>* samples) {
    SFZ_FOREACH(uint32_t i, range(sample_count), {
        uint8_t sample;
        read(in, sample);
        samples->push_back(sample);
    });
}

}  // namespace

Sound::Sound(BytesSlice in) {
    BytesSlice header(in);
    read(header, fmt);
    if (fmt == 1) {
        read_snd_format_1_header(header);
    } else if (fmt == 2) {
        read_snd_format_2_header(header);
    } else {
        throw Exception(format("unknown 'snd ' format '{0}'", fmt));
    }

    uint16_t command_count;
    read(header, command_count);
    if (command_count != 1) {
        throw Exception(format("can only handle 1 command; {0} found", command_count));
    }

    uint16_t command;
    uint16_t zero;
    uint32_t offset;
    read_snd_command(header, command, zero, offset);
    if (command != 0x8051) {
        throw Exception(format("can only handle bufferCmd; 0x{0} found", hex(command, 4)));
    }
    if (zero != 0) {
        throw Exception(format("param1 must be zero; {0} found", zero));
    }

    BytesSlice sound(in.slice(offset));
    uint32_t pointer;
    uint32_t sample_count;
    read_snd_data_table(sound, pointer, sample_count, sample_rate);
    channels = 1;
    sample_bits = 8;

    BytesSlice sample(in.slice(offset + 22 + pointer, sample_count));
    read_snd_samples(sample, sample_count, &samples);
}

namespace {

// Write `d` as an IEEE 754 80-bit floating point (extended precision) number.
//
// Not verified for special values (positive and negative zero and infinity, NaN) correctly,
// because those aren't valid sample rates, and that's the reason this function was written.
//
// @param [out] out     The WriteTarget to write the value to.
// @param [in] d        A double-precision floating point value.
void write_float80(WriteTarget out, double d) {
    // Reinterpret `d` as uint64_t, since we will be manupulating bits.
    uint64_t sample_rate_bits = *reinterpret_cast<uint64_t*>(&d);

    // Deconstruct the double:
    //   * the sign, 1 bit.
    //   * the exponent, 11 bits with a bias of 1023.
    //   * the fraction, 52 bits, with an implicit '1' bit preceding.
    uint64_t sign = sample_rate_bits >> 63;<commit_msg>Avoid warning on Linux.<commit_after>// Copyright (c) 2009 <NAME> <<EMAIL>>
//
// This file is part of librezin, a free software project.  You can redistribute it and/or modify
// it under the terms of the MIT License.

#include <rezin/snd.hpp>

#include <string.h>
#include <sfz/sfz.hpp>

using sfz::Bytes;
using sfz::BytesSlice;
using sfz::Exception;
using sfz::Json;
using sfz::JsonDefaultVisitor;
using sfz::ReadSource;
using sfz::StringMap;
using sfz::StringSlice;
using sfz::WriteTarget;
using sfz::format;
using sfz::hex;
using sfz::range;
using sfz::read;
using sfz::write;
using std::make_pair;
using std::map;
using std::vector;

namespace rezin {

namespace {

// Read the header of a format 1 'snd ' resource.
//
// @param [in] in       The ReadSource to read from.
void read_snd_format_1_header(ReadSource in) {
    uint16_t synthesizer_count;
    read(in, synthesizer_count);
    if (synthesizer_count != 1) {
        throw Exception(format("can only handle 1 synthesizer; {0} found", synthesizer_count));
    }

    uint16_t type;
    read(in, type);
    if (type != 5) {
        throw Exception(format("can only handle sampledSynth; {0} found", type));
    }

    uint32_t options;
    read(in, options);
    if ((options & 0x00F0) != options) {
        throw Exception(format("can only handle initMono and initStereo, not 0x{0}", hex(options)));
    }
}

// Read the header of a format 2 'snd ' resource.
//
// @param [in] in       The ReadSource to read from.
void read_snd_format_2_header(ReadSource in) {
    in.shift(2);
}

// Read an individual command from a 'snd ' resource.
//
// @param [in] in       The ReadSource to read from.
// @param [out] command The sound command.
// @param [out] param1  The first parameter to the sound command.
// @param [out] param2  The second parameter to the sound command.
void read_snd_command(ReadSource in, uint16_t& command, uint16_t& param1, uint32_t& param2) {
    read(in, command);
    read(in, param1);
    read(in, param2);
}

// Read the definition for a block of sampled sound data.
//
// Much of the data from the table is discarded: loop_start; loop_end; encoding; and
// base_frequency.  Hopefully this information is not important.
//
// @param [in] in       The ReadSource to read from.
// @param [out] pointer Where to look for the block of sampled sound data.
// @param [out] size    The size of the sampled sound data.
// @param [out] rate    The sample rate of the sound in Hz, e.g. 44100, 22050, 11025.
void read_snd_data_table(ReadSource in, uint32_t& pointer, uint32_t& size, double& rate) {
    uint32_t fixed_sample_rate;
    uint32_t loop_start;
    uint32_t loop_end;
    uint8_t encoding;
    uint8_t base_frequency;

    read(in, pointer);
    read(in, size);
    read(in, fixed_sample_rate);
    rate = fixed_sample_rate / 65536.0;
    read(in, loop_start);
    read(in, loop_end);
    read(in, encoding);
    read(in, base_frequency);
}

// Reads the samples for a sound into a JSON array.
//
// @param [in] in       The ReadSource to read from.
// @param [in] sample_count The number of samples to read.
// @param [out] samples The array to read the samples into.
void read_snd_samples(ReadSource in, uint32_t sample_count, vector<uint8_t>* samples) {
    SFZ_FOREACH(uint32_t i, range(sample_count), {
        uint8_t sample;
        read(in, sample);
        samples->push_back(sample);
    });
}

}  // namespace

Sound::Sound(BytesSlice in) {
    BytesSlice header(in);
    read(header, fmt);
    if (fmt == 1) {
        read_snd_format_1_header(header);
    } else if (fmt == 2) {
        read_snd_format_2_header(header);
    } else {
        throw Exception(format("unknown 'snd ' format '{0}'", fmt));
    }

    uint16_t command_count;
    read(header, command_count);
    if (command_count != 1) {
        throw Exception(format("can only handle 1 command; {0} found", command_count));
    }

    uint16_t command;
    uint16_t zero;
    uint32_t offset;
    read_snd_command(header, command, zero, offset);
    if (command != 0x8051) {
        throw Exception(format("can only handle bufferCmd; 0x{0} found", hex(command, 4)));
    }
    if (zero != 0) {
        throw Exception(format("param1 must be zero; {0} found", zero));
    }

    BytesSlice sound(in.slice(offset));
    uint32_t pointer;
    uint32_t sample_count;
    read_snd_data_table(sound, pointer, sample_count, sample_rate);
    channels = 1;
    sample_bits = 8;

    BytesSlice sample(in.slice(offset + 22 + pointer, sample_count));
    read_snd_samples(sample, sample_count, &samples);
}

namespace {

// Write `d` as an IEEE 754 80-bit floating point (extended precision) number.
//
// Not verified for special values (positive and negative zero and infinity, NaN) correctly,
// because those aren't valid sample rates, and that's the reason this function was written.
//
// @param [out] out     The WriteTarget to write the value to.
// @param [in] d        A double-precision floating point value.
void write_float80(WriteTarget out, double d) {
    // Reinterpret `d` as uint64_t, since we will be manupulating bits.
    uint64_t sample_rate_bits;
    memcpy(&sample_rate_bits, &d, sizeof(uint64_t));

    // Deconstruct the double:
    //   * the sign, 1 bit.
    //   * the exponent, 11 bits with a bias of 1023.
    //   * the fraction, 52 bits, with an implicit '1' bit preceding.
    uint64_t sign = sample_rate_bits >> 63;
{'fraction_non_alphanumeric': 0.11672862453531599, 'fraction_numerical': 0.0258364312267658, 'mean_word_length': 3.2889597449182943, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3695649', 'n_tokens_mistral': 3697, 'n_tokens_neox': 3469, 'n_words': 1170}