You can subscribe to this list here.
| 2009 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(25) |
Jul
(288) |
Aug
(119) |
Sep
(31) |
Oct
(59) |
Nov
(458) |
Dec
(359) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2010 |
Jan
(268) |
Feb
(26) |
Mar
(36) |
Apr
(48) |
May
(119) |
Jun
(37) |
Jul
(173) |
Aug
(429) |
Sep
(137) |
Oct
(156) |
Nov
(59) |
Dec
(45) |
| 2011 |
Jan
(398) |
Feb
(257) |
Mar
(49) |
Apr
(5) |
May
(34) |
Jun
(11) |
Jul
(38) |
Aug
(12) |
Sep
(1) |
Oct
(49) |
Nov
(5) |
Dec
(10) |
| 2012 |
Jan
(21) |
Feb
(32) |
Mar
(20) |
Apr
(1) |
May
(2) |
Jun
|
Jul
(173) |
Aug
|
Sep
(25) |
Oct
(6) |
Nov
(44) |
Dec
|
|
From: Yoda-JM <yo...@us...> - 2011-02-28 14:41:50
|
Author: Vincent Le Ligeour <yo...@us...>
Date: Mon Feb 28 15:36:41 2011 +0100
Started to move dancegraph to vertex array
---
game/dancegraph.cc | 18 +++++++++++++++---
1 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/game/dancegraph.cc b/game/dancegraph.cc
index cd35c33..ec45f54 100644
--- a/game/dancegraph.cc
+++ b/game/dancegraph.cc
@@ -377,6 +377,17 @@ namespace {
const float one_arrow_tex_w = 1.0 / 8.0; // Width of a single arrow in texture coordinates
/// Create a symmetric vertex pair for arrow drawing
+ void vertexPair(glutil::VertexArray& va, int arrow_i, float y, float ty) {
+ if (arrow_i < 0) {
+ // Single thing in a texture (e.g. mine)
+ va.TexCoord(0.0f, ty).Vertex(-arrowSize, y);
+ va.TexCoord(1.0f, ty).Vertex(arrowSize, y);
+ } else {
+ // Arrow from a texture atlas
+ va.TexCoord(arrow_i * one_arrow_tex_w, ty).Vertex(-arrowSize, y);
+ va.TexCoord((arrow_i+1) * one_arrow_tex_w, ty).Vertex(arrowSize, y);
+ }
+ }
void vertexPair(int arrow_i, float x, float y, float ty, float scale = 1.0f) {
if (arrow_i < 0) return;
glTexCoord2f(arrow_i * one_arrow_tex_w, ty); glVertex2f(x - arrowSize * scale, y);
@@ -463,7 +474,7 @@ void DanceGraph::draw(double time) {
void DanceGraph::drawBeats(double time) {
UseTexture tex(m_beat);
- glutil::Begin block(GL_TRIANGLE_STRIP);
+ glutil::VertexArray va;
float texCoord = 0.0f;
float tBeg = 0.0f, tEnd;
float w = 0.5 * m_pads * getScale();
@@ -476,9 +487,10 @@ void DanceGraph::drawBeats(double time) {
tEnd = future;
}*/
glutil::Color c(Color(1.0f, 1.0f, 1.0f, time2a(tEnd)));
- glNormal3f(0.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, texCoord); glVertex2f(-w, time2y(tEnd));
- glNormal3f(0.0f, 1.0f, 0.0f); glTexCoord2f(1.0f, texCoord); glVertex2f(w, time2y(tEnd));
+ va.Color(c).Normal(0.0f, 1.0f, 0.0f).TexCoord(0.0f, texCoord).Vertex(-w, time2y(tEnd));
+ va.Color(c).Normal(0.0f, 1.0f, 0.0f).TexCoord(1.0f, texCoord).Vertex(w, time2y(tEnd));
}
+ va.Draw();
}
/// Draws a single note (or hold)
|
|
From: Yoda-JM <yo...@us...> - 2011-02-28 14:41:43
|
Author: Vincent Le Ligeour <yo...@us...>
Date: Mon Feb 28 15:08:52 2011 +0100
Fixed 3d guitar notes shading
---
game/guitargraph.cc | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/game/guitargraph.cc b/game/guitargraph.cc
index 4d3b155..98232f3 100644
--- a/game/guitargraph.cc
+++ b/game/guitargraph.cc
@@ -857,7 +857,9 @@ void GuitarGraph::draw(double time) {
}
// Draw the notes
- { glutil::UseDepthTest depthtest;
+ {
+ glutil::UseLighting lighting(m_use3d);
+ glutil::UseDepthTest depthtest;
// Draw drum fills / Big Rock Endings
bool drumfill = m_dfIt != m_drumfills.end() && m_dfIt->begin - time <= future;
if (drumfill) {
|
|
From: Yoda-JM <yo...@us...> - 2011-02-28 14:41:37
|
Author: Vincent Le Ligeour <yo...@us...>
Date: Fri Feb 25 16:37:07 2011 +0100
Moved 3D object to vertes array code
---
game/3dobject.cc | 106 ++++++++++++++++++++++++-----------------------------
game/3dobject.hh | 49 +++++--------------------
2 files changed, 57 insertions(+), 98 deletions(-)
diff --git a/game/3dobject.cc b/game/3dobject.cc
index 053896c..da05471 100644
--- a/game/3dobject.cc
+++ b/game/3dobject.cc
@@ -4,13 +4,16 @@
#include <fstream>
#include <stdexcept>
#include <cmath>
-
+#include <boost/lexical_cast.hpp>
// TODO: test & fix faces that doesn't have texcoords in the file
// TODO: group handling for loader
namespace {
+ static const int HAS_TEXCOORDS = 1;
+ static const int HAS_NORMALS = 2;
+
/// Returns a word (delimited by delim) in a string st at position pos (1-based)
std::string getWord(std::string& st, size_t pos, char delim) {
std::istringstream iss(st);
@@ -21,97 +24,84 @@ namespace {
}
}
+/// A polygon containing links to required point data
+struct Face {
+ std::vector<int> vertices;
+ std::vector<int> texcoords;
+ std::vector<int> normals;
+};
+
/// Load a Wavefront .obj file and possibly scale it also
void Object3d::loadWavefrontObj(std::string filepath, float scale) {
+ int linenumber = 0;
std::string row;
std::ifstream file(filepath.c_str(), std::ios::binary);
if (!file.is_open()) throw std::runtime_error("Couldn't open object file "+filepath);
- // Get rid of old data
- m_vertices.clear();
- m_faces.clear();
- m_texcoords.clear();
- while (!file.eof()) {
- getline(file, row); // Read a line
+ std::vector<glmath::Vec4> m_vertices, m_normals, m_texcoords;
+ std::vector<Face> m_faces;
+ while (getline(file, row)) {
+ ++linenumber;
std::istringstream srow(row);
float x,y,z;
std::string tempst;
if (row.substr(0,2) == "v ") { // Vertices
srow >> tempst >> x >> y >> z;
- m_vertices.push_back(Vertex(x*scale,y*scale,z*scale));
+ m_vertices.push_back(glmath::Vec4(x*scale, y*scale, z*scale, 1.0f));
} else if (row.substr(0,2) == "vt") { // Texture Coordinates
srow >> tempst >> x >> y;
- m_texcoords.push_back(TexCoord(x,y));
+ m_texcoords.push_back(glmath::Vec4(x, y, 0.0f, 0.0f));
} else if (row.substr(0,2) == "vn") { // Normals
srow >> tempst >> x >> y >> z;
double sum = std::abs(x)+std::abs(y)+std::abs(z);
- if (sum == 0) throw std::runtime_error("Object "+filepath+" has invalid normal(s).");
+ if (sum == 0) throw std::runtime_error("Invalid normal in "+filepath+":"+boost::lexical_cast<std::string>(linenumber));
x /= sum; y /= sum; z /= sum; // Normalize components
- m_normals.push_back(Vertex(x,y,z));
+ m_normals.push_back(glmath::Vec4(x, y, z, 0.0));
} else if (row.substr(0,2) == "f ") { // Faces
Face f;
- srow >> tempst;
- int v_id;
+ srow >> tempst; // Eat away prefix
// Parse face point's coordinate references
- while (!srow.eof()) {
- srow >> tempst;
- for (size_t i = 1; i <= 3; i++) {
- std::string st_id(getWord(tempst,i,'/'));
+ for (std::string fpoint; srow >> fpoint; ) {
+ for (size_t i = 1; i <= 3; ++i) {
+ std::string st_id(getWord(fpoint,i,'/'));
if (!st_id.empty()) {
- std::istringstream conv_int(st_id);
- conv_int >> v_id;
+ // Vertex indices are 1-based in the file
+ int v_id = boost::lexical_cast<int>(st_id) - 1;
switch (i) {
- // Vertex indices are 1-based in the file
- case 1: f.vertices.push_back(v_id-1); break;
- case 2: f.texcoords.push_back(v_id-1); break;
- case 3: f.normals.push_back(v_id-1); break;
+ case 1: f.vertices.push_back(v_id); break;
+ case 2: f.texcoords.push_back(v_id); break;
+ case 3: f.normals.push_back(v_id); break;
}
}
}
}
+ // FIXME: We only allow triangle faces since the VBO generator/drawer
+ // cannot handle anything else (at least for now).
+ if (f.vertices.size() > 0 && f.vertices.size() != 3)
+ throw std::runtime_error("Only triangle faces allowed in "+filepath+":"+boost::lexical_cast<std::string>(linenumber));
// Face must have equal number of v, vt, vn or none of a kind
if (f.vertices.size() > 0
&& (f.texcoords.empty() || (f.texcoords.size() == f.vertices.size()))
&& (f.normals.empty() || (f.normals.size() == f.vertices.size()))) {
m_faces.push_back(f);
} else {
- throw std::runtime_error("Object "+filepath+" has invalid face(s).");
+ throw std::runtime_error("Invalid face in "+filepath+":"+boost::lexical_cast<std::string>(linenumber));
}
}
}
-}
-
-/// Generate a display list from the object data parsed earlier
-void Object3d::generateDisplayList() {
- if (m_displist != 0) glDeleteLists(m_displist, 1); // Get rid of old
- m_displist = glGenLists(1); // Get id for the list
- glutil::DisplayList displist(m_displist, GL_COMPILE); // From now on, gl-commands go to the list
- std::vector<Face>::const_iterator it;
- // Iterate through faces
- for (it = m_faces.begin(); it != m_faces.end(); ++it) {
- // Select a suitable primitive
- GLenum polyType = GL_POLYGON;
- switch (it->vertices.size()) {
- case 3: polyType = GL_TRIANGLES; break;
- case 4: polyType = GL_QUADS; break;
- }
- glutil::Begin block(polyType);
- // Iterate through face's points
- std::vector<int>::const_iterator it2;
- for (size_t i = 0; i < it->vertices.size(); i++) {
- // Texture coordinates
- if (!it->texcoords.empty())
- glTexCoord2f(m_texcoords[it->texcoords[i]].s, m_texcoords[it->texcoords[i]].t);
- // Normals
- if (!it->normals.empty())
- glNormal3f(
- m_normals[it->normals[i]].x,
- m_normals[it->normals[i]].y,
- m_normals[it->normals[i]].z);
- // Vertices
- glVertex3f(
- m_vertices[it->vertices[i]].x,
- m_vertices[it->vertices[i]].y,
- m_vertices[it->vertices[i]].z);
+ // Construct a vertex array
+ for (std::vector<Face>::const_iterator i = m_faces.begin(); i != m_faces.end(); ++i) {
+ bool hasNormals = !i->normals.empty();
+ bool hasTexCoords = !i->texcoords.empty();
+ for (size_t j = 0; j < i->vertices.size(); ++j) {
+ if (hasNormals) m_va.Normal(m_normals[i->normals[j]]);
+ if (hasTexCoords) m_va.TexCoord(m_texcoords[i->texcoords[j]]);
+ m_va.Vertex(m_vertices[i->vertices[j]]);
}
}
+
}
+
+void Object3d::drawVBO() {
+ m_va.Draw(GL_TRIANGLES);
+}
+
diff --git a/game/3dobject.hh b/game/3dobject.hh
index 9858de8..071eef2 100644
--- a/game/3dobject.hh
+++ b/game/3dobject.hh
@@ -9,68 +9,37 @@
// TODO: Exception handling
// TODO: Texture loading
-// TODO: Switch to vertex arrays
-
-/// Point in 3d space
-struct Vertex {
- Vertex(float x = 0, float y = 0, float z = 0): x(x), y(y), z(z) {}
- float x;
- float y;
- float z;
-};
-
-/// 2d texture coordinate
-struct TexCoord {
- TexCoord(float s = 0, float t = 0): s(s), t(t) {}
- float s;
- float t;
-};
-
-/// A polygon containing links to required point data
-struct Face {
- std::vector<int> vertices;
- std::vector<int> texcoords;
- std::vector<int> normals;
-};
/// A class representing 3d object
/// Non-copyable because of display lists getting messed up
class Object3d: boost::noncopyable {
private:
- std::vector<Vertex> m_vertices; /// vertices
- std::vector<TexCoord> m_texcoords; /// texture coordinates
- std::vector<Vertex> m_normals; /// normals
- std::vector<Face> m_faces; /// faces
- GLuint m_displist; /// display list id
+ glutil::VertexArray m_va;
boost::scoped_ptr<Texture> m_texture; /// texture
/// load a Wavefront .obj 3d object file
void loadWavefrontObj(std::string filepath, float scale = 1.0);
- /// generates a display list for the object
- void generateDisplayList();
public:
+ Object3d() {}
/// constructors
- Object3d(): m_displist(0) {};
- Object3d(std::string filepath, std::string texturepath = "", float scale = 1.0): m_displist(0) {
+ Object3d(std::string filepath, std::string texturepath = "", float scale = 1.0) {
load(filepath, texturepath, scale);
}
- /// destructor
- ~Object3d() {
- if (m_displist != 0) glDeleteLists(m_displist, 1);
- }
/// load a new object file
void load(std::string filepath, std::string texturepath = "", float scale = 1.0) {
if (!texturepath.empty()) m_texture.reset(new Texture(texturepath));
loadWavefrontObj(filepath, scale);
- generateDisplayList();
}
+ void drawVBO();
/// draws the object
- void draw(float x = 0, float y = 0, float z = 0, float s = 1.0) const {
+ void draw(float x = 0, float y = 0, float z = 0, float s = 1.0) {
glutil::PushMatrix pm;
glTranslatef(x, y, z); // Move to position
if (s != 1.0) glScalef(s,s,s); // Scale if needed
if (m_texture) {
UseTexture tex(*m_texture);
- glCallList(m_displist);
- } else glCallList(m_displist);
+ drawVBO();
+ } else {
+ drawVBO();
+ }
}
};
|
|
From: Yoda-JM <yo...@us...> - 2011-02-28 14:41:27
|
Author: Vincent Le Ligeour <yo...@us...> Date: Fri Feb 25 16:29:29 2011 +0100 Reduced differences between master and opengl2 branches --- game/glmath.hh | 155 +++++++++++++++++++++ game/glutil.hh | 143 ++++++++++++++++++-- game/guitargraph.cc | 94 +++++++------ game/instrumentgraph.hh | 9 +- game/main.cc | 9 +- game/notegraph.cc | 80 ++++------- game/screen.hh | 2 +- game/screen_intro.cc | 10 ++- game/screen_players.cc | 1 + game/screen_sing.cc | 12 +- game/screen_songs.cc | 102 ++++++++------ game/screen_songs.hh | 3 +- game/screenmanager.cc | 4 +- game/songs.cc | 2 +- game/surface.cc | 2 +- game/surface.hh | 20 ++- game/video_driver.cc | 64 ++++++++- game/video_driver.hh | 13 ++ themes/CMakeLists.txt | 1 - themes/default/songs_bg.svg | 20 ++- themes/default/songs_bg_default.svg | 47 +++++-- themes/default/songs_bg_ground.svg | 260 +++++++++++++++++++++++++++++++++++ 22 files changed, 856 insertions(+), 197 deletions(-) |
|
From: Yoda-JM <yo...@us...> - 2011-02-28 14:38:27
|
Author: Vincent Le Ligeour <yo...@us...> Date: Fri Feb 25 16:25:35 2011 +0100 Reduced differences between master and opengl2 branches --- game/glmath.hh | 155 +++++++++++++++++++++ game/glutil.hh | 143 ++++++++++++++++++-- game/guitargraph.cc | 94 +++++++------ game/instrumentgraph.hh | 9 +- game/main.cc | 9 +- game/notegraph.cc | 80 ++++------- game/screen.hh | 2 +- game/screen_intro.cc | 10 ++- game/screen_players.cc | 1 + game/screen_sing.cc | 12 +- game/screen_songs.cc | 102 ++++++++------ game/screen_songs.hh | 3 +- game/screenmanager.cc | 4 +- game/songs.cc | 2 +- game/surface.cc | 2 +- game/surface.hh | 20 ++- game/video_driver.cc | 64 ++++++++- game/video_driver.hh | 13 ++ themes/CMakeLists.txt | 1 - themes/default/songs_bg.svg | 20 ++- themes/default/songs_bg_default.svg | 47 +++++-- themes/default/songs_bg_ground.svg | 260 +++++++++++++++++++++++++++++++++++ 22 files changed, 856 insertions(+), 197 deletions(-) |
|
From: Yoda-JM <yo...@us...> - 2011-02-28 14:36:04
|
Author: Vincent Le Ligeour <yo...@us...>
Date: Mon Feb 28 15:36:41 2011 +0100
Started to move dancegraph to vertex array
---
game/dancegraph.cc | 18 +++++++++++++++---
1 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/game/dancegraph.cc b/game/dancegraph.cc
index cd35c33..ec45f54 100644
--- a/game/dancegraph.cc
+++ b/game/dancegraph.cc
@@ -377,6 +377,17 @@ namespace {
const float one_arrow_tex_w = 1.0 / 8.0; // Width of a single arrow in texture coordinates
/// Create a symmetric vertex pair for arrow drawing
+ void vertexPair(glutil::VertexArray& va, int arrow_i, float y, float ty) {
+ if (arrow_i < 0) {
+ // Single thing in a texture (e.g. mine)
+ va.TexCoord(0.0f, ty).Vertex(-arrowSize, y);
+ va.TexCoord(1.0f, ty).Vertex(arrowSize, y);
+ } else {
+ // Arrow from a texture atlas
+ va.TexCoord(arrow_i * one_arrow_tex_w, ty).Vertex(-arrowSize, y);
+ va.TexCoord((arrow_i+1) * one_arrow_tex_w, ty).Vertex(arrowSize, y);
+ }
+ }
void vertexPair(int arrow_i, float x, float y, float ty, float scale = 1.0f) {
if (arrow_i < 0) return;
glTexCoord2f(arrow_i * one_arrow_tex_w, ty); glVertex2f(x - arrowSize * scale, y);
@@ -463,7 +474,7 @@ void DanceGraph::draw(double time) {
void DanceGraph::drawBeats(double time) {
UseTexture tex(m_beat);
- glutil::Begin block(GL_TRIANGLE_STRIP);
+ glutil::VertexArray va;
float texCoord = 0.0f;
float tBeg = 0.0f, tEnd;
float w = 0.5 * m_pads * getScale();
@@ -476,9 +487,10 @@ void DanceGraph::drawBeats(double time) {
tEnd = future;
}*/
glutil::Color c(Color(1.0f, 1.0f, 1.0f, time2a(tEnd)));
- glNormal3f(0.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, texCoord); glVertex2f(-w, time2y(tEnd));
- glNormal3f(0.0f, 1.0f, 0.0f); glTexCoord2f(1.0f, texCoord); glVertex2f(w, time2y(tEnd));
+ va.Color(c).Normal(0.0f, 1.0f, 0.0f).TexCoord(0.0f, texCoord).Vertex(-w, time2y(tEnd));
+ va.Color(c).Normal(0.0f, 1.0f, 0.0f).TexCoord(1.0f, texCoord).Vertex(w, time2y(tEnd));
}
+ va.Draw();
}
/// Draws a single note (or hold)
|
|
From: Yoda-JM <yo...@us...> - 2011-02-28 14:08:26
|
Author: Vincent Le Ligeour <yo...@us...>
Date: Mon Feb 28 15:08:52 2011 +0100
Fixed 3d guitar notes shading
---
game/guitargraph.cc | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/game/guitargraph.cc b/game/guitargraph.cc
index 4d3b155..98232f3 100644
--- a/game/guitargraph.cc
+++ b/game/guitargraph.cc
@@ -857,7 +857,9 @@ void GuitarGraph::draw(double time) {
}
// Draw the notes
- { glutil::UseDepthTest depthtest;
+ {
+ glutil::UseLighting lighting(m_use3d);
+ glutil::UseDepthTest depthtest;
// Draw drum fills / Big Rock Endings
bool drumfill = m_dfIt != m_drumfills.end() && m_dfIt->begin - time <= future;
if (drumfill) {
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-28 13:58:59
|
Author: Lasse Kärkkäinen <tronic+ndrm at trn.iki.fi> Date: Mon Feb 28 14:58:32 2011 +0100 Write end of track event for MIDI, fixing (hopefully) Performous compatibility. --- songwriter-ini.cc | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/songwriter-ini.cc b/songwriter-ini.cc index aa394dd..bb2e09c 100644 --- a/songwriter-ini.cc +++ b/songwriter-ini.cc @@ -65,6 +65,10 @@ void FoFMIDIWriter::writeMIDI() const { ev.arg2 = 0; writer.writeEvent(ev); } + ev.type = Event::SPECIAL; + ev.channel = 0x0F; + ev.arg1 = Event::META_ENDOFTRACK; + writer.writeEvent(ev); // Write to file QByteArray name = (path + "/notes.mid").toLocal8Bit(); writer.save(std::string(name.data(), name.size()).c_str()); |
|
From: Tapio V. <aa...@us...> - 2011-02-28 13:54:06
|
Author: Tapio Vierros <tap...@gm...> Date: Mon Feb 28 15:53:31 2011 +0200 chmod +x deploy.sh --- 0 files changed, 0 insertions(+), 0 deletions(-) diff --git a/deploy.sh b/deploy.sh old mode 100644 new mode 100755 |
|
From: Tapio V. <aa...@us...> - 2011-02-28 13:54:00
|
Author: Tapio Vierros <tap...@gm...> Date: Mon Feb 28 15:52:59 2011 +0200 Added links to Composer wiki page. --- htdocs-source/composer.txt | 2 +- htdocs-source/download.txt | 1 + 2 files changed, 2 insertions(+), 1 deletions(-) diff --git a/htdocs-source/composer.txt b/htdocs-source/composer.txt index 5d05a8e..f440871 100644 --- a/htdocs-source/composer.txt +++ b/htdocs-source/composer.txt @@ -16,4 +16,4 @@ Key features of Composer include: Composer has a rather distinguished workflow: for example, the lyrics are imported as a whole and each time you manually put a note in place, the others automatically adjust to take use of the new information in providing a better guess of the pitch and timing. In a sense, you are not actually creating a song, but fixing and tuning the result of what the computer thinks the notes should be like. -Downloads are available at the <a href="download">download page</a>. +Downloads are available at the <a href="download">download page</a>. More information and building instructions can be found from the <a href="http://wiki.performous.org/index.php/Composer">wiki</a>. diff --git a/htdocs-source/download.txt b/htdocs-source/download.txt index 3d6bcae..34d08b3 100644 --- a/htdocs-source/download.txt +++ b/htdocs-source/download.txt @@ -34,4 +34,5 @@ If you are building Performous yourself or wish to do any development on it, you Binaries at <a href="https://sourceforge.net/projects/performous/files/editor" target="_new">SourceForge</a> Git repository: <a href="git://git.performous.org/gitroot/performous/editor">editor</a> + Build instructions and troubleshooting: <a href="http://wiki.performous.org/index.php/Composer">wiki</a> |
|
From: Tapio V. <aa...@us...> - 2011-02-28 10:04:36
|
Author: Tapio Vierros <tap...@gm...>
Date: Mon Feb 28 12:04:06 2011 +0200
Some code comments.
---
editorapp.hh | 1 +
notegraphwidget.cc | 4 +++-
notegraphwidget.hh | 2 +-
notelabel.cc | 4 ++++
notelabel.hh | 18 ++++++++++++++++++
operation.hh | 4 ++--
synth.hh | 21 +++++++++++++++------
7 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/editorapp.hh b/editorapp.hh
index 35b9f1a..0164c14 100644
--- a/editorapp.hh
+++ b/editorapp.hh
@@ -27,6 +27,7 @@ public:
AboutDialog(QWidget* parent = 0);
};
+
class Piano: public QLabel
{
Q_OBJECT
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index ef9582b..eecfcb9 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -173,6 +173,7 @@ void NoteGraphWidget::timerEvent(QTimerEvent* event)
killTimer(m_analyzeTimer);
updatePitch();
}
+
} else if (event->timerId() == m_notePixmapTimer) {
// Here we create a pixmap for a NoteLabel
if (m_nextNotePixmap >= m_notes.size()) {
@@ -181,7 +182,7 @@ void NoteGraphWidget::timerEvent(QTimerEvent* event)
m_nextNotePixmap = 0;
return;
}
- // Loop until a pixmap-to-create is found
+ // Loop until a pixmap-to-create is found - we only create one at a time to not block the UI
while (m_nextNotePixmap < m_notes.size() && !m_notes[m_nextNotePixmap]->createPixmap())
++m_nextNotePixmap;
++m_nextNotePixmap;
@@ -191,6 +192,7 @@ void NoteGraphWidget::timerEvent(QTimerEvent* event)
void NoteGraphWidget::startNotePixmapUpdates()
{
// With 0-delay, note pixmaps are created whenever there is not events to process
+ // This means fast performance while keeping snappy interface
if (!m_notePixmapTimer) m_notePixmapTimer = startTimer(0);
m_nextNotePixmap = 0;
}
diff --git a/notegraphwidget.hh b/notegraphwidget.hh
index 80e5cd7..6b770ca 100644
--- a/notegraphwidget.hh
+++ b/notegraphwidget.hh
@@ -136,7 +136,7 @@ public slots:
void updatePitch();
void abortPitch() { if (m_pitch) m_pitch->cancel(); }
void scrollToFirstNote();
- void startNotePixmapUpdates();
+ void startNotePixmapUpdates(); ///< Starts creating pixmaps for NoteLabels
signals:
void analyzeProgress(int, int);
diff --git a/notelabel.cc b/notelabel.cc
index 5c4701b..b011b24 100644
--- a/notelabel.cc
+++ b/notelabel.cc
@@ -22,6 +22,10 @@ NoteLabel::NoteLabel(const Note ¬e, QWidget *parent, bool floating)
updateLabel();
setMouseTracking(true);
hide();
+ // We don't want to show the widget and create the pixmap as that is slow.
+ // Since the undo-framework relies on rapidly creating and deleting NoteLabels,
+ // this is a necessity to get adequete performance. NoteGraphWidget creates the
+ // pixmaps later on once the final NoteLabels have been found.
}
void NoteLabel::updatePixmap()
diff --git a/notelabel.hh b/notelabel.hh
index 21eec62..d608008 100644
--- a/notelabel.hh
+++ b/notelabel.hh
@@ -6,6 +6,21 @@
#include "notes.hh"
#include "operation.hh"
+/**
+ * @brief Widget representing a single note.
+ *
+ * Notes:
+ * - Is rather useless without a parent NoteGraphWidget-object
+ * - Widget is initially hidden and without a pixmap to allow quick creation
+ * - Pixmap updates are generally delayed a little
+ * - The idea is to allow some time to apply the base operation to every note
+ * and then do the gfx updates asynchronously
+ * - NoteLabel has its own mouse handling for moving, resizing, cursors, tooltips etc,
+ but requires the parent NoteGraphWidget to update some internal states
+ * - Geometry & position is calculated from the underlying Note attributes (i.e. time and pitch)
+ * - Setting size or pos manually will be overridden so the Note must be manipulated instead
+ * - NoteLabel can be serialized to Operation-class
+ */
class NoteLabel: public QLabel
{
Q_OBJECT
@@ -45,7 +60,10 @@ public:
bool operator<(const NoteLabel &rhs) const { return m_note.begin < rhs.note().begin; }
public slots:
+ /// Shows the widget and creates the pixmap; if already visible, do nothing
+ /// @return true if pixmap was actually created, false if the widget was already visible
bool createPixmap() { if (isVisible()) return false; show(); updatePixmap(); return true; }
+ /// Updates the pixmap but only if the widget is visible (i.e. createPixmap has been called)
void updatePixmap();
protected:
diff --git a/operation.hh b/operation.hh
index c90ee9e..8324f41 100644
--- a/operation.hh
+++ b/operation.hh
@@ -35,7 +35,7 @@ struct Operation
template<typename T>
T param(int index) const { validate(index); m_params[index].value<T>(); }
- /// Get Operation parameter at certain index (1-based)
+ // Get Operation parameter at certain index (1-based)
QString s(int index) const { validate(index); return m_params[index].toString(); }
char c(int index) const { validate(index); return m_params[index].toChar().toAscii(); }
@@ -46,7 +46,7 @@ struct Operation
double d(int index) const { validate(index); return m_params[index].toDouble(); }
QVariant q(int index) const { validate(index); return m_params[index]; }
- // Array access for modifying param
+ /// Array access for modifying param
QVariant& operator[](int index) { validate(index); return m_params[index]; }
std::string dump() const {
diff --git a/synth.hh b/synth.hh
index 58f822a..fccb9bd 100644
--- a/synth.hh
+++ b/synth.hh
@@ -30,7 +30,11 @@ struct SynthNote {
typedef QList<SynthNote> SynthNotes;
-
+/**
+ * @brief Threaded WAV buffer creator.
+ *
+ * Synthesizes and schedules notes in a thread and sends them to the main thread when its time to play them.
+ */
class Synth: public QThread
{
Q_OBJECT
@@ -170,14 +174,19 @@ private:
double m_delay; ///< How many seconds until the next sound must be played
double m_pos; ///< Position where we are now
double m_noteBegin; ///< Position of the next note
- QByteArray m_soundData[2];
- int m_curBuffer;
- bool m_quit;
- QMutex m_mutex;
- QWaitCondition m_condition;
+ QByteArray m_soundData[2]; ///< The WAV buffers
+ int m_curBuffer; ///< Which buffer we are currently using
+ bool m_quit; ///< Flag to signal the thread should quit
+ QMutex m_mutex; ///< Mutex for protecting resource access
+ QWaitCondition m_condition; ///< For signaling the thread
};
+/**
+ * @brief Class for playing a WAV buffer from memory.
+ *
+ * Designed to be reused, but won't play the buffer if the previous hasn't finished.
+ */
class BufferPlayer: public QObject
{
Q_OBJECT
|
|
From: Tapio V. <aa...@us...> - 2011-02-28 09:28:05
|
Author: Tapio Vierros <tap...@gm...>
Date: Mon Feb 28 11:27:14 2011 +0200
Select first note when starting playback if no existing selection.
---
editorapp.cc | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index 06593fd..3dbe4ce 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -831,7 +831,8 @@ void EditorApp::playerStateChanged(Phonon::State newstate, Phonon::State oldstat
if (player) errst += " " + player->errorString();
QMessageBox::critical(this, tr("Playback error"), errst);
}
- }
+ } else if (!noteGraph->selectedNote() && !noteGraph->noteLabels().isEmpty())
+ noteGraph->selectNote(noteGraph->noteLabels().front());
}
void EditorApp::playBuffer(const QByteArray& buffer)
|
|
From: Tapio V. <aa...@us...> - 2011-02-28 07:52:22
|
Author: Tapio Vierros <tap...@gm...>
Date: Mon Feb 28 09:51:40 2011 +0200
Link to the proper wiki page.
---
aboutdialog.ui | 10 +++-------
1 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/aboutdialog.ui b/aboutdialog.ui
index 287603e..0251b88 100644
--- a/aboutdialog.ui
+++ b/aboutdialog.ui
@@ -67,7 +67,7 @@
</spacer>
</item>
<item>
- <widget class="QLabel" name="label">
+ <widget class="QLabel" name="lblWebsiteLink">
<property name="text">
<string>Website: <a href="http://performous.org/composer">http://performous.org/composer</a></string>
</property>
@@ -83,13 +83,9 @@
</widget>
</item>
<item>
- <widget class="QLabel" name="label_2">
+ <widget class="QLabel" name="lblWikiLink">
<property name="text">
- <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
-<html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Online documentation: <a href="http://wiki.performous.org"><span style=" text-decoration: underline; color:#0000ff;">http://wiki.performous.org</span></a></p></body></html></string>
+ <string>Online documentation: <a href="http://wiki.performous.org/index.php/Composer">http://wiki.performous.org/index.php/Composer</a></string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-28 00:44:28
|
Author: Lasse Karkkainen <tro...@tr...>
Date: Mon Feb 28 01:43:32 2011 +0100
Correct order for vertex color vs. lighting.
---
data/shaders/core.frag | 8 +++++---
1 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/data/shaders/core.frag b/data/shaders/core.frag
index 7a3f7a1..78a3c87 100644
--- a/data/shaders/core.frag
+++ b/data/shaders/core.frag
@@ -28,6 +28,10 @@ uniform sampler2D tex;
void main() {
vec4 frag = TEXFUNC;
+#ifdef ENABLE_VERTEX_COLOR
+ frag *= color;
+#endif
+
#ifdef ENABLE_LIGHTING
vec3 lightDir = normalize(vec3(-50.0, 5.0, -15.0));
const vec3 ambient = vec3(0.1, 0.1, 0.1);
@@ -35,9 +39,7 @@ void main() {
float NdotL = max(dot(normalize(normal), lightDir), 0.0);
frag = vec4(ambient + frag.rgb * NdotL, frag.a);
#endif
-#ifdef ENABLE_VERTEX_COLOR
- frag *= color;
-#endif
+
gl_FragColor = colorMatrix * frag;
}
|
|
From: Tapio V. <aa...@us...> - 2011-02-26 12:35:43
|
Author: Tapio Vierros <tap...@gm...>
Date: Sat Feb 26 14:34:18 2011 +0200
Website link now points to the Composer page.
---
aboutdialog.ui | 6 +-----
1 files changed, 1 insertions(+), 5 deletions(-)
diff --git a/aboutdialog.ui b/aboutdialog.ui
index 2bf2a95..287603e 100644
--- a/aboutdialog.ui
+++ b/aboutdialog.ui
@@ -69,11 +69,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
- <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
-<html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Website: <a href="http://performous.org"><span style=" text-decoration: underline; color:#0000ff;">http://performous.org</span></a></p></body></html></string>
+ <string>Website: <a href="http://performous.org/composer">http://performous.org/composer</a></string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
|
|
From: Tapio V. <aa...@us...> - 2011-02-26 12:31:45
|
Author: Tapio Vierros <tap...@gm...> Date: Sat Feb 26 14:30:35 2011 +0200 Rephrase Composer opening sentence. --- htdocs-source/composer.txt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/htdocs-source/composer.txt b/htdocs-source/composer.txt index a5fe36e..5d05a8e 100644 --- a/htdocs-source/composer.txt +++ b/htdocs-source/composer.txt @@ -2,7 +2,7 @@ Composer <img src="imgs/composer-small.png" alt="Anaglyph screenshot"/> -Composer is an editor for creating (and converting) song notes for music games in various formats. It attempts to make the process easy by automating as much as possible while providing a simple and attractive interface to do the remaining manual work. +Composer is a song editor for creating (and converting) notes for music games in various formats. It attempts to make the process easy by automating as much as possible while providing a simple and attractive interface to do the remaining manual work. Key features of Composer include: |
|
From: Tapio V. <aa...@us...> - 2011-02-26 12:30:41
|
Author: Tapio Vierros <tap...@gm...> Date: Sat Feb 26 14:29:29 2011 +0200 Really simple download section for Composer. --- htdocs-source/download.txt | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/htdocs-source/download.txt b/htdocs-source/download.txt index 9e4ff97..3d6bcae 100644 --- a/htdocs-source/download.txt +++ b/htdocs-source/download.txt @@ -30,3 +30,8 @@ Download Performous If you are building Performous yourself or wish to do any development on it, you should always use the git master branch rather than releases. The master branch is relatively stable as all development is done in separate branches but it often contains bugfixes and improvements that haven't been released yet. +:h2:Composer + + Binaries at <a href="https://sourceforge.net/projects/performous/files/editor" target="_new">SourceForge</a> + Git repository: <a href="git://git.performous.org/gitroot/performous/editor">editor</a> + |
|
From: Tapio V. <aa...@us...> - 2011-02-26 12:24:52
|
Author: Tapio Vierros <tap...@gm...> Date: Sat Feb 26 14:23:47 2011 +0200 640px wide Composer screenshot. --- htdocs-binary/imgs/composer-small.png | Bin 62154 -> 79513 bytes 1 files changed, 0 insertions(+), 0 deletions(-) diff --git a/htdocs-binary/imgs/composer-small.png b/htdocs-binary/imgs/composer-small.png index d46c2f0..1c42f7d 100644 Binary files a/htdocs-binary/imgs/composer-small.png and b/htdocs-binary/imgs/composer-small.png differ |
|
From: Tapio V. <aa...@us...> - 2011-02-26 12:17:30
|
Author: Tapio Vierros <tap...@gm...> Date: Sat Feb 26 14:16:31 2011 +0200 Draft Composer page. --- htdocs-binary/imgs/composer-small.png | Bin 0 -> 62154 bytes htdocs-source/AWC-cfg.txt | 1 + htdocs-source/composer.txt | 19 +++++++++++++++++++ 3 files changed, 20 insertions(+), 0 deletions(-) diff --git a/htdocs-binary/imgs/composer-small.png b/htdocs-binary/imgs/composer-small.png new file mode 100644 index 0000000..d46c2f0 Binary files /dev/null and b/htdocs-binary/imgs/composer-small.png differ diff --git a/htdocs-source/AWC-cfg.txt b/htdocs-source/AWC-cfg.txt index a53da19..982e525 100644 --- a/htdocs-source/AWC-cfg.txt +++ b/htdocs-source/AWC-cfg.txt @@ -4,6 +4,7 @@ +about What Is It +download Download +songs Free Songs ++composer Composer +others Other Games % Project links .http://wiki.performous.org/ Documentation diff --git a/htdocs-source/composer.txt b/htdocs-source/composer.txt new file mode 100644 index 0000000..a5fe36e --- /dev/null +++ b/htdocs-source/composer.txt @@ -0,0 +1,19 @@ +Composer + +<img src="imgs/composer-small.png" alt="Anaglyph screenshot"/> + +Composer is an editor for creating (and converting) song notes for music games in various formats. It attempts to make the process easy by automating as much as possible while providing a simple and attractive interface to do the remaining manual work. + +Key features of Composer include: + + Song pitch analysis based on the esteemed algorithms from Perfomous. + Zoomable interface to quickly get an overview or doing very precise timing. + Possibility to synthesize the notes to get a feel of their "sound". + Import/export in various formats including: + SingStar XML + UltraStar TXT + Frets on Fire MIDI + +Composer has a rather distinguished workflow: for example, the lyrics are imported as a whole and each time you manually put a note in place, the others automatically adjust to take use of the new information in providing a better guess of the pitch and timing. In a sense, you are not actually creating a song, but fixing and tuning the result of what the computer thinks the notes should be like. + +Downloads are available at the <a href="download">download page</a>. |
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-25 19:17:36
|
Author: Lasse Kärkkäinen <tronic+ndrm at trn.iki.fi> Date: Fri Feb 25 20:16:54 2011 +0100 Midi export support (works on my computer). --- songwriter-ini.cc | 62 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 58 insertions(+), 4 deletions(-) diff --git a/songwriter-ini.cc b/songwriter-ini.cc index 526b135..aa394dd 100644 --- a/songwriter-ini.cc +++ b/songwriter-ini.cc @@ -1,17 +1,71 @@ #include "songwriter.hh" #include "midifile.hh" +#include "util.hh" #include <QTextStream> - void FoFMIDIWriter::writeMIDI() const { Notes const& notes = s.getVocalTrack().notes; - unsigned division = 16; + if (notes.empty()) throw std::runtime_error("No notes"); + double tempo = s.bpm > 0 ? s.bpm : 120.0; + unsigned division = 64; // Allow for very precise timing + unsigned endtc = round(tempo / 60.0 * division * notes.back().end); midifile::Writer writer(1, 2, division); + using midifile::Event; writer.startTrack(); - // TODO: write timing info + Event ev; + ev.type = Event::SPECIAL; + ev.channel = 0x0F; + unsigned char buf[16]; + ev.begin = ev.end = buf; + if (tempo != 120.0) { // MIDI defaults to 120 BPM + ev.arg1 = Event::META_TEMPO; + ev.end = ev.begin + 3; + unsigned val = 6e+7 / tempo; // Microseconds per beat + buf[0] = val >> 16; + buf[1] = val >> 8; + buf[2] = val; + writer.writeEvent(ev); + } + // TODO: write Performous Composer and title= & artist= like EoF does + ev.arg1 = Event::META_ENDOFTRACK; + writer.writeEvent(ev); + // Vocals track begins writer.startTrack(); - // TODO: write notes + // Write track name + ev.arg1 = Event::META_SEQNAME; + std::string partvocals = "PART VOCALS"; + std::copy(partvocals.begin(), partvocals.end(), buf); + ev.end = ev.begin + partvocals.size(); + writer.writeEvent(ev); + // Write notes + unsigned timecode = 0; + for (Notes::const_iterator it = notes.begin(), itend = notes.end(); it != itend; ++it) { + // Lyric + QByteArray bytes = it->syllable.toUtf8(); + ev.timecode = round(tempo / 60.0 * division * it->begin) - timecode; + timecode += ev.timecode; + ev.type = Event::SPECIAL; + ev.channel = 0x0F; + ev.arg1 = Event::META_LYRIC; + ev.begin = reinterpret_cast<unsigned char*>(bytes.data()); + ev.end = ev.begin + bytes.size(); + writer.writeEvent(ev); + // Note begin + ev.channel = 0; + ev.timecode = 0; // Same timecode as the lyric + ev.type = Event::NOTE_ON; + ev.arg1 = it->note; + ev.arg2 = 64; + writer.writeEvent(ev); + // Note end + ev.timecode = round(tempo / 60.0 * division * it->end) - timecode; + timecode += ev.timecode; + ev.type = Event::NOTE_OFF; + ev.arg2 = 0; + writer.writeEvent(ev); + } + // Write to file QByteArray name = (path + "/notes.mid").toLocal8Bit(); writer.save(std::string(name.data(), name.size()).c_str()); } |
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-25 19:17:27
|
Author: Lasse Kärkkäinen <tronic+ndrm at trn.iki.fi> Date: Fri Feb 25 20:16:32 2011 +0100 Fix typo in midifile --- midifile.cc | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/midifile.cc b/midifile.cc index afee1ca..a1240cd 100644 --- a/midifile.cc +++ b/midifile.cc @@ -123,7 +123,7 @@ void Writer::startTrack() { void Writer::writeEvent(Event const& ev) { write_varlen(ev.timecode); if (ev.type & ~0xF0 || ev.type < 0x80) throw std::logic_error("Invalid MIDI event type"); - if (ev.type & ~0x0F) throw std::logic_error("Invalid MIDI channel number"); + if (ev.channel & ~0x0F) throw std::logic_error("Invalid MIDI channel number"); write<1>(ev.type | ev.channel); if (ev.type != Event::SPECIAL || ev.channel >= 8) write<1>(ev.arg1); // Everything except System Common takes one argument switch (ev.type) { |
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-25 19:17:21
|
Author: Lasse Kärkkäinen <tronic+ndrm at trn.iki.fi> Date: Fri Feb 25 20:16:09 2011 +0100 Clear lyric once used in mid parser --- songparser-ini.cc | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/songparser-ini.cc b/songparser-ini.cc index 745864e..81d8fe8 100644 --- a/songparser-ini.cc +++ b/songparser-ini.cc @@ -78,6 +78,7 @@ void SongParser::midParse() { if (ev.type == Event::NOTE_ON) { if (ev.arg1 >= 100) continue; // Skip control signals vt.notes.push_back(Note(strConv(lyric))); + lyric.clear(); Note& n = vt.notes.back(); n.begin = n.end = tsTime(timecode); n.note = ev.arg1; |
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-25 18:19:51
|
Author: Lasse Kärkkäinen <tronic+ndrm at trn.iki.fi> Date: Fri Feb 25 19:19:24 2011 +0100 Midifile cleanup and songwriter WIP --- midifile.cc | 46 ++++++++++++++++++++++++++++++++++++++++++---- midifile.hh | 48 +++++++++++++++++++++--------------------------- songparser-ini.cc | 8 ++++---- songwriter-ini.cc | 22 ++++++++++------------ 4 files changed, 77 insertions(+), 47 deletions(-) diff --git a/midifile.cc b/midifile.cc index 9386910..afee1ca 100644 --- a/midifile.cc +++ b/midifile.cc @@ -5,9 +5,10 @@ #include <iterator> #include <sstream> -using namespace mid; +using namespace midifile; Reader::Reader(char const* filename) { + // Read the entire file into a buffer { std::ifstream file(filename, std::ios::binary); if (!file.is_open()) throw std::runtime_error("Unable to open " + std::string(filename)); @@ -21,11 +22,10 @@ Reader::Reader(char const* filename) { m_fileEnd = m_pos + size; } parseMThd(); - parseMTrk(); } -bool Reader::nextTrack() { - m_pos = m_riffEnd; +bool Reader::startTrack() { + m_pos = m_riffEnd; // Jump to the end of the current riff if (m_pos == m_fileEnd) return false; parseMTrk(); return true; @@ -99,6 +99,27 @@ bool Reader::parseEvent(Event& ev) { return true; } +Writer::Writer(unsigned fmt, unsigned tracks, unsigned division) { + beginRiff("MThd"); + if (fmt == 0 && tracks != 1) throw std::logic_error("Format 0 MIDI must have exactly one track"); + if (fmt == 1 && tracks < 2) throw std::logic_error("Format 1 MIDI must have a separate timing track"); + if (division == 0) throw std::logic_error("Division must be set to a positive value"); + write<2>(fmt); + write<2>(tracks); + write<2>(division); +} + +void Writer::save(char const* filename) { + endRiff(); + std::ofstream f(filename, std::ios::binary); + f.write(reinterpret_cast<char const*>(&m_data[0]), m_data.size()); +} + +void Writer::startTrack() { + endRiff(); + beginRiff("MTrk"); +} + void Writer::writeEvent(Event const& ev) { write_varlen(ev.timecode); if (ev.type & ~0xF0 || ev.type < 0x80) throw std::logic_error("Invalid MIDI event type"); @@ -124,6 +145,23 @@ void Writer::writeEvent(Event const& ev) { } } +void Writer::beginRiff(char const* name) { + m_riffBegin = m_data.size(); + m_data.resize(m_riffBegin + 8); // Add space for header + std::copy(name, name + 4, m_data.begin() + m_riffBegin); // Set RIFF name +} + +void Writer::endRiff() { + unsigned size = m_data.size() - m_riffBegin; + if (size == 0) return; // Nothing to close + size -= 8; + m_data[m_riffBegin + 4] = size >> 24; + m_data[m_riffBegin + 5] = size >> 16; + m_data[m_riffBegin + 6] = size >> 8; + m_data[m_riffBegin + 7] = size; + m_riffBegin = m_data.size(); +} + // Debugging facilities follow namespace { diff --git a/midifile.hh b/midifile.hh index 4983d4b..6abeba6 100644 --- a/midifile.hh +++ b/midifile.hh @@ -5,7 +5,7 @@ #include <string> #include <vector> -namespace mid { +namespace midifile { typedef uint8_t value_type; typedef uint8_t* iterator; typedef uint8_t const* const_iterator; @@ -54,13 +54,16 @@ namespace mid { class Reader { public: static const unsigned margin = 20; ///< The minimum number of extra bytes required after the end of the buffer for more efficient processing + /// MIDI reader, read header. Reader(char const* filename); + /// Get the number of tracks, including the timing track if used in the current format unsigned numTracks() const { return m_tracks; } - unsigned Tracks() const { return m_tracks; } - /// Switch to the next track, returns false if end of file reached (all track processed) - bool nextTrack(); + /// Start reading a track (or jump to the next track), must be called before parsing any events + /// @returns false if end of file reached (all tracks processed) + bool startTrack(); /// Parse the next event of the current track, returns false if at the end of track bool parseEvent(Event& ev); + /// Get the number of timecode units in a beat (1/4 note) unsigned getDivision() const { return m_division; } private: void parseMThd(); @@ -94,30 +97,21 @@ namespace mid { class Writer { public: - private: - void writeMThd(unsigned tracks, unsigned division) { - beginRiff("MThd"); - write<2>(1); // fmt 1 (multitrack) - write<2>(tracks); - write<2>(division); - endRiff(); - } - void writeMTrk() { - beginRiff("MTrk"); - } + /// MIDI file writer (constructs the output in a memory buffer) + /// @param fmt MIDI format (use 1 if in doubt) + /// @param tracks The number of tracks (with fmt 1 this is one more than the actual tracks) + /// @param division How many timecode units fit into a beat (1/4 note) + Writer(unsigned fmt, unsigned tracks, unsigned division); + /// Flush the output to a file (after writing everything) + void save(char const* filename); + /// Start a new track, must be called before each track. + /// Ends any previous track but won't automatically add end of track event. + void startTrack(); + /// Writes an event to current track void writeEvent(Event const& ev); - void beginRiff(char const* name) { - m_riffBegin = m_data.size(); - m_data.resize(m_riffBegin + 8); // Add space for header - std::copy(name, name + 4, m_data.begin() + m_riffBegin); // Set RIFF name - } - void endRiff() { - unsigned size = m_data.size() - 8 - m_riffBegin; - m_data[m_riffBegin + 4] = size >> 24; - m_data[m_riffBegin + 5] = size >> 16; - m_data[m_riffBegin + 6] = size >> 8; - m_data[m_riffBegin + 7] = size; - } + private: + void beginRiff(char const* name); + void endRiff(); template <unsigned N> void write(unsigned value) { for (unsigned i = N - 1; i < N; --i) m_data.push_back(value >> (8 * i)); } diff --git a/songparser-ini.cc b/songparser-ini.cc index 52e9c54..745864e 100644 --- a/songparser-ini.cc +++ b/songparser-ini.cc @@ -61,13 +61,13 @@ namespace { void SongParser::midParse() { QByteArray name = (m_song.path + "notes.mid").toLocal8Bit(); - using namespace mid; - Reader reader(std::string(name.data(), name.size()).c_str()); + midifile::Reader reader(std::string(name.data(), name.size()).c_str()); + using midifile::Event; double tempo = 120.0; double division = reader.getDivision(); addBPM(0, tempo, division); unsigned track = 0; - do { + while (++track, reader.startTrack()) { VocalTrack vt(""); unsigned timecode = 0; std::string trackName, lyric; @@ -129,7 +129,7 @@ void SongParser::midParse() { } } if (trackName == "PART VOCALS") m_song.insertVocalTrack("vocals", vt); - } while (++track, reader.nextTrack()); + }; } #if 0 diff --git a/songwriter-ini.cc b/songwriter-ini.cc index 0ca0ffd..526b135 100644 --- a/songwriter-ini.cc +++ b/songwriter-ini.cc @@ -1,21 +1,19 @@ #include "songwriter.hh" + +#include "midifile.hh" #include <QTextStream> void FoFMIDIWriter::writeMIDI() const { - throw std::runtime_error("MIDI export is not implemented."); - /*std::ofstream f((path + "notes.mid").c_str(), std::ios::binary); - // FIXME: The following is just an example and doesn't actually output MID format - char buf[1024] = {}; Notes const& notes = s.getVocalTrack().notes; - std::cout << notes.size() << std::endl; - for (unsigned int i = 0; i < notes.size(); ++i) { - Note const& n = notes[i]; - buf[0] = 0xFF; - buf[1] = n.note; // MIDI note value - // Others are n.begin, n.end, n.type etc. (see notes.hh) - f.write(buf, 1024); - }*/ + unsigned division = 16; + midifile::Writer writer(1, 2, division); + writer.startTrack(); + // TODO: write timing info + writer.startTrack(); + // TODO: write notes + QByteArray name = (path + "/notes.mid").toLocal8Bit(); + writer.save(std::string(name.data(), name.size()).c_str()); } void FoFMIDIWriter::writeINI() const { |
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-25 18:19:42
|
Author: Lasse Kärkkäinen <tronic+ndrm at trn.iki.fi> Date: Fri Feb 25 18:33:11 2011 +0100 Writing events for midifile. --- midifile.cc | 26 ++++++++++++++++++++++++++ midifile.hh | 1 + 2 files changed, 27 insertions(+), 0 deletions(-) diff --git a/midifile.cc b/midifile.cc index b4f7102..9386910 100644 --- a/midifile.cc +++ b/midifile.cc @@ -2,6 +2,7 @@ #include <fstream> #include <iomanip> #include <iostream> +#include <iterator> #include <sstream> using namespace mid; @@ -98,6 +99,31 @@ bool Reader::parseEvent(Event& ev) { return true; } +void Writer::writeEvent(Event const& ev) { + write_varlen(ev.timecode); + if (ev.type & ~0xF0 || ev.type < 0x80) throw std::logic_error("Invalid MIDI event type"); + if (ev.type & ~0x0F) throw std::logic_error("Invalid MIDI channel number"); + write<1>(ev.type | ev.channel); + if (ev.type != Event::SPECIAL || ev.channel >= 8) write<1>(ev.arg1); // Everything except System Common takes one argument + switch (ev.type) { + case Event::NOTE_ON: + case Event::NOTE_OFF: + case Event::NOTE_AFTERTOUCH: + case Event::CONTROLLER: + case Event::PITCH_BEND: + write<1>(ev.arg2); + break; + case Event::PROGRAM_CHANGE: + case Event::CHANNEL_AFTERTOUCH: + if (ev.arg2 != 0) throw std::logic_error("MIDI event with non-zero arg2 for event that only takes one arg"); + break; // No arg2 for these + case Event::SPECIAL: // Special category (system exclusive or meta event) + write_varlen(ev.end - ev.begin); // data size + std::copy(ev.begin, ev.end, std::back_inserter(m_data)); + break; + } +} + // Debugging facilities follow namespace { diff --git a/midifile.hh b/midifile.hh index 6dd7718..4983d4b 100644 --- a/midifile.hh +++ b/midifile.hh @@ -105,6 +105,7 @@ namespace mid { void writeMTrk() { beginRiff("MTrk"); } + void writeEvent(Event const& ev); void beginRiff(char const* name) { m_riffBegin = m_data.size(); m_data.resize(m_riffBegin + 8); // Add space for header |
|
From: Yoda-JM <yo...@us...> - 2011-02-25 15:37:19
|
Author: Vincent Le Ligeour <yo...@us...>
Date: Fri Feb 25 16:37:07 2011 +0100
Moved 3D object to vertes array code
---
game/3dobject.cc | 106 ++++++++++++++++++++++++-----------------------------
game/3dobject.hh | 49 +++++--------------------
2 files changed, 57 insertions(+), 98 deletions(-)
diff --git a/game/3dobject.cc b/game/3dobject.cc
index 053896c..da05471 100644
--- a/game/3dobject.cc
+++ b/game/3dobject.cc
@@ -4,13 +4,16 @@
#include <fstream>
#include <stdexcept>
#include <cmath>
-
+#include <boost/lexical_cast.hpp>
// TODO: test & fix faces that doesn't have texcoords in the file
// TODO: group handling for loader
namespace {
+ static const int HAS_TEXCOORDS = 1;
+ static const int HAS_NORMALS = 2;
+
/// Returns a word (delimited by delim) in a string st at position pos (1-based)
std::string getWord(std::string& st, size_t pos, char delim) {
std::istringstream iss(st);
@@ -21,97 +24,84 @@ namespace {
}
}
+/// A polygon containing links to required point data
+struct Face {
+ std::vector<int> vertices;
+ std::vector<int> texcoords;
+ std::vector<int> normals;
+};
+
/// Load a Wavefront .obj file and possibly scale it also
void Object3d::loadWavefrontObj(std::string filepath, float scale) {
+ int linenumber = 0;
std::string row;
std::ifstream file(filepath.c_str(), std::ios::binary);
if (!file.is_open()) throw std::runtime_error("Couldn't open object file "+filepath);
- // Get rid of old data
- m_vertices.clear();
- m_faces.clear();
- m_texcoords.clear();
- while (!file.eof()) {
- getline(file, row); // Read a line
+ std::vector<glmath::Vec4> m_vertices, m_normals, m_texcoords;
+ std::vector<Face> m_faces;
+ while (getline(file, row)) {
+ ++linenumber;
std::istringstream srow(row);
float x,y,z;
std::string tempst;
if (row.substr(0,2) == "v ") { // Vertices
srow >> tempst >> x >> y >> z;
- m_vertices.push_back(Vertex(x*scale,y*scale,z*scale));
+ m_vertices.push_back(glmath::Vec4(x*scale, y*scale, z*scale, 1.0f));
} else if (row.substr(0,2) == "vt") { // Texture Coordinates
srow >> tempst >> x >> y;
- m_texcoords.push_back(TexCoord(x,y));
+ m_texcoords.push_back(glmath::Vec4(x, y, 0.0f, 0.0f));
} else if (row.substr(0,2) == "vn") { // Normals
srow >> tempst >> x >> y >> z;
double sum = std::abs(x)+std::abs(y)+std::abs(z);
- if (sum == 0) throw std::runtime_error("Object "+filepath+" has invalid normal(s).");
+ if (sum == 0) throw std::runtime_error("Invalid normal in "+filepath+":"+boost::lexical_cast<std::string>(linenumber));
x /= sum; y /= sum; z /= sum; // Normalize components
- m_normals.push_back(Vertex(x,y,z));
+ m_normals.push_back(glmath::Vec4(x, y, z, 0.0));
} else if (row.substr(0,2) == "f ") { // Faces
Face f;
- srow >> tempst;
- int v_id;
+ srow >> tempst; // Eat away prefix
// Parse face point's coordinate references
- while (!srow.eof()) {
- srow >> tempst;
- for (size_t i = 1; i <= 3; i++) {
- std::string st_id(getWord(tempst,i,'/'));
+ for (std::string fpoint; srow >> fpoint; ) {
+ for (size_t i = 1; i <= 3; ++i) {
+ std::string st_id(getWord(fpoint,i,'/'));
if (!st_id.empty()) {
- std::istringstream conv_int(st_id);
- conv_int >> v_id;
+ // Vertex indices are 1-based in the file
+ int v_id = boost::lexical_cast<int>(st_id) - 1;
switch (i) {
- // Vertex indices are 1-based in the file
- case 1: f.vertices.push_back(v_id-1); break;
- case 2: f.texcoords.push_back(v_id-1); break;
- case 3: f.normals.push_back(v_id-1); break;
+ case 1: f.vertices.push_back(v_id); break;
+ case 2: f.texcoords.push_back(v_id); break;
+ case 3: f.normals.push_back(v_id); break;
}
}
}
}
+ // FIXME: We only allow triangle faces since the VBO generator/drawer
+ // cannot handle anything else (at least for now).
+ if (f.vertices.size() > 0 && f.vertices.size() != 3)
+ throw std::runtime_error("Only triangle faces allowed in "+filepath+":"+boost::lexical_cast<std::string>(linenumber));
// Face must have equal number of v, vt, vn or none of a kind
if (f.vertices.size() > 0
&& (f.texcoords.empty() || (f.texcoords.size() == f.vertices.size()))
&& (f.normals.empty() || (f.normals.size() == f.vertices.size()))) {
m_faces.push_back(f);
} else {
- throw std::runtime_error("Object "+filepath+" has invalid face(s).");
+ throw std::runtime_error("Invalid face in "+filepath+":"+boost::lexical_cast<std::string>(linenumber));
}
}
}
-}
-
-/// Generate a display list from the object data parsed earlier
-void Object3d::generateDisplayList() {
- if (m_displist != 0) glDeleteLists(m_displist, 1); // Get rid of old
- m_displist = glGenLists(1); // Get id for the list
- glutil::DisplayList displist(m_displist, GL_COMPILE); // From now on, gl-commands go to the list
- std::vector<Face>::const_iterator it;
- // Iterate through faces
- for (it = m_faces.begin(); it != m_faces.end(); ++it) {
- // Select a suitable primitive
- GLenum polyType = GL_POLYGON;
- switch (it->vertices.size()) {
- case 3: polyType = GL_TRIANGLES; break;
- case 4: polyType = GL_QUADS; break;
- }
- glutil::Begin block(polyType);
- // Iterate through face's points
- std::vector<int>::const_iterator it2;
- for (size_t i = 0; i < it->vertices.size(); i++) {
- // Texture coordinates
- if (!it->texcoords.empty())
- glTexCoord2f(m_texcoords[it->texcoords[i]].s, m_texcoords[it->texcoords[i]].t);
- // Normals
- if (!it->normals.empty())
- glNormal3f(
- m_normals[it->normals[i]].x,
- m_normals[it->normals[i]].y,
- m_normals[it->normals[i]].z);
- // Vertices
- glVertex3f(
- m_vertices[it->vertices[i]].x,
- m_vertices[it->vertices[i]].y,
- m_vertices[it->vertices[i]].z);
+ // Construct a vertex array
+ for (std::vector<Face>::const_iterator i = m_faces.begin(); i != m_faces.end(); ++i) {
+ bool hasNormals = !i->normals.empty();
+ bool hasTexCoords = !i->texcoords.empty();
+ for (size_t j = 0; j < i->vertices.size(); ++j) {
+ if (hasNormals) m_va.Normal(m_normals[i->normals[j]]);
+ if (hasTexCoords) m_va.TexCoord(m_texcoords[i->texcoords[j]]);
+ m_va.Vertex(m_vertices[i->vertices[j]]);
}
}
+
}
+
+void Object3d::drawVBO() {
+ m_va.Draw(GL_TRIANGLES);
+}
+
diff --git a/game/3dobject.hh b/game/3dobject.hh
index 9858de8..071eef2 100644
--- a/game/3dobject.hh
+++ b/game/3dobject.hh
@@ -9,68 +9,37 @@
// TODO: Exception handling
// TODO: Texture loading
-// TODO: Switch to vertex arrays
-
-/// Point in 3d space
-struct Vertex {
- Vertex(float x = 0, float y = 0, float z = 0): x(x), y(y), z(z) {}
- float x;
- float y;
- float z;
-};
-
-/// 2d texture coordinate
-struct TexCoord {
- TexCoord(float s = 0, float t = 0): s(s), t(t) {}
- float s;
- float t;
-};
-
-/// A polygon containing links to required point data
-struct Face {
- std::vector<int> vertices;
- std::vector<int> texcoords;
- std::vector<int> normals;
-};
/// A class representing 3d object
/// Non-copyable because of display lists getting messed up
class Object3d: boost::noncopyable {
private:
- std::vector<Vertex> m_vertices; /// vertices
- std::vector<TexCoord> m_texcoords; /// texture coordinates
- std::vector<Vertex> m_normals; /// normals
- std::vector<Face> m_faces; /// faces
- GLuint m_displist; /// display list id
+ glutil::VertexArray m_va;
boost::scoped_ptr<Texture> m_texture; /// texture
/// load a Wavefront .obj 3d object file
void loadWavefrontObj(std::string filepath, float scale = 1.0);
- /// generates a display list for the object
- void generateDisplayList();
public:
+ Object3d() {}
/// constructors
- Object3d(): m_displist(0) {};
- Object3d(std::string filepath, std::string texturepath = "", float scale = 1.0): m_displist(0) {
+ Object3d(std::string filepath, std::string texturepath = "", float scale = 1.0) {
load(filepath, texturepath, scale);
}
- /// destructor
- ~Object3d() {
- if (m_displist != 0) glDeleteLists(m_displist, 1);
- }
/// load a new object file
void load(std::string filepath, std::string texturepath = "", float scale = 1.0) {
if (!texturepath.empty()) m_texture.reset(new Texture(texturepath));
loadWavefrontObj(filepath, scale);
- generateDisplayList();
}
+ void drawVBO();
/// draws the object
- void draw(float x = 0, float y = 0, float z = 0, float s = 1.0) const {
+ void draw(float x = 0, float y = 0, float z = 0, float s = 1.0) {
glutil::PushMatrix pm;
glTranslatef(x, y, z); // Move to position
if (s != 1.0) glScalef(s,s,s); // Scale if needed
if (m_texture) {
UseTexture tex(*m_texture);
- glCallList(m_displist);
- } else glCallList(m_displist);
+ drawVBO();
+ } else {
+ drawVBO();
+ }
}
};
|