mirror of https://github.com/OpenTTD/OpenTTD
Compare commits
7 Commits
ae24cd2077
...
ecccf4160d
Author | SHA1 | Date |
---|---|---|
|
ecccf4160d | |
|
b82ffa3542 | |
|
8e2df7809b | |
|
821784004d | |
|
f0447d59d4 | |
|
cbdd358ae8 | |
|
7edc18c977 |
|
@ -144,6 +144,7 @@ if(NOT OPTION_DEDICATED)
|
||||||
find_package(Freetype)
|
find_package(Freetype)
|
||||||
find_package(SDL2)
|
find_package(SDL2)
|
||||||
find_package(Fluidsynth)
|
find_package(Fluidsynth)
|
||||||
|
find_package(ALSA)
|
||||||
if(Freetype_FOUND)
|
if(Freetype_FOUND)
|
||||||
find_package(Fontconfig)
|
find_package(Fontconfig)
|
||||||
endif()
|
endif()
|
||||||
|
@ -326,6 +327,7 @@ if(NOT OPTION_DEDICATED)
|
||||||
link_package(ICU_i18n)
|
link_package(ICU_i18n)
|
||||||
link_package(ICU_uc)
|
link_package(ICU_uc)
|
||||||
link_package(OpusFile TARGET OpusFile::opusfile)
|
link_package(OpusFile TARGET OpusFile::opusfile)
|
||||||
|
link_package(ALSA)
|
||||||
|
|
||||||
if(SDL2_FOUND AND OPENGL_FOUND AND UNIX)
|
if(SDL2_FOUND AND OPENGL_FOUND AND UNIX)
|
||||||
# SDL2 dynamically loads OpenGL if needed, so do not link to OpenGL when
|
# SDL2 dynamically loads OpenGL if needed, so do not link to OpenGL when
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
#[=======================================================================[.rst:
|
||||||
|
FindALSA
|
||||||
|
--------
|
||||||
|
|
||||||
|
Find Advanced Linux Sound Architecture (ALSA)
|
||||||
|
|
||||||
|
Find the alsa libraries (``asound``)
|
||||||
|
|
||||||
|
IMPORTED Targets
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. versionadded:: 3.12
|
||||||
|
|
||||||
|
This module defines :prop_tgt:`IMPORTED` target ``ALSA::ALSA``, if
|
||||||
|
ALSA has been found.
|
||||||
|
|
||||||
|
Result Variables
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This module defines the following variables:
|
||||||
|
|
||||||
|
``ALSA_FOUND``
|
||||||
|
True if ALSA_INCLUDE_DIR & ALSA_LIBRARY are found
|
||||||
|
|
||||||
|
``ALSA_LIBRARIES``
|
||||||
|
List of libraries when using ALSA.
|
||||||
|
|
||||||
|
``ALSA_INCLUDE_DIRS``
|
||||||
|
Where to find the ALSA headers.
|
||||||
|
|
||||||
|
Cache variables
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The following cache variables may also be set:
|
||||||
|
|
||||||
|
``ALSA_INCLUDE_DIR``
|
||||||
|
the ALSA include directory
|
||||||
|
|
||||||
|
``ALSA_LIBRARY``
|
||||||
|
the absolute path of the asound library
|
||||||
|
#]=======================================================================]
|
||||||
|
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
pkg_check_modules(PC_ALSA QUIET alsa)
|
||||||
|
|
||||||
|
find_path(ALSA_INCLUDE_DIR NAMES alsa/asoundlib.h
|
||||||
|
DOC "The ALSA (asound) include directory"
|
||||||
|
)
|
||||||
|
|
||||||
|
find_library(ALSA_LIBRARY NAMES asound
|
||||||
|
DOC "The ALSA (asound) library"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(ALSA_INCLUDE_DIR AND EXISTS "${ALSA_INCLUDE_DIR}/alsa/version.h")
|
||||||
|
file(STRINGS "${ALSA_INCLUDE_DIR}/alsa/version.h" alsa_version_str REGEX "^#define[\t ]+SND_LIB_VERSION_STR[\t ]+\".*\"")
|
||||||
|
|
||||||
|
string(REGEX REPLACE "^.*SND_LIB_VERSION_STR[\t ]+\"([^\"]*)\".*$" "\\1" ALSA_VERSION_STRING "${alsa_version_str}")
|
||||||
|
unset(alsa_version_str)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ALSA
|
||||||
|
REQUIRED_VARS ALSA_LIBRARY ALSA_INCLUDE_DIR
|
||||||
|
VERSION_VAR ALSA_VERSION_STRING)
|
||||||
|
|
||||||
|
if(ALSA_FOUND)
|
||||||
|
set( ALSA_LIBRARIES ${ALSA_LIBRARY} )
|
||||||
|
set( ALSA_INCLUDE_DIRS ${ALSA_INCLUDE_DIR} )
|
||||||
|
if(NOT TARGET ALSA::ALSA)
|
||||||
|
add_library(ALSA::ALSA UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(ALSA::ALSA PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ALSA_INCLUDE_DIRS}")
|
||||||
|
set_property(TARGET ALSA::ALSA APPEND PROPERTY IMPORTED_LOCATION "${ALSA_LIBRARY}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY)
|
|
@ -7,3 +7,4 @@ add_subdirectory(squirrel)
|
||||||
add_subdirectory(nlohmann)
|
add_subdirectory(nlohmann)
|
||||||
add_subdirectory(opengl)
|
add_subdirectory(opengl)
|
||||||
add_subdirectory(openttd_social_integration_api)
|
add_subdirectory(openttd_social_integration_api)
|
||||||
|
add_subdirectory(midifile)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,161 @@
|
||||||
|
//
|
||||||
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
||||||
|
// Creation Date: Mon Feb 16 12:26:32 PST 2015 Adapted from binasc program.
|
||||||
|
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
|
||||||
|
// Filename: midifile/include/Binasc.h
|
||||||
|
// Website: http://midifile.sapp.org
|
||||||
|
// Syntax: C++11
|
||||||
|
// vim: ts=3 noexpandtab
|
||||||
|
//
|
||||||
|
// description: Interface to convert bytes between binary and ASCII forms.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _BINASC_H_INCLUDED
|
||||||
|
#define _BINASC_H_INCLUDED
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
namespace smf {
|
||||||
|
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
typedef unsigned short ushort;
|
||||||
|
typedef unsigned long ulong;
|
||||||
|
|
||||||
|
class Binasc {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Binasc (void);
|
||||||
|
|
||||||
|
~Binasc ();
|
||||||
|
|
||||||
|
// functions for setting options:
|
||||||
|
int setLineLength (int length);
|
||||||
|
int getLineLength (void);
|
||||||
|
int setLineBytes (int length);
|
||||||
|
int getLineBytes (void);
|
||||||
|
void setComments (int state);
|
||||||
|
void setCommentsOn (void);
|
||||||
|
void setCommentsOff (void);
|
||||||
|
int getComments (void);
|
||||||
|
void setBytes (int state);
|
||||||
|
void setBytesOn (void);
|
||||||
|
void setBytesOff (void);
|
||||||
|
int getBytes (void);
|
||||||
|
void setMidi (int state);
|
||||||
|
void setMidiOn (void);
|
||||||
|
void setMidiOff (void);
|
||||||
|
int getMidi (void);
|
||||||
|
|
||||||
|
// functions for converting into a binary file:
|
||||||
|
int writeToBinary (const std::string& outfile,
|
||||||
|
const std::string& infile);
|
||||||
|
int writeToBinary (const std::string& outfile,
|
||||||
|
std::istream& input);
|
||||||
|
int writeToBinary (std::ostream& out,
|
||||||
|
const std::string& infile);
|
||||||
|
int writeToBinary (std::ostream& out,
|
||||||
|
std::istream& input);
|
||||||
|
|
||||||
|
// functions for converting into an ASCII file with hex bytes:
|
||||||
|
int readFromBinary (const std::string&
|
||||||
|
outfile,
|
||||||
|
const std::string& infile);
|
||||||
|
int readFromBinary (const std::string& outfile,
|
||||||
|
std::istream& input);
|
||||||
|
int readFromBinary (std::ostream& out,
|
||||||
|
const std::string& infile);
|
||||||
|
int readFromBinary (std::ostream& out,
|
||||||
|
std::istream& input);
|
||||||
|
|
||||||
|
// static functions for writing ordered bytes:
|
||||||
|
static std::ostream& writeLittleEndianUShort (std::ostream& out,
|
||||||
|
ushort value);
|
||||||
|
static std::ostream& writeBigEndianUShort (std::ostream& out,
|
||||||
|
ushort value);
|
||||||
|
static std::ostream& writeLittleEndianShort (std::ostream& out,
|
||||||
|
short value);
|
||||||
|
static std::ostream& writeBigEndianShort (std::ostream& out,
|
||||||
|
short value);
|
||||||
|
static std::ostream& writeLittleEndianULong (std::ostream& out,
|
||||||
|
ulong value);
|
||||||
|
static std::ostream& writeBigEndianULong (std::ostream& out,
|
||||||
|
ulong value);
|
||||||
|
static std::ostream& writeLittleEndianLong (std::ostream& out,
|
||||||
|
long value);
|
||||||
|
static std::ostream& writeBigEndianLong (std::ostream& out,
|
||||||
|
long value);
|
||||||
|
static std::ostream& writeLittleEndianFloat (std::ostream& out,
|
||||||
|
float value);
|
||||||
|
static std::ostream& writeBigEndianFloat (std::ostream& out,
|
||||||
|
float value);
|
||||||
|
static std::ostream& writeLittleEndianDouble (std::ostream& out,
|
||||||
|
double value);
|
||||||
|
static std::ostream& writeBigEndianDouble (std::ostream& out,
|
||||||
|
double value);
|
||||||
|
|
||||||
|
static std::string keyToPitchName (int key);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int m_bytesQ; // option for printing hex bytes in ASCII output.
|
||||||
|
int m_commentsQ; // option for printing comments in ASCII output.
|
||||||
|
int m_midiQ; // output ASCII data as parsed MIDI file.
|
||||||
|
int m_maxLineLength;// number of character in ASCII output on a line.
|
||||||
|
int m_maxLineBytes; // number of hex bytes in ASCII output on a line.
|
||||||
|
|
||||||
|
private:
|
||||||
|
// helper functions for reading ASCII content to conver to binary:
|
||||||
|
int processLine (std::ostream& out,
|
||||||
|
const std::string& input,
|
||||||
|
int lineNum);
|
||||||
|
int processAsciiWord (std::ostream& out,
|
||||||
|
const std::string& input,
|
||||||
|
int lineNum);
|
||||||
|
int processStringWord (std::ostream& out,
|
||||||
|
const std::string& input,
|
||||||
|
int lineNum);
|
||||||
|
int processBinaryWord (std::ostream& out,
|
||||||
|
const std::string& input,
|
||||||
|
int lineNum);
|
||||||
|
int processDecimalWord (std::ostream& out,
|
||||||
|
const std::string& input,
|
||||||
|
int lineNum);
|
||||||
|
int processHexWord (std::ostream& out,
|
||||||
|
const std::string& input,
|
||||||
|
int lineNum);
|
||||||
|
int processVlvWord (std::ostream& out,
|
||||||
|
const std::string& input,
|
||||||
|
int lineNum);
|
||||||
|
int processMidiPitchBendWord(std::ostream& out,
|
||||||
|
const std::string& input,
|
||||||
|
int lineNum);
|
||||||
|
int processMidiTempoWord (std::ostream& out,
|
||||||
|
const std::string& input,
|
||||||
|
int lineNum);
|
||||||
|
|
||||||
|
// helper functions for reading binary content to convert to ASCII:
|
||||||
|
int outputStyleAscii (std::ostream& out, std::istream& input);
|
||||||
|
int outputStyleBinary (std::ostream& out, std::istream& input);
|
||||||
|
int outputStyleBoth (std::ostream& out, std::istream& input);
|
||||||
|
int outputStyleMidi (std::ostream& out, std::istream& input);
|
||||||
|
|
||||||
|
// MIDI parsing helper functions:
|
||||||
|
int readMidiEvent (std::ostream& out, std::istream& infile,
|
||||||
|
int& trackbytes, int& command);
|
||||||
|
int getVLV (std::istream& infile, int& trackbytes);
|
||||||
|
int getWord (std::string& word, const std::string& input,
|
||||||
|
const std::string& terminators, int index);
|
||||||
|
|
||||||
|
static const char *GMinstrument[128];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end of namespace smf
|
||||||
|
|
||||||
|
#endif /* _BINASC_H_INCLUDED */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
add_files(
|
||||||
|
Binasc.cpp
|
||||||
|
Binasc.h
|
||||||
|
MidiEvent.cpp
|
||||||
|
MidiEvent.h
|
||||||
|
MidiEventList.cpp
|
||||||
|
MidiEventList.h
|
||||||
|
MidiFile.cpp
|
||||||
|
MidiFile.h
|
||||||
|
MidiMessage.cpp
|
||||||
|
MidiMessage.h
|
||||||
|
)
|
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright (c) 1999-2018, Craig Stuart Sapp
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
and the following disclaimer in the documentation and/or other materials
|
||||||
|
provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,298 @@
|
||||||
|
//
|
||||||
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
||||||
|
// Creation Date: Sat Feb 14 21:40:14 PST 2015
|
||||||
|
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
|
||||||
|
// Filename: midifile/src/MidiEvent.cpp
|
||||||
|
// Website: http://midifile.sapp.org
|
||||||
|
// Syntax: C++11
|
||||||
|
// vim: ts=3 noexpandtab
|
||||||
|
//
|
||||||
|
// Description: A class which stores a MidiMessage and a timestamp
|
||||||
|
// for the MidiFile class.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "MidiEvent.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
|
||||||
|
namespace smf {
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::MidiEvent -- Constructor classes
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEvent::MidiEvent(void) : MidiMessage() {
|
||||||
|
clearVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent::MidiEvent(int command) : MidiMessage(command) {
|
||||||
|
clearVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent::MidiEvent(int command, int p1) : MidiMessage(command, p1) {
|
||||||
|
clearVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent::MidiEvent(int command, int p1, int p2)
|
||||||
|
: MidiMessage(command, p1, p2) {
|
||||||
|
clearVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent::MidiEvent(int aTime, int aTrack, vector<uchar>& message)
|
||||||
|
: MidiMessage(message) {
|
||||||
|
track = aTrack;
|
||||||
|
tick = aTime;
|
||||||
|
seconds = 0.0;
|
||||||
|
seq = 0;
|
||||||
|
m_eventlink = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent::MidiEvent(const MidiEvent& mfevent) : MidiMessage() {
|
||||||
|
track = mfevent.track;
|
||||||
|
tick = mfevent.tick;
|
||||||
|
seconds = mfevent.seconds;
|
||||||
|
seq = mfevent.seq;
|
||||||
|
m_eventlink = NULL;
|
||||||
|
|
||||||
|
this->resize(mfevent.size());
|
||||||
|
for (int i=0; i<(int)this->size(); i++) {
|
||||||
|
(*this)[i] = mfevent[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::~MidiEvent -- MidiFile Event destructor
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEvent::~MidiEvent() {
|
||||||
|
track = -1;
|
||||||
|
tick = -1;
|
||||||
|
seconds = -1.0;
|
||||||
|
seq = -1;
|
||||||
|
this->resize(0);
|
||||||
|
m_eventlink = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::clearVariables -- Clear everything except MidiMessage data.
|
||||||
|
//
|
||||||
|
|
||||||
|
void MidiEvent::clearVariables(void) {
|
||||||
|
track = 0;
|
||||||
|
tick = 0;
|
||||||
|
seconds = 0.0;
|
||||||
|
seq = 0;
|
||||||
|
m_eventlink = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::operator= -- Copy the contents of another MidiEvent.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEvent& MidiEvent::operator=(const MidiEvent& mfevent) {
|
||||||
|
if (this == &mfevent) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
tick = mfevent.tick;
|
||||||
|
track = mfevent.track;
|
||||||
|
seconds = mfevent.seconds;
|
||||||
|
seq = mfevent.seq;
|
||||||
|
m_eventlink = NULL;
|
||||||
|
this->resize(mfevent.size());
|
||||||
|
for (int i=0; i<(int)this->size(); i++) {
|
||||||
|
(*this)[i] = mfevent[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent& MidiEvent::operator=(const MidiMessage& message) {
|
||||||
|
if (this == &message) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
clearVariables();
|
||||||
|
this->resize(message.size());
|
||||||
|
for (int i=0; i<(int)this->size(); i++) {
|
||||||
|
(*this)[i] = message[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent& MidiEvent::operator=(const vector<uchar>& bytes) {
|
||||||
|
clearVariables();
|
||||||
|
this->resize(bytes.size());
|
||||||
|
for (int i=0; i<(int)this->size(); i++) {
|
||||||
|
(*this)[i] = bytes[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent& MidiEvent::operator=(const vector<char>& bytes) {
|
||||||
|
clearVariables();
|
||||||
|
setMessage(bytes);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent& MidiEvent::operator=(const vector<int>& bytes) {
|
||||||
|
clearVariables();
|
||||||
|
setMessage(bytes);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::unlinkEvent -- Disassociate this event with another.
|
||||||
|
// Also tell the other event to disassociate from this event.
|
||||||
|
//
|
||||||
|
|
||||||
|
void MidiEvent::unlinkEvent(void) {
|
||||||
|
if (m_eventlink == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MidiEvent* mev = m_eventlink;
|
||||||
|
m_eventlink = NULL;
|
||||||
|
mev->unlinkEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::linkEvent -- Make a link between two messages.
|
||||||
|
// Unlinking
|
||||||
|
//
|
||||||
|
|
||||||
|
void MidiEvent::linkEvent(MidiEvent* mev) {
|
||||||
|
if (mev->m_eventlink != NULL) {
|
||||||
|
// unlink other event if it is linked to something else;
|
||||||
|
mev->unlinkEvent();
|
||||||
|
}
|
||||||
|
// if this is already linked to something else, then unlink:
|
||||||
|
if (m_eventlink != NULL) {
|
||||||
|
m_eventlink->unlinkEvent();
|
||||||
|
}
|
||||||
|
unlinkEvent();
|
||||||
|
|
||||||
|
mev->m_eventlink = this;
|
||||||
|
m_eventlink = mev;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MidiEvent::linkEvent(MidiEvent& mev) {
|
||||||
|
linkEvent(&mev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::getLinkedEvent -- Returns a linked event. Usually
|
||||||
|
// this is the note-off message for a note-on message and vice-versa.
|
||||||
|
// Returns null if there are no links.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEvent* MidiEvent::getLinkedEvent(void) {
|
||||||
|
return m_eventlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const MidiEvent* MidiEvent::getLinkedEvent(void) const {
|
||||||
|
return m_eventlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::isLinked -- Returns true if there is an event which is not
|
||||||
|
// NULL. This function is similar to getLinkedEvent().
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEvent::isLinked(void) const {
|
||||||
|
return m_eventlink == NULL ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::getTickDuration -- For linked events (note-ons and note-offs),
|
||||||
|
// return the absolute tick time difference between the two events.
|
||||||
|
// The tick values are presumed to be in absolute tick mode rather than
|
||||||
|
// delta tick mode. Returns 0 if not linked.
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEvent::getTickDuration(void) const {
|
||||||
|
const MidiEvent* mev = getLinkedEvent();
|
||||||
|
if (mev == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int tick2 = mev->tick;
|
||||||
|
if (tick2 > tick) {
|
||||||
|
return tick2 - tick;
|
||||||
|
} else {
|
||||||
|
return tick - tick2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEvent::getDurationInSeconds -- For linked events (note-ons and
|
||||||
|
// note-offs), return the duration of the note in seconds. The
|
||||||
|
// seconds analysis must be done first; otherwise the duration will be
|
||||||
|
// reported as zero.
|
||||||
|
//
|
||||||
|
|
||||||
|
double MidiEvent::getDurationInSeconds(void) const {
|
||||||
|
const MidiEvent* mev = getLinkedEvent();
|
||||||
|
if (mev == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
double seconds2 = mev->seconds;
|
||||||
|
if (seconds2 > seconds) {
|
||||||
|
return seconds2 - seconds;
|
||||||
|
} else {
|
||||||
|
return seconds - seconds2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// operator<<(MidiMessage) -- Print tick value followed by MIDI bytes for event.
|
||||||
|
// The tick value will be either relative or absolute depending on the state
|
||||||
|
// of the MidiFile object containing it.
|
||||||
|
//
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, MidiEvent& event) {
|
||||||
|
out << event.tick << '(' << static_cast<MidiMessage&>(event) << ')';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // end namespace smf
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
||||||
|
// Creation Date: Sat Feb 14 21:47:39 PST 2015
|
||||||
|
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
|
||||||
|
// Filename: midifile/include/MidiEvent.h
|
||||||
|
// Website: http://midifile.sapp.org
|
||||||
|
// Syntax: C++11
|
||||||
|
// vim: ts=3 noexpandtab
|
||||||
|
//
|
||||||
|
// Description: A class which stores a MidiMessage and a timestamp
|
||||||
|
// for the MidiFile class.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _MIDIEVENT_H_INCLUDED
|
||||||
|
#define _MIDIEVENT_H_INCLUDED
|
||||||
|
|
||||||
|
#include "MidiMessage.h"
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace smf {
|
||||||
|
|
||||||
|
class MidiEvent : public MidiMessage {
|
||||||
|
public:
|
||||||
|
MidiEvent (void);
|
||||||
|
MidiEvent (int command);
|
||||||
|
MidiEvent (int command, int param1);
|
||||||
|
MidiEvent (int command, int param1, int param2);
|
||||||
|
MidiEvent (const MidiMessage& message);
|
||||||
|
MidiEvent (const MidiEvent& mfevent);
|
||||||
|
MidiEvent (int aTime, int aTrack,
|
||||||
|
std::vector<uchar>& message);
|
||||||
|
|
||||||
|
~MidiEvent ();
|
||||||
|
|
||||||
|
MidiEvent& operator= (const MidiEvent& mfevent);
|
||||||
|
MidiEvent& operator= (const MidiMessage& message);
|
||||||
|
MidiEvent& operator= (const std::vector<uchar>& bytes);
|
||||||
|
MidiEvent& operator= (const std::vector<char>& bytes);
|
||||||
|
MidiEvent& operator= (const std::vector<int>& bytes);
|
||||||
|
|
||||||
|
void clearVariables (void);
|
||||||
|
|
||||||
|
// functions related to event linking (note-ons to note-offs).
|
||||||
|
void unlinkEvent (void);
|
||||||
|
void unlinkEvents (void);
|
||||||
|
void linkEvent (MidiEvent* mev);
|
||||||
|
void linkEvents (MidiEvent* mev);
|
||||||
|
void linkEvent (MidiEvent& mev);
|
||||||
|
void linkEvents (MidiEvent& mev);
|
||||||
|
int isLinked (void) const;
|
||||||
|
MidiEvent* getLinkedEvent (void);
|
||||||
|
const MidiEvent* getLinkedEvent (void) const;
|
||||||
|
int getTickDuration (void) const;
|
||||||
|
double getDurationInSeconds (void) const;
|
||||||
|
|
||||||
|
int tick; // delta or absolute MIDI ticks
|
||||||
|
int track; // [original] track number of event in MIDI file
|
||||||
|
double seconds; // calculated time in sec. (after doTimeAnalysis())
|
||||||
|
int seq; // sorting sequence number of event
|
||||||
|
|
||||||
|
private:
|
||||||
|
MidiEvent* m_eventlink; // used to match note-ons and note-offs
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, MidiEvent& event);
|
||||||
|
|
||||||
|
|
||||||
|
} // end of namespace smf
|
||||||
|
|
||||||
|
#endif /* _MIDIEVENT_H_INCLUDED */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,623 @@
|
||||||
|
//
|
||||||
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
||||||
|
// Creation Date: Sat Feb 14 21:55:38 PST 2015
|
||||||
|
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
|
||||||
|
// Filename: midifile/src/MidiEventList.cpp
|
||||||
|
// Website: http://midifile.sapp.org
|
||||||
|
// Syntax: C++11
|
||||||
|
// vim: ts=3 noexpandtab
|
||||||
|
//
|
||||||
|
// Description: A class which stores a MidiEvents for a MidiFile track.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "MidiEventList.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace smf {
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::MidiEventList -- Constructor.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEventList::MidiEventList(void) {
|
||||||
|
reserve(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::MidiEventList(MidiEventList&) -- Copy constructor.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEventList::MidiEventList(const MidiEventList& other) {
|
||||||
|
list.reserve(other.list.size());
|
||||||
|
auto it = other.list.begin();
|
||||||
|
std::generate_n(std::back_inserter(list), other.list.size(), [&]() -> MidiEvent* {
|
||||||
|
return new MidiEvent(**it++);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::MidiEventList(MidiEventList&&) -- Move constructor.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEventList::MidiEventList(MidiEventList&& other) {
|
||||||
|
list = std::move(other.list);
|
||||||
|
other.list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::~MidiEventList -- Deconstructor. Deallocate all stored
|
||||||
|
// data.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEventList::~MidiEventList() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::operator[] --
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEvent& MidiEventList::operator[](int index) {
|
||||||
|
return *list[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const MidiEvent& MidiEventList::operator[](int index) const {
|
||||||
|
return *list[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::back -- Return the last element in the list.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEvent& MidiEventList::back(void) {
|
||||||
|
return *list.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const MidiEvent& MidiEventList::back(void) const {
|
||||||
|
return *list.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MidiEventList::last -- Alias for MidiEventList::back().
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEvent& MidiEventList::last(void) {
|
||||||
|
return back();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const MidiEvent& MidiEventList::last(void) const {
|
||||||
|
return back();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::getEvent -- The same thing as operator[], for
|
||||||
|
// internal use when operator[] would look more messy.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEvent& MidiEventList::getEvent(int index) {
|
||||||
|
return *list[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const MidiEvent& MidiEventList::getEvent(int index) const {
|
||||||
|
return *list[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::clear -- De-allocate any MidiEvents present in the list
|
||||||
|
// and set the size of the list to 0.
|
||||||
|
//
|
||||||
|
|
||||||
|
void MidiEventList::clear(void) {
|
||||||
|
for (auto& item : list) {
|
||||||
|
if (item != NULL) {
|
||||||
|
delete item;
|
||||||
|
item = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::data -- Return the low-level array of MidiMessage
|
||||||
|
// pointers. This is useful for applying your own sorting
|
||||||
|
// function to the list.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEvent** MidiEventList::data(void) {
|
||||||
|
return list.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::reserve -- Pre-allocate space in the list for storing
|
||||||
|
// elements.
|
||||||
|
//
|
||||||
|
|
||||||
|
void MidiEventList::reserve(int rsize) {
|
||||||
|
if (rsize > (int)list.size()) {
|
||||||
|
list.reserve(rsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::getSize -- Return the number of MidiEvents stored
|
||||||
|
// in the list.
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEventList::getSize(void) const {
|
||||||
|
return (int)list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MidiEventList::size -- Alias for MidiEventList::getSize().
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEventList::size(void) const {
|
||||||
|
return getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MidiEventList::getEventCount -- Alias for MidiEventList::getSize().
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEventList::getEventCount(void) const {
|
||||||
|
return getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::append -- add a MidiEvent at the end of the list. Returns
|
||||||
|
// the index of the appended event.
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEventList::append(MidiEvent& event) {
|
||||||
|
MidiEvent* ptr = new MidiEvent(event);
|
||||||
|
list.push_back(ptr);
|
||||||
|
return (int)list.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MidiEventList::push -- Alias for MidiEventList::append().
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEventList::push(MidiEvent& event) {
|
||||||
|
return append(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MidiEventList::push_back -- Alias for MidiEventList::append().
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEventList::push_back(MidiEvent& event) {
|
||||||
|
return append(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::removeEmpties -- Remove any MIDI message which contain no
|
||||||
|
// bytes. This function first deallocates any empty MIDI events, and then
|
||||||
|
// removes them from the list of events.
|
||||||
|
//
|
||||||
|
|
||||||
|
void MidiEventList::removeEmpties(void) {
|
||||||
|
int count = 0;
|
||||||
|
for (auto& item : list) {
|
||||||
|
if (item->empty()) {
|
||||||
|
delete item;
|
||||||
|
item = NULL;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::vector<MidiEvent*> newlist;
|
||||||
|
newlist.reserve(list.size() - count);
|
||||||
|
for (auto& item : list) {
|
||||||
|
if (item) {
|
||||||
|
newlist.push_back(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list.swap(newlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::linkNotePairs -- Match note-ones and note-offs together
|
||||||
|
// There are two models that can be done if two notes are overlapping
|
||||||
|
// on the same pitch: the first note-off affects the last note-on,
|
||||||
|
// or the first note-off affects the first note-on. Currently the
|
||||||
|
// first note-off affects the last note-on, but both methods could
|
||||||
|
// be implemented with user selectability. The current state of the
|
||||||
|
// track is assumed to be in time-sorted order. Returns the number
|
||||||
|
// of linked notes (note-on/note-off pairs).
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEventList::linkEventPairs(void) {
|
||||||
|
return linkNotePairs();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MidiEventList::linkNotePairs(void) {
|
||||||
|
|
||||||
|
// Note-on states:
|
||||||
|
// dimension 1: MIDI channel (0-15)
|
||||||
|
// dimension 2: MIDI key (0-127) (but 0 not used for note-ons)
|
||||||
|
// dimension 3: List of active note-ons or note-offs.
|
||||||
|
std::vector<std::vector<std::vector<MidiEvent*>>> noteons;
|
||||||
|
noteons.resize(16);
|
||||||
|
for (auto& noteon : noteons) {
|
||||||
|
noteon.resize(128);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controller linking: The following General MIDI controller numbers are
|
||||||
|
// also monitored for linking within the track (but not between tracks).
|
||||||
|
// hex dec name range
|
||||||
|
// 40 64 Hold pedal (Sustain) on/off 0..63=off 64..127=on
|
||||||
|
// 41 65 Portamento on/off 0..63=off 64..127=on
|
||||||
|
// 42 66 Sustenuto Pedal on/off 0..63=off 64..127=on
|
||||||
|
// 43 67 Soft Pedal on/off 0..63=off 64..127=on
|
||||||
|
// 44 68 Legato Pedal on/off 0..63=off 64..127=on
|
||||||
|
// 45 69 Hold Pedal 2 on/off 0..63=off 64..127=on
|
||||||
|
// 50 80 General Purpose Button 0..63=off 64..127=on
|
||||||
|
// 51 81 General Purpose Button 0..63=off 64..127=on
|
||||||
|
// 52 82 General Purpose Button 0..63=off 64..127=on
|
||||||
|
// 53 83 General Purpose Button 0..63=off 64..127=on
|
||||||
|
// 54 84 Undefined on/off 0..63=off 64..127=on
|
||||||
|
// 55 85 Undefined on/off 0..63=off 64..127=on
|
||||||
|
// 56 86 Undefined on/off 0..63=off 64..127=on
|
||||||
|
// 57 87 Undefined on/off 0..63=off 64..127=on
|
||||||
|
// 58 88 Undefined on/off 0..63=off 64..127=on
|
||||||
|
// 59 89 Undefined on/off 0..63=off 64..127=on
|
||||||
|
// 5A 90 Undefined on/off 0..63=off 64..127=on
|
||||||
|
// 7A 122 Local Keyboard On/Off 0..63=off 64..127=on
|
||||||
|
|
||||||
|
// first keep track of whether the controller is an on/off switch:
|
||||||
|
std::vector<std::pair<int, int>> contmap;
|
||||||
|
contmap.resize(128);
|
||||||
|
std::pair<int, int> zero(0, 0);
|
||||||
|
std::fill(contmap.begin(), contmap.end(), zero);
|
||||||
|
contmap[64].first = 1; contmap[64].second = 0;
|
||||||
|
contmap[65].first = 1; contmap[65].second = 1;
|
||||||
|
contmap[66].first = 1; contmap[66].second = 2;
|
||||||
|
contmap[67].first = 1; contmap[67].second = 3;
|
||||||
|
contmap[68].first = 1; contmap[68].second = 4;
|
||||||
|
contmap[69].first = 1; contmap[69].second = 5;
|
||||||
|
contmap[80].first = 1; contmap[80].second = 6;
|
||||||
|
contmap[81].first = 1; contmap[81].second = 7;
|
||||||
|
contmap[82].first = 1; contmap[82].second = 8;
|
||||||
|
contmap[83].first = 1; contmap[83].second = 9;
|
||||||
|
contmap[84].first = 1; contmap[84].second = 10;
|
||||||
|
contmap[85].first = 1; contmap[85].second = 11;
|
||||||
|
contmap[86].first = 1; contmap[86].second = 12;
|
||||||
|
contmap[87].first = 1; contmap[87].second = 13;
|
||||||
|
contmap[88].first = 1; contmap[88].second = 14;
|
||||||
|
contmap[89].first = 1; contmap[89].second = 15;
|
||||||
|
contmap[90].first = 1; contmap[90].second = 16;
|
||||||
|
contmap[122].first = 1; contmap[122].second = 17;
|
||||||
|
|
||||||
|
// dimensions:
|
||||||
|
// 1: mapped controller (0 to 17)
|
||||||
|
// 2: channel (0 to 15)
|
||||||
|
std::vector<std::vector<MidiEvent*>> contevents;
|
||||||
|
contevents.resize(18);
|
||||||
|
std::vector<std::vector<int>> oldstates;
|
||||||
|
oldstates.resize(18);
|
||||||
|
for (int i=0; i<18; i++) {
|
||||||
|
contevents[i].resize(16);
|
||||||
|
std::fill(contevents[i].begin(), contevents[i].end(), nullptr);
|
||||||
|
oldstates[i].resize(16);
|
||||||
|
std::fill(oldstates[i].begin(), oldstates[i].end(), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now iterate through the MidiEventList keeping track of note and
|
||||||
|
// select controller states and linking notes/controllers as needed.
|
||||||
|
int channel;
|
||||||
|
int key;
|
||||||
|
int contnum;
|
||||||
|
int contval;
|
||||||
|
int conti;
|
||||||
|
int contstate;
|
||||||
|
int counter = 0;
|
||||||
|
MidiEvent* mev;
|
||||||
|
MidiEvent* noteon;
|
||||||
|
for (int i=0; i<getSize(); i++) {
|
||||||
|
mev = &getEvent(i);
|
||||||
|
mev->unlinkEvent();
|
||||||
|
if (mev->isNoteOn()) {
|
||||||
|
// store the note-on to pair later with a note-off message.
|
||||||
|
key = mev->getKeyNumber();
|
||||||
|
channel = mev->getChannel();
|
||||||
|
noteons[channel][key].push_back(mev);
|
||||||
|
} else if (mev->isNoteOff()) {
|
||||||
|
key = mev->getKeyNumber();
|
||||||
|
channel = mev->getChannel();
|
||||||
|
if (noteons[channel][key].size() > 0) {
|
||||||
|
noteon = noteons[channel][key].back();
|
||||||
|
noteons[channel][key].pop_back();
|
||||||
|
noteon->linkEvent(mev);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
} else if (mev->isController()) {
|
||||||
|
contnum = mev->getP1();
|
||||||
|
if (contmap[contnum].first) {
|
||||||
|
conti = contmap[contnum].second;
|
||||||
|
channel = mev->getChannel();
|
||||||
|
contval = mev->getP2();
|
||||||
|
contstate = contval < 64 ? 0 : 1;
|
||||||
|
if ((oldstates[conti][channel] == -1) && contstate) {
|
||||||
|
// a newly initialized onstate was detected, so store for
|
||||||
|
// later linking to an off state.
|
||||||
|
contevents[conti][channel] = mev;
|
||||||
|
oldstates[conti][channel] = contstate;
|
||||||
|
} else if (oldstates[conti][channel] == contstate) {
|
||||||
|
// the controller state is redundant and will be ignored.
|
||||||
|
} else if ((oldstates[conti][channel] == 0) && contstate) {
|
||||||
|
// controller is currently off, so store on-state for next link
|
||||||
|
contevents[conti][channel] = mev;
|
||||||
|
oldstates[conti][channel] = contstate;
|
||||||
|
} else if ((oldstates[conti][channel] == 1) && (contstate == 0)) {
|
||||||
|
// controller has just been turned off, so link to
|
||||||
|
// stored on-message.
|
||||||
|
contevents[conti][channel]->linkEvent(mev);
|
||||||
|
oldstates[conti][channel] = contstate;
|
||||||
|
// not necessary, but maybe use for something later:
|
||||||
|
contevents[conti][channel] = mev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::clearLinks -- remove all note-on/note-off links.
|
||||||
|
//
|
||||||
|
|
||||||
|
void MidiEventList::clearLinks(void) {
|
||||||
|
for (int i=0; i<(int)getSize(); i++) {
|
||||||
|
getEvent(i).unlinkEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::clearSequence -- Remove any sequence serial numbers from
|
||||||
|
// MidiEvents in the list. This will cause the default ordering by
|
||||||
|
// sortTracks() to be used, in which case the ordering of MidiEvents
|
||||||
|
// occurring at the same tick may switch their ordering.
|
||||||
|
//
|
||||||
|
|
||||||
|
void MidiEventList::clearSequence(void) {
|
||||||
|
for (int i=0; i<getEventCount(); i++) {
|
||||||
|
getEvent(i).seq = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::markSequence -- Assign a sequence serial number to
|
||||||
|
// every MidiEvent in the event list. This is useful if you want
|
||||||
|
// to preseve the order of MIDI messages in a track when they occur
|
||||||
|
// at the same tick time. Particularly for use with joinTracks()
|
||||||
|
// or sortTracks(). markSequence will be done automatically when
|
||||||
|
// a MIDI file is read, in case the ordering of events occurring at
|
||||||
|
// the same time is important. Use clearSequence() to use the
|
||||||
|
// default sorting behavior of sortTracks() when events occur at the
|
||||||
|
// same time. Returns the next serial number that has not yet been
|
||||||
|
// used.
|
||||||
|
// default value: sequence = 1.
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEventList::markSequence(int sequence) {
|
||||||
|
for (int i=0; i<getEventCount(); i++) {
|
||||||
|
getEvent(i).seq = sequence++;
|
||||||
|
}
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// protected functions --
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::detach -- De-allocate any MidiEvents present in the list
|
||||||
|
// and set the size of the list to 0.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
void MidiEventList::detach(void) {
|
||||||
|
list.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::push_back_no_copy -- add a MidiEvent at the end of
|
||||||
|
// the list. The event is not copied, but memory from the
|
||||||
|
// remote location is used. Returns the index of the appended event.
|
||||||
|
//
|
||||||
|
|
||||||
|
int MidiEventList::push_back_no_copy(MidiEvent* event) {
|
||||||
|
list.push_back(event);
|
||||||
|
return (int)list.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::operator=(MidiEventList) -- Assignment.
|
||||||
|
//
|
||||||
|
|
||||||
|
MidiEventList& MidiEventList::operator=(MidiEventList& other) {
|
||||||
|
list.swap(other.list);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// private functions
|
||||||
|
//
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// MidiEventList::sort -- Private because the MidiFile class keeps
|
||||||
|
// track of delta versus absolute tick states of the MidiEventList,
|
||||||
|
// and sorting is only allowed in absolute tick state (The MidiEventList
|
||||||
|
// does not know about delta/absolute tick states of its contents).
|
||||||
|
//
|
||||||
|
|
||||||
|
void MidiEventList::sort(void) {
|
||||||
|
qsort(data(), getEventCount(), sizeof(MidiEvent*), eventcompare);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// external functions
|
||||||
|
//
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//
|
||||||
|
// eventcompare -- Event comparison function for sorting tracks.
|
||||||
|
//
|
||||||
|
// Sorting rules:
|
||||||
|
// (1) sort by (absolute) tick value; otherwise, if tick values are the same:
|
||||||
|
// (2) end-of-track meta message is always last.
|
||||||
|
// (3) other meta-messages come before regular MIDI messages.
|
||||||
|
// (4) note-offs come after all other regular MIDI messages except note-ons.
|
||||||
|
// (5) note-ons come after all other regular MIDI messages.
|
||||||
|
//
|
||||||
|
|
||||||
|
int eventcompare(const void* a, const void* b) {
|
||||||
|
MidiEvent& aevent = **((MidiEvent**)a);
|
||||||
|
MidiEvent& bevent = **((MidiEvent**)b);
|
||||||
|
|
||||||
|
if (aevent.tick > bevent.tick) {
|
||||||
|
// aevent occurs after bevent
|
||||||
|
return +1;
|
||||||
|
} else if (aevent.tick < bevent.tick) {
|
||||||
|
// aevent occurs before bevent
|
||||||
|
return -1;
|
||||||
|
} else if ((aevent.seq != 0) && (bevent.seq != 0) && (aevent.seq > bevent.seq)) {
|
||||||
|
// aevent sequencing state occurs after bevent
|
||||||
|
// see MidiEventList::markSequence()
|
||||||
|
return +1;
|
||||||
|
} else if ((aevent.seq != 0) && (bevent.seq != 0) && (aevent.seq < bevent.seq)) {
|
||||||
|
// aevent sequencing state occurs before bevent
|
||||||
|
// see MidiEventList::markSequence()
|
||||||
|
return -1;
|
||||||
|
} else if (aevent.getP0() == 0xff && aevent.getP1() == 0x2f) {
|
||||||
|
// end-of-track meta-message should always be last (but won't really
|
||||||
|
// matter since the writing function ignores all end-of-track messages
|
||||||
|
// and writes its own.
|
||||||
|
return +1;
|
||||||
|
} else if (bevent.getP0() == 0xff && bevent.getP1() == 0x2f) {
|
||||||
|
// end-of-track meta-message should always be last (but won't really
|
||||||
|
// matter since the writing function ignores all end-of-track messages
|
||||||
|
// and writes its own.
|
||||||
|
return -1;
|
||||||
|
} else if (aevent.getP0() == 0xff && bevent.getP0() != 0xff) {
|
||||||
|
// other meta-messages are placed before real MIDI messages
|
||||||
|
return -1;
|
||||||
|
} else if (aevent.getP0() != 0xff && bevent.getP0() == 0xff) {
|
||||||
|
// other meta-messages are placed before real MIDI messages
|
||||||
|
return +1;
|
||||||
|
} else if (((aevent.getP0() & 0xf0) == 0x90) && (aevent.getP2() != 0)) {
|
||||||
|
// note-ons come after all other types of MIDI messages
|
||||||
|
return +1;
|
||||||
|
} else if (((bevent.getP0() & 0xf0) == 0x90) && (bevent.getP2() != 0)) {
|
||||||
|
// note-ons come after all other types of MIDI messages
|
||||||
|
return -1;
|
||||||
|
} else if (((aevent.getP0() & 0xf0) == 0x90) || ((aevent.getP0() & 0xf0) == 0x80)) {
|
||||||
|
// note-offs come after all other MIDI messages (except note-ons)
|
||||||
|
return +1;
|
||||||
|
} else if (((bevent.getP0() & 0xf0) == 0x90) || ((bevent.getP0() & 0xf0) == 0x80)) {
|
||||||
|
// note-offs come after all other MIDI messages (except note-ons)
|
||||||
|
return -1;
|
||||||
|
} else if (((aevent.getP0() & 0xf0) == 0xb0) && ((bevent.getP0() & 0xf0) == 0xb0)) {
|
||||||
|
// both events are continuous controllers. Sort them by controller number
|
||||||
|
if (aevent.getP1() > bevent.getP1()) {
|
||||||
|
return +1;
|
||||||
|
} if (aevent.getP1() < bevent.getP1()) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
// same controller number, so sort by data value
|
||||||
|
if (aevent.getP2() > bevent.getP2()) {
|
||||||
|
return +1;
|
||||||
|
} if (aevent.getP2() < bevent.getP2()) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // end namespace smf
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
//
|
||||||
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
||||||
|
// Creation Date: Sat Feb 14 21:55:38 PST 2015
|
||||||
|
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
|
||||||
|
// Filename: midifile/include/MidiEventList.h
|
||||||
|
// Website: http://midifile.sapp.org
|
||||||
|
// Syntax: C++11
|
||||||
|
// vim: ts=3 noexpandtab
|
||||||
|
//
|
||||||
|
// Description: A class that stores a MidiEvents for a MidiFile track.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _MIDIEVENTLIST_H_INCLUDED
|
||||||
|
#define _MIDIEVENTLIST_H_INCLUDED
|
||||||
|
|
||||||
|
#include "MidiEvent.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace smf {
|
||||||
|
|
||||||
|
class MidiEventList {
|
||||||
|
public:
|
||||||
|
MidiEventList (void);
|
||||||
|
MidiEventList (const MidiEventList& other);
|
||||||
|
MidiEventList (MidiEventList&& other);
|
||||||
|
|
||||||
|
~MidiEventList ();
|
||||||
|
|
||||||
|
MidiEventList& operator= (MidiEventList& other);
|
||||||
|
MidiEvent& operator[] (int index);
|
||||||
|
const MidiEvent& operator[] (int index) const;
|
||||||
|
|
||||||
|
MidiEvent& back (void);
|
||||||
|
const MidiEvent& back (void) const;
|
||||||
|
MidiEvent& last (void);
|
||||||
|
const MidiEvent& last (void) const;
|
||||||
|
MidiEvent& getEvent (int index);
|
||||||
|
const MidiEvent& getEvent (int index) const;
|
||||||
|
void clear (void);
|
||||||
|
void reserve (int rsize);
|
||||||
|
int getEventCount (void) const;
|
||||||
|
int getSize (void) const;
|
||||||
|
int size (void) const;
|
||||||
|
void removeEmpties (void);
|
||||||
|
int linkNotePairs (void);
|
||||||
|
int linkEventPairs (void);
|
||||||
|
void clearLinks (void);
|
||||||
|
void clearSequence (void);
|
||||||
|
int markSequence (int sequence = 1);
|
||||||
|
|
||||||
|
int push (MidiEvent& event);
|
||||||
|
int push_back (MidiEvent& event);
|
||||||
|
int append (MidiEvent& event);
|
||||||
|
|
||||||
|
// careful when using these, intended for internal use in MidiFile class:
|
||||||
|
void detach (void);
|
||||||
|
int push_back_no_copy (MidiEvent* event);
|
||||||
|
|
||||||
|
// access to the list of MidiEvents for sorting with an external function:
|
||||||
|
MidiEvent** data (void);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<MidiEvent*> list;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sort (void);
|
||||||
|
|
||||||
|
// MidiFile class calls sort()
|
||||||
|
friend class MidiFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int eventcompare(const void* a, const void* b);
|
||||||
|
|
||||||
|
} // end of namespace smf
|
||||||
|
|
||||||
|
#endif /* _MIDIEVENTLIST_H_INCLUDED */
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,332 @@
|
||||||
|
//
|
||||||
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
||||||
|
// Creation Date: Fri Nov 26 14:12:01 PST 1999
|
||||||
|
// Last Modified: Mon Jan 18 20:54:04 PST 2021 Added readSmf().
|
||||||
|
// Filename: midifile/include/MidiFile.h
|
||||||
|
// Website: http://midifile.sapp.org
|
||||||
|
// Syntax: C++11
|
||||||
|
// vim: ts=3 noexpandtab
|
||||||
|
//
|
||||||
|
// Description: A class that can read/write Standard MIDI files.
|
||||||
|
// MIDI data is stored by track in an array.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _MIDIFILE_H_INCLUDED
|
||||||
|
#define _MIDIFILE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "MidiEventList.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <istream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace smf {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TRACK_STATE_SPLIT = 0, // Tracks are separated into separate vector postions.
|
||||||
|
TRACK_STATE_JOINED = 1 // Tracks are merged into a single vector position,
|
||||||
|
}; // like a Type-0 MIDI file, but reversible.
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TIME_STATE_DELTA = 0, // MidiMessage::ticks are in delta time format (like MIDI file).
|
||||||
|
TIME_STATE_ABSOLUTE = 1 // MidiMessage::ticks are in absolute time format (0=start time).
|
||||||
|
};
|
||||||
|
|
||||||
|
class _TickTime {
|
||||||
|
public:
|
||||||
|
int tick;
|
||||||
|
double seconds;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class MidiFile {
|
||||||
|
public:
|
||||||
|
MidiFile (void);
|
||||||
|
MidiFile (const std::string& filename);
|
||||||
|
MidiFile (std::istream& input);
|
||||||
|
MidiFile (const MidiFile& other);
|
||||||
|
MidiFile (MidiFile&& other);
|
||||||
|
|
||||||
|
~MidiFile ();
|
||||||
|
|
||||||
|
MidiFile& operator= (const MidiFile& other);
|
||||||
|
MidiFile& operator= (MidiFile&& other);
|
||||||
|
|
||||||
|
// Reading/writing functions:
|
||||||
|
|
||||||
|
// Auto-detected SMF or ASCII-encoded SMF (decoded with Binasc class):
|
||||||
|
bool read (const std::string& filename);
|
||||||
|
bool read (std::istream& instream);
|
||||||
|
bool readBase64 (const std::string& base64data);
|
||||||
|
bool readBase64 (std::istream& instream);
|
||||||
|
|
||||||
|
// Only allow Standard MIDI File input:
|
||||||
|
bool readSmf (const std::string& filename);
|
||||||
|
bool readSmf (std::istream& instream);
|
||||||
|
|
||||||
|
bool write (const std::string& filename);
|
||||||
|
bool write (std::ostream& out);
|
||||||
|
bool writeBase64 (const std::string& out, int width = 0);
|
||||||
|
bool writeBase64 (std::ostream& out, int width = 0);
|
||||||
|
std::string getBase64 (int width = 0);
|
||||||
|
bool writeHex (const std::string& filename, int width = 25);
|
||||||
|
bool writeHex (std::ostream& out, int width = 25);
|
||||||
|
bool writeBinasc (const std::string& filename);
|
||||||
|
bool writeBinasc (std::ostream& out);
|
||||||
|
bool writeBinascWithComments (const std::string& filename);
|
||||||
|
bool writeBinascWithComments (std::ostream& out);
|
||||||
|
bool status (void) const;
|
||||||
|
|
||||||
|
// track-related functions:
|
||||||
|
const MidiEventList& operator[] (int aTrack) const;
|
||||||
|
MidiEventList& operator[] (int aTrack);
|
||||||
|
int getTrackCount (void) const;
|
||||||
|
int getNumTracks (void) const;
|
||||||
|
int size (void) const;
|
||||||
|
void removeEmpties (void);
|
||||||
|
|
||||||
|
// tick-related functions:
|
||||||
|
void makeDeltaTicks (void);
|
||||||
|
void deltaTicks (void);
|
||||||
|
void makeAbsoluteTicks (void);
|
||||||
|
void absoluteTicks (void);
|
||||||
|
int getTickState (void) const;
|
||||||
|
bool isDeltaTicks (void) const;
|
||||||
|
bool isAbsoluteTicks (void) const;
|
||||||
|
|
||||||
|
// join/split track functionality:
|
||||||
|
void joinTracks (void);
|
||||||
|
void splitTracks (void);
|
||||||
|
void splitTracksByChannel (void);
|
||||||
|
int getTrackState (void) const;
|
||||||
|
int hasJoinedTracks (void) const;
|
||||||
|
int hasSplitTracks (void) const;
|
||||||
|
int getSplitTrack (int track, int index) const;
|
||||||
|
int getSplitTrack (int index) const;
|
||||||
|
|
||||||
|
// track sorting funcionality:
|
||||||
|
void sortTrack (int track);
|
||||||
|
void sortTracks (void);
|
||||||
|
void markSequence (void);
|
||||||
|
void markSequence (int track, int sequence = 1);
|
||||||
|
void clearSequence (void);
|
||||||
|
void clearSequence (int track);
|
||||||
|
|
||||||
|
// track manipulation functionality:
|
||||||
|
int addTrack (void);
|
||||||
|
int addTrack (int count);
|
||||||
|
int addTracks (int count);
|
||||||
|
void deleteTrack (int aTrack);
|
||||||
|
void mergeTracks (int aTrack1, int aTrack2);
|
||||||
|
int getTrackCountAsType1 (void);
|
||||||
|
|
||||||
|
// ticks-per-quarter related functions:
|
||||||
|
void setMillisecondTicks (void);
|
||||||
|
int getTicksPerQuarterNote (void) const;
|
||||||
|
int getTPQ (void) const;
|
||||||
|
void setTicksPerQuarterNote (int ticks);
|
||||||
|
void setTPQ (int ticks);
|
||||||
|
|
||||||
|
// physical-time analysis functions:
|
||||||
|
void doTimeAnalysis (void);
|
||||||
|
double getTimeInSeconds (int aTrack, int anIndex);
|
||||||
|
double getTimeInSeconds (int tickvalue);
|
||||||
|
double getAbsoluteTickTime (double starttime);
|
||||||
|
int getFileDurationInTicks (void);
|
||||||
|
double getFileDurationInQuarters (void);
|
||||||
|
double getFileDurationInSeconds (void);
|
||||||
|
|
||||||
|
// note-analysis functions:
|
||||||
|
int linkNotePairs (void);
|
||||||
|
int linkEventPairs (void);
|
||||||
|
void clearLinks (void);
|
||||||
|
|
||||||
|
// filename functions:
|
||||||
|
void setFilename (const std::string& aname);
|
||||||
|
const char* getFilename (void) const;
|
||||||
|
|
||||||
|
// event functionality:
|
||||||
|
MidiEvent* addEvent (int aTrack, int aTick,
|
||||||
|
std::vector<uchar>& midiData);
|
||||||
|
MidiEvent* addEvent (MidiEvent& mfevent);
|
||||||
|
MidiEvent* addEvent (int aTrack, MidiEvent& mfevent);
|
||||||
|
MidiEvent& getEvent (int aTrack, int anIndex);
|
||||||
|
const MidiEvent& getEvent (int aTrack, int anIndex) const;
|
||||||
|
int getEventCount (int aTrack) const;
|
||||||
|
int getNumEvents (int aTrack) const;
|
||||||
|
void allocateEvents (int track, int aSize);
|
||||||
|
void erase (void);
|
||||||
|
void clear (void);
|
||||||
|
void clear_no_deallocate (void);
|
||||||
|
|
||||||
|
// MIDI message adding convenience functions:
|
||||||
|
MidiEvent* addNoteOn (int aTrack, int aTick,
|
||||||
|
int aChannel, int key,
|
||||||
|
int vel);
|
||||||
|
MidiEvent* addNoteOff (int aTrack, int aTick,
|
||||||
|
int aChannel, int key,
|
||||||
|
int vel);
|
||||||
|
MidiEvent* addNoteOff (int aTrack, int aTick,
|
||||||
|
int aChannel, int key);
|
||||||
|
MidiEvent* addController (int aTrack, int aTick,
|
||||||
|
int aChannel, int num,
|
||||||
|
int value);
|
||||||
|
MidiEvent* addPatchChange (int aTrack, int aTick,
|
||||||
|
int aChannel, int patchnum);
|
||||||
|
MidiEvent* addTimbre (int aTrack, int aTick,
|
||||||
|
int aChannel, int patchnum);
|
||||||
|
MidiEvent* addPitchBend (int aTrack, int aTick,
|
||||||
|
int aChannel, double amount);
|
||||||
|
|
||||||
|
// RPN settings:
|
||||||
|
void setPitchBendRange (int aTrack, int aTick,
|
||||||
|
int aChannel, double range);
|
||||||
|
|
||||||
|
// Controller message adding convenience functions:
|
||||||
|
MidiEvent* addSustain (int aTrack, int aTick,
|
||||||
|
int aChannel, int value);
|
||||||
|
MidiEvent* addSustainPedal (int aTrack, int aTick,
|
||||||
|
int aChannel, int value);
|
||||||
|
MidiEvent* addSustainOn (int aTrack, int aTick,
|
||||||
|
int aChannel);
|
||||||
|
MidiEvent* addSustainPedalOn (int aTrack, int aTick,
|
||||||
|
int aChannel);
|
||||||
|
MidiEvent* addSustainOff (int aTrack, int aTick,
|
||||||
|
int aChannel);
|
||||||
|
MidiEvent* addSustainPedalOff (int aTrack, int aTick,
|
||||||
|
int aChannel);
|
||||||
|
|
||||||
|
// Meta-event adding convenience functions:
|
||||||
|
MidiEvent* addMetaEvent (int aTrack, int aTick,
|
||||||
|
int aType,
|
||||||
|
std::vector<uchar>& metaData);
|
||||||
|
MidiEvent* addMetaEvent (int aTrack, int aTick,
|
||||||
|
int aType,
|
||||||
|
const std::string& metaData);
|
||||||
|
MidiEvent* addText (int aTrack, int aTick,
|
||||||
|
const std::string& text);
|
||||||
|
MidiEvent* addCopyright (int aTrack, int aTick,
|
||||||
|
const std::string& text);
|
||||||
|
MidiEvent* addTrackName (int aTrack, int aTick,
|
||||||
|
const std::string& name);
|
||||||
|
MidiEvent* addInstrumentName (int aTrack, int aTick,
|
||||||
|
const std::string& name);
|
||||||
|
MidiEvent* addLyric (int aTrack, int aTick,
|
||||||
|
const std::string& text);
|
||||||
|
MidiEvent* addMarker (int aTrack, int aTick,
|
||||||
|
const std::string& text);
|
||||||
|
MidiEvent* addCue (int aTrack, int aTick,
|
||||||
|
const std::string& text);
|
||||||
|
MidiEvent* addTempo (int aTrack, int aTick,
|
||||||
|
double aTempo);
|
||||||
|
MidiEvent* addKeySignature (int aTrack, int aTick,
|
||||||
|
int fifths, bool mode = 0);
|
||||||
|
MidiEvent* addTimeSignature (int aTrack, int aTick,
|
||||||
|
int top, int bottom,
|
||||||
|
int clocksPerClick = 24,
|
||||||
|
int num32dsPerQuarter = 8);
|
||||||
|
MidiEvent* addCompoundTimeSignature(int aTrack, int aTick,
|
||||||
|
int top, int bottom,
|
||||||
|
int clocksPerClick = 36,
|
||||||
|
int num32dsPerQuarter = 8);
|
||||||
|
|
||||||
|
uchar readByte (std::istream& input);
|
||||||
|
|
||||||
|
// static functions:
|
||||||
|
static ushort readLittleEndian2Bytes (std::istream& input);
|
||||||
|
static ulong readLittleEndian4Bytes (std::istream& input);
|
||||||
|
static std::ostream& writeLittleEndianUShort (std::ostream& out,
|
||||||
|
ushort value);
|
||||||
|
static std::ostream& writeBigEndianUShort (std::ostream& out,
|
||||||
|
ushort value);
|
||||||
|
static std::ostream& writeLittleEndianShort (std::ostream& out,
|
||||||
|
short value);
|
||||||
|
static std::ostream& writeBigEndianShort (std::ostream& out,
|
||||||
|
short value);
|
||||||
|
static std::ostream& writeLittleEndianULong (std::ostream& out,
|
||||||
|
ulong value);
|
||||||
|
static std::ostream& writeBigEndianULong (std::ostream& out,
|
||||||
|
ulong value);
|
||||||
|
static std::ostream& writeLittleEndianLong (std::ostream& out,
|
||||||
|
long value);
|
||||||
|
static std::ostream& writeBigEndianLong (std::ostream& out,
|
||||||
|
long value);
|
||||||
|
static std::ostream& writeLittleEndianFloat (std::ostream& out,
|
||||||
|
float value);
|
||||||
|
static std::ostream& writeBigEndianFloat (std::ostream& out,
|
||||||
|
float value);
|
||||||
|
static std::ostream& writeLittleEndianDouble (std::ostream& out,
|
||||||
|
double value);
|
||||||
|
static std::ostream& writeBigEndianDouble (std::ostream& out,
|
||||||
|
double value);
|
||||||
|
static std::string getGMInstrumentName (int patchIndex);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// m_events == Lists of MidiEvents for each MIDI file track.
|
||||||
|
std::vector<MidiEventList*> m_events;
|
||||||
|
|
||||||
|
// m_ticksPerQuarterNote == A value for the MIDI file header
|
||||||
|
// which represents the number of ticks in a quarter note
|
||||||
|
// that are used as units for the delta times for MIDI events
|
||||||
|
// in MIDI file track data.
|
||||||
|
int m_ticksPerQuarterNote = 120;
|
||||||
|
|
||||||
|
// m_theTrackState == state variable for whether the tracks
|
||||||
|
// are joined or split.
|
||||||
|
int m_theTrackState = TRACK_STATE_SPLIT;
|
||||||
|
|
||||||
|
// m_theTimeState == state variable for whether the MidiEvent::tick
|
||||||
|
// variable contain absolute ticks since the start of the file's
|
||||||
|
// time, or delta ticks since the last MIDI event in the track.
|
||||||
|
int m_theTimeState = TIME_STATE_ABSOLUTE;
|
||||||
|
|
||||||
|
// m_readFileName == the filename of the last file read into
|
||||||
|
// the object.
|
||||||
|
std::string m_readFileName;
|
||||||
|
|
||||||
|
// m_timemapvalid ==
|
||||||
|
bool m_timemapvalid = false;
|
||||||
|
|
||||||
|
// m_timemap ==
|
||||||
|
std::vector<_TickTime> m_timemap;
|
||||||
|
|
||||||
|
// m_rwstatus == True if last read was successful, false if a problem.
|
||||||
|
bool m_rwstatus = true;
|
||||||
|
|
||||||
|
// m_linkedEventQ == True if link analysis has been done.
|
||||||
|
bool m_linkedEventsQ = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int extractMidiData (std::istream& inputfile,
|
||||||
|
std::vector<uchar>& array,
|
||||||
|
uchar& runningCommand);
|
||||||
|
ulong readVLValue (std::istream& inputfile);
|
||||||
|
ulong unpackVLV (uchar a = 0, uchar b = 0,
|
||||||
|
uchar c = 0, uchar d = 0,
|
||||||
|
uchar e = 0);
|
||||||
|
void writeVLValue (long aValue,
|
||||||
|
std::vector<uchar>& data);
|
||||||
|
int makeVLV (uchar *buffer, int number);
|
||||||
|
static int ticksearch (const void* A, const void* B);
|
||||||
|
static int secondsearch (const void* A, const void* B);
|
||||||
|
void buildTimeMap (void);
|
||||||
|
double linearTickInterpolationAtSecond (double seconds);
|
||||||
|
double linearSecondInterpolationAtTick (int ticktime);
|
||||||
|
std::string base64Encode (const std::string &input);
|
||||||
|
std::string base64Decode (const std::string &input);
|
||||||
|
|
||||||
|
static const std::string encodeLookup;
|
||||||
|
static const std::vector<int> decodeLookup;
|
||||||
|
static const char *GMinstrument[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end of namespace smf
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, smf::MidiFile& aMidiFile);
|
||||||
|
|
||||||
|
#endif /* _MIDIFILE_H_INCLUDED */
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,219 @@
|
||||||
|
//
|
||||||
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
||||||
|
// Creation Date: Sat Feb 14 20:36:32 PST 2015
|
||||||
|
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
|
||||||
|
// Filename: midifile/include/MidiMessage.h
|
||||||
|
// Website: http://midifile.sapp.org
|
||||||
|
// Syntax: C++11
|
||||||
|
// vim: ts=3 noexpandtab
|
||||||
|
//
|
||||||
|
// Description: Storage for bytes of a MIDI message for use in MidiFile
|
||||||
|
// class.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _MIDIMESSAGE_H_INCLUDED
|
||||||
|
#define _MIDIMESSAGE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace smf {
|
||||||
|
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
typedef unsigned short ushort;
|
||||||
|
typedef unsigned long ulong;
|
||||||
|
|
||||||
|
class MidiMessage : public std::vector<uchar> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
MidiMessage (void);
|
||||||
|
MidiMessage (int command);
|
||||||
|
MidiMessage (int command, int p1);
|
||||||
|
MidiMessage (int command, int p1, int p2);
|
||||||
|
MidiMessage (const MidiMessage& message);
|
||||||
|
MidiMessage (const std::vector<uchar>& message);
|
||||||
|
MidiMessage (const std::vector<char>& message);
|
||||||
|
MidiMessage (const std::vector<int>& message);
|
||||||
|
|
||||||
|
~MidiMessage ();
|
||||||
|
|
||||||
|
MidiMessage& operator= (const MidiMessage& message);
|
||||||
|
MidiMessage& operator= (const std::vector<uchar>& bytes);
|
||||||
|
MidiMessage& operator= (const std::vector<char>& bytes);
|
||||||
|
MidiMessage& operator= (const std::vector<int>& bytes);
|
||||||
|
|
||||||
|
void sortTrack (void);
|
||||||
|
void sortTrackWithSequence(void);
|
||||||
|
|
||||||
|
static std::vector<uchar> intToVlv (int value);
|
||||||
|
static double frequencyToSemitones (double frequency, double a4frequency = 440.0);
|
||||||
|
|
||||||
|
// data access convenience functions (returns -1 if not present):
|
||||||
|
int getP0 (void) const;
|
||||||
|
int getP1 (void) const;
|
||||||
|
int getP2 (void) const;
|
||||||
|
int getP3 (void) const;
|
||||||
|
void setP0 (int value);
|
||||||
|
void setP1 (int value);
|
||||||
|
void setP2 (int value);
|
||||||
|
void setP3 (int value);
|
||||||
|
|
||||||
|
int getSize (void) const;
|
||||||
|
void setSize (int asize);
|
||||||
|
int setSizeToCommand (void);
|
||||||
|
int resizeToCommand (void);
|
||||||
|
|
||||||
|
// note-message convenience functions:
|
||||||
|
int getKeyNumber (void) const;
|
||||||
|
int getVelocity (void) const;
|
||||||
|
void setKeyNumber (int value);
|
||||||
|
void setVelocity (int value);
|
||||||
|
void setSpelling (int base7, int accidental);
|
||||||
|
void getSpelling (int& base7, int& accidental);
|
||||||
|
|
||||||
|
// controller-message convenience functions:
|
||||||
|
int getControllerNumber (void) const;
|
||||||
|
int getControllerValue (void) const;
|
||||||
|
|
||||||
|
int getCommandNibble (void) const;
|
||||||
|
int getCommandByte (void) const;
|
||||||
|
int getChannelNibble (void) const;
|
||||||
|
int getChannel (void) const;
|
||||||
|
|
||||||
|
void setCommandByte (int value);
|
||||||
|
void setCommand (int value);
|
||||||
|
void setCommand (int value, int p1);
|
||||||
|
void setCommand (int value, int p1, int p2);
|
||||||
|
void setCommandNibble (int value);
|
||||||
|
void setChannelNibble (int value);
|
||||||
|
void setChannel (int value);
|
||||||
|
void setParameters (int p1, int p2);
|
||||||
|
void setParameters (int p1);
|
||||||
|
void setMessage (const std::vector<uchar>& message);
|
||||||
|
void setMessage (const std::vector<char>& message);
|
||||||
|
void setMessage (const std::vector<int>& message);
|
||||||
|
|
||||||
|
// message-type convenience functions:
|
||||||
|
bool isMetaMessage (void) const;
|
||||||
|
bool isMeta (void) const;
|
||||||
|
bool isNote (void) const;
|
||||||
|
bool isNoteOff (void) const;
|
||||||
|
bool isNoteOn (void) const;
|
||||||
|
bool isAftertouch (void) const;
|
||||||
|
bool isController (void) const;
|
||||||
|
bool isSustain (void) const; // controller 64
|
||||||
|
bool isSustainOn (void) const;
|
||||||
|
bool isSustainOff (void) const;
|
||||||
|
bool isSoft (void) const; // controller 67
|
||||||
|
bool isSoftOn (void) const;
|
||||||
|
bool isSoftOff (void) const;
|
||||||
|
bool isPatchChange (void) const;
|
||||||
|
bool isTimbre (void) const;
|
||||||
|
bool isPressure (void) const;
|
||||||
|
bool isPitchbend (void) const;
|
||||||
|
bool isEmpty (void) const; // see MidiFile::removeEmpties()
|
||||||
|
|
||||||
|
// helper functions to create various MidiMessages:
|
||||||
|
void makeNoteOn (int channel, int key, int velocity);
|
||||||
|
void makeNoteOff (int channel, int key, int velocity);
|
||||||
|
void makeNoteOff (int channel, int key);
|
||||||
|
void makeNoteOff (void);
|
||||||
|
void makePatchChange (int channel, int patchnum);
|
||||||
|
void makeTimbre (int channel, int patchnum);
|
||||||
|
void makeController (int channel, int num, int value);
|
||||||
|
void makePitchBend (int channel, int lsb, int msb);
|
||||||
|
void makePitchBend (int channel, int value);
|
||||||
|
void makePitchBendDouble (int channel, double value);
|
||||||
|
void makePitchbend (int channel, int lsb, int msb) { makePitchBend(channel, lsb, msb); }
|
||||||
|
void makePitchbend (int channel, int value) { makePitchBend(channel, value); }
|
||||||
|
void makePitchbendDouble (int channel, double value) { makePitchBendDouble(channel, value); }
|
||||||
|
|
||||||
|
// helper functions to create various continuous controller messages:
|
||||||
|
void makeSustain (int channel, int value);
|
||||||
|
void makeSustainPedal (int channel, int value);
|
||||||
|
void makeSustainOn (int channel);
|
||||||
|
void makeSustainPedalOn (int channel);
|
||||||
|
void makeSustainOff (int channel);
|
||||||
|
void makeSustainPedalOff (int channel);
|
||||||
|
|
||||||
|
// meta-message creation and helper functions:
|
||||||
|
void makeMetaMessage (int mnum, const std::string& data);
|
||||||
|
void makeText (const std::string& name);
|
||||||
|
void makeCopyright (const std::string& text);
|
||||||
|
void makeTrackName (const std::string& name);
|
||||||
|
void makeInstrumentName (const std::string& name);
|
||||||
|
void makeLyric (const std::string& text);
|
||||||
|
void makeMarker (const std::string& text);
|
||||||
|
void makeCue (const std::string& text);
|
||||||
|
void makeKeySignature (int fifths, bool mode = 0);
|
||||||
|
void makeTimeSignature (int top, int bottom,
|
||||||
|
int clocksPerClick = 24,
|
||||||
|
int num32dsPerQuarter = 8);
|
||||||
|
|
||||||
|
void makeTempo (double tempo) { setTempo(tempo); }
|
||||||
|
int getTempoMicro (void) const;
|
||||||
|
int getTempoMicroseconds (void) const;
|
||||||
|
double getTempoSeconds (void) const;
|
||||||
|
double getTempoBPM (void) const;
|
||||||
|
double getTempoTPS (int tpq) const;
|
||||||
|
double getTempoSPT (int tpq) const;
|
||||||
|
|
||||||
|
int getMetaType (void) const;
|
||||||
|
bool isText (void) const;
|
||||||
|
bool isCopyright (void) const;
|
||||||
|
bool isTrackName (void) const;
|
||||||
|
bool isInstrumentName (void) const;
|
||||||
|
bool isLyricText (void) const;
|
||||||
|
bool isMarkerText (void) const;
|
||||||
|
bool isTempo (void) const;
|
||||||
|
bool isTimeSignature (void) const;
|
||||||
|
bool isKeySignature (void) const;
|
||||||
|
bool isEndOfTrack (void) const;
|
||||||
|
|
||||||
|
std::string getMetaContent (void) const;
|
||||||
|
void setMetaContent (const std::string& content);
|
||||||
|
void setTempo (double tempo);
|
||||||
|
void setTempoMicroseconds (int microseconds);
|
||||||
|
void setMetaTempo (double tempo);
|
||||||
|
|
||||||
|
|
||||||
|
void makeSysExMessage (const std::vector<uchar>& data);
|
||||||
|
|
||||||
|
// helper functions to create MTS tunings by key (real-time sysex)
|
||||||
|
|
||||||
|
// MTS type 2: Real-time frequency assignment to a arbitrary list of MIDI key numbers.
|
||||||
|
// See page 2 of: https://docs.google.com/viewer?url=https://www.midi.org/component/edocman/midi-tuning-updated/fdocument?Itemid=9999
|
||||||
|
void makeMts2_KeyTuningByFrequency (int key, double frequency, int program = 0);
|
||||||
|
void makeMts2_KeyTuningsByFrequency (int key, double frequency, int program = 0);
|
||||||
|
void makeMts2_KeyTuningsByFrequency (std::vector<std::pair<int, double>>& mapping, int program = 0);
|
||||||
|
void makeMts2_KeyTuningBySemitone (int key, double semitone, int program = 0);
|
||||||
|
void makeMts2_KeyTuningsBySemitone (int key, double semitone, int program = 0);
|
||||||
|
void makeMts2_KeyTuningsBySemitone (std::vector<std::pair<int, double>>& mapping, int program = 0);
|
||||||
|
|
||||||
|
// MTS type 9: Real-time octave temperaments by +/- 100 cents deviation from ET
|
||||||
|
// See page 7 of: https://docs.google.com/viewer?url=https://www.midi.org/component/edocman/midi-tuning-updated/fdocument?Itemid=9999
|
||||||
|
void makeMts9_TemperamentByCentsDeviationFromET (std::vector<double>& mapping, int referencePitchClass = 0, int channelMask = 0b1111111111111111);
|
||||||
|
void makeTemperamentEqual(int referencePitchClass = 0, int channelMask = 0b1111111111111111);
|
||||||
|
void makeTemperamentBad(double maxDeviationCents = 100.0, int referencePitchClass = 0, int channelMask = 0b1111111111111111);
|
||||||
|
void makeTemperamentPythagorean(int referencePitchClass = 2, int channelMask = 0b1111111111111111);
|
||||||
|
void makeTemperamentMeantone(double fraction = 0.25, int referencePitchClass = 2, int channelMask = 0b1111111111111111);
|
||||||
|
void makeTemperamentMeantoneCommaQuarter(int referencePitchClass = 2, int channelMask = 0b1111111111111111);
|
||||||
|
void makeTemperamentMeantoneCommaThird(int referencePitchClass = 2, int channelMask = 0b1111111111111111);
|
||||||
|
void makeTemperamentMeantoneCommaHalf(int referencePitchClass = 2, int channelMask = 0b1111111111111111);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, MidiMessage& event);
|
||||||
|
|
||||||
|
|
||||||
|
} // end of namespace smf
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _MIDIMESSAGE_H_INCLUDED */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -321,19 +321,19 @@ void Blitter_32bppAnim::DrawColourMappingRect(void *dst, int width, int height,
|
||||||
Debug(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('{}')", pal);
|
Debug(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('{}')", pal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_32bppAnim::SetPixel(void *video, int x, int y, uint8_t colour)
|
void Blitter_32bppAnim::SetPixel(void *video, int x, int y, PixelColour colour)
|
||||||
{
|
{
|
||||||
*((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour);
|
*((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour.p);
|
||||||
|
|
||||||
/* Set the colour in the anim-buffer too, if we are rendering to the screen */
|
/* Set the colour in the anim-buffer too, if we are rendering to the screen */
|
||||||
if (_screen_disable_anim) return;
|
if (_screen_disable_anim) return;
|
||||||
|
|
||||||
this->anim_buf[this->ScreenToAnimOffset((uint32_t *)video) + x + y * this->anim_buf_pitch] = colour | (DEFAULT_BRIGHTNESS << 8);
|
this->anim_buf[this->ScreenToAnimOffset((uint32_t *)video) + x + y * this->anim_buf_pitch] = colour.p | (DEFAULT_BRIGHTNESS << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_32bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash)
|
void Blitter_32bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash)
|
||||||
{
|
{
|
||||||
const Colour c = LookupColourInPalette(colour);
|
const Colour c = LookupColourInPalette(colour.p);
|
||||||
|
|
||||||
if (_screen_disable_anim) {
|
if (_screen_disable_anim) {
|
||||||
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) {
|
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) {
|
||||||
|
@ -341,7 +341,7 @@ void Blitter_32bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
uint16_t * const offset_anim_buf = this->anim_buf + this->ScreenToAnimOffset((uint32_t *)video);
|
uint16_t * const offset_anim_buf = this->anim_buf + this->ScreenToAnimOffset((uint32_t *)video);
|
||||||
const uint16_t anim_colour = colour | (DEFAULT_BRIGHTNESS << 8);
|
const uint16_t anim_colour = colour.p | (DEFAULT_BRIGHTNESS << 8);
|
||||||
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) {
|
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) {
|
||||||
*((Colour *)video + x + y * _screen.pitch) = c;
|
*((Colour *)video + x + y * _screen.pitch) = c;
|
||||||
offset_anim_buf[x + y * this->anim_buf_pitch] = anim_colour;
|
offset_anim_buf[x + y * this->anim_buf_pitch] = anim_colour;
|
||||||
|
@ -349,7 +349,7 @@ void Blitter_32bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8_t colour)
|
void Blitter_32bppAnim::DrawRect(void *video, int width, int height, PixelColour colour)
|
||||||
{
|
{
|
||||||
if (_screen_disable_anim) {
|
if (_screen_disable_anim) {
|
||||||
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
|
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
|
||||||
|
@ -357,7 +357,7 @@ void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8_t col
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Colour colour32 = LookupColourInPalette(colour);
|
Colour colour32 = LookupColourInPalette(colour.p);
|
||||||
uint16_t *anim_line = this->ScreenToAnimOffset((uint32_t *)video) + this->anim_buf;
|
uint16_t *anim_line = this->ScreenToAnimOffset((uint32_t *)video) + this->anim_buf;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -367,7 +367,7 @@ void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8_t col
|
||||||
for (int i = width; i > 0; i--) {
|
for (int i = width; i > 0; i--) {
|
||||||
*dst = colour32;
|
*dst = colour32;
|
||||||
/* Set the colour in the anim-buffer too */
|
/* Set the colour in the anim-buffer too */
|
||||||
*anim = colour | (DEFAULT_BRIGHTNESS << 8);
|
*anim = colour.p | (DEFAULT_BRIGHTNESS << 8);
|
||||||
dst++;
|
dst++;
|
||||||
anim++;
|
anim++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,9 @@ public:
|
||||||
|
|
||||||
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
|
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
|
||||||
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override;
|
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override;
|
||||||
void SetPixel(void *video, int x, int y, uint8_t colour) override;
|
void SetPixel(void *video, int x, int y, PixelColour colour) override;
|
||||||
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) override;
|
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash) override;
|
||||||
void DrawRect(void *video, int width, int height, uint8_t colour) override;
|
void DrawRect(void *video, int width, int height, PixelColour colour) override;
|
||||||
void CopyFromBuffer(void *video, const void *src, int width, int height) override;
|
void CopyFromBuffer(void *video, const void *src, int width, int height) override;
|
||||||
void CopyToBuffer(const void *video, void *dst, int width, int height) override;
|
void CopyToBuffer(const void *video, void *dst, int width, int height) override;
|
||||||
void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override;
|
void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override;
|
||||||
|
|
|
@ -18,22 +18,22 @@ void *Blitter_32bppBase::MoveTo(void *video, int x, int y)
|
||||||
return (uint32_t *)video + x + y * _screen.pitch;
|
return (uint32_t *)video + x + y * _screen.pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_32bppBase::SetPixel(void *video, int x, int y, uint8_t colour)
|
void Blitter_32bppBase::SetPixel(void *video, int x, int y, PixelColour colour)
|
||||||
{
|
{
|
||||||
*((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour);
|
*((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour.p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_32bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash)
|
void Blitter_32bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash)
|
||||||
{
|
{
|
||||||
const Colour c = LookupColourInPalette(colour);
|
const Colour c = LookupColourInPalette(colour.p);
|
||||||
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) {
|
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) {
|
||||||
*((Colour *)video + x + y * _screen.pitch) = c;
|
*((Colour *)video + x + y * _screen.pitch) = c;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_32bppBase::DrawRect(void *video, int width, int height, uint8_t colour)
|
void Blitter_32bppBase::DrawRect(void *video, int width, int height, PixelColour colour)
|
||||||
{
|
{
|
||||||
Colour colour32 = LookupColourInPalette(colour);
|
Colour colour32 = LookupColourInPalette(colour.p);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
Colour *dst = (Colour *)video;
|
Colour *dst = (Colour *)video;
|
||||||
|
|
|
@ -19,9 +19,9 @@ class Blitter_32bppBase : public Blitter {
|
||||||
public:
|
public:
|
||||||
uint8_t GetScreenDepth() override { return 32; }
|
uint8_t GetScreenDepth() override { return 32; }
|
||||||
void *MoveTo(void *video, int x, int y) override;
|
void *MoveTo(void *video, int x, int y) override;
|
||||||
void SetPixel(void *video, int x, int y, uint8_t colour) override;
|
void SetPixel(void *video, int x, int y, PixelColour colour) override;
|
||||||
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) override;
|
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash) override;
|
||||||
void DrawRect(void *video, int width, int height, uint8_t colour) override;
|
void DrawRect(void *video, int width, int height, PixelColour colour) override;
|
||||||
void CopyFromBuffer(void *video, const void *src, int width, int height) override;
|
void CopyFromBuffer(void *video, const void *src, int width, int height) override;
|
||||||
void CopyToBuffer(const void *video, void *dst, int width, int height) override;
|
void CopyToBuffer(const void *video, void *dst, int width, int height) override;
|
||||||
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;
|
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;
|
||||||
|
|
|
@ -27,7 +27,7 @@ static FBlitter_40bppAnim iFBlitter_40bppAnim;
|
||||||
static const Colour _black_colour(0, 0, 0);
|
static const Colour _black_colour(0, 0, 0);
|
||||||
|
|
||||||
|
|
||||||
void Blitter_40bppAnim::SetPixel(void *video, int x, int y, uint8_t colour)
|
void Blitter_40bppAnim::SetPixel(void *video, int x, int y, PixelColour colour)
|
||||||
{
|
{
|
||||||
if (_screen_disable_anim) {
|
if (_screen_disable_anim) {
|
||||||
Blitter_32bppOptimized::SetPixel(video, x, y, colour);
|
Blitter_32bppOptimized::SetPixel(video, x, y, colour);
|
||||||
|
@ -35,11 +35,11 @@ void Blitter_40bppAnim::SetPixel(void *video, int x, int y, uint8_t colour)
|
||||||
size_t y_offset = static_cast<size_t>(y) * _screen.pitch;
|
size_t y_offset = static_cast<size_t>(y) * _screen.pitch;
|
||||||
*((Colour *)video + x + y_offset) = _black_colour;
|
*((Colour *)video + x + y_offset) = _black_colour;
|
||||||
|
|
||||||
VideoDriver::GetInstance()->GetAnimBuffer()[((uint32_t *)video - (uint32_t *)_screen.dst_ptr) + x + y_offset] = colour;
|
VideoDriver::GetInstance()->GetAnimBuffer()[((uint32_t *)video - (uint32_t *)_screen.dst_ptr) + x + y_offset] = colour.p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_40bppAnim::DrawRect(void *video, int width, int height, uint8_t colour)
|
void Blitter_40bppAnim::DrawRect(void *video, int width, int height, PixelColour colour)
|
||||||
{
|
{
|
||||||
if (_screen_disable_anim) {
|
if (_screen_disable_anim) {
|
||||||
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
|
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
|
||||||
|
@ -56,7 +56,7 @@ void Blitter_40bppAnim::DrawRect(void *video, int width, int height, uint8_t col
|
||||||
|
|
||||||
for (int i = width; i > 0; i--) {
|
for (int i = width; i > 0; i--) {
|
||||||
*dst = _black_colour;
|
*dst = _black_colour;
|
||||||
*anim = colour;
|
*anim = colour.p;
|
||||||
dst++;
|
dst++;
|
||||||
anim++;
|
anim++;
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ void Blitter_40bppAnim::DrawRect(void *video, int width, int height, uint8_t col
|
||||||
} while (--height);
|
} while (--height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_40bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash)
|
void Blitter_40bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash)
|
||||||
{
|
{
|
||||||
if (_screen_disable_anim) {
|
if (_screen_disable_anim) {
|
||||||
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
|
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
|
||||||
|
@ -78,7 +78,7 @@ void Blitter_40bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int
|
||||||
|
|
||||||
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) {
|
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) {
|
||||||
*((Colour *)video + x + y * _screen.pitch) = _black_colour;
|
*((Colour *)video + x + y * _screen.pitch) = _black_colour;
|
||||||
*(anim + x + y * _screen.pitch) = colour;
|
*(anim + x + y * _screen.pitch) = colour.p;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
class Blitter_40bppAnim : public Blitter_32bppOptimized {
|
class Blitter_40bppAnim : public Blitter_32bppOptimized {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void SetPixel(void *video, int x, int y, uint8_t colour) override;
|
void SetPixel(void *video, int x, int y, PixelColour colour) override;
|
||||||
void DrawRect(void *video, int width, int height, uint8_t colour) override;
|
void DrawRect(void *video, int width, int height, PixelColour colour) override;
|
||||||
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) override;
|
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash) override;
|
||||||
void CopyFromBuffer(void *video, const void *src, int width, int height) override;
|
void CopyFromBuffer(void *video, const void *src, int width, int height) override;
|
||||||
void CopyToBuffer(const void *video, void *dst, int width, int height) override;
|
void CopyToBuffer(const void *video, void *dst, int width, int height) override;
|
||||||
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;
|
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;
|
||||||
|
|
|
@ -29,23 +29,23 @@ void *Blitter_8bppBase::MoveTo(void *video, int x, int y)
|
||||||
return (uint8_t *)video + x + y * _screen.pitch;
|
return (uint8_t *)video + x + y * _screen.pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_8bppBase::SetPixel(void *video, int x, int y, uint8_t colour)
|
void Blitter_8bppBase::SetPixel(void *video, int x, int y, PixelColour colour)
|
||||||
{
|
{
|
||||||
*((uint8_t *)video + x + y * _screen.pitch) = colour;
|
*((uint8_t *)video + x + y * _screen.pitch) = colour.p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_8bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash)
|
void Blitter_8bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash)
|
||||||
{
|
{
|
||||||
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) {
|
this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) {
|
||||||
*((uint8_t *)video + x + y * _screen.pitch) = colour;
|
*((uint8_t *)video + x + y * _screen.pitch) = colour.p;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blitter_8bppBase::DrawRect(void *video, int width, int height, uint8_t colour)
|
void Blitter_8bppBase::DrawRect(void *video, int width, int height, PixelColour colour)
|
||||||
{
|
{
|
||||||
std::byte *p = static_cast<std::byte *>(video);
|
std::byte *p = static_cast<std::byte *>(video);
|
||||||
do {
|
do {
|
||||||
std::fill_n(p, width, static_cast<std::byte>(colour));
|
std::fill_n(p, width, static_cast<std::byte>(colour.p));
|
||||||
p += _screen.pitch;
|
p += _screen.pitch;
|
||||||
} while (--height);
|
} while (--height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ public:
|
||||||
uint8_t GetScreenDepth() override { return 8; }
|
uint8_t GetScreenDepth() override { return 8; }
|
||||||
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override;
|
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override;
|
||||||
void *MoveTo(void *video, int x, int y) override;
|
void *MoveTo(void *video, int x, int y) override;
|
||||||
void SetPixel(void *video, int x, int y, uint8_t colour) override;
|
void SetPixel(void *video, int x, int y, PixelColour colour) override;
|
||||||
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) override;
|
void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash) override;
|
||||||
void DrawRect(void *video, int width, int height, uint8_t colour) override;
|
void DrawRect(void *video, int width, int height, PixelColour colour) override;
|
||||||
void CopyFromBuffer(void *video, const void *src, int width, int height) override;
|
void CopyFromBuffer(void *video, const void *src, int width, int height) override;
|
||||||
void CopyToBuffer(const void *video, void *dst, int width, int height) override;
|
void CopyToBuffer(const void *video, void *dst, int width, int height) override;
|
||||||
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;
|
void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override;
|
||||||
|
|
|
@ -95,18 +95,18 @@ public:
|
||||||
* @param video The destination pointer (video-buffer).
|
* @param video The destination pointer (video-buffer).
|
||||||
* @param x The x position within video-buffer.
|
* @param x The x position within video-buffer.
|
||||||
* @param y The y position within video-buffer.
|
* @param y The y position within video-buffer.
|
||||||
* @param colour A 8bpp mapping colour.
|
* @param colour A pixel colour.
|
||||||
*/
|
*/
|
||||||
virtual void SetPixel(void *video, int x, int y, uint8_t colour) = 0;
|
virtual void SetPixel(void *video, int x, int y, PixelColour colour) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a single horizontal line in a single colour on the video-buffer.
|
* Make a single horizontal line in a single colour on the video-buffer.
|
||||||
* @param video The destination pointer (video-buffer).
|
* @param video The destination pointer (video-buffer).
|
||||||
* @param width The length of the line.
|
* @param width The length of the line.
|
||||||
* @param height The height of the line.
|
* @param height The height of the line.
|
||||||
* @param colour A 8bpp mapping colour.
|
* @param colour A pixel colour.
|
||||||
*/
|
*/
|
||||||
virtual void DrawRect(void *video, int width, int height, uint8_t colour) = 0;
|
virtual void DrawRect(void *video, int width, int height, PixelColour colour) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw a line with a given colour.
|
* Draw a line with a given colour.
|
||||||
|
@ -117,11 +117,11 @@ public:
|
||||||
* @param y2 The y coordinate to where the lines goes.
|
* @param y2 The y coordinate to where the lines goes.
|
||||||
* @param screen_width The width of the screen you are drawing in (to avoid buffer-overflows).
|
* @param screen_width The width of the screen you are drawing in (to avoid buffer-overflows).
|
||||||
* @param screen_height The height of the screen you are drawing in (to avoid buffer-overflows).
|
* @param screen_height The height of the screen you are drawing in (to avoid buffer-overflows).
|
||||||
* @param colour A 8bpp mapping colour.
|
* @param colour A pixel colour.
|
||||||
* @param width Line width.
|
* @param width Line width.
|
||||||
* @param dash Length of dashes for dashed lines. 0 means solid line.
|
* @param dash Length of dashes for dashed lines. 0 means solid line.
|
||||||
*/
|
*/
|
||||||
virtual void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash = 0) = 0;
|
virtual void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash = 0) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy from a buffer to the screen.
|
* Copy from a buffer to the screen.
|
||||||
|
|
|
@ -20,9 +20,9 @@ public:
|
||||||
void DrawColourMappingRect(void *, int, int, PaletteID) override {};
|
void DrawColourMappingRect(void *, int, int, PaletteID) override {};
|
||||||
Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override;
|
Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override;
|
||||||
void *MoveTo(void *, int, int) override { return nullptr; };
|
void *MoveTo(void *, int, int) override { return nullptr; };
|
||||||
void SetPixel(void *, int, int, uint8_t) override {};
|
void SetPixel(void *, int, int, PixelColour) override {};
|
||||||
void DrawRect(void *, int, int, uint8_t) override {};
|
void DrawRect(void *, int, int, PixelColour) override {};
|
||||||
void DrawLine(void *, int, int, int, int, int, int, uint8_t, int, int) override {};
|
void DrawLine(void *, int, int, int, int, int, int, PixelColour, int, int) override {};
|
||||||
void CopyFromBuffer(void *, const void *, int, int) override {};
|
void CopyFromBuffer(void *, const void *, int, int) override {};
|
||||||
void CopyToBuffer(const void *, void *, int, int) override {};
|
void CopyToBuffer(const void *, void *, int, int) override {};
|
||||||
void CopyImageToBuffer(const void *, void *, int, int, int) override {};
|
void CopyImageToBuffer(const void *, void *, int, int, int) override {};
|
||||||
|
|
|
@ -60,8 +60,8 @@ public:
|
||||||
|
|
||||||
void DrawWidget(const Rect &r, WidgetID) const override
|
void DrawWidget(const Rect &r, WidgetID) const override
|
||||||
{
|
{
|
||||||
GfxFillRect(r.left, r.top, r.right, r.bottom, 4, FILLRECT_OPAQUE);
|
GfxFillRect(r.left, r.top, r.right, r.bottom, PixelColour{4}, FILLRECT_OPAQUE);
|
||||||
GfxFillRect(r.left, r.top, r.right, r.bottom, 0, FILLRECT_CHECKER);
|
GfxFillRect(r.left, r.top, r.right, r.bottom, PixelColour{0}, FILLRECT_CHECKER);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -385,10 +385,10 @@ bool HandleBootstrap()
|
||||||
/* Initialise the palette. The biggest step is 'faking' some recolour sprites.
|
/* Initialise the palette. The biggest step is 'faking' some recolour sprites.
|
||||||
* This way the mauve and gray colours work and we can show the user interface. */
|
* This way the mauve and gray colours work and we can show the user interface. */
|
||||||
GfxInitPalettes();
|
GfxInitPalettes();
|
||||||
static const int offsets[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0x04, 0x08 };
|
static const uint8_t offsets[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0x04, 0x08 };
|
||||||
for (Colours i = COLOUR_BEGIN; i != COLOUR_END; i++) {
|
for (Colours i = COLOUR_BEGIN; i != COLOUR_END; i++) {
|
||||||
for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) {
|
for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) {
|
||||||
SetColourGradient(i, j, offsets[i] + j);
|
SetColourGradient(i, j, PixelColour(offsets[i] + j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -956,7 +956,7 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
|
||||||
int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
|
int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
|
||||||
int sprite_width = sprite_left + sprite_right;
|
int sprite_width = sprite_left + sprite_right;
|
||||||
int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
|
int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
|
||||||
int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
|
PixelColour linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
|
||||||
|
|
||||||
auto badge_column_widths = badge_classes.GetColumnWidths();
|
auto badge_column_widths = badge_classes.GetColumnWidths();
|
||||||
|
|
||||||
|
|
|
@ -410,7 +410,7 @@ void VehicleCargoList::AgeCargo()
|
||||||
return (accepted && cp->first_station != current_station) ? MTA_DELIVER : MTA_KEEP;
|
return (accepted && cp->first_station != current_station) ? MTA_DELIVER : MTA_KEEP;
|
||||||
} else if (cargo_next == current_station) {
|
} else if (cargo_next == current_station) {
|
||||||
return MTA_DELIVER;
|
return MTA_DELIVER;
|
||||||
} else if (next_station.Contains(cargo_next.base())) {
|
} else if (next_station.Contains(cargo_next)) {
|
||||||
return MTA_KEEP;
|
return MTA_KEEP;
|
||||||
} else {
|
} else {
|
||||||
return MTA_TRANSFER;
|
return MTA_TRANSFER;
|
||||||
|
@ -470,7 +470,7 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID
|
||||||
new_shares.ChangeShare(current_station, INT_MIN);
|
new_shares.ChangeShare(current_station, INT_MIN);
|
||||||
StationIDStack excluded = next_station;
|
StationIDStack excluded = next_station;
|
||||||
while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
|
while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
|
||||||
new_shares.ChangeShare(StationID{excluded.Pop()}, INT_MIN);
|
new_shares.ChangeShare(excluded.Pop(), INT_MIN);
|
||||||
}
|
}
|
||||||
if (new_shares.GetShares()->empty()) {
|
if (new_shares.GetShares()->empty()) {
|
||||||
cargo_next = StationID::Invalid();
|
cargo_next = StationID::Invalid();
|
||||||
|
@ -743,7 +743,7 @@ uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool incl
|
||||||
{
|
{
|
||||||
uint max_move = action.MaxMove();
|
uint max_move = action.MaxMove();
|
||||||
while (!next.IsEmpty()) {
|
while (!next.IsEmpty()) {
|
||||||
this->ShiftCargo(action, StationID{next.Pop()});
|
this->ShiftCargo(action, next.Pop());
|
||||||
if (action.MaxMove() == 0) break;
|
if (action.MaxMove() == 0) break;
|
||||||
}
|
}
|
||||||
if (include_invalid && action.MaxMove() > 0) {
|
if (include_invalid && action.MaxMove() > 0) {
|
||||||
|
@ -853,7 +853,7 @@ uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, StationIDStac
|
||||||
*/
|
*/
|
||||||
uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
|
uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
|
||||||
{
|
{
|
||||||
return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid.base(), false);
|
return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -555,7 +555,7 @@ public:
|
||||||
inline bool HasCargoFor(StationIDStack next) const
|
inline bool HasCargoFor(StationIDStack next) const
|
||||||
{
|
{
|
||||||
while (!next.IsEmpty()) {
|
while (!next.IsEmpty()) {
|
||||||
if (this->packets.find(StationID{next.Pop()}) != this->packets.end()) return true;
|
if (this->packets.find(next.Pop()) != this->packets.end()) return true;
|
||||||
}
|
}
|
||||||
/* Packets for StationID::Invalid() can go anywhere. */
|
/* Packets for StationID::Invalid() can go anywhere. */
|
||||||
return this->packets.find(StationID::Invalid()) != this->packets.end();
|
return this->packets.find(StationID::Invalid()) != this->packets.end();
|
||||||
|
|
|
@ -74,8 +74,8 @@ static const uint TOWN_PRODUCTION_DIVISOR = 256;
|
||||||
struct CargoSpec {
|
struct CargoSpec {
|
||||||
CargoLabel label; ///< Unique label of the cargo type.
|
CargoLabel label; ///< Unique label of the cargo type.
|
||||||
uint8_t bitnum = INVALID_CARGO_BITNUM; ///< Cargo bit number, is #INVALID_CARGO_BITNUM for a non-used spec.
|
uint8_t bitnum = INVALID_CARGO_BITNUM; ///< Cargo bit number, is #INVALID_CARGO_BITNUM for a non-used spec.
|
||||||
uint8_t legend_colour;
|
PixelColour legend_colour;
|
||||||
uint8_t rating_colour;
|
PixelColour rating_colour;
|
||||||
uint8_t weight; ///< Weight of a single unit of this cargo type in 1/16 ton (62.5 kg).
|
uint8_t weight; ///< Weight of a single unit of this cargo type in 1/16 ton (62.5 kg).
|
||||||
uint16_t multiplier = 0x100; ///< Capacity multiplier for vehicles. (8 fractional bits)
|
uint16_t multiplier = 0x100; ///< Capacity multiplier for vehicles. (8 fractional bits)
|
||||||
CargoClasses classes; ///< Classes of this cargo type. @see CargoClass
|
CargoClasses classes; ///< Classes of this cargo type. @see CargoClass
|
||||||
|
|
|
@ -152,8 +152,8 @@ void SetLocalCompany(CompanyID new_company)
|
||||||
*/
|
*/
|
||||||
TextColour GetDrawStringCompanyColour(CompanyID company)
|
TextColour GetDrawStringCompanyColour(CompanyID company)
|
||||||
{
|
{
|
||||||
if (!Company::IsValidID(company)) return (TextColour)GetColourGradient(COLOUR_WHITE, SHADE_NORMAL) | TC_IS_PALETTE_COLOUR;
|
if (!Company::IsValidID(company)) return GetColourGradient(COLOUR_WHITE, SHADE_NORMAL).ToTextColour();
|
||||||
return (TextColour)GetColourGradient(_company_colours[company], SHADE_NORMAL) | TC_IS_PALETTE_COLOUR;
|
return GetColourGradient(_company_colours[company], SHADE_NORMAL).ToTextColour();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -544,7 +544,7 @@ bool IsValidConsoleColour(TextColour c)
|
||||||
* colour gradient, so it must be one of those. */
|
* colour gradient, so it must be one of those. */
|
||||||
c &= ~TC_IS_PALETTE_COLOUR;
|
c &= ~TC_IS_PALETTE_COLOUR;
|
||||||
for (Colours i = COLOUR_BEGIN; i < COLOUR_END; i++) {
|
for (Colours i = COLOUR_BEGIN; i < COLOUR_END; i++) {
|
||||||
if (GetColourGradient(i, SHADE_NORMAL) == c) return true;
|
if (GetColourGradient(i, SHADE_NORMAL).p == c) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -113,13 +113,14 @@ struct SmallStackItem {
|
||||||
* index types of the same length.
|
* index types of the same length.
|
||||||
* @tparam Titem Value type to be used.
|
* @tparam Titem Value type to be used.
|
||||||
* @tparam Tindex Index type to use for the pool.
|
* @tparam Tindex Index type to use for the pool.
|
||||||
* @tparam Tinvalid Invalid item to keep at the bottom of each stack.
|
* @tparam Tinvalid_value Value to construct invalid item to keep at the bottom of each stack.
|
||||||
* @tparam Tgrowth_step Growth step for pool.
|
* @tparam Tgrowth_step Growth step for pool.
|
||||||
* @tparam Tmax_size Maximum size for pool.
|
* @tparam Tmax_size Maximum size for pool.
|
||||||
*/
|
*/
|
||||||
template <typename Titem, typename Tindex, Titem Tinvalid, Tindex Tgrowth_step, Tindex Tmax_size>
|
template <typename Titem, typename Tindex, auto Tinvalid_value, Tindex Tgrowth_step, Tindex Tmax_size>
|
||||||
class SmallStack : public SmallStackItem<Titem, Tindex> {
|
class SmallStack : public SmallStackItem<Titem, Tindex> {
|
||||||
public:
|
public:
|
||||||
|
static constexpr Titem Tinvalid{Tinvalid_value};
|
||||||
|
|
||||||
typedef SmallStackItem<Titem, Tindex> Item;
|
typedef SmallStackItem<Titem, Tindex> Item;
|
||||||
|
|
||||||
|
|
|
@ -413,7 +413,7 @@ struct DepotWindow : Window {
|
||||||
*/
|
*/
|
||||||
if (this->type == VEH_TRAIN && _consistent_train_width != 0) {
|
if (this->type == VEH_TRAIN && _consistent_train_width != 0) {
|
||||||
int w = ScaleSpriteTrad(2 * _consistent_train_width);
|
int w = ScaleSpriteTrad(2 * _consistent_train_width);
|
||||||
int col = GetColourGradient(wid->colour, SHADE_NORMAL);
|
PixelColour col = GetColourGradient(wid->colour, SHADE_NORMAL);
|
||||||
Rect image = ir.Indent(this->header_width, rtl).Indent(this->count_width, !rtl);
|
Rect image = ir.Indent(this->header_width, rtl).Indent(this->count_width, !rtl);
|
||||||
int first_line = w + (-this->hscroll->GetPosition()) % w;
|
int first_line = w + (-this->hscroll->GetPosition()) % w;
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
|
|
|
@ -37,8 +37,8 @@ public:
|
||||||
|
|
||||||
void Draw(const Rect &full, const Rect &, bool, int, Colours bg_colour) const override
|
void Draw(const Rect &full, const Rect &, bool, int, Colours bg_colour) const override
|
||||||
{
|
{
|
||||||
uint8_t c1 = GetColourGradient(bg_colour, SHADE_DARK);
|
PixelColour c1 = GetColourGradient(bg_colour, SHADE_DARK);
|
||||||
uint8_t c2 = GetColourGradient(bg_colour, SHADE_LIGHTEST);
|
PixelColour c2 = GetColourGradient(bg_colour, SHADE_LIGHTEST);
|
||||||
|
|
||||||
int mid = CentreBounds(full.top, full.bottom, 0);
|
int mid = CentreBounds(full.top, full.bottom, 0);
|
||||||
GfxFillRect(full.left, mid - WidgetDimensions::scaled.bevel.bottom, full.right, mid - 1, c1);
|
GfxFillRect(full.left, mid - WidgetDimensions::scaled.bevel.bottom, full.right, mid - 1, c1);
|
||||||
|
|
|
@ -70,16 +70,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual int GetFontSize() const { return this->height; }
|
virtual int GetFontSize() const { return this->height; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Map a SpriteID to the key
|
|
||||||
* @param key The key to map to.
|
|
||||||
* @param sprite The sprite that is being mapped.
|
|
||||||
*/
|
|
||||||
virtual void SetUnicodeGlyph(char32_t key, SpriteID sprite) = 0;
|
|
||||||
|
|
||||||
/** Initialize the glyph map */
|
|
||||||
virtual void InitializeUnicodeGlyphMap() = 0;
|
|
||||||
|
|
||||||
/** Clear the font cache. */
|
/** Clear the font cache. */
|
||||||
virtual void ClearFontCache() = 0;
|
virtual void ClearFontCache() = 0;
|
||||||
|
|
||||||
|
@ -153,20 +143,6 @@ public:
|
||||||
virtual bool IsBuiltInFont() = 0;
|
virtual bool IsBuiltInFont() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Map a SpriteID to the font size and key */
|
|
||||||
inline void SetUnicodeGlyph(FontSize size, char32_t key, SpriteID sprite)
|
|
||||||
{
|
|
||||||
FontCache::Get(size)->SetUnicodeGlyph(key, sprite);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initialize the glyph map */
|
|
||||||
inline void InitializeUnicodeGlyphMap()
|
|
||||||
{
|
|
||||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
|
||||||
FontCache::Get(fs)->InitializeUnicodeGlyphMap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ClearFontCache(FontSizes fontsizes)
|
inline void ClearFontCache(FontSizes fontsizes)
|
||||||
{
|
{
|
||||||
for (FontSize fs : fontsizes) {
|
for (FontSize fs : fontsizes) {
|
||||||
|
@ -237,4 +213,8 @@ void UninitFontCache();
|
||||||
bool GetFontAAState();
|
bool GetFontAAState();
|
||||||
void SetFont(FontSize fontsize, const std::string &font, uint size);
|
void SetFont(FontSize fontsize, const std::string &font, uint size);
|
||||||
|
|
||||||
|
/* Implemented in spritefontcache.cpp */
|
||||||
|
void InitializeUnicodeGlyphMap();
|
||||||
|
void SetUnicodeGlyph(FontSize size, char32_t key, SpriteID sprite);
|
||||||
|
|
||||||
#endif /* FONTCACHE_H */
|
#endif /* FONTCACHE_H */
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "../stdafx.h"
|
#include "../stdafx.h"
|
||||||
#include "../fontcache.h"
|
#include "../fontcache.h"
|
||||||
#include "../gfx_layout.h"
|
#include "../gfx_layout.h"
|
||||||
|
#include "../string_func.h"
|
||||||
#include "../zoom_func.h"
|
#include "../zoom_func.h"
|
||||||
#include "spritefontcache.h"
|
#include "spritefontcache.h"
|
||||||
|
|
||||||
|
@ -31,41 +32,43 @@ static int ScaleFontTrad(int value)
|
||||||
return UnScaleByZoom(value * ZOOM_BASE, _font_zoom);
|
return UnScaleByZoom(value * ZOOM_BASE, _font_zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static std::array<std::unordered_map<char32_t, SpriteID>, FS_END> _char_maps{}; ///< Glyph map for each font size.
|
||||||
* Create a new sprite font cache.
|
|
||||||
* @param fs The font size to create the cache for.
|
|
||||||
*/
|
|
||||||
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs)
|
|
||||||
{
|
|
||||||
this->InitializeUnicodeGlyphMap();
|
|
||||||
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
|
|
||||||
this->ascender = (this->height - ScaleFontTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get SpriteID associated with a character.
|
* Get SpriteID associated with a character.
|
||||||
* @param key Character to find.
|
* @param key Character to find.
|
||||||
* @return SpriteID for character, or 0 if not present.
|
* @return SpriteID for character, or 0 if not present.
|
||||||
*/
|
*/
|
||||||
SpriteID SpriteFontCache::GetUnicodeGlyph(char32_t key)
|
static SpriteID GetUnicodeGlyph(FontSize fs, char32_t key)
|
||||||
{
|
{
|
||||||
const auto found = this->char_map.find(key);
|
auto found = _char_maps[fs].find(key);
|
||||||
if (found == std::end(this->char_map)) return 0;
|
if (found != std::end(_char_maps[fs])) return found->second;
|
||||||
return found->second;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteFontCache::SetUnicodeGlyph(char32_t key, SpriteID sprite)
|
/**
|
||||||
|
* Set the SpriteID for a unicode character.
|
||||||
|
* @param fs Font size to set.
|
||||||
|
* @param key Unicode character to set.
|
||||||
|
* @param sprite SpriteID of character.
|
||||||
|
*/
|
||||||
|
void SetUnicodeGlyph(FontSize fs, char32_t key, SpriteID sprite)
|
||||||
{
|
{
|
||||||
this->char_map[key] = sprite;
|
_char_maps[fs][key] = sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteFontCache::InitializeUnicodeGlyphMap()
|
/**
|
||||||
|
* Initialize the glyph map for a font size.
|
||||||
|
* This populates the glyph map with the baseset font sprites.
|
||||||
|
* @param fs Font size to initialize.
|
||||||
|
*/
|
||||||
|
void InitializeUnicodeGlyphMap(FontSize fs)
|
||||||
{
|
{
|
||||||
/* Clear out existing glyph map if it exists */
|
/* Clear out existing glyph map if it exists */
|
||||||
this->char_map.clear();
|
_char_maps[fs].clear();
|
||||||
|
|
||||||
SpriteID base;
|
SpriteID base;
|
||||||
switch (this->fs) {
|
switch (fs) {
|
||||||
default: NOT_REACHED();
|
default: NOT_REACHED();
|
||||||
case FS_MONO: // Use normal as default for mono spaced font
|
case FS_MONO: // Use normal as default for mono spaced font
|
||||||
case FS_NORMAL: base = SPR_ASCII_SPACE; break;
|
case FS_NORMAL: base = SPR_ASCII_SPACE; break;
|
||||||
|
@ -76,24 +79,45 @@ void SpriteFontCache::InitializeUnicodeGlyphMap()
|
||||||
for (uint i = ASCII_LETTERSTART; i < 256; i++) {
|
for (uint i = ASCII_LETTERSTART; i < 256; i++) {
|
||||||
SpriteID sprite = base + i - ASCII_LETTERSTART;
|
SpriteID sprite = base + i - ASCII_LETTERSTART;
|
||||||
if (!SpriteExists(sprite)) continue;
|
if (!SpriteExists(sprite)) continue;
|
||||||
this->SetUnicodeGlyph(i, sprite);
|
SetUnicodeGlyph(fs, i, sprite);
|
||||||
this->SetUnicodeGlyph(i + SCC_SPRITE_START, sprite);
|
SetUnicodeGlyph(fs, i + SCC_SPRITE_START, sprite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Modify map to move non-standard glyphs to a better unicode codepoint. */
|
||||||
for (const auto &unicode_map : _default_unicode_map) {
|
for (const auto &unicode_map : _default_unicode_map) {
|
||||||
uint8_t key = unicode_map.key;
|
uint8_t key = unicode_map.key;
|
||||||
if (key == CLRA) {
|
if (key == CLRA) {
|
||||||
/* Clear the glyph. This happens if the glyph at this code point
|
/* Clear the glyph. This happens if the glyph at this code point
|
||||||
* is non-standard and should be accessed by an SCC_xxx enum
|
* is non-standard and should be accessed by an SCC_xxx enum
|
||||||
* entry only. */
|
* entry only. */
|
||||||
this->SetUnicodeGlyph(unicode_map.code, 0);
|
SetUnicodeGlyph(fs, unicode_map.code, 0);
|
||||||
} else {
|
} else {
|
||||||
SpriteID sprite = base + key - ASCII_LETTERSTART;
|
SpriteID sprite = base + key - ASCII_LETTERSTART;
|
||||||
this->SetUnicodeGlyph(unicode_map.code, sprite);
|
SetUnicodeGlyph(fs, unicode_map.code, sprite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the glyph map.
|
||||||
|
*/
|
||||||
|
void InitializeUnicodeGlyphMap()
|
||||||
|
{
|
||||||
|
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||||
|
InitializeUnicodeGlyphMap(fs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new sprite font cache.
|
||||||
|
* @param fs The font size to create the cache for.
|
||||||
|
*/
|
||||||
|
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs)
|
||||||
|
{
|
||||||
|
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
|
||||||
|
this->ascender = (this->height - ScaleFontTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
void SpriteFontCache::ClearFontCache()
|
void SpriteFontCache::ClearFontCache()
|
||||||
{
|
{
|
||||||
Layouter::ResetFontCache(this->fs);
|
Layouter::ResetFontCache(this->fs);
|
||||||
|
@ -104,21 +128,21 @@ void SpriteFontCache::ClearFontCache()
|
||||||
const Sprite *SpriteFontCache::GetGlyph(GlyphID key)
|
const Sprite *SpriteFontCache::GetGlyph(GlyphID key)
|
||||||
{
|
{
|
||||||
SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH);
|
SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH);
|
||||||
if (sprite == 0) sprite = this->GetUnicodeGlyph('?');
|
if (sprite == 0) sprite = GetUnicodeGlyph(this->fs, '?');
|
||||||
return GetSprite(sprite, SpriteType::Font);
|
return GetSprite(sprite, SpriteType::Font);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint SpriteFontCache::GetGlyphWidth(GlyphID key)
|
uint SpriteFontCache::GetGlyphWidth(GlyphID key)
|
||||||
{
|
{
|
||||||
SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH);
|
SpriteID sprite = static_cast<SpriteID>(key & ~SPRITE_GLYPH);
|
||||||
if (sprite == 0) sprite = this->GetUnicodeGlyph('?');
|
if (sprite == 0) sprite = GetUnicodeGlyph(this->fs, '?');
|
||||||
return SpriteExists(sprite) ? GetSprite(sprite, SpriteType::Font)->width + ScaleFontTrad(this->fs != FS_NORMAL ? 1 : 0) : 0;
|
return SpriteExists(sprite) ? GetSprite(sprite, SpriteType::Font)->width + ScaleFontTrad(this->fs != FS_NORMAL ? 1 : 0) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlyphID SpriteFontCache::MapCharToGlyph(char32_t key, [[maybe_unused]] bool allow_fallback)
|
GlyphID SpriteFontCache::MapCharToGlyph(char32_t key, [[maybe_unused]] bool allow_fallback)
|
||||||
{
|
{
|
||||||
assert(IsPrintable(key));
|
assert(IsPrintable(key));
|
||||||
SpriteID sprite = this->GetUnicodeGlyph(key);
|
SpriteID sprite = GetUnicodeGlyph(this->fs, key);
|
||||||
if (sprite == 0) return 0;
|
if (sprite == 0) return 0;
|
||||||
return SPRITE_GLYPH | sprite;
|
return SPRITE_GLYPH | sprite;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,12 @@
|
||||||
#ifndef SPRITEFONTCACHE_H
|
#ifndef SPRITEFONTCACHE_H
|
||||||
#define SPRITEFONTCACHE_H
|
#define SPRITEFONTCACHE_H
|
||||||
|
|
||||||
#include "../string_func.h"
|
|
||||||
#include "../fontcache.h"
|
#include "../fontcache.h"
|
||||||
|
|
||||||
/** Font cache for fonts that are based on a freetype font. */
|
/** Font cache for fonts that are based on a freetype font. */
|
||||||
class SpriteFontCache : public FontCache {
|
class SpriteFontCache : public FontCache {
|
||||||
public:
|
public:
|
||||||
SpriteFontCache(FontSize fs);
|
SpriteFontCache(FontSize fs);
|
||||||
void SetUnicodeGlyph(char32_t key, SpriteID sprite) override;
|
|
||||||
void InitializeUnicodeGlyphMap() override;
|
|
||||||
void ClearFontCache() override;
|
void ClearFontCache() override;
|
||||||
const Sprite *GetGlyph(GlyphID key) override;
|
const Sprite *GetGlyph(GlyphID key) override;
|
||||||
uint GetGlyphWidth(GlyphID key) override;
|
uint GetGlyphWidth(GlyphID key) override;
|
||||||
|
@ -26,10 +23,6 @@ public:
|
||||||
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
|
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
|
||||||
std::string GetFontName() override { return "sprite"; }
|
std::string GetFontName() override { return "sprite"; }
|
||||||
bool IsBuiltInFont() override { return true; }
|
bool IsBuiltInFont() override { return true; }
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<char32_t, SpriteID> char_map{}; ///< Mapping of characters to sprite IDs.
|
|
||||||
SpriteID GetUnicodeGlyph(char32_t key);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* SPRITEFONTCACHE_H */
|
#endif /* SPRITEFONTCACHE_H */
|
||||||
|
|
|
@ -46,8 +46,6 @@ public:
|
||||||
TrueTypeFontCache(FontSize fs, int pixels);
|
TrueTypeFontCache(FontSize fs, int pixels);
|
||||||
virtual ~TrueTypeFontCache();
|
virtual ~TrueTypeFontCache();
|
||||||
int GetFontSize() const override { return this->used_size; }
|
int GetFontSize() const override { return this->used_size; }
|
||||||
void SetUnicodeGlyph(char32_t key, SpriteID sprite) override { this->parent->SetUnicodeGlyph(key, sprite); }
|
|
||||||
void InitializeUnicodeGlyphMap() override { this->parent->InitializeUnicodeGlyphMap(); }
|
|
||||||
const Sprite *GetGlyph(GlyphID key) override;
|
const Sprite *GetGlyph(GlyphID key) override;
|
||||||
void ClearFontCache() override;
|
void ClearFontCache() override;
|
||||||
uint GetGlyphWidth(GlyphID key) override;
|
uint GetGlyphWidth(GlyphID key) override;
|
||||||
|
|
|
@ -867,9 +867,9 @@ struct FrametimeGraphWindow : Window {
|
||||||
const int x_max = r.right;
|
const int x_max = r.right;
|
||||||
const int y_zero = r.top + (int)this->graph_size.height;
|
const int y_zero = r.top + (int)this->graph_size.height;
|
||||||
const int y_max = r.top;
|
const int y_max = r.top;
|
||||||
const int c_grid = PC_DARK_GREY;
|
const PixelColour c_grid = PC_DARK_GREY;
|
||||||
const int c_lines = PC_BLACK;
|
const PixelColour c_lines = PC_BLACK;
|
||||||
const int c_peak = PC_DARK_RED;
|
const PixelColour c_peak = PC_DARK_RED;
|
||||||
|
|
||||||
const TimingMeasurement draw_horz_scale = (TimingMeasurement)this->horizontal_scale * TIMESTAMP_PRECISION / 2;
|
const TimingMeasurement draw_horz_scale = (TimingMeasurement)this->horizontal_scale * TIMESTAMP_PRECISION / 2;
|
||||||
const TimingMeasurement draw_vert_scale = (TimingMeasurement)this->vertical_scale;
|
const TimingMeasurement draw_vert_scale = (TimingMeasurement)this->vertical_scale;
|
||||||
|
@ -959,7 +959,7 @@ struct FrametimeGraphWindow : Window {
|
||||||
|
|
||||||
/* If the peak value is significantly larger than the average, mark and label it */
|
/* If the peak value is significantly larger than the average, mark and label it */
|
||||||
if (points_drawn > 0 && peak_value > TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
|
if (points_drawn > 0 && peak_value > TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
|
||||||
TextColour tc_peak = (TextColour)(TC_IS_PALETTE_COLOUR | c_peak);
|
TextColour tc_peak = c_peak.ToTextColour();
|
||||||
GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
|
GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
|
||||||
uint64_t value = peak_value * 1000 / TIMESTAMP_PRECISION;
|
uint64_t value = peak_value * 1000 / TIMESTAMP_PRECISION;
|
||||||
int label_y = std::max(y_max, peak_point.y - GetCharacterHeight(FS_SMALL));
|
int label_y = std::max(y_max, peak_point.y - GetCharacterHeight(FS_SMALL));
|
||||||
|
|
35
src/gfx.cpp
35
src/gfx.cpp
|
@ -113,7 +113,7 @@ void GfxScroll(int left, int top, int width, int height, int xo, int yo)
|
||||||
* FILLRECT_CHECKER: Like FILLRECT_OPAQUE, but only draw every second pixel (used to grey out things)
|
* FILLRECT_CHECKER: Like FILLRECT_OPAQUE, but only draw every second pixel (used to grey out things)
|
||||||
* FILLRECT_RECOLOUR: Apply a recolour sprite to every pixel in the rectangle currently on screen
|
* FILLRECT_RECOLOUR: Apply a recolour sprite to every pixel in the rectangle currently on screen
|
||||||
*/
|
*/
|
||||||
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
|
void GfxFillRect(int left, int top, int right, int bottom, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode)
|
||||||
{
|
{
|
||||||
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
||||||
const DrawPixelInfo *dpi = _cur_dpi;
|
const DrawPixelInfo *dpi = _cur_dpi;
|
||||||
|
@ -142,17 +142,18 @@ void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectM
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
default: // FILLRECT_OPAQUE
|
default: // FILLRECT_OPAQUE
|
||||||
blitter->DrawRect(dst, right, bottom, (uint8_t)colour);
|
blitter->DrawRect(dst, right, bottom, std::get<PixelColour>(colour));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FILLRECT_RECOLOUR:
|
case FILLRECT_RECOLOUR:
|
||||||
blitter->DrawColourMappingRect(dst, right, bottom, GB(colour, 0, PALETTE_WIDTH));
|
blitter->DrawColourMappingRect(dst, right, bottom, GB(std::get<PaletteID>(colour), 0, PALETTE_WIDTH));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FILLRECT_CHECKER: {
|
case FILLRECT_CHECKER: {
|
||||||
uint8_t bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
|
uint8_t bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
|
||||||
|
PixelColour pc = std::get<PixelColour>(colour);
|
||||||
do {
|
do {
|
||||||
for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8_t)colour);
|
for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, pc);
|
||||||
dst = blitter->MoveTo(dst, 0, 1);
|
dst = blitter->MoveTo(dst, 0, 1);
|
||||||
} while (--bottom > 0);
|
} while (--bottom > 0);
|
||||||
break;
|
break;
|
||||||
|
@ -209,7 +210,7 @@ static std::vector<LineSegment> MakePolygonSegments(std::span<const Point> shape
|
||||||
* FILLRECT_CHECKER: Fill every other pixel with the specified colour, in a checkerboard pattern.
|
* FILLRECT_CHECKER: Fill every other pixel with the specified colour, in a checkerboard pattern.
|
||||||
* FILLRECT_RECOLOUR: Apply a recolour sprite to every pixel in the polygon.
|
* FILLRECT_RECOLOUR: Apply a recolour sprite to every pixel in the polygon.
|
||||||
*/
|
*/
|
||||||
void GfxFillPolygon(std::span<const Point> shape, int colour, FillRectMode mode)
|
void GfxFillPolygon(std::span<const Point> shape, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode)
|
||||||
{
|
{
|
||||||
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
||||||
const DrawPixelInfo *dpi = _cur_dpi;
|
const DrawPixelInfo *dpi = _cur_dpi;
|
||||||
|
@ -278,18 +279,20 @@ void GfxFillPolygon(std::span<const Point> shape, int colour, FillRectMode mode)
|
||||||
void *dst = blitter->MoveTo(dpi->dst_ptr, x1, y);
|
void *dst = blitter->MoveTo(dpi->dst_ptr, x1, y);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
default: // FILLRECT_OPAQUE
|
default: // FILLRECT_OPAQUE
|
||||||
blitter->DrawRect(dst, x2 - x1, 1, (uint8_t)colour);
|
blitter->DrawRect(dst, x2 - x1, 1, std::get<PixelColour>(colour));
|
||||||
break;
|
break;
|
||||||
case FILLRECT_RECOLOUR:
|
case FILLRECT_RECOLOUR:
|
||||||
blitter->DrawColourMappingRect(dst, x2 - x1, 1, GB(colour, 0, PALETTE_WIDTH));
|
blitter->DrawColourMappingRect(dst, x2 - x1, 1, GB(std::get<PaletteID>(colour), 0, PALETTE_WIDTH));
|
||||||
break;
|
break;
|
||||||
case FILLRECT_CHECKER:
|
case FILLRECT_CHECKER: {
|
||||||
/* Fill every other pixel, offset such that the sum of filled pixels' X and Y coordinates is odd.
|
/* Fill every other pixel, offset such that the sum of filled pixels' X and Y coordinates is odd.
|
||||||
* This creates a checkerboard effect. */
|
* This creates a checkerboard effect. */
|
||||||
|
PixelColour pc = std::get<PixelColour>(colour);
|
||||||
for (int x = (x1 + y) & 1; x < x2 - x1; x += 2) {
|
for (int x = (x1 + y) & 1; x < x2 - x1; x += 2) {
|
||||||
blitter->SetPixel(dst, x, 0, (uint8_t)colour);
|
blitter->SetPixel(dst, x, 0, pc);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +315,7 @@ void GfxFillPolygon(std::span<const Point> shape, int colour, FillRectMode mode)
|
||||||
* @param width Width of the line.
|
* @param width Width of the line.
|
||||||
* @param dash Length of dashes for dashed lines. 0 means solid line.
|
* @param dash Length of dashes for dashed lines. 0 means solid line.
|
||||||
*/
|
*/
|
||||||
static inline void GfxDoDrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash = 0)
|
static inline void GfxDoDrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash = 0)
|
||||||
{
|
{
|
||||||
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
||||||
|
|
||||||
|
@ -385,7 +388,7 @@ static inline bool GfxPreprocessLine(DrawPixelInfo *dpi, int &x, int &y, int &x2
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfxDrawLine(int x, int y, int x2, int y2, int colour, int width, int dash)
|
void GfxDrawLine(int x, int y, int x2, int y2, PixelColour colour, int width, int dash)
|
||||||
{
|
{
|
||||||
DrawPixelInfo *dpi = _cur_dpi;
|
DrawPixelInfo *dpi = _cur_dpi;
|
||||||
if (GfxPreprocessLine(dpi, x, y, x2, y2, width)) {
|
if (GfxPreprocessLine(dpi, x, y, x2, y2, width)) {
|
||||||
|
@ -393,7 +396,7 @@ void GfxDrawLine(int x, int y, int x2, int y2, int colour, int width, int dash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfxDrawLineUnscaled(int x, int y, int x2, int y2, int colour)
|
void GfxDrawLineUnscaled(int x, int y, int x2, int y2, PixelColour colour)
|
||||||
{
|
{
|
||||||
DrawPixelInfo *dpi = _cur_dpi;
|
DrawPixelInfo *dpi = _cur_dpi;
|
||||||
if (GfxPreprocessLine(dpi, x, y, x2, y2, 1)) {
|
if (GfxPreprocessLine(dpi, x, y, x2, y2, 1)) {
|
||||||
|
@ -434,7 +437,7 @@ void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
|
||||||
* ....V.
|
* ....V.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const uint8_t colour = PC_WHITE;
|
static constexpr PixelColour colour = PC_WHITE;
|
||||||
|
|
||||||
GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
|
GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
|
||||||
GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
|
GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
|
||||||
|
@ -455,7 +458,7 @@ void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
|
||||||
* @param width Width of the outline.
|
* @param width Width of the outline.
|
||||||
* @param dash Length of dashes for dashed lines. 0 means solid lines.
|
* @param dash Length of dashes for dashed lines. 0 means solid lines.
|
||||||
*/
|
*/
|
||||||
void DrawRectOutline(const Rect &r, int colour, int width, int dash)
|
void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
|
||||||
{
|
{
|
||||||
GfxDrawLine(r.left, r.top, r.right, r.top, colour, width, dash);
|
GfxDrawLine(r.left, r.top, r.right, r.top, colour, width, dash);
|
||||||
GfxDrawLine(r.left, r.top, r.left, r.bottom, colour, width, dash);
|
GfxDrawLine(r.left, r.top, r.left, r.bottom, colour, width, dash);
|
||||||
|
@ -477,7 +480,7 @@ static void SetColourRemap(TextColour colour)
|
||||||
bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0;
|
bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0;
|
||||||
colour &= ~(TC_NO_SHADE | TC_IS_PALETTE_COLOUR | TC_FORCED);
|
colour &= ~(TC_NO_SHADE | TC_IS_PALETTE_COLOUR | TC_FORCED);
|
||||||
|
|
||||||
_string_colourremap[1] = raw_colour ? (uint8_t)colour : _string_colourmap[colour];
|
_string_colourremap[1] = raw_colour ? (uint8_t)colour : _string_colourmap[colour].p;
|
||||||
_string_colourremap[2] = no_shade ? 0 : 1;
|
_string_colourremap[2] = no_shade ? 0 : 1;
|
||||||
_colour_remap_ptr = _string_colourremap;
|
_colour_remap_ptr = _string_colourremap;
|
||||||
}
|
}
|
||||||
|
@ -637,7 +640,7 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (underline) {
|
if (underline) {
|
||||||
GfxFillRect(left, y + h, right, y + h + WidgetDimensions::scaled.bevel.top - 1, _string_colourremap[1]);
|
GfxFillRect(left, y + h, right, y + h + WidgetDimensions::scaled.bevel.top - 1, PixelColour{_string_colourremap[1]});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (align & SA_HOR_MASK) == SA_RIGHT ? left : right;
|
return (align & SA_HOR_MASK) == SA_RIGHT ? left : right;
|
||||||
|
|
|
@ -103,11 +103,11 @@ bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, s
|
||||||
|
|
||||||
void DrawCharCentered(char32_t c, const Rect &r, TextColour colour);
|
void DrawCharCentered(char32_t c, const Rect &r, TextColour colour);
|
||||||
|
|
||||||
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode = FILLRECT_OPAQUE);
|
void GfxFillRect(int left, int top, int right, int bottom, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode = FILLRECT_OPAQUE);
|
||||||
void GfxFillPolygon(std::span<const Point> shape, int colour, FillRectMode mode = FILLRECT_OPAQUE);
|
void GfxFillPolygon(std::span<const Point> shape, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode = FILLRECT_OPAQUE);
|
||||||
void GfxDrawLine(int left, int top, int right, int bottom, int colour, int width = 1, int dash = 0);
|
void GfxDrawLine(int left, int top, int right, int bottom, PixelColour colour, int width = 1, int dash = 0);
|
||||||
void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3);
|
void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3);
|
||||||
void DrawRectOutline(const Rect &r, int colour, int width = 1, int dash = 0);
|
void DrawRectOutline(const Rect &r, PixelColour colour, int width = 1, int dash = 0);
|
||||||
|
|
||||||
/* Versions of DrawString/DrawStringMultiLine that accept a Rect instead of separate left, right, top and bottom parameters. */
|
/* Versions of DrawString/DrawStringMultiLine that accept a Rect instead of separate left, right, top and bottom parameters. */
|
||||||
inline int DrawString(const Rect &r, std::string_view str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL)
|
inline int DrawString(const Rect &r, std::string_view str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL)
|
||||||
|
@ -135,7 +135,7 @@ inline bool DrawStringMultiLineWithClipping(const Rect &r, std::string_view str,
|
||||||
return DrawStringMultiLineWithClipping(r.left, r.right, r.top, r.bottom, str, colour, align, underline, fontsize);
|
return DrawStringMultiLineWithClipping(r.left, r.right, r.top, r.bottom, str, colour, align, underline, fontsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void GfxFillRect(const Rect &r, int colour, FillRectMode mode = FILLRECT_OPAQUE)
|
inline void GfxFillRect(const Rect &r, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode = FILLRECT_OPAQUE)
|
||||||
{
|
{
|
||||||
GfxFillRect(r.left, r.top, r.right, r.bottom, colour, mode);
|
GfxFillRect(r.left, r.top, r.right, r.bottom, colour, mode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,6 @@ using Colour = std::conditional_t<std::endian::native == std::endian::little, Co
|
||||||
|
|
||||||
static_assert(sizeof(Colour) == sizeof(uint32_t));
|
static_assert(sizeof(Colour) == sizeof(uint32_t));
|
||||||
|
|
||||||
|
|
||||||
/** Available font sizes */
|
/** Available font sizes */
|
||||||
enum FontSize : uint8_t {
|
enum FontSize : uint8_t {
|
||||||
FS_NORMAL, ///< Index of the normal font in the font tables.
|
FS_NORMAL, ///< Index of the normal font in the font tables.
|
||||||
|
@ -403,4 +402,14 @@ enum StringAlignment : uint8_t {
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_AS_BIT_SET(StringAlignment)
|
DECLARE_ENUM_AS_BIT_SET(StringAlignment)
|
||||||
|
|
||||||
|
/** Colour for pixel/line drawing. */
|
||||||
|
struct PixelColour {
|
||||||
|
uint8_t p; ///< Palette index.
|
||||||
|
|
||||||
|
constexpr PixelColour() : p(0) {}
|
||||||
|
explicit constexpr PixelColour(uint8_t p) : p(p) {}
|
||||||
|
|
||||||
|
constexpr inline TextColour ToTextColour() const { return static_cast<TextColour>(this->p) | TC_IS_PALETTE_COLOUR; }
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* GFX_TYPE_H */
|
#endif /* GFX_TYPE_H */
|
||||||
|
|
|
@ -165,11 +165,11 @@ struct ValuesInterval {
|
||||||
struct BaseGraphWindow : Window {
|
struct BaseGraphWindow : Window {
|
||||||
protected:
|
protected:
|
||||||
static const int GRAPH_MAX_DATASETS = 64;
|
static const int GRAPH_MAX_DATASETS = 64;
|
||||||
static const int GRAPH_BASE_COLOUR = GREY_SCALE(2);
|
static constexpr PixelColour GRAPH_BASE_COLOUR = GREY_SCALE(2);
|
||||||
static const int GRAPH_GRID_COLOUR = GREY_SCALE(3);
|
static constexpr PixelColour GRAPH_GRID_COLOUR = GREY_SCALE(3);
|
||||||
static const int GRAPH_AXIS_LINE_COLOUR = GREY_SCALE(1);
|
static constexpr PixelColour GRAPH_AXIS_LINE_COLOUR = GREY_SCALE(1);
|
||||||
static const int GRAPH_ZERO_LINE_COLOUR = GREY_SCALE(8);
|
static constexpr PixelColour GRAPH_ZERO_LINE_COLOUR = GREY_SCALE(8);
|
||||||
static const int GRAPH_YEAR_LINE_COLOUR = GREY_SCALE(5);
|
static constexpr PixelColour GRAPH_YEAR_LINE_COLOUR = GREY_SCALE(5);
|
||||||
static const int GRAPH_NUM_MONTHS = 24; ///< Number of months displayed in the graph.
|
static const int GRAPH_NUM_MONTHS = 24; ///< Number of months displayed in the graph.
|
||||||
static const int GRAPH_PAYMENT_RATE_STEPS = 20; ///< Number of steps on Payment rate graph.
|
static const int GRAPH_PAYMENT_RATE_STEPS = 20; ///< Number of steps on Payment rate graph.
|
||||||
static const int PAYMENT_GRAPH_X_STEP_DAYS = 10; ///< X-axis step label for cargo payment rates "Days in transit".
|
static const int PAYMENT_GRAPH_X_STEP_DAYS = 10; ///< X-axis step label for cargo payment rates "Days in transit".
|
||||||
|
@ -204,7 +204,7 @@ protected:
|
||||||
|
|
||||||
struct DataSet {
|
struct DataSet {
|
||||||
std::array<OverflowSafeInt64, GRAPH_NUM_MONTHS> values;
|
std::array<OverflowSafeInt64, GRAPH_NUM_MONTHS> values;
|
||||||
uint8_t colour;
|
PixelColour colour;
|
||||||
uint8_t exclude_bit;
|
uint8_t exclude_bit;
|
||||||
uint8_t range_bit;
|
uint8_t range_bit;
|
||||||
uint8_t dash;
|
uint8_t dash;
|
||||||
|
@ -398,7 +398,7 @@ protected:
|
||||||
|
|
||||||
/* Draw the grid lines. */
|
/* Draw the grid lines. */
|
||||||
int gridline_width = WidgetDimensions::scaled.bevel.top;
|
int gridline_width = WidgetDimensions::scaled.bevel.top;
|
||||||
int grid_colour = GRAPH_GRID_COLOUR;
|
PixelColour grid_colour = GRAPH_GRID_COLOUR;
|
||||||
|
|
||||||
/* Don't draw the first line, as that's where the axis will be. */
|
/* Don't draw the first line, as that's where the axis will be. */
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
|
@ -524,7 +524,7 @@ protected:
|
||||||
uint pointoffs1 = pointwidth / 2;
|
uint pointoffs1 = pointwidth / 2;
|
||||||
uint pointoffs2 = pointwidth - pointoffs1;
|
uint pointoffs2 = pointwidth - pointoffs1;
|
||||||
|
|
||||||
auto draw_dataset = [&](const DataSet &dataset, uint8_t colour) {
|
auto draw_dataset = [&](const DataSet &dataset, PixelColour colour) {
|
||||||
if (HasBit(this->excluded_data, dataset.exclude_bit)) return;
|
if (HasBit(this->excluded_data, dataset.exclude_bit)) return;
|
||||||
if (HasBit(this->excluded_range, dataset.range_bit)) return;
|
if (HasBit(this->excluded_range, dataset.range_bit)) return;
|
||||||
|
|
||||||
|
@ -1222,7 +1222,7 @@ struct BaseCargoGraphWindow : BaseGraphWindow {
|
||||||
/* Cargo-colour box with outline */
|
/* Cargo-colour box with outline */
|
||||||
const Rect cargo = text.WithWidth(this->legend_width, rtl);
|
const Rect cargo = text.WithWidth(this->legend_width, rtl);
|
||||||
GfxFillRect(cargo, PC_BLACK);
|
GfxFillRect(cargo, PC_BLACK);
|
||||||
uint8_t pc = cs->legend_colour;
|
PixelColour pc = cs->legend_colour;
|
||||||
if (this->highlight_data == cs->Index()) pc = this->highlight_state ? PC_WHITE : PC_BLACK;
|
if (this->highlight_data == cs->Index()) pc = this->highlight_state ? PC_WHITE : PC_BLACK;
|
||||||
GfxFillRect(cargo.Shrink(WidgetDimensions::scaled.bevel), pc);
|
GfxFillRect(cargo.Shrink(WidgetDimensions::scaled.bevel), pc);
|
||||||
|
|
||||||
|
@ -1499,8 +1499,8 @@ struct PerformanceRatingDetailWindow : Window {
|
||||||
ScoreID score_type = (ScoreID)(widget - WID_PRD_SCORE_FIRST);
|
ScoreID score_type = (ScoreID)(widget - WID_PRD_SCORE_FIRST);
|
||||||
|
|
||||||
/* The colours used to show how the progress is going */
|
/* The colours used to show how the progress is going */
|
||||||
int colour_done = GetColourGradient(COLOUR_GREEN, SHADE_NORMAL);
|
PixelColour colour_done = GetColourGradient(COLOUR_GREEN, SHADE_NORMAL);
|
||||||
int colour_notdone = GetColourGradient(COLOUR_RED, SHADE_NORMAL);
|
PixelColour colour_notdone = GetColourGradient(COLOUR_RED, SHADE_NORMAL);
|
||||||
|
|
||||||
/* Draw all the score parts */
|
/* Draw all the score parts */
|
||||||
int64_t val = _score_part[company][score_type];
|
int64_t val = _score_part[company][score_type];
|
||||||
|
|
|
@ -305,7 +305,7 @@ private:
|
||||||
|
|
||||||
const int offset = (rtl ? -(int)this->column_size[VGC_FOLD].width : (int)this->column_size[VGC_FOLD].width) / 2;
|
const int offset = (rtl ? -(int)this->column_size[VGC_FOLD].width : (int)this->column_size[VGC_FOLD].width) / 2;
|
||||||
const int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
|
const int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
|
||||||
const int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
|
const PixelColour linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
|
||||||
|
|
||||||
if (indent > 0) {
|
if (indent > 0) {
|
||||||
/* Draw tree continuation lines. */
|
/* Draw tree continuation lines. */
|
||||||
|
|
|
@ -1986,8 +1986,8 @@ struct CargoesField {
|
||||||
static Dimension cargo_space;
|
static Dimension cargo_space;
|
||||||
static Dimension cargo_stub;
|
static Dimension cargo_stub;
|
||||||
|
|
||||||
static const int INDUSTRY_LINE_COLOUR;
|
static const PixelColour INDUSTRY_LINE_COLOUR;
|
||||||
static const int CARGO_LINE_COLOUR;
|
static const PixelColour CARGO_LINE_COLOUR;
|
||||||
|
|
||||||
static int small_height, normal_height;
|
static int small_height, normal_height;
|
||||||
static int cargo_field_width;
|
static int cargo_field_width;
|
||||||
|
@ -2395,8 +2395,8 @@ int CargoesField::vert_inter_industry_space; ///< Amount of space between two in
|
||||||
|
|
||||||
int CargoesField::blob_distance; ///< Distance of the industry legend colour from the edge of the industry box.
|
int CargoesField::blob_distance; ///< Distance of the industry legend colour from the edge of the industry box.
|
||||||
|
|
||||||
const int CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW; ///< Line colour of the industry type box.
|
const PixelColour CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW; ///< Line colour of the industry type box.
|
||||||
const int CargoesField::CARGO_LINE_COLOUR = PC_YELLOW; ///< Line colour around the cargo.
|
const PixelColour CargoesField::CARGO_LINE_COLOUR = PC_YELLOW; ///< Line colour around the cargo.
|
||||||
|
|
||||||
/** A single row of #CargoesField. */
|
/** A single row of #CargoesField. */
|
||||||
struct CargoesRow {
|
struct CargoesRow {
|
||||||
|
|
|
@ -119,7 +119,7 @@ struct IndustrySpec {
|
||||||
IndustryLifeTypes life_type; ///< This is also known as Industry production flag, in newgrf specs
|
IndustryLifeTypes life_type; ///< This is also known as Industry production flag, in newgrf specs
|
||||||
LandscapeTypes climate_availability; ///< Bitmask, giving landscape enums as bit position
|
LandscapeTypes climate_availability; ///< Bitmask, giving landscape enums as bit position
|
||||||
IndustryBehaviours behaviour; ///< How this industry will behave, and how others entities can use it
|
IndustryBehaviours behaviour; ///< How this industry will behave, and how others entities can use it
|
||||||
uint8_t map_colour; ///< colour used for the small map
|
PixelColour map_colour; ///< colour used for the small map
|
||||||
StringID name; ///< Displayed name of the industry
|
StringID name; ///< Displayed name of the industry
|
||||||
StringID new_industry_text; ///< Message appearing when the industry is built
|
StringID new_industry_text; ///< Message appearing when the industry is built
|
||||||
StringID closure_text; ///< Message appearing when the industry closes
|
StringID closure_text; ///< Message appearing when the industry closes
|
||||||
|
|
|
@ -30,26 +30,26 @@
|
||||||
* Colours for the various "load" states of links. Ordered from "unused" to
|
* Colours for the various "load" states of links. Ordered from "unused" to
|
||||||
* "overloaded".
|
* "overloaded".
|
||||||
*/
|
*/
|
||||||
const uint8_t LinkGraphOverlay::LINK_COLOURS[][12] = {
|
const PixelColour LinkGraphOverlay::LINK_COLOURS[][12] = {
|
||||||
{
|
{
|
||||||
0x0f, 0xd1, 0xd0, 0x57,
|
PixelColour{0x0f}, PixelColour{0xd1}, PixelColour{0xd0}, PixelColour{0x57},
|
||||||
0x55, 0x53, 0xbf, 0xbd,
|
PixelColour{0x55}, PixelColour{0x53}, PixelColour{0xbf}, PixelColour{0xbd},
|
||||||
0xba, 0xb9, 0xb7, 0xb5
|
PixelColour{0xba}, PixelColour{0xb9}, PixelColour{0xb7}, PixelColour{0xb5}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
0x0f, 0xd1, 0xd0, 0x57,
|
PixelColour{0x0f}, PixelColour{0xd1}, PixelColour{0xd0}, PixelColour{0x57},
|
||||||
0x55, 0x53, 0x96, 0x95,
|
PixelColour{0x55}, PixelColour{0x53}, PixelColour{0x96}, PixelColour{0x95},
|
||||||
0x94, 0x93, 0x92, 0x91
|
PixelColour{0x94}, PixelColour{0x93}, PixelColour{0x92}, PixelColour{0x91}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
0x0f, 0x0b, 0x09, 0x07,
|
PixelColour{0x0f}, PixelColour{0x0b}, PixelColour{0x09}, PixelColour{0x07},
|
||||||
0x05, 0x03, 0xbf, 0xbd,
|
PixelColour{0x05}, PixelColour{0x03}, PixelColour{0xbf}, PixelColour{0xbd},
|
||||||
0xba, 0xb9, 0xb7, 0xb5
|
PixelColour{0xba}, PixelColour{0xb9}, PixelColour{0xb7}, PixelColour{0xb5}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
0x0f, 0x0b, 0x0a, 0x09,
|
PixelColour{0x0f}, PixelColour{0x0b}, PixelColour{0x0a}, PixelColour{0x09},
|
||||||
0x08, 0x07, 0x06, 0x05,
|
PixelColour{0x08}, PixelColour{0x07}, PixelColour{0x06}, PixelColour{0x05},
|
||||||
0x04, 0x03, 0x02, 0x01
|
PixelColour{0x04}, PixelColour{0x03}, PixelColour{0x02}, PixelColour{0x01}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ void LinkGraphOverlay::DrawLinks(const DrawPixelInfo *dpi) const
|
||||||
void LinkGraphOverlay::DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const
|
void LinkGraphOverlay::DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const
|
||||||
{
|
{
|
||||||
uint usage_or_plan = std::min(cargo.capacity * 2 + 1, cargo.Usage());
|
uint usage_or_plan = std::min(cargo.capacity * 2 + 1, cargo.Usage());
|
||||||
int colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][usage_or_plan * lengthof(LinkGraphOverlay::LINK_COLOURS[0]) / (cargo.capacity * 2 + 2)];
|
PixelColour colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][usage_or_plan * lengthof(LinkGraphOverlay::LINK_COLOURS[0]) / (cargo.capacity * 2 + 2)];
|
||||||
int width = ScaleGUITrad(this->scale);
|
int width = ScaleGUITrad(this->scale);
|
||||||
int dash = cargo.shared ? width * 4 : 0;
|
int dash = cargo.shared ? width * 4 : 0;
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ void LinkGraphOverlay::DrawStationDots(const DrawPixelInfo *dpi) const
|
||||||
* @param colour Colour with which the vertex will be filled.
|
* @param colour Colour with which the vertex will be filled.
|
||||||
* @param border_colour Colour for the border of the vertex.
|
* @param border_colour Colour for the border of the vertex.
|
||||||
*/
|
*/
|
||||||
/* static */ void LinkGraphOverlay::DrawVertex(int x, int y, int size, int colour, int border_colour)
|
/* static */ void LinkGraphOverlay::DrawVertex(int x, int y, int size, PixelColour colour, PixelColour border_colour)
|
||||||
{
|
{
|
||||||
size--;
|
size--;
|
||||||
int w1 = size / 2;
|
int w1 = size / 2;
|
||||||
|
@ -607,7 +607,7 @@ void LinkGraphLegendWindow::DrawWidget(const Rect &r, WidgetID widget) const
|
||||||
DrawCompanyIcon(cid, CentreBounds(br.left, br.right, sprite_size.width), CentreBounds(br.top, br.bottom, sprite_size.height));
|
DrawCompanyIcon(cid, CentreBounds(br.left, br.right, sprite_size.width), CentreBounds(br.top, br.bottom, sprite_size.height));
|
||||||
}
|
}
|
||||||
if (IsInsideMM(widget, WID_LGL_SATURATION_FIRST, WID_LGL_SATURATION_LAST + 1)) {
|
if (IsInsideMM(widget, WID_LGL_SATURATION_FIRST, WID_LGL_SATURATION_LAST + 1)) {
|
||||||
uint8_t colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][widget - WID_LGL_SATURATION_FIRST];
|
PixelColour colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][widget - WID_LGL_SATURATION_FIRST];
|
||||||
GfxFillRect(br, colour);
|
GfxFillRect(br, colour);
|
||||||
StringID str = STR_NULL;
|
StringID str = STR_NULL;
|
||||||
if (widget == WID_LGL_SATURATION_FIRST) {
|
if (widget == WID_LGL_SATURATION_FIRST) {
|
||||||
|
|
|
@ -44,7 +44,7 @@ public:
|
||||||
typedef std::map<StationID, StationLinkMap> LinkMap;
|
typedef std::map<StationID, StationLinkMap> LinkMap;
|
||||||
typedef std::vector<std::pair<StationID, uint> > StationSupplyList;
|
typedef std::vector<std::pair<StationID, uint> > StationSupplyList;
|
||||||
|
|
||||||
static const uint8_t LINK_COLOURS[][12];
|
static const PixelColour LINK_COLOURS[][12];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a link graph overlay for the specified window.
|
* Create a link graph overlay for the specified window.
|
||||||
|
@ -95,7 +95,7 @@ protected:
|
||||||
void RebuildCache();
|
void RebuildCache();
|
||||||
|
|
||||||
static void AddStats(CargoType new_cargo, uint new_cap, uint new_usg, uint new_flow, uint32_t time, bool new_shared, LinkProperties &cargo);
|
static void AddStats(CargoType new_cargo, uint new_cap, uint new_usg, uint new_flow, uint32_t time, bool new_shared, LinkProperties &cargo);
|
||||||
static void DrawVertex(int x, int y, int size, int colour, int border_colour);
|
static void DrawVertex(int x, int y, int size, PixelColour colour, PixelColour border_colour);
|
||||||
};
|
};
|
||||||
|
|
||||||
void ShowLinkGraphLegend();
|
void ShowLinkGraphLegend();
|
||||||
|
|
|
@ -42,13 +42,13 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig) :
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erase all flows originating at a specific node.
|
* Erase all flows originating at a specific station.
|
||||||
* @param from Node to erase flows for.
|
* @param from StationID to erase flows for.
|
||||||
*/
|
*/
|
||||||
void LinkGraphJob::EraseFlows(NodeID from)
|
void LinkGraphJob::EraseFlows(StationID from)
|
||||||
{
|
{
|
||||||
for (NodeID node_id = 0; node_id < this->Size(); ++node_id) {
|
for (NodeID node_id = 0; node_id < this->Size(); ++node_id) {
|
||||||
(*this)[node_id].flows.erase(StationID{from});
|
(*this)[node_id].flows.erase(from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ LinkGraphJob::~LinkGraphJob()
|
||||||
/* The station can have been deleted. Remove all flows originating from it then. */
|
/* The station can have been deleted. Remove all flows originating from it then. */
|
||||||
Station *st = Station::GetIfValid(from.base.station);
|
Station *st = Station::GetIfValid(from.base.station);
|
||||||
if (st == nullptr) {
|
if (st == nullptr) {
|
||||||
this->EraseFlows(node_id);
|
this->EraseFlows(from.base.station);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ LinkGraphJob::~LinkGraphJob()
|
||||||
* sure that everything is still consistent or ignore it otherwise. */
|
* sure that everything is still consistent or ignore it otherwise. */
|
||||||
GoodsEntry &ge = st->goods[this->Cargo()];
|
GoodsEntry &ge = st->goods[this->Cargo()];
|
||||||
if (ge.link_graph != this->link_graph.index || ge.node != node_id) {
|
if (ge.link_graph != this->link_graph.index || ge.node != node_id) {
|
||||||
this->EraseFlows(node_id);
|
this->EraseFlows(from.base.station);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ LinkGraphJob::~LinkGraphJob()
|
||||||
/* Delete old flows for source stations which have been deleted
|
/* Delete old flows for source stations which have been deleted
|
||||||
* from the new flows. This avoids flow cycles between old and
|
* from the new flows. This avoids flow cycles between old and
|
||||||
* new flows. */
|
* new flows. */
|
||||||
while (!erased.IsEmpty()) geflows.erase(StationID{erased.Pop()});
|
while (!erased.IsEmpty()) geflows.erase(erased.Pop());
|
||||||
} else if ((*lg)[node_id][dest_id].last_unrestricted_update == EconomyTime::INVALID_DATE) {
|
} else if ((*lg)[node_id][dest_id].last_unrestricted_update == EconomyTime::INVALID_DATE) {
|
||||||
/* Edge is fully restricted. */
|
/* Edge is fully restricted. */
|
||||||
flows.RestrictFlows(to);
|
flows.RestrictFlows(to);
|
||||||
|
|
|
@ -167,7 +167,7 @@ protected:
|
||||||
std::atomic<bool> job_completed = false; ///< Is the job still running. This is accessed by multiple threads and reads may be stale.
|
std::atomic<bool> job_completed = false; ///< Is the job still running. This is accessed by multiple threads and reads may be stale.
|
||||||
std::atomic<bool> job_aborted = false; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale.
|
std::atomic<bool> job_aborted = false; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale.
|
||||||
|
|
||||||
void EraseFlows(NodeID from);
|
void EraseFlows(StationID from);
|
||||||
void JoinThread();
|
void JoinThread();
|
||||||
void SpawnThread();
|
void SpawnThread();
|
||||||
|
|
||||||
|
|
|
@ -556,7 +556,7 @@ void SetupColoursAndInitialWindow()
|
||||||
const uint8_t *b = GetNonSprite(GetColourPalette(i), SpriteType::Recolour) + 1;
|
const uint8_t *b = GetNonSprite(GetColourPalette(i), SpriteType::Recolour) + 1;
|
||||||
assert(b != nullptr);
|
assert(b != nullptr);
|
||||||
for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) {
|
for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) {
|
||||||
SetColourGradient(i, j, b[0xC6 + j]);
|
SetColourGradient(i, j, PixelColour{b[0xC6 + j]});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,12 @@ if(NOT OPTION_DEDICATED)
|
||||||
CONDITION HAIKU
|
CONDITION HAIKU
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_files(
|
||||||
|
alsamidi.cpp
|
||||||
|
alsamidi.h
|
||||||
|
CONDITION ALSA_FOUND
|
||||||
|
)
|
||||||
|
|
||||||
add_files(
|
add_files(
|
||||||
midi.h
|
midi.h
|
||||||
midifile.cpp
|
midifile.cpp
|
||||||
|
|
|
@ -0,0 +1,604 @@
|
||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* OpenTTD 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, version 2.
|
||||||
|
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file alsamidi.cpp Support for ALSA Linux MIDI. */
|
||||||
|
|
||||||
|
#include "../stdafx.h"
|
||||||
|
#include "../openttd.h"
|
||||||
|
#include "alsamidi.h"
|
||||||
|
#include "../base_media_base.h"
|
||||||
|
#include "midifile.hpp"
|
||||||
|
#include "../debug.h"
|
||||||
|
#include "midi.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "../safeguards.h"
|
||||||
|
|
||||||
|
/** Factory for ALSA MIDI player. */
|
||||||
|
static FMusicDriver_AlsaMidi iFMusicDriver_AlsaMidi;
|
||||||
|
|
||||||
|
std::optional<std::string_view> MusicDriver_AlsaMidi::Start(const StringList &parm)
|
||||||
|
{
|
||||||
|
this->playing.store(false);
|
||||||
|
Debug(driver, 2, "ALSA MIDI: Start");
|
||||||
|
this->dev_port = (uint)GetDriverParamInt(parm, "port", UINT_MAX);
|
||||||
|
Debug(driver, 2, "ALSA MIDI: using MIDI device at port {}", dev_port);
|
||||||
|
|
||||||
|
// Open sequencer
|
||||||
|
if (snd_seq_open(&this->seq, "default", SND_SEQ_OPEN_OUTPUT, 0) < 0) {
|
||||||
|
return "Failed to open ALSA sequencer";
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_seq_set_client_name(this->seq, "OpenTTD MIDI Out");
|
||||||
|
|
||||||
|
// Create port
|
||||||
|
this->seq_port = snd_seq_create_simple_port(this->seq, "MIDI Out",
|
||||||
|
SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
|
||||||
|
SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION);
|
||||||
|
|
||||||
|
if (this->seq_port < 0) {
|
||||||
|
return "Failed to create ALSA sequencer port";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create event queue
|
||||||
|
Debug(driver, 2, "ALSA MIDI: Creating sequencer event queue");
|
||||||
|
this->queue_id = snd_seq_alloc_named_queue(seq, "OpenTTD Sequencer Queue");
|
||||||
|
if (this->queue_id < 0) {
|
||||||
|
return "Failed to create ALSA sequencer event queue";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a slightly larger event output buffer than normal
|
||||||
|
snd_seq_set_client_pool_output(this->seq, 1000);
|
||||||
|
snd_seq_set_client_pool_output_room(this->seq, 1000);
|
||||||
|
snd_seq_set_output_buffer_size(this->seq, 1000);
|
||||||
|
|
||||||
|
// Turn on nonblocking mode
|
||||||
|
snd_seq_nonblock(this->seq, 1);
|
||||||
|
|
||||||
|
snd_seq_addr_t sender, dest;
|
||||||
|
|
||||||
|
// Setup event port
|
||||||
|
sender.client = snd_seq_client_id(this->seq);
|
||||||
|
sender.port = this->seq_port;
|
||||||
|
|
||||||
|
dest.client = this->dev_port;
|
||||||
|
dest.port = this->seq_port;
|
||||||
|
|
||||||
|
snd_seq_port_subscribe_t* subs;
|
||||||
|
snd_seq_port_subscribe_alloca(&subs);
|
||||||
|
snd_seq_port_subscribe_set_sender(subs, &sender);
|
||||||
|
snd_seq_port_subscribe_set_dest(subs, &dest);
|
||||||
|
|
||||||
|
if (snd_seq_subscribe_port(this->seq, subs) < 0) {
|
||||||
|
return "Failed to connect to port";
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(driver, 2, "ALSA MIDI: opened sequencer port {}", this->seq_port);
|
||||||
|
|
||||||
|
this->InitMidiVolume();
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MusicDriver_AlsaMidi::Stop()
|
||||||
|
{
|
||||||
|
Debug(driver, 2, "ALSA MIDI: stopping");
|
||||||
|
this->StopSong();
|
||||||
|
|
||||||
|
if (this->queue_id) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: freeing sequencer event queue");
|
||||||
|
snd_seq_free_queue(this->seq, this->queue_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->seq) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: closing sequencer handle");
|
||||||
|
snd_seq_close(this->seq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MusicDriver_AlsaMidi::PlaySong(const MusicSongInfo &song)
|
||||||
|
{
|
||||||
|
Debug(driver, 2, "ALSA MIDI: PlaySong");
|
||||||
|
std::string filename = MidiFile::GetSMFFile(song);
|
||||||
|
smf::MidiFile midifile;
|
||||||
|
|
||||||
|
Debug(driver, 2, "ALSA MIDI: reading SMFFile");
|
||||||
|
if (!filename.empty()) {
|
||||||
|
if (!midifile.read(filename)) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: error reading SMFFile");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
midifile.sortTracks();
|
||||||
|
// Convert MIDI ticks to absolute seconds
|
||||||
|
midifile.doTimeAnalysis();
|
||||||
|
|
||||||
|
// Merge > 1 tracks into single track for easier queueing.
|
||||||
|
// (WriteSMF only creates single-track MIDIs, other packs may be multitrack)
|
||||||
|
midifile.joinTracks();
|
||||||
|
|
||||||
|
if (this->playing.load() == true) {
|
||||||
|
this->StopSong();
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(driver, 2, "ALSA MIDI: starting playback of {}", song.songname);
|
||||||
|
Debug(driver, 2, "ALSA MIDI: SMF filename {}", filename);
|
||||||
|
|
||||||
|
// ALSA does not allow setting PPQ on started queues, so do this first.
|
||||||
|
// Tempo may be adjusted later, on a started/running queue.
|
||||||
|
int ppq = midifile.getTPQ();
|
||||||
|
this->SetPPQ(ppq);
|
||||||
|
|
||||||
|
this->SetupPolling();
|
||||||
|
|
||||||
|
snd_seq_start_queue(this->seq, this->queue_id, nullptr);
|
||||||
|
snd_seq_drain_output(this->seq);
|
||||||
|
|
||||||
|
this->playing.store(true);
|
||||||
|
|
||||||
|
StartNewThread(&this->_queue_thread, "ottd:alsamidi", &StartQueue, this, std::move(midifile));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MusicDriver_AlsaMidi::SetupPolling()
|
||||||
|
{
|
||||||
|
int poll_fd_cnt = snd_seq_poll_descriptors_count(this->seq, POLLOUT);
|
||||||
|
this->poll_fds.resize(poll_fd_cnt);
|
||||||
|
snd_seq_poll_descriptors(this->seq, this->poll_fds.data(), poll_fd_cnt, POLLOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the ALSA sequencer queue, iterates through the MIDI events in the file,
|
||||||
|
* converts them to ALSA sequencer events, and pushes them onto the queue.
|
||||||
|
*
|
||||||
|
* This function is blocking and expects to be run in a thread. It will block
|
||||||
|
* until either it is signaled to stop (in which case it will purge the ALSA queue,
|
||||||
|
* send a GM RESET, and terminate), or it has enqueued all events in the MIDI file,
|
||||||
|
* and waited for the queue to finish processing them all.
|
||||||
|
*
|
||||||
|
* @param drv Pointer to `this` instance of the class
|
||||||
|
* @param midifile The previously-loaded, sorted, and time-corrected STD MIDI file.
|
||||||
|
*
|
||||||
|
* @see Stopping()
|
||||||
|
* @see StopSong()
|
||||||
|
* @see WaitForFinish()
|
||||||
|
*/
|
||||||
|
static void StartQueue(MusicDriver_AlsaMidi *drv, const smf::MidiFile midifile)
|
||||||
|
{
|
||||||
|
Debug(driver, 2, "ALSA MIDI: queue thread started");
|
||||||
|
|
||||||
|
unsigned int last_tick;
|
||||||
|
|
||||||
|
// Push all events for all tracks to the sequencer queue
|
||||||
|
for (int track = 0; track < midifile.getNumTracks(); track++) {
|
||||||
|
|
||||||
|
std::vector<uint8_t> sysex_buffer;
|
||||||
|
|
||||||
|
for (int event = 0; event < midifile[track].size(); event++) {
|
||||||
|
|
||||||
|
auto& ev = midifile[track][event];
|
||||||
|
|
||||||
|
last_tick = static_cast<unsigned int>(ev.tick);
|
||||||
|
|
||||||
|
if (drv->Stopping()) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: Looks like we are stopping, bailing out of queue thread");
|
||||||
|
drv->SendResetEvent();
|
||||||
|
drv->StopQueue();
|
||||||
|
sysex_buffer.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.isTempo()) {
|
||||||
|
// Handle tempo change here, as we have to change it for the whole queue
|
||||||
|
Debug(driver, 2, "ALSA MIDI: Got tempo change event in queue thread");
|
||||||
|
int tempo_uspq = ev.getTempoMicroseconds();
|
||||||
|
drv->UpdateTempo(tempo_uspq);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Handle SYSEX events
|
||||||
|
// SYSEX events may
|
||||||
|
// 1. Be a complete SYSEX event (begin with F0 and end with F7)
|
||||||
|
// 2. Be a "middle" SYSEX event (a previous message began with F0)
|
||||||
|
// 3. Be an "end" SYSEX event (a previous message began with F0, and this one ends with F7)
|
||||||
|
// This basically means you need an accumulator. Split SYSEX messages are *rare* but exist.
|
||||||
|
if (ev.getCommandByte() == MIDIST_SYSEX) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: got SYSEX message");
|
||||||
|
sysex_buffer.clear();
|
||||||
|
|
||||||
|
// If this is is a complete (not partial) SYSEX message, send it
|
||||||
|
// Otherwise, accumulate it as a partial and continue to the next
|
||||||
|
if (ev.back() == MIDIST_ENDSYSEX) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: complete SYSEX, sending");
|
||||||
|
sysex_buffer.insert(sysex_buffer.end(), ev.begin() + 1, ev.end() -1);
|
||||||
|
drv->SendSysexEvent(sysex_buffer);
|
||||||
|
sysex_buffer.clear();
|
||||||
|
} else {
|
||||||
|
sysex_buffer.insert(sysex_buffer.end(), ev.begin() + 1, ev.end());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!sysex_buffer.empty() && ev.back() == MIDIST_ENDSYSEX) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: partial SYSEX completed, sending");
|
||||||
|
sysex_buffer.insert(sysex_buffer.end(), ev.begin(), ev.end() -1);
|
||||||
|
drv->SendSysexEvent(sysex_buffer);
|
||||||
|
sysex_buffer.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!sysex_buffer.empty()) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: partial SYSEX continuing");
|
||||||
|
sysex_buffer.insert(sysex_buffer.end(), ev.begin(), ev.end());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, it's just a regular event - handle it.
|
||||||
|
drv->SendEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(driver, 2, "ALSA MIDI: queue thread finished, waiting for events");
|
||||||
|
drv->WaitForFinish(last_tick);
|
||||||
|
drv->StopQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the ALSA sequencer queue, and sets `playing` to false.
|
||||||
|
*
|
||||||
|
* Note that this does not clear or drop any pending events in the queue
|
||||||
|
* before stopping it.
|
||||||
|
*
|
||||||
|
* @see SendResetEvent()
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::StopQueue()
|
||||||
|
{
|
||||||
|
Debug(driver, 2, "ALSA MIDI: stopping current queue!");
|
||||||
|
snd_seq_stop_queue(this->seq, this->queue_id, nullptr);
|
||||||
|
snd_seq_drain_output(this->seq);
|
||||||
|
|
||||||
|
this->poll_fds.clear();
|
||||||
|
|
||||||
|
this->playing.store(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a SYSEX GM reset message, after dropping all pending events in the
|
||||||
|
* queue.
|
||||||
|
*
|
||||||
|
* Does not stop the queue.
|
||||||
|
*
|
||||||
|
* @see StopQueue()
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::SendResetEvent()
|
||||||
|
{
|
||||||
|
// Drop anything still in the queue, this is a disruptive reset.
|
||||||
|
snd_seq_drop_output(this->seq);
|
||||||
|
|
||||||
|
std::vector<unsigned char> sysex_rst_msg = {0x7E, 0x7F, 0x09, 0x01};
|
||||||
|
this->SendSysexEvent(sysex_rst_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic helper for sending SYSEX messages.
|
||||||
|
*
|
||||||
|
* Note that this sends all SYSEX messages as "direct"/unscheduled events
|
||||||
|
* (skips tick queue).
|
||||||
|
*
|
||||||
|
* @param data The SYSEX message data (excluding 0xF0/0xF7 begin/end markers).
|
||||||
|
*
|
||||||
|
* @see SendEvent()
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::SendSysexEvent(const std::vector<uint8_t> data)
|
||||||
|
{
|
||||||
|
snd_seq_event_t seqev;
|
||||||
|
snd_seq_ev_clear(&seqev);
|
||||||
|
snd_seq_ev_set_source(&seqev, this->seq_port);
|
||||||
|
snd_seq_ev_set_subs(&seqev);
|
||||||
|
|
||||||
|
std::vector<uint8_t> complete_message;
|
||||||
|
complete_message.reserve(data.size() + 2);
|
||||||
|
complete_message.push_back(0xF0); // Start of SysEx
|
||||||
|
complete_message.insert(complete_message.end(), data.begin(), data.end());
|
||||||
|
complete_message.push_back(0xF7); // End of SysEx
|
||||||
|
snd_seq_ev_set_sysex(&seqev, complete_message.size(), complete_message.data());
|
||||||
|
|
||||||
|
// TODO this assumes all SYSEX msgs are immediate, and not queued with a tick
|
||||||
|
// this is correct for SYSEX GM RESET (all this is currently used for) but
|
||||||
|
// might not be globally correct.
|
||||||
|
snd_seq_ev_set_direct(&seqev);
|
||||||
|
|
||||||
|
this->PushEvent(seqev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic helper for sending non-SYSEX messages.
|
||||||
|
*
|
||||||
|
* Converts MIDI events from the file into ALSA-specific sequencer queue events,
|
||||||
|
* and schedules them on the tick-based sequencer queue.
|
||||||
|
*
|
||||||
|
* @param ev The raw MIDI event from the file.
|
||||||
|
*
|
||||||
|
* @see SendSysexEvent()
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::SendEvent(const smf::MidiEvent& ev)
|
||||||
|
{
|
||||||
|
snd_seq_event_t seqev;
|
||||||
|
snd_seq_ev_clear(&seqev);
|
||||||
|
|
||||||
|
unsigned int ticks = static_cast<unsigned int>(ev.tick);
|
||||||
|
|
||||||
|
if (ev.isNoteOn()) {
|
||||||
|
snd_seq_ev_set_noteon(&seqev, ev.getChannel(), ev[1], ev[2]);
|
||||||
|
} else if (ev.isNoteOff()) {
|
||||||
|
snd_seq_ev_set_noteoff(&seqev, ev.getChannel(), ev[1], ev[2]);
|
||||||
|
} else if (ev.isController()) {
|
||||||
|
if (ev[1] == MIDI_CTL_MSB_MAIN_VOLUME) {
|
||||||
|
this->UpdateChannelVolume(ev.getChannel(), ev[2]);
|
||||||
|
snd_seq_ev_set_controller(&seqev, ev.getChannel(), ev[1], this->vol_state.current_volume[ev.getChannel()]);
|
||||||
|
}
|
||||||
|
snd_seq_ev_set_controller(&seqev, ev.getChannel(), ev[1], ev[2]);
|
||||||
|
} else if (ev.isPatchChange()) {
|
||||||
|
snd_seq_ev_set_pgmchange(&seqev, ev.getChannel(), ev[1]);
|
||||||
|
} else if (ev.isPitchbend()) {
|
||||||
|
snd_seq_ev_set_pitchbend(&seqev, ev.getChannel(), ((ev[2] << 7) | ev[1]) - 8192);
|
||||||
|
} else if (ev.isPressure()) {
|
||||||
|
snd_seq_ev_set_chanpress(&seqev, ev.getChannel(), ev[1]);
|
||||||
|
} else if (ev.isAftertouch()) {
|
||||||
|
snd_seq_ev_set_keypress(&seqev, ev.getChannel(), ev[1], ev[2]);
|
||||||
|
} else if (ev.getCommandNibble() == 0xF0 && ev.getCommandByte() == MIDIST_SYSRESET) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: reset event");
|
||||||
|
snd_seq_ev_set_fixed(&seqev);
|
||||||
|
seqev.type = SND_SEQ_EVENT_RESET;
|
||||||
|
} else if (ev.isMeta()) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: ignoring meta message");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: unknown message: {}", ev.getCommandNibble());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule event
|
||||||
|
snd_seq_ev_schedule_tick(&seqev, this->queue_id, 0, ticks);
|
||||||
|
snd_seq_ev_set_source(&seqev, this->seq_port);
|
||||||
|
snd_seq_ev_set_subs(&seqev);
|
||||||
|
|
||||||
|
this->PushEvent(seqev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits until either:
|
||||||
|
*
|
||||||
|
* 1. The ALSA sequencer finishes processing up to the last event
|
||||||
|
* that was enqueued, as measured by comparing the tick value of the
|
||||||
|
* last event against the current tick value of the ALSA queue state.
|
||||||
|
*
|
||||||
|
* 2. `Stopping()` returns true, signaling early exit (don't wait for playback to finish).
|
||||||
|
*
|
||||||
|
* @param last_event_tick Tick value of the last event that was added to the queue.
|
||||||
|
*
|
||||||
|
* @see Stopping()
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::WaitForFinish(const unsigned int last_event_tick)
|
||||||
|
{
|
||||||
|
Debug(driver, 2, "ALSA MIDI: waiting for events finish");
|
||||||
|
|
||||||
|
// First wait for queue to drain
|
||||||
|
int res = 0;
|
||||||
|
do {
|
||||||
|
res = snd_seq_drain_output(this->seq);
|
||||||
|
if (res != 0) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
}
|
||||||
|
} while (res != 0);
|
||||||
|
|
||||||
|
// Now get queue status and wait until we've passed the last scheduled tick
|
||||||
|
snd_seq_queue_status_t *status;
|
||||||
|
snd_seq_queue_status_alloca(&status);
|
||||||
|
|
||||||
|
do {
|
||||||
|
snd_seq_get_queue_status(this->seq, this->queue_id, status);
|
||||||
|
const snd_seq_tick_time_t current_tick = snd_seq_queue_status_get_tick_time(status);
|
||||||
|
|
||||||
|
if (this->Stopping()) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: got stop signal, not waiting for events to finish");
|
||||||
|
this->SendResetEvent();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_tick >= last_event_tick) {
|
||||||
|
// This is necessarily imprecise, just because the queue has processed the last
|
||||||
|
// tick event doesn't mean whatever output device in use has played it yet,
|
||||||
|
// but in practice this is good enough to not cut off the last few notes.
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
} while(true);
|
||||||
|
|
||||||
|
Debug(driver, 2, "ALSA MIDI: events finished");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes an ALSA sequencer event onto the ALSA sequencer queue.
|
||||||
|
*
|
||||||
|
* If the push fails because the output buffer is full, uses `poll()`
|
||||||
|
* to wait until there's space/device is ready.
|
||||||
|
*
|
||||||
|
* @param seqev ALSA sequencer event to push.
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::PushEvent(snd_seq_event_t seqev)
|
||||||
|
{
|
||||||
|
// Wait for space in the queue via `poll()`
|
||||||
|
while (snd_seq_event_output_direct(this->seq, &seqev) < 0) {
|
||||||
|
poll(this->poll_fds.data(), this->poll_fds.size(), 100); // 100ms timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals the queue thread to terminate and joins it.
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::StopSong()
|
||||||
|
{
|
||||||
|
this->stopping.store(true);
|
||||||
|
|
||||||
|
Debug(driver, 2, "ALSA MIDI: StopSong waiting for queue thread");
|
||||||
|
if (this->_queue_thread.joinable()) {
|
||||||
|
this->_queue_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(driver, 2, "ALSA MIDI: stopping current queue");
|
||||||
|
|
||||||
|
this->stopping.store(false);
|
||||||
|
|
||||||
|
Debug(driver, 2, "ALSA MIDI: stopped song");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MusicDriver_AlsaMidi::Stopping()
|
||||||
|
{
|
||||||
|
return this->stopping.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MusicDriver_AlsaMidi::IsSongPlaying()
|
||||||
|
{
|
||||||
|
return this->playing.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the desired volume for the MIDI sequencer (from the UI thread)
|
||||||
|
*
|
||||||
|
* Note that this implementation will internally debounce rapid subsequent calls to
|
||||||
|
* SetVolume(), to avoid overwhelming the sequencer and its queues and buffers with
|
||||||
|
* incremental volume updates. The magnitude of the volume change is taken into account
|
||||||
|
* for this.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::SetVolume(uint8_t vol)
|
||||||
|
{
|
||||||
|
Debug(driver, 2, "ALSA MIDI: got volume level update {}", vol);
|
||||||
|
|
||||||
|
// Adaptive debounce: small changes need more time between updates
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_volume_update);
|
||||||
|
|
||||||
|
int current_vol = this->current_vol.load();
|
||||||
|
int change_magnitude = std::abs(vol - current_vol);
|
||||||
|
|
||||||
|
int required_ms = change_magnitude < 5 ? SMALL_VOL_DEBOUNCE :
|
||||||
|
change_magnitude < 15 ? MED_VOL_DEBOUNCE :
|
||||||
|
LARGE_VOL_DEBOUNCE;
|
||||||
|
|
||||||
|
|
||||||
|
if (elapsed.count() >= required_ms || current_vol == 127) {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: got volume level update {}", vol);
|
||||||
|
if (vol != current_vol) {
|
||||||
|
this->SetScaledVolume(vol);
|
||||||
|
this->last_volume_update = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the volume state for the player.
|
||||||
|
*
|
||||||
|
* @see UpdateChannelVolume()
|
||||||
|
* @see SetScaledVolume()
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::InitMidiVolume()
|
||||||
|
{
|
||||||
|
this->last_volume_update = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
this->vol_state.master_scale = 127;
|
||||||
|
for (int i = 0; i < MIDI_CHANNELS; i++) {
|
||||||
|
this->vol_state.base_volume[i] = 127;
|
||||||
|
this->vol_state.current_volume[i] = 127;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales current volume level for each channel according to the scale
|
||||||
|
* factor provided. This is to maintain the relative volume levels between
|
||||||
|
* channels as set by the midi file, while scaling up or down.
|
||||||
|
*
|
||||||
|
* @param channel to update the volume for.
|
||||||
|
* @param value requested volume level to scale selected channel volume against.
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::UpdateChannelVolume(int channel, int value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(this->vol_state.mutex);
|
||||||
|
this->vol_state.base_volume[channel] = value;
|
||||||
|
this->vol_state.current_volume[channel] = (value * this->vol_state.master_scale) / 127;
|
||||||
|
Debug(driver, 2, "ALSA MIDI: upading volume for channel {} to {}, base: {}, scale {}", channel, this->vol_state.current_volume[channel], this->vol_state.base_volume[channel], this->vol_state.master_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales current volume level for each channel according to the scale
|
||||||
|
* factor provided. This is to maintain the relative volume levels between
|
||||||
|
* channels as set by the midi file, while scaling up or down.
|
||||||
|
*
|
||||||
|
* @param value requested volume level (0-127) to scale all channel volume levels against.
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::SetScaledVolume(int value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(this->vol_state.mutex);
|
||||||
|
this->vol_state.master_scale = (value > 127) ? 127 : (value < 0) ? 0 : value;
|
||||||
|
|
||||||
|
for (int i = 0; i < MIDI_CHANNELS; i++) {
|
||||||
|
this->vol_state.current_volume[i] = ( this->vol_state.base_volume[i] * this->vol_state.master_scale) / 127;
|
||||||
|
snd_seq_event_t vol_ev;
|
||||||
|
snd_seq_ev_clear(&vol_ev);
|
||||||
|
Debug(driver, 2, "ALSA MIDI: setting volume for channel {} to {}, master: {} base: {}", i, this->vol_state.current_volume[i], this->vol_state.master_scale, this->vol_state.base_volume[i]);
|
||||||
|
snd_seq_ev_set_controller(&vol_ev, i, MIDI_CTL_MSB_MAIN_VOLUME, this->vol_state.current_volume[i]);
|
||||||
|
snd_seq_ev_set_source(&vol_ev, this->seq_port);
|
||||||
|
snd_seq_ev_set_subs(&vol_ev);
|
||||||
|
snd_seq_ev_set_direct(&vol_ev);
|
||||||
|
this->PushEvent(vol_ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->current_vol.store(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the tempo of the current (started) ALSA sequencer queue.
|
||||||
|
*
|
||||||
|
* @param tempo_uspq Tempo value in units per quarter note.
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::UpdateTempo(const int tempo_uspq)
|
||||||
|
{
|
||||||
|
if (snd_seq_change_queue_tempo(this->seq, this->queue_id, tempo_uspq, nullptr) < 0) {
|
||||||
|
throw std::runtime_error("Failed to update queue tempo");
|
||||||
|
}
|
||||||
|
snd_seq_drain_output(this->seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the Pulses Per Quarternote (PPQ) of the current ALSA sequencer queue.
|
||||||
|
*
|
||||||
|
* Note that the PPQ of an ALSA sequencer queue cannot be changed after it is started.
|
||||||
|
*
|
||||||
|
* @param ppq Pulse per quarter note value.
|
||||||
|
*
|
||||||
|
* @see StopQueue()
|
||||||
|
*/
|
||||||
|
void MusicDriver_AlsaMidi::SetPPQ(const int ppq)
|
||||||
|
{
|
||||||
|
Debug(driver, 2, "ALSA MIDI: setting PPQ to {}", ppq);
|
||||||
|
snd_seq_queue_tempo_t* tempo;
|
||||||
|
snd_seq_queue_tempo_alloca(&tempo);
|
||||||
|
snd_seq_queue_tempo_set_ppq(tempo, ppq);
|
||||||
|
snd_seq_queue_tempo_set_tempo(tempo, 1000000); // 60 BPM
|
||||||
|
|
||||||
|
snd_seq_queue_status_t *status;
|
||||||
|
snd_seq_queue_status_alloca(&status);
|
||||||
|
snd_seq_get_queue_status(seq, queue_id, status);
|
||||||
|
|
||||||
|
if (snd_seq_queue_status_get_status(status) == 0) {
|
||||||
|
if (snd_seq_set_queue_tempo(this->seq, this->queue_id, tempo) < 0) {
|
||||||
|
throw std::runtime_error("Failed to set queue PPQ");
|
||||||
|
}
|
||||||
|
snd_seq_drain_output(this->seq);
|
||||||
|
} else {
|
||||||
|
Debug(driver, 2, "ALSA MIDI: tried to set PPQ on non-stopped queue!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* OpenTTD 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, version 2.
|
||||||
|
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file alsamidi.h Base of Alsa MIDI support. */
|
||||||
|
|
||||||
|
#ifndef MUSIC_ALSAMIDI_H
|
||||||
|
#define MUSIC_ALSAMIDI_H
|
||||||
|
|
||||||
|
#include "music_driver.hpp"
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include "../3rdparty/midifile/MidiFile.h"
|
||||||
|
#include "../../thread.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
const int SMALL_VOL_DEBOUNCE = 200; // Tiny changes: 200ms
|
||||||
|
const int MED_VOL_DEBOUNCE = 50; // Small changes: 50ms
|
||||||
|
const int LARGE_VOL_DEBOUNCE = 10; // Large changes: 10ms
|
||||||
|
|
||||||
|
/** The midi player for ALSA on Linux. */
|
||||||
|
class MusicDriver_AlsaMidi : public MusicDriver {
|
||||||
|
public:
|
||||||
|
std::optional<std::string_view> Start(const StringList ¶m) override;
|
||||||
|
|
||||||
|
void Stop() override;
|
||||||
|
|
||||||
|
void PlaySong(const MusicSongInfo &song) override;
|
||||||
|
|
||||||
|
void StopSong() override;
|
||||||
|
|
||||||
|
bool IsSongPlaying() override;
|
||||||
|
|
||||||
|
void SetVolume(uint8_t vol) override;
|
||||||
|
|
||||||
|
std::string_view GetName() const override { return "alsamidi"; }
|
||||||
|
|
||||||
|
void WaitForFinish(const unsigned int last_tick);
|
||||||
|
|
||||||
|
void StopQueue();
|
||||||
|
|
||||||
|
void SendEvent(const smf::MidiEvent& event);
|
||||||
|
|
||||||
|
void SendSysexEvent(const std::vector<uint8_t> data);
|
||||||
|
|
||||||
|
void SendResetEvent();
|
||||||
|
|
||||||
|
void UpdateTempo(const int tempo_uspq);
|
||||||
|
|
||||||
|
bool Stopping();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int base_volume[MIDI_CHANNELS];
|
||||||
|
int master_scale;
|
||||||
|
int current_volume[MIDI_CHANNELS];
|
||||||
|
std::mutex mutex;
|
||||||
|
}MidiVolume;
|
||||||
|
|
||||||
|
MidiVolume vol_state;
|
||||||
|
snd_seq_t* seq;
|
||||||
|
int queue_id;
|
||||||
|
int seq_port;
|
||||||
|
int dev_port;
|
||||||
|
std::vector<struct pollfd> poll_fds;
|
||||||
|
std::thread _queue_thread;
|
||||||
|
std::atomic<bool> playing{false};
|
||||||
|
std::atomic<bool> stopping{false};
|
||||||
|
std::atomic<uint8_t> current_vol{127};
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> last_volume_update;
|
||||||
|
void SetPPQ(const int ppq);
|
||||||
|
void SetScaledVolume(int value);
|
||||||
|
void SetupPolling();
|
||||||
|
void PushEvent(snd_seq_event_t seqev);
|
||||||
|
void InitMidiVolume();
|
||||||
|
void UpdateChannelVolume(int channel, int value);
|
||||||
|
void VolumeAdjust (const uint8_t new_vol);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Factory for the Linux ALSA midi player. */
|
||||||
|
class FMusicDriver_AlsaMidi : public DriverFactoryBase {
|
||||||
|
public:
|
||||||
|
FMusicDriver_AlsaMidi() : DriverFactoryBase(Driver::DT_MUSIC, 10, "alsamidi", "ALSA Linux MIDI Driver") {}
|
||||||
|
Driver *CreateInstance() const override { return new MusicDriver_AlsaMidi(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void StartQueue(MusicDriver_AlsaMidi *drv, const smf::MidiFile midifile);
|
||||||
|
|
||||||
|
#endif /* MUSIC_ALSAMIDI_H */
|
|
@ -97,11 +97,11 @@ static ChangeInfoResult CargoReserveInfo(uint first, uint last, int prop, ByteRe
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x13: // Colour for station rating bars
|
case 0x13: // Colour for station rating bars
|
||||||
cs->rating_colour = buf.ReadByte();
|
cs->rating_colour = PixelColour{buf.ReadByte()};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x14: // Colour for cargo graph
|
case 0x14: // Colour for cargo graph
|
||||||
cs->legend_colour = buf.ReadByte();
|
cs->legend_colour = PixelColour{buf.ReadByte()};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x15: // Freight status
|
case 0x15: // Freight status
|
||||||
|
|
|
@ -565,7 +565,7 @@ static ChangeInfoResult IndustriesChangeInfo(uint first, uint last, int prop, By
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x19: // Map colour
|
case 0x19: // Map colour
|
||||||
indsp->map_colour = buf.ReadByte();
|
indsp->map_colour = PixelColour{buf.ReadByte()};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x1A: // Special industry flags to define special behavior
|
case 0x1A: // Special industry flags to define special behavior
|
||||||
|
|
|
@ -121,7 +121,7 @@ static ChangeInfoResult RailTypeChangeInfo(uint first, uint last, int prop, Byte
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x16: // Map colour
|
case 0x16: // Map colour
|
||||||
rti->map_colour = buf.ReadByte();
|
rti->map_colour = PixelColour{buf.ReadByte()};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x17: // Introduction date
|
case 0x17: // Introduction date
|
||||||
|
|
|
@ -109,7 +109,7 @@ static ChangeInfoResult RoadTypeChangeInfo(uint first, uint last, int prop, Byte
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x16: // Map colour
|
case 0x16: // Map colour
|
||||||
rti->map_colour = buf.ReadByte();
|
rti->map_colour = PixelColour{buf.ReadByte()};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x17: // Introduction date
|
case 0x17: // Introduction date
|
||||||
|
|
|
@ -368,7 +368,7 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, VehicleOrderI
|
||||||
next = v->cur_implicit_order_index;
|
next = v->cur_implicit_order_index;
|
||||||
if (next >= this->GetNumOrders()) {
|
if (next >= this->GetNumOrders()) {
|
||||||
next = this->GetFirstOrder();
|
next = this->GetFirstOrder();
|
||||||
if (next == INVALID_VEH_ORDER_ID) return StationID::Invalid().base();
|
if (next == INVALID_VEH_ORDER_ID) return StationID::Invalid();
|
||||||
} else {
|
} else {
|
||||||
/* GetNext never returns INVALID_VEH_ORDER_ID if there is a valid station in the list.
|
/* GetNext never returns INVALID_VEH_ORDER_ID if there is a valid station in the list.
|
||||||
* As the given "next" is already valid and a station in the list, we
|
* As the given "next" is already valid and a station in the list, we
|
||||||
|
@ -404,11 +404,11 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, VehicleOrderI
|
||||||
if (next == INVALID_VEH_ORDER_ID || ((orders[next].IsType(OT_GOTO_STATION) || orders[next].IsType(OT_IMPLICIT)) &&
|
if (next == INVALID_VEH_ORDER_ID || ((orders[next].IsType(OT_GOTO_STATION) || orders[next].IsType(OT_IMPLICIT)) &&
|
||||||
orders[next].GetDestination() == v->last_station_visited &&
|
orders[next].GetDestination() == v->last_station_visited &&
|
||||||
(orders[next].GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) {
|
(orders[next].GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) {
|
||||||
return StationID::Invalid().base();
|
return StationID::Invalid();
|
||||||
}
|
}
|
||||||
} while (orders[next].IsType(OT_GOTO_DEPOT) || orders[next].GetDestination() == v->last_station_visited);
|
} while (orders[next].IsType(OT_GOTO_DEPOT) || orders[next].GetDestination() == v->last_station_visited);
|
||||||
|
|
||||||
return orders[next].GetDestination().ToStationID().base();
|
return orders[next].GetDestination().ToStationID();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -358,9 +358,9 @@ void DoPaletteAnimations()
|
||||||
* @param threshold Background colour brightness threshold below which the background is considered dark and TC_WHITE is returned, range: 0 - 255, default 128.
|
* @param threshold Background colour brightness threshold below which the background is considered dark and TC_WHITE is returned, range: 0 - 255, default 128.
|
||||||
* @return TC_BLACK or TC_WHITE depending on what gives a better contrast.
|
* @return TC_BLACK or TC_WHITE depending on what gives a better contrast.
|
||||||
*/
|
*/
|
||||||
TextColour GetContrastColour(uint8_t background, uint8_t threshold)
|
TextColour GetContrastColour(PixelColour background, uint8_t threshold)
|
||||||
{
|
{
|
||||||
Colour c = _cur_palette.palette[background];
|
Colour c = _cur_palette.palette[background.p];
|
||||||
/* Compute brightness according to http://www.w3.org/TR/AERT#color-contrast.
|
/* Compute brightness according to http://www.w3.org/TR/AERT#color-contrast.
|
||||||
* The following formula computes 1000 * brightness^2, with brightness being in range 0 to 255. */
|
* The following formula computes 1000 * brightness^2, with brightness being in range 0 to 255. */
|
||||||
uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114;
|
uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114;
|
||||||
|
@ -374,7 +374,7 @@ TextColour GetContrastColour(uint8_t background, uint8_t threshold)
|
||||||
*/
|
*/
|
||||||
struct ColourGradients
|
struct ColourGradients
|
||||||
{
|
{
|
||||||
using ColourGradient = std::array<uint8_t, SHADE_END>;
|
using ColourGradient = std::array<PixelColour, SHADE_END>;
|
||||||
|
|
||||||
static inline std::array<ColourGradient, COLOUR_END> gradient{};
|
static inline std::array<ColourGradient, COLOUR_END> gradient{};
|
||||||
};
|
};
|
||||||
|
@ -385,7 +385,7 @@ struct ColourGradients
|
||||||
* @param shade Shade level from 1 to 7.
|
* @param shade Shade level from 1 to 7.
|
||||||
* @returns palette index of colour.
|
* @returns palette index of colour.
|
||||||
*/
|
*/
|
||||||
uint8_t GetColourGradient(Colours colour, ColourShade shade)
|
PixelColour GetColourGradient(Colours colour, ColourShade shade)
|
||||||
{
|
{
|
||||||
return ColourGradients::gradient[colour % COLOUR_END][shade % SHADE_END];
|
return ColourGradients::gradient[colour % COLOUR_END][shade % SHADE_END];
|
||||||
}
|
}
|
||||||
|
@ -396,7 +396,7 @@ uint8_t GetColourGradient(Colours colour, ColourShade shade)
|
||||||
* @param shade Shade level from 1 to 7.
|
* @param shade Shade level from 1 to 7.
|
||||||
* @param palette_index Palette index to set.
|
* @param palette_index Palette index to set.
|
||||||
*/
|
*/
|
||||||
void SetColourGradient(Colours colour, ColourShade shade, uint8_t palette_index)
|
void SetColourGradient(Colours colour, ColourShade shade, PixelColour palette_index)
|
||||||
{
|
{
|
||||||
assert(colour < COLOUR_END);
|
assert(colour < COLOUR_END);
|
||||||
assert(shade < SHADE_END);
|
assert(shade < SHADE_END);
|
||||||
|
|
|
@ -67,7 +67,7 @@ inline bool IsValidColours(Colours colours)
|
||||||
return colours < COLOUR_END;
|
return colours < COLOUR_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextColour GetContrastColour(uint8_t background, uint8_t threshold = 128);
|
TextColour GetContrastColour(PixelColour background, uint8_t threshold = 128);
|
||||||
|
|
||||||
enum ColourShade : uint8_t {
|
enum ColourShade : uint8_t {
|
||||||
SHADE_BEGIN = 0,
|
SHADE_BEGIN = 0,
|
||||||
|
@ -83,45 +83,45 @@ enum ColourShade : uint8_t {
|
||||||
};
|
};
|
||||||
DECLARE_INCREMENT_DECREMENT_OPERATORS(ColourShade)
|
DECLARE_INCREMENT_DECREMENT_OPERATORS(ColourShade)
|
||||||
|
|
||||||
uint8_t GetColourGradient(Colours colour, ColourShade shade);
|
PixelColour GetColourGradient(Colours colour, ColourShade shade);
|
||||||
void SetColourGradient(Colours colour, ColourShade shade, uint8_t palette_colour);
|
void SetColourGradient(Colours colour, ColourShade shade, PixelColour palette_colour);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the colour for a particular greyscale level.
|
* Return the colour for a particular greyscale level.
|
||||||
* @param level Intensity, 0 = black, 15 = white
|
* @param level Intensity, 0 = black, 15 = white
|
||||||
* @return colour
|
* @return colour
|
||||||
*/
|
*/
|
||||||
constexpr uint8_t GREY_SCALE(uint8_t level) { return level; }
|
inline constexpr PixelColour GREY_SCALE(uint8_t level) { return PixelColour{level}; }
|
||||||
|
|
||||||
static const uint8_t PC_BLACK = GREY_SCALE(1); ///< Black palette colour.
|
static constexpr PixelColour PC_BLACK {GREY_SCALE(1)}; ///< Black palette colour.
|
||||||
static const uint8_t PC_DARK_GREY = GREY_SCALE(6); ///< Dark grey palette colour.
|
static constexpr PixelColour PC_DARK_GREY {GREY_SCALE(6)}; ///< Dark grey palette colour.
|
||||||
static const uint8_t PC_GREY = GREY_SCALE(10); ///< Grey palette colour.
|
static constexpr PixelColour PC_GREY {GREY_SCALE(10)}; ///< Grey palette colour.
|
||||||
static const uint8_t PC_WHITE = GREY_SCALE(15); ///< White palette colour.
|
static constexpr PixelColour PC_WHITE {GREY_SCALE(15)}; ///< White palette colour.
|
||||||
|
|
||||||
static const uint8_t PC_VERY_DARK_RED = 0xB2; ///< Almost-black red palette colour.
|
static constexpr PixelColour PC_VERY_DARK_RED {0xB2}; ///< Almost-black red palette colour.
|
||||||
static const uint8_t PC_DARK_RED = 0xB4; ///< Dark red palette colour.
|
static constexpr PixelColour PC_DARK_RED {0xB4}; ///< Dark red palette colour.
|
||||||
static const uint8_t PC_RED = 0xB8; ///< Red palette colour.
|
static constexpr PixelColour PC_RED {0xB8}; ///< Red palette colour.
|
||||||
|
|
||||||
static const uint8_t PC_VERY_DARK_BROWN = 0x56; ///< Almost-black brown palette colour.
|
static constexpr PixelColour PC_VERY_DARK_BROWN {0x56}; ///< Almost-black brown palette colour.
|
||||||
|
|
||||||
static const uint8_t PC_ORANGE = 0xC2; ///< Orange palette colour.
|
static constexpr PixelColour PC_ORANGE {0xC2}; ///< Orange palette colour.
|
||||||
|
|
||||||
static const uint8_t PC_YELLOW = 0xBF; ///< Yellow palette colour.
|
static constexpr PixelColour PC_YELLOW {0xBF}; ///< Yellow palette colour.
|
||||||
static const uint8_t PC_LIGHT_YELLOW = 0x44; ///< Light yellow palette colour.
|
static constexpr PixelColour PC_LIGHT_YELLOW {0x44}; ///< Light yellow palette colour.
|
||||||
static const uint8_t PC_VERY_LIGHT_YELLOW = 0x45; ///< Almost-white yellow palette colour.
|
static constexpr PixelColour PC_VERY_LIGHT_YELLOW {0x45}; ///< Almost-white yellow palette colour.
|
||||||
|
|
||||||
static const uint8_t PC_GREEN = 0xD0; ///< Green palette colour.
|
static constexpr PixelColour PC_GREEN {0xD0}; ///< Green palette colour.
|
||||||
|
|
||||||
static const uint8_t PC_VERY_DARK_BLUE = 0x9A; ///< Almost-black blue palette colour.
|
static constexpr PixelColour PC_VERY_DARK_BLUE {0x9A}; ///< Almost-black blue palette colour.
|
||||||
static const uint8_t PC_DARK_BLUE = 0x9D; ///< Dark blue palette colour.
|
static constexpr PixelColour PC_DARK_BLUE {0x9D}; ///< Dark blue palette colour.
|
||||||
static const uint8_t PC_LIGHT_BLUE = 0x98; ///< Light blue palette colour.
|
static constexpr PixelColour PC_LIGHT_BLUE {0x98}; ///< Light blue palette colour.
|
||||||
|
|
||||||
static const uint8_t PC_ROUGH_LAND = 0x52; ///< Dark green palette colour for rough land.
|
static constexpr PixelColour PC_ROUGH_LAND {0x52}; ///< Dark green palette colour for rough land.
|
||||||
static const uint8_t PC_GRASS_LAND = 0x54; ///< Dark green palette colour for grass land.
|
static constexpr PixelColour PC_GRASS_LAND {0x54}; ///< Dark green palette colour for grass land.
|
||||||
static const uint8_t PC_BARE_LAND = 0x37; ///< Brown palette colour for bare land.
|
static constexpr PixelColour PC_BARE_LAND {0x37}; ///< Brown palette colour for bare land.
|
||||||
static const uint8_t PC_RAINFOREST = 0x5C; ///< Pale green palette colour for rainforest.
|
static constexpr PixelColour PC_RAINFOREST {0x5C}; ///< Pale green palette colour for rainforest.
|
||||||
static const uint8_t PC_FIELDS = 0x25; ///< Light brown palette colour for fields.
|
static constexpr PixelColour PC_FIELDS {0x25}; ///< Light brown palette colour for fields.
|
||||||
static const uint8_t PC_TREES = 0x57; ///< Green palette colour for trees.
|
static constexpr PixelColour PC_TREES {0x57}; ///< Green palette colour for trees.
|
||||||
static const uint8_t PC_WATER = 0xC9; ///< Dark blue palette colour for water.
|
static constexpr PixelColour PC_WATER {0xC9}; ///< Dark blue palette colour for water.
|
||||||
|
|
||||||
#endif /* PALETTE_FUNC_H */
|
#endif /* PALETTE_FUNC_H */
|
||||||
|
|
|
@ -234,7 +234,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Colour on mini-map
|
* Colour on mini-map
|
||||||
*/
|
*/
|
||||||
uint8_t map_colour;
|
PixelColour map_colour;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Introduction date.
|
* Introduction date.
|
||||||
|
|
|
@ -145,7 +145,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Colour on mini-map
|
* Colour on mini-map
|
||||||
*/
|
*/
|
||||||
uint8_t map_colour;
|
PixelColour map_colour;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Introduction date.
|
* Introduction date.
|
||||||
|
|
|
@ -99,7 +99,7 @@ uint BaseSettingEntry::Draw(GameSettings *settings_ptr, int left, int right, int
|
||||||
|
|
||||||
int x = rtl ? right : left;
|
int x = rtl ? right : left;
|
||||||
if (cur_row >= first_row) {
|
if (cur_row >= first_row) {
|
||||||
int colour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
|
PixelColour colour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
|
||||||
y += (cur_row - first_row) * SETTING_HEIGHT; // Compute correct y start position
|
y += (cur_row - first_row) * SETTING_HEIGHT; // Compute correct y start position
|
||||||
|
|
||||||
/* Draw vertical for parent nesting levels */
|
/* Draw vertical for parent nesting levels */
|
||||||
|
|
|
@ -1849,7 +1849,7 @@ void ShowGameOptions()
|
||||||
*/
|
*/
|
||||||
void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_left, bool clickable_right)
|
void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_left, bool clickable_right)
|
||||||
{
|
{
|
||||||
int colour = GetColourGradient(button_colour, SHADE_DARKER);
|
PixelColour colour = GetColourGradient(button_colour, SHADE_DARKER);
|
||||||
Dimension dim = NWidgetScrollbar::GetHorizontalDimension();
|
Dimension dim = NWidgetScrollbar::GetHorizontalDimension();
|
||||||
|
|
||||||
Rect lr = {x, y, x + (int)dim.width - 1, y + (int)dim.height - 1};
|
Rect lr = {x, y, x + (int)dim.width - 1, y + (int)dim.height - 1};
|
||||||
|
@ -1881,7 +1881,7 @@ void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool c
|
||||||
*/
|
*/
|
||||||
void DrawUpDownButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_up, bool clickable_down)
|
void DrawUpDownButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_up, bool clickable_down)
|
||||||
{
|
{
|
||||||
int colour = GetColourGradient(button_colour, SHADE_DARKER);
|
PixelColour colour = GetColourGradient(button_colour, SHADE_DARKER);
|
||||||
|
|
||||||
Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1};
|
Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1};
|
||||||
Rect ur = r.WithWidth(SETTING_BUTTON_WIDTH / 2, (_current_text_dir == TD_RTL));
|
Rect ur = r.WithWidth(SETTING_BUTTON_WIDTH / 2, (_current_text_dir == TD_RTL));
|
||||||
|
@ -1907,7 +1907,7 @@ void DrawUpDownButtons(int x, int y, Colours button_colour, uint8_t state, bool
|
||||||
*/
|
*/
|
||||||
void DrawDropDownButton(int x, int y, Colours button_colour, bool state, bool clickable)
|
void DrawDropDownButton(int x, int y, Colours button_colour, bool state, bool clickable)
|
||||||
{
|
{
|
||||||
int colour = GetColourGradient(button_colour, SHADE_DARKER);
|
PixelColour colour = GetColourGradient(button_colour, SHADE_DARKER);
|
||||||
|
|
||||||
Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1};
|
Rect r = {x, y, x + SETTING_BUTTON_WIDTH - 1, y + SETTING_BUTTON_HEIGHT - 1};
|
||||||
|
|
||||||
|
|
|
@ -45,9 +45,9 @@ void DrawSliderWidget(Rect r, Colours wedge_colour, Colours handle_colour, TextC
|
||||||
int wx1 = r.left + sw / 2;
|
int wx1 = r.left + sw / 2;
|
||||||
int wx2 = r.right - sw / 2;
|
int wx2 = r.right - sw / 2;
|
||||||
if (_current_text_dir == TD_RTL) std::swap(wx1, wx2);
|
if (_current_text_dir == TD_RTL) std::swap(wx1, wx2);
|
||||||
const uint shadow = GetColourGradient(wedge_colour, SHADE_DARK);
|
const PixelColour shadow = GetColourGradient(wedge_colour, SHADE_DARK);
|
||||||
const uint fill = GetColourGradient(wedge_colour, SHADE_LIGHTER);
|
const PixelColour fill = GetColourGradient(wedge_colour, SHADE_LIGHTER);
|
||||||
const uint light = GetColourGradient(wedge_colour, SHADE_LIGHTEST);
|
const PixelColour light = GetColourGradient(wedge_colour, SHADE_LIGHTEST);
|
||||||
const std::array<Point, 3> wedge{ Point{wx1, r.bottom - ha}, Point{wx2, r.top + ha}, Point{wx2, r.bottom - ha} };
|
const std::array<Point, 3> wedge{ Point{wx1, r.bottom - ha}, Point{wx2, r.top + ha}, Point{wx2, r.bottom - ha} };
|
||||||
GfxFillPolygon(wedge, fill);
|
GfxFillPolygon(wedge, fill);
|
||||||
GfxDrawLine(wedge[0].x, wedge[0].y, wedge[2].x, wedge[2].y, light, t);
|
GfxDrawLine(wedge[0].x, wedge[0].y, wedge[2].x, wedge[2].y, light, t);
|
||||||
|
|
|
@ -44,7 +44,7 @@ static int _smallmap_cargo_count; ///< Number of cargos in the link stats leg
|
||||||
|
|
||||||
/** Structure for holding relevant data for legends in small map */
|
/** Structure for holding relevant data for legends in small map */
|
||||||
struct LegendAndColour {
|
struct LegendAndColour {
|
||||||
uint8_t colour; ///< Colour of the item on the map.
|
PixelColour colour; ///< Colour of the item on the map.
|
||||||
StringID legend; ///< String corresponding to the coloured item.
|
StringID legend; ///< String corresponding to the coloured item.
|
||||||
IndustryType type; ///< Type of industry. Only valid for industry entries.
|
IndustryType type; ///< Type of industry. Only valid for industry entries.
|
||||||
uint8_t height; ///< Height in tiles. Only valid for height legend entries.
|
uint8_t height; ///< Height in tiles. Only valid for height legend entries.
|
||||||
|
@ -63,16 +63,16 @@ static const int NUM_NO_COMPANY_ENTRIES = 4; ///< Number of entries in the owner
|
||||||
#define MK(a, b) {a, b, IT_INVALID, 0, CompanyID::Invalid(), true, false, false}
|
#define MK(a, b) {a, b, IT_INVALID, 0, CompanyID::Invalid(), true, false, false}
|
||||||
|
|
||||||
/** Macro for a height legend entry with configurable colour. */
|
/** Macro for a height legend entry with configurable colour. */
|
||||||
#define MC(col_break) {0, STR_TINY_BLACK_HEIGHT, IT_INVALID, 0, CompanyID::Invalid(), true, false, col_break}
|
#define MC(col_break) {{}, STR_TINY_BLACK_HEIGHT, IT_INVALID, 0, CompanyID::Invalid(), true, false, col_break}
|
||||||
|
|
||||||
/** Macro for non-company owned property entry of LegendAndColour */
|
/** Macro for non-company owned property entry of LegendAndColour */
|
||||||
#define MO(a, b) {a, b, IT_INVALID, 0, CompanyID::Invalid(), true, false, false}
|
#define MO(a, b) {a, b, IT_INVALID, 0, CompanyID::Invalid(), true, false, false}
|
||||||
|
|
||||||
/** Macro used for forcing a rebuild of the owner legend the first time it is used. */
|
/** Macro used for forcing a rebuild of the owner legend the first time it is used. */
|
||||||
#define MOEND() {0, STR_NULL, IT_INVALID, 0, OWNER_NONE, true, true, false}
|
#define MOEND() {{}, STR_NULL, IT_INVALID, 0, OWNER_NONE, true, true, false}
|
||||||
|
|
||||||
/** Macro for end of list marker in arrays of LegendAndColour */
|
/** Macro for end of list marker in arrays of LegendAndColour */
|
||||||
#define MKEND() {0, STR_NULL, IT_INVALID, 0, CompanyID::Invalid(), true, true, false}
|
#define MKEND() {{}, STR_NULL, IT_INVALID, 0, CompanyID::Invalid(), true, true, false}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Macro for break marker in arrays of LegendAndColour.
|
* Macro for break marker in arrays of LegendAndColour.
|
||||||
|
@ -149,7 +149,7 @@ static const LegendAndColour _legend_vegetation[] = {
|
||||||
|
|
||||||
static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = {
|
static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = {
|
||||||
MO(PC_WATER, STR_SMALLMAP_LEGENDA_WATER),
|
MO(PC_WATER, STR_SMALLMAP_LEGENDA_WATER),
|
||||||
MO(0x00, STR_SMALLMAP_LEGENDA_NO_OWNER), // This colour will vary depending on settings.
|
MO({}, STR_SMALLMAP_LEGENDA_NO_OWNER), // This colour will vary depending on settings.
|
||||||
MO(PC_DARK_RED, STR_SMALLMAP_LEGENDA_TOWNS),
|
MO(PC_DARK_RED, STR_SMALLMAP_LEGENDA_TOWNS),
|
||||||
MO(PC_DARK_GREY, STR_SMALLMAP_LEGENDA_INDUSTRIES),
|
MO(PC_DARK_GREY, STR_SMALLMAP_LEGENDA_INDUSTRIES),
|
||||||
/* The legend will be terminated the first time it is used. */
|
/* The legend will be terminated the first time it is used. */
|
||||||
|
@ -258,15 +258,15 @@ static const LegendAndColour * const _legend_table[] = {
|
||||||
|
|
||||||
#define MKCOLOUR(x) TO_LE32(x)
|
#define MKCOLOUR(x) TO_LE32(x)
|
||||||
|
|
||||||
#define MKCOLOUR_XXXX(x) (MKCOLOUR(0x01010101) * (uint)(x))
|
#define MKCOLOUR_XXXX(x) (MKCOLOUR(0x01010101) * (uint)(x.p))
|
||||||
#define MKCOLOUR_0XX0(x) (MKCOLOUR(0x00010100) * (uint)(x))
|
#define MKCOLOUR_0XX0(x) (MKCOLOUR(0x00010100) * (uint)(x.p))
|
||||||
#define MKCOLOUR_X00X(x) (MKCOLOUR(0x01000001) * (uint)(x))
|
#define MKCOLOUR_X00X(x) (MKCOLOUR(0x01000001) * (uint)(x.p))
|
||||||
|
|
||||||
#define MKCOLOUR_XYYX(x, y) (MKCOLOUR_X00X(x) | MKCOLOUR_0XX0(y))
|
#define MKCOLOUR_XYYX(x, y) (MKCOLOUR_X00X(x) | MKCOLOUR_0XX0(y))
|
||||||
|
|
||||||
#define MKCOLOUR_0000 MKCOLOUR_XXXX(0x00)
|
#define MKCOLOUR_0000 MKCOLOUR_XXXX(PixelColour{0x00})
|
||||||
#define MKCOLOUR_F00F MKCOLOUR_X00X(0xFF)
|
#define MKCOLOUR_F00F MKCOLOUR_X00X(PixelColour{0xFF})
|
||||||
#define MKCOLOUR_FFFF MKCOLOUR_XXXX(0xFF)
|
#define MKCOLOUR_FFFF MKCOLOUR_XXXX(PixelColour{0xFF})
|
||||||
|
|
||||||
#include "table/heightmap_colours.h"
|
#include "table/heightmap_colours.h"
|
||||||
|
|
||||||
|
@ -279,9 +279,9 @@ struct SmallMapColourScheme {
|
||||||
|
|
||||||
/** Available colour schemes for height maps. */
|
/** Available colour schemes for height maps. */
|
||||||
static SmallMapColourScheme _heightmap_schemes[] = {
|
static SmallMapColourScheme _heightmap_schemes[] = {
|
||||||
{{}, _green_map_heights, MKCOLOUR_XXXX(0x54)}, ///< Green colour scheme.
|
{{}, _green_map_heights, MKCOLOUR_XXXX(PixelColour{0x54})}, ///< Green colour scheme.
|
||||||
{{}, _dark_green_map_heights, MKCOLOUR_XXXX(0x62)}, ///< Dark green colour scheme.
|
{{}, _dark_green_map_heights, MKCOLOUR_XXXX(PixelColour{0x62})}, ///< Dark green colour scheme.
|
||||||
{{}, _violet_map_heights, MKCOLOUR_XXXX(0x81)}, ///< Violet colour scheme.
|
{{}, _violet_map_heights, MKCOLOUR_XXXX(PixelColour{0x81})}, ///< Violet colour scheme.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,7 +329,7 @@ void BuildLandLegend()
|
||||||
_legend_land_contours[i].col_break = j % rows == 0;
|
_legend_land_contours[i].col_break = j % rows == 0;
|
||||||
_legend_land_contours[i].end = false;
|
_legend_land_contours[i].end = false;
|
||||||
_legend_land_contours[i].height = j * delta;
|
_legend_land_contours[i].height = j * delta;
|
||||||
_legend_land_contours[i].colour = static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[_legend_land_contours[i].height]);
|
_legend_land_contours[i].colour = PixelColour{static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[_legend_land_contours[i].height])};
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
_legend_land_contours[i].end = true;
|
_legend_land_contours[i].end = true;
|
||||||
|
@ -340,7 +340,7 @@ void BuildLandLegend()
|
||||||
*/
|
*/
|
||||||
void BuildOwnerLegend()
|
void BuildOwnerLegend()
|
||||||
{
|
{
|
||||||
_legend_land_owners[1].colour = static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour);
|
_legend_land_owners[1].colour = PixelColour{static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour)};
|
||||||
|
|
||||||
int i = NUM_NO_COMPANY_ENTRIES;
|
int i = NUM_NO_COMPANY_ENTRIES;
|
||||||
for (const Company *c : Company::Iterate()) {
|
for (const Company *c : Company::Iterate()) {
|
||||||
|
@ -607,7 +607,7 @@ uint32_t GetSmallMapOwnerPixels(TileIndex tile, TileType t, IncludeHeightmap inc
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Vehicle colours in #SMT_VEHICLES mode. Indexed by #VehicleType. */
|
/** Vehicle colours in #SMT_VEHICLES mode. Indexed by #VehicleType. */
|
||||||
static const uint8_t _vehicle_type_colours[6] = {
|
static const PixelColour _vehicle_type_colours[6] = {
|
||||||
PC_RED, PC_YELLOW, PC_LIGHT_BLUE, PC_WHITE, PC_BLACK, PC_RED
|
PC_RED, PC_YELLOW, PC_LIGHT_BLUE, PC_WHITE, PC_BLACK, PC_RED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -935,7 +935,7 @@ protected:
|
||||||
uint8_t *val8 = (uint8_t *)&val;
|
uint8_t *val8 = (uint8_t *)&val;
|
||||||
int idx = std::max(0, -start_pos);
|
int idx = std::max(0, -start_pos);
|
||||||
for (int pos = std::max(0, start_pos); pos < end_pos; pos++) {
|
for (int pos = std::max(0, start_pos); pos < end_pos; pos++) {
|
||||||
blitter->SetPixel(dst, idx, 0, val8[idx]);
|
blitter->SetPixel(dst, idx, 0, PixelColour{val8[idx]});
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
/* Switch to next tile in the column */
|
/* Switch to next tile in the column */
|
||||||
|
@ -973,7 +973,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate pointer to pixel and the colour */
|
/* Calculate pointer to pixel and the colour */
|
||||||
uint8_t colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : PC_WHITE;
|
PixelColour colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : PC_WHITE;
|
||||||
|
|
||||||
/* And draw either one or two pixels depending on clipping */
|
/* And draw either one or two pixels depending on clipping */
|
||||||
blitter->SetPixel(dpi->dst_ptr, x, y, colour);
|
blitter->SetPixel(dpi->dst_ptr, x, y, colour);
|
||||||
|
@ -1348,7 +1348,7 @@ protected:
|
||||||
if (type == _smallmap_industry_highlight) {
|
if (type == _smallmap_industry_highlight) {
|
||||||
if (_smallmap_industry_highlight_state) return MKCOLOUR_XXXX(PC_WHITE);
|
if (_smallmap_industry_highlight_state) return MKCOLOUR_XXXX(PC_WHITE);
|
||||||
} else {
|
} else {
|
||||||
return GetIndustrySpec(type)->map_colour * 0x01010101;
|
return GetIndustrySpec(type)->map_colour.p * 0x01010101;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Otherwise make it disappear */
|
/* Otherwise make it disappear */
|
||||||
|
@ -1644,7 +1644,7 @@ public:
|
||||||
i = 1;
|
i = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t legend_colour = tbl->colour;
|
PixelColour legend_colour = tbl->colour;
|
||||||
|
|
||||||
std::array<StringParameter, 2> params{};
|
std::array<StringParameter, 2> params{};
|
||||||
switch (this->map_type) {
|
switch (this->map_type) {
|
||||||
|
|
|
@ -5079,7 +5079,7 @@ StationIDStack FlowStatMap::DeleteFlows(StationID via)
|
||||||
FlowStat &s_flows = f_it->second;
|
FlowStat &s_flows = f_it->second;
|
||||||
s_flows.ChangeShare(via, INT_MIN);
|
s_flows.ChangeShare(via, INT_MIN);
|
||||||
if (s_flows.GetShares()->empty()) {
|
if (s_flows.GetShares()->empty()) {
|
||||||
ret.Push(f_it->first.base());
|
ret.Push(f_it->first);
|
||||||
this->erase(f_it++);
|
this->erase(f_it++);
|
||||||
} else {
|
} else {
|
||||||
++f_it;
|
++f_it;
|
||||||
|
|
|
@ -225,7 +225,7 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoType c
|
||||||
|
|
||||||
int padding = ScaleGUITrad(1);
|
int padding = ScaleGUITrad(1);
|
||||||
int width = right - left;
|
int width = right - left;
|
||||||
int colour = cs->rating_colour;
|
PixelColour colour = cs->rating_colour;
|
||||||
TextColour tc = GetContrastColour(colour);
|
TextColour tc = GetContrastColour(colour);
|
||||||
uint w = std::min(amount + 5, units_full) * width / units_full;
|
uint w = std::min(amount + 5, units_full) * width / units_full;
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct RoadStop;
|
||||||
struct StationSpec;
|
struct StationSpec;
|
||||||
struct Waypoint;
|
struct Waypoint;
|
||||||
|
|
||||||
using StationIDStack = SmallStack<StationID::BaseType, StationID::BaseType, StationID::Invalid().base(), 8, StationID::End().base()>;
|
using StationIDStack = SmallStack<StationID, StationID::BaseType, StationID::Invalid().base(), 8, StationID::End().base()>;
|
||||||
|
|
||||||
/** Station types */
|
/** Station types */
|
||||||
enum class StationType : uint8_t {
|
enum class StationType : uint8_t {
|
||||||
|
|
|
@ -1134,7 +1134,7 @@ enum IndustryTypes : uint8_t {
|
||||||
{r1, r2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, m, \
|
{r1, r2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, m, \
|
||||||
{INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO}, \
|
{INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO}, \
|
||||||
{{im1, 0}, {im2, 0}, {im3, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, \
|
{{im1, 0}, {im2, 0}, {im3, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, \
|
||||||
pr, clim, bev, col, in, intx, s1, s2, s3, STR_UNDEFINED, {ai1, ai2, ai3, ai4}, {ag1, ag2, ag3, ag4}, \
|
pr, clim, bev, PixelColour{col}, in, intx, s1, s2, s3, STR_UNDEFINED, {ai1, ai2, ai3, ai4}, {ag1, ag2, ag3, ag4}, \
|
||||||
IndustryCallbackMasks{}, true, SubstituteGRFFileProps(IT_INVALID), snd, {}, \
|
IndustryCallbackMasks{}, true, SubstituteGRFFileProps(IT_INVALID), snd, {}, \
|
||||||
{{p1, p2}}, {{a1, a2, a3}}}
|
{{p1, p2}}, {{a1, a2, a3}}}
|
||||||
/* Format:
|
/* Format:
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
* @param classes Classes of this cargo type. @see CargoClass
|
* @param classes Classes of this cargo type. @see CargoClass
|
||||||
*/
|
*/
|
||||||
#define MK(bt, label, colour, weight, mult, ip, td1, td2, freight, tae, str_plural, str_singular, str_volume, classes) \
|
#define MK(bt, label, colour, weight, mult, ip, td1, td2, freight, tae, str_plural, str_singular, str_volume, classes) \
|
||||||
{label, bt, colour, colour, weight, mult, classes, ip, {td1, td2}, freight, tae, INVALID_TPE, TOWN_PRODUCTION_DIVISOR, CargoCallbackMasks{}, \
|
{label, bt, PixelColour{colour}, PixelColour{colour}, weight, mult, classes, ip, {td1, td2}, freight, tae, INVALID_TPE, TOWN_PRODUCTION_DIVISOR, CargoCallbackMasks{}, \
|
||||||
MK_STR_CARGO_PLURAL(str_plural), MK_STR_CARGO_SINGULAR(str_singular), str_volume, MK_STR_QUANTITY(str_plural), MK_STR_ABBREV(str_plural), \
|
MK_STR_CARGO_PLURAL(str_plural), MK_STR_CARGO_SINGULAR(str_singular), str_volume, MK_STR_QUANTITY(str_plural), MK_STR_ABBREV(str_plural), \
|
||||||
MK_SPRITE(str_plural), nullptr, nullptr, 0}
|
MK_SPRITE(str_plural), nullptr, nullptr, 0}
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ static const RailTypeInfo _original_railtypes[] = {
|
||||||
RailTypeLabelList(),
|
RailTypeLabelList(),
|
||||||
|
|
||||||
/* map colour */
|
/* map colour */
|
||||||
0x0A,
|
PC_GREY,
|
||||||
|
|
||||||
/* introduction date */
|
/* introduction date */
|
||||||
CalendarTime::INVALID_DATE,
|
CalendarTime::INVALID_DATE,
|
||||||
|
@ -200,7 +200,7 @@ static const RailTypeInfo _original_railtypes[] = {
|
||||||
RailTypeLabelList(),
|
RailTypeLabelList(),
|
||||||
|
|
||||||
/* map colour */
|
/* map colour */
|
||||||
0x0A,
|
PC_GREY,
|
||||||
|
|
||||||
/* introduction date */
|
/* introduction date */
|
||||||
CalendarTime::INVALID_DATE,
|
CalendarTime::INVALID_DATE,
|
||||||
|
@ -298,7 +298,7 @@ static const RailTypeInfo _original_railtypes[] = {
|
||||||
RailTypeLabelList(),
|
RailTypeLabelList(),
|
||||||
|
|
||||||
/* map colour */
|
/* map colour */
|
||||||
0x0A,
|
PC_GREY,
|
||||||
|
|
||||||
/* introduction date */
|
/* introduction date */
|
||||||
CalendarTime::INVALID_DATE,
|
CalendarTime::INVALID_DATE,
|
||||||
|
@ -396,7 +396,7 @@ static const RailTypeInfo _original_railtypes[] = {
|
||||||
RailTypeLabelList(),
|
RailTypeLabelList(),
|
||||||
|
|
||||||
/* map colour */
|
/* map colour */
|
||||||
0x0A,
|
PC_GREY,
|
||||||
|
|
||||||
/* introduction date */
|
/* introduction date */
|
||||||
CalendarTime::INVALID_DATE,
|
CalendarTime::INVALID_DATE,
|
||||||
|
|
|
@ -81,7 +81,7 @@ static const RoadTypeInfo _original_roadtypes[] = {
|
||||||
RoadTypeLabelList(),
|
RoadTypeLabelList(),
|
||||||
|
|
||||||
/* map colour */
|
/* map colour */
|
||||||
0x01,
|
PC_BLACK,
|
||||||
|
|
||||||
/* introduction date */
|
/* introduction date */
|
||||||
CalendarTime::MIN_DATE,
|
CalendarTime::MIN_DATE,
|
||||||
|
@ -162,7 +162,7 @@ static const RoadTypeInfo _original_roadtypes[] = {
|
||||||
RoadTypeLabelList(),
|
RoadTypeLabelList(),
|
||||||
|
|
||||||
/* map colour */
|
/* map colour */
|
||||||
0x01,
|
PC_BLACK,
|
||||||
|
|
||||||
/* introduction date */
|
/* introduction date */
|
||||||
CalendarTime::INVALID_DATE,
|
CalendarTime::INVALID_DATE,
|
||||||
|
|
|
@ -8,22 +8,22 @@
|
||||||
/** @file string_colours.h The colour translation of GRF's strings. */
|
/** @file string_colours.h The colour translation of GRF's strings. */
|
||||||
|
|
||||||
/** Colour mapping for #TextColour. */
|
/** Colour mapping for #TextColour. */
|
||||||
static const uint8_t _string_colourmap[17] = {
|
static constexpr PixelColour _string_colourmap[17] = {
|
||||||
150, // TC_BLUE
|
PixelColour{150}, // TC_BLUE
|
||||||
12, // TC_SILVER
|
PixelColour{ 12}, // TC_SILVER
|
||||||
189, // TC_GOLD
|
PixelColour{189}, // TC_GOLD
|
||||||
184, // TC_RED
|
PixelColour{184}, // TC_RED
|
||||||
174, // TC_PURPLE
|
PixelColour{174}, // TC_PURPLE
|
||||||
30, // TC_LIGHT_BROWN
|
PixelColour{ 30}, // TC_LIGHT_BROWN
|
||||||
195, // TC_ORANGE
|
PixelColour{195}, // TC_ORANGE
|
||||||
209, // TC_GREEN
|
PixelColour{209}, // TC_GREEN
|
||||||
68, // TC_YELLOW
|
PixelColour{ 68}, // TC_YELLOW
|
||||||
95, // TC_DARK_GREEN
|
PixelColour{ 95}, // TC_DARK_GREEN
|
||||||
79, // TC_CREAM
|
PixelColour{ 79}, // TC_CREAM
|
||||||
116, // TC_BROWN
|
PixelColour{116}, // TC_BROWN
|
||||||
15, // TC_WHITE
|
PixelColour{ 15}, // TC_WHITE
|
||||||
152, // TC_LIGHT_BLUE
|
PixelColour{152}, // TC_LIGHT_BLUE
|
||||||
6, // TC_GREY
|
PixelColour{ 6}, // TC_GREY
|
||||||
133, // TC_DARK_BLUE
|
PixelColour{133}, // TC_DARK_BLUE
|
||||||
1, // TC_BLACK
|
PixelColour{ 1}, // TC_BLACK
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,8 +21,6 @@ public:
|
||||||
this->height = FontCache::GetDefaultFontHeight(this->fs);
|
this->height = FontCache::GetDefaultFontHeight(this->fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetUnicodeGlyph(char32_t, SpriteID) override {}
|
|
||||||
void InitializeUnicodeGlyphMap() override {}
|
|
||||||
void ClearFontCache() override {}
|
void ClearFontCache() override {}
|
||||||
const Sprite *GetGlyph(GlyphID) override { return nullptr; }
|
const Sprite *GetGlyph(GlyphID) override { return nullptr; }
|
||||||
uint GetGlyphWidth(GlyphID) override { return this->height / 2; }
|
uint GetGlyphWidth(GlyphID) override { return this->height / 2; }
|
||||||
|
|
|
@ -741,7 +741,7 @@ public:
|
||||||
*/
|
*/
|
||||||
inline StationIDStack GetNextStoppingStation() const
|
inline StationIDStack GetNextStoppingStation() const
|
||||||
{
|
{
|
||||||
return (this->orders == nullptr) ? StationID::Invalid().base() : this->orders->GetNextStoppingStation(this);
|
return (this->orders == nullptr) ? StationID::Invalid() : this->orders->GetNextStoppingStation(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetRefitCaps();
|
void ResetRefitCaps();
|
||||||
|
|
|
@ -697,7 +697,7 @@ static void DrawVehicleRefitWindow(const RefitOptions &refits, const RefitOption
|
||||||
bool rtl = _current_text_dir == TD_RTL;
|
bool rtl = _current_text_dir == TD_RTL;
|
||||||
uint iconwidth = std::max(GetSpriteSize(SPR_CIRCLE_FOLDED).width, GetSpriteSize(SPR_CIRCLE_UNFOLDED).width);
|
uint iconwidth = std::max(GetSpriteSize(SPR_CIRCLE_FOLDED).width, GetSpriteSize(SPR_CIRCLE_UNFOLDED).width);
|
||||||
uint iconheight = GetSpriteSize(SPR_CIRCLE_FOLDED).height;
|
uint iconheight = GetSpriteSize(SPR_CIRCLE_FOLDED).height;
|
||||||
int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
|
PixelColour linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
|
||||||
|
|
||||||
int iconleft = rtl ? ir.right - iconwidth : ir.left;
|
int iconleft = rtl ? ir.right - iconwidth : ir.left;
|
||||||
int iconcenter = rtl ? ir.right - iconwidth / 2 : ir.left + iconwidth / 2;
|
int iconcenter = rtl ? ir.right - iconwidth / 2 : ir.left + iconwidth / 2;
|
||||||
|
|
|
@ -1732,13 +1732,13 @@ static void ViewportDrawDirtyBlocks()
|
||||||
int right = UnScaleByZoom(dpi->width, dpi->zoom);
|
int right = UnScaleByZoom(dpi->width, dpi->zoom);
|
||||||
int bottom = UnScaleByZoom(dpi->height, dpi->zoom);
|
int bottom = UnScaleByZoom(dpi->height, dpi->zoom);
|
||||||
|
|
||||||
int colour = _string_colourmap[_dirty_block_colour & 0xF];
|
PixelColour colour = _string_colourmap[_dirty_block_colour & 0xF];
|
||||||
|
|
||||||
dst = dpi->dst_ptr;
|
dst = dpi->dst_ptr;
|
||||||
|
|
||||||
uint8_t bo = UnScaleByZoom(dpi->left + dpi->top, dpi->zoom) & 1;
|
uint8_t bo = UnScaleByZoom(dpi->left + dpi->top, dpi->zoom) & 1;
|
||||||
do {
|
do {
|
||||||
for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8_t)colour);
|
for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, colour);
|
||||||
dst = blitter->MoveTo(dst, 0, 1);
|
dst = blitter->MoveTo(dst, 0, 1);
|
||||||
} while (--bottom > 0);
|
} while (--bottom > 0);
|
||||||
}
|
}
|
||||||
|
@ -1761,7 +1761,7 @@ static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector *
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ss.flags.Test(ViewportStringFlag::TextColour)) {
|
if (ss.flags.Test(ViewportStringFlag::TextColour)) {
|
||||||
if (ss.colour != INVALID_COLOUR) colour = static_cast<TextColour>(GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR);
|
if (ss.colour != INVALID_COLOUR) colour = GetColourGradient(ss.colour, SHADE_LIGHTER).ToTextColour();
|
||||||
}
|
}
|
||||||
|
|
||||||
int left = x + WidgetDimensions::scaled.fullbevel.left;
|
int left = x + WidgetDimensions::scaled.fullbevel.left;
|
||||||
|
|
|
@ -302,11 +302,11 @@ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, Fra
|
||||||
} else {
|
} else {
|
||||||
assert(colour < COLOUR_END);
|
assert(colour < COLOUR_END);
|
||||||
|
|
||||||
const uint dark = GetColourGradient(colour, SHADE_DARK);
|
const PixelColour dark = GetColourGradient(colour, SHADE_DARK);
|
||||||
const uint medium_dark = GetColourGradient(colour, SHADE_LIGHT);
|
const PixelColour medium_dark = GetColourGradient(colour, SHADE_LIGHT);
|
||||||
const uint medium_light = GetColourGradient(colour, SHADE_LIGHTER);
|
const PixelColour medium_light = GetColourGradient(colour, SHADE_LIGHTER);
|
||||||
const uint light = GetColourGradient(colour, SHADE_LIGHTEST);
|
const PixelColour light = GetColourGradient(colour, SHADE_LIGHTEST);
|
||||||
uint interior;
|
PixelColour interior;
|
||||||
|
|
||||||
Rect outer = {left, top, right, bottom}; // Outside rectangle
|
Rect outer = {left, top, right, bottom}; // Outside rectangle
|
||||||
Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
|
Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
|
||||||
|
@ -469,7 +469,7 @@ static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint3
|
||||||
row_height = r.Height() / num_rows;
|
row_height = r.Height() / num_rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
int col = GetColourGradient(colour, SHADE_LIGHTER);
|
PixelColour col = GetColourGradient(colour, SHADE_LIGHTER);
|
||||||
|
|
||||||
int x = r.left;
|
int x = r.left;
|
||||||
for (int ctr = num_columns; ctr > 1; ctr--) {
|
for (int ctr = num_columns; ctr > 1; ctr--) {
|
||||||
|
@ -515,8 +515,8 @@ static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_
|
||||||
DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER);
|
DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER);
|
||||||
DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
|
DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
|
||||||
|
|
||||||
int c1 = GetColourGradient(colour, SHADE_DARK);
|
PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
|
||||||
int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
|
PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
|
||||||
|
|
||||||
/* draw "shaded" background */
|
/* draw "shaded" background */
|
||||||
GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
|
GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
|
||||||
|
@ -554,8 +554,8 @@ static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool l
|
||||||
DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER);
|
DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER);
|
||||||
DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
|
DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
|
||||||
|
|
||||||
int c1 = GetColourGradient(colour, SHADE_DARK);
|
PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
|
||||||
int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
|
PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
|
||||||
|
|
||||||
/* draw "shaded" background */
|
/* draw "shaded" background */
|
||||||
GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
|
GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
|
||||||
|
@ -593,8 +593,8 @@ static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colo
|
||||||
|
|
||||||
if (!str.empty()) x2 = DrawString(r.left + WidgetDimensions::scaled.frametext.left, r.right - WidgetDimensions::scaled.frametext.right, r.top, str, text_colour, align, false, fs);
|
if (!str.empty()) x2 = DrawString(r.left + WidgetDimensions::scaled.frametext.left, r.right - WidgetDimensions::scaled.frametext.right, r.top, str, text_colour, align, false, fs);
|
||||||
|
|
||||||
int c1 = GetColourGradient(colour, SHADE_DARK);
|
PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
|
||||||
int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
|
PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
|
||||||
|
|
||||||
/* If the frame has text, adjust the top bar to fit half-way through */
|
/* If the frame has text, adjust the top bar to fit half-way through */
|
||||||
Rect inner = r.Shrink(ScaleGUITrad(1));
|
Rect inner = r.Shrink(ScaleGUITrad(1));
|
||||||
|
@ -791,7 +791,7 @@ void Window::DrawWidgets() const
|
||||||
Rect outer = widget->GetCurrentRect();
|
Rect outer = widget->GetCurrentRect();
|
||||||
Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel).Expand(1);
|
Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel).Expand(1);
|
||||||
|
|
||||||
int colour = _string_colourmap[_window_highlight_colour ? widget->GetHighlightColour() : TC_WHITE];
|
PixelColour colour = _string_colourmap[_window_highlight_colour ? widget->GetHighlightColour() : TC_WHITE];
|
||||||
|
|
||||||
GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
|
GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
|
||||||
GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
|
GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
|
||||||
|
|
Loading…
Reference in New Issue