summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2016-10-14 19:14:40 +0200
committerRémi Verschelde <rverschelde@gmail.com>2016-10-15 12:20:53 +0200
commite6dc51a0f764dcd7cd07482c022c1e92e6a4da3d (patch)
tree4562ecd03b4d438cae0b179968f091130dadc14d /modules
parentcbf52606f4928df46fc89d37d781bad782f0616e (diff)
chibi: Move to a module
Diffstat (limited to 'modules')
-rw-r--r--modules/chibi/SCsub7
-rw-r--r--modules/chibi/config.py6
-rw-r--r--modules/chibi/cp_config.h52
-rw-r--r--modules/chibi/cp_envelope.cpp369
-rw-r--r--modules/chibi/cp_envelope.h129
-rw-r--r--modules/chibi/cp_file_access_wrapper.h96
-rw-r--r--modules/chibi/cp_instrument.cpp344
-rw-r--r--modules/chibi/cp_instrument.h219
-rw-r--r--modules/chibi/cp_loader.h64
-rw-r--r--modules/chibi/cp_loader_it.cpp216
-rw-r--r--modules/chibi/cp_loader_it.h125
-rw-r--r--modules/chibi/cp_loader_it_info.cpp268
-rw-r--r--modules/chibi/cp_loader_it_instruments.cpp224
-rw-r--r--modules/chibi/cp_loader_it_patterns.cpp166
-rw-r--r--modules/chibi/cp_loader_it_samples.cpp620
-rw-r--r--modules/chibi/cp_loader_mod.cpp482
-rw-r--r--modules/chibi/cp_loader_mod.h52
-rw-r--r--modules/chibi/cp_loader_s3m.cpp413
-rw-r--r--modules/chibi/cp_loader_s3m.h111
-rw-r--r--modules/chibi/cp_loader_xm.cpp752
-rw-r--r--modules/chibi/cp_loader_xm.h89
-rw-r--r--modules/chibi/cp_mixer.h115
-rw-r--r--modules/chibi/cp_note.h102
-rw-r--r--modules/chibi/cp_order.h43
-rw-r--r--modules/chibi/cp_pattern.cpp574
-rw-r--r--modules/chibi/cp_pattern.h94
-rw-r--r--modules/chibi/cp_player_data.cpp151
-rw-r--r--modules/chibi/cp_player_data.h582
-rw-r--r--modules/chibi/cp_player_data_control.cpp324
-rw-r--r--modules/chibi/cp_player_data_effects.cpp1232
-rw-r--r--modules/chibi/cp_player_data_envelopes.cpp89
-rw-r--r--modules/chibi/cp_player_data_events.cpp679
-rw-r--r--modules/chibi/cp_player_data_filter.cpp89
-rw-r--r--modules/chibi/cp_player_data_nna.cpp144
-rw-r--r--modules/chibi/cp_player_data_notes.cpp345
-rw-r--r--modules/chibi/cp_player_data_utils.cpp138
-rw-r--r--modules/chibi/cp_sample.cpp203
-rw-r--r--modules/chibi/cp_sample.h112
-rw-r--r--modules/chibi/cp_sample_defs.h97
-rw-r--r--modules/chibi/cp_sample_manager.cpp78
-rw-r--r--modules/chibi/cp_sample_manager.h99
-rw-r--r--modules/chibi/cp_song.cpp957
-rw-r--r--modules/chibi/cp_song.h261
-rw-r--r--modules/chibi/cp_tables.cpp254
-rw-r--r--modules/chibi/cp_tables.h67
-rw-r--r--modules/chibi/event_stream_chibi.cpp872
-rw-r--r--modules/chibi/event_stream_chibi.h314
-rw-r--r--modules/chibi/register_types.cpp41
-rw-r--r--modules/chibi/register_types.h30
49 files changed, 12890 insertions, 0 deletions
diff --git a/modules/chibi/SCsub b/modules/chibi/SCsub
new file mode 100644
index 0000000000..e39554977a
--- /dev/null
+++ b/modules/chibi/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+Import('env_modules')
+
+env_chibi = env_modules.Clone()
+
+# Godot source files
+env_chibi.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/chibi/config.py b/modules/chibi/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/chibi/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/chibi/cp_config.h b/modules/chibi/cp_config.h
new file mode 100644
index 0000000000..2ad704ace7
--- /dev/null
+++ b/modules/chibi/cp_config.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* cp_config.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_CONFIG_H
+#define CP_CONFIG_H
+
+
+#include "typedefs.h"
+#include "error_macros.h"
+#include "math_funcs.h"
+#include "os/memory.h"
+#include "os/copymem.h"
+
+#define CP_PRINTERR(m_err) ERR_PRINT(m_err)
+#define CP_ERR_COND(m_cond) ERR_FAIL_COND(m_cond)
+#define CP_ERR_COND_V(m_cond,m_ret) ERR_FAIL_COND_V(m_cond,m_ret)
+#define CP_FAIL_INDEX(m_index,m_size) ERR_FAIL_INDEX(m_index,m_size)
+#define CP_FAIL_INDEX_V(m_index,m_size,m_ret) ERR_FAIL_INDEX_V(m_index,m_size,m_ret)
+#define cp_intabs(m_val) ABS(m_val)
+
+#define CP_ALLOC(m_mem) memalloc(m_mem)
+#define CP_REALLOC(m_mem,m_size) memrealloc(m_mem,m_size)
+#define CP_FREE(m_mem) memfree(m_mem)
+
+#define cp_memzero(m_mem,m_size) zeromem(m_mem,m_size)
+
+#endif // CP_CONFIG_H
diff --git a/modules/chibi/cp_envelope.cpp b/modules/chibi/cp_envelope.cpp
new file mode 100644
index 0000000000..9892b6d4b0
--- /dev/null
+++ b/modules/chibi/cp_envelope.cpp
@@ -0,0 +1,369 @@
+/*************************************************************************/
+/* cp_envelope.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_envelope.h"
+
+
+CPEnvelope::CPEnvelope() {
+
+
+ reset();
+}
+
+void CPEnvelope::reset() {
+
+
+
+ on=false;
+ carry=false;
+ loop_on=false;
+ loop_begin_node=0;
+ loop_end_node=0;
+ sustain_loop_on=false;
+ sustain_loop_begin_node=0;
+ sustain_loop_end_node=0;
+ node_count=0;
+
+}
+
+int CPEnvelope::get_height_at_pos(int pos) {
+
+ if (node_count && pos>node[node_count-1].tick_offset)
+ return node[node_count-1].value;
+
+ int begin_x,begin_y;
+ int end_x,end_y,xdif;
+ int count=0;
+ int limit=-1;
+
+ if (node_count<2) return NO_POINT;
+
+ while ((count<node_count) && (limit==-1)) {
+
+ if (node[count].tick_offset>=pos) limit=count;
+ count++;
+ }
+
+ if (pos==0) return node[0].value;
+
+ if (limit==-1) return NO_POINT;
+
+ begin_x=node[limit-1].tick_offset;
+ end_x=node[limit].tick_offset;
+ begin_y=node[limit-1].value;
+ end_y=node[limit].value;
+
+ xdif=end_x-begin_x;
+ return begin_y+((pos-begin_x)*(end_y-begin_y))/(xdif?xdif:1);
+}
+
+/*
+int CPEnvelope::get_fx_height_at_pos(int pos) {
+
+ if (node_count && pos>node[node_count-1].tick_offset)
+ return node[node_count-1].value<<FX_HEIGHT_BITS;
+
+ int begin_x,begin_y;
+ int end_x,end_y,xdif;
+ int count=0;
+ int limit=-1;
+
+ if (node_count<2) return NO_POINT;
+
+ while ((count<node_count) && (limit==-1)) {
+
+ if (node[count].tick_offset>=pos) limit=count;
+ count++;
+ }
+
+ if (pos==0) return node[0].value<<FX_HEIGHT_BITS;
+
+ if (limit==-1) return NO_POINT;
+
+ begin_x=node[limit-1].tick_offset;
+ end_x=node[limit].tick_offset;
+ begin_y=node[limit-1].value;
+ end_y=node[limit].value;
+
+ xdif=end_x-begin_x;
+ return (begin_y<<FX_HEIGHT_BITS)+((pos-begin_x)*(end_y-begin_y)*(int)(1<<FX_HEIGHT_BITS))/(xdif?xdif:1);
+}
+*/
+
+float CPEnvelope::get_interp_height_at_pos(float pos) {
+
+ if (node_count && pos>node[node_count-1].tick_offset)
+ return node[node_count-1].value;
+
+ int begin_x,begin_y;
+ int end_x,end_y,xdif;
+ int count=0;
+ int limit=-1;
+
+ if (node_count<2) return NO_POINT;
+
+ while ((count<node_count) && (limit==-1)) {
+
+ if (node[count].tick_offset>=pos) limit=count;
+ count++;
+ }
+
+ if (pos==0) return node[0].value;
+
+ if (limit==-1) return NO_POINT;
+
+ begin_x=node[limit-1].tick_offset;
+ end_x=node[limit].tick_offset;
+ begin_y=node[limit-1].value;
+ end_y=node[limit].value;
+
+ xdif=end_x-begin_x;
+ return begin_y+((pos-begin_x)*(end_y-begin_y))/(xdif?xdif:1);
+}
+
+void CPEnvelope::set_position(int p_node,int p_x,int p_y) {
+
+ if (p_node>=node_count) return;
+
+
+
+ if (p_node==0) {
+
+ p_x=0;
+
+ } else if (p_x<=node[p_node-1].tick_offset) {
+
+ p_x=node[p_node-1].tick_offset+1;
+
+ } else if ((p_node<(node_count-1)) && (p_x>=node[p_node+1].tick_offset)) {
+
+ p_x=node[p_node+1].tick_offset-1;
+ }
+
+ if (p_x>=9999) p_x=9999;
+
+ if (p_y>max_value) p_y=max_value;
+ if (p_y<min_value) p_y=min_value;
+
+
+ node[p_node].tick_offset=p_x;
+ node[p_node].value=p_y;
+
+
+
+}
+
+int CPEnvelope::add_position(int p_x,int p_y,bool p_move_loops) {
+
+ if (node_count==MAX_POINTS) return -1;
+
+
+ int i,new_node;
+
+ // if this is assigning an existing node, let's quit.
+ for (i=0;i<node_count;i++) if (p_x==node[i].tick_offset) return -1;
+
+
+ i=0;
+ while ((i<node_count) && (p_x>=node[i].tick_offset)) i++;
+
+ new_node=i;
+ node_count++;
+
+ if (p_move_loops) {
+ if (loop_begin_node>=new_node) loop_begin_node++;
+ if (loop_end_node>=new_node) loop_end_node++;
+ if (sustain_loop_begin_node>=new_node) sustain_loop_begin_node++;
+ if (sustain_loop_end_node>=new_node) sustain_loop_end_node++;
+ }
+ for (i=node_count-1;i>new_node;i--) node[i]=node[i-1];
+
+
+
+ set_position(new_node,p_x,p_y);
+
+
+
+ return new_node;
+
+}
+
+void CPEnvelope::set_loop_begin(int pos) {
+
+ if ((pos<0) || (pos>=node_count)) return;
+
+
+
+ loop_begin_node=pos;
+
+ if (loop_end_node<loop_begin_node) loop_end_node=loop_begin_node;
+
+
+
+}
+
+void CPEnvelope::set_loop_end(int pos) {
+
+ if ((pos<0) || (pos>=node_count)) return;
+
+
+
+ loop_end_node=pos;
+
+ if (loop_end_node<loop_begin_node) loop_begin_node=loop_end_node;
+
+
+
+
+}
+
+
+void CPEnvelope::set_sustain_loop_begin(int pos) {
+
+ if ((pos<0) || (pos>=node_count)) return;
+
+
+
+ sustain_loop_begin_node=pos;
+
+ if (sustain_loop_end_node<sustain_loop_begin_node) sustain_loop_end_node=sustain_loop_begin_node;
+
+
+
+}
+
+void CPEnvelope::set_sustain_loop_end(int pos) {
+
+ if ((pos<0) || (pos>=node_count)) return;
+
+
+
+ sustain_loop_end_node=pos;
+
+ if (sustain_loop_end_node<sustain_loop_begin_node) sustain_loop_begin_node=sustain_loop_end_node;
+
+
+
+}
+
+void CPEnvelope::set_loop_enabled(bool p_enabled) {
+
+ loop_on=p_enabled;
+}
+bool CPEnvelope::is_loop_enabled() {
+
+ return loop_on;
+}
+
+
+void CPEnvelope::set_sustain_loop_enabled(bool p_enabled) {
+
+ sustain_loop_on=p_enabled;
+}
+bool CPEnvelope::is_sustain_loop_enabled() {
+
+ return sustain_loop_on;
+}
+
+void CPEnvelope::del_position(int p_node) {
+
+ if ((node_count<3) || (p_node<=0) || (p_node>=node_count)) return;
+
+
+
+ int i;
+
+ if (loop_begin_node>=p_node) loop_begin_node--;
+ if (loop_end_node>=p_node) loop_end_node--;
+ if (sustain_loop_begin_node>=p_node) sustain_loop_begin_node--;
+ if (sustain_loop_end_node>=p_node) sustain_loop_end_node--;
+
+ for (i=p_node;i<node_count-1;i++) node[i]=node[i+1];
+
+ node_count--;
+
+
+
+}
+
+uint8_t CPEnvelope::get_loop_begin() {
+
+
+ return loop_begin_node;
+}
+uint8_t CPEnvelope::get_loop_end() {
+
+ return loop_end_node;
+}
+
+uint8_t CPEnvelope::get_sustain_loop_begin() {
+
+
+ return sustain_loop_begin_node;
+}
+uint8_t CPEnvelope::get_sustain_loop_end() {
+
+ return sustain_loop_end_node;
+}
+
+
+
+void CPEnvelope::set_enabled(bool p_enabled) {
+
+ on=p_enabled;
+}
+
+bool CPEnvelope::is_enabled() {
+
+ return on;
+}
+
+void CPEnvelope::set_carry_enabled(bool p_enabled) {
+
+ carry=p_enabled;
+}
+bool CPEnvelope::is_carry_enabled() {
+
+ return carry;
+}
+
+uint8_t CPEnvelope::get_node_count() {
+
+ return node_count;
+}
+
+const CPEnvelope::Point& CPEnvelope::get_node(int p_idx) {
+
+ if (p_idx<0 || p_idx>=node_count)
+ return node[node_count-1];
+
+ return node[p_idx];
+
+}
+
+
diff --git a/modules/chibi/cp_envelope.h b/modules/chibi/cp_envelope.h
new file mode 100644
index 0000000000..d1ada53f7d
--- /dev/null
+++ b/modules/chibi/cp_envelope.h
@@ -0,0 +1,129 @@
+/*************************************************************************/
+/* cp_envelope.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_ENVELOPE_H
+#define CP_ENVELOPE_H
+
+#include "cp_config.h"
+
+/**envelope?
+ *@author Juan Linietsky
+ */
+
+/******************************
+ envelope.h
+ ----------
+
+Proovides an envelope, and basic functions
+for it that can be used for both player
+and interface
+********************************/
+
+
+class CPEnvelope {
+ enum {
+
+ MAX_POINTS=25
+ };
+
+ struct Point {
+
+ uint16_t tick_offset;
+ int16_t value;
+ };
+
+ Point node[MAX_POINTS];
+
+ int8_t node_count;
+
+ bool on;
+ bool carry;
+
+ bool loop_on;
+
+ uint8_t loop_begin_node;
+ uint8_t loop_end_node;
+
+ bool sustain_loop_on;
+ uint8_t sustain_loop_begin_node;
+ uint8_t sustain_loop_end_node;
+
+
+ int8_t max_value;
+ int8_t min_value;
+
+
+public:
+ enum {
+
+ NO_POINT=-5000,
+ };
+
+ void set_max(int8_t p_max) { max_value=p_max; }
+ int8_t get_max() { return max_value; }
+ void set_min(int8_t p_min) { min_value=p_min; }
+ int8_t get_min() { return min_value; }
+
+ uint8_t get_node_count();
+ const Point& get_node(int p_idx);
+
+ void set_position(int p_node,int p_x,int p_y);
+ int add_position(int p_x,int p_y,bool p_move_loops=true);
+ void del_position(int p_node);
+
+ void set_loop_enabled(bool p_enabled);
+ bool is_loop_enabled();
+ void set_loop_begin(int pos);
+ void set_loop_end(int pos);
+ uint8_t get_loop_begin();
+ uint8_t get_loop_end();
+
+ void set_sustain_loop_enabled(bool p_enabled);
+ bool is_sustain_loop_enabled();
+ void set_sustain_loop_begin(int pos);
+ void set_sustain_loop_end(int pos);
+ uint8_t get_sustain_loop_begin();
+ uint8_t get_sustain_loop_end();
+
+ void set_enabled(bool p_enabled);
+ bool is_enabled();
+
+ void set_carry_enabled(bool p_enabled);
+ bool is_carry_enabled();
+
+ void reset();
+ int get_height_at_pos(int pos);
+ float get_interp_height_at_pos(float pos);
+
+
+ CPEnvelope();
+
+};
+
+#endif
diff --git a/modules/chibi/cp_file_access_wrapper.h b/modules/chibi/cp_file_access_wrapper.h
new file mode 100644
index 0000000000..5b361c0ea8
--- /dev/null
+++ b/modules/chibi/cp_file_access_wrapper.h
@@ -0,0 +1,96 @@
+/*************************************************************************/
+/* cp_file_access_wrapper.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_FILE_ACCESS_WRAPPER_H
+#define CP_FILE_ACCESS_WRAPPER_H
+
+#include "cp_config.h"
+
+class CPFileAccessWrapper {
+public:
+
+ enum ModeFlags {
+
+ READ=1,
+ WRITE=2,
+ READ_WRITE=3,
+ };
+
+ enum Error {
+
+ OK,
+ ERROR_FILE_NOT_FOUND,
+ ERROR_FILE_BAD_DRIVE,
+ ERROR_FILE_BAD_PATH,
+ ERROR_FILE_NO_PERMISSION,
+ ERROR_ALREADY_IN_USE,
+ ERROR_INVALID_PARAMETERS,
+ ERROR_OPENING_FILE,
+ ERROR_READING_FILE,
+ ERROR_WRITING_FILE
+ };
+
+ virtual Error open(const char *p_filename, int p_mode_flags)=0;
+ virtual void close()=0;
+
+ virtual void seek(uint32_t p_position)=0;
+ virtual void seek_end()=0;
+ virtual uint32_t get_pos()=0;
+
+ virtual bool eof_reached()=0;
+
+ virtual uint8_t get_byte()=0;
+ virtual void get_byte_array(uint8_t *p_dest,int p_elements)=0;
+ virtual void get_word_array(uint16_t *p_dest,int p_elements)=0;
+
+ virtual uint16_t get_word()=0;
+ virtual uint32_t get_dword()=0;
+
+ // use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
+ // It's not about the current CPU type but file formats.
+ // this flags get reset to false (little endian) on each open
+ virtual void set_endian_conversion(bool p_swap)=0;
+ virtual bool is_open()=0;
+
+ virtual Error get_error()=0;
+
+ virtual void store_byte(uint8_t p_dest)=0;
+ virtual void store_byte_array(const uint8_t *p_dest,int p_elements)=0;
+
+ virtual void store_word(uint16_t p_dest)=0;
+ virtual void store_dword(uint32_t p_dest)=0;
+
+
+
+ virtual ~CPFileAccessWrapper(){}
+
+};
+
+
+
+#endif
diff --git a/modules/chibi/cp_instrument.cpp b/modules/chibi/cp_instrument.cpp
new file mode 100644
index 0000000000..7a732e33a4
--- /dev/null
+++ b/modules/chibi/cp_instrument.cpp
@@ -0,0 +1,344 @@
+/*************************************************************************/
+/* cp_instrument.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_instrument.h"
+#include "cp_song.h"
+#include "cp_note.h"
+
+
+
+const char *CPInstrument::get_name() {
+
+ return name;
+
+}
+void CPInstrument::set_name(const char *p_name) {
+
+
+ if (p_name==NULL) {
+ name[0]=0;
+ return;
+ }
+
+
+ bool done=false;
+ for (int i=0;i<MAX_NAME_LEN;i++) {
+
+
+ name[i]=done?0:p_name[i];
+ if (!done && p_name[i]==0)
+ done=true;
+ }
+
+ name[MAX_NAME_LEN-1]=0; /* just in case */
+
+
+}
+
+void CPInstrument::set_sample_number(uint8_t p_note,uint8_t p_sample_id) {
+
+ CP_ERR_COND(p_note>=CPNote::NOTES);
+ CP_ERR_COND(p_sample_id>CPSong::MAX_SAMPLES && p_sample_id!=CPNote::EMPTY);
+ data.sample_number[p_note]=p_sample_id;
+
+
+}
+uint8_t CPInstrument::get_sample_number(uint8_t p_note) {
+
+ CP_ERR_COND_V(p_note>=CPNote::NOTES,0);
+ return data.sample_number[p_note];
+}
+
+void CPInstrument::set_note_number(uint8_t p_note,uint8_t p_note_id) {
+
+ CP_ERR_COND(p_note>=CPNote::NOTES);
+ CP_ERR_COND(p_note_id>=CPNote::NOTES && p_note_id!=CPNote::EMPTY);
+ data.note_number[p_note]=p_note_id;
+
+}
+uint8_t CPInstrument::get_note_number(uint8_t p_note) {
+
+ CP_ERR_COND_V(p_note>=CPNote::NOTES,0);
+ return data.note_number[p_note];
+
+}
+
+void CPInstrument::set_NNA_type(NNA_Type p_NNA_type) {
+
+ data.NNA_type=p_NNA_type;
+}
+CPInstrument::NNA_Type CPInstrument::get_NNA_type() {
+
+ return data.NNA_type;
+}
+
+void CPInstrument::set_DC_type(DC_Type p_DC_type) {
+
+ data.DC_type=p_DC_type;
+}
+CPInstrument::DC_Type CPInstrument::get_DC_type() {
+
+ return data.DC_type;
+
+}
+
+void CPInstrument::set_DC_action(DC_Action p_DC_action) {
+
+ data.DC_action=p_DC_action;
+}
+CPInstrument::DC_Action CPInstrument::get_DC_action() {
+
+ return data.DC_action;
+}
+
+/* Volume */
+
+void CPInstrument::set_volume_global_amount(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_VOLUME);
+ data.volume.global_amount=p_amount;
+
+}
+uint8_t CPInstrument::get_volume_global_amount() {
+
+ return data.volume.global_amount;
+}
+
+void CPInstrument::set_volume_fadeout(uint16_t p_amount) {
+ CP_ERR_COND(p_amount>MAX_FADEOUT);
+ data.volume.fadeout=p_amount;
+}
+uint16_t CPInstrument::get_volume_fadeout() {
+
+ return data.volume.fadeout;
+}
+void CPInstrument::set_volume_random_variation(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_VOLUME_RANDOM);
+ data.volume.random_variation=p_amount;
+}
+uint8_t CPInstrument::get_volume_random_variation() {
+
+ return data.volume.random_variation;
+}
+
+/* Panning */
+
+void CPInstrument::set_pan_default_amount(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_PAN);
+ data.pan.default_amount=p_amount;
+}
+uint8_t CPInstrument::get_pan_default_amount() {
+
+ return data.pan.default_amount;
+}
+
+void CPInstrument::set_pan_default_enabled(bool p_enabled) {
+
+ data.pan.use_default=p_enabled;
+}
+bool CPInstrument::is_pan_default_enabled() {
+
+ return data.pan.use_default;
+
+}
+
+void CPInstrument::set_pan_pitch_separation(int8_t p_amount) {
+
+ CP_ERR_COND(p_amount<-32);
+ CP_ERR_COND(p_amount>32);
+ data.pan.pitch_separation=p_amount;
+}
+int8_t CPInstrument::get_pan_pitch_separation() {
+
+ return data.pan.pitch_separation;
+}
+
+void CPInstrument::set_pan_pitch_center(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>=CPNote::NOTES);
+ data.pan.pitch_center=p_amount;
+}
+uint8_t CPInstrument::get_pan_pitch_center() {
+
+ return data.pan.pitch_center;
+}
+
+void CPInstrument::set_pan_random_variation(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_PAN_RANDOM);
+ data.pan.random_variation=p_amount;
+}
+uint8_t CPInstrument::get_pan_random_variation() {
+
+ return data.pan.random_variation;
+}
+
+/* Pitch / Filter */
+
+void CPInstrument::set_pitch_use_as_filter(bool p_enabled) {
+
+ data.pitch.use_as_filter=p_enabled;
+}
+bool CPInstrument::is_pitch_use_as_filter() {
+
+ return data.pitch.use_as_filter;
+}
+
+void CPInstrument::set_filter_use_default_cutoff(bool p_enabled) {
+
+ data.pitch.use_default_cutoff=p_enabled;
+
+}
+bool CPInstrument::filter_use_default_cutoff() {
+
+ return data.pitch.use_default_cutoff;
+}
+
+void CPInstrument::set_filter_default_cutoff(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_FILTER_CUTOFF);
+ data.pitch.default_cutoff=p_amount;
+}
+uint8_t CPInstrument::get_filter_default_cutoff() {
+
+ return data.pitch.default_cutoff;
+}
+
+void CPInstrument::set_filter_use_default_resonance(bool p_enabled) {
+
+ data.pitch.use_default_resonance=p_enabled;
+}
+bool CPInstrument::filter_use_default_resonance() {
+
+ return data.pitch.use_default_resonance;
+}
+
+void CPInstrument::set_filter_default_resonance(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_FILTER_RESONANCE);
+ data.pitch.default_resonance=p_amount;
+
+}
+uint8_t CPInstrument::get_filter_default_resonance() {
+
+ return data.pitch.default_resonance;
+}
+
+/* Envelopes */
+
+
+CPEnvelope* CPInstrument::get_volume_envelope() {
+
+ return &data.volume.envelope;
+}
+CPEnvelope* CPInstrument::get_pan_envelope() {
+
+ return &data.pan.envelope;
+}
+CPEnvelope* CPInstrument::get_pitch_filter_envelope() {
+
+ return &data.pitch.envelope;
+
+
+}
+
+
+void CPInstrument::reset() {
+
+ name[0]=0;
+
+ data.NNA_type=NNA_NOTE_CUT;
+ data.DC_action=DCA_NOTE_CUT;
+ data.DC_type=DCT_DISABLED;
+
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ data.sample_number[i]=CPNote::EMPTY;
+ data.note_number[i]=i;
+ }
+
+ data.volume.envelope.reset();
+ data.volume.envelope.set_max(64);
+ data.volume.envelope.set_min(0);
+ data.volume.envelope.add_position(0,64,false);
+ data.volume.envelope.add_position(30,64,false);
+
+ data.volume.global_amount=MAX_VOLUME;
+ data.volume.fadeout=0;
+ data.volume.random_variation=0;
+
+ data.pan.envelope.reset();
+ data.pan.envelope.set_max(32);
+ data.pan.envelope.set_min(-32);
+ data.pan.envelope.add_position(0,0,false);
+ data.pan.envelope.add_position(30,0,false);
+
+ data.pan.default_amount=32;
+ data.pan.pitch_center=48;
+ data.pan.pitch_separation=0;
+ data.pan.use_default=false;
+ data.pan.random_variation=0;
+
+
+ data.pitch.envelope.reset();
+ data.pitch.envelope.set_max(32);
+ data.pitch.envelope.set_min(-32);
+ data.pitch.envelope.add_position(0,0,false);
+ data.pitch.envelope.add_position(30,0,false);
+ data.pitch.use_as_filter=false;
+ data.pitch.use_default_cutoff=false;
+ data.pitch.use_default_resonance=false;
+ data.pitch.default_cutoff=0;
+ data.pitch.default_resonance=0;
+
+}
+
+bool CPInstrument::is_empty() {
+
+ bool has_sample=false;
+
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ if (data.sample_number[i]!=CPNote::EMPTY) {
+
+ has_sample=true;
+ break;
+ }
+ }
+
+ return !has_sample;
+}
+
+CPInstrument::CPInstrument() {
+
+ reset();
+
+}
+
diff --git a/modules/chibi/cp_instrument.h b/modules/chibi/cp_instrument.h
new file mode 100644
index 0000000000..d8eb8333ee
--- /dev/null
+++ b/modules/chibi/cp_instrument.h
@@ -0,0 +1,219 @@
+/*************************************************************************/
+/* cp_instrument.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_INSTRUMENT_H
+#define CP_INSTRUMENT_H
+
+
+#include "cp_config.h"
+#include "cp_note.h"
+#include "cp_envelope.h"
+
+class CPInstrument {
+public:
+
+
+ enum NNA_Type {
+
+ NNA_NOTE_CUT,
+ NNA_NOTE_CONTINUE,
+ NNA_NOTE_OFF,
+ NNA_NOTE_FADE
+ };
+
+ enum DC_Type {
+
+ DCT_DISABLED,
+ DCT_NOTE,
+ DCT_SAMPLE,
+ DCT_INSTRUMENT
+ };
+
+ enum DC_Action
+ {
+
+ DCA_NOTE_CUT,
+ DCA_NOTE_OFF,
+ DCA_NOTE_FADE,
+ };
+
+ enum EnvelopeType {
+ VOLUME_ENVELOPE,
+ PAN_ENVELOPE,
+ PITCH_ENVELOPE
+ };
+
+
+ enum {
+ MAX_NAME_LEN=26,
+ MAX_ENVELOPE_NODES=25,
+ ENVELOPE_FRAC_BITS=8,
+ MAX_VOLUME=128,
+ MAX_FADEOUT=256,
+ MAX_PAN=128,
+ MAX_VOLUME_RANDOM=100,
+ MAX_PAN_RANDOM=64, //what did this guy have inside his head?
+
+ MAX_FILTER_CUTOFF=127,
+ MAX_FILTER_RESONANCE=127
+
+ };
+
+
+ struct Data {
+
+
+ uint8_t sample_number[CPNote::NOTES];
+ uint8_t note_number[CPNote::NOTES];
+
+ NNA_Type NNA_type;
+ DC_Type DC_type;
+ DC_Action DC_action;
+
+ struct Volume {
+
+ CPEnvelope envelope;
+ uint8_t global_amount;
+ uint16_t fadeout;
+ uint8_t random_variation;
+
+ } volume;
+
+ struct Pan {
+
+ CPEnvelope envelope;
+ bool use_default;
+ uint8_t default_amount;
+ int8_t pitch_separation;
+ uint8_t pitch_center;
+ uint8_t random_variation;
+
+ } pan;
+
+ struct Pitch {
+
+ CPEnvelope envelope;
+ bool use_as_filter;
+ bool use_default_cutoff;
+ uint8_t default_cutoff;
+ bool use_default_resonance;
+ uint8_t default_resonance;
+ } pitch;
+
+ };
+
+private:
+
+
+
+ Data data;
+ char name[MAX_NAME_LEN];
+
+public:
+
+ /* CPInstrument General */
+
+ const char *get_name();
+ void set_name(const char *p_name);
+
+ void set_sample_number(uint8_t p_note,uint8_t p_sample_id);
+ uint8_t get_sample_number(uint8_t p_note);
+
+ void set_note_number(uint8_t p_note,uint8_t p_note_id);
+ uint8_t get_note_number(uint8_t p_note);
+
+ void set_NNA_type(NNA_Type p_NNA_type);
+ NNA_Type get_NNA_type();
+
+ void set_DC_type(DC_Type p_DC_type);
+ DC_Type get_DC_type();
+
+ void set_DC_action(DC_Action p_DC_action);
+ DC_Action get_DC_action();
+
+ /* Volume */
+
+ void set_volume_global_amount(uint8_t p_amount);
+ uint8_t get_volume_global_amount();
+
+ void set_volume_fadeout(uint16_t p_amount);
+ uint16_t get_volume_fadeout();
+
+ void set_volume_random_variation(uint8_t p_amount);
+ uint8_t get_volume_random_variation();
+
+ /* Panning */
+
+ void set_pan_default_amount(uint8_t p_amount);
+ uint8_t get_pan_default_amount();
+
+ void set_pan_default_enabled(bool p_enabled);
+ bool is_pan_default_enabled();
+
+ void set_pan_pitch_separation(int8_t p_amount);
+ int8_t get_pan_pitch_separation();
+
+ void set_pan_pitch_center(uint8_t p_amount);
+ uint8_t get_pan_pitch_center();
+
+ void set_pan_random_variation(uint8_t p_amount);
+ uint8_t get_pan_random_variation();
+
+ /* Pitch / Filter */
+
+ void set_pitch_use_as_filter(bool p_enabled);
+ bool is_pitch_use_as_filter();
+
+ void set_filter_use_default_cutoff(bool p_enabled);
+ bool filter_use_default_cutoff();
+
+ void set_filter_default_cutoff(uint8_t p_amount);
+ uint8_t get_filter_default_cutoff();
+
+ void set_filter_use_default_resonance(bool p_enabled);
+ bool filter_use_default_resonance();
+
+ void set_filter_default_resonance(uint8_t p_amount);
+ uint8_t get_filter_default_resonance();
+
+ CPEnvelope* get_volume_envelope();
+ CPEnvelope* get_pan_envelope();
+ CPEnvelope* get_pitch_filter_envelope();
+
+ bool is_empty();
+
+ void reset();
+ CPInstrument();
+
+};
+
+
+
+#endif
+
+
diff --git a/modules/chibi/cp_loader.h b/modules/chibi/cp_loader.h
new file mode 100644
index 0000000000..9d1074d1b8
--- /dev/null
+++ b/modules/chibi/cp_loader.h
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* cp_loader.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_LOADER_H
+#define CP_LOADER_H
+
+
+#include "cp_song.h"
+#include "cp_file_access_wrapper.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class CPLoader {
+
+public:
+
+ enum Error {
+ FILE_OK,
+ FILE_UNRECOGNIZED,
+ FILE_CANNOT_OPEN,
+ FILE_CORRUPTED,
+ FILE_OUT_OF_MEMORY,
+ };
+
+
+ virtual bool can_load_song()=0;
+ virtual bool can_load_sample()=0;
+ virtual bool can_load_instrument()=0;
+
+ virtual Error load_song(const char *p_file,CPSong *p_song,bool p_sampleset)=0;
+ virtual Error load_sample(const char *p_file,CPSample *p_sample)=0;
+ virtual Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx)=0;
+
+
+ virtual ~CPLoader() {}
+
+};
+
+#endif
diff --git a/modules/chibi/cp_loader_it.cpp b/modules/chibi/cp_loader_it.cpp
new file mode 100644
index 0000000000..20a3960a23
--- /dev/null
+++ b/modules/chibi/cp_loader_it.cpp
@@ -0,0 +1,216 @@
+/*************************************************************************/
+/* cp_loader_it.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_it.h"
+
+bool CPLoader_IT::can_load_song() { return true; }
+bool CPLoader_IT::can_load_sample() { return true; }
+bool CPLoader_IT::can_load_instrument() { return true; }
+
+CPLoader::Error CPLoader_IT::load_song(const char *p_file,CPSong *p_song, bool p_sampleset) {
+
+
+ song=p_song;
+
+ if (file->open( p_file, CPFileAccessWrapper::READ )!=CPFileAccessWrapper::OK)
+ return CPLoader::FILE_CANNOT_OPEN;
+
+
+ Error err;
+
+ char aux_identifier[4];
+ file->get_byte_array((uint8_t*)aux_identifier,4);
+
+ if ( aux_identifier[0]!='I' ||
+ aux_identifier[1]!='M' ||
+ aux_identifier[2]!='P' ||
+ aux_identifier[3]!='M') {
+
+
+ CP_PRINTERR("IT CPLoader CPSong: Failed Identifier");
+ return FILE_UNRECOGNIZED;
+ }
+
+
+ if (p_sampleset) {
+
+ song->reset(false,true,true,false);
+
+ if ((err=load_header(true))) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_samples())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_instruments())) {
+ file->close();
+ return err;
+ }
+
+ return FILE_OK;
+ }
+
+ song->reset();
+
+ if ((err=load_header(false))) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_orders())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_patterns())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_samples())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_effects())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_instruments())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_message())) {
+ file->close();
+ return err;
+ }
+
+ file->close();
+ return FILE_OK;
+
+}
+
+
+
+
+CPLoader::Error CPLoader_IT::load_sample(const char *p_file,CPSample *p_sample) {
+
+ if (file->open( p_file, CPFileAccessWrapper::READ )!=CPFileAccessWrapper::OK)
+ return CPLoader::FILE_CANNOT_OPEN;
+
+ p_sample->reset();
+ CPLoader::Error res=load_sample(p_sample);
+
+ file->close();
+
+ return res;
+}
+CPLoader::Error CPLoader_IT::load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx) {
+
+ CP_FAIL_INDEX_V(p_instr_idx,CPSong::MAX_INSTRUMENTS,CPLoader::FILE_CANNOT_OPEN);
+
+ if (file->open( p_file, CPFileAccessWrapper::READ )!=CPFileAccessWrapper::OK)
+ return CPLoader::FILE_CANNOT_OPEN;
+
+
+ p_song->get_instrument( p_instr_idx )->reset();
+
+
+ int samples=0;
+ CPLoader::Error res=load_instrument( p_song->get_instrument( p_instr_idx ), &samples );
+
+ if (res) {
+ file->close();
+ return res;
+ }
+
+
+ char exchange[CPSong::MAX_SAMPLES];
+ for (int i=0;i<CPSong::MAX_SAMPLES;i++)
+ exchange[i]=0;
+
+ for (int i=0;i<samples;i++) {
+
+ file->seek( 554+i*80 ); //i think this should work?! seems to.. but i'm not sure
+
+ /* find free sample */
+
+ int free_idx=-1;
+ for (int s=0;s<CPSong::MAX_SAMPLES;s++) {
+
+ if (p_song->get_sample( s )->get_sample_data().is_null()) {
+ free_idx=s;
+ break;
+ }
+ }
+ if (free_idx==-1)
+ break; //can't seem to be able to load more samples
+
+ exchange[i]=free_idx;
+ res=load_sample( p_song->get_sample( free_idx ) );
+
+ if (res) {
+
+ file->close();
+ return res;
+ }
+ }
+
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ int smp=song->get_instrument(p_instr_idx)->get_sample_number(i);
+
+ if (smp>=CPSong::MAX_SAMPLES)
+ continue;
+
+ if (smp<0)
+ continue;
+
+ smp=exchange[smp];
+
+ song->get_instrument(p_instr_idx)->set_sample_number(i,smp);
+
+ }
+
+ file->close();
+
+ return res;
+
+}
+
+CPLoader_IT::CPLoader_IT(CPFileAccessWrapper *p_file) {
+
+ file=p_file;
+
+}
diff --git a/modules/chibi/cp_loader_it.h b/modules/chibi/cp_loader_it.h
new file mode 100644
index 0000000000..38a1cdd9c4
--- /dev/null
+++ b/modules/chibi/cp_loader_it.h
@@ -0,0 +1,125 @@
+/*************************************************************************/
+/* cp_loader_it.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_LOADER_IT_H
+#define CP_LOADER_IT_H
+
+#include "cp_loader.h"
+/**
+ *@author Juan Linietsky
+ */
+
+/******************************
+ loader_it.h
+ ----------
+Impulse Tracker Module CPLoader!
+It lacks support for old
+instrument files methinks...
+and some other things like
+midi.
+********************************/
+
+class AuxSampleData; //used for internal crap
+
+class CPLoader_IT : public CPLoader {
+
+
+
+ CPFileAccessWrapper *file;
+ CPSong *song;
+
+ struct IT_Header {
+ uint8_t blank01[2];
+ uint16_t ordnum;
+ uint16_t insnum;
+ uint16_t smpnum;
+ uint16_t patnum;
+ uint16_t cwt; /* Created with tracker (y.xx = 0x0yxx) */
+ uint16_t cmwt; /* Compatible with tracker ver > than val. */
+ uint16_t flags;
+ uint16_t special; /* bit 0 set = song message attached */
+ uint16_t msglength;
+ uint32_t msgoffset;
+ bool is_chibi;
+ };
+
+ /* Variables to store temp data */
+ IT_Header header;
+
+ /* CPSong Info Methods */
+ Error load_header(bool p_dont_set);
+ Error load_orders();
+ Error load_message();
+
+ /* CPPattern Methods */
+ Error load_patterns();
+
+ /* CPSample Methods */
+
+ Error load_samples();
+ Error load_sample(CPSample *p_sample);
+ CPSample_ID load_sample_data(AuxSampleData& p_sample_data);
+
+ // CPSample decompression
+
+ uint32_t read_n_bits_from_IT_compressed_block(uint8_t p_bits_to_read);
+ bool read_IT_compressed_block (bool p_16bits);
+ void free_IT_compressed_block ();
+ bool load_sample_8bits_IT_compressed(void *p_dest_buffer,int p_buffsize);
+ bool load_sample_16bits_IT_compressed(void *p_dest_buffer,int p_buffsize);
+ uint32_t *source_buffer; /* source buffer */
+ uint32_t *source_position; /* actual reading position */
+ uint8_t source_remaining_bits; /* bits remaining in read dword */
+ uint8_t* pat_data;
+
+ /* CPInstruments Methods */
+ Error load_effects();
+ Error load_instruments();
+ Error load_instrument(CPInstrument *p_instrument,int *p_samples=0);
+ void load_envelope(CPEnvelope *p_envelope,bool*p_has_filter_flag=0);
+
+
+public:
+
+
+ bool can_load_song();
+ bool can_load_sample();
+ bool can_load_instrument();
+
+ Error load_song(const char *p_file,CPSong *p_song, bool p_sampleset=false);
+ Error load_sample(const char *p_file,CPSample *p_sample);
+ Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx);
+
+ CPLoader_IT(CPFileAccessWrapper *p_file);
+
+};
+
+
+
+#endif
diff --git a/modules/chibi/cp_loader_it_info.cpp b/modules/chibi/cp_loader_it_info.cpp
new file mode 100644
index 0000000000..0360f7f9a4
--- /dev/null
+++ b/modules/chibi/cp_loader_it_info.cpp
@@ -0,0 +1,268 @@
+/*************************************************************************/
+/* cp_loader_it_info.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_it.h"
+
+
+
+CPLoader::Error CPLoader_IT::load_header(bool p_dont_set) {
+
+
+ char aux_songname[26];
+
+ file->get_byte_array((uint8_t*)aux_songname,26);
+ if (!p_dont_set)
+ song->set_name( aux_songname );
+
+ uint8_t aux_hlmin=file->get_byte();
+ uint8_t aux_hlmaj=file->get_byte();
+
+ if (aux_hlmin==0) aux_hlmin=4;
+ if (aux_hlmaj==0) aux_hlmaj=16;
+
+ if (!p_dont_set) {
+ song->set_row_highlight_minor( aux_hlmin );
+ song->set_row_highlight_major( aux_hlmaj );
+ }
+
+ header.ordnum=file->get_word();
+ header.insnum=file->get_word();
+ header.smpnum=file->get_word();
+ header.patnum=file->get_word();
+
+ header.cwt=file->get_word(); /* Created with tracker (y.xx = 0x0yxx) */
+ header.cmwt=file->get_word(); /* Compatible with tracker ver > than val. */
+ header.flags=file->get_word();
+
+ if (!p_dont_set) {
+ song->set_stereo( header.flags & 1 );
+ song->set_linear_slides( header.flags & 8 );
+ song->set_old_effects( header.flags & 16 );
+ song->set_compatible_gxx( header.flags & 32 );
+ song->set_instruments( header.flags & 4 );
+ }
+
+
+ header.special=file->get_word();
+ if (!p_dont_set) {
+
+ song->set_global_volume( file->get_byte() );
+ song->set_mixing_volume( file->get_byte() );
+ song->set_speed( file->get_byte() );
+ song->set_tempo( file->get_byte() );
+ song->set_stereo_separation( file->get_byte() );
+
+ } else {
+
+ file->get_byte(); // skip
+ file->get_byte(); // skip
+ file->get_byte(); // skip
+ file->get_byte(); // skip
+ file->get_byte(); // skip
+ }
+ file->get_byte(); // ZERO Byte
+ header.msglength=file->get_word();
+ header.msgoffset=file->get_dword();
+ char chibi[4];
+ file->get_byte_array((uint8_t*)chibi,4);
+ header.is_chibi=(chibi[0]=='C' && chibi[1]=='H' && chibi[2]=='B' && chibi[3]=='I');
+
+ for (int i=0;i<64;i++) {
+
+ uint8_t panbyte=file->get_byte();
+
+ uint8_t pan_dst=(panbyte<65) ? panbyte : 32;
+ bool surround_dst=(panbyte==100);
+ bool mute_dst=(panbyte>=128);
+
+ if (!p_dont_set) {
+ song->set_channel_pan( i, pan_dst );
+ song->set_channel_surround( i, surround_dst );
+ song->set_channel_mute( i, mute_dst );
+ }
+ }
+ for (int i=0;i<64;i++) {
+ unsigned char cv = file->get_byte();
+ if (!p_dont_set)
+ song->set_channel_volume( i, cv );
+ }
+
+ CP_ERR_COND_V( file->eof_reached(),FILE_CORRUPTED );
+ CP_ERR_COND_V( file->get_error(),FILE_CORRUPTED );
+
+ return FILE_OK;
+}
+
+CPLoader::Error CPLoader_IT::load_effects() {
+
+ if (!header.is_chibi)
+ return FILE_OK; //no effects, regular IT file
+
+ /* GOTO End of IT header */
+ file->seek(0xC0+header.ordnum+header.insnum*4+header.smpnum*4+header.patnum*4);
+
+
+ if (file->get_byte()>0) //not made with this version, ignore extended info
+ return FILE_OK;
+
+ /* Chibitracker Extended info */
+
+ switch(file->get_byte()) {
+
+ case CPSong::REVERB_MODE_ROOM: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_ROOM );
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_SMALL: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_STUDIO_SMALL );
+
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_MEDIUM: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_STUDIO_MEDIUM );
+
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_LARGE: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_STUDIO_LARGE );
+
+ } break;
+ case CPSong::REVERB_MODE_HALL: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_HALL );
+
+ } break;
+ case CPSong::REVERB_MODE_SPACE_ECHO: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_SPACE_ECHO );
+
+ } break;
+
+ case CPSong::REVERB_MODE_ECHO: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_ECHO );
+
+ } break;
+ case CPSong::REVERB_MODE_DELAY: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_DELAY );
+
+ } break;
+ case CPSong::REVERB_MODE_HALF_ECHO: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_HALF_ECHO );
+
+ } break;
+
+ }
+
+ //chorus
+ song->set_chorus_speed_hz10( file->get_byte() );
+ song->set_chorus_delay_ms( file->get_byte() );
+ song->set_chorus_depth_ms10( file->get_byte() );
+ song->set_chorus_separation_ms( file->get_byte() );
+
+ for (int i=0;i<CPPattern::WIDTH;i++) {
+ song->set_channel_reverb(i,file->get_byte());
+ }
+ for (int i=0;i<CPPattern::WIDTH;i++) {
+ song->set_channel_chorus(i,file->get_byte());
+ }
+
+ return FILE_OK;
+
+}
+
+CPLoader::Error CPLoader_IT::load_message() {
+
+
+ if (!(header.special & 1)) {
+
+ return FILE_OK;
+ }
+
+
+ file->seek(header.msgoffset);
+
+ //(void*)tmpmsg=malloc(header.msglength+1);
+
+ char message[8000];
+
+
+ char *tmpmsg = message;
+
+ file->get_byte_array((uint8_t*)tmpmsg,header.msglength);
+ tmpmsg[header.msglength]=0;
+
+ for (int i=0;i<header.msglength;i++) if (tmpmsg[i]=='\r') tmpmsg[i]='\n';
+
+ song->set_message(tmpmsg);
+
+ return FILE_OK;
+}
+
+CPLoader::Error CPLoader_IT::load_orders() {
+
+ file->seek(0xC0);
+
+
+ for (int i=0;i<header.ordnum;i++) {
+
+ uint8_t aux_order=file->get_byte();
+ CPOrder order=CP_ORDER_NONE;
+
+
+ if (i>=CPSong::MAX_ORDERS)
+ continue;
+ if (aux_order==254) {
+
+ order=CP_ORDER_BREAK;
+
+ } else if (aux_order<200) {
+
+ order=aux_order;
+ //nothing!
+
+ }
+ song->set_order(i,order);
+
+ }
+
+ if (file->eof_reached() || file->get_error()) {
+
+
+ return FILE_CORRUPTED;
+
+ }
+
+ return FILE_OK;
+}
+
+
+
diff --git a/modules/chibi/cp_loader_it_instruments.cpp b/modules/chibi/cp_loader_it_instruments.cpp
new file mode 100644
index 0000000000..ccb24bd81c
--- /dev/null
+++ b/modules/chibi/cp_loader_it_instruments.cpp
@@ -0,0 +1,224 @@
+/*************************************************************************/
+/* cp_loader_it_instruments.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_loader_it.h"
+
+enum EnvFlags {
+ ENV_ON=1,
+ ENV_LOOP=2,
+ ENV_SUSLOOP=4,
+ ENV_CARRY=8,
+ ENV_FILTER=128
+};
+
+void CPLoader_IT::load_envelope(CPEnvelope *p_envelope,bool*p_has_filter_flag) {
+
+ uint8_t flags=file->get_byte();
+ uint8_t points=file->get_byte();
+ uint8_t begin=file->get_byte();
+ uint8_t end=file->get_byte();
+ uint8_t susbegin=file->get_byte();
+ uint8_t susend=file->get_byte();
+
+ p_envelope->reset();
+
+ for (int i=0;i<25;i++) {
+
+ uint8_t height=file->get_byte();
+ int8_t &signed_height=(int8_t&)height;
+ uint16_t tick=file->get_word();
+
+ if (i>=points)
+ continue;
+ p_envelope->add_position( tick, signed_height );
+
+ }
+
+ p_envelope->set_enabled( flags & ENV_ON );
+ p_envelope->set_carry_enabled( flags & ENV_CARRY);
+
+ p_envelope->set_loop_enabled( flags & ENV_LOOP );
+ p_envelope->set_loop_begin( begin );
+ p_envelope->set_loop_end( end );
+
+ p_envelope->set_sustain_loop_enabled( flags & ENV_SUSLOOP );
+ p_envelope->set_sustain_loop_begin( susbegin );
+ p_envelope->set_sustain_loop_end( susend );
+
+ if (p_has_filter_flag)
+ *p_has_filter_flag=flags&ENV_FILTER;
+
+ file->get_byte(); //zerobyte
+
+ //fill with stuff if the envelope hass less than 2 points
+ while(p_envelope->get_node_count()<2) {
+
+ p_envelope->add_position( 30*p_envelope->get_node_count(), p_envelope->get_min()==0 ? 64 : 0, false );
+ }
+}
+
+
+CPLoader::Error CPLoader_IT::load_instrument(CPInstrument *p_instrument,int *p_samples) {
+
+
+
+ char aux_header[4];
+
+ file->get_byte_array((uint8_t*)aux_header,4);
+
+
+ if ( aux_header[0]!='I' ||
+ aux_header[1]!='M' ||
+ aux_header[2]!='P' ||
+ aux_header[3]!='I') {
+ CP_PRINTERR("IT CPLoader CPInstrument: Failed Identifier");
+
+ return FILE_UNRECOGNIZED;
+ }
+
+
+
+ // Ignore deprecated 8.3 filename field
+ for (int i=0;i<12;i++) file->get_byte();
+
+ //Ignore zerobyte
+ file->get_byte(); /* (byte) CPInstrument type (always 0) */
+
+ switch( file->get_byte() ) { /* New CPNote Action [0,1,2,3] */
+ case 0: p_instrument->set_NNA_type( CPInstrument::NNA_NOTE_CUT ) ; break;
+ case 1: p_instrument->set_NNA_type( CPInstrument::NNA_NOTE_CONTINUE ) ; break;
+ case 2: p_instrument->set_NNA_type( CPInstrument::NNA_NOTE_OFF ) ; break;
+ case 3: p_instrument->set_NNA_type( CPInstrument::NNA_NOTE_FADE ) ; break;
+ };
+ switch( file->get_byte() ) { // Duplicate Check Type
+ case 0: p_instrument->set_DC_type( CPInstrument::DCT_DISABLED ); break ;
+ case 1: p_instrument->set_DC_type( CPInstrument::DCT_NOTE ); break ;
+ case 2: p_instrument->set_DC_type( CPInstrument::DCT_SAMPLE ); break ;
+ case 3: p_instrument->set_DC_type( CPInstrument::DCT_INSTRUMENT ); break ;
+ }
+ switch( file->get_byte() ) { //Duplicate Check Action
+ case 0: p_instrument->set_DC_action( CPInstrument::DCA_NOTE_CUT ); break ;
+ case 1: p_instrument->set_DC_action( CPInstrument::DCA_NOTE_OFF ); break ;
+ case 2: p_instrument->set_DC_action( CPInstrument::DCA_NOTE_FADE ); break ;
+ }
+
+ int fade = file->get_word();
+ //intf("AFADE: %i\n",fade);
+ if (fade>CPInstrument::MAX_FADEOUT) //needs to be clipped because of horrible modplug doings
+ fade=CPInstrument::MAX_FADEOUT;
+
+ p_instrument->set_volume_fadeout( fade );
+ p_instrument->set_pan_pitch_separation( file->get_byte() );
+ p_instrument->set_pan_pitch_center( file->get_byte() );
+ p_instrument->set_volume_global_amount( file->get_byte() );
+ uint8_t pan=file->get_byte();
+ p_instrument->set_pan_default_amount(pan&0x7F);
+ p_instrument->set_pan_default_enabled( !(pan&0x80) );
+ p_instrument->set_volume_random_variation( file->get_byte() );
+ p_instrument->set_pan_random_variation( file->get_byte() );
+
+
+
+ file->get_word(); //empty (version)
+ uint8_t samples=file->get_byte();
+ if (p_samples)
+ *p_samples=samples;
+ file->get_byte(); //empty
+ char aux_name[26];
+ file->get_byte_array((uint8_t*)aux_name,26);
+ p_instrument->set_name(aux_name);
+
+ uint8_t cutoff=file->get_byte();
+
+ p_instrument->set_filter_default_cutoff(cutoff&0x7F);
+ p_instrument->set_filter_use_default_cutoff(cutoff&0x80);
+
+ uint8_t resonance=file->get_byte();
+
+ p_instrument->set_filter_default_resonance(resonance&0x7F);
+ p_instrument->set_filter_use_default_resonance(resonance&0x80);
+
+ file->get_dword(); //MIDI, IGNORED!
+
+ /* CPNote -> CPSample table */
+ for (uint8_t i=0;i<CPNote::NOTES;i++) {
+
+
+ uint8_t note=file->get_byte();
+ if (note>=CPNote::NOTES)
+ note=0;
+ p_instrument->set_note_number(i,note);
+
+ uint8_t samp=file->get_byte();
+ if (samp==0 || samp>99)
+ samp=CPNote::EMPTY;
+ else
+ samp--;
+
+
+ p_instrument->set_sample_number(i,samp);
+
+
+ }
+
+
+ load_envelope( p_instrument->get_volume_envelope() );
+ load_envelope( p_instrument->get_pan_envelope() );
+ bool use_as_filter;
+ load_envelope( p_instrument->get_pitch_filter_envelope(), &use_as_filter );
+ p_instrument->set_pitch_use_as_filter( use_as_filter );
+
+ return FILE_OK;
+
+}
+
+
+CPLoader::Error CPLoader_IT::load_instruments() {
+
+
+ for (int i=0;i<header.insnum;i++) {
+
+
+ file->seek(0xC0+header.ordnum+i*4);
+ uint32_t final_location=file->get_dword();
+ file->seek( final_location );
+
+ Error err=load_instrument( song->get_instrument( i ) );
+ if (err)
+ return err;
+
+ }
+
+ return FILE_OK;
+
+ if (file->eof_reached() || file->get_error())
+ return FILE_CORRUPTED;
+}
+
+
diff --git a/modules/chibi/cp_loader_it_patterns.cpp b/modules/chibi/cp_loader_it_patterns.cpp
new file mode 100644
index 0000000000..d951a91620
--- /dev/null
+++ b/modules/chibi/cp_loader_it_patterns.cpp
@@ -0,0 +1,166 @@
+/*************************************************************************/
+/* cp_loader_it_patterns.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_it.h"
+
+
+CPLoader::Error CPLoader_IT::load_patterns() {
+
+
+ for (int i=0;i<header.patnum;i++) {
+
+ if (i>=CPSong::MAX_PATTERNS)
+ break;
+
+ /* Position where pattern offsets are stored */
+ file->seek(0xC0+header.ordnum+header.insnum*4+header.smpnum*4+i*4);
+ uint32_t pattern_offset=file->get_dword();
+
+ if (pattern_offset==0) {
+
+ continue;
+ }
+
+ uint16_t pat_size;
+ uint16_t pat_length;
+
+ int row=0,flag,channel,j;
+ uint8_t aux_byte;
+ uint32_t reserved;
+ uint8_t chan_mask[64]; //mask cache for each
+ CPNote last_value[64]; //last value of each
+
+ for (j=0;j<64;j++) {
+
+ chan_mask[j]=0;
+ last_value[j].clear();
+ }
+
+ file->seek(pattern_offset);
+
+ pat_size=file->get_word();
+ pat_length=file->get_word();
+ reserved=file->get_dword();
+
+ song->get_pattern(i)->set_length( pat_length );
+
+ do {
+
+ aux_byte=file->get_byte();
+ flag=aux_byte;
+
+ if ( flag==0 ) {
+
+ row++;
+ } else {
+
+ channel=(flag-1) & 63;
+
+ if ( flag & 128 ) {
+
+ aux_byte=file->get_byte();
+ chan_mask[channel]=aux_byte;
+ }
+
+ CPNote note; //note used for reading
+
+ if ( chan_mask[channel]&1 ) { // read note
+
+ aux_byte=file->get_byte();
+
+ if ( aux_byte<120 )
+ note.note=aux_byte;
+ else if ( aux_byte==255 )
+ note.note=CPNote::OFF;
+ else if ( aux_byte==254 )
+ note.note=CPNote::CUT;
+
+ last_value[channel].note=note.note;
+ }
+
+
+ if ( chan_mask[channel]&2 ) {
+
+ aux_byte=file->get_byte();
+ if ( aux_byte<100 )
+ note.instrument=aux_byte-1;
+
+ last_value[channel].instrument=note.instrument;
+ }
+ if ( chan_mask[channel]&4 ) {
+
+ aux_byte=file->get_byte();
+ if ( aux_byte<213 )
+ note.volume=aux_byte;
+
+ last_value[channel].volume=note.volume;
+ }
+ if ( chan_mask[channel]&8 ) {
+
+ aux_byte=file->get_byte();
+ if ( aux_byte>0 )
+ note.command=aux_byte-1;
+
+
+ last_value[channel].command=note.command;
+
+ note.parameter=file->get_byte();
+
+ last_value[channel].parameter=note.parameter;
+ }
+
+ if ( chan_mask[channel]&16 ) {
+
+ note.note=last_value[channel].note;
+ }
+
+ if ( chan_mask[channel]&32 ) {
+
+ note.instrument=last_value[channel].instrument;
+ }
+ if ( chan_mask[channel]&64 ) {
+
+ note.volume=last_value[channel].volume;
+ }
+ if ( chan_mask[channel]&128 ) {
+
+ note.command=last_value[channel].command;
+ note.parameter=last_value[channel].parameter;
+ }
+
+ song->get_pattern(i)->set_note(channel,row,note);
+ }
+
+
+ } while(row<pat_length);
+
+ }
+
+ return FILE_OK;
+}
+
diff --git a/modules/chibi/cp_loader_it_samples.cpp b/modules/chibi/cp_loader_it_samples.cpp
new file mode 100644
index 0000000000..ced7252a6c
--- /dev/null
+++ b/modules/chibi/cp_loader_it_samples.cpp
@@ -0,0 +1,620 @@
+/*************************************************************************/
+/* cp_loader_it_samples.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_it.h"
+#include "cp_sample.h"
+
+struct AuxSampleData {
+
+
+ uint32_t fileofs;
+ uint32_t c5spd;
+ uint32_t length;
+ uint32_t loop_begin;
+ uint32_t loop_end;
+ bool loop_enabled;
+ bool pingpong_enabled;
+ bool is16bit;
+ bool stereo;
+ bool exists;
+ bool compressed;
+
+};
+
+
+enum IT_Sample_Flags {
+
+ IT_SAMPLE_EXISTS=1,
+ IT_SAMPLE_16BITS=2,
+ IT_SAMPLE_STEREO=4,
+ IT_SAMPLE_COMPRESSED=8,
+ IT_SAMPLE_LOOPED=16,
+ IT_SAMPLE_SUSTAIN_LOOPED=32,
+ IT_SAMPLE_LOOP_IS_PINGPONG=64,
+ IT_SAMPLE_SUSTAIN_LOOP_IS_PINGPONG=128
+};
+
+
+CPLoader::Error CPLoader_IT::load_sample(CPSample *p_sample) {
+
+
+ AuxSampleData aux_sample_data;
+
+ char aux_header[4];
+
+ file->get_byte_array((uint8_t*)aux_header,4);
+
+ if ( aux_header[0]!='I' ||
+ aux_header[1]!='M' ||
+ aux_header[2]!='P' ||
+ aux_header[3]!='S') {
+
+ //CP_PRINTERR("IT CPLoader CPSample: Failed Identifier");
+ return FILE_UNRECOGNIZED;
+ }
+
+
+ // Ignore deprecated 8.3 filename
+ for (int i=0;i<12;i++) file->get_byte();
+
+ file->get_byte(); //ignore zerobyte
+
+ p_sample->set_global_volume( file->get_byte() );
+
+ /* SAMPLE FLAGS */
+ uint8_t flags=file->get_byte();
+ aux_sample_data.loop_enabled=flags&IT_SAMPLE_LOOPED;
+ aux_sample_data.pingpong_enabled=flags&IT_SAMPLE_LOOP_IS_PINGPONG;
+ aux_sample_data.is16bit=flags&IT_SAMPLE_16BITS;
+ aux_sample_data.exists=flags&IT_SAMPLE_EXISTS;
+ aux_sample_data.stereo=flags&IT_SAMPLE_STEREO;
+ aux_sample_data.compressed=flags&IT_SAMPLE_COMPRESSED;
+
+ p_sample->set_default_volume(file->get_byte());
+ /* SAMPLE NAME */
+ char aux_name[26];
+ file->get_byte_array((uint8_t*)aux_name,26);
+ p_sample->set_name(aux_name);
+
+ // ??
+ uint8_t convert_flag=file->get_byte();
+ // PAN
+ uint8_t pan=file->get_byte();
+ p_sample->set_pan( pan&0x7F );
+ p_sample->set_pan_enabled( pan & 0x80 );
+
+ aux_sample_data.length=file->get_dword();
+
+
+ aux_sample_data.loop_begin= file->get_dword();
+ aux_sample_data.loop_end= file->get_dword();
+ aux_sample_data.c5spd=file->get_dword();
+ /*p_sample->data.set_sustain_loop_begin=*/file->get_dword();
+ /*p_sample->data.sustain_loop_end=*/file->get_dword();
+ aux_sample_data.fileofs=file->get_dword();
+ p_sample->set_vibrato_speed( file->get_byte() );
+ p_sample->set_vibrato_depth( file->get_byte() );
+ p_sample->set_vibrato_rate( file->get_byte() );
+ switch( file->get_byte() ) {
+ /* Vibrato Wave: 0=sine, 1=rampdown, 2=square, 3=random */
+ case 0: p_sample->set_vibrato_type( CPSample::VIBRATO_SINE ); break;
+ case 1: p_sample->set_vibrato_type( CPSample::VIBRATO_SAW ); break;
+ case 2: p_sample->set_vibrato_type( CPSample::VIBRATO_SQUARE ); break;
+ case 3: p_sample->set_vibrato_type( CPSample::VIBRATO_RANDOM ); break;
+ default: p_sample->set_vibrato_type( CPSample::VIBRATO_SINE ); break;
+ }
+
+ //printf("Name %s - Flags: fileofs :%i - c5spd %i - len %i 16b %i - data?: %i\n",p_sample->get_name(),aux_sample_data.fileofs,aux_sample_data.c5spd, aux_sample_data.length, aux_sample_data.is16bit,aux_sample_data.exists);
+ CPSample_ID samp_id;
+
+ if (aux_sample_data.exists) {
+ samp_id=load_sample_data(aux_sample_data);
+ CPSampleManager::get_singleton()->set_c5_freq(samp_id,aux_sample_data.c5spd);
+ CPSampleManager::get_singleton()->set_loop_begin( samp_id,aux_sample_data.loop_begin );
+ CPSampleManager::get_singleton()->set_loop_end( samp_id,aux_sample_data.loop_end );
+ CPSample_Loop_Type loop_type=aux_sample_data.loop_enabled?( aux_sample_data.pingpong_enabled? CP_LOOP_BIDI: CP_LOOP_FORWARD):CP_LOOP_NONE;
+ CPSampleManager::get_singleton()->set_loop_end( samp_id,aux_sample_data.loop_end );
+ CPSampleManager::get_singleton()->set_loop_type( samp_id, loop_type);
+
+ }
+
+ //printf("Loaded id is null?: %i\n",samp_id.is_null());
+ p_sample->set_sample_data(samp_id);
+ if (!samp_id.is_null()) {
+
+ // printf("Loaded ID: stereo: %i len %i 16bit %i\n",CPSampleManager::get_singleton()->is_stereo(samp_id), CPSampleManager::get_singleton()->get_size( samp_id), CPSampleManager::get_singleton()->is_16bits( samp_id) );
+ }
+
+ CP_ERR_COND_V( file->eof_reached(),FILE_CORRUPTED );
+ CP_ERR_COND_V( file->get_error(),FILE_CORRUPTED );
+
+ return FILE_OK;
+
+}
+
+CPSample_ID CPLoader_IT::load_sample_data(AuxSampleData& p_sample_data) {
+
+
+ int aux_sample_properties = (p_sample_data.is16bit?IT_SAMPLE_16BITS:0)|(p_sample_data.compressed?IT_SAMPLE_COMPRESSED:0)|(p_sample_data.stereo?IT_SAMPLE_STEREO:0);
+
+ file->seek(p_sample_data.fileofs);
+
+ CPSampleManager *sm=CPSampleManager::get_singleton();
+
+ CPSample_ID id;
+
+ switch (aux_sample_properties) {
+
+ case (0): // 8 bits, mono
+ case (IT_SAMPLE_16BITS): // 16 bits mono
+ case (IT_SAMPLE_STEREO): // 8 bits stereo
+ case (IT_SAMPLE_16BITS|IT_SAMPLE_STEREO): { // 16 bits mono
+
+ id=sm->create(p_sample_data.is16bit,p_sample_data.stereo,p_sample_data.length);
+ if (id.is_null())
+ break;
+
+ sm->lock_data(id);
+
+ int16_t *ptr16 = (int16_t*)sm->get_data(id);
+ int8_t *ptr8=(int8_t*)ptr16;
+
+ int chans=p_sample_data.stereo?2:1;
+
+ if (p_sample_data.is16bit) {
+
+ for (int c=0;c<chans;c++) {
+
+ for (int i=0;i<p_sample_data.length;i++) {
+
+ ptr16[i*chans+c]=file->get_word();
+ }
+ }
+ } else {
+
+ for (int c=0;c<chans;c++) {
+
+ for (int i=0;i<p_sample_data.length;i++) {
+
+ ptr8[i*chans+c]=file->get_byte();
+ }
+ }
+
+ }
+
+ sm->unlock_data(id);
+
+ } break;
+ case (IT_SAMPLE_COMPRESSED): { // 8 bits compressed
+
+
+ id=sm->create(false,false,p_sample_data.length);
+ if (id.is_null())
+ break;
+ sm->lock_data(id);
+
+ if ( load_sample_8bits_IT_compressed((void*)sm->get_data( id),p_sample_data.length) ) {
+
+ sm->unlock_data(id);
+ sm->destroy(id);
+
+ break;
+ }
+
+ sm->unlock_data(id);
+
+
+ } break;
+ case (IT_SAMPLE_16BITS|IT_SAMPLE_COMPRESSED): { // 16 bits compressed
+
+
+ id=sm->create(true,false,p_sample_data.length);
+ if (id.is_null())
+ break;
+ sm->lock_data(id);
+
+ if ( load_sample_16bits_IT_compressed((void*)sm->get_data(id),p_sample_data.length) ) {
+
+ sm->unlock_data(id);
+ sm->destroy(id);
+ break;
+ }
+
+ sm->unlock_data(id);
+
+ } break;
+ default: {
+
+ // I dont know how to handle stereo compressed, does that exist?
+ } break;
+
+ }
+
+
+ return id;
+}
+
+
+CPLoader::Error CPLoader_IT::load_samples() {
+
+ for (int i=0;i<header.smpnum;i++) {
+
+ //seek to sample
+ file->seek(0xC0+header.ordnum+header.insnum*4+i*4);
+
+ uint32_t final_location=file->get_dword();
+ file->seek( final_location );
+
+
+ Error err=load_sample(song->get_sample(i));
+ CP_ERR_COND_V(err,err);
+
+ }
+
+ if (file->eof_reached() || file->get_error())
+ return FILE_CORRUPTED;
+
+ return FILE_OK;
+}
+/* * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE
+
+ -The following sample decompression code is based on xmp's code.(http://xmp.helllabs.org) which is based in openCP code.
+
+* NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE */
+
+uint32_t CPLoader_IT::read_n_bits_from_IT_compressed_block (uint8_t p_bits_to_read) {
+
+ uint32_t aux_return_value;
+ uint32_t val;
+
+ uint8_t *buffer=(uint8_t*)source_position;
+ if ( p_bits_to_read <= source_remaining_bits ) {
+
+ val=buffer[3];
+ val<<=8;
+ val|=buffer[2];
+ val<<=8;
+ val|=buffer[1];
+ val<<=8;
+ val|=buffer[0];
+
+ aux_return_value = val & ((1 << p_bits_to_read) - 1);
+ val >>= p_bits_to_read;
+ source_remaining_bits -= p_bits_to_read;
+
+ buffer[3]=val>>24;
+ buffer[2]=(val>>16)&0xFF;
+ buffer[1]=(val>>8)&0xFF;
+ buffer[0]=(val)&0xFF;
+
+ } else {
+ aux_return_value=buffer[3];
+ aux_return_value<<=8;
+ aux_return_value|=buffer[2];
+ aux_return_value<<=8;
+ aux_return_value|=buffer[1];
+ aux_return_value<<=8;
+ aux_return_value|=buffer[0];
+
+ uint32_t nbits = p_bits_to_read - source_remaining_bits;
+ source_position++;
+
+ buffer+=4;
+ val=buffer[3];
+ val<<=8;
+ val|=buffer[2];
+ val<<=8;
+ val|=buffer[1];
+ val<<=8;
+ val|=buffer[0];
+ aux_return_value |= ((val & ((1 << nbits) - 1)) << source_remaining_bits);
+ val >>= nbits;
+ source_remaining_bits = 32 - nbits;
+ buffer[3]=val>>24;
+ buffer[2]=(val>>16)&0xFF;
+ buffer[1]=(val>>8)&0xFF;
+ buffer[0]=(val)&0xFF;
+
+ }
+
+ return aux_return_value;
+}
+
+bool CPLoader_IT::read_IT_compressed_block (bool p_16bits) {
+
+ uint16_t size;
+
+ size=file->get_word();
+
+ if (file->eof_reached() || file->get_error()) return true;
+
+ pat_data = (uint8_t*)CP_ALLOC( 4* ((size >> 2) + 2) );
+ if (!pat_data)
+ return true;
+
+
+ source_buffer=(uint32_t*)pat_data;
+ file->get_byte_array((uint8_t*)source_buffer,size);
+
+ if (file->eof_reached() || file->get_error()) {
+
+ free_IT_compressed_block();
+ return true;
+ }
+
+ source_position = source_buffer;
+ source_remaining_bits = 32;
+
+ return false;
+}
+
+void CPLoader_IT::free_IT_compressed_block () {
+
+
+ if (pat_data) {
+ CP_FREE(pat_data);
+ pat_data=NULL;
+ }
+
+}
+
+bool CPLoader_IT::load_sample_8bits_IT_compressed(void *p_dest_buffer,int p_buffsize) {
+
+ int8_t *dest_buffer; /* destination buffer which will be returned */
+ uint16_t block_length; /* length of compressed data block in samples */
+ uint16_t block_position; /* position in block */
+ uint8_t bit_width; /* actual "bit width" */
+ uint16_t aux_value; /* value read from file to be processed */
+ int8_t d1, d2; /* integrator buffers (d2 for it2.15) */
+ int8_t *dest_position; /* position in output buffer */
+ int8_t v; /* sample value */
+ bool it215; // is this an it215 module?
+
+ dest_buffer = (int8_t *) p_dest_buffer;
+
+ if (dest_buffer==NULL)
+ return true;
+
+ for (int i=0;i<p_buffsize;i++)
+ dest_buffer[i]=0;
+
+
+ dest_position = dest_buffer;
+
+ it215=(header.cmwt==0x215);
+
+ /* now unpack data till the dest buffer is full */
+
+ while (p_buffsize) {
+ /* read a new block of compressed data and reset variables */
+ if ( read_IT_compressed_block(false) ) {
+ CP_PRINTERR("Out of memory decompressing IT CPSample");
+ return true;
+ }
+
+
+ block_length = (p_buffsize < 0x8000) ? p_buffsize : 0x8000;
+
+ block_position = 0;
+
+ bit_width = 9; /* start with width of 9 bits */
+
+ d1 = d2 = 0; /* reset integrator buffers */
+
+ /* now uncompress the data block */
+ while ( block_position < block_length ) {
+
+ aux_value = read_n_bits_from_IT_compressed_block(bit_width); /* read bits */
+
+ if ( bit_width < 7 ) { /* method 1 (1-6 bits) */
+
+ if ( aux_value == (1 << (bit_width - 1)) ) { /* check for "100..." */
+
+ aux_value = read_n_bits_from_IT_compressed_block(3) + 1; /* yes -> read new width; */
+ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
+ /* and expand it */
+ continue; /* ... next value */
+ }
+
+ } else if ( bit_width < 9 ) { /* method 2 (7-8 bits) */
+
+ uint8_t border = (0xFF >> (9 - bit_width)) - 4;
+ /* lower border for width chg */
+
+ if ( aux_value > border && aux_value <= (border + 8) ) {
+
+ aux_value -= border; /* convert width to 1-8 */
+ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
+ /* and expand it */
+ continue; /* ... next value */
+ }
+
+
+ } else if ( bit_width == 9 ) { /* method 3 (9 bits) */
+
+ if ( aux_value & 0x100 ) { /* bit 8 set? */
+
+ bit_width = (aux_value + 1) & 0xff; /* new width... */
+ continue; /* ... and next value */
+ }
+
+ } else { /* illegal width, abort */
+
+
+ free_IT_compressed_block();
+ CP_PRINTERR("CPSample has illegal BitWidth ");
+ return true;
+ }
+
+ /* now expand value to signed byte */
+ if ( bit_width < 8 ) {
+
+ uint8_t tmp_shift = 8 - bit_width;
+
+ v=(aux_value << tmp_shift);
+ v>>=tmp_shift;
+
+ } else v = (int8_t) aux_value;
+
+ /* integrate upon the sample values */
+ d1 += v;
+ d2 += d1;
+
+ /* ... and store it into the buffer */
+ *(dest_position++) = it215 ? d2 : d1;
+ block_position++;
+
+ }
+
+ /* now subtract block lenght from total length and go on */
+ free_IT_compressed_block();
+ p_buffsize -= block_length;
+ }
+
+
+ return false;
+}
+
+bool CPLoader_IT::load_sample_16bits_IT_compressed(void *p_dest_buffer,int p_buffsize) {
+
+ int16_t *dest_buffer; /* destination buffer which will be returned */
+ uint16_t block_length; /* length of compressed data block in samples */
+ uint16_t block_position; /* position in block */
+ uint8_t bit_width; /* actual "bit width" */
+ uint32_t aux_value; /* value read from file to be processed */
+ int16_t d1, d2; /* integrator buffers (d2 for it2.15) */
+ int16_t *dest_position; /* position in output buffer */
+ int16_t v; /* sample value */
+
+ bool it215; // is this an it215 module?
+
+ dest_buffer = (int16_t *) p_dest_buffer;
+
+ if (dest_buffer==NULL)
+ return true;
+
+ for (int i=0;i<p_buffsize;i++)
+ dest_buffer[i]=0;
+
+ dest_position = dest_buffer;
+
+ it215=(header.cmwt==0x215);
+
+
+ while (p_buffsize) {
+ /* read a new block of compressed data and reset variables */
+ if ( read_IT_compressed_block(true) ) {
+
+ return true;
+ }
+
+
+ block_length = (p_buffsize < 0x4000) ? p_buffsize : 0x4000;
+
+ block_position = 0;
+
+ bit_width = 17; /* start with width of 9 bits */
+
+ d1 = d2 = 0; /* reset integrator buffers */
+
+ while ( block_position < block_length ) {
+
+ aux_value = read_n_bits_from_IT_compressed_block(bit_width); /* read bits */
+
+ if ( bit_width < 7 ) { /* method 1 (1-6 bits) */
+
+ if ( (signed)aux_value == (1 << (bit_width - 1)) ) { /* check for "100..." */
+
+ aux_value = read_n_bits_from_IT_compressed_block(4) + 1; /* yes -> read new width; */
+ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
+ /* and expand it */
+ continue; /* ... next value */
+ }
+
+ } else if ( bit_width < 17 ) {
+
+ uint16_t border = (0xFFFF >> (17 - bit_width)) - 8;
+
+ if ( (int)aux_value > (int)border && (int)aux_value <= ((int)border + 16) ) {
+
+ aux_value -= border; /* convert width to 1-8 */
+ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
+ /* and expand it */
+ continue; /* ... next value */
+ }
+
+
+ } else if ( bit_width == 17 ) {
+
+ if ( aux_value & 0x10000 ) { /* bit 8 set? */
+
+ bit_width = (aux_value + 1) & 0xff; /* new width... */
+ continue; /* ... and next value */
+ }
+
+ } else { /* illegal width, abort */
+
+ CP_PRINTERR("CPSample has illegal BitWidth ");
+
+ free_IT_compressed_block();
+
+ return true;
+ }
+
+ /* now expand value to signed byte */
+ if ( bit_width < 16 ) {
+
+ uint8_t tmp_shift = 16 - bit_width;
+
+ v=(aux_value << tmp_shift);
+ v>>=tmp_shift;
+
+ } else v = (int16_t) aux_value;
+
+ /* integrate upon the sample values */
+ d1 += v;
+ d2 += d1;
+
+ /* ... and store it into the buffer */
+ *(dest_position++) = it215 ? d2 : d1;
+ block_position++;
+
+ }
+
+ /* now subtract block lenght from total length and go on */
+ free_IT_compressed_block();
+ p_buffsize -= block_length;
+ }
+
+
+ return false;
+
+}
+
+
+
diff --git a/modules/chibi/cp_loader_mod.cpp b/modules/chibi/cp_loader_mod.cpp
new file mode 100644
index 0000000000..f867b77914
--- /dev/null
+++ b/modules/chibi/cp_loader_mod.cpp
@@ -0,0 +1,482 @@
+/*************************************************************************/
+/* cp_loader_mod.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_mod.h"
+
+
+static bool tag_equal_to(const char *p_tag, const char *p_string) {
+
+ return( p_tag[0]==p_string[0] &&
+ p_tag[1]==p_string[1] &&
+ p_tag[2]==p_string[2] &&
+ p_tag[3]==p_string[3]);
+}
+/* ProTracker period table */
+uint16_t period_table[6*12] = {
+ 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907,
+ 856,808,762,720,678,640,604,570,538,508,480,453,
+ 428,404,381,360,339,320,302,285,269,254,240,226,
+ 214,202,190,180,170,160,151,143,135,127,120,113,
+ 107,101,95,90,85,80,75,71,67,63,60,56,
+ 53,50,47,45,42,40,37,35,33,31,30,28
+};
+
+
+CPLoader::Error CPLoader_MOD::load_song(const char *p_file,CPSong *p_song,bool p_sampleset) {
+
+ if (file->open(p_file,CPFileAccessWrapper::READ)) {
+ //printf("Can't open file! %s\n",p_file);
+ return FILE_CANNOT_OPEN;
+ };
+
+ /* FIRST OF ALL, one needs to read the .mod file format tag */
+ file->seek( 1080 ); //located at 1080
+
+ char format_tag[4];
+
+ file->get_byte_array( (uint8_t*)format_tag, 4 );
+
+ int channels=-1;
+
+ /** THE PAIN!! - COMPARE TAGS */
+
+ /* Classic 4-chan */
+ if (tag_equal_to(format_tag,"M.K.") )
+ channels=4;
+ if (tag_equal_to(format_tag,"FLT4") )
+ channels=4;
+ if (tag_equal_to(format_tag,"M!K!") )
+ channels=4;
+
+ /* 8 Channel MODS */
+
+ if (tag_equal_to(format_tag,"FLT8") )
+ channels=2;
+
+ if (tag_equal_to(format_tag,"CD81") )
+ channels=2;
+
+ /* Custom channel MODS */
+
+ for (int i=1;i<=32;i++) {
+
+ if (i<10) { // up to 9 channels mods
+
+ /* Old Take Tracker */
+ char old_take_tracker[4]={'T','D','Z',char('0'+i)};
+
+ if (tag_equal_to(format_tag,old_take_tracker)) {
+
+ channels=i;
+ break;
+ }
+
+ /* Contemplates many XCHN Formats */
+ char xchn[4]={char('0'+i),'C','H','N'};
+
+ if (tag_equal_to(format_tag,xchn)) {
+
+ channels=i;
+ break;
+ }
+ }
+
+ /* Fast Tracker */
+ char fast_tracker[4]={char('0'+(i/10)),char('0'+(i%10)),'C','H'};
+
+ if (tag_equal_to(format_tag,fast_tracker)) {
+
+ channels=i;
+ break;
+ }
+
+ }
+
+
+ if (channels==-1) {
+
+ file->close();
+ return FILE_UNRECOGNIZED;
+ }
+
+
+
+ /** Load CPSong INFO */
+
+ file->seek( 0 ); //go to begining of file
+
+ file->set_endian_conversion( true );
+ p_song->reset();
+ p_song->set_instruments( false );
+
+ char name[21];
+
+ file->get_byte_array( (uint8_t*)name,20);
+ name[20]=0;
+
+ p_song->set_name(name);
+ p_song->set_old_effects( true );
+ p_song->set_linear_slides( false );
+ p_song->set_compatible_gxx( true );
+
+
+
+ CPSampleManager *sm=CPSampleManager::get_singleton();
+
+ int instruments=31;
+
+ for (int i=0;i<instruments;i++) {
+
+ char sample_name[23];
+ file->get_byte_array( (uint8_t*)sample_name,22);
+ sample_name[22]=0;
+
+ uint32_t sample_len=file->get_word();
+ sample_len<<=1;
+
+ uint8_t fine_nibble=file->get_byte()&0xF;
+
+
+ //(int8_t)(fine_nibble & 7) - (int8_t)(fine_nibble & 8); //yesso's genius trick
+ // boo, I can't use it :( but i leave it here because of how cool it is
+ uint8_t linear_volume=file->get_byte(); //0 .. ?
+
+ uint32_t loop_begin=file->get_word(); //0 .. ?
+ loop_begin<<=1;
+ uint32_t loop_end=file->get_word(); //0 .. ?
+ loop_end<<=1;
+
+ if (sample_len>0) {
+
+ CPSample_ID sid=sm->create( false, false, sample_len );
+
+ if (sid.is_null()) {
+
+ file->close();
+ return FILE_OUT_OF_MEMORY;
+ }
+
+ if (loop_end>2) {
+ sm->set_loop_begin( sid, loop_begin );
+ sm->set_loop_end( sid, loop_end+loop_begin );
+ sm->set_loop_type( sid,CP_LOOP_FORWARD );
+ }
+ static const uint16_t fine_to_freq[16]={
+ 8363,8413,8463,8529,8581,8651,8723,8757,
+ 7895,7941,7985,8046,8107,8169,8232,8280
+ };
+
+ sm->set_c5_freq( sid, fine_to_freq[fine_nibble] );
+ p_song->get_sample(i)->set_sample_data(sid);
+ }
+
+ p_song->get_sample(i)->set_name(sample_name);
+ p_song->get_sample(i)->set_default_volume( linear_volume );
+
+
+
+ }
+
+ /* pan for MODs */
+ for (int i=0;i<channels;i++)
+ p_song->set_channel_pan( i, (((i&3)==1) || ((i&3)==2)) ? 0: 64);
+
+
+ uint8_t order_count=file->get_byte();
+// uint8_t loop_to=file->get_byte();
+
+
+ int pattern_count=0;
+
+ for (int i=0;i<128;i++) {
+
+ uint8_t order=file->get_byte();
+
+
+ if (i<order_count) {
+ p_song->set_order(i,order);
+
+ /* Determine the amount of patterns */
+ if ((order+1)>pattern_count)
+ pattern_count=order+1;
+ } else
+ p_song->set_order( i, CP_ORDER_NONE );
+ }
+
+ if (instruments==31)
+ file->get_dword(); // identiefier, now skip it
+
+ for (int i=0;i<pattern_count;i++) {
+
+ for(int line=0;line<64;line++) {
+
+ for(int column=0;column<channels;column++) {
+
+ uint32_t note_w=file->get_dword();
+
+ CPNote note;
+
+ note.instrument=(note_w>>12)&0xF;
+ note.instrument|=(note_w>>24)&0xF0;
+
+ if (note.instrument==0)
+ note.instrument=CPNote::EMPTY;
+ else
+ note.instrument--;
+
+ note.parameter=note_w&0xFF;
+
+ int cmd=(note_w>>8)&0xF;
+
+ uint32_t period=(note_w>>16)&0xFFF;
+
+ if (period>0 && period<0xFFF) {
+
+ //period>>=2;
+ //period<<=1;
+ for (int n=0; n<6*12; n++) {
+
+ if (period >= period_table[n]) {
+
+ if ((period!=period_table[n]) && (n))
+ {
+ uint32_t p1 = period_table[n-1];
+ uint32_t p2 = period_table[n];
+ if (p1 - period < (period - p2)) {
+
+ note.note=n+36;
+ break;
+ }
+ }
+ note.note=n+1+36;
+ break;
+ }
+ }
+ if (note.note==CPNote::EMPTY)
+ note.note=6*12+36;
+
+ note.note--;
+ }
+
+
+ switch(cmd) {
+
+ case 0x0: {
+
+ if (note.parameter>0)
+ note.command='J'-'A';
+ } break;
+ case 0x1: {
+ note.command='F'-'A';
+ } break;
+ case 0x2: {
+
+ note.command='E'-'A';
+ } break;
+ case 0x3: {
+
+ note.command='G'-'A';
+ } break;
+ case 0x4: {
+
+ note.command='H'-'A';
+ } break;
+ case 0x5: {
+ note.command='L'-'A';
+ } break;
+ case 0x6: {
+
+ note.command='K'-'A';
+ } break;
+ case 0x7: {
+ note.command='R'-'A';
+ } break;
+ case 0x8: {
+
+ note.command='X'-'A';
+ } break;
+ case 0x9: {
+
+ note.command='O'-'A';
+
+ } break;
+ case 0xA: {
+
+ note.command='D'-'A';
+
+ } break;
+ case 0xB: {
+
+ note.command='B'-'A';
+
+ } break;
+ case 0xC: {
+
+ note.volume=note.parameter;
+ if (note.volume>64)
+ note.volume=64;
+ note.parameter=0;
+
+ } break;
+ case 0xD: {
+
+ note.command='C'-'A';
+ note.parameter=(note.parameter>>4)*10 + (note.parameter&0xF);
+
+ } break;
+ case 0xE: { //SPECIAL EFFECT!
+
+ note.command='S'-'A';
+
+ switch(note.parameter>>4) {
+
+ case 0x1: {
+
+ note.command='F'-'A';
+ note.parameter=0xF0|(note.parameter&0xF);
+ } break;
+ case 0x2: {
+
+ note.command='E'-'A';
+ note.parameter=0xF0|(note.parameter&0xF);
+ } break;
+ case 0x4: {
+
+ note.command='S'-'A';
+ note.parameter=0x30|(note.parameter&0x3);
+
+ } break;
+ case 0x6: {
+
+ note.command='S'-'A';
+ note.parameter=0xB0|(note.parameter&0xF);
+
+ } break;
+ case 0x7: {
+ note.command='S'-'A';
+ note.parameter=0x40|(note.parameter&0x3);
+
+ } break;
+ case 0x8: {
+
+ note.command='S'-'A'; // wow, it's the same!
+
+ } break;
+ case 0x9: {
+ note.command='Q'-'A';
+ note.parameter=(note.parameter&0xF);
+
+ } break;
+ case 0xA: {
+
+ note.command='D'-'A';
+ note.parameter=0xF|((note.parameter&0xF)<<4);
+
+ } break;
+ case 0xB: {
+ note.command='D'-'A';
+ note.parameter=0xF0|(note.parameter&0xF);
+
+ } break;
+ case 0xC:
+ case 0xD: {
+
+ note.command='S'-'A'; //wow, they are the same!
+
+ } break;
+ case 0xE: {
+ note.command='S'-'A';
+ note.parameter=0x60|(note.parameter&0xF);
+
+ } break;
+
+ default: {
+
+ note.command=CPNote::EMPTY;
+ note.parameter=0;
+ } break;
+
+ }
+ } break;
+ case 0xF: {
+
+ if (note.parameter<32)
+ note.command='A'-'A';
+ else
+ note.command='T'-'A';
+
+ } break;
+ }
+
+ p_song->get_pattern(i)->set_note( column,line, note );
+ }
+ }
+ }
+
+
+
+ for (int i=0;i<instruments;i++) {
+
+ CPSample_ID sid=p_song->get_sample(i)->get_sample_data();
+ if (sid.is_null()) {
+ continue; //empty sample, not stored?
+ }
+ sm->lock_data(sid);
+ uint8_t *dataptr = (uint8_t*)sm->get_data(sid);
+
+ int len=sm->get_size(sid);
+ for (int s=0;s<len;s++) {
+
+ uint8_t d=file->get_byte();
+ //d-=128; //convert to signed
+ int8_t*ds=(int8_t*)&d;
+ dataptr[s]=*ds;
+
+ }
+ sm->unlock_data(sid);
+ }
+
+ file->close();
+
+ return FILE_OK;
+
+
+}
+
+
+CPLoader_MOD::CPLoader_MOD(CPFileAccessWrapper *p_file) {
+
+ file=p_file;
+}
+
+
+CPLoader_MOD::~CPLoader_MOD()
+{
+}
+
+
diff --git a/modules/chibi/cp_loader_mod.h b/modules/chibi/cp_loader_mod.h
new file mode 100644
index 0000000000..636f4f00f2
--- /dev/null
+++ b/modules/chibi/cp_loader_mod.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* cp_loader_mod.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_LOADER_MOD_H
+#define CP_LOADER_MOD_H
+#include "cp_loader.h"
+/**
+ @author Juan Linietsky <reduz@gmail.com>
+*/
+class CPLoader_MOD : public CPLoader {
+
+ CPFileAccessWrapper *file;
+public:
+
+ bool can_load_song() { return true; }
+ bool can_load_sample() { return false; }
+ bool can_load_instrument() { return false; }
+
+ Error load_song(const char *p_file,CPSong *p_song,bool p_sampleset);
+ Error load_sample(const char *p_file,CPSample *p_sample) { return FILE_UNRECOGNIZED; }
+ Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx) { return FILE_UNRECOGNIZED; }
+
+ CPLoader_MOD(CPFileAccessWrapper *p_file);
+ ~CPLoader_MOD();
+};
+
+#endif
diff --git a/modules/chibi/cp_loader_s3m.cpp b/modules/chibi/cp_loader_s3m.cpp
new file mode 100644
index 0000000000..0fc15c1e2f
--- /dev/null
+++ b/modules/chibi/cp_loader_s3m.cpp
@@ -0,0 +1,413 @@
+/*************************************************************************/
+/* cp_loader_s3m.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_loader_s3m.h"
+
+#define BITBOOL(m_exp) ((m_exp)?1:0)
+
+
+CPLoader::Error CPLoader_S3M::load_header() {
+
+ int i;
+
+
+ file->get_byte_array((uint8_t*)header.songname,28);
+ header.t1a=file->get_byte();
+ header.type=file->get_byte();
+ file->get_byte_array((uint8_t*)header.unused1,2);
+ header.ordnum=file->get_word();
+ header.insnum=file->get_word();
+ header.patnum=file->get_word();
+ header.flags=file->get_word();
+ header.tracker=file->get_word();
+ header.fileformat=file->get_word();
+ file->get_byte_array((uint8_t*)header.scrm,4);
+ header.scrm[4]=0;
+
+ if (header.scrm[0]!='S' || header.scrm[1]!='C' || header.scrm[2]!='R' || header.scrm[3]!='M')
+ return FILE_UNRECOGNIZED;
+
+ header.mastervol=file->get_byte();
+ header.initspeed=file->get_byte();
+ header.inittempo=file->get_byte();
+ header.mastermult=file->get_byte();
+ header.ultraclick=file->get_byte();
+ header.pantable=file->get_byte();
+ file->get_byte_array((uint8_t*)header.unused2,8);
+ header.special=file->get_word();
+ file->get_byte_array((uint8_t*)header.channels,32);
+
+ file->get_byte_array((uint8_t*)header.orderlist,header.ordnum);
+
+ header.scrm[4]=0;
+ if (header.scrm[0]!='S' || header.scrm[1]!='C' || header.scrm[2]!='R' || header.scrm[3]!='M') //again?
+ return FILE_UNRECOGNIZED;
+ //sample parapointers
+ for (i=0;i<header.insnum;i++) {
+
+ int parapointer;
+ parapointer=file->get_word();
+ parapointer=(parapointer*16);
+ sample_parapointers[i]=parapointer;
+ }
+ //pattern
+ for (i=0;i<header.patnum;i++) {
+
+ int parapointer;
+ parapointer=file->get_word();
+ parapointer=(parapointer*16);
+ pattern_parapointers[i]=parapointer;
+ }
+
+ if (header.pantable==252) {
+
+ file->get_byte_array((uint8_t*)header.pannings,32);
+ }
+
+ return FILE_OK;
+
+
+}
+
+
+void CPLoader_S3M::set_header() {
+
+
+
+
+ song->set_name( header.songname );
+// song->variables.filename=
+
+ song->set_row_highlight_minor( 4 );
+ song->set_row_highlight_major( 16 );
+ song->set_mixing_volume( header.mastervol );
+ song->set_linear_slides( false );
+ song->set_old_effects( !(header.flags&64) );
+ song->set_compatible_gxx( true );
+
+ song->set_global_volume( header.mastermult );
+ song->set_speed( header.initspeed );
+ song->set_tempo( header.inittempo );
+
+ //[TODO] Set Panning Positions.. ?
+
+ for (int i=0;i<header.ordnum;i++) song->set_order(i,header.orderlist[i]);
+
+}
+
+CPLoader::Error CPLoader_S3M::load_sample(CPSample *p_sample) {
+
+
+
+ int type=file->get_byte();
+
+ char filename[13];
+ file->get_byte_array((uint8_t*)filename,12);
+ filename[12]=0;
+
+
+ uint32_t samplepos=(uint32_t)file->get_byte() << 16;
+ samplepos|=file->get_word();
+ samplepos*=16;
+// printf("sample at %i\n",samplepos);
+ /**/
+ int sample_size=file->get_dword();
+
+
+ int loop_begin=file->get_dword();
+ int loop_end=file->get_dword();
+
+ int def_volume=file->get_byte();;
+ int dsk=file->get_byte();
+ int pack=file->get_byte();
+
+ int flags=file->get_byte();
+ int c2speed=file->get_dword();
+
+ file->get_dword(); //useless crap
+ file->get_dword();
+ file->get_dword();
+
+
+ char name[29];
+ file->get_byte_array((uint8_t*)name,28);
+ name[28]=0;
+
+ p_sample->set_default_volume(def_volume);
+ p_sample->set_name(name);
+
+ char scrs[5];
+ file->get_byte_array((uint8_t*)scrs,4);
+ scrs[4]=0;
+
+
+
+ bool data_is_16bits=flags&4;
+ bool data_is_stereo=flags&2;
+
+ if (type==0) {
+ //empty sample
+ return FILE_OK;
+ }
+
+
+ if ((type!=1) || scrs[0]!='S' || scrs[1]!='C' || scrs[2]!='R' || scrs[3]!='S' ) {
+// printf("type: %i, %c%c%c%c\n",type,scrs[0],scrs[1],scrs[2],scrs[3]);
+ CP_PRINTERR("Not an S3M CPSample!");
+ return FILE_CORRUPTED;
+ }
+
+ //p_sample->data.set_c5_freq(p_sample->c2spd<<1);
+
+ file->seek(samplepos);
+
+ int real_sample_size=sample_size<<BITBOOL(data_is_16bits);
+ real_sample_size<<=BITBOOL(data_is_stereo);
+
+ CPSampleManager *sm=CPSampleManager::get_singleton();
+
+ CPSample_ID id =sm->create( data_is_16bits, data_is_stereo, sample_size );
+
+ if (id.is_null())
+ return FILE_OUT_OF_MEMORY;
+
+ sm->lock_data(id);
+ void *dataptr = sm->get_data(id);
+
+ int chans = (data_is_stereo?2:1);
+ for (int c=0;c<chans;c++) {
+ for (int i=0;i<sample_size;i++) {
+
+ if (data_is_16bits) {
+
+ uint16_t s=file->get_word();
+ s-=32768; //toggle sign
+
+ int16_t *v=(int16_t*)&s;
+ ((int16_t*)dataptr)[i*chans+c]=*v;
+ } else {
+
+
+ int8_t *v;
+ uint8_t s=file->get_byte();
+ s-=128; //toggle sign
+ v=(int8_t*)&s;
+ ((int8_t*)dataptr)[i*chans+c]=*v;
+
+ }
+
+ }
+
+ }
+
+ sm->unlock_data(id);
+
+
+ sm->set_loop_begin( id, loop_begin );
+ sm->set_loop_end( id, loop_end );
+ sm->set_loop_type( id, (flags&1) ? CP_LOOP_FORWARD : CP_LOOP_NONE );
+ sm->set_c5_freq( id, c2speed << 1 );
+ p_sample->set_sample_data(id);
+
+ /* Scream tracker previous to 3.10 seems to be buggy, as in, wont save what is after the sample loop, including the loop end point. Because of this I must fix it by habd */
+ if (flags&1) {
+
+ for (int c=0;c<(data_is_stereo?2:1);c++) {
+ sm->set_data( id, loop_end, sm->get_data( id, loop_begin,c ),c );
+
+ }
+ }
+
+
+ return FILE_OK;
+
+}
+
+
+CPLoader::Error CPLoader_S3M::load_pattern(CPPattern *p_pattern) {
+
+ int row=0,flag,ch;
+ CPNote n;
+ int length,accum=0;
+
+ length=file->get_word();
+ p_pattern->set_length(64);
+
+ /* clear pattern data */
+ while((row<64) && (accum<=length) ) {
+ flag=file->get_byte();
+ accum++;
+
+ n.clear();
+ if(flag) {
+ // ch=remap[flag&31];
+// ch=remap[flag&31];
+// if(ch!=-1)
+// n=s3mbuf[(64U*ch)+row];
+// else
+// n=&dummy;
+
+ ch=flag&31;
+
+ if(flag&32) {
+ n.note=file->get_byte();
+ if (n.note==255) {
+
+ n.note=CPNote::EMPTY;
+ } else if (n.note==254) {
+
+ n.note=CPNote::CUT;
+ } else {
+
+ n.note=((n.note>>4)*12)+(n.note&0xF);
+ }
+
+ n.instrument=file->get_byte()-1;
+ accum+=2;
+
+ }
+ if(flag&64) {
+ n.volume=file->get_byte();
+ if (n.volume>64) n.volume=64;
+ accum++;
+
+ }
+ if(flag&128) {
+ n.command=file->get_byte()-1;
+ n.parameter=file->get_byte();
+ accum+=2;
+ }
+
+ p_pattern->set_note(ch,row,n);
+ } else row++;
+ }
+ return FILE_OK;
+
+
+}
+
+CPLoader::Error CPLoader_S3M::load_sample(const char *p_file,CPSample *p_sample) {
+
+ return FILE_UNRECOGNIZED;
+}
+CPLoader::Error CPLoader_S3M::load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx) {
+
+ return FILE_UNRECOGNIZED;
+
+}
+
+
+CPLoader::Error CPLoader_S3M::load_samples() {
+
+ int i;
+
+ for(i=0;i<header.insnum;i++) {
+
+ file->seek(sample_parapointers[i]);
+ load_sample(song->get_sample(i));
+ sample_count++;
+ }
+
+ return FILE_OK;
+}
+
+CPLoader::Error CPLoader_S3M::load_patterns() {
+
+ int i;
+
+ Error err;
+ for(i=0;i<header.patnum;i++) {
+
+ file->seek(pattern_parapointers[i]);
+
+ err=load_pattern(song->get_pattern(i) );
+ CP_ERR_COND_V(err,err);
+
+
+ pattern_count++;
+ }
+ return FILE_OK;
+
+}
+
+CPLoader::Error CPLoader_S3M::load_song(const char *p_file,CPSong *p_song,bool p_sampleset) {
+
+ song=p_song;
+
+ if (file->open(p_file,CPFileAccessWrapper::READ)) {
+ //printf("Can't open file! %s\n",p_file);
+ return FILE_CANNOT_OPEN;
+ };
+
+ sample_count=0;
+ pattern_count=0;
+
+ //printf("LOADING HEADER\n");
+ CPLoader::Error err;
+ if ((err=load_header())) {
+ file->close();
+ CP_ERR_COND_V(err,err);
+
+ }
+
+ song->reset(); //file type recognized, reset song!
+
+ set_header();
+
+ //printf("LOADING SAMPLES\n");
+
+ if ((err=load_samples())) {
+ file->close();
+
+ CP_ERR_COND_V(err,err);
+ }
+
+ //printf("LOADING PATTERNS\n");
+
+ if ((err=load_patterns())) {
+
+ file->close();
+ return err;
+ }
+
+ file->close();
+
+ return FILE_OK;
+}
+
+
+
+CPLoader_S3M::CPLoader_S3M(CPFileAccessWrapper *p_file){
+
+ file=p_file;
+
+}
+CPLoader_S3M::~CPLoader_S3M(){
+}
+
diff --git a/modules/chibi/cp_loader_s3m.h b/modules/chibi/cp_loader_s3m.h
new file mode 100644
index 0000000000..175e5e80fe
--- /dev/null
+++ b/modules/chibi/cp_loader_s3m.h
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* cp_loader_s3m.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_LOADER_S3M_H
+#define CP_LOADER_S3M_H
+
+#include "cp_loader.h"
+
+/**
+ *@author Juan Linietsky
+ */
+/******************************
+ loader_s3m.h
+ ----------
+Scream Tracker Module CPLoader!
+It lacks support for
+individual sample loading
+and reorganizing the columns.
+********************************/
+
+
+
+
+class CPLoader_S3M : public CPLoader {
+
+ struct S3M_Header {
+ char songname[28];
+ uint8_t t1a;
+ uint8_t type;
+ uint8_t unused1[2];
+ uint16_t ordnum;
+ uint16_t insnum;
+ uint16_t patnum;
+ uint16_t flags;
+ uint16_t tracker;
+ uint16_t fileformat;
+ char scrm[5];
+ uint8_t mastervol;
+ uint8_t initspeed;
+ uint8_t inittempo;
+ uint8_t mastermult;
+ uint8_t ultraclick;
+ uint8_t pantable;
+ uint8_t unused2[8];
+ uint16_t special;
+ uint8_t channels[32];
+ uint8_t pannings[32];
+ uint8_t orderlist[300];
+ };
+
+
+ int sample_parapointers[CPSong::MAX_SAMPLES];
+ int pattern_parapointers[CPSong::MAX_PATTERNS];
+
+ Error load_header();
+ void set_header();
+ Error load_sample(CPSample *p_sample);
+ Error load_pattern(CPPattern *p_pattern);
+ Error load_patterns();
+
+ Error load_samples();
+
+ S3M_Header header;
+ int sample_count;
+ int pattern_count;
+
+ CPFileAccessWrapper *file;
+ CPSong *song;
+public:
+
+ bool can_load_song() { return true; }
+ bool can_load_sample() { return false; }
+ bool can_load_instrument() { return false; }
+
+ Error load_song(const char *p_file,CPSong *p_song,bool p_sampleset);
+ Error load_sample(const char *p_file,CPSample *p_sample);
+ Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx);
+
+ CPLoader_S3M(CPFileAccessWrapper *p_file);
+ ~CPLoader_S3M();
+};
+
+
+
+#endif
diff --git a/modules/chibi/cp_loader_xm.cpp b/modules/chibi/cp_loader_xm.cpp
new file mode 100644
index 0000000000..bff8615a32
--- /dev/null
+++ b/modules/chibi/cp_loader_xm.cpp
@@ -0,0 +1,752 @@
+/*************************************************************************/
+/* cp_loader_xm.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_loader_xm.h"
+#include "cp_tables.h"
+
+#define ABORT_LOAD { file->close(); return FILE_CORRUPTED; }
+
+
+
+
+CPLoader::Error CPLoader_XM::load_song(const char *p_file,CPSong *p_song,bool p_sampleset) {
+
+ song=p_song;
+
+ if (file->open(p_file,CPFileAccessWrapper::READ)) {
+
+ return FILE_CANNOT_OPEN;
+ };
+
+
+ /**************************************
+ LOAD HEADER
+ ***************************************/
+
+ file->get_byte_array(header.idtext,17);
+ header.idtext[17]=0;
+
+ file->get_byte_array(header.songname,20);
+
+
+
+ header.songname[20]=0;
+ header.hex1a=file->get_byte();
+ if (header.hex1a!=0x1A) { //XM "magic" byte.. this sucks :)
+
+ file->close();
+ return FILE_UNRECOGNIZED;
+
+ }
+
+
+ //magic byte sucks, but can't do much about it..
+
+ song->reset(); //must reset the song
+
+ song->set_name( (const char*)header.songname );
+
+ file->get_byte_array(header.trackername,20);
+ header.trackername[20]=0;
+
+
+ header.version=file->get_word();
+
+ header.headersize=file->get_dword();
+
+ header.songlength=file->get_word();
+
+ header.restart_pos=file->get_word();
+
+ header.channels_used=file->get_word();
+
+ header.patterns_used=file->get_word();
+
+ header.instruments_used=file->get_word();
+
+ song->set_linear_slides( file->get_word() );
+
+ song->set_speed( file->get_word() );
+
+ song->set_tempo( file->get_word() );
+ song->set_instruments( true );
+
+ file->get_byte_array(header.orderlist,256);
+
+ for (int i=0;i<header.songlength;i++) {
+
+ if (i>199)
+ break;
+ song->set_order(i,header.orderlist[i]);
+ }
+
+ /**************************************
+ LOAD PATTERNS
+ ***************************************/
+
+ for (int i=0;i<header.patterns_used;i++) {
+
+ uint32_t aux,rows;
+
+ aux=file->get_dword(); //length
+ aux=file->get_byte(); //packing type
+ rows=aux=file->get_word(); //rows!
+
+ song->get_pattern(i)->set_length( aux );
+
+ aux=file->get_word(); //packed size
+ if (aux==0)
+ continue;
+ //unpaaack!
+ for(int j=0;j<(int)rows;j++)
+ for(int k=0;k<header.channels_used;k++) {
+
+ CPNote aux_note;
+ uint8_t aux_byte;
+ //uint8_t field;
+ aux_byte=file->get_byte(); //packing type
+ if (!(aux_byte&0x80)) {
+
+ aux_note.note=aux_byte;
+ aux_byte=0xFE; //if bit 7 not set, read all of them except the note
+ }
+
+ if (aux_byte&1) aux_note.note=file->get_byte();
+ if (aux_byte&2) aux_note.instrument=file->get_byte();
+ if (aux_byte&4) aux_note.volume=file->get_byte();
+ if (aux_byte&8) aux_note.command=file->get_byte();
+ if (aux_byte&16) aux_note.parameter=file->get_byte();
+
+ if (aux_note.note!=CPNote::EMPTY) {
+
+ if (aux_note.note==97) aux_note.note=CPNote::OFF;
+ else {
+ aux_note.note+=11; //octave minus one (XM C-0 is 1, not zero )
+ }
+ }
+ if (aux_note.instrument!=CPNote::EMPTY) {
+
+ if ((aux_note.instrument>0) && (aux_note.instrument<100))
+ aux_note.instrument--;
+ else
+ aux_note.instrument=CPNote::EMPTY;
+ }
+ if (aux_note.volume!=CPNote::EMPTY) {
+
+ if (aux_note.volume<0x10) {}
+ else if (aux_note.volume<0x50) {
+
+ aux_note.volume-=0x10;
+
+ } else if (aux_note.volume<0x60) {
+ //
+ aux_note.volume=CPNote::EMPTY;
+
+ } else if (aux_note.volume<0x70) {
+ //60 -- volume slide down
+ aux_note.volume-=0x60;
+ if (aux_note.volume>9) aux_note.volume=9;
+ aux_note.volume+=95;
+
+ } else if (aux_note.volume<0x80) {
+ //70 -- volume slide up
+ aux_note.volume-=0x70;
+ if (aux_note.volume>9) aux_note.volume=9;
+ aux_note.volume+=85;
+
+
+ } else if (aux_note.volume<0x90) {
+ //80 -- fine volume slide down
+ aux_note.volume-=0x80;
+ if (aux_note.volume>9) aux_note.volume=9;
+ aux_note.volume+=75;
+
+
+ } else if (aux_note.volume<0xA0) {
+ //9 -- fine volume slide up
+
+ aux_note.volume-=0x90;
+ if (aux_note.volume>9) aux_note.volume=9;
+
+ aux_note.volume+=65;
+
+
+
+ } else if (aux_note.volume<0xB0) {
+ //A -- set vibrato speed
+ aux_note.volume=CPNote::EMPTY;
+
+ } else if (aux_note.volume<0xC0) {
+ //B -- vibrato
+ aux_note.volume-=0xB0;
+ if (aux_note.volume>9) aux_note.volume=9;
+ aux_note.volume+=203;
+
+
+ } else if (aux_note.volume<0xD0) {
+ //C -- set panning
+ int aux=aux_note.volume-=0xC0;
+ aux=aux*65/0xF;
+ aux_note.volume=128+aux;
+
+ } else if (aux_note.volume<0xE0) {
+ aux_note.volume=CPNote::EMPTY;
+
+
+ } else if (aux_note.volume<0xF0) {
+ aux_note.volume=CPNote::EMPTY;
+
+
+ } else {
+ //F -- tone porta
+ aux_note.volume-=0xF0;
+ aux_note.volume*=9;
+ aux_note.volume/=0xF;
+ aux_note.volume+=193;
+ }
+ }
+ if (aux_note.command!=CPNote::EMPTY) {
+
+ switch(aux_note.command) {
+
+ case 0x0:
+ aux_note.command='J'-'A';
+ break;
+ case 0x1:
+ aux_note.command='F'-'A';
+ break;
+ case 0x2:
+ aux_note.command='E'-'A';
+ break;
+ case 0x3:
+ aux_note.command='G'-'A';
+ break;
+ case 0x4:
+ aux_note.command='H'-'A';
+ break;
+ case 0x5:
+ aux_note.command='L'-'A';
+ break;
+ case 0x6:
+ aux_note.command='K'-'A';
+ break;
+ case 0x7:
+ aux_note.command='R'-'A';
+ break;
+ case 0x8:
+ aux_note.command='X'-'A';
+ break;
+ case 0x9:
+ aux_note.command='O'-'A';
+ break;
+ case 0xa:
+ aux_note.command='D'-'A';
+ break;
+ case 0xb:
+ aux_note.command='B'-'A';
+ break;
+ case 0xc:
+ //printf("XM Import: Warning! effect C (set volume) not implemented!\n");
+ break;
+ case 0xd:
+ aux_note.command='C'-'A';
+ break;
+
+ case 0xe: /* Extended effects */
+
+ aux_note.command='S'-'A';
+ switch(aux_note.parameter>>4) {
+ case 0x1: /* XM fine porta up */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='F'-'A';
+ aux_note.parameter=0xF0|(aux_note.parameter&0xF);
+ break;
+ case 0x2: /* XM fine porta down */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='E'-'A';
+ aux_note.parameter=0xF0|(aux_note.parameter&0xF);
+ break;
+ case 0xa: /* XM fine volume up */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='D'-'A';
+ aux_note.parameter=0x0F|((aux_note.parameter&0xF)<<4);
+
+ break;
+ case 0xb: /* XM fine volume down */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='D'-'A';
+ aux_note.parameter=0xF0|(aux_note.parameter&0xF);
+
+ break;
+ case 0x9: /* XM fine volume down */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='Q'-'A';
+ aux_note.parameter=0x00|(aux_note.parameter&0xF);
+ break;
+
+ case 0xc: //notecut
+
+ aux_note.parameter=0xC0|(aux_note.parameter&0xF);
+ break;
+
+ case 0xd: //notedelay
+
+ aux_note.parameter=0xD0|(aux_note.parameter&0xF);
+ break;
+
+ case 0xe: //patterndelay
+
+ aux_note.parameter=0xE0|(aux_note.parameter&0xF);
+ break;
+ }
+
+ break;
+ case 0xf:
+ if (aux_note.parameter<32) {
+ aux_note.command='A'-'A';
+ } else {
+ aux_note.command='T'-'A';
+ }
+ break;
+ case 'G'-55:
+ aux_note.command='V'-'A';
+ break;
+ case 'H'-55:
+ aux_note.command='W'-'A';
+ break;
+ case 'K'-55:
+ if (aux_note.note!=CPNote::EMPTY) break;
+ aux_note.note=CPNote::OFF;
+ break;
+ case 'P'-55:
+ aux_note.command='P'-'A';
+ break;
+ case 'R'-55:
+ aux_note.command='Q'-'A';
+ break;
+ case 'T'-55:
+ aux_note.command='I'-'A';
+ break;
+ default: {
+
+ aux_note.command=CPNote::EMPTY;
+ }
+ }
+
+
+ }
+
+ song->get_pattern( i)->set_note( k,j,aux_note );
+ }
+ }
+
+ /**************************************
+ LOAD INSTRUMENTS!
+ ***************************************/
+
+ for (int i=0;i<header.instruments_used;i++) {
+
+
+ uint32_t aux;
+ int sampnum;
+
+ CPInstrument &instrument=*song->get_instrument(i);
+ uint32_t cpos=file->get_pos();
+ //printf("pos is %i\n",cpos);
+
+
+
+/* +4 */ uint32_t hsize=file->get_dword(); //header length
+
+ char instrname[23];
+ instrname[22]=0;
+
+ file->get_byte_array((uint8_t*)instrname,22);
+//XM_LOAD_DEBUG printf("name is %s\n",instrname);
+
+/* +27 */ aux=file->get_byte(); //byte that must be ignored
+//XM_LOAD_DEBUG printf("header size is %i\n",hsize);
+
+/* +29 */ sampnum=file->get_word();
+
+//XM_LOAD_DEBUG printf("samples %i\n",sampnum);
+
+
+ instrument.set_name( instrname );
+// printf("Header Len: %i, CPInstrument %i, %i samples , name: s,\n",hsize,i,sampnum,instrname);
+
+ if (sampnum==0) {
+ //aux=file->get_dword(); //Why is this for? -- for nothing, skipped
+ if (hsize) {
+
+ file->seek( cpos+hsize ); //skip header if size has been specified
+ }
+ continue;
+ }
+
+/* +33 */ file->get_dword();
+
+ if (Error result=load_instrument_internal(&instrument,false,cpos,hsize,sampnum)) {
+
+ CP_PRINTERR("Error loading instrument");
+ file->close();
+ return result;
+ }
+
+ }
+//
+ file->close();
+ return FILE_OK;
+}
+
+CPLoader::Error CPLoader_XM::load_instrument_internal(CPInstrument *p_instr,bool p_xi,int p_cpos, int p_hsize, int p_sampnum) {
+
+ int sampnum;
+ uint32_t aux;
+ uint8_t notenumb[96];
+ uint16_t panenv[24],volenv[24];
+ int volpoints,panpoints;
+ int vol_loop_begin,vol_loop_end,vol_sustain_loop;
+ int pan_loop_begin,pan_loop_end,pan_sustain_loop;
+ char instrname[23];
+ int sample_index[16]={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; //-1 means no index!
+
+ instrname[22]=0;
+
+
+/* +129 */ file->get_byte_array((uint8_t*)notenumb,96);
+ for (int j=0;j<24;j++) {
+ volenv[j]=file->get_word();
+ }
+ for (int j=0;j<24;j++) {
+ panenv[j]=file->get_word();
+ }
+
+/* +177 */
+/* +225 */
+/* +226 */ volpoints=file->get_byte();
+/* +227 */ panpoints=file->get_byte();
+/* +230 */ vol_sustain_loop=file->get_byte();
+/* +228 */ vol_loop_begin=file->get_byte();
+/* +229 */ vol_loop_end=file->get_byte();
+
+//XM_LOAD_DEBUG printf("1- volpoints: %i, panpoints: %i, susloop: %i, loop begin: %i, loop end %i\n",volpoints,panpoints,vol_sustain_loop,vol_loop_begin,vol_loop_end);
+ pan_sustain_loop=file->get_byte();
+/* +231 */ pan_loop_begin=file->get_byte();
+/* +232 */ pan_loop_end=file->get_byte();
+
+
+
+/* +234 */ aux=file->get_byte();
+ p_instr->get_volume_envelope()->reset();
+ p_instr->get_volume_envelope()->set_enabled(aux&1);
+ p_instr->get_volume_envelope()->set_sustain_loop_enabled((aux&2)?true:false);
+ p_instr->get_volume_envelope()->set_loop_enabled((aux&4)?true:false);
+/* +235 */ aux=file->get_byte();
+ p_instr->get_pan_envelope()->reset();
+ p_instr->get_pan_envelope()->set_enabled(aux&1);
+ p_instr->get_pan_envelope()->set_sustain_loop_enabled((aux&2)?true:false);
+ p_instr->get_pan_envelope()->set_loop_enabled((aux&4)?true:false);
+
+/* +239 */ aux=file->get_dword(); // sadly, cant use those
+/* +241 */ p_instr->set_volume_fadeout( file->get_word() >> 4 );
+/* +243 */ aux=file->get_word(); // reserved!
+
+
+
+ for (int j=0;j<volpoints;j++) {
+ int ofs=volenv[j*2];
+ int val=volenv[j*2+1];
+ p_instr->get_volume_envelope()->add_position(ofs,val);
+
+ }
+
+ //make sure minimum is 2
+ while (p_instr->get_volume_envelope()->get_node_count()<2) {
+
+ p_instr->get_volume_envelope()->add_position( p_instr->get_volume_envelope()->get_node_count()*20,64 );
+ }
+
+ for (int j=0;j<panpoints;j++) {
+ int ofs=panenv[j*2];
+ int val=panenv[j*2+1];
+ p_instr->get_pan_envelope()->add_position(ofs,val-32);
+ }
+
+ //make sure minimum is 2
+ while (p_instr->get_pan_envelope()->get_node_count()<2) {
+
+ p_instr->get_pan_envelope()->add_position( p_instr->get_pan_envelope()->get_node_count()*20,0 );
+ }
+
+
+ p_instr->get_volume_envelope()->set_loop_begin(vol_loop_begin);
+ p_instr->get_volume_envelope()->set_loop_end(vol_loop_end);
+ p_instr->get_volume_envelope()->set_sustain_loop_end(vol_sustain_loop);
+ p_instr->get_volume_envelope()->set_sustain_loop_begin(vol_sustain_loop);
+ p_instr->get_pan_envelope()->set_loop_begin(pan_loop_begin);
+ p_instr->get_pan_envelope()->set_loop_end(pan_loop_end);
+ p_instr->get_pan_envelope()->set_sustain_loop_end(pan_sustain_loop);
+ p_instr->get_pan_envelope()->set_sustain_loop_begin(pan_sustain_loop);
+
+
+ if (!p_xi) {
+
+ if ((file->get_pos()-p_cpos)<p_hsize) {
+
+ uint8_t junkbuster[500];
+
+ //printf("extra junk XM instrument in header! hsize is %i, extra junk: %i\n",p_hsize,(file->get_pos()-p_cpos));
+ //printf("extra: %i\n",p_hsize-(file->get_pos()-p_cpos));
+ file->get_byte_array((uint8_t*)junkbuster,p_hsize-(file->get_pos()-p_cpos));
+ }
+
+ sampnum=p_sampnum;
+ } else {
+
+ uint8_t junkbuster[500];
+ file->get_byte_array((uint8_t*)junkbuster,20); //14 bytes?
+
+ sampnum=file->get_word();
+
+ }
+
+
+ CPSampleManager *sm=CPSampleManager::get_singleton();
+
+ /*SAMPLE!!*/
+
+ for (int j=0;j<sampnum;j++) {
+
+ if (j>16) ABORT_LOAD;
+
+
+ int s_idx=-1;
+ for (int s=0;s<CPSong::MAX_SAMPLES;s++) {
+
+ if (song->get_sample(s)->get_sample_data().is_null()) {
+ //empty sample!
+ s_idx=s;
+ break;
+ }
+ }
+
+ if (s_idx==-1) ABORT_LOAD;
+ //printf("free sample: %i\n",s_idx);
+
+
+ CPSample& sample=*song->get_sample(s_idx);
+
+ int sample_size=file->get_dword();
+ int tmp_loop_begin=file->get_dword();
+
+ int tmp_loop_end=file->get_dword();
+
+ sample.set_default_volume(file->get_byte());
+
+ uint8_t ftb=file->get_byte();
+ int8_t *fts=(int8_t*)&ftb;
+ int finetune=*fts;
+ uint32_t flags=file->get_byte();
+
+ if (flags&16) { // is 16 bits.. at flag 16.. fun :)
+
+ tmp_loop_end/=2;
+ tmp_loop_begin/=2;
+ sample_size/=2;
+ }
+
+
+ CPSample_ID sample_data=sm->create( flags&16, false, sample_size );
+
+ sample.set_sample_data(sample_data);
+ sm->set_loop_begin(sample_data,tmp_loop_begin);
+ sm->set_loop_end(sample_data,tmp_loop_end+tmp_loop_begin);
+
+ sm->set_loop_type( sample_data, (flags&3)?( (flags&2) ? CP_LOOP_BIDI : CP_LOOP_FORWARD ):CP_LOOP_NONE );
+
+
+
+ sample.set_pan_enabled(true);
+ sample.set_pan(file->get_byte()*64/255);
+ uint8_t noteb=file->get_byte();
+ int8_t *notes=(int8_t*)&noteb;
+ int note_offset=*notes;
+ note_offset+=48;
+ //note_offset+=60;
+
+
+
+ //int linear_period=10*12*16*4 - (note_offset)*16*4 - finetune/2;
+ //int freq=(int)(8363*pow(2.0,(double)(6*12*16*4 - linear_period) / (double)(12*16*4)));
+
+ //sm->set_c5_freq( sample_data, freq);
+ sm->set_c5_freq( sample_data, CPTables::get_linear_frequency(CPTables::get_linear_period(note_offset<<1,finetune)) );
+ //printf("NOTE %i,fine %i\n",note_offset,finetune);
+
+ char auxb;
+ auxb=file->get_byte(); //reserved?
+ file->get_byte_array((uint8_t*)instrname,22);
+ sample.set_name(instrname);
+
+ sample_index[j]=s_idx;
+ }
+
+ /*SAMPLE __DATA__!!*/
+
+ for (int j=0;j<sampnum;j++) {
+
+ if (sample_index[j]==-1) continue;
+
+ CPSample *sample=song->get_sample(sample_index[j]);
+ CPSample_ID sid=sample->get_sample_data();
+
+ sm->lock_data(sid);
+
+ void*dataptr=sm->get_data(sid);
+
+ if (sm->is_16bits( sid)) {
+
+ int16_t old=0;
+
+
+ for (int k=0;k<sm->get_size(sid);k++) {
+
+ int16_t newsample;
+ int16_t sampleval=file->get_word();
+ newsample=sampleval+old;
+ old=newsample;
+
+ ((int16_t*)dataptr)[k]=newsample;
+ //sm->set_data( sid, k, newsample );
+ }
+ } else {
+
+ int8_t old=0;
+
+
+ for (int k=0;k<sm->get_size(sid);k++) {
+
+ int8_t newsample;
+ int8_t sampleval=file->get_byte();
+ newsample=sampleval+old;
+ old=newsample;
+
+ ((int8_t*)dataptr)[k]=newsample;
+
+ //sm->set_data( sid, k, (int16_t)newsample << 8 );
+
+ }
+ }
+
+ sm->unlock_data(sid);
+
+ }
+
+ for (int j=0;j<96;j++) {
+
+ int val=notenumb[j];
+ if ((val<0) || (val>15)) continue;
+ else val=sample_index[val];
+ if (val==-1) continue;
+ p_instr->set_sample_number( 12+j,val );
+ }
+
+
+ return FILE_OK;
+}
+
+
+
+CPLoader::Error CPLoader_XM::load_sample(const char *p_file,CPSample *p_sample) {
+
+ return FILE_UNRECOGNIZED;
+}
+
+
+/* Compute CPInstrument Info */
+CPLoader::Error CPLoader_XM::load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx) {
+
+ if ( file->open(p_file,CPFileAccessWrapper::READ) ) return FILE_CANNOT_OPEN;
+ //int i;
+ song=p_song;
+ CPInstrument& instr=*p_song->get_instrument( p_instr_idx );
+ int aux;
+
+
+ char buffer[500];
+ file->get_byte_array((uint8_t*)buffer,0x15);
+ buffer[8]=0;
+ if ( buffer[0]!='E' ||
+ buffer[1]!='x' ||
+ buffer[2]!='t' ||
+ buffer[3]!='e' ||
+ buffer[4]!='n' ||
+ buffer[5]!='d' ||
+ buffer[6]!='e' ||
+ buffer[7]!='d') {
+ file->close();
+ return FILE_UNRECOGNIZED;
+ }
+
+ file->get_byte_array((uint8_t*)buffer,0x16);
+ buffer[0x16]=0;
+ instr.set_name(buffer);
+ aux=file->get_byte(); //says ignore ti
+ /*if(aux!=0x1a) { I'm not sure. this is supposed to be ignored...
+
+ file->close();
+ return FILE_UNRECOGNIZED;
+ } */
+
+ file->get_byte_array((uint8_t*)buffer,0x14); //somethingaboutthename
+ aux=file->get_word(); //version or blahblah
+
+ if (load_instrument_internal(&instr,true,0,0)) {
+
+ file->close();
+ return FILE_CORRUPTED;
+ }
+
+ file->close(); //ook, we got it..
+
+
+ return FILE_OK;
+
+}
+
+
+
+CPLoader_XM::CPLoader_XM(CPFileAccessWrapper *p_file){
+
+ file=p_file;
+}
+CPLoader_XM::~CPLoader_XM(){
+}
+
diff --git a/modules/chibi/cp_loader_xm.h b/modules/chibi/cp_loader_xm.h
new file mode 100644
index 0000000000..9ae480cc8f
--- /dev/null
+++ b/modules/chibi/cp_loader_xm.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* cp_loader_xm.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_LOADER_XM_H
+#define CP_LOADER_XM_H
+
+#include "cp_loader.h"
+
+
+/**
+ *@author red
+ */
+
+
+
+
+class CPLoader_XM : public CPLoader {
+
+
+ struct XM_Header {
+
+ uint8_t idtext[18];
+ uint8_t songname[21];
+ uint8_t hex1a; // ?
+ uint8_t trackername[21];
+ uint16_t version;
+ uint32_t headersize; //from here
+
+ uint16_t songlength; //pattern ordertable
+ uint16_t restart_pos;
+ uint16_t channels_used;
+ uint16_t patterns_used;
+ uint16_t instruments_used;
+ uint16_t use_linear_freq;
+ uint16_t tempo;
+ uint16_t speed;
+ uint8_t orderlist[256];
+
+ } header;
+
+ CPFileAccessWrapper *file;
+
+ Error load_instrument_internal(CPInstrument *pint,bool p_xi,int p_cpos, int p_hsize, int p_sampnumb=-1);
+ CPSong *song;
+
+public:
+
+ bool can_load_song() { return true; }
+ bool can_load_sample() { return false; }
+ bool can_load_instrument() { return true; }
+
+ Error load_song(const char *p_file,CPSong *p_song,bool p_sampleset);
+ Error load_sample(const char *p_file,CPSample *p_sample);
+ Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx);
+
+
+ CPLoader_XM(CPFileAccessWrapper *p_file);
+ ~CPLoader_XM();
+};
+
+
+
+#endif
diff --git a/modules/chibi/cp_mixer.h b/modules/chibi/cp_mixer.h
new file mode 100644
index 0000000000..7ad22ac146
--- /dev/null
+++ b/modules/chibi/cp_mixer.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* cp_mixer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_MIXER_H
+#define CP_MIXER_H
+
+#include "cp_sample_defs.h"
+
+/**Abstract base class representing a mixer
+ *@author Juan Linietsky
+ */
+
+
+/******************************
+ mixer.h
+ ----------
+
+Abstract base class for the mixer.
+This is what the player uses to setup
+voices and stuff.. this way
+it can be abstracted to hardware
+devices or other stuff..
+********************************/
+
+class CPSample_ID; /* need this */
+
+class CPMixer {
+public:
+
+ enum {
+
+ FREQUENCY_BITS=8
+
+ };
+
+ enum ReverbMode {
+ REVERB_MODE_ROOM,
+ REVERB_MODE_STUDIO_SMALL,
+ REVERB_MODE_STUDIO_MEDIUM,
+ REVERB_MODE_STUDIO_LARGE,
+ REVERB_MODE_HALL,
+ REVERB_MODE_SPACE_ECHO,
+ REVERB_MODE_ECHO,
+ REVERB_MODE_DELAY,
+ REVERB_MODE_HALF_ECHO
+ };
+
+ /* Callback */
+
+ virtual void set_callback_interval(int p_interval_us)=0; //in usecs, for tracker it's 2500000/tempo
+ virtual void set_callback(void (*p_callback)(void*),void *p_userdata)=0;
+
+ /* Voice Control */
+
+ virtual void setup_voice(int p_voice_index,CPSample_ID p_sample_id,int32_t p_start_index) =0;
+ virtual void stop_voice(int p_voice_index) =0;
+ virtual void set_voice_frequency(int p_voice_index,int32_t p_freq) =0; //in freq*FREQUENCY_BITS
+ virtual void set_voice_panning(int p_voice_index,int p_pan) =0;
+ virtual void set_voice_volume(int p_voice_index,int p_vol) =0;
+ virtual void set_voice_filter(int p_filter,bool p_enabled,uint8_t p_cutoff, uint8_t p_resonance )=0;
+ virtual void set_voice_reverb_send(int p_voice_index,int p_reverb)=0;
+ virtual void set_voice_chorus_send(int p_voice_index,int p_chorus)=0; /* 0 - 255 */
+
+ virtual void set_reverb_mode(ReverbMode p_mode)=0;
+ virtual void set_chorus_params(unsigned int p_delay_ms,unsigned int p_separation_ms,unsigned int p_depth_ms10,unsigned int p_speed_hz10)=0;
+
+
+ /* Info retrieving */
+
+ virtual int32_t get_voice_sample_pos_index(int p_voice_index) =0;
+ virtual int get_voice_panning(int p_voice_index) =0;
+ virtual int get_voice_volume(int p_voice_index) =0;
+ virtual CPSample_ID get_voice_sample_id(int p_voice_index) =0;
+ virtual bool is_voice_active(int p_voice_index) =0;
+ virtual int get_active_voice_count()=0;
+ virtual int get_total_voice_count()=0;
+
+
+ virtual uint32_t get_mix_frequency()=0; //if mixer is not software, return 0
+
+ /* Methods below only work with software mixers, meant for software-based sound drivers, hardware mixers ignore them */
+ virtual int32_t process(int32_t p_frames)=0; /* Call this to process N frames, returns how much it was processed */
+ virtual int32_t *get_mixdown_buffer_ptr()=0; /* retrieve what was mixed */
+ virtual void set_mix_frequency(int32_t p_mix_frequency)=0;
+
+ virtual ~CPMixer() {}
+};
+
+#endif
diff --git a/modules/chibi/cp_note.h b/modules/chibi/cp_note.h
new file mode 100644
index 0000000000..5cfa3f11ec
--- /dev/null
+++ b/modules/chibi/cp_note.h
@@ -0,0 +1,102 @@
+/*************************************************************************/
+/* cp_note.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_NOTE_H
+#define CP_NOTE_H
+
+#include "cp_config.h"
+
+struct CPNote {
+
+ enum {
+
+ NOTES=120,
+ OFF=254,
+ CUT=253,
+ EMPTY=255,
+ SCRIPT=252,
+ };
+
+
+ uint8_t note;
+ uint8_t instrument;
+ uint8_t volume;
+ uint8_t command;
+ uint8_t parameter;
+ unsigned int script_source_sign;
+ bool cloned;
+
+ void clear() {
+
+ note=EMPTY;
+ instrument=EMPTY;
+ volume=EMPTY;
+ command=EMPTY;
+ parameter=0;
+ script_source_sign='\0';
+ cloned=false;
+ }
+
+ void raise() {
+
+ if (note<(NOTES-1))
+ note++;
+ else if (note==SCRIPT && parameter<0xFF)
+ parameter++;
+ }
+
+ void lower() {
+
+ if ((note>0) && (note<NOTES))
+ note--;
+ else if (note==SCRIPT && parameter>0)
+ parameter--;
+
+ }
+
+ bool operator== (const CPNote &rvalue) {
+
+ return (
+ (note==rvalue.note) &&
+ (instrument==rvalue.instrument) &&
+ (volume==rvalue.volume) &&
+ (command==rvalue.command) &&
+ (parameter==rvalue.parameter)
+ );
+ }
+
+ bool is_empty() const { return (note==EMPTY && instrument==EMPTY && volume==EMPTY && command==EMPTY && parameter==0 && !cloned); }
+ CPNote() {
+
+ clear();
+ }
+};
+
+
+#endif
+
diff --git a/modules/chibi/cp_order.h b/modules/chibi/cp_order.h
new file mode 100644
index 0000000000..03ecc00bba
--- /dev/null
+++ b/modules/chibi/cp_order.h
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* cp_order.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_ORDER_H
+#define CP_ORDER_H
+
+
+#include "cp_config.h"
+
+enum CPOrderType {
+ CP_ORDER_NONE=255,
+ CP_ORDER_BREAK=254
+};
+
+typedef uint8_t CPOrder;
+
+#endif
+
diff --git a/modules/chibi/cp_pattern.cpp b/modules/chibi/cp_pattern.cpp
new file mode 100644
index 0000000000..83e165bf87
--- /dev/null
+++ b/modules/chibi/cp_pattern.cpp
@@ -0,0 +1,574 @@
+/*************************************************************************/
+/* cp_pattern.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_pattern.h"
+
+void CPPattern::clear() {
+
+ if (event_count>0) {
+
+
+ CP_FREE(events);
+ events=NULL;
+ event_count=0;
+ }
+
+ length=DEFAULT_LEN;
+
+}
+
+
+bool CPPattern::resize_event_list_to(uint32_t p_events) {
+
+
+ //Module is slow in some cpus, so this should be fast enough
+ uint32_t new_size=((p_events-1)&(~((1<<RESIZE_EVERY_BITS)-1)))+(1<<RESIZE_EVERY_BITS);
+
+ CP_ERR_COND_V(new_size<p_events,true); //bugARM_INFO
+
+ if (event_count==0 && new_size==0)
+ return false; //nothing to do
+
+ if (event_count==0) {
+
+ events=(Event*)CP_ALLOC( new_size*sizeof(Event) );
+
+ } else if (new_size==0) {
+
+ CP_FREE(events);
+ events=NULL;
+ } else {
+
+ CP_ERR_COND_V(events==NULL,true);
+ events=(Event*)CP_REALLOC(events, new_size*sizeof(Event));
+
+ }
+
+ event_count=p_events;
+
+ return false;
+}
+
+
+int32_t CPPattern::get_event_pos(uint16_t p_target_pos) {
+
+
+ if (event_count==0)
+ return -1;
+
+ int low = 0;
+ int high = event_count -1;
+ int middle;
+
+ while( low <= high )
+ {
+ middle = ( low + high ) / 2;
+
+ if( p_target_pos == events[middle].pos ) { //match
+ break;
+ } else if( p_target_pos < events[middle].pos )
+ high = middle - 1; //search low end of array
+ else
+ low = middle + 1; //search high end of array
+ }
+
+ /* adapt so we are behind 2 */
+
+ if (events[middle].pos<p_target_pos)
+ middle++;
+ return middle;
+
+ /* Linear search for now */
+
+ /*
+ int32_t pos_idx=0;
+
+ for (;pos_idx<event_count;pos_idx++) {
+
+ if (event_list[pos_idx].pos>=p_target_pos)
+ break;
+
+ } */
+
+ //return pos_idx;
+}
+
+bool CPPattern::erase_event_at_pos(uint16_t p_pos) {
+
+ if (event_count==0)
+ return false;
+
+
+
+ Event *event_list=events;
+
+ int32_t pos_idx = get_event_pos(p_pos);
+ if (pos_idx==-1) {
+ CP_ERR_COND_V(pos_idx==-1,true);
+ }
+
+ if (pos_idx==event_count || event_list[pos_idx].pos!=p_pos) {
+ /* Nothing to Erase */
+ return false;
+ }
+
+ for (int32_t i=pos_idx;i<(event_count-1);i++) {
+
+ event_list[i]=event_list[i+1];
+ }
+
+
+ resize_event_list_to(event_count-1);
+
+ return false;
+}
+
+bool CPPattern::set_note(uint8_t p_column, uint16_t p_row,const CPNote& p_note) {
+
+ CP_ERR_COND_V(p_column>=WIDTH,true);
+ CP_ERR_COND_V(p_row>=length,true);
+
+ int32_t new_pos;
+ uint16_t target_pos=p_row*WIDTH+p_column;
+
+
+
+ if (p_note.is_empty()) {
+ bool res=erase_event_at_pos(target_pos);
+
+ return res;;
+ }
+
+ Event *event_list=0;
+
+ if (event_count==0) {
+ /* If no events, create the first */
+
+ if (resize_event_list_to(1)) {
+
+ CP_PRINTERR("Can't resize event list to 1");
+ return true;
+ }
+
+ event_list=events;
+ if (event_list==0) {
+
+
+ CP_PRINTERR("Can't get event list");
+ return true;
+ }
+
+ new_pos=0;
+
+ } else {
+ /* Prepare to add */
+
+ event_list=events;
+ if (event_list==0) {
+
+
+ CP_PRINTERR("Can't get event list");
+ return true;
+ }
+
+ int32_t pos_idx = get_event_pos(target_pos);
+
+ if (pos_idx==-1) {
+
+
+ CP_PRINTERR("Can't find add position");
+ return true;
+ }
+
+
+ if (pos_idx==event_count || event_list[pos_idx].pos!=target_pos) {
+ /* If the note being modified didnt exist, then we add it */
+
+ //resize, and return if out of mem
+ if (resize_event_list_to( event_count+1)) {
+
+
+ CP_PRINTERR("Can't resize event list");
+ return true;
+ }
+ event_list=events;
+ if (event_list==0) {
+
+
+ CP_PRINTERR("Can't get event list");
+ return true;
+ }
+
+ //make room for new pos, this wont do a thing if pos_idx was ==event_count
+ for(int32_t i=(event_count-1);i>pos_idx;i--) {
+ event_list[i]=event_list[i-1];
+
+ }
+
+ } /* Else it means that position is taken, so we just modify it! */
+
+
+ new_pos=pos_idx;
+ }
+
+ event_list[new_pos].pos=target_pos;
+ event_list[new_pos].note=p_note.note;
+ event_list[new_pos].instrument=p_note.instrument;
+ event_list[new_pos].volume=p_note.volume;
+ event_list[new_pos].command=p_note.command;
+ event_list[new_pos].parameter=p_note.parameter;
+ event_list[new_pos].script_source_sign=p_note.script_source_sign;
+ event_list[new_pos].cloned=p_note.cloned;
+
+
+
+
+ return false;
+
+}
+CPNote CPPattern::get_note(uint8_t p_column,uint16_t p_row) {
+
+ if (p_column==CPNote::EMPTY) return CPNote();
+
+ CP_ERR_COND_V(p_column>=WIDTH,CPNote());
+ CP_ERR_COND_V(p_row>=length,CPNote());
+
+ if (event_count==0)
+ return CPNote();
+
+
+ Event *event_list=events;
+
+ CP_ERR_COND_V(event_list==0,CPNote());
+
+ uint16_t target_pos=p_row*WIDTH+p_column;
+ int32_t pos_idx = get_event_pos(target_pos);
+ if (pos_idx==-1) {
+
+ CP_PRINTERR("Can't find event pos");
+ return CPNote();
+ }
+
+ if (pos_idx>=event_count || event_list[pos_idx].pos!=target_pos) {
+ /* no note found */
+
+ return CPNote();
+ }
+
+ CPNote n;
+ n.note=event_list[pos_idx].note;
+ n.instrument=event_list[pos_idx].instrument;
+ n.volume=event_list[pos_idx].volume;
+ n.command=event_list[pos_idx].command;
+ n.parameter=event_list[pos_idx].parameter;
+ n.script_source_sign=event_list[pos_idx].script_source_sign;
+ n.cloned=event_list[pos_idx].cloned;
+
+
+ return n;
+
+}
+
+CPNote CPPattern::get_transformed_script_note(uint8_t p_column,uint16_t p_row ) {
+
+ CPNote n = get_note( p_column, p_row );
+
+ // get source channel and note
+
+ int channel = get_scripted_note_target_channel( p_column, p_row );
+ CPNote src_n = get_note( channel, 0 );
+
+ if ( src_n.note == CPNote::SCRIPT ) return CPNote();
+
+ script_transform_note( src_n, n );
+
+ return src_n;
+
+}
+
+int CPPattern::get_scripted_note_target_channel(uint8_t p_column, uint16_t p_row) {
+
+ CPNote n = get_note( p_column, p_row );
+
+ if ( n.note != CPNote::SCRIPT ) return CPNote::EMPTY;
+
+ int channel = n.instrument;
+
+ if ( n.script_source_sign == '\0' ) {
+
+ if ( channel < 0 || channel >= CPPattern::WIDTH ) return CPNote::EMPTY;
+
+ } else {
+
+ channel = p_column + ( ( n.script_source_sign=='+') ? 1 : -1 ) * (channel+1);
+ if ( channel < 0 || channel >= CPPattern::WIDTH ) return CPNote::EMPTY;
+
+ }
+
+ return channel;
+}
+
+void CPPattern::scripted_clone(uint8_t p_column, uint16_t p_row) {
+
+ int channel = get_scripted_note_target_channel( p_column, p_row );
+ int src_row = 1;
+ CPNote script_n = get_note( p_column, p_row );
+
+ for ( int row = p_row+1; row < length; ++row ) {
+
+ CPNote src_n = get_note( channel, src_row );
+ CPNote target_n = get_note( p_column, row );
+
+ if ( target_n.note != CPNote::SCRIPT ) {
+ if ( src_n.note == CPNote::SCRIPT ) {
+ src_n = CPNote();
+ channel = CPNote::EMPTY;
+ }
+
+ script_transform_note( src_n, script_n );
+
+ src_n.cloned = true;
+ set_note( p_column, row, src_n );
+
+ } else {
+
+ return;
+
+ }
+
+ src_row++;
+ }
+
+}
+
+void CPPattern::scripted_clone_remove(uint8_t p_column, uint16_t p_row) {
+
+ if ( get_note( p_column, p_row ).cloned )
+ set_note( p_column, p_row, CPNote() );
+
+ for ( int row = p_row+1; row < length; ++row ) {
+
+ CPNote target_n = get_note( p_column, row );
+
+ if ( target_n.note != CPNote::SCRIPT ) {
+
+ set_note( p_column, row, CPNote() );
+
+ } else {
+
+ return;
+
+ }
+
+ }
+
+}
+
+void CPPattern::script_transform_note(CPNote& n, const CPNote& p_note) {
+
+ // set instrument
+
+ if ( n.note < CPNote::NOTES && p_note.volume != CPNote::EMPTY ) {
+
+ n.instrument = p_note.volume;
+
+ }
+
+ // transpose
+
+ if ( n.note < CPNote::NOTES && p_note.command != CPNote::EMPTY ) {
+
+ int transpose = ( p_note.parameter & 0xF ) + ( p_note.parameter / 0x10 ) * 12;
+
+ if ( p_note.command == '^' ) {
+
+ if ( n.note >= CPNote::NOTES-transpose )
+ n.note = CPNote::NOTES-1;
+ else
+ n.note += transpose;
+
+ } else if ( p_note.command == 'v' ) {
+
+ if ( n.note <= transpose )
+ n.note = 0;
+ else
+ n.note -= transpose;
+
+ }
+ }
+
+}
+
+bool CPPattern::update_scripted_clones_sourcing_channel( int channel ) {
+
+ bool updated = false;
+
+ for ( int x = 0; x < WIDTH; ++x ) {
+
+ for (int y = 0; y < length; ++y ) {
+
+ if ( channel == get_scripted_note_target_channel( x, y ) ) {
+
+ scripted_clone( x, y );
+ updated = true;
+ }
+
+ }
+
+ }
+
+ return updated;
+}
+
+void CPPattern::set_length(uint16_t p_rows) {
+
+
+
+ if (event_count==0) {
+
+ if (p_rows>=MIN_ROWS)
+ length=p_rows;
+
+
+ return;
+
+ }
+
+ if (p_rows<MIN_ROWS) {
+
+ return;
+ }
+
+ if (p_rows<length) {
+
+ Event* event_list=events;
+ if (event_list==0) {
+
+ CP_PRINTERR("get_event_list() Failed");
+ return;
+ }
+
+
+ uint16_t target_pos=p_rows*WIDTH;
+ int32_t pos_idx = get_event_pos(target_pos);
+
+
+ if (pos_idx==-1) {
+
+ CP_ERR_COND(pos_idx==-1);
+ }
+
+ if (resize_event_list_to(pos_idx)) {
+
+ CP_PRINTERR("resize_event_list_to(pos_idx) Failed");
+ return;
+ }
+
+ }
+
+ length=p_rows;
+
+
+}
+#if 0
+void CPPattern::copy_to(CPPattern *p_pattern) const {
+
+
+
+
+ p_pattern->clear();
+ p_pattern->length=length;
+
+
+ if (!event_count)
+ return;
+
+
+
+ int bufsiz=MemPool_Wrapper::get_singleton()->get_mem_size( mem_handle );
+ MemPool_Handle aux_mem_handle=MemPool_Wrapper::get_singleton()->alloc_mem( bufsiz );
+
+ if (aux_mem_handle.is_null()) {
+
+ CP_PRINTERR("own handle is null");
+
+ return;
+ }
+
+
+ if (MemPool_Wrapper::get_singleton()->lock_mem(aux_mem_handle)) {
+ CP_PRINTERR("Unable to lock aux new handle");
+
+ return;
+
+ }
+
+ if (MemPool_Wrapper::get_singleton()->lock_mem(mem_handle)) {
+
+ CP_PRINTERR("Unable to lock own handle");
+
+ return;
+ }
+
+ uint8_t* srcuint8_tt8_t*)MemPool_Wrapper::get_singleton()->get_mem(mem_handle);
+ uint8_t* dstuint8_tt8_t*)MemPool_Wrapper::get_singleton()->get_mem(aux_mem_handle);
+
+ for (int i=0;i<bufsiz;i++)
+ dst[i]=src[i];
+
+ MemPool_Wrapper::get_singleton()->unlock_mem(mem_handle);
+ MemPool_Wrapper::get_singleton()->unlock_mem(aux_mem_handle);
+
+ p_pattern->mem_handle=aux_mem_handle;
+ p_pattern->event_count=event_count;
+
+
+}
+#endif
+uint16_t CPPattern::get_length() {
+
+
+ return length;
+}
+CPPattern::CPPattern() {
+
+
+ length=DEFAULT_LEN;
+ event_count=0;
+ clear();
+
+}
+bool CPPattern::is_empty() {
+
+ return events==NULL;
+}
+
+CPPattern::~CPPattern() {
+
+ clear();
+}
diff --git a/modules/chibi/cp_pattern.h b/modules/chibi/cp_pattern.h
new file mode 100644
index 0000000000..4065caa5e5
--- /dev/null
+++ b/modules/chibi/cp_pattern.h
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* cp_pattern.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_PATTERN_H
+#define CP_PATTERN_H
+
+#include "cp_note.h"
+
+class CPPattern {
+public:
+
+
+ enum {
+ WIDTH=64,
+ DEFAULT_LEN=64,
+ RESIZE_EVERY_BITS=4,
+ MIN_ROWS=1, //otherwise clipboard wont work
+ MAX_LEN=256
+
+ };
+
+private:
+ struct Event {
+
+ uint16_t pos; //column*WIDTH+row
+ uint8_t note;
+ uint8_t instrument;
+ uint8_t volume;
+ uint8_t command;
+ uint8_t parameter;
+ unsigned int script_source_sign;
+ bool cloned;
+ };
+
+ uint16_t length;
+ uint32_t event_count;
+ Event* events;
+
+ int32_t get_event_pos(uint16_t p_target_pos);
+ bool erase_event_at_pos(uint16_t p_pos);
+
+ bool resize_event_list_to(uint32_t p_events);
+
+ void operator=(const CPPattern& p_pattern); //no operator=
+public:
+
+ bool is_empty();
+ void clear();
+
+ bool set_note(uint8_t p_column, uint16_t p_row,const CPNote& p_note); //true if no more memory
+ CPNote get_note(uint8_t p_column,uint16_t p_row);
+
+ CPNote get_transformed_script_note(uint8_t p_column, uint16_t p_row);
+ int get_scripted_note_target_channel(uint8_t p_column, uint16_t p_row);
+ void scripted_clone(uint8_t p_column, uint16_t p_row);
+ void scripted_clone_remove(uint8_t p_column, uint16_t p_row);
+ void script_transform_note(CPNote& n, const CPNote& p_note);
+ bool update_scripted_clones_sourcing_channel(int channel);
+
+ //void copy_to(CPPattern *p_pattern) const;
+ void set_length(uint16_t p_rows);
+ uint16_t get_length();
+ CPPattern();
+ ~CPPattern();
+
+
+};
+
+#endif
diff --git a/modules/chibi/cp_player_data.cpp b/modules/chibi/cp_player_data.cpp
new file mode 100644
index 0000000000..3f3e9a5202
--- /dev/null
+++ b/modules/chibi/cp_player_data.cpp
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* cp_player_data.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+#include <stdio.h>
+
+
+CPPlayer::CPPlayer(CPMixer *p_mixer,CPSong *p_song){
+
+ song=p_song;
+ mixer=p_mixer;
+ control.max_voices=p_mixer->get_total_voice_count()-1; //leave one for the sample
+ control.force_no_nna=false;
+ control.external_vibrato=false;
+ control.filters=true;
+ control.random_seed=128364; //anything
+ control.play_mode=0;
+ set_virtual_channels(p_mixer->get_total_voice_count());
+ mixer->set_callback( &CPPlayer::callback_function, this );
+
+ reset();
+}
+CPPlayer::~CPPlayer(){
+}
+
+void CPPlayer::set_virtual_channels(int p_amount) {
+
+ if (p_amount<1) return;
+ if (p_amount>mixer->get_total_voice_count())
+ return;
+
+ control.max_voices=p_amount;
+
+}
+
+
+void CPPlayer::callback_function(void *p_userdata) {
+
+ CPPlayer*pd=(CPPlayer*)p_userdata;
+ pd->process_tick();
+
+}
+
+void CPPlayer::process_tick() {
+
+ handle_tick();
+ mixer->set_callback_interval( 2500000/control.tempo );
+ song_usecs+=2500000/control.tempo;
+}
+
+void CPPlayer::reset() {
+
+ if ( mixer==NULL ) return ;
+ if ( song==NULL ) return ;
+
+ int i;
+
+ for (i=0;i<control.max_voices;i++) {
+
+ voice[i].reset();
+ mixer->stop_voice(i);
+ }
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ control.channel[i].reset();
+ control.channel[i].channel_volume=song->get_channel_volume(i);
+ control.channel[i].channel_panning=((int)song->get_channel_pan( i)*PAN_RIGHT/64);
+ if (song->is_channel_surround(i))
+ control.channel[i].channel_panning=PAN_SURROUND;
+ control.channel[i].mute=song->is_channel_mute( i );
+ control.channel[i].chorus_send=song->get_channel_chorus(i)*0xFF/64;
+ control.channel[i].reverb_send=song->get_channel_reverb(i)*0xFF/64;
+ }
+
+
+ control.speed=song->get_speed();
+ control.tempo=song->get_tempo();
+ control.global_volume=song->get_global_volume();
+
+ control.position.current_pattern=0;
+ control.position.current_row=0;
+ control.position.current_order=0;
+ control.position.force_next_order=-1;
+ control.ticks_counter=control.speed;
+ control.position.forbid_jump=false;
+
+ song_usecs=0;
+
+}
+
+int64_t CPPlayer::get_channel_last_note_time_usec(int p_channel) const {
+
+ CP_FAIL_INDEX_V(p_channel,64,-1);
+ return control.channel[p_channel].last_event_usecs;
+
+}
+
+void CPPlayer::set_channel_global_volume(int p_channel,int p_volume) {
+
+ CP_FAIL_INDEX(p_channel,64);
+ control.channel[p_channel].channel_global_volume=CLAMP(p_volume,0,255);
+
+}
+
+int CPPlayer::get_channel_global_volume(int p_channel) const{
+
+ CP_FAIL_INDEX_V(p_channel,64,-1);
+ return control.channel[p_channel].channel_global_volume;
+
+}
+
+bool CPPlayer::reached_end_of_song() {
+
+ return control.reached_end;
+
+}
+void CPPlayer::set_force_external_vibratos(bool p_force) {
+
+ control.external_vibrato=p_force;
+}
+void CPPlayer::set_force_no_nna(bool p_force) {
+
+ control.force_no_nna=p_force;
+}
diff --git a/modules/chibi/cp_player_data.h b/modules/chibi/cp_player_data.h
new file mode 100644
index 0000000000..282592b8f4
--- /dev/null
+++ b/modules/chibi/cp_player_data.h
@@ -0,0 +1,582 @@
+/*************************************************************************/
+/* cp_player_data.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_PLAYER_DATA_H
+#define CP_PLAYER_DATA_H
+
+#include "cp_config.h"
+#include "cp_song.h"
+#include "cp_mixer.h"
+#include "cp_tables.h"
+
+/**CPPlayer Data
+ *@author Juan Linietsky
+ */
+
+/******************************
+ player_data.h
+ ------------------------
+
+The player and its data.
+I hope you dont get sick reading this
+********************************/
+
+//Default pan values
+
+
+class CPPlayer {
+
+ enum {
+ PAN_SURROUND=512,
+ PAN_RIGHT=255,
+ PAN_LEFT=0,
+ PAN_CENTER=128
+ };
+
+
+ CPSong *song;
+
+ CPMixer *mixer;
+
+ struct Filter_Control {
+
+ int32_t it_reso;
+ int32_t it_cutoff;
+ int32_t envelope_cutoff;
+ int32_t final_cutoff;
+
+ void process();
+ void set_filter_parameters(int *p_cutoff,uint8_t *p_reso);
+
+ };
+
+ //tells you if a channel is doing
+ //noteoff/notekill/notefade/etc
+ enum {
+
+ END_NOTE_NOTHING=0,
+ END_NOTE_OFF=1,
+ END_NOTE_FADE=2,
+ END_NOTE_KILL=4
+ };
+
+ //Tells you what should a channel restart
+ enum {
+
+ KICK_NOTHING,
+ KICK_NOTE,
+ KICK_NOTEOFF,
+ KICK_ENVELOPE
+ };
+
+ enum {
+
+ MAX_VOICES=256
+ };
+
+ struct Channel_Control;
+
+ struct Voice_Control {
+
+ struct Envelope_Control {
+
+ int pos_index;
+ int status;
+ int value;
+ bool sustain_looping;
+ bool looping;
+ bool terminated;
+ bool active;
+ bool kill;
+
+ };
+
+ Filter_Control filter;
+ uint16_t reverb_send;
+ uint16_t chorus_send;
+
+ CPInstrument* instrument_ptr;
+ CPSample* sample_ptr;
+
+// Sample_Data *sample_data;
+
+ int32_t period;
+
+ int32_t sample_start_index; /* The starting byte index in the sample */
+
+ bool has_master_channel;
+ int master_channel_index;
+ int instruement_index;
+
+ int instrument_index;
+ int sample_index;
+ int8_t NNA_type;
+
+ int note_end_flags;
+
+ uint8_t sample; /* which instrument number */
+
+ int16_t output_volume; /* output volume (vol + sampcol + instvol) */
+ int8_t channel_volume; /* channel's "global" volume */
+ uint16_t fadeout_volume; /* fading volume rate */
+ int32_t total_volume; /* total volume of channel (before global mixings) */
+ uint8_t kick; /* if true = sample has to be restarted */
+
+ uint8_t note; /* the audible note (as heard, direct rep of period) */
+
+ int16_t panning; /* panning position */
+
+ uint8_t nna; /* New note action type + master/slave flags */
+ uint8_t volflg; /* volume envelope settings */
+ uint8_t panflg; /* panning envelope settings */
+ uint8_t pitflg; /* pitch envelope settings */
+ uint8_t keyoff; /* if true = fade out and stuff */
+ int16_t handle; /* which sample-handle */
+ int32_t start; /* The start byte index in the sample */
+
+ /* Below here is info NOT in MP_CONTROL!! */
+ //ENVPR venv;
+ //ENVPR penv;
+ //ENVPR cenv;
+
+ Envelope_Control volume_envelope_ctrl;
+ Envelope_Control panning_envelope_ctrl;
+ Envelope_Control pitch_envelope_ctrl;
+
+ uint16_t auto_vibrato_pos; /* autovibrato pos */
+ uint16_t auto_vibrato_sweep_pos; /* autovibrato sweep pos */
+
+ int16_t masterchn;
+ uint16_t masterperiod;
+
+ Channel_Control* master_channel; /* index of "master" effects channel */
+
+ void start_envelope(CPEnvelope *p_envelope,Envelope_Control *p_envelope_ctrl,Envelope_Control *p_from_env);
+ bool process_envelope(CPEnvelope *p_envelope,Envelope_Control *p_envelope_ctrl);
+
+ uint16_t display_volume;
+
+ Voice_Control() {
+
+ reset();
+ }
+
+ void reset();
+ void update_info_from_master_channel();
+
+
+ };
+
+
+ struct Channel_Control {
+
+ /* NOTE info */
+ uint8_t note; /* the audible note as heard, direct rep of period */
+ uint8_t real_note; /* the note that indexes the audible */
+ int32_t sample_start_index; /* The starting byte index in the sample */
+ uint8_t old_note;
+
+ uint8_t kick;
+
+ Filter_Control filter;
+ uint16_t reverb_send;
+ uint16_t chorus_send;
+
+
+ int note_end_flags;
+
+ /* INSTRUMENT INFO */
+
+ CPInstrument* instrument_ptr;
+ CPSample* sample_ptr;
+
+ uint8_t instrument_index;
+ uint8_t sample_index;
+ bool new_instrument;
+
+ /* SAMPLE SPECIFIC INFO */
+ int32_t base_speed; /* what finetune to use */
+
+ /* INSTRUMENT SPECIFIC INFO */
+
+ int8_t NNA_type;
+ int8_t duplicate_check_type;
+ int8_t duplicate_check_action;
+
+ bool volume_envelope_on;
+ bool panning_envelope_on;
+ bool pitch_envelope_on;
+
+ bool has_own_period;
+
+ bool row_has_note;
+
+ /* VOLUME COLUMN */
+
+ int16_t volume; /* amiga volume (0 t/m 64) to play the sample at */
+ int16_t aux_volume;
+ bool has_own_volume;
+ bool mute;
+ int16_t random_volume_variation; /* 0-100 - 100 has no effect */
+
+ /* VOLUME/PAN/PITCH MODIFIERS */
+
+ int8_t default_volume; // CHANNEL default volume (0-64)
+ int16_t channel_volume; // CHANNEL current volume //chanvol - current!
+ int16_t output_volume; /* output volume (vol + sampcol + instvol) //volume */
+ int16_t channel_global_volume;
+
+ uint16_t fadeout_volume; /* fading volume rate */
+
+ int32_t period; /* period to play the sample at */
+
+ /* PAN */
+
+ int16_t panning; /* panning position */
+ int16_t channel_panning;
+ int8_t sliding;
+
+ uint16_t aux_period; /* temporary period */
+
+
+
+ /* TIMING */
+ uint8_t note_delay; /* (used for note delay) */
+
+ /* Slave Voice Control */
+
+ Voice_Control *slave_voice; /* Audio Slave of current effects control channel */
+
+ struct Carry {
+
+ Voice_Control::Envelope_Control vol;
+ Voice_Control::Envelope_Control pan;
+ Voice_Control::Envelope_Control pitch;
+ bool maybe;
+
+ } carry;
+
+
+
+ uint8_t slave_voice_index; /* Audio Slave of current effects control channel */
+
+ uint8_t* row; /* row currently playing on this channel */
+
+ /* effect memory variables */
+
+ uint8_t current_command;
+ uint8_t current_parameter;
+ uint8_t current_volume_command;
+ uint8_t current_volume_parameter;
+ uint8_t volcol_volume_slide;
+
+ /* CPSample Offset */
+
+ int32_t lo_offset;
+ int32_t hi_offset;
+
+ /* Panbrello waveform */
+ uint8_t panbrello_type; /* current panbrello waveform */
+ uint8_t panbrello_position; /* current panbrello position */
+ int8_t panbrello_speed; /* "" speed */
+ uint8_t panbrello_depth; /* "" depth */
+ uint8_t panbrello_info;
+ /* Arpegio */
+
+ uint8_t arpegio_info;
+ /* CPPattern Loop */
+
+ int pattern_loop_position;
+ int8_t pattern_loop_count;
+
+ /* Vibrato */
+ bool doing_vibrato;
+ int8_t vibrato_position; /* current vibrato position */
+ uint8_t vibrato_speed; /* "" speed */
+ uint8_t vibrato_depth; /* "" depth */
+ uint8_t vibrato_type;
+ /* Tremor */
+ int8_t tremor_position;
+ uint8_t tremor_speed; /* s3m tremor ontime/offtime */
+ uint8_t tremor_depth;
+ uint8_t tremor_info;
+
+ /* Tremolo */
+ int8_t tremolo_position;
+ uint8_t tremolo_speed; /* s3m tremor ontime/offtime */
+ uint8_t tremolo_depth;
+ uint8_t tremolo_info;
+ uint8_t tremolo_type;
+
+ /* Retrig */
+ int8_t retrig_counter; /* retrig value (0 means don't retrig) */
+ uint8_t retrig_speed; /* last used retrig speed */
+ uint8_t retrig_volslide; /* last used retrig slide */
+
+ /* CPSample Offset */
+ int32_t sample_offset_hi; /* last used high order of sample offset */
+ uint16_t sample_offset; /* last used low order of sample-offset (effect 9) */
+ uint16_t sample_offset_fine; /* fine sample offset memory */
+
+ /* Portamento */
+ uint16_t slide_to_period; /* period to slide to (with effect 3 or 5) */
+ uint8_t portamento_speed;
+
+ /* Volume Slide */
+
+ uint8_t volume_slide_info;
+
+ /* Channel Volume Slide */
+
+ uint8_t channel_volume_slide_info;
+
+ /* Global Volume Slide */
+
+ uint8_t global_volume_slide_info;
+
+ /* Channel Pan Slide */
+
+ uint8_t channel_pan_slide_info;
+
+ /* Pitch Slide */
+
+ uint8_t pitch_slide_info;
+ /* Tempo Slide */
+
+ uint8_t tempo_slide_info;
+
+ /* S effects memory */
+
+ uint8_t current_S_effect;
+ uint8_t current_S_data;
+
+ /* Volume column memory */
+
+ uint8_t volume_column_effect_mem;
+ uint8_t volume_column_data_mem;
+
+ int64_t last_event_usecs;
+ bool reserved;
+
+ void reset();
+
+ Channel_Control() { channel_global_volume=255; last_event_usecs=-1; }
+ };
+
+ struct Control_Variables { // control variables (dynamic version) of initial variables
+
+ bool reached_end;
+
+ char play_mode;
+ bool filters;
+ int global_volume;
+ int speed;
+ int tempo;
+
+ int ticks_counter;
+
+ int pattern_delay_1;
+ int pattern_delay_2;
+
+ Channel_Control channel[CPPattern::WIDTH];
+
+ int max_voices;
+
+ int voices_used; /* reference value */
+
+ bool force_no_nna;
+ bool external_vibrato;
+
+ struct Position {
+
+ int current_order;
+ int current_pattern;
+ int current_row;
+ int force_next_order;
+ bool forbid_jump;
+ };
+
+ int32_t random_seed;
+
+ Position position;
+ Position previous_position;
+
+ };
+
+
+ Voice_Control voice[MAX_VOICES];
+
+ Control_Variables control;
+
+ /* VOICE SETUP */
+
+ void setup_voices();
+
+ /* MIXER SETUP */
+ void handle_tick();
+ void update_mixer();
+
+ /* NOTE / INSTRUMENT PROCESSING */
+
+ void process_new_note(int p_track,uint8_t p_note);
+ bool process_new_instrument(int p_track,uint8_t p_instrument);
+ bool process_note_and_instrument(int p_track,int p_note,int p_instrument);
+
+ /* EFFECT PROCESSING */
+ void do_effect_S(int p_track);
+ void do_panbrello(int p_track);
+ void do_global_volume_slide(int p_track);
+ void do_tremolo(int p_track);
+ void do_retrig(int p_track);
+ void do_pan_slide(int p_track);
+ void do_channel_volume_slide(int p_track);
+ void do_volume_slide(int p_track,int inf);
+ void do_pitch_slide_down(int p_track,uint8_t inf);
+ void do_pitch_slide_up(int p_track,uint8_t inf);
+ void do_tremor(int p_track);
+ void do_vibrato(int p_track,bool fine);
+ void do_pitch_slide_to_note(int p_track);
+ void run_effects(int p_track);
+ void run_volume_column_effects(int p_track);
+ void pre_process_effects();
+ void do_arpegio(int p_track);
+ uint64_t song_usecs;
+ /* NNA */
+
+ void process_NNAs();
+
+
+ /* MISC UTILS */
+
+
+ int find_empty_voice();
+ void process_volume_column(int p_track,uint8_t p_volume);
+ void process_note(int p_track,CPNote p_note);
+
+ /* CPTables */
+ static uint8_t auto_vibrato_table[128];
+ static uint8_t vibrato_table[32];
+ static int8_t panbrello_table[256];
+
+ static void callback_function(void *p_userdata);
+
+public:
+ //Play modes
+
+ enum {
+
+ PLAY_NOTHING =0,
+ PLAY_PATTERN =1,
+ PLAY_SONG =2
+ };
+
+
+ int32_t get_frequency(int32_t period);
+ int32_t get_period(uint16_t note,int32_t p_c5freq);
+
+
+ int get_current_tempo() { return control.tempo; };
+ int get_current_speed() { return control.speed; };
+
+ int get_voices_used() { return control.voices_used;};
+ int get_voice_envelope_pos(int p_voice,CPEnvelope *p_envelope);
+ int get_voice_amount_limit() { return control.max_voices; };
+ void set_voice_amount_limit(int p_limit);
+ void set_reserved_voices(int p_amount);
+ int get_reserved_voices_amount();
+
+ bool is_voice_active(int p_voice);
+ int get_channel_voice(int p_channel);
+ const char* get_voice_sample_name(int p_voice);
+ const char* get_voice_instrument_name(int p_voice);
+ CPEnvelope* get_voice_envelope(int p_voice,CPInstrument::EnvelopeType p_env_type);
+ int get_voice_envelope_pos(int p_voice,CPInstrument::EnvelopeType p_env_type);
+ int get_voice_volume(int p_voice);
+
+ int get_voice_sample_index(int p_voice);
+
+ void set_virtual_channels(int p_amount);
+ int get_virtual_channels() { return control.max_voices; };
+
+
+ /* Play Info/Position */
+ bool is_playing() { return (control.play_mode>0); };
+ int get_play_mode() {return (control.play_mode);};
+ int get_current_order() { return control.position.current_order; };
+ int get_current_row() { return control.position.current_row; };
+ int get_current_pattern() { return control.position.current_pattern; };
+
+ void goto_next_order();
+ void goto_previous_order();
+
+ void process_tick();
+
+
+ CPMixer* get_mixer_ptr() {
+
+ return mixer;
+ }
+
+
+ void reset();
+
+
+
+ /* External player control - editor - */
+
+ void play_start_pattern(int p_pattern);
+ void play_start_song();
+ void play_start_song_from_order(int p_order);
+ void play_start_song_from_order_and_row(int p_order,int p_row);
+ void play_start(int p_pattern, int p_order, int p_row,bool p_lock=true);
+
+ void play_stop();
+ void play_note(int p_channel,CPNote note,bool p_reserve=false);
+
+ bool reached_end_of_song();
+
+ void set_force_no_nna(bool p_force);
+ void set_force_external_vibratos(bool p_force);
+
+ void set_filters_enabled(bool p_enable);
+ bool are_filters_enabled() { return control.filters; }
+
+ void set_channel_global_volume(int p_channel,int p_volume); //0-255
+ int get_channel_global_volume(int p_channel) const;
+
+ int64_t get_channel_last_note_time_usec(int p_channel) const;
+
+ CPSong *get_song() { return song; };
+
+
+ CPPlayer(CPMixer *p_mixer,CPSong *p_song);
+ ~CPPlayer();
+};
+
+#endif
diff --git a/modules/chibi/cp_player_data_control.cpp b/modules/chibi/cp_player_data_control.cpp
new file mode 100644
index 0000000000..d9aaed904f
--- /dev/null
+++ b/modules/chibi/cp_player_data_control.cpp
@@ -0,0 +1,324 @@
+/*************************************************************************/
+/* cp_player_data_control.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+
+void CPPlayer::play_start_pattern(int p_pattern) {
+
+ play_start(p_pattern,-1,-1);
+}
+
+void CPPlayer::play_start_song() {
+
+ play_start(-1,-1,-1);
+}
+
+void CPPlayer::play_start_song_from_order(int p_order) {
+
+ play_start(-1,p_order,-1);
+}
+
+void CPPlayer::play_start_song_from_order_and_row(int p_order,int p_row) {
+
+ play_start(-1,p_order,p_row);
+}
+
+void CPPlayer::play_start(int p_pattern, int p_order, int p_row,bool p_lock) {
+
+
+ if (control.play_mode!=PLAY_NOTHING) play_stop();
+
+
+ reset();
+
+ if (p_pattern!=-1) {
+
+ control.play_mode=PLAY_PATTERN;
+ control.position.current_pattern=p_pattern;
+ control.position.current_row=(p_row!=-1)?p_row:0;
+
+ } else {
+
+ control.position.current_order=get_song_next_order_idx(song,(p_order==-1)?p_order:p_order-1);
+ if (control.position.current_order!=-1) {
+
+ control.play_mode=PLAY_SONG;
+ control.position.current_pattern=song->get_order(control.position.current_order);
+ control.position.current_row=(p_row!=-1)?p_row:0;
+ }
+ }
+
+
+ control.reached_end=(control.play_mode==PLAY_NOTHING);
+
+
+}
+
+void CPPlayer::play_stop() {
+
+ int i;
+
+
+ control.play_mode=PLAY_NOTHING;
+
+ for (i=0;i<control.max_voices;i++) {
+
+ voice[i].reset();
+ mixer->stop_voice(i);
+ }
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ control.channel[i].reset();
+ }
+
+ reset();
+
+}
+
+void CPPlayer::play_note(int p_channel,CPNote note,bool p_reserve) {
+
+
+
+ if (control.play_mode==PLAY_NOTHING) {
+
+ control.ticks_counter=0;
+ }
+
+ /*control.channel[p_channel].reset();
+ control.channel[p_channel].channel_volume=song->get_channel_volume(p_channel);
+ control.channel[p_channel].channel_panning=((int)song->get_channel_pan( p_channel)*255/64);*/
+ if (p_reserve) {
+ control.channel[p_channel].mute=false;
+ control.channel[p_channel].reserved=true;
+ } else {
+
+ control.channel[p_channel].reserved=false;
+
+ }
+ process_note(p_channel,note);
+
+
+
+}
+
+
+int CPPlayer::get_voice_volume(int p_voice) {
+
+ return voice[p_voice].display_volume;
+}
+
+
+int CPPlayer::get_voice_envelope_pos(int p_voice,CPEnvelope *p_envelope) {
+
+ int i,tmp_index=-1;
+
+ i=p_voice;
+
+
+
+
+ if ((song->has_instruments()) && (voice[i].instrument_ptr!=NULL) && (voice[i].fadeout_volume>0)) {
+
+ if ((p_envelope==voice[i].instrument_ptr->get_volume_envelope()) && (voice[i].instrument_ptr->get_volume_envelope()->is_enabled())) {
+
+ tmp_index=voice[i].volume_envelope_ctrl.pos_index;
+ }
+
+ if ((p_envelope==voice[i].instrument_ptr->get_pan_envelope()) && (voice[i].instrument_ptr->get_pan_envelope()->is_enabled())) {
+
+ tmp_index=voice[i].panning_envelope_ctrl.pos_index;
+ }
+
+ if ((p_envelope==voice[i].instrument_ptr->get_pitch_filter_envelope()) && (voice[i].instrument_ptr->get_pitch_filter_envelope()->is_enabled())) {
+
+
+ tmp_index=voice[i].pitch_envelope_ctrl.pos_index;
+ }
+
+ }
+
+
+
+ return tmp_index;
+}
+
+void CPPlayer::goto_next_order() {
+
+
+ if (control.play_mode!=PLAY_SONG) return;
+
+
+
+ control.position.current_row=0;
+
+
+ control.position.current_order=get_song_next_order_idx(song, control.position.current_order);
+
+
+
+ if (control.position.current_order==-1) {
+
+ reset();
+ }
+
+ control.position.current_pattern=song->get_order(control.position.current_order);
+
+
+}
+void CPPlayer::goto_previous_order() {
+
+ if (control.play_mode!=PLAY_SONG) return;
+
+
+ int next_order,current_order;
+
+ control.position.current_row=0;
+
+ current_order=control.position.current_order;
+
+ next_order=get_song_next_order_idx(song, current_order);
+
+ while ((next_order!=control.position.current_order) && (next_order!=-1)) {
+
+ current_order=next_order;
+ next_order=get_song_next_order_idx(song, current_order);
+ }
+
+ if (next_order==-1) {
+
+ reset();
+ } else {
+
+ control.position.current_order=current_order;
+ control.position.current_pattern=song->get_order(control.position.current_order);
+
+ }
+
+
+
+}
+
+int CPPlayer::get_channel_voice(int p_channel) {
+
+ if (control.channel[p_channel].slave_voice==NULL) return -1;
+ else return control.channel[p_channel].slave_voice_index;
+}
+
+const char* CPPlayer::get_voice_sample_name(int p_voice) {
+
+ const char *name = NULL;
+
+
+
+ if (!voice[p_voice].sample_ptr) name=voice[p_voice].sample_ptr->get_name();
+
+
+
+ return name;
+
+}
+
+
+bool CPPlayer::is_voice_active(int p_voice) {
+
+ return !( ((voice[p_voice].kick==KICK_NOTHING)||(voice[p_voice].kick==KICK_ENVELOPE))&&!mixer->is_voice_active(p_voice) );
+
+}
+
+
+
+int CPPlayer::get_voice_envelope_pos(int p_voice,CPInstrument::EnvelopeType p_env_type) {
+
+ if (!is_voice_active(p_voice))
+ return -1;
+
+ Voice_Control::Envelope_Control *env=0;
+
+ switch (p_env_type) {
+
+ case CPInstrument::VOLUME_ENVELOPE: env=&voice[p_voice].volume_envelope_ctrl; break;
+ case CPInstrument::PAN_ENVELOPE: env=&voice[p_voice].panning_envelope_ctrl; break;
+ case CPInstrument::PITCH_ENVELOPE: env=&voice[p_voice].pitch_envelope_ctrl; break;
+
+ }
+
+ if (!env)
+ return -1;
+
+ if (!env->active || env->terminated)
+ return -1;
+
+ return env->pos_index;
+}
+
+
+CPEnvelope* CPPlayer::get_voice_envelope(int p_voice,CPInstrument::EnvelopeType p_env_type) {
+
+ CPInstrument *ins=voice[p_voice].instrument_ptr;
+
+ if (!ins)
+ return 0;
+
+ switch( p_env_type ) {
+
+
+ case CPInstrument::VOLUME_ENVELOPE: return ins->get_volume_envelope();
+ case CPInstrument::PAN_ENVELOPE: return ins->get_pan_envelope();
+ case CPInstrument::PITCH_ENVELOPE: return ins->get_pitch_filter_envelope();
+ };
+
+ return 0;
+
+}
+
+const char * CPPlayer::get_voice_instrument_name(int p_voice) {
+
+
+
+ const char *name = NULL;
+
+
+
+ if (voice[p_voice].instrument_ptr!=NULL) name=voice[p_voice].instrument_ptr->get_name();
+
+
+
+ return name;
+
+}
+void CPPlayer::set_filters_enabled(bool p_enable){
+
+ control.filters=p_enable;
+}
+
+int CPPlayer::get_voice_sample_index(int p_voice) {
+
+ return voice[p_voice].sample_index;
+}
diff --git a/modules/chibi/cp_player_data_effects.cpp b/modules/chibi/cp_player_data_effects.cpp
new file mode 100644
index 0000000000..3a52a3b91b
--- /dev/null
+++ b/modules/chibi/cp_player_data_effects.cpp
@@ -0,0 +1,1232 @@
+/*************************************************************************/
+/* cp_player_data_effects.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+
+
+
+/**********************
+ complex effects
+***********************/
+#define RANDOM_MAX 2147483647
+
+static inline int32_t cp_random_generate(int32_t *seed) {
+ int32_t k;
+ int32_t s = (int32_t)(*seed);
+ if (s == 0)
+ s = 0x12345987;
+ k = s / 127773;
+ s = 16807 * (s - k * 127773) - 2836 * k;
+ if (s < 0)
+ s += 2147483647;
+ (*seed) = (int32_t)s;
+ return (int32_t)(s & RANDOM_MAX);
+}
+
+
+
+void CPPlayer::do_vibrato(int p_track,bool fine) {
+
+ uint8_t q;
+ uint16_t temp=0;
+
+ if ((control.ticks_counter==0) && control.channel[p_track].row_has_note) control.channel[p_track].vibrato_position=0;
+
+ q=(control.channel[p_track].vibrato_position>>2)&0x1f;
+
+ switch (control.channel[p_track].vibrato_type) {
+ case 0: /* sine */
+ temp=vibrato_table[q];
+ break;
+ case 1: /* square wave */
+ temp=255;
+ break;
+ case 2: /* ramp down */
+ q<<=3;
+ if (control.channel[p_track].vibrato_position<0) q=255-q;
+ temp=q;
+ break;
+ case 3: /* random */
+ temp=cp_random_generate(&control.random_seed) %256;//getrandom(256);
+ break;
+ }
+
+ temp*=control.channel[p_track].vibrato_depth;
+
+ if (song->has_old_effects()) {
+
+ temp>>=7;
+ } else {
+
+ temp>>=8;
+ }
+
+ if (!fine) temp<<=2;
+
+ if (control.channel[p_track].vibrato_position>=0) {
+
+ control.channel[p_track].period=control.channel[p_track].aux_period+temp;
+ } else {
+
+ control.channel[p_track].period=control.channel[p_track].aux_period-temp;
+ }
+
+ if (!song->has_old_effects() || control.ticks_counter) control.channel[p_track].vibrato_position+=control.channel[p_track].vibrato_speed;
+}
+
+
+void CPPlayer::do_pitch_slide_down(int p_track,uint8_t inf) {
+
+ uint8_t hi,lo;
+
+ if (inf) control.channel[p_track].pitch_slide_info=inf;
+ else inf=control.channel[p_track].pitch_slide_info;
+
+ hi=inf>>4;
+ lo=inf&0xf;
+
+ if (hi==0xf) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_period+=(uint16_t)lo<<2;
+ } else if (hi==0xe) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_period+=lo;
+ } else {
+
+ if (control.ticks_counter) control.channel[p_track].aux_period+=(uint16_t)inf<<2;
+ }
+}
+
+void CPPlayer::do_pitch_slide_up(int p_track,uint8_t inf) {
+
+ uint8_t hi,lo;
+
+ if (inf) control.channel[p_track].pitch_slide_info=inf;
+ else inf=control.channel[p_track].pitch_slide_info;
+
+ hi=inf>>4;
+ lo=inf&0xf;
+
+ if (hi==0xf) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_period-=(uint16_t)lo<<2;
+ } else if (hi==0xe) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_period-=lo;
+ } else {
+
+ if (control.ticks_counter) control.channel[p_track].aux_period-=(uint16_t)inf<<2;
+ }
+}
+
+void CPPlayer::do_pitch_slide_to_note(int p_track) {
+
+ if (control.ticks_counter) {
+ int dist;
+
+ /* We have to slide a->period towards a->wantedperiod, compute the
+ difference between those two values */
+ dist=control.channel[p_track].period-control.channel[p_track].slide_to_period;
+
+ /* if they are equal or if portamentospeed is too big... */
+ if ((!dist)||((control.channel[p_track].portamento_speed<<2)>cp_intabs(dist))) {
+ /* ... make tmpperiod equal tperiod */
+ control.channel[p_track].aux_period=control.channel[p_track].period=control.channel[p_track].slide_to_period;
+ } else {
+
+ if (dist>0) {
+
+ control.channel[p_track].aux_period-=control.channel[p_track].portamento_speed<<2;
+ control.channel[p_track].period-=control.channel[p_track].portamento_speed<<2; /* dist>0 slide up */
+ } else {
+ control.channel[p_track].aux_period+=control.channel[p_track].portamento_speed<<2;
+ control.channel[p_track].period+=control.channel[p_track].portamento_speed<<2; /* dist<0 slide down */
+ }
+ }
+
+ } else {
+
+ control.channel[p_track].aux_period=control.channel[p_track].period;
+ }
+}
+
+void CPPlayer::do_tremor(int p_track) {
+
+ uint8_t on,off,inf;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) {
+ control.channel[p_track].tremor_info=inf;
+ } else {
+ inf= control.channel[p_track].tremor_info;
+ if (!inf) return;
+ }
+
+ //if (!control.ticks_counter) return;
+
+ on=(inf>>4);
+ off=(inf&0xf);
+
+ control.channel[p_track].tremor_position%=(on+off);
+ control.channel[p_track].volume=(control.channel[p_track].tremor_position<on)?control.channel[p_track].aux_volume:0;
+ control.channel[p_track].tremor_position++;
+}
+
+void CPPlayer::do_pan_slide(int p_track) {
+
+ uint8_t lo,hi,inf;
+ int16_t pan;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].channel_pan_slide_info=inf;
+ else inf=control.channel[p_track].channel_pan_slide_info;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ pan=(control.channel[p_track].panning==PAN_SURROUND)?PAN_CENTER:control.channel[p_track].panning;
+
+ if (!hi)
+ pan+=lo<<2;
+ else
+ if (!lo) {
+ pan-=hi<<2;
+ } else
+ if (hi==0xf) {
+ if (!control.ticks_counter) pan+=lo<<2;
+ } else
+ if (lo==0xf) {
+ if (!control.ticks_counter) pan-=hi<<2;
+ }
+ //this sets both chan & voice paning
+ control.channel[p_track].panning=(pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);
+ control.channel[p_track].channel_panning=control.channel[p_track].panning;
+}
+
+void CPPlayer::do_volume_slide(int p_track,int inf) {
+
+ uint8_t hi,lo;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!lo) {
+
+ if ((control.ticks_counter)) control.channel[p_track].aux_volume+=hi;
+
+ } else if (!hi) {
+
+ if ((control.ticks_counter)) control.channel[p_track].aux_volume-=lo;
+
+ } else if (lo==0xf) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_volume+=(hi?hi:0xf);
+ } else if (hi==0xf) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_volume-=(lo?lo:0xf);
+ } else return;
+
+ if (control.channel[p_track].aux_volume<0) {
+
+ control.channel[p_track].aux_volume=0;
+ } else if (control.channel[p_track].aux_volume>64) {
+
+ control.channel[p_track].aux_volume=64;
+ }
+}
+
+void CPPlayer::do_channel_volume_slide(int p_track) {
+
+ uint8_t lo, hi,inf;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].channel_volume_slide_info=inf;
+ inf=control.channel[p_track].channel_volume_slide_info;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!hi)
+ control.channel[p_track].channel_volume-=lo;
+ else
+ if (!lo) {
+ control.channel[p_track].channel_volume+=hi;
+ } else
+ if (hi==0xf) {
+ if (!control.ticks_counter) control.channel[p_track].channel_volume-=lo;
+ } else
+ if (lo==0xf) {
+ if (!control.ticks_counter) control.channel[p_track].channel_volume+=hi;
+ }
+
+ if (control.channel[p_track].channel_volume<0) control.channel[p_track].channel_volume=0;
+ if (control.channel[p_track].channel_volume>64) control.channel[p_track].channel_volume=64;
+}
+
+void CPPlayer::do_tremolo(int p_track) {
+
+ uint8_t q;
+ int16_t temp=0;
+
+ if ((control.ticks_counter==0) && control.channel[p_track].row_has_note) control.channel[p_track].tremolo_position=0;
+
+ q=(control.channel[p_track].tremolo_position>>2)&0x1f;
+
+ switch (control.channel[p_track].tremolo_type) {
+ case 0: /* sine */
+ temp=vibrato_table[q];
+ break;
+ case 1: /* ramp down */
+ q<<=3;
+ if (control.channel[p_track].tremolo_position<0) q=255-q;
+ temp=q;
+ break;
+ case 2: /* square wave */
+ temp=255;
+ break;
+ case 3: /* random */
+ temp=cp_random_generate(&control.random_seed) % 256;//getrandom(256);
+ break;
+ }
+
+ temp*=control.channel[p_track].tremolo_depth;
+ temp>>=7;
+
+
+
+ if (control.channel[p_track].tremolo_position>=0) {
+
+
+ control.channel[p_track].volume=control.channel[p_track].aux_volume+temp;
+ if (control.channel[p_track].volume>64) control.channel[p_track].volume=64;
+ } else {
+
+ control.channel[p_track].volume=control.channel[p_track].aux_volume-temp;
+ if (control.channel[p_track].volume<0) control.channel[p_track].volume=0;
+ }
+
+ /*if (control.ticks_counter)*/ control.channel[p_track].tremolo_position+=control.channel[p_track].tremolo_speed;
+
+}
+
+void CPPlayer::do_arpegio(int p_track) {
+
+ uint8_t note,dat;
+ //note=control.channel[p_track].note;
+ note=0;
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].arpegio_info=control.channel[p_track].current_parameter;
+ }
+
+ dat=control.channel[p_track].arpegio_info;
+
+ if (dat) {
+
+ switch (control.ticks_counter%3) {
+
+ case 1: {
+
+ note+=(dat>>4);
+
+ } break;
+ case 2: {
+
+ note+=(dat&0xf);
+ } break;
+ }
+
+ if (song->has_linear_slides()) {
+
+ control.channel[p_track].period=control.channel[p_track].aux_period-cp_intabs(get_period((uint16_t)46,0)-get_period((uint16_t)44,0))*note;
+ } else if (control.channel[p_track].sample_ptr) {
+
+ control.channel[p_track].period=get_period( (((uint16_t)control.channel[p_track].note)+note)<<1,CPSampleManager::get_singleton()->get_c5_freq( (control.channel[p_track].sample_ptr->get_sample_data())));
+ }
+
+ control.channel[p_track].has_own_period=true;
+ }
+
+
+}
+
+
+void CPPlayer::do_retrig(int p_track) {
+
+ uint8_t inf;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) {
+
+ control.channel[p_track].retrig_volslide=inf>>4;
+ control.channel[p_track].retrig_speed=inf&0xf;
+ }
+
+ /* only retrigger if low nibble > 0 */
+ if ( control.channel[p_track].retrig_speed>0) {
+
+ if ( !control.channel[p_track].retrig_counter ) {
+ /* when retrig counter reaches 0, reset counter and restart the
+ sample */
+ if (control.channel[p_track].kick!=KICK_NOTE) control.channel[p_track].kick=KICK_NOTEOFF;
+ control.channel[p_track].retrig_counter=control.channel[p_track].retrig_speed;
+
+
+ if ((control.ticks_counter)/*||(pf->flags&UF_S3MSLIDES)*/) {
+ switch (control.channel[p_track].retrig_volslide) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ control.channel[p_track].aux_volume-=(1<<(control.channel[p_track].retrig_volslide-1));
+ break;
+ case 6:
+ control.channel[p_track].aux_volume=(2*control.channel[p_track].aux_volume)/3;
+ break;
+ case 7:
+ control.channel[p_track].aux_volume>>=1;
+ break;
+ case 9:
+ case 0xa:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ control.channel[p_track].aux_volume+=(1<<(control.channel[p_track].retrig_volslide-9));
+ break;
+ case 0xe:
+ control.channel[p_track].aux_volume=(3*control.channel[p_track].aux_volume)>>1;
+ break;
+ case 0xf:
+ control.channel[p_track].aux_volume=control.channel[p_track].aux_volume<<1;
+ break;
+ }
+ if (control.channel[p_track].aux_volume<0) control.channel[p_track].aux_volume=0;
+ else if (control.channel[p_track].aux_volume>64) control.channel[p_track].aux_volume=64;
+ }
+ }
+ control.channel[p_track].retrig_counter--; /* countdown */
+ }
+}
+
+void CPPlayer::do_global_volume_slide(int p_track) {
+
+ uint8_t lo,hi,inf;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].global_volume_slide_info=inf;
+ inf=control.channel[p_track].global_volume_slide_info;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!lo) {
+ if (control.ticks_counter) control.global_volume+=hi;
+ } else
+ if (!hi) {
+ if (control.ticks_counter) control.global_volume-=lo;
+ } else
+ if (lo==0xf) {
+ if (!control.ticks_counter) control.global_volume+=hi;
+ } else
+ if (hi==0xf) {
+ if (!control.ticks_counter) control.global_volume-=lo;
+ }
+
+ if (control.global_volume<0) control.global_volume=0;
+ if (control.global_volume>128) control.global_volume=128;
+}
+
+void CPPlayer::do_panbrello(int p_track) {
+
+ uint8_t q;
+ int32_t temp=0;
+
+ q=control.channel[p_track].panbrello_position;
+
+ switch (control.channel[p_track].panbrello_type) {
+ case 0: {/* sine */
+ temp=panbrello_table[q];
+ } break;
+ case 1: {/* square wave */
+ temp=(q<0x80)?64:0;
+ } break;
+ case 2: {/* ramp down */
+ q<<=3;
+ temp=q;
+ } break;
+ case 3: {/* random */
+ if (control.channel[p_track].panbrello_position>=control.channel[p_track].panbrello_speed) {
+ control.channel[p_track].panbrello_position=0;
+ temp=cp_random_generate(&control.random_seed)%256;//getrandom(256);
+ }
+ } break;
+ }
+
+
+
+ temp=temp*(int)control.channel[p_track].panbrello_depth/0xF;
+ temp<<=1;
+ if (control.channel[p_track].channel_panning!=PAN_SURROUND)
+ temp+=control.channel[p_track].channel_panning;
+
+ control.channel[p_track].panning=(temp<PAN_LEFT)?PAN_LEFT:(temp>PAN_RIGHT?PAN_RIGHT:temp);
+ control.channel[p_track].panbrello_position+=control.channel[p_track].panbrello_speed;
+}
+
+/******************
+ S effect
+*******************/
+
+
+void CPPlayer::do_effect_S(int p_track) {
+
+ uint8_t inf,c,dat;
+
+ dat=control.channel[p_track].current_parameter;
+
+ inf=dat&0xf;
+ c=dat>>4;
+
+ if (!dat) {
+ c=control.channel[p_track].current_S_effect;
+ inf=control.channel[p_track].current_S_data;
+ } else {
+ control.channel[p_track].current_S_effect=c;
+ control.channel[p_track].current_S_data=inf;
+ }
+
+ switch (c) {
+ case 1: {/* S1x set glissando voice */
+ // this is unsupported in IT!
+
+ control.channel[p_track].chorus_send=inf*0xFF/0xF;
+
+ }break;
+ case 2: /* S2x set finetune */
+ //Also not supported!
+ break;
+ case 3: /* S3x set vibrato waveform */
+ if (inf<4) control.channel[p_track].vibrato_type=inf;
+ break;
+ case 4: /* S4x set tremolo waveform */
+ if (inf<4) control.channel[p_track].tremolo_type=inf;
+ break;
+ case 5: /* S5x panbrello */
+ if (inf<4) control.channel[p_track].panbrello_type=inf;
+ break;
+ case 6: {/* S6x delay x number of frames (patdly) */
+
+ if (control.ticks_counter) break;
+ if (!control.pattern_delay_2) control.pattern_delay_1=inf+1; /* only once, when vbtick=0 */
+
+ } break;
+ case 7: /* S7x instrument / NNA commands */
+
+ if (!song->has_instruments())
+ break;
+ switch(inf) {
+
+ case 0x3: {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_CUT;
+ } break;
+ case 0x4: {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_CONTINUE;
+ } break;
+ case 0x5: {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_OFF;
+ } break;
+ case 0x6: {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_FADE;
+ } break;
+ case 0x7: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->volume_envelope_ctrl.active=false;
+ } break;
+ case 0x8: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->volume_envelope_ctrl.active=true;
+
+ } break;
+ case 0x9: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->panning_envelope_ctrl.active=false;
+
+ } break;
+ case 0xA: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->panning_envelope_ctrl.active=true;
+
+ } break;
+ case 0xB: {
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->pitch_envelope_ctrl.active=false;
+
+ } break;
+ case 0xC: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->pitch_envelope_ctrl.active=true;
+
+ } break;
+
+ } break;
+
+ break;
+ case 8: {/* S8x set panning position */
+
+// if (pf->panflag) {
+ if (inf<=8) inf<<=4;
+ else inf*=17;
+ control.channel[p_track].panning=control.channel[p_track].channel_panning=inf;
+// }
+ } break;
+
+ case 9: { /* S9x set surround sound */
+ //if (pf->panflag)
+ control.channel[p_track].panning=control.channel[p_track].channel_panning=PAN_SURROUND;
+ } break;
+ case 0xA:{ /* SAy set high order sample offset yxx00h */
+
+ if (control.channel[p_track].current_parameter) control.channel[p_track].hi_offset=(int32_t)inf<<16;
+ control.channel[p_track].sample_start_index=control.channel[p_track].hi_offset|control.channel[p_track].lo_offset;
+ } break;
+ case 0xB: { /* SBx pattern loop */
+ if (control.ticks_counter) break;
+
+ if (inf) { /* set reppos or repcnt ? */
+ /* set repcnt, so check if repcnt already is set, which means we
+ are already looping */
+ if (control.channel[p_track].pattern_loop_count>0)
+ control.channel[p_track].pattern_loop_count--; /* already looping, decrease counter */
+ else {
+ control.channel[p_track].pattern_loop_count=inf; /* not yet looping, so set repcnt */
+ }
+
+ if (control.channel[p_track].pattern_loop_count>0) { /* jump to reppos if repcnt>0 */
+
+ control.position=control.previous_position; // This will also anulate any Cxx or break..
+
+ control.position.current_row=control.channel[p_track].pattern_loop_position;
+ control.position.forbid_jump=true;
+ }
+
+ } else {
+
+
+ control.channel[p_track].pattern_loop_position=control.position.current_row-1;
+ }
+
+ } break;
+ case 0xC: { /* SCx notecut */
+
+ if (control.ticks_counter>=inf) {
+
+ control.channel[p_track].aux_volume=0;
+ control.channel[p_track].note_end_flags|=END_NOTE_OFF;
+ control.channel[p_track].note_end_flags|=END_NOTE_KILL;
+ }
+ } break;
+ case 0xD: {/* SDx notedelay */
+
+ if (!control.ticks_counter) {
+
+ control.channel[p_track].note_delay=inf;
+
+ } else if (control.channel[p_track].note_delay) {
+
+ control.channel[p_track].note_delay--;
+ }
+
+ } break;
+ case 0xF: {/* SEx patterndelay */
+
+ if (control.ticks_counter) break;
+ if (!control.pattern_delay_2) control.pattern_delay_1=inf+1; /* only once, when vbtick=0 */
+
+ } break;
+ }
+}
+
+
+
+
+
+
+
+
+/*********************
+ volume effects
+**********************/
+
+void CPPlayer::run_volume_column_effects(int p_track) {
+
+ uint8_t param=control.channel[p_track].current_volume_parameter;
+
+
+ switch ('A'+control.channel[p_track].current_volume_command) {
+
+ case 'A': {
+
+ if (param>0) control.channel[p_track].volcol_volume_slide=param;
+ else param=control.channel[p_track].volcol_volume_slide;
+
+ do_volume_slide(p_track,param*0x10+0xF);
+
+ } break;
+ case 'B': {
+
+ if (param>0) control.channel[p_track].volcol_volume_slide=param;
+ else param=control.channel[p_track].volcol_volume_slide;
+
+ do_volume_slide(p_track,0xF0+param);
+
+ } break;
+ case 'C': {
+
+ if (param>0) control.channel[p_track].volcol_volume_slide=param;
+ else param=control.channel[p_track].volcol_volume_slide;
+
+ do_volume_slide(p_track,param*0x10);
+ } break;
+ case 'D': {
+
+ if (param>0) control.channel[p_track].volcol_volume_slide=param;
+ else param=control.channel[p_track].volcol_volume_slide;
+ do_volume_slide(p_track,param);
+
+ } break;
+ case 'E': {
+
+ do_pitch_slide_down(p_track,param<<2);
+ } break;
+ case 'F': {
+
+ do_pitch_slide_up(p_track,param<<2);
+ } break;
+ case 'G': {
+
+ const uint8_t slide_table[]={0,1,4,8,16,32,64,96,128,255};
+ if (param) {
+
+ control.channel[p_track].portamento_speed=slide_table[param];
+ }
+
+ if (control.channel[p_track].period && (control.channel[p_track].old_note<=120)) {
+
+ if ( (!control.ticks_counter) && (control.channel[p_track].new_instrument) ){
+
+ //control.channel[p_track].kick=KICK_NOTE;
+ //control.channel[p_track].sample_start_index=0; // < am i stupid?
+ } else {
+
+ control.channel[p_track].kick=(control.channel[p_track].kick==KICK_NOTE)?KICK_ENVELOPE:KICK_NOTHING;
+ do_pitch_slide_to_note(p_track);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ }
+ } break;
+ case 'H': {
+
+
+ if (!control.ticks_counter) {
+ if (param&0x0f) control.channel[p_track].vibrato_depth=param;
+ }
+ control.channel[p_track].doing_vibrato=true;
+ if (control.external_vibrato) break;
+ if (control.channel[p_track].period) {
+
+ do_vibrato(p_track,false);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ } break;
+ }
+}
+/*********************
+ table
+**********************/
+
+
+void CPPlayer::run_effects(int p_track) {
+
+ switch ('A'+control.channel[p_track].current_command) {
+
+ case 'A': {
+
+ if ((control.ticks_counter>0) || (control.pattern_delay_2>0)) break;
+
+ int new_speed;
+
+ new_speed=control.channel[p_track].current_parameter % 128;
+
+ if (new_speed>0) {
+ control.speed=new_speed;
+ control.ticks_counter=0;
+ }
+ } break;
+ case 'B': {
+
+ int next_order;
+
+ if (control.ticks_counter || control.position.forbid_jump) break;
+
+ control.position.current_row=0;
+
+ if (control.play_mode==PLAY_PATTERN) break;
+
+ next_order=get_song_next_order_idx(song, (int)control.channel[p_track].current_parameter-1);
+
+ if (next_order!=-1) {
+ // Do we have a "next order?"
+ control.position.current_pattern=song->get_order(next_order);
+ control.position.force_next_order=next_order;
+
+ } else {
+ // no, probably the user deleted the orderlist.
+ control.play_mode=PLAY_NOTHING;
+ reset();
+ }
+ } break;
+ case 'C': {
+
+ int next_order;
+
+ if (control.ticks_counter || control.position.forbid_jump) break;
+
+ control.position.current_row=control.channel[p_track].current_parameter;
+
+ if (control.play_mode==PLAY_PATTERN) {
+
+ if (control.position.current_row>=song->get_pattern(control.position.current_pattern)->get_length()) {
+
+ control.position.current_row=0;
+ }
+
+ break;
+ }
+
+ next_order=get_song_next_order_idx(song, (int)control.position.current_order);
+
+ if (next_order!=-1) {
+ // Do we have a "next order?"
+ control.position.current_pattern=song->get_order(next_order);
+
+ if (control.position.current_row>=song->get_pattern(song->get_order(next_order))->get_length()) {
+
+ control.position.current_row=0;
+ }
+
+ control.position.force_next_order=next_order;
+
+ } else {
+ // no, probably the user deleted the orderlist.
+ control.play_mode=PLAY_NOTHING;
+ reset();
+ }
+
+ } break;
+ case 'D': {
+
+ uint8_t inf ;
+ //explicitslides=1;
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].volume_slide_info=inf;
+ else inf=control.channel[p_track].volume_slide_info;
+
+ do_volume_slide(p_track,inf);
+
+ } break;
+ case 'E': {
+
+ uint8_t inf;
+
+ inf=control.channel[p_track].current_parameter;
+ do_pitch_slide_down(p_track,inf);
+
+ } break;
+ case 'F': {
+
+ uint8_t inf;
+
+ inf=control.channel[p_track].current_parameter;
+ do_pitch_slide_up(p_track,inf);
+
+ } break;
+ case 'G': {
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].portamento_speed=control.channel[p_track].current_parameter;
+ }
+
+ if (control.channel[p_track].period && (control.channel[p_track].old_note<=120)) {
+
+ if ( (!control.ticks_counter) && (control.channel[p_track].new_instrument) ){
+
+
+ control.channel[p_track].kick=KICK_NOTE;
+ control.channel[p_track].sample_start_index=0;
+
+ } else {
+
+ control.channel[p_track].kick=(control.channel[p_track].kick==KICK_NOTE)?KICK_ENVELOPE:KICK_NOTHING;
+ }
+
+ do_pitch_slide_to_note(p_track);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ } break;
+ case 'H': {
+
+ uint8_t dat;
+
+ control.channel[p_track].doing_vibrato=true;
+
+ dat=control.channel[p_track].current_parameter;
+
+ if (!control.ticks_counter) {
+ if (dat&0x0f) control.channel[p_track].vibrato_depth=dat&0xf;
+ if (dat&0xf0) control.channel[p_track].vibrato_speed=(dat&0xf0)>>2;
+ }
+
+ if (control.external_vibrato) break;
+
+ if (control.channel[p_track].period) {
+
+ do_vibrato(p_track,false);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ } break;
+ case 'I': {
+
+ do_tremor(p_track);
+ control.channel[p_track].has_own_volume=true;
+ } break;
+ case 'J': {
+
+ do_arpegio(p_track);
+ } break;
+ case 'K': {
+
+ uint8_t inf ;
+ //explicitslides=1;
+ inf=control.channel[p_track].current_parameter;
+
+ control.channel[p_track].doing_vibrato=true;
+
+
+ if (inf) control.channel[p_track].volume_slide_info=inf;
+ else inf=control.channel[p_track].volume_slide_info;
+
+ do_volume_slide(p_track,inf);
+
+ if (control.external_vibrato) break;
+
+ if (control.channel[p_track].period) {
+
+ do_vibrato(p_track,false);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ } break;
+ case 'L': {
+ uint8_t inf ;
+ //explicitslides=1;
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].volume_slide_info=inf;
+ else inf=control.channel[p_track].volume_slide_info;
+
+ do_volume_slide(p_track,inf);
+
+ if (control.channel[p_track].period && (control.channel[p_track].old_note<=120)) {
+ if ( (!control.ticks_counter) && (control.channel[p_track].new_instrument) ){
+
+ control.channel[p_track].kick=KICK_NOTE;
+ control.channel[p_track].sample_start_index=0;
+
+ } else {
+
+ control.channel[p_track].kick=(control.channel[p_track].kick==KICK_NOTE)?KICK_ENVELOPE:KICK_NOTHING;
+ }
+
+ do_pitch_slide_to_note(p_track);
+ control.channel[p_track].has_own_period=true;
+ }
+ } break;
+ case 'M': {
+ control.channel[p_track].channel_volume=control.channel[p_track].current_parameter;
+ if (control.channel[p_track].channel_volume>64) control.channel[p_track].channel_volume=64;
+ else if (control.channel[p_track].channel_volume<0) control.channel[p_track].channel_volume=0;
+ } break;
+ case 'N': {
+
+ do_channel_volume_slide(p_track);
+ }
+ case 'O': {
+
+ if (!control.ticks_counter) {
+
+ if (control.channel[p_track].current_parameter) control.channel[p_track].lo_offset=(uint16_t)control.channel[p_track].current_parameter<<8;
+ control.channel[p_track].sample_start_index=control.channel[p_track].hi_offset|control.channel[p_track].lo_offset;
+
+ //if ((control.channel[p_track].sample_ptr!=NULL)&&(control.channel[p_track].sample_start_index>control.channel[p_track].sample_ptr->data.size)) {
+ //TODO, O effect
+ //a->start=a->s->flags&(SF_LOOP|SF_BIDI)?a->s->loopstart:a->s->length;
+ //}
+ }
+ } break;
+ case 'P': {
+
+ do_pan_slide(p_track);
+ } break;
+ case 'Q': {
+ do_retrig(p_track);
+
+ } break;
+ case 'R': {
+
+
+ uint8_t dat;
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].tremolo_info=control.channel[p_track].current_parameter;
+ }
+
+ dat=control.channel[p_track].tremolo_info;
+
+ if (!control.ticks_counter && dat) {
+
+ if (dat&0x0f) control.channel[p_track].tremolo_depth=dat&0xf;
+ if (dat&0xf0) control.channel[p_track].tremolo_speed=(dat&0xf0)>>2;
+ }
+
+ do_tremolo(p_track);
+ control.channel[p_track].has_own_volume=true;
+
+ } break;
+ case 'S': {
+
+ do_effect_S(p_track);
+ } break;
+ case 'T': {
+ uint8_t dat;
+ int16_t temp=control.tempo;
+
+ if (control.pattern_delay_2) return;
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].tempo_slide_info=control.channel[p_track].current_parameter;
+ }
+
+ dat=control.channel[p_track].tempo_slide_info;
+
+ if (dat>=0x20) {
+
+ if (control.ticks_counter) break;
+ control.tempo=dat;
+ } else {
+
+ if (!control.ticks_counter) break;
+
+ if (dat&0x10) {
+
+ temp+=(dat&0x0f);
+ } else {
+
+ temp-=dat;
+ }
+ control.tempo=(temp>255)?255:(temp<0x20?0x20:temp);
+ }
+
+ } break;
+ case 'U': {
+
+ uint8_t dat;
+
+ dat=control.channel[p_track].current_parameter;
+ control.channel[p_track].doing_vibrato=true;
+ if (!control.ticks_counter) {
+ if (dat&0x0f) control.channel[p_track].vibrato_depth=dat&0xf;
+ if (dat&0xf0) control.channel[p_track].vibrato_speed=(dat&0xf0)>>2;
+ }
+
+ if (control.external_vibrato) break;
+
+ if (control.channel[p_track].period) {
+
+ do_vibrato(p_track,true);
+ control.channel[p_track].has_own_period=true;
+ }
+ } break;
+ case 'V': {
+
+ control.global_volume=control.channel[p_track].current_parameter;
+ if (control.global_volume>128) control.global_volume=128;
+ } break;
+ case 'W': {
+ do_global_volume_slide(p_track);
+ } break;
+ case 'X': {
+ //sets both channel and current
+ control.channel[p_track].channel_panning=control.channel[p_track].current_parameter;
+ control.channel[p_track].panning=control.channel[p_track].current_parameter;
+ } break;
+ case 'Y': {
+
+ uint8_t dat;
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].panbrello_info=control.channel[p_track].current_parameter;
+ }
+
+ dat=control.channel[p_track].panbrello_info;
+
+ if (!control.ticks_counter) {
+
+ if (dat&0x0f) control.channel[p_track].panbrello_depth=(dat&0xf);
+ if (dat&0xf0) control.channel[p_track].panbrello_speed=(dat&0xf0)>>4;
+ }
+
+ //if (pf->panflag)
+ if (control.channel[p_track].panning!=PAN_SURROUND)do_panbrello(p_track);
+
+ } break;
+ case 'Z': {
+ //I DO! cuttoff!
+ uint16_t dat=control.channel[p_track].current_parameter;
+
+ if (dat<0x80) {
+
+ control.channel[p_track].filter.it_cutoff=dat*2;
+ if (control.channel[p_track].filter.it_cutoff>0x80)
+ control.channel[p_track].filter.it_cutoff++;
+ } else if (dat<0x90) {
+
+ control.channel[p_track].filter.it_reso=(dat-0x80)*0x10;
+ } else {
+
+ control.channel[p_track].reverb_send=(dat-0x90)*255/0x6F;
+ }
+
+ } break;
+
+ }
+
+}
+
+void CPPlayer::pre_process_effects() {
+
+// MP_VOICE *aout;
+ int i;
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ //a=&pf->control[mp_channel];
+
+ // if ((aout=a->slave)) {
+ // a->fadevol=aout->fadevol;
+ // a->period=aout->period;
+ // if (a->kick==KICK_KEYOFF) a->keyoff=aout->keyoff;
+ //}
+
+ //if (!a->row) continue;
+ //UniSetRow(a->row);
+ control.channel[i].has_own_period=false;
+ control.channel[i].has_own_volume=false;
+ control.channel[i].doing_vibrato=false;
+ //explicitslides=0;
+ //pt_playeffects();
+ if (control.ticks_counter<control.speed) {
+
+ run_effects(i);
+ run_volume_column_effects(i);
+ }
+
+ /* continue volume slide if necessary for XM and IT */
+ //if (pf->flags&UF_BGSLIDES) {
+ // if (!explicitslides && a->sliding)
+ // DoS3MVolSlide(0);
+ // else if (a->tmpvolume) a->sliding=explicitslides;
+ //}
+
+ if (!control.channel[i].has_own_period) control.channel[i].period=control.channel[i].aux_period;
+ if (!control.channel[i].has_own_volume) control.channel[i].volume=control.channel[i].aux_volume;
+
+ if ((control.channel[i].sample_ptr!=NULL) && !(song->has_instruments() && (control.channel[i].instrument_ptr==NULL))) {
+
+ if (song->has_instruments()) {
+
+ control.channel[i].output_volume=
+ (control.channel[i].volume*control.channel[i].sample_ptr->get_global_volume()*control.channel[i].instrument_ptr->get_volume_global_amount())/2048;
+ control.channel[i].output_volume=control.channel[i].output_volume*control.channel[i].random_volume_variation/100;
+
+ } else {
+
+ control.channel[i].output_volume=
+ (control.channel[i].volume*control.channel[i].sample_ptr->get_global_volume())>>4;
+
+ }
+
+ if (control.channel[i].output_volume>256) {
+
+ control.channel[i].output_volume=256;
+
+ } else if (control.channel[i].output_volume<0) {
+
+ control.channel[i].output_volume=0;
+ }
+
+
+ }
+ }
+
+}
diff --git a/modules/chibi/cp_player_data_envelopes.cpp b/modules/chibi/cp_player_data_envelopes.cpp
new file mode 100644
index 0000000000..96af42d19f
--- /dev/null
+++ b/modules/chibi/cp_player_data_envelopes.cpp
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* cp_player_data_envelopes.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+
+
+void CPPlayer::Voice_Control::start_envelope(CPEnvelope *p_envelope,Envelope_Control *p_envelope_ctrl,Envelope_Control *p_from_env) {
+
+
+ if (p_from_env && p_envelope->is_carry_enabled() && !p_from_env->terminated) {
+
+
+ *p_envelope_ctrl=*p_from_env;
+ } else {
+ p_envelope_ctrl->pos_index=0;
+ p_envelope_ctrl->status=1;
+ p_envelope_ctrl->sustain_looping=p_envelope->is_sustain_loop_enabled();
+ p_envelope_ctrl->looping=p_envelope->is_loop_enabled();
+ p_envelope_ctrl->terminated=false;
+ p_envelope_ctrl->kill=false;
+ p_envelope_ctrl->value=p_envelope->get_height_at_pos(p_envelope_ctrl->pos_index);
+ }
+}
+
+bool CPPlayer::Voice_Control::process_envelope(CPEnvelope *p_envelope,Envelope_Control *p_envelope_ctrl) {
+
+ if (!p_envelope_ctrl->active)
+ return false;
+
+ if (note_end_flags&END_NOTE_OFF) p_envelope_ctrl->sustain_looping=false;
+
+ p_envelope_ctrl->value=p_envelope->get_height_at_pos(p_envelope_ctrl->pos_index);
+ if (p_envelope_ctrl->value==CPEnvelope::NO_POINT)
+ return false;
+
+
+ p_envelope_ctrl->pos_index++;
+
+ if (p_envelope_ctrl->sustain_looping) {
+
+ if (p_envelope_ctrl->pos_index>p_envelope->get_node(p_envelope->get_sustain_loop_end()).tick_offset) {
+
+ p_envelope_ctrl->pos_index=p_envelope->get_node(p_envelope->get_sustain_loop_begin()).tick_offset;
+ }
+
+ } else if (p_envelope_ctrl->looping) {
+
+ if (p_envelope_ctrl->pos_index>p_envelope->get_node(p_envelope->get_loop_end()).tick_offset) {
+
+ p_envelope_ctrl->pos_index=p_envelope->get_node(p_envelope->get_loop_begin()).tick_offset;
+ }
+
+ }
+
+ if (p_envelope_ctrl->pos_index>p_envelope->get_node(p_envelope->get_node_count()-1).tick_offset) {
+
+ p_envelope_ctrl->terminated=true;
+ p_envelope_ctrl->pos_index=p_envelope->get_node(p_envelope->get_node_count()-1).tick_offset;
+ if (p_envelope->get_node(p_envelope->get_node_count()-1).value==0) p_envelope_ctrl->kill=true;
+ }
+
+ return true;
+}
diff --git a/modules/chibi/cp_player_data_events.cpp b/modules/chibi/cp_player_data_events.cpp
new file mode 100644
index 0000000000..fb5090461b
--- /dev/null
+++ b/modules/chibi/cp_player_data_events.cpp
@@ -0,0 +1,679 @@
+/*************************************************************************/
+/* cp_player_data_events.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+#include "cp_sample_manager.h"
+#include "stdio.h"
+/*
+ setup_voices():
+
+This will go throught all the REAL channels, if it finds a channel
+that needs to be restarted or assigned a new VIRTUAL channel, then it
+will just find one and do it.
+
+*/
+
+
+#define C5FREQ 261.6255653006
+static const int32_t C5FREQ_MIXER = ((int32_t)(C5FREQ*(float)(1<<CPMixer::FREQUENCY_BITS)));
+
+
+void CPPlayer::setup_voices() {
+
+ int i,voice_index;
+
+
+
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ voice_index=-1;
+
+ if (control.channel[i].note_delay) continue;
+
+
+ // check if we need a new empty voice
+ if (control.channel[i].kick==KICK_NOTE) {
+
+ /* if no channel was cut above, find an empty or quiet channel
+ here */
+ if ( song->has_instruments() && !control.force_no_nna) {
+
+ if (control.channel[i].slave_voice==NULL) { // no slave??
+
+ int newchn;
+ if ((newchn=find_empty_voice())!=-1) {
+
+ control.channel[i].slave_voice_index=newchn;
+ control.channel[i].slave_voice=&voice[newchn];
+ }
+ }
+
+ } else {
+ if (i<control.max_voices) {
+
+ control.channel[i].slave_voice_index=i;
+ control.channel[i].slave_voice=&voice[i];
+ } else {
+ //This is a _DIRTY_ hack, but i cant think a better way.
+ control.channel[i].slave_voice_index=control.max_voices-1;
+ control.channel[i].slave_voice=&voice[control.max_voices-1];
+ }
+
+ }
+
+
+ /* assign parts of MP_VOICE only done for a KICK_NOTE */
+ if ( ( control.channel[i].slave_voice!=NULL ) ) {
+
+ voice_index=control.channel[i].slave_voice_index;
+ Voice_Control &v=voice[voice_index];
+
+ if (v.has_master_channel && (v.master_channel!=NULL) ) {
+ // If this voice already has a master channel, make sure to remove the reference to it.
+ v.master_channel->slave_voice=NULL;
+
+ }
+ //notify the voice that the current channel is the master
+ v.master_channel=&control.channel[i];
+ //set the voice as slave of the current channel
+ control.channel[i].slave_voice=&v;
+ //master channel index of the voice
+ v.master_channel_index=i;
+ v.has_master_channel=true;
+ }
+
+ } else {
+ // nope..
+ // so if we DO have a slave voice then use it.
+ if ( control.channel[i].slave_voice!=NULL ) {
+
+ voice_index=control.channel[i].slave_voice_index;
+ }
+ }
+ //assuming this channel has a slave voice..
+ if (voice_index>=0) {
+
+ // IMPROVE: Code a method for this:
+ voice[voice_index].update_info_from_master_channel();
+ }
+
+ control.channel[i].kick=KICK_NOTHING;
+ }
+}
+void CPPlayer::Voice_Control::reset() {
+
+ cp_memzero(this,sizeof(*this));
+
+ instrument_ptr=NULL;
+ sample_ptr=NULL;
+ has_master_channel=false;
+ instrument_index=-1;
+ reverb_send=0;
+ chorus_send=0;
+ filter.it_cutoff=255;
+ filter.it_reso=0;
+ display_volume=0;
+
+
+}
+
+void CPPlayer::Channel_Control::reset() {
+
+ int prev_gv =channel_global_volume;
+ cp_memzero(this,sizeof(*this));
+
+ slave_voice=NULL;
+ slave_voice_index=255;
+
+ mute=false;
+ old_note=255;
+ real_note=255;
+ instrument_index=255;
+ filter.it_cutoff=255;
+ filter.it_reso=0;
+ reverb_send=0;
+ chorus_send=0;
+ reserved=false;
+ carry.maybe=false;
+ last_event_usecs=-1;
+ channel_global_volume=prev_gv;
+}
+
+void CPPlayer::Voice_Control::update_info_from_master_channel() {
+
+ instrument_ptr=master_channel->instrument_ptr;
+ sample_ptr=master_channel->sample_ptr;
+
+ instrument_index=master_channel->instrument_index;
+ sample_index=master_channel->sample_index;
+
+ note=master_channel->note;
+ output_volume=master_channel->output_volume;
+
+ channel_volume=master_channel->channel_volume;
+
+ panning=master_channel->panning;
+
+ kick=master_channel->kick;
+ note_end_flags=master_channel->note_end_flags;
+ period=master_channel->period;
+
+ volume_envelope_ctrl.active=master_channel->volume_envelope_on;
+ panning_envelope_ctrl.active=master_channel->panning_envelope_on;
+ pitch_envelope_ctrl.active=master_channel->pitch_envelope_on;
+
+
+ NNA_type=master_channel->NNA_type;
+ reverb_send=master_channel->reverb_send;
+ chorus_send=master_channel->chorus_send;
+
+// last_note_type=master_channel->last_note_type;
+
+ sample_start_index=master_channel->sample_start_index;
+ filter=master_channel->filter;
+
+}
+
+
+void CPPlayer::update_mixer() {
+
+ int tmp_mixer_period;
+ int32_t tmp_vibrato_value,tmp_vibrato_depth,tmp_volenv_value;
+ uint64_t tmpvol; // 64bits should be the only way to avoid getting notes raped out
+ int i;
+
+
+ control.voices_used=0;
+
+ for (i=0;i<control.max_voices;i++) {
+
+
+ int filter_env=-1;
+ Voice_Control &v=voice[i];
+
+ if ( !((v.kick==KICK_NOTE)||(v.kick==KICK_NOTEOFF)) && !is_voice_active(i))
+ continue;
+
+ //if voice doesnt have a sample set or size is 0.. forget it
+ if ( v.sample_ptr==NULL) continue;
+
+
+ //TODO set limits somewhere else
+
+ if (v.period<40) {
+
+ v.period=40;
+
+ } else if (v.period>50000) {
+
+ v.period=50000;
+ }
+
+
+ if ((v.kick==KICK_NOTE)||(v.kick==KICK_NOTEOFF)) {
+
+ int real_start_index;
+
+ if (v.sample_start_index==-1) {
+
+ real_start_index=0;
+
+ } else {
+
+ real_start_index=v.sample_start_index;
+ }
+
+ mixer->setup_voice(i,v.sample_ptr->get_sample_data(),real_start_index);
+ v.fadeout_volume=1024; //IT Docs it is 1024 internally
+ v.auto_vibrato_sweep_pos=0;
+
+
+
+ }
+
+
+ /* Start Envelopes */
+ if ( song->has_instruments() && ((v.kick==KICK_NOTE)||(v.kick==KICK_ENVELOPE))) {
+
+// Voice_Control *carry=0;
+
+
+ if (v.has_master_channel && v.master_channel->carry.maybe) {
+
+ v.start_envelope(v.instrument_ptr->get_volume_envelope(),&v.volume_envelope_ctrl,&v.master_channel->carry.vol);
+ v.start_envelope(v.instrument_ptr->get_pan_envelope(),&v.panning_envelope_ctrl,&v.master_channel->carry.pan);
+ v.start_envelope(v.instrument_ptr->get_pitch_filter_envelope(),&v.pitch_envelope_ctrl,&v.master_channel->carry.pitch);
+
+ } else {
+
+ v.start_envelope(v.instrument_ptr->get_volume_envelope(),&v.volume_envelope_ctrl,NULL);
+ v.start_envelope(v.instrument_ptr->get_pan_envelope(),&v.panning_envelope_ctrl,NULL);
+ v.start_envelope(v.instrument_ptr->get_pitch_filter_envelope(),&v.pitch_envelope_ctrl,NULL);
+
+ }
+
+
+ }
+
+ v.kick=KICK_NOTHING;
+
+ if (song->has_instruments()) {
+
+ if (!v.process_envelope(v.instrument_ptr->get_volume_envelope(),&v.volume_envelope_ctrl))
+ v.volume_envelope_ctrl.value=64;
+
+ if (!v.process_envelope(v.instrument_ptr->get_pan_envelope(),&v.panning_envelope_ctrl))
+ v.panning_envelope_ctrl.value=0;
+
+ if (!v.process_envelope(v.instrument_ptr->get_pitch_filter_envelope(),&v.pitch_envelope_ctrl))
+ v.pitch_envelope_ctrl.value=0;
+
+
+ if (v.volume_envelope_ctrl.terminated) {
+
+ if (v.volume_envelope_ctrl.kill) {
+
+ v.fadeout_volume=0;
+ } else {
+
+ v.note_end_flags|=END_NOTE_FADE;
+ }
+ }
+
+ }
+
+ if (song->has_instruments()) {
+
+ tmp_volenv_value=v.volume_envelope_ctrl.value;
+ } else {
+
+ tmp_volenv_value=64;
+
+ }
+
+ /*printf("fadeout %i\n",(int)v.fadeout_volume);
+ printf("channel %i\n",(int)v.channel_volume);
+ printf("output %i\n",(int)v.output_volume);
+ printf("env %i\n",(int)tmp_volenv_value);
+ printf("cgb %i\n",(int)v.master_channel->channel_global_volume);
+*/
+
+
+ tmpvol=(uint64_t)v.fadeout_volume; /* max 1024 - 10 bits */
+ tmpvol*=(uint64_t)v.channel_volume; /* * max 64 - 6 bits */
+ tmpvol*=(uint64_t)v.output_volume; /* * max 256 - 8 bits */
+ tmpvol*=(uint64_t)tmp_volenv_value; /* max 64 - 6 bits*/
+ tmpvol*=(uint64_t)v.master_channel->channel_global_volume;
+ v.display_volume=tmpvol>>22; //volume used for display purposes , 0 -- 256
+
+ tmpvol*=(uint64_t)song->get_mixing_volume(); /* max 128 - 7 bits */
+ tmpvol*=(uint64_t)control.global_volume; /* max 128 - 7 bits*/
+ /* total 10+6+8+6+7+7=44 bits */
+
+ tmpvol>>=43; /* Move back to 8 bits range , 44-19+8=43*/
+
+ if (tmpvol>CP_VOL_MAX)
+ tmpvol=CP_VOL_MAX;
+
+ //printf("volume check - fade %i, channel %i, output %i, env %i, mix %i, global %i -- final %i\n",v.fadeout_volume, v.channel_volume,v.output_volume,tmp_volenv_value, song->get_mixing_volume(),control.global_volume,tmpvol);
+
+ v.total_volume=tmpvol;
+
+
+ if ((v.master_channel!=NULL) && song->is_channel_mute( v.master_channel_index ) && !v.master_channel->reserved) {
+
+ mixer->set_voice_volume(i,0);
+ } else {
+ mixer->set_voice_volume(i,tmpvol);
+ if (v.fadeout_volume>0) control.voices_used++;
+ }
+
+
+ if (!song->is_stereo()) {
+
+ mixer->set_voice_panning(i,PAN_CENTER);
+
+ } else if (v.panning==PAN_SURROUND) {
+
+ mixer->set_voice_panning(i,PAN_SURROUND);
+ } else if (song->has_instruments()) {
+
+ int newpan,real_modifier;
+
+
+ real_modifier=(v.panning_envelope_ctrl.value*(PAN_CENTER-cp_intabs(v.panning-PAN_CENTER)))/32;
+
+ newpan=v.panning+real_modifier;
+
+ newpan=(newpan<PAN_LEFT)?PAN_LEFT:(newpan>PAN_RIGHT)?PAN_RIGHT:newpan;
+ //printf("panenv val: %i, finalpan val %i\n",v.panning_envelope_ctrl.value,newpan);
+
+ mixer->set_voice_panning(i,newpan);
+ } else {
+ mixer->set_voice_panning(i,v.panning);
+ }
+
+
+
+ /* VIBRATO */
+
+ if ( (v.period>0) && (v.sample_ptr->get_vibrato_depth()>0) ) {
+
+ switch (v.sample_ptr->get_vibrato_type()) {
+ case CPSample::VIBRATO_SINE:
+ tmp_vibrato_value=auto_vibrato_table[v.auto_vibrato_pos&127];
+ if (v.auto_vibrato_pos & 0x80) tmp_vibrato_value=-tmp_vibrato_value;
+ break;
+ case CPSample::VIBRATO_SQUARE:
+ tmp_vibrato_value=64;
+ if (v.auto_vibrato_pos & 0x80) tmp_vibrato_value=-tmp_vibrato_value;
+ break;
+ case CPSample::VIBRATO_SAW:
+ tmp_vibrato_value=63-(((v.auto_vibrato_pos+128)&255)>>1);
+ break;
+ default:
+ tmp_vibrato_value=(((v.auto_vibrato_pos+128)&255)>>1)-64;
+ break;
+ }
+ } else {
+
+ tmp_vibrato_value=0;
+ }
+
+ if ((v.auto_vibrato_sweep_pos>>8)<v.sample_ptr->get_vibrato_depth()) {
+
+ v.auto_vibrato_sweep_pos+=v.sample_ptr->get_vibrato_speed(); //FIXME - speed? i think so
+ tmp_vibrato_depth=v.auto_vibrato_sweep_pos;
+
+ } else {
+
+ tmp_vibrato_depth=v.sample_ptr->get_vibrato_depth()<<8;
+ }
+
+ tmp_vibrato_value=(tmp_vibrato_value*tmp_vibrato_depth)>>16;
+ if (song->has_linear_slides())
+ tmp_vibrato_value>>=1;
+ v.period-=tmp_vibrato_value;
+
+
+ /* update vibrato position */
+ v.auto_vibrato_pos=(v.auto_vibrato_pos+v.sample_ptr->get_vibrato_rate())&0xff;
+
+
+ /* process pitch envelope */
+ tmp_mixer_period=v.period;
+
+ if (v.pitch_envelope_ctrl.active) {
+
+ long aux_pitch_diff;
+ int pe_value=v.pitch_envelope_ctrl.value;
+
+ if (!v.instrument_ptr->is_pitch_use_as_filter()) {
+
+
+ if (((uint16_t)v.note<<1)+pe_value<=0)
+ pe_value=-(v.note<<1);
+
+ int smp_c5=CPSampleManager::get_singleton()->get_c5_freq( v.sample_ptr->get_sample_data());
+
+ int base=get_period(((uint16_t)v.note<<1),smp_c5);
+ int env=get_period(((uint16_t)v.note<<1)+pe_value,smp_c5);
+ /*
+ int env_next=(pe_value<0)?get_period(((uint16_t)(v.note-1)<<1)+pe_value,smp_c5):get_period(((uint16_t)(v.note+1)<<1)+pe_value,smp_c5);
+
+ env=env+(abs(v.pitch_envelope_ctrl.value)&((1<<CPEnvelope::FX_HEIGHT_BITS)-1))*(env_next-env)/(1<<CPEnvelope::FX_HEIGHT_BITS);
+
+ printf("env %i\n",env);
+ */
+ aux_pitch_diff=env-base;
+
+
+ if ( ((int)tmp_mixer_period-aux_pitch_diff)<0 ) aux_pitch_diff=0;
+
+ tmp_mixer_period+=aux_pitch_diff;
+
+ } else {
+
+ filter_env=pe_value+32; //max 64
+// printf("pitch envelope at %i",filter_env);
+
+ }
+ }
+
+ if (v.fadeout_volume==0 || (v.note_end_flags & END_NOTE_KILL)) { /* check for a dead note (fadevol=0) */
+
+ mixer->stop_voice(i);
+
+ } else {
+
+
+ int32_t freq=get_frequency(tmp_mixer_period);
+ int32_t tracker_c5=get_frequency(get_period(60<<1,CPSampleManager::get_singleton()->get_c5_freq( v.sample_ptr->get_sample_data())));
+
+ freq=(int32_t)((uint64_t)freq*(uint64_t)C5FREQ_MIXER/(uint64_t)tracker_c5); //numbers may become very high
+ mixer->set_voice_frequency(i,freq);
+
+ /* if keyfade, start substracting fadeoutspeed from fadevol: */
+ if ((song->has_instruments())&&(v.note_end_flags & END_NOTE_FADE)) {
+
+ if (v.fadeout_volume>=(v.instrument_ptr->get_volume_fadeout())) {
+
+ v.fadeout_volume-=(v.instrument_ptr->get_volume_fadeout());
+ } else {
+
+ v.fadeout_volume=0;
+ }
+ }
+
+ /*FILTARSSSSSSSS*/
+
+
+
+ v.filter.envelope_cutoff=filter_env;
+ v.filter.process();
+
+ if ((v.filter.final_cutoff<0xFF) && (control.filters)) {
+
+ //int final_cutoff;
+ //uint8_t final_reso;
+
+ //v.filter.set_filter_parameters( &final_cutoff, &final_reso );
+
+ mixer->set_voice_filter(i,true,v.filter.final_cutoff,v.filter.it_reso);
+ } else {
+
+
+ mixer->set_voice_filter(i,false,0,0);
+ }
+
+ /* RAIVERV */
+
+ mixer->set_voice_reverb_send(i,v.reverb_send);
+
+ /* CHAURUZ */
+
+ mixer->set_voice_chorus_send(i,v.chorus_send);
+
+ }
+ }
+
+
+ switch(song->get_reverb_mode()) {
+
+ case CPSong::REVERB_MODE_ROOM: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_ROOM );
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_SMALL: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_STUDIO_SMALL );
+
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_MEDIUM: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_STUDIO_MEDIUM );
+
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_LARGE: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_STUDIO_LARGE );
+
+ } break;
+ case CPSong::REVERB_MODE_HALL: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_HALL );
+
+ } break;
+ case CPSong::REVERB_MODE_SPACE_ECHO: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_SPACE_ECHO );
+
+ } break;
+ case CPSong::REVERB_MODE_ECHO: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_ECHO );
+
+ } break;
+ case CPSong::REVERB_MODE_DELAY: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_DELAY );
+
+ } break;
+ case CPSong::REVERB_MODE_HALF_ECHO: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_HALF_ECHO );
+
+ } break;
+
+ }
+
+ mixer->set_chorus_params(song->get_chorus_delay_ms(),song->get_chorus_separation_ms(),song->get_chorus_depth_ms10(),song->get_chorus_speed_hz10() );
+
+
+}
+
+
+
+
+
+
+
+
+void CPPlayer::handle_tick() {
+
+ int i;
+
+
+ if ( mixer==NULL ) return;
+ if ( song==NULL ) return;
+
+
+ /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */
+
+ if (control.ticks_counter>=control.speed) { // time to process... ***THE ROW***!
+
+ /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is
+ the command memory. */
+
+// if (control.pattern_delay_1) {
+
+// control.pattern_delay_2=control.pattern_delay_1;
+// control.pattern_delay_1=0;
+// }
+// if (control.pattern_delay_2) {
+// patterndelay active
+// if (--control.pattern_delay_2)
+// so turn back pf->patpos by 1
+// if (pf->patpos) pf->patpos--;
+// }
+
+ if (control.play_mode!=PLAY_NOTHING) {
+
+ control.ticks_counter=0;
+
+
+ if (control.position.force_next_order>=0) {
+
+ control.position.current_order=control.position.force_next_order;
+ }
+
+ control.position.force_next_order=-1;
+
+ control.previous_position=control.position; // for those special cases...
+ control.position.forbid_jump=false;
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ process_note(i,song->get_pattern(control.position.current_pattern)->get_note(i,control.position.current_row));
+ }
+
+ control.position.current_row++;
+
+ if ( control.position.current_row>=song->get_pattern(control.position.current_pattern)->get_length() ) {
+
+ if (control.play_mode==PLAY_SONG) {
+
+ int next_order;
+
+ next_order=get_song_next_order_idx(song,control.position.current_order);
+
+ if (next_order!=-1) {
+ // Do we have a "next order?"
+ control.position.current_pattern=song->get_order(next_order);
+ if (next_order<=control.position.current_order)
+ control.reached_end=true;
+ control.position.current_order=next_order;
+
+ } else {
+ // no, probably the user deleted the orderlist.
+ control.play_mode=PLAY_NOTHING;
+ reset();
+ control.reached_end=true;
+ }
+ }
+ control.position.current_row=0;
+ }
+
+ }
+
+
+ }
+
+
+
+ pre_process_effects();
+ process_NNAs();
+ setup_voices();
+
+ /* now set up the actual hardware channel playback information */
+ update_mixer();
+
+ control.ticks_counter++;
+}
diff --git a/modules/chibi/cp_player_data_filter.cpp b/modules/chibi/cp_player_data_filter.cpp
new file mode 100644
index 0000000000..30db807eed
--- /dev/null
+++ b/modules/chibi/cp_player_data_filter.cpp
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* cp_player_data_filter.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+
+#include "cp_player_data.h"
+
+static float filter_cutoff[256] = {
+ 130, 132, 134, 136, 138, 140, 142, 144,
+ 146, 148, 151, 153, 155, 157, 160, 162,
+ 164, 167, 169, 172, 174, 177, 179, 182,
+ 184, 187, 190, 193, 195, 198, 201, 204,
+ 207, 210, 213, 216, 220, 223, 226, 229,
+ 233, 236, 239, 243, 246, 250, 254, 257,
+ 261, 265, 269, 273, 277, 281, 285, 289,
+ 293, 297, 302, 306, 311, 315, 320, 324,
+ 329, 334, 339, 344, 349, 354, 359, 364,
+ 369, 375, 380, 386, 391, 397, 403, 409,
+ 415, 421, 427, 433, 440, 446, 452, 459,
+ 466, 472, 479, 486, 493, 501, 508, 515,
+ 523, 530, 538, 546, 554, 562, 570, 578,
+ 587, 595, 604, 613, 622, 631, 640, 649,
+ 659, 668, 678, 688, 698, 708, 718, 729,
+ 739, 750, 761, 772, 783, 795, 806, 818,
+ 830, 842, 854, 867, 880, 892, 905, 918,
+ 932, 945, 959, 973, 987, 1002, 1016, 1031,
+ 1046, 1061, 1077, 1092, 1108, 1124, 1141, 1157,
+ 1174, 1191, 1209, 1226, 1244, 1262, 1280, 1299,
+ 1318, 1337, 1357, 1376, 1396, 1417, 1437, 1458,
+ 1479, 1501, 1523, 1545, 1567, 1590, 1613, 1637,
+ 1661, 1685, 1709, 1734, 1760, 1785, 1811, 1837,
+ 1864, 1891, 1919, 1947, 1975, 2004, 2033, 2062,
+ 2093, 2123, 2154, 2185, 2217, 2249, 2282, 2315,
+ 2349, 2383, 2418, 2453, 2489, 2525, 2561, 2599,
+ 2637, 2675, 2714, 2753, 2793, 2834, 2875, 2917,
+ 2959, 3003, 3046, 3091, 3135, 3181, 3227, 3274,
+ 3322, 3370, 3419, 3469, 3520, 3571, 3623, 3675,
+ 3729, 3783, 3838, 3894, 3951, 4008, 4066, 4125,
+ 4186, 4246, 4308, 4371, 4434, 4499, 4564, 4631,
+ 4698, 4766, 4836, 4906, 4978, 5050, 5123, 5198
+};
+
+
+void CPPlayer::Filter_Control::process() {
+
+
+ final_cutoff=it_cutoff;
+ if (envelope_cutoff>=0) {
+
+ envelope_cutoff=envelope_cutoff*255/64;
+ final_cutoff=final_cutoff*envelope_cutoff/255;
+ if (final_cutoff>=0xFF) final_cutoff=0xFE;
+
+ }
+
+}
+
+void CPPlayer::Filter_Control::set_filter_parameters(int *p_cutoff,uint8_t *p_reso) {
+
+
+
+ *p_cutoff=filter_cutoff[final_cutoff];
+ *p_reso=it_reso;
+}
diff --git a/modules/chibi/cp_player_data_nna.cpp b/modules/chibi/cp_player_data_nna.cpp
new file mode 100644
index 0000000000..844f043694
--- /dev/null
+++ b/modules/chibi/cp_player_data_nna.cpp
@@ -0,0 +1,144 @@
+/*************************************************************************/
+/* cp_player_data_nna.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+
+void CPPlayer::process_NNAs() {
+
+ int i;
+
+ if (!song->has_instruments()) return;
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ Channel_Control *aux_chn_ctrl = &control.channel[i];
+
+ if (aux_chn_ctrl->kick==KICK_NOTE) {
+
+ bool k=false;
+
+ if (aux_chn_ctrl->slave_voice!=NULL) {
+
+ Voice_Control *aux_voc_ctrl;
+
+ aux_voc_ctrl=aux_chn_ctrl->slave_voice;
+
+ if (aux_chn_ctrl->instrument_index==aux_chn_ctrl->slave_voice->instrument_index) { //maybe carry
+
+ aux_chn_ctrl->carry.pan=aux_chn_ctrl->slave_voice->panning_envelope_ctrl;
+ aux_chn_ctrl->carry.vol=aux_chn_ctrl->slave_voice->volume_envelope_ctrl;
+ aux_chn_ctrl->carry.pitch=aux_chn_ctrl->slave_voice->pitch_envelope_ctrl;
+ aux_chn_ctrl->carry.maybe=true;
+ } else
+ aux_chn_ctrl->carry.maybe=false;
+
+ if (aux_voc_ctrl->NNA_type != CPInstrument::NNA_NOTE_CUT) {
+ /* Make sure the old MP_VOICE channel knows it has no
+ master now ! */
+
+
+
+ aux_chn_ctrl->slave_voice=NULL;
+ /* assume the channel is taken by NNA */
+ aux_voc_ctrl->has_master_channel=false;
+
+ switch (aux_voc_ctrl->NNA_type) {
+ case CPInstrument::NNA_NOTE_CONTINUE: {
+
+ } break;
+ case CPInstrument::NNA_NOTE_OFF: {
+
+
+ aux_voc_ctrl->note_end_flags|=END_NOTE_OFF;
+
+ if (!aux_voc_ctrl->volume_envelope_ctrl.active || aux_voc_ctrl->instrument_ptr->get_volume_envelope()->is_loop_enabled()) {
+ aux_voc_ctrl->note_end_flags|=END_NOTE_FADE;
+ }
+ } break;
+ case CPInstrument::NNA_NOTE_FADE: {
+
+ aux_voc_ctrl->note_end_flags|=END_NOTE_FADE;
+ } break;
+ }
+ }
+ }
+
+ if (aux_chn_ctrl->duplicate_check_type!=CPInstrument::DCT_DISABLED) {
+ int i;
+
+ for (i=0;i<control.max_voices;i++) {
+ if (!mixer->is_voice_active(i)||
+ (voice[i].master_channel!=aux_chn_ctrl) ||
+ (aux_chn_ctrl->instrument_index!=voice[i].instrument_index))
+ continue;
+
+ Voice_Control *aux_voc_ctrl;
+
+ aux_voc_ctrl=&voice[i];
+
+ k=false;
+ switch (aux_chn_ctrl->duplicate_check_type) {
+ case CPInstrument::DCT_NOTE:
+ if (aux_chn_ctrl->note==aux_voc_ctrl->note)
+ k=true;
+ break;
+ case CPInstrument::DCT_SAMPLE:
+ if (aux_chn_ctrl->sample_ptr==aux_voc_ctrl->sample_ptr)
+ k=true;
+ break;
+ case CPInstrument::DCT_INSTRUMENT:
+ k=true;
+ break;
+ }
+ if (k) {
+ switch (aux_chn_ctrl->duplicate_check_action) {
+ case CPInstrument::DCA_NOTE_CUT: {
+ aux_voc_ctrl->fadeout_volume=0;
+ } break;
+ case CPInstrument::DCA_NOTE_OFF: {
+
+ aux_voc_ctrl->note_end_flags|=END_NOTE_OFF;
+
+ if (!aux_voc_ctrl->volume_envelope_ctrl.active || aux_chn_ctrl->instrument_ptr->get_volume_envelope()->is_loop_enabled()) {
+
+ aux_voc_ctrl->note_end_flags|=END_NOTE_FADE;
+ }
+
+ } break;
+ case CPInstrument::DCA_NOTE_FADE: {
+ aux_voc_ctrl->note_end_flags|=END_NOTE_FADE;
+ } break;
+ }
+ }
+ }
+
+ }
+ } /* if (aux_chn_ctrl->kick==KICK_NOTE) */
+ }
+}
diff --git a/modules/chibi/cp_player_data_notes.cpp b/modules/chibi/cp_player_data_notes.cpp
new file mode 100644
index 0000000000..621be019e1
--- /dev/null
+++ b/modules/chibi/cp_player_data_notes.cpp
@@ -0,0 +1,345 @@
+/*************************************************************************/
+/* cp_player_data_notes.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+#include "cp_sample_manager.h"
+
+#define RANDOM_MAX 2147483647
+
+static inline int32_t cp_random_generate(int32_t *seed) {
+ int32_t k;
+ int32_t s = (int32_t)(*seed);
+ if (s == 0)
+ s = 0x12345987;
+ k = s / 127773;
+ s = 16807 * (s - k * 127773) - 2836 * k;
+ if (s < 0)
+ s += 2147483647;
+ (*seed) = (int32_t)s;
+ return (int32_t)(s & RANDOM_MAX);
+}
+
+
+void CPPlayer::process_new_note(int p_track,uint8_t p_note) { // if there's really a new note....
+
+ if (control.channel[p_track].real_note!=255) {
+ control.channel[p_track].old_note=control.channel[p_track].real_note;
+
+ }
+
+ control.channel[p_track].real_note=p_note;
+
+ control.channel[p_track].kick=KICK_NOTE;
+
+ control.channel[p_track].sample_start_index=-1;
+ control.channel[p_track].sliding=0;
+ control.channel[p_track].row_has_note=true;
+ control.channel[p_track].last_event_usecs=song_usecs;
+
+ if (control.channel[p_track].panbrello_type) control.channel[p_track].panbrello_position=0;
+}
+
+bool CPPlayer::process_new_instrument(int p_track,uint8_t p_instrument) {
+
+// bool different_instrument=false;
+ ERR_FAIL_INDEX_V(p_instrument,CPSong::MAX_INSTRUMENTS,false);
+
+ if ( song->has_instruments() ) {
+
+
+ control.channel[p_track].instrument_ptr=song->get_instrument(p_instrument);
+ } else {
+
+ control.channel[p_track].instrument_ptr=NULL;
+ }
+
+ control.channel[p_track].retrig_counter=0;
+ control.channel[p_track].tremor_position=0;
+ control.channel[p_track].sample_offset_fine=0;
+ int old_instr_index=control.channel[p_track].instrument_index;
+ control.channel[p_track].instrument_index=p_instrument;
+
+ return (old_instr_index!=p_instrument);
+
+
+}
+
+
+ // returns if it was able to process
+bool CPPlayer::process_note_and_instrument(int p_track,int p_note,int p_instrument) {
+
+ bool aux_result;
+ aux_result=false;
+ CPSample *aux_sample=0; // current sample
+ int dest_sample_index;
+ bool new_instrument=false;
+
+ control.channel[p_track].row_has_note=false; // wise man says.. "we dont have a note... until we really know we have a note".
+ control.channel[p_track].new_instrument=false;
+
+ if ( (p_note<0) && (p_instrument<0) ) return aux_result; // nothing to do here
+ if ( (p_note==255) && (p_instrument==255) ) return aux_result;
+
+ if ( (p_note>=0) && (p_note<120) ) {
+
+ process_new_note(p_track,p_note);
+
+ } else if (p_note==CPNote::CUT) {
+
+ control.channel[p_track].aux_volume=0;
+ control.channel[p_track].note_end_flags|=END_NOTE_OFF;
+ control.channel[p_track].note_end_flags|=END_NOTE_KILL;
+ return aux_result;
+
+ } else if ((p_note==CPNote::OFF) && (song->has_instruments())) {
+
+ if (control.channel[p_track].instrument_ptr!=NULL) {
+
+ control.channel[p_track].note_end_flags|=END_NOTE_OFF;
+
+ if (!control.channel[p_track].instrument_ptr->get_volume_envelope()->is_enabled() || control.channel[p_track].instrument_ptr->get_volume_envelope()->is_loop_enabled()) {
+
+ control.channel[p_track].note_end_flags|=END_NOTE_FADE;
+ }
+ }
+
+ return aux_result;
+ } else return aux_result; // invalid note!
+
+
+ if ( (p_instrument>=0) && (p_instrument<CPSong::MAX_INSTRUMENTS)) {
+ new_instrument=process_new_instrument(p_track,p_instrument);
+
+ if ( song->has_instruments() ) {
+ // If we're in instrument mode...
+ if ( control.channel[p_track].instrument_ptr->get_sample_number(control.channel[p_track].real_note) >= CPSong::MAX_SAMPLES) {
+
+ control.channel[p_track].kick=KICK_NOTHING;
+ return aux_result;
+
+ } else {
+ dest_sample_index=control.channel[p_track].instrument_ptr->get_sample_number(control.channel[p_track].real_note);
+ control.channel[p_track].note=control.channel[p_track].instrument_ptr->get_note_number(control.channel[p_track].real_note);
+ }
+
+ } else {
+ // If we're in sample mode...
+ dest_sample_index=control.channel[p_track].instrument_index;
+ control.channel[p_track].note=control.channel[p_track].real_note;
+ }
+
+ control.channel[p_track].sample_index=dest_sample_index;
+ aux_sample=song->get_sample(dest_sample_index);
+
+ if (!CPSampleManager::get_singleton()->check( aux_sample->get_sample_data() )) {
+ /* INVALID SAMPLE */
+ control.channel[p_track].kick=KICK_NOTHING;
+ return aux_result;
+
+ }
+
+ aux_sample=song->get_sample(dest_sample_index);
+ } else {
+
+
+ if (!control.channel[p_track].sample_ptr)
+ return aux_result;
+
+ if (song->has_instruments()) {
+
+ if (!control.channel[p_track].instrument_ptr)
+ return aux_result;
+
+ control.channel[p_track].note=control.channel[p_track].instrument_ptr->get_note_number(control.channel[p_track].real_note);
+
+ } else {
+
+ control.channel[p_track].note=control.channel[p_track].real_note;
+
+ }
+
+ aux_sample=control.channel[p_track].sample_ptr;
+
+ }
+
+
+
+ if (p_instrument>=CPSong::MAX_INSTRUMENTS && control.channel[p_track].sample_ptr!=aux_sample) {
+
+ control.channel[p_track].new_instrument=(control.channel[p_track].period>0);
+ }
+
+ control.channel[p_track].sample_ptr=aux_sample;
+
+ /* channel or instrument determined panning ? */
+
+ control.channel[p_track].panning=control.channel[p_track].channel_panning;
+
+ /* set filter,if any ? */
+
+
+ if (aux_sample->is_pan_enabled()) {
+
+ control.channel[p_track].panning=(int)aux_sample->get_pan()*255/64;
+
+ } else if ( song->has_instruments() && (control.channel[p_track].instrument_ptr->is_pan_default_enabled()) ) {
+
+ control.channel[p_track].panning=(int)control.channel[p_track].instrument_ptr->get_pan_default_amount()*255/64;
+ }
+
+
+ if (song->has_instruments()) {
+
+
+ /* Pitch-Pan Separation */
+ if ((control.channel[p_track].instrument_ptr->get_pan_pitch_separation()!=0) && (control.channel[p_track].channel_panning!=PAN_SURROUND)){
+
+ control.channel[p_track].panning+=((control.channel[p_track].real_note-control.channel[p_track].instrument_ptr->get_pan_pitch_center())*control.channel[p_track].instrument_ptr->get_pan_pitch_separation())/8;
+
+ if (control.channel[p_track].panning<PAN_LEFT) control.channel[p_track].panning=PAN_LEFT;
+ if (control.channel[p_track].panning>PAN_RIGHT) control.channel[p_track].panning=PAN_RIGHT;
+ }
+
+ /* Random Volume Variation */
+ if (control.channel[p_track].instrument_ptr->get_volume_random_variation()>0) {
+
+ control.channel[p_track].random_volume_variation=100-(cp_random_generate(&control.random_seed) % control.channel[p_track].instrument_ptr->get_volume_random_variation());
+
+ } else {
+
+ control.channel[p_track].random_volume_variation=100;
+ }
+
+
+ /* Random Pan Variation */
+ if ((control.channel[p_track].instrument_ptr->get_pan_random_variation()>0) && (control.channel[p_track].panning!=PAN_SURROUND)){
+
+ int aux_pan_modifier;
+
+ aux_pan_modifier=(cp_random_generate(&control.random_seed) % (control.channel[p_track].instrument_ptr->get_pan_random_variation() << 2));
+ if ((cp_random_generate(&control.random_seed) % 2)==1) aux_pan_modifier=0-aux_pan_modifier; /* it's 5am, let me sleep :) */
+
+ control.channel[p_track].panning+=aux_pan_modifier;
+
+ if (control.channel[p_track].panning<PAN_LEFT) control.channel[p_track].panning=PAN_LEFT;
+ if (control.channel[p_track].panning>PAN_RIGHT) control.channel[p_track].panning=PAN_RIGHT;
+
+
+ }
+
+ /*filter*/
+
+ if (control.channel[p_track].instrument_ptr->filter_use_default_cutoff()) {
+
+ control.channel[p_track].filter.it_cutoff=control.channel[p_track].instrument_ptr->get_filter_default_cutoff()*2;
+
+ }
+
+ if (control.channel[p_track].instrument_ptr->filter_use_default_resonance()) {
+
+ control.channel[p_track].filter.it_reso=control.channel[p_track].instrument_ptr->get_filter_default_resonance()*2;
+
+ }
+
+ /*envelopes*/
+
+
+ control.channel[p_track].volume_envelope_on=control.channel[p_track].instrument_ptr->get_volume_envelope()->is_enabled();
+ control.channel[p_track].panning_envelope_on=control.channel[p_track].instrument_ptr->get_pan_envelope()->is_enabled();
+ control.channel[p_track].pitch_envelope_on=control.channel[p_track].instrument_ptr->get_pitch_filter_envelope()->is_enabled();
+ control.channel[p_track].NNA_type=control.channel[p_track].instrument_ptr->get_NNA_type();
+ control.channel[p_track].duplicate_check_type=control.channel[p_track].instrument_ptr->get_DC_type();
+ control.channel[p_track].duplicate_check_action=control.channel[p_track].instrument_ptr->get_DC_action();
+
+
+ } else {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_CUT;
+ control.channel[p_track].duplicate_check_type=CPInstrument::DCT_DISABLED;
+ control.channel[p_track].duplicate_check_action=CPInstrument::DCA_NOTE_CUT;
+ }
+
+
+ if (p_instrument<CPSong::MAX_INSTRUMENTS) { // instrument change
+
+ control.channel[p_track].volume=control.channel[p_track].aux_volume=aux_sample->get_default_volume();
+
+ }
+
+
+ control.channel[p_track].slide_to_period=control.channel[p_track].aux_period=get_period((uint16_t)(control.channel[p_track].note)<<1,CPSampleManager::get_singleton()->get_c5_freq( (aux_sample->get_sample_data())));
+
+ control.channel[p_track].note_end_flags=END_NOTE_NOTHING; /* clears flags */
+
+ return true;
+}
+
+void CPPlayer::process_volume_column(int p_track,uint8_t p_volume) {
+
+ control.channel[p_track].current_volume_command=CPNote::EMPTY;
+ control.channel[p_track].current_volume_parameter=CPNote::EMPTY;
+
+ if (p_volume<65) { // VOLUME
+
+ control.channel[p_track].aux_volume=p_volume;
+ } else if (p_volume<125) { // Volume Command
+
+
+ control.channel[p_track].current_volume_command=(p_volume-65) / 10;
+ control.channel[p_track].current_volume_parameter=(p_volume-65) % 10;
+ } else if (p_volume<193) { // PAN
+
+ control.channel[p_track].channel_panning=(p_volume-128)*PAN_RIGHT/64;
+ control.channel[p_track].panning=control.channel[p_track].channel_panning;
+
+ } else if (p_volume<213) { //More volume Commands
+
+ control.channel[p_track].current_volume_command=((p_volume-193) / 10)+6;
+ control.channel[p_track].current_volume_parameter=(p_volume-193) % 10;
+ }
+}
+
+
+void CPPlayer::process_note(int p_track,CPNote p_note) {
+
+ if ( p_note.note!=CPNote::SCRIPT ) {
+
+ process_note_and_instrument(p_track,p_note.note,p_note.instrument);
+ process_volume_column(p_track,p_note.volume);
+ control.channel[p_track].current_command=p_note.command;
+ control.channel[p_track].current_parameter=p_note.parameter;
+
+ } else {
+
+ CPNote n = song->get_pattern( control.position.current_pattern )->get_transformed_script_note( p_track, control.position.current_row );
+ process_note( p_track, n );
+
+ song->get_pattern( control.position.current_pattern )->scripted_clone( p_track, control.position.current_row );
+ }
+}
diff --git a/modules/chibi/cp_player_data_utils.cpp b/modules/chibi/cp_player_data_utils.cpp
new file mode 100644
index 0000000000..170a849863
--- /dev/null
+++ b/modules/chibi/cp_player_data_utils.cpp
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* cp_player_data_utils.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+uint8_t CPPlayer::vibrato_table[32]={
+ 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253,
+ 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24
+};
+
+uint8_t CPPlayer::auto_vibrato_table[128]={
+ 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23,
+ 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44,
+ 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58,
+ 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63,
+ 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59,
+ 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46,
+ 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25,
+ 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1
+};
+
+
+int8_t CPPlayer::panbrello_table[256]={
+ 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
+ 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
+ 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
+ 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
+ 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
+ 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
+ 0,- 2,- 3,- 5,- 6,- 8,- 9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
+ -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44,
+ -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59,
+ -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60,
+ -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,
+ -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,
+ -24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2
+};
+
+
+
+
+
+
+int32_t CPPlayer::get_period(uint16_t p_note,int32_t p_c5speed) {
+
+ if (song->has_linear_slides()) {
+
+ return CPTables::get_linear_period(p_note,0);
+ } else {
+
+
+ return CPTables::get_log_period(p_note>>1,p_c5speed >>1);
+ }
+}
+
+
+int32_t CPPlayer::get_frequency(int32_t period) {
+
+ if (song->has_linear_slides()) {
+
+ return CPTables::get_linear_frequency(period);
+ } else {
+
+ return CPTables::get_old_frequency(period);
+ }
+}
+
+int CPPlayer::find_empty_voice() {
+
+ int i;
+ int min_priority,min_priority_chan=0,priority;
+
+ for (i=0;i<control.max_voices;i++) {
+
+ if ( ((voice[i].kick==KICK_NOTHING)||(voice[i].kick==KICK_ENVELOPE))&&!mixer->is_voice_active(i) ) {
+
+ return i;
+
+ }
+ }
+
+ // todo more
+
+ for (i=0;i<control.max_voices;i++) {
+ /* allow us to take over a nonexisting sample */
+// if ((voice[i].s==NULL)
+// return k;
+
+ if ((voice[i].kick==KICK_NOTHING)||(voice[i].kick==KICK_ENVELOPE)) {
+
+ priority=voice[i].total_volume<<((CPSampleManager::get_singleton()->get_loop_type( voice[i].sample_ptr->get_sample_data())!=CP_LOOP_NONE)?1:0);
+
+ if ((voice[i].has_master_channel)&&(&voice[i]==voice[i].master_channel->slave_voice)) {
+
+ priority<<=2;
+
+ }
+
+ if ((i==0) || (priority<min_priority)) {
+ min_priority=priority;
+ min_priority_chan=i;
+ }
+ }
+ }
+
+ if (min_priority>8000*7) return -1; /* what the fuck is this? */
+
+ return min_priority_chan;
+}
+
diff --git a/modules/chibi/cp_sample.cpp b/modules/chibi/cp_sample.cpp
new file mode 100644
index 0000000000..55c2c910a5
--- /dev/null
+++ b/modules/chibi/cp_sample.cpp
@@ -0,0 +1,203 @@
+/*************************************************************************/
+/* cp_sample.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_sample.h"
+
+const char * CPSample::get_name() const {
+
+ return name;
+}
+void CPSample::set_name(const char *p_name) {
+
+ if (p_name==NULL) {
+ name[0]=0;
+ return;
+ }
+
+
+ bool done=false;
+ for (int i=0;i<NAME_MAX_LEN;i++) {
+
+
+ name[i]=done?0:p_name[i];
+ if (!done && p_name[i]==0)
+ done=true;
+ }
+
+ name[NAME_MAX_LEN-1]=0; /* just in case */
+
+}
+
+void CPSample::set_default_volume(uint8_t p_vol) {
+
+ default_volume=p_vol;
+}
+uint8_t CPSample::get_default_volume() const{
+
+ return default_volume;
+}
+
+void CPSample::set_global_volume(uint8_t p_vol) {
+
+ global_volume=p_vol;
+}
+uint8_t CPSample::get_global_volume() const{
+
+ return global_volume;
+}
+
+void CPSample::set_pan_enabled(bool p_vol) {
+
+ pan_enabled=p_vol;
+}
+bool CPSample::is_pan_enabled() const{
+
+ return pan_enabled;
+}
+
+void CPSample::set_pan(uint8_t p_pan) {
+
+ pan=p_pan;
+
+}
+uint8_t CPSample::get_pan() const{
+
+ return pan;
+}
+
+
+void CPSample::set_vibrato_type(VibratoType p_vibrato_type) {
+
+ vibrato_type=p_vibrato_type;
+}
+CPSample::VibratoType CPSample::get_vibrato_type() const{
+
+ return vibrato_type;
+}
+
+void CPSample::set_vibrato_speed(uint8_t p_vibrato_speed) {
+
+ vibrato_speed=p_vibrato_speed;
+}
+uint8_t CPSample::get_vibrato_speed() const {
+
+ return vibrato_speed;
+}
+
+void CPSample::set_vibrato_depth(uint8_t p_vibrato_depth) {
+
+ vibrato_depth=p_vibrato_depth;
+}
+uint8_t CPSample::get_vibrato_depth() const{
+
+ return vibrato_depth;
+}
+
+void CPSample::set_vibrato_rate(uint8_t p_vibrato_rate) {
+
+ vibrato_rate=p_vibrato_rate;
+}
+uint8_t CPSample::get_vibrato_rate() const{
+
+ return vibrato_rate;
+}
+
+void CPSample::set_sample_data(CPSample_ID p_ID) {
+
+ id=p_ID;
+}
+CPSample_ID CPSample::get_sample_data() const{
+
+ return id;
+}
+
+void CPSample::operator=(const CPSample &p_sample) {
+
+ copy_from(p_sample);
+}
+void CPSample::copy_from(const CPSample &p_sample) {
+
+ reset();
+ set_name(p_sample.get_name());
+
+ default_volume=p_sample.default_volume;
+ global_volume=p_sample.global_volume;
+
+ pan_enabled=p_sample.pan_enabled;
+ pan=p_sample.pan;
+
+ vibrato_type=p_sample.vibrato_type;
+ vibrato_speed=p_sample.vibrato_speed;
+ vibrato_depth=p_sample.vibrato_depth;
+ vibrato_rate=p_sample.vibrato_rate;
+
+ if (CPSampleManager::get_singleton() && !p_sample.id.is_null())
+ CPSampleManager::get_singleton()->copy_to( p_sample.id, id );
+}
+
+
+
+
+
+void CPSample::reset() {
+
+
+ name[0]=0;
+
+ default_volume=64;
+ global_volume=64;
+
+ pan_enabled=false;
+ pan=32;
+
+ vibrato_type=VIBRATO_SINE;
+ vibrato_speed=0;
+ vibrato_depth=0;
+ vibrato_rate=0;
+
+ if (!id.is_null() && CPSampleManager::get_singleton())
+ CPSampleManager::get_singleton()->destroy( id );
+
+ id=CPSample_ID();
+
+}
+
+CPSample::CPSample(const CPSample&p_from) {
+
+ reset();
+ copy_from(p_from);
+}
+CPSample::CPSample() {
+
+ reset();
+}
+
+CPSample::~CPSample() {
+
+ reset();
+}
diff --git a/modules/chibi/cp_sample.h b/modules/chibi/cp_sample.h
new file mode 100644
index 0000000000..4b3d218106
--- /dev/null
+++ b/modules/chibi/cp_sample.h
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* cp_sample.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CPSAMPLE_H
+#define CPSAMPLE_H
+
+
+#include "cp_config.h"
+#include "cp_sample_manager.h"
+class CPSample {
+
+public:
+ enum VibratoType {
+ VIBRATO_SINE,
+ VIBRATO_SAW,
+ VIBRATO_SQUARE,
+ VIBRATO_RANDOM
+
+ };
+
+private:
+
+ enum { NAME_MAX_LEN=26 };
+
+ char name[NAME_MAX_LEN];
+
+ uint8_t default_volume; /* 0.. 64 */
+ uint8_t global_volume; /* 0.. 64 */
+
+ bool pan_enabled;
+ uint8_t pan; /* 0.. 64 */
+
+ VibratoType vibrato_type;
+ uint8_t vibrato_speed; /* 0.. 64 */
+ uint8_t vibrato_depth; /* 0.. 64 */
+ uint8_t vibrato_rate; /* 0.. 64 */
+
+ CPSample_ID id;
+
+ void copy_from(const CPSample &p_sample);
+public:
+
+
+ void operator=(const CPSample &p_sample);
+
+ const char * get_name() const;
+ void set_name(const char *p_name);
+
+ void set_default_volume(uint8_t p_vol);
+ uint8_t get_default_volume() const;
+
+ void set_global_volume(uint8_t p_vol);
+ uint8_t get_global_volume() const;
+
+ void set_pan_enabled(bool p_vol);
+ bool is_pan_enabled() const;
+
+ void set_pan(uint8_t p_pan);
+ uint8_t get_pan() const;
+
+ void set_vibrato_type(VibratoType p_vibrato_type);
+ VibratoType get_vibrato_type() const;
+
+ void set_vibrato_speed(uint8_t p_vibrato_speed) ;
+ uint8_t get_vibrato_speed() const;
+
+ void set_vibrato_depth(uint8_t p_vibrato_depth);
+ uint8_t get_vibrato_depth() const;
+
+ void set_vibrato_rate(uint8_t p_vibrato_rate);
+ uint8_t get_vibrato_rate() const;
+
+ void set_sample_data(CPSample_ID);
+ CPSample_ID get_sample_data() const;
+
+ void reset();
+
+ CPSample(const CPSample&p_from);
+ CPSample();
+ ~CPSample();
+
+};
+
+
+
+
+#endif
diff --git a/modules/chibi/cp_sample_defs.h b/modules/chibi/cp_sample_defs.h
new file mode 100644
index 0000000000..169963c98e
--- /dev/null
+++ b/modules/chibi/cp_sample_defs.h
@@ -0,0 +1,97 @@
+/*************************************************************************/
+/* cp_sample_defs.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_SAMPLE_DEFS_H
+#define CP_SAMPLE_DEFS_H
+
+#include "cp_config.h"
+
+enum CPSample_Loop_Type {
+
+ CP_LOOP_NONE,
+ CP_LOOP_FORWARD,
+ CP_LOOP_BIDI
+};
+
+//#define INVALID_SAMPLE_ID -1
+
+#define CP_MIXING_FRAC_BITS_MACRO 13
+#define CP_MIXING_FRAC_BITS_TEXT "13"
+// 1<<9 - 1
+#define CP_MIXING_FRAC_BITS_MASK_TEXT "8191"
+
+enum CPMixConstants {
+ CP_MIXING_FRAC_BITS=CP_MIXING_FRAC_BITS_MACRO,
+ CP_MIXING_FRAC_LENGTH=(1<<CP_MIXING_FRAC_BITS),
+ CP_MIXING_FRAC_MASK=CP_MIXING_FRAC_LENGTH-1,
+ CP_MIXING_VOL_FRAC_BITS=8,
+ CP_MIXING_FREQ_FRAC_BITS=8
+};
+
+enum CPFilterConstants {
+ CP_FILTER_SHIFT=16,
+ CP_FILTER_LENGTH=(1<<CP_FILTER_SHIFT)
+};
+
+
+enum CPInterpolationType {
+ CP_INTERPOLATION_RAW,
+ CP_INTERPOLATION_LINEAR,
+ CP_INTERPOLATION_CUBIC
+};
+
+enum CPPanConstants {
+
+ CP_PAN_BITS=8, // 0 .. 256
+ CP_PAN_LEFT=0,
+ CP_PAN_RIGHT=((1<<CP_PAN_BITS)-1), // 255
+ CP_PAN_CENTER=CP_PAN_RIGHT/2, // 128
+ CP_PAN_SURROUND=512
+};
+
+enum CPMixerVolConstants {
+ CP_VOL_MAX=512,
+ CP_VOL_RAMP_BITS=9,
+ CP_VOL_SHIFT=2
+
+
+};
+
+enum CPStereoCannels {
+ CP_CHAN_LEFT,
+ CP_CHAN_RIGHT
+};
+
+#define CP_FIRST_SAMPLE_DECLICK_THRESHOLD 1000
+#define CP_FIRST_SAMPLE_RAMP_LEN 32
+
+typedef signed char CPFrame8;
+typedef signed short CPFrame16;
+
+
+#endif
diff --git a/modules/chibi/cp_sample_manager.cpp b/modules/chibi/cp_sample_manager.cpp
new file mode 100644
index 0000000000..5c2988e3f9
--- /dev/null
+++ b/modules/chibi/cp_sample_manager.cpp
@@ -0,0 +1,78 @@
+/*************************************************************************/
+/* cp_sample_manager.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_sample_manager.h"
+
+
+CPSampleManager * CPSampleManager::singleton=NULL;
+
+
+void CPSampleManager::copy_to(CPSample_ID p_from,CPSample_ID &p_to) {
+
+ ERR_FAIL_COND(!check( p_from ));
+
+
+ if (p_to.is_null()) {
+
+ p_to=create( is_16bits( p_from), is_stereo( p_from), get_size(p_from));
+ } else {
+
+ recreate( p_to, is_16bits( p_from), is_stereo( p_from), get_size(p_from));
+
+ }
+
+ int len=get_size( p_from );
+ int ch=is_stereo( p_from ) ? 2 : 1;
+
+ for (int c=0;c<ch;c++) {
+
+ for (int i=0;i<len;i++) {
+
+ int16_t s=get_data( p_from, i, c );
+ set_data( p_to, i, s, c );
+ }
+ }
+
+ set_loop_type( p_to, get_loop_type( p_from ) );
+ set_loop_begin( p_to, get_loop_begin( p_from ) );
+ set_loop_end( p_to, get_loop_end( p_from ) );
+ set_c5_freq( p_to, get_c5_freq( p_from ) );
+
+
+
+}
+
+CPSampleManager::CPSampleManager() {
+
+ singleton=this;
+}
+
+CPSampleManager *CPSampleManager::get_singleton() {
+
+ return singleton;
+}
diff --git a/modules/chibi/cp_sample_manager.h b/modules/chibi/cp_sample_manager.h
new file mode 100644
index 0000000000..74bcafc0cf
--- /dev/null
+++ b/modules/chibi/cp_sample_manager.h
@@ -0,0 +1,99 @@
+/*************************************************************************/
+/* cp_sample_manager.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_SAMPLE_MANAGER_H
+#define CP_SAMPLE_MANAGER_H
+
+#include "cp_config.h"
+#include "cp_sample_defs.h"
+
+
+
+/**
+@author Juan Linietsky
+*/
+
+
+/* abstract base CPSample_ID class */
+
+struct CPSample_ID {
+
+ void *_private;
+
+ bool operator==(const CPSample_ID&p_other) const { return _private==p_other._private; }
+ bool operator!=(const CPSample_ID&p_other) const { return _private!=p_other._private; }
+ bool is_null() const { return _private==0; }
+ CPSample_ID(void *p_private=0) { _private=p_private; };
+};
+
+
+class CPSampleManager {
+
+ static CPSampleManager * singleton;
+
+public:
+
+ /* get the singleton instance */
+ static CPSampleManager *get_singleton();
+
+ virtual void copy_to(CPSample_ID p_from,CPSample_ID &p_to); ///< if p_to is null, it gets created
+
+ virtual CPSample_ID create(bool p_16bits,bool p_stereo,int32_t p_len)=0;
+ virtual void recreate(CPSample_ID p_id,bool p_16bits,bool p_stereo,int32_t p_len)=0;
+ virtual void destroy(CPSample_ID p_id)=0;
+ virtual bool check(CPSample_ID p_id)=0; // return false if invalid
+
+ virtual void set_c5_freq(CPSample_ID p_id,int32_t p_freq)=0;
+ virtual void set_loop_begin(CPSample_ID p_id,int32_t p_begin)=0;
+ virtual void set_loop_end(CPSample_ID p_id,int32_t p_end)=0;
+ virtual void set_loop_type(CPSample_ID p_id,CPSample_Loop_Type p_type)=0;
+ virtual void set_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len)=0;
+
+
+ virtual int32_t get_loop_begin(CPSample_ID p_id)=0;
+ virtual int32_t get_loop_end(CPSample_ID p_id)=0;
+ virtual CPSample_Loop_Type get_loop_type(CPSample_ID p_id)=0;
+ virtual int32_t get_c5_freq(CPSample_ID p_id)=0;
+ virtual int32_t get_size(CPSample_ID p_id)=0;
+ virtual bool is_16bits(CPSample_ID p_id)=0;
+ virtual bool is_stereo(CPSample_ID p_id)=0;
+ virtual bool lock_data(CPSample_ID p_id)=0;
+ virtual void *get_data(CPSample_ID p_id)=0; /* WARNING: Not all sample managers
+may be able to implement this, it depends on the mixer in use! */
+ virtual int16_t get_data(CPSample_ID p_id, int p_sample, int p_channel=0)=0; /// Does not need locking
+ virtual void set_data(CPSample_ID p_id, int p_sample, int16_t p_data,int p_channel=0)=0; /// Does not need locking
+ virtual void unlock_data(CPSample_ID p_id)=0;
+
+ virtual void get_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len)=0;
+
+ CPSampleManager();
+ virtual ~CPSampleManager(){}
+
+};
+
+#endif
diff --git a/modules/chibi/cp_song.cpp b/modules/chibi/cp_song.cpp
new file mode 100644
index 0000000000..4aa1a4228d
--- /dev/null
+++ b/modules/chibi/cp_song.cpp
@@ -0,0 +1,957 @@
+/*************************************************************************/
+/* cp_song.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_song.h"
+
+void CPSong::set_name(const char *p_name) {
+
+ if (p_name==NULL) {
+ variables.name[0]=0;
+ return;
+ }
+
+
+ bool done=false;
+ for (int i=0;i<MAX_SONG_NAME;i++) {
+
+
+ variables.name[i]=done?0:p_name[i];
+ if (!done && p_name[i]==0)
+ done=true;
+ }
+
+ variables.name[MAX_SONG_NAME-1]=0; /* just in case */
+}
+
+const char * CPSong::get_name() {
+
+ return variables.name;
+
+}
+
+void CPSong::set_message(const char *p_message) {
+
+ if (p_message==NULL) {
+ variables.message[0]=0;
+ return;
+ }
+
+ bool done=false;
+ for (int i=0;i<MAX_MESSAGE_LEN;i++) {
+
+
+ variables.message[i]=done?0:p_message[i];
+ if (!done && p_message[i]==0)
+ done=true;
+ }
+
+ variables.message[MAX_MESSAGE_LEN-1]=0; /* just in case */
+}
+
+const char * CPSong::get_message() {
+
+ return variables.message;
+
+}
+
+void CPSong::set_row_highlight_minor(int p_hl_minor) {
+
+ variables.row_highlight_minor=p_hl_minor;
+}
+int CPSong::get_row_highlight_minor() {
+
+ return variables.row_highlight_minor;
+}
+
+void CPSong::set_row_highlight_major(int p_hl_major) {
+
+ variables.row_highlight_major=p_hl_major;
+
+
+} /* 0 .. 256 */
+int CPSong::get_row_highlight_major() {
+
+ return variables.row_highlight_major;
+
+
+} /* 0 .. 256 */
+
+void CPSong::set_mixing_volume(int p_mix_volume) {
+
+
+ variables.mixing_volume=p_mix_volume;
+} /* 0 .. 128 */
+int CPSong::get_mixing_volume() {
+
+ return variables.mixing_volume;
+
+} /* 0 .. 128 */
+
+void CPSong::set_global_volume(int p_global_volume) {
+
+
+ initial_variables.global_volume=p_global_volume;
+
+} /* 0 .. 128 */
+int CPSong::get_global_volume() {
+
+ return initial_variables.global_volume;
+
+} /* 0 .. 128 */
+
+void CPSong::set_stereo_separation(int p_separation) {
+
+ variables.stereo_separation=p_separation;
+
+} /* 0 .. 128 */
+int CPSong::get_stereo_separation() {
+
+ return variables.stereo_separation;
+} /* 0 .. 128 */
+
+void CPSong::set_stereo(bool p_stereo) {
+
+ variables.use_stereo=p_stereo;
+
+}
+bool CPSong::is_stereo() {
+
+ return variables.use_stereo;
+}
+
+void CPSong::set_instruments(bool p_instruments) {
+
+ variables.use_instruments=p_instruments;
+
+
+}
+bool CPSong::has_instruments() {
+
+
+ return variables.use_instruments;
+
+}
+
+void CPSong::set_linear_slides(bool p_linear_slides) {
+
+ variables.use_linear_slides=p_linear_slides;
+
+
+}
+bool CPSong::has_linear_slides() {
+
+ return variables.use_linear_slides;
+
+
+}
+
+void CPSong::set_old_effects(bool p_old_effects) {
+
+ variables.old_effects=p_old_effects;
+
+
+}
+bool CPSong::has_old_effects() {
+
+ return variables.old_effects;
+}
+
+void CPSong::set_compatible_gxx(bool p_compatible_gxx) {
+
+
+ variables.compatible_gxx=p_compatible_gxx;
+}
+bool CPSong::has_compatible_gxx() {
+
+ return variables.compatible_gxx;
+
+}
+
+void CPSong::set_speed(int p_speed) {
+
+ CP_ERR_COND(p_speed<MIN_SPEED);
+ CP_ERR_COND(p_speed>MAX_SPEED);
+
+ initial_variables.speed=p_speed;
+
+} /* 1 .. 255 */
+int CPSong::get_speed() {
+
+ return initial_variables.speed;
+
+} /* 1 .. 255 */
+
+void CPSong::set_tempo(int p_tempo) {
+
+ CP_ERR_COND( p_tempo<MIN_TEMPO );
+ CP_ERR_COND( p_tempo>MAX_TEMPO );
+
+ initial_variables.tempo=p_tempo;
+
+} /* MIN_TEMPO .. MAX_TEMPO */
+int CPSong::get_tempo() {
+
+ return initial_variables.tempo;
+
+
+} /* MIN_TEMPO .. MAX_TEMPO */
+
+void CPSong::set_channel_pan(int p_channel,int p_pan) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ CP_FAIL_INDEX(p_pan,CHANNEL_MAX_PAN+1);
+
+ initial_variables.channel[p_channel].pan=p_pan;
+
+} /* 0 .. CHANNEL_MAX_PAN */
+int CPSong::get_channel_pan(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
+
+ return initial_variables.channel[p_channel].pan;
+}
+
+void CPSong::set_channel_volume(int p_channel,int p_volume) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ CP_FAIL_INDEX(p_volume,CHANNEL_MAX_VOLUME+1);
+
+
+ initial_variables.channel[p_channel].volume=p_volume;
+
+
+} /* 0 .. CHANNEL_MAX_VOLUME */
+
+
+int CPSong::get_channel_volume(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
+
+ return initial_variables.channel[p_channel].volume;
+
+}
+
+void CPSong::set_channel_chorus(int p_channel,int p_chorus) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ CP_FAIL_INDEX(p_chorus,CHANNEL_MAX_CHORUS+1);
+
+
+ initial_variables.channel[p_channel].chorus=p_chorus;
+
+
+} /* 0 .. CHANNEL_MAX_CHORUS */
+
+
+int CPSong::get_channel_chorus(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
+
+ return initial_variables.channel[p_channel].chorus;
+
+}
+
+void CPSong::set_channel_reverb(int p_channel,int p_reverb) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ CP_FAIL_INDEX(p_reverb,CHANNEL_MAX_REVERB+1);
+
+
+ initial_variables.channel[p_channel].reverb=p_reverb;
+
+
+} /* 0 .. CHANNEL_MAX_CHORUS */
+
+
+int CPSong::get_channel_reverb(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
+
+ return initial_variables.channel[p_channel].reverb;
+
+}
+
+void CPSong::set_channel_surround(int p_channel,bool p_surround) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ initial_variables.channel[p_channel].surround=p_surround;
+
+}
+bool CPSong::is_channel_surround(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,false);
+
+ return initial_variables.channel[p_channel].surround;
+
+
+}
+
+void CPSong::set_channel_mute(int p_channel,bool p_mute) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+
+ initial_variables.channel[p_channel].mute=p_mute;
+
+}
+bool CPSong::is_channel_mute(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,false);
+
+ return initial_variables.channel[p_channel].mute;
+
+}
+
+/* arrays of stuff */
+
+CPPattern* CPSong::get_pattern(int p_pattern) {
+
+ CP_FAIL_INDEX_V(p_pattern,MAX_PATTERNS, NULL);
+
+ return &pattern[p_pattern];
+
+}
+CPSample* CPSong::get_sample(int p_sample) {
+
+ CP_FAIL_INDEX_V(p_sample,MAX_SAMPLES,NULL);
+
+ return &sample[p_sample];
+
+
+}
+CPInstrument* CPSong::get_instrument(int p_instrument) {
+
+
+ CP_FAIL_INDEX_V(p_instrument,MAX_INSTRUMENTS,NULL);
+
+ return &instrument[p_instrument];
+
+}
+
+int CPSong::get_order(int p_order) {
+
+ CP_FAIL_INDEX_V(p_order,MAX_ORDERS,CP_ORDER_NONE);
+
+
+ return order[p_order];
+
+}
+void CPSong::set_order(int p_order,int p_pattern) {
+
+ CP_FAIL_INDEX(p_order,MAX_ORDERS);
+
+ order[p_order]=p_pattern;
+
+}
+
+
+void CPSong::clear_instrument_with_samples(int p_instrument) {
+
+ CPInstrument *ins = get_instrument( p_instrument );
+ if (!ins)
+ return;
+
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ CPSample *s=get_sample( ins->get_sample_number( i ) );
+
+ if (!s)
+ continue;
+
+ if (s->get_sample_data().is_null())
+ continue;
+
+ s->reset();
+ }
+ ins->reset();
+}
+
+void CPSong::make_instrument_from_sample(int p_sample) {
+
+ if (!has_instruments())
+ return;
+ CP_ERR_COND(!get_sample( p_sample ));
+
+ for (int i=0;i<MAX_INSTRUMENTS;i++) {
+
+
+ CPInstrument *ins=get_instrument(i);
+
+ bool empty_slot=true;
+ for (int n=0;n<CPNote::NOTES;n++) {
+
+ if (ins->get_sample_number(n)<MAX_SAMPLES) {
+
+ empty_slot=false;
+ break;
+ }
+ }
+
+ if (!empty_slot)
+ continue;
+
+ for (int n=0;n<CPNote::NOTES;n++) {
+
+ ins->set_sample_number(n,p_sample);
+ ins->set_note_number(n,n);
+ }
+
+ ins->set_name( get_sample( p_sample )->get_name() );
+ break;
+ }
+
+}
+
+void CPSong::make_instruments_from_samples() {
+
+ for (int i=0;i<MAX_SAMPLES;i++) {
+
+ CPInstrument *ins=get_instrument( i );
+
+ if (!ins)
+ continue;
+
+ ins->reset();
+
+ CPSample *s=get_sample( i );
+
+ if (!s)
+ continue;
+
+ ins->set_name( s->get_name() );
+
+ if (s->get_sample_data().is_null())
+ continue;
+
+
+
+
+ for(int j=0;j<CPNote::NOTES;j++)
+ ins->set_sample_number( j, i );
+
+
+
+ }
+}
+
+void CPSong::reset(bool p_clear_patterns,bool p_clear_samples,bool p_clear_instruments,bool p_clear_variables) {
+
+ if (p_clear_variables) {
+ variables.name[0]=0;
+ variables.message[0]=0;
+ variables.row_highlight_major=16;
+ variables.row_highlight_minor=4;
+ variables.mixing_volume=48;
+ variables.old_effects=false;
+ if (p_clear_instruments) //should not be cleared, if not clearing instruments!!
+ variables.use_instruments=false;
+ variables.stereo_separation=128;
+ variables.use_linear_slides=true;
+ variables.use_stereo=true;
+
+ initial_variables.global_volume=128;
+ initial_variables.speed=6;
+ initial_variables.tempo=125;
+
+ for (int i=0;i<CPPattern::WIDTH;i++) {
+
+ initial_variables.channel[i].pan=32;
+ initial_variables.channel[i].volume=CHANNEL_MAX_VOLUME;
+ initial_variables.channel[i].mute=false;
+ initial_variables.channel[i].surround=false;
+ initial_variables.channel[i].chorus=0;
+ initial_variables.channel[i].reverb=0;
+
+ }
+
+ effects.chorus.delay_ms=6;
+ effects.chorus.separation_ms=3;
+ effects.chorus.depth_ms10=6,
+ effects.chorus.speed_hz10=5;
+ effects.reverb_mode=REVERB_MODE_ROOM;
+ }
+
+ if (p_clear_samples) {
+ for (int i=0;i<MAX_SAMPLES;i++)
+ get_sample(i)->reset();
+ }
+
+ if (p_clear_instruments) {
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ get_instrument(i)->reset();
+ }
+
+ if (p_clear_patterns) {
+ for (int i=0;i<MAX_PATTERNS;i++)
+ get_pattern(i)->clear();
+
+ for (int i=0;i<MAX_ORDERS;i++)
+ set_order( i, CP_ORDER_NONE );
+ }
+
+
+}
+
+
+CPSong::ReverbMode CPSong::get_reverb_mode() {
+
+ return effects.reverb_mode;
+}
+void CPSong::set_reverb_mode(ReverbMode p_mode) {
+
+ effects.reverb_mode=p_mode;
+}
+
+void CPSong::set_chorus_delay_ms(int p_amount) {
+
+ effects.chorus.delay_ms=p_amount;
+}
+void CPSong::set_chorus_separation_ms(int p_amount) {
+
+ effects.chorus.separation_ms=p_amount;
+
+}
+void CPSong::set_chorus_depth_ms10(int p_amount) {
+
+ effects.chorus.depth_ms10=p_amount;
+
+}
+void CPSong::set_chorus_speed_hz10(int p_amount) {
+
+ effects.chorus.speed_hz10=p_amount;
+
+}
+
+int CPSong::get_chorus_delay_ms() {
+
+ return effects.chorus.delay_ms;
+
+}
+int CPSong::get_chorus_separation_ms() {
+
+ return effects.chorus.separation_ms;
+}
+int CPSong::get_chorus_depth_ms10() {
+
+ return effects.chorus.depth_ms10;
+
+}
+int CPSong::get_chorus_speed_hz10() {
+
+ return effects.chorus.speed_hz10;
+
+}
+
+void CPSong::cleanup_unused_patterns() {
+
+ for (int i=0;i<MAX_PATTERNS;i++) {
+
+ bool used=false;
+ if (get_pattern(i)->is_empty())
+ continue;
+
+ for (int j=0;j<MAX_ORDERS;j++) {
+
+ if (get_order(j)==i) {
+ used=true;
+
+ }
+ }
+
+ if (!used)
+ get_pattern(i)->clear();
+ }
+
+}
+void CPSong::cleanup_unused_instruments(){
+
+ if (!has_instruments())
+ return;
+
+ bool instr_found[MAX_INSTRUMENTS];
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ instr_found[i]=false;
+
+ for (int i=0;i<MAX_PATTERNS;i++) {
+
+ if (get_pattern(i)->is_empty())
+ continue;
+
+ for (int row=0;row<get_pattern(i)->get_length();row++) {
+
+
+ for (int col=0;col<CPPattern::WIDTH;col++) {
+
+ CPNote n;
+ n=get_pattern(i)->get_note( col,row );
+
+ if (n.instrument<MAX_INSTRUMENTS)
+ instr_found[n.instrument]=true;
+
+ }
+
+ }
+
+ }
+
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ if (!instr_found[i])
+ get_instrument(i)->reset();
+
+
+}
+void CPSong::cleanup_unused_samples(){
+
+ if (!has_instruments())
+ return;
+
+ bool sample_found[MAX_SAMPLES];
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ sample_found[i]=false;
+
+ for (int i=0;i<MAX_PATTERNS;i++) {
+
+ if (get_pattern(i)->is_empty())
+ continue;
+
+
+ for (int row=0;row<get_pattern(i)->get_length();row++) {
+
+
+ for (int col=0;col<CPPattern::WIDTH;col++) {
+
+ CPNote n;
+ n=get_pattern(i)->get_note( col,row );
+
+ if (n.instrument>=MAX_SAMPLES)
+ continue;
+
+ if (has_instruments()) {
+
+ for (int nt=0;nt<CPNote::NOTES;nt++) {
+
+ int smp=get_instrument(n.instrument)->get_sample_number(nt);
+ if (smp<MAX_SAMPLES)
+ sample_found[smp]=true;
+ }
+
+ } else {
+ if (n.instrument<MAX_SAMPLES)
+ sample_found[n.instrument]=true;
+ }
+
+ }
+
+ }
+
+ }
+
+ for (int i=0;i<MAX_SAMPLES;i++)
+ if (!sample_found[i])
+ get_sample(i)->reset();
+
+}
+void CPSong::cleanup_unused_orders(){
+
+ bool finito=false;
+ for (int j=0;j<MAX_ORDERS;j++) {
+
+
+ if (get_order(j)==CP_ORDER_NONE)
+ finito=true;
+ if (finito)
+ set_order(j,CP_ORDER_NONE);
+
+ }
+
+}
+
+void CPSong::clear_all_default_pan() {
+
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ get_instrument(i)->set_pan_default_enabled( false ); //die!
+
+ for (int i=0;i<MAX_SAMPLES;i++)
+ get_sample(i)->set_pan_enabled( false ); //die!
+
+}
+
+
+void CPSong::clear_all_default_vol(){
+
+ for (int i=0;i<MAX_SAMPLES;i++)
+ get_sample(i)->set_default_volume( 64 ); //die!
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ get_instrument(i)->set_volume_global_amount( CPInstrument::MAX_VOLUME );
+
+}
+
+
+int CPSong::get_order_in_use_count() {
+
+
+ int order_count = 0;
+
+ for (int i=(MAX_ORDERS-1);i>=0;i--) {
+
+
+ if (get_order(i)!=CP_ORDER_NONE) {
+ order_count=i+1;
+ break;
+ }
+ }
+
+ return order_count;
+}
+int CPSong::get_pattern_in_use_count() {
+
+
+ int pattern_count=0;
+
+ for (int i=(CPSong::MAX_PATTERNS-1);i>=0;i--) {
+
+
+ if (!get_pattern(i)->is_empty()) {
+ pattern_count=i+1;
+ break;
+ }
+ }
+
+ return pattern_count;
+}
+
+int CPSong::get_instrument_in_use_count() {
+
+ int instrument_count=0;
+
+ for (int i=(CPSong::MAX_INSTRUMENTS-1);i>=0;i--) {
+
+ CPInstrument *ins = get_instrument(i);
+ bool in_use=false;
+
+ for (int s = 0 ; s < CPNote::NOTES ; s++ ) {
+
+ int smp_idx = ins->get_sample_number(s);
+ if (smp_idx<0 || smp_idx>=CPSong::MAX_SAMPLES)
+ continue;
+
+ if (!get_sample(smp_idx)->get_sample_data().is_null()) {
+ in_use=true;
+ break;
+ }
+
+ }
+
+ if (in_use) {
+ instrument_count=i+1;
+ break;
+ }
+ }
+
+ return instrument_count;
+}
+#include <stdio.h>
+int CPSong::get_channels_in_use() {
+
+ int max=0;
+
+ for (int p=0;p<CPSong::MAX_PATTERNS;p++) {
+
+ CPPattern *pat = get_pattern(p);
+ if (pat->is_empty())
+ continue;
+
+
+ for (int c=(CPPattern::WIDTH-1);c>=0;c--) {
+
+ if (c<max)
+ break;
+
+ bool has_note=false;
+ for (int r=0;r<pat->get_length();r++) {
+
+ CPNote n = pat->get_note( c, r );
+ if (!n.is_empty()) {
+ has_note=true;
+ break;
+ }
+ }
+
+ if (has_note) {
+
+ max=c+1;
+ }
+ }
+ }
+
+ return max;
+}
+
+
+void CPSong::separate_in_one_sample_instruments(int p_instrument) {
+
+ CP_ERR_COND( !variables.use_instruments );
+ CP_FAIL_INDEX( p_instrument, MAX_INSTRUMENTS );
+
+ int remapped_count=0;
+
+ signed char remap[MAX_SAMPLES];
+
+ for (int i=0;i<MAX_SAMPLES;i++) {
+
+ remap[i]=-1;
+ }
+
+ /* Find remaps */
+ CPInstrument *ins=get_instrument(p_instrument);
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ int sn = ins->get_sample_number(i);
+
+ // check for unusable sample
+ if (sn<0 || sn>=MAX_SAMPLES || get_sample(sn)->get_sample_data().is_null())
+ continue;
+ printf("sample %i\n",sn);
+ if ( remap[sn] !=-1 ) {
+ printf("already mapped to %i\n",remap[sn]);
+ continue;
+ }
+
+ printf("isn't remapped\n");
+
+ // find remap
+
+ for (int j=0;j<MAX_INSTRUMENTS;j++) {
+
+ if (!get_instrument(j)->is_empty())
+ continue;
+
+ printf("map to %i\n",j);
+
+ //copy
+ *get_instrument(j)=*ins;
+
+ // assign samples
+ for (int k=0;k<CPNote::NOTES;k++) {
+
+ get_instrument(j)->set_note_number(k,k);
+ get_instrument(j)->set_sample_number(k,sn);
+ }
+ remap[sn]=j;
+ remapped_count++;
+ break;
+ }
+
+ CP_ERR_COND(remap[sn]==-1); // no more free instruments
+ }
+
+ printf("remapped %i\n",remapped_count);
+
+ if (remapped_count<2) {
+ //undo if only one is remapped
+ for (int i=0;i<MAX_SAMPLES;i++) {
+
+ if (remap[i]!=-1) {
+
+ get_instrument(remap[i])->reset();
+ }
+ }
+ return;
+ }
+
+ /* remap all song */
+
+ for (int p=0;p<CPSong::MAX_PATTERNS;p++) {
+
+ CPPattern *pat = get_pattern(p);
+ if (pat->is_empty())
+ continue;
+
+
+ for (int c=0;c<CPPattern::WIDTH;c++) {
+
+ for (int r=0;r<pat->get_length();r++) {
+
+ CPNote n = pat->get_note(c,r);
+ if (n.note<CPNote::NOTES && n.instrument==p_instrument) {
+
+ int sn = ins->get_sample_number(n.note);
+ if (remap[sn]==-1)
+ pat->set_note(c,r,CPNote());
+ else {
+
+ n.instrument=remap[sn];
+ pat->set_note(c,r,n);
+ }
+ }
+ }
+ }
+ }
+
+ ins->reset();
+
+}
+
+
+CPSong::CPSong() {
+
+ reset();
+}
+CPSong::~CPSong() {
+
+}
+
+
+
+
+int get_song_next_order_idx(CPSong *p_song, int p_order_idx) {
+
+ int baseorder,order_counter;
+
+ order_counter=-1;
+
+ baseorder=p_order_idx;
+
+ do {
+
+ baseorder++;
+ if ( baseorder>(CPSong::MAX_ORDERS-1) ) baseorder=0;
+ order_counter++;
+
+ } while ( (p_song->get_order(baseorder)>=(CPSong::MAX_PATTERNS) ) && (order_counter<CPSong::MAX_ORDERS) );
+
+
+ if (order_counter==CPSong::MAX_ORDERS) {
+
+ return -1;
+
+ } else {
+
+ return baseorder;
+ }
+
+}
diff --git a/modules/chibi/cp_song.h b/modules/chibi/cp_song.h
new file mode 100644
index 0000000000..da5d106a63
--- /dev/null
+++ b/modules/chibi/cp_song.h
@@ -0,0 +1,261 @@
+/*************************************************************************/
+/* cp_song.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CPSONG_H
+#define CPSONG_H
+
+
+/**CPSong Class
+ *@author Juan Linietsky
+ */
+
+#include "cp_order.h"
+#include "cp_pattern.h"
+#include "cp_sample.h"
+#include "cp_instrument.h"
+
+class CPSong {
+public:
+ enum {
+ MAX_SONG_NAME=26,
+ MAX_ORDERS=200,
+ MAX_PATTERNS=200,
+ MAX_SAMPLES=99,
+ MAX_INSTRUMENTS=99,
+
+ CHANNEL_MAX_PAN=64,
+ CHANNEL_MAX_VOLUME=64,
+ CHANNEL_MAX_CHORUS=64,
+ CHANNEL_MAX_REVERB=64,
+
+ MIN_TEMPO=31,
+ MAX_TEMPO=255,
+ MIN_SPEED=1,
+ MAX_SPEED=255,
+ MAX_MESSAGE_LEN=8000,
+
+
+
+ };
+
+ enum ReverbMode {
+ REVERB_MODE_ROOM,
+ REVERB_MODE_STUDIO_SMALL,
+ REVERB_MODE_STUDIO_MEDIUM,
+ REVERB_MODE_STUDIO_LARGE,
+ REVERB_MODE_HALL,
+ REVERB_MODE_SPACE_ECHO,
+ REVERB_MODE_ECHO,
+ REVERB_MODE_DELAY,
+ REVERB_MODE_HALF_ECHO
+ };
+
+private:
+ CPOrder order[MAX_ORDERS];
+ CPPattern pattern[MAX_PATTERNS];
+ CPSample sample[MAX_SAMPLES];
+ CPInstrument instrument[MAX_INSTRUMENTS];
+
+
+
+ struct Song_Variables { // variables that wont change in playback
+
+ char name[MAX_SONG_NAME];
+ char message[MAX_MESSAGE_LEN];
+ /* string message; */
+
+ int row_highlight_minor;
+ int row_highlight_major;
+
+ int mixing_volume;
+ int stereo_separation;
+
+ bool use_stereo;
+ bool use_instruments;
+ bool use_linear_slides;
+
+ bool old_effects;
+ bool compatible_gxx;
+
+ } variables;
+
+ struct Initial_Variables { // Initial values used for playback
+
+ struct Channel_State {
+
+ int pan,volume; // 0-- CHANNEL_MAX_PAN, CHANNEL_MAX_VOLUME
+ bool surround;
+ bool mute;
+ int chorus; //0 - 64
+ int reverb; //0 - 64
+ };
+
+ int global_volume;
+ int speed;
+ int tempo;
+
+ Channel_State channel[CPPattern::WIDTH];
+ } initial_variables;
+
+ struct Effects {
+
+ ReverbMode reverb_mode;
+
+ struct Chorus {
+
+ int delay_ms;
+ int separation_ms;
+ int depth_ms10;
+ int speed_hz10;
+ } chorus;
+
+ } effects;
+
+public:
+
+ /* Properties */
+
+ const char *get_name();
+ void set_name(const char *p_name);
+
+ const char *get_message();
+ void set_message(const char *p_message);
+
+ void set_row_highlight_minor(int p_hl_minor); /* 0 .. 256 */
+ int get_row_highlight_minor(); /* 0 .. 256 */
+
+ void set_row_highlight_major(int p_hl_major); /* 0 .. 256 */
+ int get_row_highlight_major(); /* 0 .. 256 */
+
+ void set_mixing_volume(int p_mix_volume); /* 0 .. 128 */
+ int get_mixing_volume(); /* 0 .. 128 */
+
+ void set_global_volume(int p_global_volume); /* 0 .. 128 */
+ int get_global_volume(); /* 0 .. 128 */
+
+ void set_stereo_separation(int p_separation); /* 0 .. 128 */
+ int get_stereo_separation(); /* 0 .. 128 */
+
+ void set_stereo(bool p_stereo);
+ bool is_stereo();
+
+ void set_instruments(bool p_instruments);
+ bool has_instruments();
+
+ void set_linear_slides(bool p_linear_slides);
+ bool has_linear_slides();
+
+ void set_old_effects(bool p_old_effects);
+ bool has_old_effects();
+
+ void set_compatible_gxx(bool p_compatible_gxx);
+ bool has_compatible_gxx();
+
+ void set_speed(int p_speed); /* 1 .. 255 */
+ int get_speed(); /* 1 .. 255 */
+
+ void set_tempo(int p_tempo); /* 31 .. 255 */
+ int get_tempo(); /* 31 .. 255 */
+
+ void set_channel_pan(int p_channel,int p_pan); /* 0 .. 64 */
+ int get_channel_pan(int p_channel);
+
+ void set_channel_volume(int p_channel,int p_volume); /* 0 .. 64 */
+ int get_channel_volume(int p_channel);
+
+ void set_channel_surround(int p_channel,bool p_surround);
+ bool is_channel_surround(int p_channel);
+
+ void set_channel_mute(int p_channel,bool p_mute);
+ bool is_channel_mute(int p_channel);
+
+ void set_channel_chorus(int p_channel,int p_chorus); /* 0 .. 64 */
+ int get_channel_chorus(int p_channel);
+
+ void set_channel_reverb(int p_channel,int p_reverb); /* 0 .. 64 */
+ int get_channel_reverb(int p_channel);
+
+ /* arrays of stuff */
+
+ CPPattern* get_pattern(int p_pattern);
+ CPSample* get_sample(int p_sample);
+ CPInstrument* get_instrument(int p_instrument);
+
+ int get_order(int p_position);
+ void set_order(int p_position,int p_order);
+
+
+ /* Effects */
+
+ ReverbMode get_reverb_mode();
+ void set_reverb_mode(ReverbMode p_mode);
+
+ void set_chorus_delay_ms(int p_amount);
+ void set_chorus_separation_ms(int p_amount);
+ void set_chorus_depth_ms10(int p_amount);
+ void set_chorus_speed_hz10(int p_amount);
+
+ int get_chorus_delay_ms();
+ int get_chorus_separation_ms();
+ int get_chorus_depth_ms10();
+ int get_chorus_speed_hz10();
+
+ /* utils */
+
+ void reset(bool p_clear_patterns=true,bool p_clear_samples=true,bool p_clear_instruments=true,bool p_clear_variables=true);
+
+ void cleanup_unused_patterns();
+ void cleanup_unused_instruments();
+ void cleanup_unused_samples();
+ void cleanup_unused_orders();
+ void clear_all_default_pan();
+ void clear_all_default_vol();
+
+ void clear_instrument_with_samples(int p_instrument);
+
+ void make_instruments_from_samples();
+ void make_instrument_from_sample(int p_sample);
+
+ void separate_in_one_sample_instruments(int p_instrument);
+
+ int get_order_in_use_count();
+ int get_pattern_in_use_count();
+ int get_instrument_in_use_count();
+ int get_channels_in_use();
+
+ CPSong();
+ ~CPSong();
+
+};
+
+
+/* Some helper for something used a lot */
+
+int get_song_next_order_idx(CPSong *p_song, int p_order_idx);
+
+#endif
diff --git a/modules/chibi/cp_tables.cpp b/modules/chibi/cp_tables.cpp
new file mode 100644
index 0000000000..8c62150f31
--- /dev/null
+++ b/modules/chibi/cp_tables.cpp
@@ -0,0 +1,254 @@
+/*************************************************************************/
+/* cp_tables.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_tables.h"
+
+int32_t CPTables::linear_period_to_freq_tab[768]={
+
+ 535232,534749,534266,533784,533303,532822,532341,531861,
+ 531381,530902,530423,529944,529466,528988,528511,528034,
+ 527558,527082,526607,526131,525657,525183,524709,524236,
+ 523763,523290,522818,522346,521875,521404,520934,520464,
+ 519994,519525,519057,518588,518121,517653,517186,516720,
+ 516253,515788,515322,514858,514393,513929,513465,513002,
+ 512539,512077,511615,511154,510692,510232,509771,509312,
+ 508852,508393,507934,507476,507018,506561,506104,505647,
+ 505191,504735,504280,503825,503371,502917,502463,502010,
+ 501557,501104,500652,500201,499749,499298,498848,498398,
+ 497948,497499,497050,496602,496154,495706,495259,494812,
+ 494366,493920,493474,493029,492585,492140,491696,491253,
+ 490809,490367,489924,489482,489041,488600,488159,487718,
+ 487278,486839,486400,485961,485522,485084,484647,484210,
+ 483773,483336,482900,482465,482029,481595,481160,480726,
+ 480292,479859,479426,478994,478562,478130,477699,477268,
+ 476837,476407,475977,475548,475119,474690,474262,473834,
+ 473407,472979,472553,472126,471701,471275,470850,470425,
+ 470001,469577,469153,468730,468307,467884,467462,467041,
+ 466619,466198,465778,465358,464938,464518,464099,463681,
+ 463262,462844,462427,462010,461593,461177,460760,460345,
+ 459930,459515,459100,458686,458272,457859,457446,457033,
+ 456621,456209,455797,455386,454975,454565,454155,453745,
+ 453336,452927,452518,452110,451702,451294,450887,450481,
+ 450074,449668,449262,448857,448452,448048,447644,447240,
+ 446836,446433,446030,445628,445226,444824,444423,444022,
+ 443622,443221,442821,442422,442023,441624,441226,440828,
+ 440430,440033,439636,439239,438843,438447,438051,437656,
+ 437261,436867,436473,436079,435686,435293,434900,434508,
+ 434116,433724,433333,432942,432551,432161,431771,431382,
+ 430992,430604,430215,429827,429439,429052,428665,428278,
+ 427892,427506,427120,426735,426350,425965,425581,425197,
+ 424813,424430,424047,423665,423283,422901,422519,422138,
+ 421757,421377,420997,420617,420237,419858,419479,419101,
+ 418723,418345,417968,417591,417214,416838,416462,416086,
+ 415711,415336,414961,414586,414212,413839,413465,413092,
+ 412720,412347,411975,411604,411232,410862,410491,410121,
+ 409751,409381,409012,408643,408274,407906,407538,407170,
+ 406803,406436,406069,405703,405337,404971,404606,404241,
+ 403876,403512,403148,402784,402421,402058,401695,401333,
+ 400970,400609,400247,399886,399525,399165,398805,398445,
+ 398086,397727,397368,397009,396651,396293,395936,395579,
+ 395222,394865,394509,394153,393798,393442,393087,392733,
+ 392378,392024,391671,391317,390964,390612,390259,389907,
+ 389556,389204,388853,388502,388152,387802,387452,387102,
+ 386753,386404,386056,385707,385359,385012,384664,384317,
+ 383971,383624,383278,382932,382587,382242,381897,381552,
+ 381208,380864,380521,380177,379834,379492,379149,378807,
+ 378466,378124,377783,377442,377102,376762,376422,376082,
+ 375743,375404,375065,374727,374389,374051,373714,373377,
+ 373040,372703,372367,372031,371695,371360,371025,370690,
+ 370356,370022,369688,369355,369021,368688,368356,368023,
+ 367691,367360,367028,366697,366366,366036,365706,365376,
+ 365046,364717,364388,364059,363731,363403,363075,362747,
+ 362420,362093,361766,361440,361114,360788,360463,360137,
+ 359813,359488,359164,358840,358516,358193,357869,357547,
+ 357224,356902,356580,356258,355937,355616,355295,354974,
+ 354654,354334,354014,353695,353376,353057,352739,352420,
+ 352103,351785,351468,351150,350834,350517,350201,349885,
+ 349569,349254,348939,348624,348310,347995,347682,347368,
+ 347055,346741,346429,346116,345804,345492,345180,344869,
+ 344558,344247,343936,343626,343316,343006,342697,342388,
+ 342079,341770,341462,341154,340846,340539,340231,339924,
+ 339618,339311,339005,338700,338394,338089,337784,337479,
+ 337175,336870,336566,336263,335959,335656,335354,335051,
+ 334749,334447,334145,333844,333542,333242,332941,332641,
+ 332341,332041,331741,331442,331143,330844,330546,330247,
+ 329950,329652,329355,329057,328761,328464,328168,327872,
+ 327576,327280,326985,326690,326395,326101,325807,325513,
+ 325219,324926,324633,324340,324047,323755,323463,323171,
+ 322879,322588,322297,322006,321716,321426,321136,320846,
+ 320557,320267,319978,319690,319401,319113,318825,318538,
+ 318250,317963,317676,317390,317103,316817,316532,316246,
+ 315961,315676,315391,315106,314822,314538,314254,313971,
+ 313688,313405,313122,312839,312557,312275,311994,311712,
+ 311431,311150,310869,310589,310309,310029,309749,309470,
+ 309190,308911,308633,308354,308076,307798,307521,307243,
+ 306966,306689,306412,306136,305860,305584,305308,305033,
+ 304758,304483,304208,303934,303659,303385,303112,302838,
+ 302565,302292,302019,301747,301475,301203,300931,300660,
+ 300388,300117,299847,299576,299306,299036,298766,298497,
+ 298227,297958,297689,297421,297153,296884,296617,296349,
+ 296082,295815,295548,295281,295015,294749,294483,294217,
+ 293952,293686,293421,293157,292892,292628,292364,292100,
+ 291837,291574,291311,291048,290785,290523,290261,289999,
+ 289737,289476,289215,288954,288693,288433,288173,287913,
+ 287653,287393,287134,286875,286616,286358,286099,285841,
+ 285583,285326,285068,284811,284554,284298,284041,283785,
+ 283529,283273,283017,282762,282507,282252,281998,281743,
+ 281489,281235,280981,280728,280475,280222,279969,279716,
+ 279464,279212,278960,278708,278457,278206,277955,277704,
+ 277453,277203,276953,276703,276453,276204,275955,275706,
+ 275457,275209,274960,274712,274465,274217,273970,273722,
+ 273476,273229,272982,272736,272490,272244,271999,271753,
+ 271508,271263,271018,270774,270530,270286,270042,269798,
+ 269555,269312,269069,268826,268583,268341,268099,267857
+};
+
+uint16_t CPTables::old_period_table[OCTAVE*2]={
+
+ 0x6b00, 0x6800, 0x6500, 0x6220, 0x5f50, 0x5c80,
+ 0x5a00, 0x5740, 0x54d0, 0x5260, 0x5010, 0x4dc0,
+ 0x4b90, 0x4960, 0x4750, 0x4540, 0x4350, 0x4160,
+ 0x3f90, 0x3dc0, 0x3c10, 0x3a40, 0x38b0, 0x3700
+};
+
+#define LOGFAC 2*16
+
+uint16_t CPTables::log_table[104]= {
+ LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887,
+ LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862,
+ LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838,
+ LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814,
+ LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791,
+ LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768,
+ LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746,
+ LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725,
+ LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704,
+ LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684,
+ LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665,
+ LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646,
+ LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628,
+ LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610,
+ LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592,
+ LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575,
+ LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559,
+ LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543,
+ LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528,
+ LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513,
+ LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498,
+ LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484,
+ LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470,
+ LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457,
+ LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443,
+ LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431
+};
+
+
+
+int32_t CPTables::get_linear_period(uint16_t note,int32_t fine) {
+
+ int32_t t;
+
+ t=(24L*OCTAVE-(int32_t)note)*32L-(fine>>1);
+ return t;
+}
+
+static int s3m_period_table[12]={1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907};
+
+
+int32_t CPTables::get_log_period(uint16_t note,int32_t p_c5freq) {
+
+ return (8363L * 16 * s3m_period_table[note%12] >> (note/12)) / p_c5freq;
+
+}
+
+/*
+int32_t CPTables::get_log_period(uint16_t note,int32_t p_c5freq)
+{
+ uint16_t n,o;
+ uint16_t p1,p2;
+ int32_t i;
+
+ n=note%(2*OCTAVE);
+ o=note/(2*OCTAVE);
+ i=(n<<2); // n*8 + fine/16
+
+ if (i<0)
+ i=0;
+
+ if (i>102)
+ i=102;
+
+
+ p1=log_table[i];
+ p2=log_table[i+1];
+
+
+ return (Interpolate(fine>>4,0,15,p1,p2)>>o);
+
+} */
+
+int32_t CPTables::get_old_period(uint16_t note,int32_t speed) {
+
+ uint16_t n,o,res;
+
+// if (!speed) {
+
+ // return 4242; /* <- prevent divide overflow */
+ // }
+
+ n=note%(2*OCTAVE);
+ o=note/(2*OCTAVE);
+
+ res=((8363L*(int32_t)old_period_table[n])>>o)/((old_period_table[17]>>1)+(speed<<2)); /*/(128-speed)*/;
+
+ return res;
+}
+
+int32_t CPTables::get_linear_frequency(int32_t period) {
+
+ int32_t shift_value=(((int32_t)period/768)-2);
+ if (shift_value>0) {
+
+ return linear_period_to_freq_tab[period%768]>>shift_value;
+ } else {
+ shift_value=0-shift_value;
+ return linear_period_to_freq_tab[period%768]<<shift_value;
+ }
+}
+
+int32_t CPTables::get_old_frequency(int32_t period) {
+
+ return (8363L*1712L)/(period?period:1);
+
+}
+
+CPTables::CPTables(){
+}
+CPTables::~CPTables(){
+}
diff --git a/modules/chibi/cp_tables.h b/modules/chibi/cp_tables.h
new file mode 100644
index 0000000000..ac7ee562b7
--- /dev/null
+++ b/modules/chibi/cp_tables.h
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* cp_tables.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CPTABLES_H
+#define CPTABLES_H
+
+#include "cp_config.h"
+
+/**conversion CPTables/functions
+ *@author Juan Linietsky
+ */
+
+/******************************
+ CPTables.h
+ --------
+
+CPTables methods for miscelaneous
+conversion utilities
+********************************/
+
+class CPTables {
+public:
+
+ enum { OCTAVE=12 };
+
+ static uint16_t old_period_table[OCTAVE*2];
+ static uint16_t log_table[104];
+ static int32_t linear_period_to_freq_tab[768];
+
+ static int32_t get_old_period(uint16_t note,int32_t speed);
+ static int32_t get_amiga_period(uint16_t note,int32_t fine);
+ static int32_t get_linear_period(uint16_t note,int32_t fine);
+ static int32_t get_linear_frequency(int32_t period);
+ static int32_t get_old_frequency(int32_t period);
+ static int32_t get_log_period(uint16_t note,int32_t p_c5freq);
+
+ CPTables();
+ ~CPTables();
+};
+
+#endif
diff --git a/modules/chibi/event_stream_chibi.cpp b/modules/chibi/event_stream_chibi.cpp
new file mode 100644
index 0000000000..b88f4ee70e
--- /dev/null
+++ b/modules/chibi/event_stream_chibi.cpp
@@ -0,0 +1,872 @@
+/*************************************************************************/
+/* event_stream_chibi.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "event_stream_chibi.h"
+#include "cp_loader_it.h"
+#include "cp_loader_xm.h"
+#include "cp_loader_s3m.h"
+#include "cp_loader_mod.h"
+
+static CPSampleManagerImpl *sample_manager;
+static ResourceFormatLoaderChibi *resource_loader;
+
+CPSample_ID CPSampleManagerImpl::create(bool p_16bits,bool p_stereo,int32_t p_len) {
+
+ AudioServer::SampleFormat sf=p_16bits?AudioServer::SAMPLE_FORMAT_PCM16:AudioServer::SAMPLE_FORMAT_PCM8;
+
+ SampleData *sd = memnew( SampleData );
+ sd->rid = AudioServer::get_singleton()->sample_create(sf,p_stereo,p_len);
+ sd->stereo=p_stereo;
+ sd->len=p_len;
+ sd->is16=p_16bits;
+ sd->mixfreq=44100;
+ sd->loop_begin=0;
+ sd->loop_end=0;
+ sd->loop_type=CP_LOOP_NONE;
+ sd->locks=0;
+#ifdef DEBUG_ENABLED
+ valid.insert(sd);
+#endif
+ CPSample_ID sid;
+ sid._private=sd;
+ return sid;
+}
+
+void CPSampleManagerImpl::recreate(CPSample_ID p_id,bool p_16bits,bool p_stereo,int32_t p_len){
+
+ AudioServer::SampleFormat sf=p_16bits?AudioServer::SAMPLE_FORMAT_PCM16:AudioServer::SAMPLE_FORMAT_PCM8;
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+ AudioServer::get_singleton()->free(sd->rid);
+ sd->rid = AudioServer::get_singleton()->sample_create(sf,p_stereo,p_len);
+ sd->stereo=p_stereo;
+ sd->len=p_len;
+ sd->is16=p_16bits;
+ sd->mixfreq=44100;
+ sd->loop_begin=0;
+ sd->loop_end=0;
+ sd->loop_type=CP_LOOP_NONE;
+}
+void CPSampleManagerImpl::destroy(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+ valid.erase(sd);
+#endif
+ AudioServer::get_singleton()->free(sd->rid);
+
+ memdelete(sd);
+}
+bool CPSampleManagerImpl::check(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ return valid.has(sd);
+#else
+ return _getsd(p_id)!=NULL;
+#endif
+}
+
+void CPSampleManagerImpl::set_c5_freq(CPSample_ID p_id,int32_t p_freq){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+ sd->mixfreq=p_freq;
+ AudioServer::get_singleton()->sample_set_mix_rate(sd->rid,p_freq);
+
+}
+void CPSampleManagerImpl::set_loop_begin(CPSample_ID p_id,int32_t p_begin){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+ sd->loop_begin=p_begin;
+ AudioServer::get_singleton()->sample_set_loop_begin(sd->rid,p_begin);
+
+}
+void CPSampleManagerImpl::set_loop_end(CPSample_ID p_id,int32_t p_end){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+ sd->loop_end=p_end;
+ AudioServer::get_singleton()->sample_set_loop_end(sd->rid,p_end);
+
+}
+void CPSampleManagerImpl::set_loop_type(CPSample_ID p_id,CPSample_Loop_Type p_type){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ sd->loop_type=p_type;
+ AudioServer::get_singleton()->sample_set_loop_format(sd->rid,AudioServer::SampleLoopFormat(p_type));
+
+
+}
+void CPSampleManagerImpl::set_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ ERR_FAIL();
+}
+
+
+int32_t CPSampleManagerImpl::get_loop_begin(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ return sd->loop_begin;
+
+}
+int32_t CPSampleManagerImpl::get_loop_end(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ return sd->loop_end;
+}
+CPSample_Loop_Type CPSampleManagerImpl::get_loop_type(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),CP_LOOP_NONE);
+#endif
+
+ return sd->loop_type;
+}
+int32_t CPSampleManagerImpl::get_c5_freq(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ return sd->mixfreq;
+}
+int32_t CPSampleManagerImpl::get_size(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ return sd->len;
+
+}
+bool CPSampleManagerImpl::is_16bits(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),false);
+#endif
+
+ return sd->is16;
+
+}
+bool CPSampleManagerImpl::is_stereo(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),false);
+#endif
+ return sd->stereo;
+
+
+}
+bool CPSampleManagerImpl::lock_data(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ sd->locks++;
+ if (sd->locks==1) {
+ sd->lock=AudioServer::get_singleton()->sample_get_data(sd->rid);
+ sd->w=sd->lock.write();
+ }
+
+ return true;
+}
+void *CPSampleManagerImpl::get_data(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ ERR_FAIL_COND_V(sd->locks==0,0);
+ return sd->w.ptr();
+}
+
+int16_t CPSampleManagerImpl::get_data(CPSample_ID p_id, int p_sample, int p_channel){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ ERR_FAIL_V(0);
+ lock_data(p_id);
+
+ int sofs = sd->stereo ? 2:1;
+ uint16_t v=0;
+ if (sd->is16) {
+ int16_t *p=(int16_t*)sd->w.ptr();
+ v=p[p_sample*sofs+p_channel];
+ } else {
+ int8_t *p=(int8_t*)sd->w.ptr();
+ v=p[p_sample*sofs+p_channel];
+ }
+
+ unlock_data(p_id);
+
+ return v;
+}
+void CPSampleManagerImpl::set_data(CPSample_ID p_id, int p_sample, int16_t p_data,int p_channel){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ ERR_FAIL();
+ lock_data(p_id);
+
+ int sofs = sd->stereo ? 2:1;
+ if (sd->is16) {
+ int16_t *p=(int16_t*)sd->w.ptr();
+ p[p_sample*sofs+p_channel]=p_data;
+ } else {
+ int8_t *p=(int8_t*)sd->w.ptr();
+ p[p_sample*sofs+p_channel]=p_data;
+ }
+
+ unlock_data(p_id);
+
+}
+void CPSampleManagerImpl::unlock_data(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ ERR_FAIL_COND(sd->locks==0);
+
+ sd->locks--;
+ if (sd->locks==0) {
+ sd->w=DVector<uint8_t>::Write();
+ AudioServer::get_singleton()->sample_set_data(sd->rid,sd->lock);
+ sd->lock=DVector<uint8_t>();
+ }
+}
+
+void CPSampleManagerImpl::get_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len) {
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ ERR_FAIL();
+}
+
+
+/** MIXER **/
+
+void CPMixerImpl::set_callback_interval(int p_interval_us) {
+
+ callback_interval=p_interval_us;
+}
+
+void CPMixerImpl::set_callback(void (*p_callback)(void*),void *p_userdata) {
+
+ callback=p_callback;
+ userdata=p_userdata;
+}
+
+void CPMixerImpl::setup_voice(int p_voice_index,CPSample_ID p_sample_id,int32_t p_start_index) {
+
+ Voice &v=voices[p_voice_index];
+ if (v.channel!=AudioMixer::INVALID_CHANNEL) {
+ mixer->channel_free(v.channel);
+ }
+ v.channel=mixer->channel_alloc(sample_manager->get_rid(p_sample_id));
+ v.freq_mult = sample_manager->get_c5_freq(p_sample_id)/261.6255653006;
+ v.sample = p_sample_id;
+}
+
+void CPMixerImpl::stop_voice(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ if (v.channel==AudioMixer::INVALID_CHANNEL)
+ return;
+
+ mixer->channel_free(v.channel);
+ v.channel=AudioMixer::INVALID_CHANNEL;
+
+}
+
+void CPMixerImpl::set_voice_frequency(int p_voice_index,int32_t p_freq) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ float f = p_freq / 256.0;
+ f*=pitch_scale;
+ mixer->channel_set_mix_rate(v.channel,f * v.freq_mult );
+}
+
+void CPMixerImpl::set_voice_panning(int p_voice_index,int p_pan) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ if (p_pan==CP_PAN_SURROUND)
+ p_pan=CP_PAN_CENTER;
+ float p = p_pan / 256.0;
+ mixer->channel_set_pan(v.channel,p);
+
+}
+
+void CPMixerImpl::set_voice_volume(int p_voice_index,int p_vol) {
+
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ float vol = p_vol/512.0;
+ vol*=voice_scale;
+ mixer->channel_set_volume(v.channel,vol);
+ mixer->channel_set_reverb(v.channel,reverb_type,vol*v.reverb);
+}
+
+void CPMixerImpl::set_voice_filter(int p_voice_index,bool p_enabled,uint8_t p_cutoff, uint8_t p_resonance ){
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+
+}
+
+void CPMixerImpl::set_voice_reverb_send(int p_voice_index,int p_reverb){
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ v.reverb=p_reverb/255.0;
+ //mixer->channel_set_reverb(v.channel,reverb_type,p_reverb/255.0);
+
+}
+
+void CPMixerImpl::set_voice_chorus_send(int p_voice_index,int p_chorus){
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ mixer->channel_set_chorus(v.channel,p_chorus/255.0);
+
+}
+
+
+void CPMixerImpl::set_reverb_mode(ReverbMode p_mode){
+
+// Voice &v=voices[p_voice_index];
+// ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ switch(p_mode) {
+ case CPMixer::REVERB_MODE_STUDIO_SMALL: reverb_type=AudioMixer::REVERB_SMALL; break;
+ case CPMixer::REVERB_MODE_STUDIO_MEDIUM: reverb_type=AudioMixer::REVERB_MEDIUM; break;
+ case CPMixer::REVERB_MODE_STUDIO_LARGE: reverb_type=AudioMixer::REVERB_LARGE; break;
+ case CPMixer::REVERB_MODE_HALL: reverb_type=AudioMixer::REVERB_HALL; break;
+ default: reverb_type=AudioMixer::REVERB_SMALL; break;
+ }
+
+}
+
+void CPMixerImpl::set_chorus_params(unsigned int p_delay_ms,unsigned int p_separation_ms,unsigned int p_depth_ms10,unsigned int p_speed_hz10){
+
+// Voice &v=voices[p_voice_index];
+// ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+
+}
+
+
+
+/* Info retrieving */
+
+int32_t CPMixerImpl::get_voice_sample_pos_index(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND_V(v.channel==AudioMixer::INVALID_CHANNEL,0);
+ return 0;
+
+}
+
+int CPMixerImpl::get_voice_panning(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND_V(!is_voice_active(p_voice_index),0);
+ return mixer->channel_get_pan(v.channel)*CP_PAN_RIGHT;
+
+}
+
+int CPMixerImpl::get_voice_volume(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND_V(!is_voice_active(p_voice_index),0);
+ return mixer->channel_get_volume(v.channel);
+
+
+}
+
+CPSample_ID CPMixerImpl::get_voice_sample_id(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND_V(v.channel==AudioMixer::INVALID_CHANNEL,CPSample_ID());
+ return v.sample;
+
+
+}
+
+bool CPMixerImpl::is_voice_active(int p_voice_index){
+
+ Voice &v=voices[p_voice_index];
+ if (v.channel==AudioMixer::INVALID_CHANNEL)
+ return false;
+ if (!mixer->channel_is_valid(v.channel))
+ v.channel=AudioMixer::INVALID_CHANNEL;
+
+ return v.channel!=AudioMixer::INVALID_CHANNEL;
+}
+
+void CPMixerImpl::process_usecs(int p_usec,float p_volume,float p_pitch_scale,float p_tempo_scale) {
+
+ ERR_FAIL_COND(callback_interval==0);
+ //update this somewhere
+ pitch_scale=p_pitch_scale;
+ tempo_scale=p_tempo_scale;
+ voice_scale = AudioServer::get_singleton()->get_event_voice_global_volume_scale()*p_volume;
+ while(p_usec) {
+
+ if (p_usec>=callback_timeout) {
+
+ p_usec-=callback_timeout;
+ callback_timeout=0;
+ if (callback) {
+ callback(userdata);
+ }
+ callback_timeout=callback_interval*(1.0/p_tempo_scale);
+
+ } else {
+
+ callback_timeout-=p_usec;
+ p_usec=0;
+ }
+ }
+}
+
+
+CPMixerImpl::CPMixerImpl(AudioMixer *p_mixer) {
+
+ callback_interval=1;
+ callback_timeout=0;
+ userdata=0;
+ callback=0;
+ tempo_scale=1.0;
+ pitch_scale=1.0;
+ mixer=p_mixer;
+ voice_scale = AudioServer::get_singleton()->get_event_voice_global_volume_scale();
+ reverb_type = AudioMixer::REVERB_SMALL;
+
+}
+
+/** FILE ACCESS WRAPPER **/
+
+
+CPFileAccessWrapperImpl::Error CPFileAccessWrapperImpl::open(const char *p_filename, int p_mode_flags) {
+
+ ERR_FAIL_COND_V(p_mode_flags&WRITE,ERROR_WRITING_FILE);
+ close();
+ f = FileAccess::open(String::utf8(p_filename),p_mode_flags);
+ if (!f)
+ return ERROR_FILE_NOT_FOUND;
+ return OK;
+}
+
+void CPFileAccessWrapperImpl::close(){
+
+ if (f)
+ memdelete(f);
+ f=NULL;
+
+
+}
+
+void CPFileAccessWrapperImpl::seek(uint32_t p_position){
+
+ f->seek(p_position);
+}
+void CPFileAccessWrapperImpl::seek_end(){
+
+ f->seek_end();
+}
+uint32_t CPFileAccessWrapperImpl::get_pos(){
+
+ return f->get_pos();
+}
+
+bool CPFileAccessWrapperImpl::eof_reached(){
+
+ return f->eof_reached();
+}
+
+uint8_t CPFileAccessWrapperImpl::get_byte(){
+
+ return f->get_8();
+}
+void CPFileAccessWrapperImpl::get_byte_array(uint8_t *p_dest,int p_elements){
+
+ f->get_buffer(p_dest,p_elements);
+}
+void CPFileAccessWrapperImpl::get_word_array(uint16_t *p_dest,int p_elements){
+
+ for(int i=0;i<p_elements;i++) {
+ p_dest[i]=f->get_16();
+ }
+
+}
+
+uint16_t CPFileAccessWrapperImpl::get_word(){
+
+ return f->get_16();
+}
+uint32_t CPFileAccessWrapperImpl::get_dword(){
+
+ return f->get_32();
+}
+
+void CPFileAccessWrapperImpl::set_endian_conversion(bool p_swap){
+
+ f->set_endian_swap(p_swap);
+}
+bool CPFileAccessWrapperImpl::is_open(){
+
+ return f!=NULL;
+}
+
+CPFileAccessWrapperImpl::Error CPFileAccessWrapperImpl::get_error(){
+
+ return (f->get_error()!=::OK)?ERROR_READING_FILE:OK;
+}
+
+void CPFileAccessWrapperImpl::store_byte(uint8_t p_dest){
+
+}
+void CPFileAccessWrapperImpl::store_byte_array(const uint8_t *p_dest,int p_elements){
+
+}
+
+void CPFileAccessWrapperImpl::store_word(uint16_t p_dest){
+
+}
+void CPFileAccessWrapperImpl::store_dword(uint32_t p_dest){
+
+}
+
+////////////////////////////////////////////////
+
+
+Error EventStreamPlaybackChibi::_play() {
+
+ last_order=0;
+ loops=0;
+ player->play_start_song();
+ total_usec=0;
+
+ return OK;
+}
+
+bool EventStreamPlaybackChibi::_update(AudioMixer* p_mixer, uint64_t p_usec){
+
+ total_usec+=p_usec;
+ mixer.process_usecs(p_usec,volume,pitch_scale,tempo_scale);
+ int order=player->get_current_order();
+ if (order<last_order) {
+ if (!loop) {
+ stop();
+ } else {
+ loops++;
+ }
+ }
+ last_order=order;
+ return false;
+}
+void EventStreamPlaybackChibi::_stop(){
+
+ player->play_stop();
+}
+
+void EventStreamPlaybackChibi::set_paused(bool p_paused){
+
+}
+bool EventStreamPlaybackChibi::is_paused() const{
+
+ return false;
+}
+void EventStreamPlaybackChibi::set_loop(bool p_loop){
+
+ loop=p_loop;
+
+}
+bool EventStreamPlaybackChibi::is_loop_enabled() const{
+
+ return loop;
+}
+
+int EventStreamPlaybackChibi::get_loop_count() const{
+
+ //return player->is
+ return loops;
+}
+
+float EventStreamPlaybackChibi::get_pos() const{
+
+ return double(total_usec)/1000000.0;
+}
+void EventStreamPlaybackChibi::seek_pos(float p_time){
+
+ WARN_PRINT("seek_pos unimplemented.");
+}
+
+void EventStreamPlaybackChibi::set_volume(float p_volume) {
+
+ volume=p_volume;
+}
+
+float EventStreamPlaybackChibi::get_volume() const{
+
+ return volume;
+}
+
+void EventStreamPlaybackChibi::set_pitch_scale(float p_pitch_scale) {
+
+ pitch_scale=p_pitch_scale;
+}
+
+float EventStreamPlaybackChibi::get_pitch_scale() const{
+
+ return pitch_scale;
+}
+
+void EventStreamPlaybackChibi::set_tempo_scale(float p_tempo_scale) {
+
+ tempo_scale=p_tempo_scale;
+}
+
+float EventStreamPlaybackChibi::get_tempo_scale() const{
+
+ return tempo_scale;
+}
+
+
+void EventStreamPlaybackChibi::set_channel_volume(int p_channel,float p_volume) {
+
+
+ if (p_channel>=64)
+ return;
+ player->set_channel_global_volume(p_channel,p_volume*256);
+}
+
+
+
+float EventStreamPlaybackChibi::get_channel_volume(int p_channel) const{
+
+ return player->get_channel_global_volume(p_channel)/256.0;
+
+}
+
+float EventStreamPlaybackChibi::get_last_note_time(int p_channel) const {
+
+
+ double v = (player->get_channel_last_note_time_usec(p_channel))/1000000.0;
+ if (v<0)
+ v=-1;
+ return v;
+}
+
+EventStreamPlaybackChibi::EventStreamPlaybackChibi(Ref<EventStreamChibi> p_stream) : mixer(_get_mixer()) {
+
+ stream=p_stream;
+ player = memnew( CPPlayer(&mixer,&p_stream->song) );
+ loop=false;
+ last_order=0;
+ loops=0;
+ volume=1.0;
+ pitch_scale=1.0;
+ tempo_scale=1.0;
+}
+EventStreamPlaybackChibi::~EventStreamPlaybackChibi(){
+
+ player->play_stop();
+ memdelete(player);
+}
+
+////////////////////////////////////////////////////
+
+Ref<EventStreamPlayback> EventStreamChibi::instance_playback() {
+
+ return Ref<EventStreamPlayback>( memnew(EventStreamPlaybackChibi(Ref<EventStreamChibi>(this))) );
+}
+
+String EventStreamChibi::get_stream_name() const{
+
+ return song.get_name();
+
+}
+
+
+
+float EventStreamChibi::get_length() const{
+
+ return 1;
+}
+
+
+EventStreamChibi::EventStreamChibi() {
+
+
+}
+
+
+
+//////////////////////////////////////////////////////////////////
+
+
+
+
+RES ResourceFormatLoaderChibi::load(const String &p_path, const String& p_original_path, Error *r_error) {
+
+ if (r_error)
+ *r_error=ERR_FILE_CANT_OPEN;
+ String el = p_path.extension().to_lower();
+
+ CPFileAccessWrapperImpl f;
+
+ if (el=="it") {
+
+ Ref<EventStreamChibi> esc( memnew( EventStreamChibi ) );
+ CPLoader_IT loader(&f);
+ CPLoader::Error err = loader.load_song(p_path.utf8().get_data(),&esc->song,false);
+ ERR_FAIL_COND_V(err!=CPLoader::FILE_OK,RES());
+ if (r_error)
+ *r_error=OK;
+
+ return esc;
+
+ } else if (el=="xm") {
+
+ Ref<EventStreamChibi> esc( memnew( EventStreamChibi ) );
+ CPLoader_XM loader(&f);
+ CPLoader::Error err=loader.load_song(p_path.utf8().get_data(),&esc->song,false);
+ ERR_FAIL_COND_V(err!=CPLoader::FILE_OK,RES());
+ if (r_error)
+ *r_error=OK;
+ return esc;
+
+ } else if (el=="s3m") {
+
+ Ref<EventStreamChibi> esc( memnew( EventStreamChibi ) );
+ CPLoader_S3M loader(&f);
+ CPLoader::Error err=loader.load_song(p_path.utf8().get_data(),&esc->song,false);
+ ERR_FAIL_COND_V(err!=CPLoader::FILE_OK,RES());
+ if (r_error)
+ *r_error=OK;
+
+ return esc;
+
+ } else if (el=="mod") {
+
+ Ref<EventStreamChibi> esc( memnew( EventStreamChibi ) );
+ CPLoader_MOD loader(&f);
+ CPLoader::Error err=loader.load_song(p_path.utf8().get_data(),&esc->song,false);
+ ERR_FAIL_COND_V(err!=CPLoader::FILE_OK,RES());
+ if (r_error)
+ *r_error=OK;
+ return esc;
+ }
+
+ return RES();
+
+}
+
+void ResourceFormatLoaderChibi::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("it");
+ p_extensions->push_back("xm");
+ p_extensions->push_back("s3m");
+ p_extensions->push_back("mod");
+}
+bool ResourceFormatLoaderChibi::handles_type(const String& p_type) const {
+
+ return (p_type=="EventStreamChibi" || p_type=="EventStream");
+}
+
+String ResourceFormatLoaderChibi::get_resource_type(const String &p_path) const {
+ String el = p_path.extension().to_lower();
+ if (el=="it" || el=="s3m" || el=="xm" || el=="mod")
+ return "EventStreamChibi";
+ return "";
+}
+
+/////////////////////////////////////////////////////////////////
+void initialize_chibi() {
+
+ sample_manager = memnew( CPSampleManagerImpl );
+ resource_loader = memnew( ResourceFormatLoaderChibi );
+ ObjectTypeDB::register_type<EventStreamChibi>();
+ ResourceLoader::add_resource_format_loader( resource_loader );
+}
+
+void finalize_chibi() {
+
+ memdelete( sample_manager );
+ memdelete( resource_loader );
+}
+
diff --git a/modules/chibi/event_stream_chibi.h b/modules/chibi/event_stream_chibi.h
new file mode 100644
index 0000000000..cc7b0ace86
--- /dev/null
+++ b/modules/chibi/event_stream_chibi.h
@@ -0,0 +1,314 @@
+/*************************************************************************/
+/* event_stream_chibi.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef EVENT_STREAM_CHIBI_H
+#define EVENT_STREAM_CHIBI_H
+
+#include "scene/resources/event_stream.h"
+#include "cp_sample_manager.h"
+#include "cp_mixer.h"
+#include "cp_song.h"
+#include "cp_file_access_wrapper.h"
+#include "cp_player_data.h"
+#include "resource.h"
+#include "servers/audio_server.h"
+#include "os/file_access.h"
+#include "io/resource_loader.h"
+
+/** SAMPLE MANAGER **/
+
+class CPSampleManagerImpl : public CPSampleManager {
+
+ struct SampleData {
+
+ RID rid;
+ bool stereo;
+ bool is16;
+ int len;
+ int mixfreq;
+ int loop_begin;
+ int loop_end;
+ int locks;
+ DVector<uint8_t> lock;
+ DVector<uint8_t>::Write w;
+ CPSample_Loop_Type loop_type;
+ };
+
+
+ _FORCE_INLINE_ SampleData* _getsd(CPSample_ID p_id) {
+
+ return ((SampleData*)p_id._private);
+ }
+ Set<SampleData*> valid;
+
+public:
+
+ _FORCE_INLINE_ RID get_rid(CPSample_ID p_id) { return _getsd(p_id)->rid; }
+ virtual CPSample_ID create(bool p_16bits,bool p_stereo,int32_t p_len);
+ virtual void recreate(CPSample_ID p_id,bool p_16bits,bool p_stereo,int32_t p_len);
+ virtual void destroy(CPSample_ID p_id);
+ virtual bool check(CPSample_ID p_id); // return false if invalid
+
+ virtual void set_c5_freq(CPSample_ID p_id,int32_t p_freq);
+ virtual void set_loop_begin(CPSample_ID p_id,int32_t p_begin);
+ virtual void set_loop_end(CPSample_ID p_id,int32_t p_end);
+ virtual void set_loop_type(CPSample_ID p_id,CPSample_Loop_Type p_type);
+ virtual void set_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len);
+
+
+ virtual int32_t get_loop_begin(CPSample_ID p_id);
+ virtual int32_t get_loop_end(CPSample_ID p_id);
+ virtual CPSample_Loop_Type get_loop_type(CPSample_ID p_id);
+ virtual int32_t get_c5_freq(CPSample_ID p_id);
+ virtual int32_t get_size(CPSample_ID p_id);
+ virtual bool is_16bits(CPSample_ID p_id);
+ virtual bool is_stereo(CPSample_ID p_id);
+ virtual bool lock_data(CPSample_ID p_id);
+ virtual void *get_data(CPSample_ID p_id); /* WARNING: Not all sample managers
+may be able to implement this, it depends on the mixer in use! */
+ virtual int16_t get_data(CPSample_ID p_id, int p_sample, int p_channel=0); /// Does not need locking
+ virtual void set_data(CPSample_ID p_id, int p_sample, int16_t p_data,int p_channel=0); /// Does not need locking
+ virtual void unlock_data(CPSample_ID p_id);
+
+ virtual void get_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len);
+
+};
+
+
+/** MIXER **/
+
+class CPMixerImpl : public CPMixer {
+
+ enum {
+ MAX_VOICES=64
+ };
+
+ struct Voice {
+
+ AudioMixer::ChannelID channel;
+ CPSample_ID sample;
+ float freq_mult;
+ float reverb;
+ Voice() { reverb=0.0; }
+ };
+
+ Voice voices[MAX_VOICES];
+
+
+ int callback_interval;
+ int callback_timeout;
+ void (*callback)(void*);
+ void *userdata;
+ float voice_scale;
+ float tempo_scale;
+ float pitch_scale;
+ AudioMixer::ReverbRoomType reverb_type;
+ AudioMixer *mixer;
+public:
+
+ void process_usecs(int p_usec,float p_volume,float p_pitch_scale,float p_tempo_scale);
+
+ /* Callback */
+
+ virtual void set_callback_interval(int p_interval_us); //in usecs, for tracker it's 2500000/tempo
+ virtual void set_callback(void (*p_callback)(void*),void *p_userdata);
+
+ /* Voice Control */
+
+ virtual void setup_voice(int p_voice_index,CPSample_ID p_sample_id,int32_t p_start_index) ;
+ virtual void stop_voice(int p_voice_index) ;
+ virtual void set_voice_frequency(int p_voice_index,int32_t p_freq) ; //in freq*FREQUENCY_BITS
+ virtual void set_voice_panning(int p_voice_index,int p_pan) ;
+ virtual void set_voice_volume(int p_voice_index,int p_vol) ;
+ virtual void set_voice_filter(int p_filter,bool p_enabled,uint8_t p_cutoff, uint8_t p_resonance );
+ virtual void set_voice_reverb_send(int p_voice_index,int p_reverb);
+ virtual void set_voice_chorus_send(int p_voice_index,int p_chorus); /* 0 - 255 */
+
+ virtual void set_reverb_mode(ReverbMode p_mode);
+ virtual void set_chorus_params(unsigned int p_delay_ms,unsigned int p_separation_ms,unsigned int p_depth_ms10,unsigned int p_speed_hz10);
+
+
+ /* Info retrieving */
+
+ virtual int32_t get_voice_sample_pos_index(int p_voice_index) ;
+ virtual int get_voice_panning(int p_voice_index) ;
+ virtual int get_voice_volume(int p_voice_index) ;
+ virtual CPSample_ID get_voice_sample_id(int p_voice_index) ;
+ virtual bool is_voice_active(int p_voice_index);
+ virtual int get_active_voice_count() { return 0; }
+ virtual int get_total_voice_count() { return MAX_VOICES; }
+
+
+ virtual uint32_t get_mix_frequency() { return 0; }
+
+ /* Methods below only work with software mixers, meant for software-based sound drivers, hardware mixers ignore them */
+ virtual int32_t process(int32_t p_frames) { return 0; }
+ virtual int32_t *get_mixdown_buffer_ptr() { return NULL; }
+ virtual void set_mix_frequency(int32_t p_mix_frequency) {};
+
+ CPMixerImpl(AudioMixer *p_mixer=NULL);
+ virtual ~CPMixerImpl() {}
+};
+
+/** FILE ACCESS **/
+
+class CPFileAccessWrapperImpl : public CPFileAccessWrapper {
+
+ FileAccess *f;
+public:
+
+
+ virtual Error open(const char *p_filename, int p_mode_flags);
+ virtual void close();
+
+ virtual void seek(uint32_t p_position);
+ virtual void seek_end();
+ virtual uint32_t get_pos();
+
+ virtual bool eof_reached();
+
+ virtual uint8_t get_byte();
+ virtual void get_byte_array(uint8_t *p_dest,int p_elements);
+ virtual void get_word_array(uint16_t *p_dest,int p_elements);
+
+ virtual uint16_t get_word();
+ virtual uint32_t get_dword();
+
+ virtual void set_endian_conversion(bool p_swap);
+ virtual bool is_open();
+
+ virtual Error get_error();
+
+ virtual void store_byte(uint8_t p_dest);
+ virtual void store_byte_array(const uint8_t *p_dest,int p_elements);
+
+ virtual void store_word(uint16_t p_dest);
+ virtual void store_dword(uint32_t p_dest);
+
+ CPFileAccessWrapperImpl() { f=NULL; }
+ virtual ~CPFileAccessWrapperImpl(){ if (f) memdelete(f); }
+
+};
+
+
+
+/////////////////////
+
+class EventStreamChibi;
+
+class EventStreamPlaybackChibi : public EventStreamPlayback {
+
+ OBJ_TYPE(EventStreamPlaybackChibi,EventStreamPlayback);
+
+ CPMixerImpl mixer;
+ uint64_t total_usec;
+ Ref<EventStreamChibi> stream;
+ mutable CPPlayer *player;
+ bool loop;
+ int last_order;
+ int loops;
+ virtual Error _play();
+ virtual bool _update(AudioMixer* p_mixer, uint64_t p_usec);
+ virtual void _stop();
+ float volume;
+ float tempo_scale;
+ float pitch_scale;
+
+
+public:
+
+
+ virtual void set_paused(bool p_paused);
+ virtual bool is_paused() const;
+
+ virtual void set_loop(bool p_loop);
+ virtual bool is_loop_enabled() const;
+
+ virtual int get_loop_count() const;
+
+ virtual float get_pos() const;
+ virtual void seek_pos(float p_time);
+
+ virtual void set_volume(float p_vol);
+ virtual float get_volume() const;
+
+ virtual void set_pitch_scale(float p_pitch_scale);
+ virtual float get_pitch_scale() const;
+
+ virtual void set_tempo_scale(float p_tempo_scale);
+ virtual float get_tempo_scale() const;
+
+ virtual void set_channel_volume(int p_channel,float p_volume);
+ virtual float get_channel_volume(int p_channel) const;
+
+ virtual float get_last_note_time(int p_channel) const;
+
+ EventStreamPlaybackChibi(Ref<EventStreamChibi> p_stream=Ref<EventStreamChibi>());
+ ~EventStreamPlaybackChibi();
+};
+
+
+class EventStreamChibi : public EventStream {
+
+ OBJ_TYPE(EventStreamChibi,EventStream);
+
+friend class ResourceFormatLoaderChibi;
+friend class EventStreamPlaybackChibi;
+ //I think i didn't know what const was when i wrote this more than a decade ago
+ //so it goes mutable :(
+ mutable CPSong song;
+
+
+public:
+
+ virtual Ref<EventStreamPlayback> instance_playback();
+
+ virtual String get_stream_name() const;
+
+ virtual float get_length() const;
+
+ virtual int get_channel_count() const { return 64; } //tracker limit
+
+ EventStreamChibi();
+};
+
+
+class ResourceFormatLoaderChibi : public ResourceFormatLoader {
+
+public:
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+};
+
+void initialize_chibi();
+void finalize_chibi();
+
+#endif // EVENT_STREAM_CHIBI_H
diff --git a/modules/chibi/register_types.cpp b/modules/chibi/register_types.cpp
new file mode 100644
index 0000000000..b2ba16fa03
--- /dev/null
+++ b/modules/chibi/register_types.cpp
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "event_stream_chibi.h"
+
+void register_chibi_types() {
+
+ initialize_chibi();
+}
+
+void unregister_chibi_types() {
+
+ finalize_chibi();
+}
diff --git a/modules/chibi/register_types.h b/modules/chibi/register_types.h
new file mode 100644
index 0000000000..159823b85d
--- /dev/null
+++ b/modules/chibi/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_chibi_types();
+void unregister_chibi_types();