mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-26 14:33:01 +00:00
Merge pull request #2083 from tildearrow/sample_banks
Sample banks backport
This commit is contained in:
commit
dca10faff2
15 changed files with 1437 additions and 68 deletions
|
@ -681,6 +681,14 @@ src/engine/fileOps/text.cpp
|
||||||
src/engine/fileOps/tfm.cpp
|
src/engine/fileOps/tfm.cpp
|
||||||
src/engine/fileOps/xm.cpp
|
src/engine/fileOps/xm.cpp
|
||||||
|
|
||||||
|
src/engine/fileOps/p.cpp
|
||||||
|
src/engine/fileOps/p86.cpp
|
||||||
|
src/engine/fileOps/pdx.cpp
|
||||||
|
src/engine/fileOps/ppc.cpp
|
||||||
|
src/engine/fileOps/pps.cpp
|
||||||
|
src/engine/fileOps/pvi.cpp
|
||||||
|
src/engine/fileOps/pzi.cpp
|
||||||
|
|
||||||
src/engine/blip_buf.c
|
src/engine/blip_buf.c
|
||||||
src/engine/brrUtils.c
|
src/engine/brrUtils.c
|
||||||
src/engine/safeReader.cpp
|
src/engine/safeReader.cpp
|
||||||
|
|
|
@ -620,6 +620,17 @@ class DivEngine {
|
||||||
void loadWOPL(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadWOPL(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
void loadWOPN(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadWOPN(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
|
|
||||||
|
//sample banks
|
||||||
|
void loadP(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPPC(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPPS(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPVI(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPDX(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPZI(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadP86(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret);
|
int loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret);
|
||||||
|
|
||||||
bool initAudioBackend();
|
bool initAudioBackend();
|
||||||
|
@ -1034,7 +1045,8 @@ class DivEngine {
|
||||||
int addSamplePtr(DivSample* which);
|
int addSamplePtr(DivSample* which);
|
||||||
|
|
||||||
// get sample from file
|
// get sample from file
|
||||||
DivSample* sampleFromFile(const char* path);
|
//DivSample* sampleFromFile(const char* path);
|
||||||
|
std::vector<DivSample*> sampleFromFile(const char* path);
|
||||||
|
|
||||||
// get raw sample
|
// get raw sample
|
||||||
DivSample* sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign, bool swapNibbles, int rate);
|
DivSample* sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign, bool swapNibbles, int rate);
|
||||||
|
|
|
@ -63,3 +63,34 @@ enum DivFurVariants: int {
|
||||||
DIV_FUR_VARIANT_VANILLA=0,
|
DIV_FUR_VARIANT_VANILLA=0,
|
||||||
DIV_FUR_VARIANT_B=1,
|
DIV_FUR_VARIANT_B=1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// MIDI-related
|
||||||
|
struct midibank_t {
|
||||||
|
String name;
|
||||||
|
uint8_t bankMsb,
|
||||||
|
bankLsb;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reused patch data structures
|
||||||
|
|
||||||
|
// SBI and some other OPL containers
|
||||||
|
|
||||||
|
struct sbi_t {
|
||||||
|
uint8_t Mcharacteristics,
|
||||||
|
Ccharacteristics,
|
||||||
|
Mscaling_output,
|
||||||
|
Cscaling_output,
|
||||||
|
Meg_AD,
|
||||||
|
Ceg_AD,
|
||||||
|
Meg_SR,
|
||||||
|
Ceg_SR,
|
||||||
|
Mwave,
|
||||||
|
Cwave,
|
||||||
|
FeedConnect;
|
||||||
|
};
|
||||||
|
|
||||||
|
//bool stringNotBlank(String& str);
|
||||||
|
// detune needs extra translation from register to furnace format
|
||||||
|
//uint8_t fmDtRegisterToFurnace(uint8_t&& dtNative);
|
||||||
|
|
||||||
|
//void readSbiOpData(sbi_t& sbi, SafeReader& reader);
|
||||||
|
|
124
src/engine/fileOps/p.cpp
Normal file
124
src/engine/fileOps/p.cpp
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* 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; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//P VOX ADPCM sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 - 0x03FF 256 * {
|
||||||
|
Sample start (uint32_t)
|
||||||
|
- 0x00000000 = unused
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
MSM6258 ADPCM encoding
|
||||||
|
nibble-swapped VOX / Dialogic ADPCM
|
||||||
|
Mono
|
||||||
|
Sample rate?
|
||||||
|
16000Hz seems fine
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define P_BANK_SIZE 256
|
||||||
|
#define P_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t start_pointer;
|
||||||
|
} P_HEADER;
|
||||||
|
|
||||||
|
|
||||||
|
void DivEngine::loadP(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
P_HEADER headers[P_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < P_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned int)reader.readI_BE();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < P_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(headers[i].start_pointer != 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = P_SAMPLE_RATE;
|
||||||
|
s->centerRate = P_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_VOX;
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
int sample_len = 0;
|
||||||
|
|
||||||
|
if(i < P_BANK_SIZE - 1)
|
||||||
|
{
|
||||||
|
sample_len = headers[i + 1].start_pointer - headers[i].start_pointer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sample_len = (int)reader.size() - headers[i].start_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sample_len > 0)
|
||||||
|
{
|
||||||
|
s->init(sample_len * 2);
|
||||||
|
|
||||||
|
for(int j = 0; j < sample_len; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
curr_byte = (curr_byte << 4) | (curr_byte >> 4);
|
||||||
|
|
||||||
|
s->dataVOX[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
logI("p: start %d len %d", headers[i].start_pointer, sample_len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
142
src/engine/fileOps/p86.cpp
Normal file
142
src/engine/fileOps/p86.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* 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; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//P86 8-bit PCM sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 Identifier (12b)
|
||||||
|
"PCM86 DATA(\n)(\0)"
|
||||||
|
0x000C Targeted P86DRV version (1b)
|
||||||
|
version <high nibble>.<low nibble>
|
||||||
|
0x000D File Length (3b)
|
||||||
|
0x0010 - 0x060F 256 * {
|
||||||
|
|
||||||
|
Pointer to Sample Data Start (3b)
|
||||||
|
Length of Sample Data (3b)
|
||||||
|
|
||||||
|
(0x000000 0x000000 -> no sample for this instrument ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
8-Bit Signed
|
||||||
|
Mono
|
||||||
|
16540Hz
|
||||||
|
(above sample rate according to KAJA's documentation
|
||||||
|
any sample rate possible, for different base note & octave)
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define P86_BANK_SIZE 256
|
||||||
|
#define P86_SAMPLE_RATE 16540
|
||||||
|
|
||||||
|
#define P86_FILE_SIG "PCM86 DATA\n\0"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t start_pointer;
|
||||||
|
uint32_t sample_length;
|
||||||
|
} P86_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
uint32_t read_3bytes(SafeReader& reader)
|
||||||
|
{
|
||||||
|
unsigned char arr[3];
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
arr[i] = (unsigned char)reader.readC();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (arr[0] | (arr[1] << 8) | (arr[2] << 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivEngine::loadP86(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
P86_HEADER headers[P86_BANK_SIZE];
|
||||||
|
|
||||||
|
String file_sig = reader.readString(12);
|
||||||
|
if(file_sig != P86_FILE_SIG) return;
|
||||||
|
|
||||||
|
uint8_t version = reader.readC();
|
||||||
|
UNUSED(version);
|
||||||
|
|
||||||
|
uint32_t file_size = read_3bytes(reader);
|
||||||
|
UNUSED(file_size);
|
||||||
|
|
||||||
|
for(int i = 0; i < P86_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = read_3bytes(reader);
|
||||||
|
headers[i].sample_length = read_3bytes(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < P86_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(headers[i].start_pointer != 0 && headers[i].sample_length != 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = P86_SAMPLE_RATE;
|
||||||
|
s->centerRate = P86_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_8BIT;
|
||||||
|
s->init(headers[i].sample_length); //byte per sample
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
|
||||||
|
for(uint32_t j = 0; j < headers[i].sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
//curr_byte += 0x80;
|
||||||
|
|
||||||
|
s->data8[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
|
||||||
|
logI("p86: start %06X len %06X", headers[i].start_pointer, headers[i].sample_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
101
src/engine/fileOps/pdx.cpp
Normal file
101
src/engine/fileOps/pdx.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* 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; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PDX 8-bit OKI ADPCM sample bank
|
||||||
|
|
||||||
|
/* File format
|
||||||
|
The file starts with a header with 96 8-byte pairs, with each pair containing offset and length of ADPCM data chunks for each note value, like so:
|
||||||
|
|
||||||
|
[[byte pointer to sample in file -- 32-bit unsigned integer (4 bytes)] [empty: $0000 -- 16-bit integer] [length of sample, amount in bytes -- 16-bit unsigned integer] ×96] [ADPCM data] EOF
|
||||||
|
|
||||||
|
The first sample (1) is mapped to 0x80 (= the lowest note within MXDRV) and the last sample (96) is mapped to 0xDF (= the highest note within MXDRV).
|
||||||
|
|
||||||
|
|
||||||
|
Samples are encoded in 4-bit OKI ADPCM encoded nibbles, where each byte contains 2 nibbles: [nibble 2 << 4 || nibble 1]
|
||||||
|
|
||||||
|
|
||||||
|
Unfortunately, sample rates for each samples are not defined within the .PDX file and have to be set manually with the appropriate command for that at play-time */
|
||||||
|
|
||||||
|
#define PDX_BANK_SIZE 96
|
||||||
|
#define PDX_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int start_pointer;
|
||||||
|
unsigned short sample_length;
|
||||||
|
} PDX_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
void DivEngine::loadPDX(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
PDX_HEADER headers[PDX_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < PDX_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned int)reader.readI_BE();
|
||||||
|
unsigned short empty = (unsigned short)reader.readS_BE(); //skip 1st 2 bytes
|
||||||
|
UNUSED(empty);
|
||||||
|
headers[i].sample_length = (unsigned short)reader.readS_BE();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PDX_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(headers[i].start_pointer != 0 && headers[i].sample_length != 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = PDX_SAMPLE_RATE;
|
||||||
|
s->centerRate = PDX_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_VOX;
|
||||||
|
s->init(headers[i].sample_length * 2);
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
|
||||||
|
for(unsigned short j = 0; j < headers[i].sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
curr_byte = (curr_byte << 4) | (curr_byte >> 4);
|
||||||
|
|
||||||
|
s->dataVOX[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
|
||||||
|
logI("pdx: start %d len %d", headers[i].start_pointer, headers[i].sample_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
142
src/engine/fileOps/ppc.cpp
Normal file
142
src/engine/fileOps/ppc.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* 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; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PPC PMD's YM2608 ADPCM-B sample bank
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
General
|
||||||
|
========================================
|
||||||
|
|
||||||
|
ADPCM RAM addresses: see docs/common.txt {
|
||||||
|
|
||||||
|
address_start = 0x0026
|
||||||
|
file_header_size = 0x0420
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
========================================
|
||||||
|
Header
|
||||||
|
========================================
|
||||||
|
|
||||||
|
0x0000 Identifier (30b)
|
||||||
|
"ADPCM DATA for PMD ver.4.4- "
|
||||||
|
0x001E Address of End of Data (32B blocks) in ADPCM RAM (1h)
|
||||||
|
File Size == address -> file offset
|
||||||
|
|
||||||
|
0x0020 - 0x041F 256 * {
|
||||||
|
|
||||||
|
Start of Sample (32b blocks) in ADPCM RAM (1h)
|
||||||
|
End of Sample (32b blocks) in ADPCM RAM (1h)
|
||||||
|
|
||||||
|
(0x0000 0x0000 -> no sample for this instrument ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
========================================
|
||||||
|
Body
|
||||||
|
========================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
Yamaha ADPCM-B encoding (4-Bit Signed ADPCM)
|
||||||
|
Mono
|
||||||
|
16kHz
|
||||||
|
(above sample rate according to KAJA's documentation
|
||||||
|
any sample rate possible, for different base note & octave)
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define PPC_FILE_SIG "ADPCM DATA for PMD ver.4.4- "
|
||||||
|
|
||||||
|
#define PPC_BANK_SIZE 256
|
||||||
|
#define PPC_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t start_pointer;
|
||||||
|
uint16_t end_pointer;
|
||||||
|
} PPC_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
#define ADPCM_DATA_START 0x0420
|
||||||
|
|
||||||
|
void DivEngine::loadPPC(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
String file_sig = reader.readString(30);
|
||||||
|
unsigned short end_of_data = (unsigned short)reader.readS();
|
||||||
|
UNUSED(end_of_data);
|
||||||
|
|
||||||
|
if(file_sig != PPC_FILE_SIG) return;
|
||||||
|
|
||||||
|
PPC_HEADER headers[PPC_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < PPC_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned short)reader.readS();
|
||||||
|
headers[i].end_pointer = (unsigned short)reader.readS();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PPC_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if((headers[i].start_pointer != 0 || headers[i].end_pointer != 0) && headers[i].start_pointer < headers[i].end_pointer)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = PPC_SAMPLE_RATE;
|
||||||
|
s->centerRate = PPC_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_ADPCM_B;
|
||||||
|
s->init((headers[i].end_pointer - headers[i].start_pointer) * 32 * 2);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
int sample_length = (headers[i].end_pointer - headers[i].start_pointer) * 32;
|
||||||
|
|
||||||
|
//reader.seek(ADPCM_DATA_START + headers[i].start_pointer * 32, SEEK_SET);
|
||||||
|
|
||||||
|
for(int j = 0; j < sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
//curr_byte=(curr_byte<<4)|(curr_byte>>4);
|
||||||
|
|
||||||
|
s->dataB[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
logI("ppc: start %d end %d len in bytes %d", headers[i].start_pointer, headers[i].end_pointer, (headers[i].end_pointer - headers[i].start_pointer) * 32);
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
125
src/engine/fileOps/pps.cpp
Normal file
125
src/engine/fileOps/pps.cpp
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* 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; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PPS AY-3-8910 sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 - 0x0053 14 * {
|
||||||
|
|
||||||
|
Pointer to Sample Data Start (1h)
|
||||||
|
Length of Sample Data (1h)
|
||||||
|
"Pitch"(?) (1b)
|
||||||
|
Volume Reduction (1b)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
(0x0000 0x0000 [0x00 0x00] -> no sample for this instrument ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
4-Bit Unsigned
|
||||||
|
(afaict)
|
||||||
|
Mono
|
||||||
|
16Hz
|
||||||
|
(based on tests, maybe alternatively 8kHz)
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define PPS_BANK_SIZE 14
|
||||||
|
#define PPS_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t start_pointer;
|
||||||
|
uint16_t sample_length;
|
||||||
|
uint8_t _pitch;
|
||||||
|
uint8_t _vol;
|
||||||
|
} PPS_HEADER;
|
||||||
|
|
||||||
|
|
||||||
|
void DivEngine::loadPPS(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
PPS_HEADER headers[PPS_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < PPS_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned short)reader.readS();
|
||||||
|
headers[i].sample_length = (unsigned short)reader.readS();
|
||||||
|
headers[i]._pitch = (unsigned char)reader.readC();
|
||||||
|
headers[i]._vol = (unsigned char)reader.readC();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PPS_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(headers[i].start_pointer != 0 || headers[i].sample_length != 0
|
||||||
|
|| headers[i]._pitch != 0 || headers[i]._vol != 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = PPS_SAMPLE_RATE;
|
||||||
|
s->centerRate = PPS_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_8BIT;
|
||||||
|
s->init(headers[i].sample_length * 2); //byte per sample
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
|
||||||
|
for(int j = 0; j < headers[i].sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
|
||||||
|
s->data8[sample_pos] = (curr_byte >> 4) | (curr_byte & 0xf0);
|
||||||
|
s->data8[sample_pos] += 0x80;
|
||||||
|
sample_pos++;
|
||||||
|
s->data8[sample_pos] = (curr_byte << 4) | (curr_byte & 0xf);
|
||||||
|
s->data8[sample_pos] += 0x80;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
|
||||||
|
logI("pps: start %d len %d", headers[i].start_pointer, headers[i].sample_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
158
src/engine/fileOps/pvi.cpp
Normal file
158
src/engine/fileOps/pvi.cpp
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* 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; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PVI YM2608 ADPCM-B sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
General
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
ADPCM RAM addresses: see docs/common.txt {
|
||||||
|
|
||||||
|
address_start = 0x0000
|
||||||
|
file_header_size = 0x0210
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 Identifier (4b)
|
||||||
|
"PVI2"
|
||||||
|
0x0004 - 0x0007 Unknown Settings (4b)
|
||||||
|
Unknown mappings PCME switches <-> Values
|
||||||
|
"0x10 0x00 0x10 0x02" in all example files
|
||||||
|
First 1h may be Start Address in ADPCM RAM?
|
||||||
|
0x0008 - 0x0009 "Delta-N" playback frequency (1h)
|
||||||
|
Default 0x49BA "== 16kHz"??
|
||||||
|
0x000A Unknown (1b)
|
||||||
|
RAM type? (Docs indicate values 0 or 8, all example files have value 0x02?)
|
||||||
|
0x000B Amount of defined Samples (1b)
|
||||||
|
0x000C - 0x000F Unknown (4b)
|
||||||
|
Padding?
|
||||||
|
"0x00 0x00 0x00 0x00" in all example files
|
||||||
|
0x0010 - 0x020F 128 * {
|
||||||
|
|
||||||
|
Start of Sample (32b blocks) in ADPCM RAM (1h)
|
||||||
|
End of Sample (32b blocks) in ADPCM RAM (1h)
|
||||||
|
|
||||||
|
(0x0000 0x0000 -> no sample for this instrument ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
Yamaha ADPCM-B encoding (4-Bit Signed ADPCM)
|
||||||
|
Mono
|
||||||
|
Sample rate as specified earlier
|
||||||
|
(examples i have seems Stereo or 32kHz despite "16kHz" playback frequency setting?)
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define PVIV2_FILE_SIG "PVI2"
|
||||||
|
#define PVIV1_FILE_SIG "PVI1"
|
||||||
|
|
||||||
|
#define PVI_BANK_SIZE 128
|
||||||
|
#define PVI_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t start_pointer;
|
||||||
|
uint16_t end_pointer;
|
||||||
|
} PVI_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
#define ADPCM_DATA_START 0x0210
|
||||||
|
|
||||||
|
void DivEngine::loadPVI(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
String file_sig = reader.readString(4);
|
||||||
|
if(file_sig != PVIV1_FILE_SIG && file_sig != PVIV2_FILE_SIG) return;
|
||||||
|
|
||||||
|
unsigned int unknown_settings = (unsigned int)reader.readI();
|
||||||
|
UNUSED(unknown_settings);
|
||||||
|
unsigned short delta_n = (unsigned short)reader.readS();
|
||||||
|
UNUSED(delta_n);
|
||||||
|
unsigned char one_byte = (unsigned char)reader.readC();
|
||||||
|
UNUSED(one_byte);
|
||||||
|
unsigned char amount_of_samples = (unsigned char)reader.readC();
|
||||||
|
UNUSED(amount_of_samples);
|
||||||
|
unsigned int padding = (unsigned int)reader.readI();
|
||||||
|
UNUSED(padding);
|
||||||
|
|
||||||
|
PVI_HEADER headers[PVI_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < PVI_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned short)reader.readS();
|
||||||
|
headers[i].end_pointer = (unsigned short)reader.readS();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PVI_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if((headers[i].start_pointer != 0 || headers[i].end_pointer != 0) && headers[i].start_pointer < headers[i].end_pointer)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = PVI_SAMPLE_RATE;
|
||||||
|
s->centerRate = PVI_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_ADPCM_B;
|
||||||
|
s->init((headers[i].end_pointer - headers[i].start_pointer) * 32 * 2);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
int sample_length = (headers[i].end_pointer - headers[i].start_pointer) * 32;
|
||||||
|
|
||||||
|
reader.seek(ADPCM_DATA_START + headers[i].start_pointer * 32, SEEK_SET);
|
||||||
|
|
||||||
|
for(int j = 0; j < sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
//curr_byte=(curr_byte<<4)|(curr_byte>>4);
|
||||||
|
|
||||||
|
s->dataB[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
logI("pvi: start %d end %d len in bytes %d", headers[i].start_pointer, headers[i].end_pointer, (headers[i].end_pointer - headers[i].start_pointer) * 32);
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
155
src/engine/fileOps/pzi.cpp
Normal file
155
src/engine/fileOps/pzi.cpp
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* 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; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PZI 8-bit PCM sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 Identifier (4b)
|
||||||
|
"PZI1"
|
||||||
|
0x0004 - 0x001F Unknown (28b)
|
||||||
|
Part of identifier? Settings?
|
||||||
|
All (\0)s in all example files
|
||||||
|
0x0020 - 0x091F 128 * {
|
||||||
|
|
||||||
|
Start of Sample after header (2h)
|
||||||
|
Length of Sample (2h)
|
||||||
|
Offset of loop start from sample start (2h)
|
||||||
|
Offset of loop end from sample start (2h)
|
||||||
|
Sample rate (1h)
|
||||||
|
|
||||||
|
(0xFFFFFFFF 0xFFFFFFFF loop offsets -> no loop information)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
Unsigned 8-Bit
|
||||||
|
Mono
|
||||||
|
Sample rate as specified in header
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define PZI_BANK_SIZE 128
|
||||||
|
|
||||||
|
#define PZI_FILE_SIG "PZI1"
|
||||||
|
|
||||||
|
#define NO_LOOP (0xFFFFFFFFU)
|
||||||
|
|
||||||
|
#define SAMPLE_DATA_OFFSET 0x0920
|
||||||
|
|
||||||
|
#define MAX_SANITY_CAP 9999999
|
||||||
|
|
||||||
|
#define HEADER_JUNK_SIZE 28
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t start_pointer;
|
||||||
|
uint32_t sample_length;
|
||||||
|
uint32_t loop_start;
|
||||||
|
uint32_t loop_end;
|
||||||
|
uint16_t sample_rate;
|
||||||
|
} PZI_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
void DivEngine::loadPZI(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
PZI_HEADER headers[PZI_BANK_SIZE];
|
||||||
|
|
||||||
|
String file_sig = reader.readString(4);
|
||||||
|
if(file_sig != PZI_FILE_SIG) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < HEADER_JUNK_SIZE; i++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
UNUSED(curr_byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PZI_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned int)reader.readI();
|
||||||
|
headers[i].sample_length = (unsigned int)reader.readI();
|
||||||
|
headers[i].loop_start = (unsigned int)reader.readI();
|
||||||
|
headers[i].loop_end = (unsigned int)reader.readI();
|
||||||
|
headers[i].sample_rate = (unsigned short)reader.readS();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PZI_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if (headers[i].start_pointer < MAX_SANITY_CAP && headers[i].sample_length < MAX_SANITY_CAP &&
|
||||||
|
headers[i].loop_start < MAX_SANITY_CAP && headers[i].loop_end < MAX_SANITY_CAP &&
|
||||||
|
headers[i].start_pointer > 0 && headers[i].sample_length > 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = headers[i].sample_rate;
|
||||||
|
s->centerRate = headers[i].sample_rate;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_8BIT;
|
||||||
|
s->init(headers[i].sample_length); //byte per sample
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer + SAMPLE_DATA_OFFSET, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
|
||||||
|
for (uint32_t j = 0; j < headers[i].sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
curr_byte += 0x80;
|
||||||
|
|
||||||
|
s->data8[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers[i].loop_start != NO_LOOP && headers[i].loop_end != NO_LOOP)
|
||||||
|
{
|
||||||
|
s->loop = true;
|
||||||
|
s->loopMode = DIV_SAMPLE_LOOP_FORWARD;
|
||||||
|
s->loopStart = headers[i].loop_start;
|
||||||
|
s->loopEnd = headers[i].loop_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
|
||||||
|
logI("pzi: start %d len %d sample rate %d loop start %d loop end %d", headers[i].start_pointer, headers[i].sample_length,
|
||||||
|
headers[i].sample_rate, headers[i].loop_start, headers[i].loop_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,21 +19,6 @@
|
||||||
|
|
||||||
#include "fileOpsCommon.h"
|
#include "fileOpsCommon.h"
|
||||||
|
|
||||||
// SBI and some other OPL containers
|
|
||||||
struct sbi_t {
|
|
||||||
uint8_t Mcharacteristics,
|
|
||||||
Ccharacteristics,
|
|
||||||
Mscaling_output,
|
|
||||||
Cscaling_output,
|
|
||||||
Meg_AD,
|
|
||||||
Ceg_AD,
|
|
||||||
Meg_SR,
|
|
||||||
Ceg_SR,
|
|
||||||
Mwave,
|
|
||||||
Cwave,
|
|
||||||
FeedConnect;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void readSbiOpData(sbi_t& sbi, SafeReader& reader) {
|
static void readSbiOpData(sbi_t& sbi, SafeReader& reader) {
|
||||||
sbi.Mcharacteristics = reader.readC();
|
sbi.Mcharacteristics = reader.readC();
|
||||||
sbi.Ccharacteristics = reader.readC();
|
sbi.Ccharacteristics = reader.readC();
|
||||||
|
|
|
@ -24,10 +24,12 @@
|
||||||
#include "sfWrapper.h"
|
#include "sfWrapper.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DivSample* DivEngine::sampleFromFile(const char* path) {
|
std::vector<DivSample*> DivEngine::sampleFromFile(const char* path) {
|
||||||
|
std::vector<DivSample*> ret;
|
||||||
|
|
||||||
if (song.sample.size()>=256) {
|
if (song.sample.size()>=256) {
|
||||||
lastError="too many samples!";
|
lastError="too many samples!";
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
BUSY_BEGIN;
|
BUSY_BEGIN;
|
||||||
warnings="";
|
warnings="";
|
||||||
|
@ -58,6 +60,110 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
}
|
}
|
||||||
extS+=i;
|
extS+=i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(extS == ".pps" || extS == ".ppc" || extS == ".pvi" ||
|
||||||
|
extS == ".pdx" || extS == ".pzi" || extS == ".p86" ||
|
||||||
|
extS == ".p") //sample banks!
|
||||||
|
{
|
||||||
|
String stripPath;
|
||||||
|
const char* pathReduxEnd=strrchr(pathRedux,'.');
|
||||||
|
if (pathReduxEnd==NULL) {
|
||||||
|
stripPath=pathRedux;
|
||||||
|
} else {
|
||||||
|
for (const char* i=pathRedux; i!=pathReduxEnd && (*i); i++) {
|
||||||
|
stripPath+=*i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* f=ps_fopen(path,"rb");
|
||||||
|
if (f==NULL) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
unsigned char* buf;
|
||||||
|
ssize_t len;
|
||||||
|
if (fseek(f,0,SEEK_END)!=0) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
len=ftell(f);
|
||||||
|
if (len<0) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (len==(SIZE_MAX>>1)) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (len==0) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (fseek(f,0,SEEK_SET)!=0) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
buf=new unsigned char[len];
|
||||||
|
if (fread(buf,1,len,f)!=(size_t)len) {
|
||||||
|
logW("did not read entire sample bank file buffer!");
|
||||||
|
lastError=_("did not read entire sample bank file!");
|
||||||
|
delete[] buf;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
SafeReader reader = SafeReader(buf,len);
|
||||||
|
|
||||||
|
if(extS == ".pps")
|
||||||
|
{
|
||||||
|
loadPPS(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".ppc")
|
||||||
|
{
|
||||||
|
loadPPC(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".pvi")
|
||||||
|
{
|
||||||
|
loadPVI(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".pdx")
|
||||||
|
{
|
||||||
|
loadPDX(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".pzi")
|
||||||
|
{
|
||||||
|
loadPZI(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".p86")
|
||||||
|
{
|
||||||
|
loadP86(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".p")
|
||||||
|
{
|
||||||
|
loadP(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((int)ret.size() > 0)
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
for(DivSample* s: ret)
|
||||||
|
{
|
||||||
|
s->name = fmt::sprintf("%s sample %d", stripPath, counter);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] buf; //done with buffer
|
||||||
|
BUSY_END;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (extS==".dmc" || extS==".brr") { // read as .dmc or .brr
|
if (extS==".dmc" || extS==".brr") { // read as .dmc or .brr
|
||||||
size_t len=0;
|
size_t len=0;
|
||||||
DivSample* sample=new DivSample;
|
DivSample* sample=new DivSample;
|
||||||
|
@ -68,7 +174,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fseek(f,0,SEEK_END)<0) {
|
if (fseek(f,0,SEEK_END)<0) {
|
||||||
|
@ -76,7 +182,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
|
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
len=ftell(f);
|
len=ftell(f);
|
||||||
|
@ -86,7 +192,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="file is empty!";
|
lastError="file is empty!";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len==(SIZE_MAX>>1)) {
|
if (len==(SIZE_MAX>>1)) {
|
||||||
|
@ -94,7 +200,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="file is invalid!";
|
lastError="file is invalid!";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fseek(f,0,SEEK_SET)<0) {
|
if (fseek(f,0,SEEK_SET)<0) {
|
||||||
|
@ -102,7 +208,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
|
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extS==".dmc") {
|
if (extS==".dmc") {
|
||||||
|
@ -120,7 +226,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="wait... is that right? no I don't think so...";
|
lastError="wait... is that right? no I don't think so...";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* dataBuf=sample->dataDPCM;
|
unsigned char* dataBuf=sample->dataDPCM;
|
||||||
|
@ -147,14 +253,14 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="BRR sample is empty!";
|
lastError="BRR sample is empty!";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
} else if ((len%9)!=0) {
|
} else if ((len%9)!=0) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="possibly corrupt BRR sample!";
|
lastError="possibly corrupt BRR sample!";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,16 +269,17 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
|
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
return sample;
|
ret.push_back(sample);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_SNDFILE
|
#ifndef HAVE_SNDFILE
|
||||||
lastError="Furnace was not compiled with libsndfile!";
|
lastError="Furnace was not compiled with libsndfile!";
|
||||||
return NULL;
|
return ret;
|
||||||
#else
|
#else
|
||||||
SF_INFO si;
|
SF_INFO si;
|
||||||
SFWrapper sfWrap;
|
SFWrapper sfWrap;
|
||||||
|
@ -186,13 +293,13 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
} else {
|
} else {
|
||||||
lastError=fmt::sprintf("could not open file! (%s)\nif this is raw sample data, you may import it by right-clicking the Load Sample icon and selecting \"import raw\".",sf_error_number(err));
|
lastError=fmt::sprintf("could not open file! (%s)\nif this is raw sample data, you may import it by right-clicking the Load Sample icon and selecting \"import raw\".",sf_error_number(err));
|
||||||
}
|
}
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
if (si.frames>16777215) {
|
if (si.frames>16777215) {
|
||||||
lastError="this sample is too big! max sample size is 16777215.";
|
lastError="this sample is too big! max sample size is 16777215.";
|
||||||
sfWrap.doClose();
|
sfWrap.doClose();
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
void* buf=NULL;
|
void* buf=NULL;
|
||||||
sf_count_t sampleLen=sizeof(short);
|
sf_count_t sampleLen=sizeof(short);
|
||||||
|
@ -298,7 +405,8 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
if (sample->centerRate>64000) sample->centerRate=64000;
|
if (sample->centerRate>64000) sample->centerRate=64000;
|
||||||
sfWrap.doClose();
|
sfWrap.doClose();
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
return sample;
|
ret.push_back(sample);
|
||||||
|
return ret;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
341
src/gui/gui.cpp
341
src/gui/gui.cpp
|
@ -3828,8 +3828,9 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
int sampleCountBefore=e->song.sampleLen;
|
int sampleCountBefore=e->song.sampleLen;
|
||||||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file,true,settings.readInsNames);
|
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file,true,settings.readInsNames);
|
||||||
|
std::vector<DivSample*> samples = e->sampleFromFile(ev.drop.file);
|
||||||
DivWavetable* droppedWave=NULL;
|
DivWavetable* droppedWave=NULL;
|
||||||
DivSample* droppedSample=NULL;
|
//DivSample* droppedSample=NULL;
|
||||||
if (!instruments.empty()) {
|
if (!instruments.empty()) {
|
||||||
if (e->song.sampleLen!=sampleCountBefore) {
|
if (e->song.sampleLen!=sampleCountBefore) {
|
||||||
e->renderSamplesP();
|
e->renderSamplesP();
|
||||||
|
@ -3854,10 +3855,24 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
nextWindow=GUI_WINDOW_WAVE_LIST;
|
nextWindow=GUI_WINDOW_WAVE_LIST;
|
||||||
MARK_MODIFIED;
|
MARK_MODIFIED;
|
||||||
} else if ((droppedSample=e->sampleFromFile(ev.drop.file))!=NULL) {
|
}
|
||||||
|
else if (!samples.empty())
|
||||||
|
{
|
||||||
|
if (e->song.sampleLen!=sampleCountBefore) {
|
||||||
|
//e->renderSamplesP();
|
||||||
|
}
|
||||||
|
if (!e->getWarnings().empty())
|
||||||
|
{
|
||||||
|
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
|
||||||
|
}
|
||||||
int sampleCount=-1;
|
int sampleCount=-1;
|
||||||
sampleCount=e->addSamplePtr(droppedSample);
|
for (DivSample* s: samples)
|
||||||
if (sampleCount>=0 && settings.selectAssetOnLoad) {
|
{
|
||||||
|
sampleCount=e->addSamplePtr(s);
|
||||||
|
}
|
||||||
|
//sampleCount=e->addSamplePtr(droppedSample);
|
||||||
|
if (sampleCount>=0 && settings.selectAssetOnLoad)
|
||||||
|
{
|
||||||
curSample=sampleCount;
|
curSample=sampleCount;
|
||||||
updateSampleTex=true;
|
updateSampleTex=true;
|
||||||
}
|
}
|
||||||
|
@ -5319,24 +5334,43 @@ bool FurnaceGUI::loop() {
|
||||||
String errs=_("there were some errors while loading samples:\n");
|
String errs=_("there were some errors while loading samples:\n");
|
||||||
bool warn=false;
|
bool warn=false;
|
||||||
for (String i: fileDialog->getFileName()) {
|
for (String i: fileDialog->getFileName()) {
|
||||||
DivSample* s=e->sampleFromFile(i.c_str());
|
std::vector<DivSample*> samples=e->sampleFromFile(i.c_str());
|
||||||
if (s==NULL) {
|
if (samples.empty()) {
|
||||||
if (fileDialog->getFileName().size()>1) {
|
if (fileDialog->getFileName().size()>1) {
|
||||||
warn=true;
|
warn=true;
|
||||||
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
||||||
} else {;
|
} else {;
|
||||||
showError(e->getLastError());
|
showError(e->getLastError());
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if (e->addSamplePtr(s)==-1) {
|
else
|
||||||
if (fileDialog->getFileName().size()>1) {
|
{
|
||||||
warn=true;
|
if((int)samples.size() == 1)
|
||||||
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
{
|
||||||
} else {
|
if (e->addSamplePtr(samples[0]) == -1)
|
||||||
showError(e->getLastError());
|
{
|
||||||
|
if (fileDialog->getFileName().size()>1)
|
||||||
|
{
|
||||||
|
warn=true;
|
||||||
|
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showError(e->getLastError());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
else
|
||||||
MARK_MODIFIED;
|
{
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (DivSample* s: samples) { //ask which samples to load!
|
||||||
|
pendingSamples.push_back(std::make_pair(s,false));
|
||||||
|
}
|
||||||
|
displayPendingSamples=true;
|
||||||
|
replacePendingSample = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5345,24 +5379,44 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GUI_FILE_SAMPLE_OPEN_REPLACE: {
|
case GUI_FILE_SAMPLE_OPEN_REPLACE:
|
||||||
DivSample* s=e->sampleFromFile(copyOfName.c_str());
|
{
|
||||||
if (s==NULL) {
|
std::vector<DivSample*> samples=e->sampleFromFile(copyOfName.c_str());
|
||||||
|
if (samples.empty())
|
||||||
|
{
|
||||||
showError(e->getLastError());
|
showError(e->getLastError());
|
||||||
} else {
|
}
|
||||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
|
else
|
||||||
e->lockEngine([this,s]() {
|
{
|
||||||
// if it crashes here please tell me...
|
if((int)samples.size() == 1)
|
||||||
DivSample* oldSample=e->song.sample[curSample];
|
{
|
||||||
e->song.sample[curSample]=s;
|
if (curSample>=0 && curSample<(int)e->song.sample.size())
|
||||||
delete oldSample;
|
{
|
||||||
e->renderSamples();
|
DivSample* s = samples[0];
|
||||||
MARK_MODIFIED;
|
e->lockEngine([this, s]()
|
||||||
});
|
{
|
||||||
updateSampleTex=true;
|
// if it crashes here please tell me...
|
||||||
} else {
|
DivSample* oldSample=e->song.sample[curSample];
|
||||||
showError(_("...but you haven't selected a sample!"));
|
e->song.sample[curSample]= s;
|
||||||
delete s;
|
delete oldSample;
|
||||||
|
e->renderSamples();
|
||||||
|
MARK_MODIFIED;
|
||||||
|
});
|
||||||
|
updateSampleTex=true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showError(_("...but you haven't selected a sample!"));
|
||||||
|
delete samples[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (DivSample* s: samples) { //ask which samples to load!
|
||||||
|
pendingSamples.push_back(std::make_pair(s,false));
|
||||||
|
}
|
||||||
|
displayPendingSamples=true;
|
||||||
|
replacePendingSample = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -5741,6 +5795,11 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::OpenPopup(_("Select Instrument"));
|
ImGui::OpenPopup(_("Select Instrument"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (displayPendingSamples) {
|
||||||
|
displayPendingSamples=false;
|
||||||
|
ImGui::OpenPopup(_("Select Sample"));
|
||||||
|
}
|
||||||
|
|
||||||
if (displayPendingRawSample) {
|
if (displayPendingRawSample) {
|
||||||
displayPendingRawSample=false;
|
displayPendingRawSample=false;
|
||||||
ImGui::OpenPopup(_("Import Raw Sample"));
|
ImGui::OpenPopup(_("Import Raw Sample"));
|
||||||
|
@ -6569,6 +6628,191 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: fix style
|
||||||
|
centerNextWindow(_("Select Sample"),canvasW,canvasH);
|
||||||
|
if (ImGui::BeginPopupModal(_("Select Sample"),NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
bool quitPlease=false;
|
||||||
|
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::Text(_("this is a sample bank! select which ones to load:"));
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(_("All"))) {
|
||||||
|
for (std::pair<DivSample*,bool>& i: pendingSamples) {
|
||||||
|
i.second=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(_("None"))) {
|
||||||
|
for (std::pair<DivSample*,bool>& i: pendingSamples) {
|
||||||
|
i.second=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool reissueSearch=false;
|
||||||
|
|
||||||
|
bool anySelected=false;
|
||||||
|
float sizeY=ImGui::GetFrameHeightWithSpacing()*pendingSamples.size();
|
||||||
|
if (sizeY>(canvasH-180.0*dpiScale))
|
||||||
|
{
|
||||||
|
sizeY=canvasH-180.0*dpiScale;
|
||||||
|
if (sizeY<60.0*dpiScale) sizeY=60.0*dpiScale;
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTable("PendingSamplesList",1,ImGuiTableFlags_ScrollY,ImVec2(0.0f,sizeY)))
|
||||||
|
{
|
||||||
|
if (sampleBankSearchQuery.empty())
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<pendingSamples.size(); i++)
|
||||||
|
{
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
String id=fmt::sprintf("%d: %s",(int)i,pendingSamples[i].first->name);
|
||||||
|
if (pendingInsSingle)
|
||||||
|
{
|
||||||
|
if (ImGui::Selectable(id.c_str()))
|
||||||
|
{
|
||||||
|
pendingSamples[i].second=true;
|
||||||
|
quitPlease=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO:fixstyle from hereonwards
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if(ImGui::Checkbox(id.c_str(),&pendingSamples[i].second) && io.KeyShift)
|
||||||
|
{
|
||||||
|
for(int jj = (int)i - 1; jj >= 0; jj--)
|
||||||
|
{
|
||||||
|
if(pendingSamples[jj].second) //pressed shift and there's selected item above
|
||||||
|
{
|
||||||
|
for(int k = jj; k < (int)i; k++)
|
||||||
|
{
|
||||||
|
pendingSamples[k].second = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pendingSamples[i].second) anySelected=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //display search results
|
||||||
|
{
|
||||||
|
if(reissueSearch)
|
||||||
|
{
|
||||||
|
String lowerCase=sampleBankSearchQuery;
|
||||||
|
|
||||||
|
for (char& ii: lowerCase)
|
||||||
|
{
|
||||||
|
if (ii>='A' && ii<='Z') ii+='a'-'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleBankSearchResults.clear();
|
||||||
|
for (int j=0; j < (int)pendingSamples.size(); j++)
|
||||||
|
{
|
||||||
|
String lowerCase1 = pendingSamples[j].first->name;
|
||||||
|
|
||||||
|
for (char& ii: lowerCase1)
|
||||||
|
{
|
||||||
|
if (ii>='A' && ii<='Z') ii+='a'-'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lowerCase1.find(lowerCase)!=String::npos)
|
||||||
|
{
|
||||||
|
sampleBankSearchResults.push_back(std::make_pair(pendingSamples[j].first, pendingSamples[j].second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<sampleBankSearchResults.size(); i++)
|
||||||
|
{
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
String id=fmt::sprintf("%d: %s",(int)i,sampleBankSearchResults[i].first->name);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if(ImGui::Checkbox(id.c_str(),&sampleBankSearchResults[i].second) && io.KeyShift)
|
||||||
|
{
|
||||||
|
for(int jj = (int)i - 1; jj >= 0; jj--)
|
||||||
|
{
|
||||||
|
if(sampleBankSearchResults[jj].second) //pressed shift and there's selected item above
|
||||||
|
{
|
||||||
|
for(int k = jj; k < (int)i; k++)
|
||||||
|
{
|
||||||
|
sampleBankSearchResults[k].second = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sampleBankSearchResults[i].second) anySelected=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<pendingSamples.size(); i++)
|
||||||
|
{
|
||||||
|
if(sampleBankSearchResults.size() > 0)
|
||||||
|
{
|
||||||
|
for (size_t j=0; j<sampleBankSearchResults.size(); j++)
|
||||||
|
{
|
||||||
|
if(sampleBankSearchResults[j].first == pendingSamples[i].first && sampleBankSearchResults[j].second && pendingSamples[i].first != NULL)
|
||||||
|
{
|
||||||
|
pendingSamples[i].second = true;
|
||||||
|
if (pendingSamples[i].second) anySelected=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(!anySelected);
|
||||||
|
if (ImGui::Button(_("OK"))) {
|
||||||
|
quitPlease=true;
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button(_("Cancel")) || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||||
|
for (std::pair<DivSample*,bool>& i: pendingSamples) {
|
||||||
|
i.second=false;
|
||||||
|
}
|
||||||
|
quitPlease=true;
|
||||||
|
}
|
||||||
|
if (quitPlease)
|
||||||
|
{
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
int counter = 0;
|
||||||
|
for (std::pair<DivSample*,bool>& i: pendingSamples)
|
||||||
|
{
|
||||||
|
if (!i.second)
|
||||||
|
{
|
||||||
|
delete i.first;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(counter == 0 && replacePendingSample)
|
||||||
|
{
|
||||||
|
*e->song.sample[curSample]=*i.first;
|
||||||
|
replacePendingSample = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e->addSamplePtr(i.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
curSample = (int)e->song.sample.size() - 1;
|
||||||
|
pendingSamples.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
centerNextWindow(_("Import Raw Sample"),canvasW,canvasH);
|
centerNextWindow(_("Import Raw Sample"),canvasW,canvasH);
|
||||||
if (ImGui::BeginPopupModal(_("Import Raw Sample"),NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal(_("Import Raw Sample"),NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
ImGui::Text(_("Data type:"));
|
ImGui::Text(_("Data type:"));
|
||||||
|
@ -7581,7 +7825,15 @@ bool FurnaceGUI::init() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
compatFormats+="*.dmc ";
|
compatFormats+="*.dmc ";
|
||||||
compatFormats+="*.brr";
|
compatFormats+="*.brr ";
|
||||||
|
|
||||||
|
compatFormats+="*.ppc ";
|
||||||
|
compatFormats+="*.pps ";
|
||||||
|
compatFormats+="*.pvi ";
|
||||||
|
compatFormats+="*.pdx ";
|
||||||
|
compatFormats+="*.pzi ";
|
||||||
|
compatFormats+="*.p86 ";
|
||||||
|
compatFormats+="*.p";
|
||||||
audioLoadFormats[1]=compatFormats;
|
audioLoadFormats[1]=compatFormats;
|
||||||
|
|
||||||
audioLoadFormats.push_back(_("NES DPCM data"));
|
audioLoadFormats.push_back(_("NES DPCM data"));
|
||||||
|
@ -7590,6 +7842,27 @@ bool FurnaceGUI::init() {
|
||||||
audioLoadFormats.push_back(_("SNES Bit Rate Reduction"));
|
audioLoadFormats.push_back(_("SNES Bit Rate Reduction"));
|
||||||
audioLoadFormats.push_back("*.brr");
|
audioLoadFormats.push_back("*.brr");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("PMD YM2608 ADPCM-B sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.ppc");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("PDR 4-bit AY-3-8910 sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.pps");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("FMP YM2608 ADPCM-B sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.pvi");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("MDX OKI ADPCM sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.pdx");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("FMP 8-bit PCM sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.pzi");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("PMD 8-bit PCM sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.p86");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("PMD OKI ADPCM sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.p");
|
||||||
|
|
||||||
audioLoadFormats.push_back(_("all files"));
|
audioLoadFormats.push_back(_("all files"));
|
||||||
audioLoadFormats.push_back("*");
|
audioLoadFormats.push_back("*");
|
||||||
|
|
||||||
|
@ -8012,6 +8285,8 @@ FurnaceGUI::FurnaceGUI():
|
||||||
snesFilterHex(false),
|
snesFilterHex(false),
|
||||||
modTableHex(false),
|
modTableHex(false),
|
||||||
displayEditString(false),
|
displayEditString(false),
|
||||||
|
displayPendingSamples(false),
|
||||||
|
replacePendingSample(false),
|
||||||
displayExportingROM(false),
|
displayExportingROM(false),
|
||||||
changeCoarse(false),
|
changeCoarse(false),
|
||||||
mobileEdit(false),
|
mobileEdit(false),
|
||||||
|
|
|
@ -1592,7 +1592,7 @@ class FurnaceGUI {
|
||||||
int sampleTexW, sampleTexH;
|
int sampleTexW, sampleTexH;
|
||||||
bool updateSampleTex;
|
bool updateSampleTex;
|
||||||
|
|
||||||
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery;
|
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery, sampleBankSearchQuery;
|
||||||
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
|
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
|
||||||
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport;
|
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport;
|
||||||
String workingDirFont, workingDirColors, workingDirKeybinds;
|
String workingDirFont, workingDirColors, workingDirKeybinds;
|
||||||
|
@ -1604,6 +1604,7 @@ class FurnaceGUI {
|
||||||
String folderString;
|
String folderString;
|
||||||
|
|
||||||
std::vector<DivSystem> sysSearchResults;
|
std::vector<DivSystem> sysSearchResults;
|
||||||
|
std::vector<std::pair<DivSample*,bool>> sampleBankSearchResults;
|
||||||
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
||||||
std::vector<int> paletteSearchResults;
|
std::vector<int> paletteSearchResults;
|
||||||
FixedQueue<String,32> recentFile;
|
FixedQueue<String,32> recentFile;
|
||||||
|
@ -1619,6 +1620,7 @@ class FurnaceGUI {
|
||||||
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange;
|
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange;
|
||||||
bool wantScrollListIns, wantScrollListWave, wantScrollListSample;
|
bool wantScrollListIns, wantScrollListWave, wantScrollListSample;
|
||||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
||||||
|
bool displayPendingSamples, replacePendingSample;
|
||||||
bool displayExportingROM;
|
bool displayExportingROM;
|
||||||
bool changeCoarse;
|
bool changeCoarse;
|
||||||
bool mobileEdit;
|
bool mobileEdit;
|
||||||
|
@ -2215,7 +2217,7 @@ class FurnaceGUI {
|
||||||
maxUndoSteps(100),
|
maxUndoSteps(100),
|
||||||
vibrationStrength(0.5f),
|
vibrationStrength(0.5f),
|
||||||
vibrationLength(20),
|
vibrationLength(20),
|
||||||
s3mOPL3(0),
|
s3mOPL3(1),
|
||||||
mainFontPath(""),
|
mainFontPath(""),
|
||||||
headFontPath(""),
|
headFontPath(""),
|
||||||
patFontPath(""),
|
patFontPath(""),
|
||||||
|
@ -2372,6 +2374,7 @@ class FurnaceGUI {
|
||||||
std::vector<DivCommand> cmdStream;
|
std::vector<DivCommand> cmdStream;
|
||||||
std::vector<Particle> particles;
|
std::vector<Particle> particles;
|
||||||
std::vector<std::pair<DivInstrument*,bool>> pendingIns;
|
std::vector<std::pair<DivInstrument*,bool>> pendingIns;
|
||||||
|
std::vector<std::pair<DivSample*,bool>> pendingSamples;
|
||||||
|
|
||||||
std::vector<FurnaceGUISysCategory> sysCategories;
|
std::vector<FurnaceGUISysCategory> sysCategories;
|
||||||
|
|
||||||
|
|
|
@ -4754,7 +4754,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
settings.vibrationStrength=conf.getFloat("vibrationStrength",0.5f);
|
settings.vibrationStrength=conf.getFloat("vibrationStrength",0.5f);
|
||||||
settings.vibrationLength=conf.getInt("vibrationLength",20);
|
settings.vibrationLength=conf.getInt("vibrationLength",20);
|
||||||
|
|
||||||
settings.s3mOPL3=conf.getInt("s3mOPL3",0);
|
settings.s3mOPL3=conf.getInt("s3mOPL3",1);
|
||||||
|
|
||||||
settings.backupEnable=conf.getInt("backupEnable",1);
|
settings.backupEnable=conf.getInt("backupEnable",1);
|
||||||
settings.backupInterval=conf.getInt("backupInterval",30);
|
settings.backupInterval=conf.getInt("backupInterval",30);
|
||||||
|
|
Loading…
Reference in a new issue