Tutorials

Tutorials about HTML, CSS, PHP, Javascript, and Photoshop

  • Home
    Home This is where you can find all the blog posts throughout the site.
  • Categories
    Categories Displays a list of categories from this blog.
  • Tags
    Tags Displays a list of tags that has been used in the blog.
  • Archives
    Archives Contains a list of blog posts that were created previously.
  • Login
Ten20Five Editor

Ten20Five Editor

Ten20Five Editor has not set their biography yet
6506
Final product image
What You'll Be Creating

Introduction

Then you will want your audio to sound the best it possibly can on every device it is compatible with, be it a game or any other kind of app, If you're developing a mobile app that has any kind of audio output

Some mobile devices support Dolby Digital Plus, in particular the Kindle Fire range but also a growing number of other Android devices. For example music or voice, Dolby Digital Plus can dramatically improve the audio output of your app by applying filters that can boost certain parts of your audio output. But luckily Dolby have provided an API that makes using this functionality incredibly simple, This may sound like a complicated thing to achieve

In this tutorial, you will learn how you can develop an app using the Marmalade SDK that can take advantage of Dolby Digital Plus using the Dolby Audio API Marmalade extension. If you're only interested in integrating the Dolby Audio API into your Marmalade application, then head to the bottom of this article

1. Overview

The app we'll be creating in this tutorial will provide a simple user interface containing a top row of buttons that can start and stop different kinds of audio, and a bottom row of buttons to set the type of audio filtering that needs to be applied

And the folders and source files that will make up the project, We'll first create a new Marmalade project

Next I'll explain how to implement the user interface by loading an image containing the required button images and drawing different parts of it on the screen. I will also illustrate how to use Marmalade to respond to user touches

And raw sound sample data, compressed audio, such as an MP3 file, Once the user interface is up and running, I'll show you how to make Marmalade play two different types of audio

Finally, I'll integrate the Dolby Audio API into the app and make use of the different audio filter types it provides

I will assume you are developing on a machine running Windows and already have the Marmalade SDK and a version of Microsoft Visual Studio installed, Throughout this tutorial. Marmalade can also be used with Xcode on OS X and you should still find it easy to follow the steps of this tutorial if you are using a Mac for development

2. Setting Up the Marmalade Project

Step 1: Creating the Source Files and Folder Structure

Create a top level folder calledDolbyTestAppTo contain the project files. Within this folder, create another folder calledSourceLet's also create the files we'll be needing in our project. In theSourceFolder, create five empty files calledButton. CppButton. HMain. CppSound. CppAndSound. H

Step 2: Creating the MKB File

Create an empty file calledDolbyTestApp. MkbIn theDolbyTestAppFolder and open it in a code editor. The MKB file is the file used by Marmalade to configure the project. It brings together all your source and data files, and allows you to configure things like the icons used by your app when installed on the different platforms supported by Marmalade. Add the following to theDolbyTestApp. MkbFile

{
    [Source]
    (source)
    button. Cpp
    button. H
    main. Cpp
    sound. Cpp
    sound. H
}Subprojects
{
    iwgeom
    iwgx
}

TheFilesSection of an MKB file is used to list all the source files needed by your project. It also allows you to apply arbitrary groupings to these files and reference files from more than one folder. Groups are defined using square brackets, so the line[Source]Will create a group calledSourceUsing groups allows us to create organizational folders within the Visual Studio solution that we'll generate in the next step

Rounded brackets are used to specify a folder in which Marmalade should look for source files. The line(source)Instructs Marmalade to look in theSourceSubfolder of our main project folder. The five source files for the project are listed after this line

TheSubprojectsSection allows you to reference other source code modules that your project requires. This project will need theIwgeomAndIwgxModules which are standard components provided by the Marmalade SDK

Step 3: Creating a Visual Studio Project

You can now use the MKB file to create a Visual Studio solution that can be used to build and test the app. Double-click the MKB file in Windows File Explorer, which should automatically launch Visual Studio and open the solution

If you take a look at theDolbyTestAppYou will see that a few new files have been created, folder. There's a folder calledBuild_dolbytestapp_vcxxWhere theXxPart of the folder name depends on the version of Visual Studio you're using. This folder is used by Marmalade to contain all files needed as part of the build process, including the Visual Studio solution file

ADataFolder has also been created, such as graphics and audio files, which is where you should place any resources required by the app. Two files have also been automatically created for you in this folder:

  • App. Icf:Such as the maximum amount of RAM the app can use,  a configuration file that allows you to change app settings
  • App. Config. Txt: used to define custom application specific parameters that can then be used in theApp. IcfFile

For this tutorial you can safely ignore these files as no changes will need to be made to it

3. Building the User Interface

Step 1: Implementing the Main Program Loop

Let's start writing the main program loop for our app. In Visual Studio open theSourceDouble-click the, folder in the Solution ExplorerMain. CppFile, and add the following code snippet

#include "IwGx.h"
#include "s3e.h"
#include "button.h"
#include "sound.h"

void Initialise(){
}Void Terminate(){
}Void Update(){
}Void Render(){
}Int main()
{
    Initialise();
    while (. S3eDeviceCheckQuitRequest())
    {
        Update();
        Render();
    }Terminate();
    return 0;
}

This code snippet starts by including two Marmalade header files. TheIwGx. HFile declares the functions and structures that make up Marmalade'sIwGxWhich can be used for rendering both 2D and 3D graphics in the most efficient manner possible on the target device,  API

TheS3e. HSound support, and much more, touch screen input, header file lets you access all the lower level functions that provide direct access to such things as device settings

TheMainFunction is self-explanatory. It starts by calling InitialiseWhich will perform any setup required by the app and ends by callingTerminateWhich will release any resources used by the app

The main while loop starts after invoking InitialiseThe exit condition is little more than a call toS3eDeviceCheckQuitRequestWhen the user has closed the app or the operating system has requested it to shut down for some reason, which is a Marmalade function that checks to see if a request to close the app has been received, for example

The main loop just calls theUpdateAndRenderFunctions continuously. TheUpdateFunction will be used for things like detecting user input while Render is responsible for drawing the user interface

TheInitialiseUpdateRenderAndTerminateFunctions are all empty at the moment, but the app can be built and executed. If you want to give it a try in theMarmalade Windows SimulatorSelect theX86 DebugOption from theSolution ConfigurationsDrop-down menu in the Visual Studio toolbar, pressF7To build the app, andF5To execute it. You should see something similar to the screenshot below

Step 2: Loading an Image

To display the user interface the app is going to need to have some images to render, so I'll now show you how to load a PNG image into memory and get it into a state where it can be rendered to the screen

First, add the following line at the top of theMain. CppBelow the include statements at the top:, file

CIwTexture* gpTexture = NULL;

TheCIwTextureClass is used to represent a bitmapped image. In the above snippet,  I'm declaring a global variable calledGpTextureWhich will be used to store a pointer to the image used in the app's user interface.  The image file we'll be using is namedUi. Png

The image can be loaded into memory and prepared for use by adding the following lines to theInitialiseFunction

// Initialise Marmalade modules
IwGxInit();

// Create a new CIwTexture and use it to load the GUI image file
gpTexture = new CIwTexture;
gpTexture->LoadFromFile("ui.png");
gpTexture->Upload();

The call toIwGxInitPerforms all the necessary initialization steps needed to allow the app to draw to the screen, which includes being able to load image files

TheGpTextureVariable is used to store a pointer to a new instance of theCIwTextureClass. A call to theLoadFromFileMethod with the file name of the image will load the PNG file into memory and convert it into a suitable format for rendering. The image file name is specified relative to the app'sDataSo you'll need to ensure the, folderUi. PngFile is copied to this folder

The call to theUploadReady to be drawn on screen, method will upload the converted image data to the device's video memory

So when the app shuts down it should release any resources it may be using, It's also important that we tidy up after ourselves. Add the following to theTerminateFunction

// Destroy texture instance
delete gpTexture;

// Terminate Marmalade modules
IwGxTerminate();

The above snippet first destroys theCIwTextureRepresenting the, instanceUi. PngWhich will also release any hardware resources and memory used by the image, image. The call toIwGxTerminate releases any resources that were initially allocated by the call toIwGxInit inInitialise

Step 3: Creating a Button Class

The user interface for this app is going to need some clickable buttons, so let's create a new class calledButtonThat will implement this behavior. Open Button. HFile and enter the following code

#ifndef BUTTON_H
#define BUTTON_H

#include "IwGx.h"

class Button
{
public:
    Button(CIwTexture* apTexture, int32 aHeight, int32 aWidth, int32 aX, int32 aY, int32 aUWidth, int32 aU, int32 aVWidth, int32 aV, bool aEnabled);
    ~Button();

    void Render();

private:
    CIwMaterial* mpMaterial;
    CIwSVec2 mTopLeft;
    CIwSVec2 mSize;
    CIwFVec2 mUV;
    CIwFVec2 mUVSize;
    bool mEnabled;
};

#endif

The constructor for this class is used to position and size the button on screen, and also to indicate which region of the source image should be displayed on the button. It also indicates whether this button should be enabled for user input

The destructor will release any resources created by the constructor while theRenderUnsurprisingly, draw the button on the screen, method will

Some more Marmalade classes are introduced for the member variables of theButtonClass. TheCIwMaterialClass is used by Marmalade to combine images with other rendering information, such as color data that might be required when rendering. TheCIwSVec2Class is a two component vector where each component is a 16-bit signed integer. TheCIwFVec2Class is another two component vector with each component being of type Float

OpenButton. CppAnd add the following code snippet to implement the ButtonClass

Bool aEnabled)
{
    mpMaterial = new CIwMaterial();
    mpMaterial->SetTexture(apTexture);

    float lTextureWidth = (float) apTexture->GetWidth();
    float lTextureHeight = (float) apTexture->GetHeight();

    mTopLeft, int32 aV, int32 aVHeight, int32 aU, int32 aHeight, int32 aY, #include "button.h"
#include "s3e.h"

Button::Button(CIwTexture* apTexture, int32 aX, int32 aWidth, int32 aUWidth. X = aX;
    mTopLeft. Y = aY;
    mSize. X = aWidth;
    mSize. Y = aHeight;
    mUV. X = (float) aU / lTextureWidth;
    mUV. Y = (float) aV / lTextureHeight;
    mUVSize. X = (float) aUWidth / lTextureWidth;
    mUVSize. Y = (float) aVHeight / lTextureHeight;

    mEnabled = aEnabled;
}

The constructor for theButtonClass starts by creating a new instance ofCIwMaterialWhich will be used to render the button image. EachButtonInstance has its own CIwMaterial instance as it makes it easier to change the button's color. Once theCIwMaterialThe, instance is createdCIwTextureInstance passed into the constructor is set as its image

TheMTopLeftMember variable is used to store the top left corner of theButtonOn the screen while MSizeStores the width and height. These values are specified in pixels

TheMUVAndMUVSizeMember variables store the top left corner and dimensions of the image region to be rendered. These are specified as a floating point fraction of the source image size, with0), (0Being the top left corner of the image and1), (1Being the bottom right corner

So you need to convert these into fractional values by dividing by the pixel width or height of the source image, The values passed into the constructor are specified as pixel offsets into the texture. It is possible to find the image dimensions by calling theGetWidthAndGetHeightMethods on theCIwTextureClass

The next code snippet shows the class's destructor. As you can see, all it has to do is delete theCIwMaterialInstance that was allocated in the constructor

Button::~Button()
{
    delete mpMaterial;
}

TheRenderMethod will draw theButtonOn screen. It starts by checking theMEnabledMember variable and sets the ambient color of theCIwMaterialSo that theButtonIs drawn at full brightness when enabled and a bit darker when disabled.  A call toIwGxSetMaterialTells Marmalade whichCIwMaterialInstance to draw with and IwGxDrawRectScreenSpaceWill cause theButtonTo be rendered

Void Button::Render()
{
    if (. &mUVSize);, 255);

    IwGxSetMaterial(mpMaterial);
    IwGxDrawRectScreenSpace(&mTopLeft, &mUV, &mSize, 96, mEnabled)
        mpMaterial->SetColAmbient(96, 255);
    else
        mpMaterial->SetColAmbient(255, 255, 96, 255
}

Step 4: Laying Out the User Interface

The user interface for the app is going to automatically adjust to the screen resolution of the device it is running on, but to make things a little simpler we're only going to support landscape orientation. The first step is to force landscape by entering the following into theApp. IcfFile

[S3E]
DispFixRot=LANDSCAPE
MemSize0=12000000

{OS=WINDOWS}WinWidth=1280
WinHeight=800
{}

All settings in the app.icf file have a group associated with them. Square brackets are used to denote a group, so in this case the line [S3E] indicates the settings that follow are part of the S3E group, which is a group reserved by Marmalade for hardware related settings.

The DispFixRot setting will force the screen to always be in landscape orientation. The MemSize0 setting has also been added to increase the amount of RAM the app has available to it. When you add sound support later in this tutorial, the extra RAM will be needed to store the sound sample data.

The WinWidth and WinHeight settings are used to specify the dimensions of the windows used when running in the simulator. The {OS=WINDOWS} line ensures these settings are only used on the Windows simulator. The {}Line disables this restriction so any settings following it become global settings again

You can now start creating the elements of the user interface. Open theMain. CppFile and start by adding the following snippet after the declaration of theGpTextureGlobal variable

BUTTON_FILTER_OFF, BUTTON_FILTER_GAME, BUTTON_AUDIO_OFF, BUTTON_FILTER_MOVIE, BUTTON_COUNT, BUTTON_AUDIO_MUSIC, BUTTON_FILTER_MUSIC, BUTTON_AUDIO_SFX, BUTTON_FILTER_VOICE, enum ButtonIDs
{
    BUTTON_AUDIO_LABEL, BUTTON_FILTER_LABEL, BUTTON_AUDIO_SPEECH
};

Button* gButton[BUTTON_COUNT];

bool gDolbySupported;

TheButtonIDsEnumeration provides a convenient way of naming each of the user interface elements. TheGButtonArray will store pointers to each of theButtonInstances in the user interface and theGDolbySupportedBoolean flag will be used to disable parts of the interface if the target device does not support the Dolby Audio API

To create the requiredButtonInstances, add the following code to the end of theInitialiseFunction

// Check for Dolby Digital Plus support
gDolbySupported = false;

// Create our interface buttons
int32 lSize = IwGxGetScreenWidth() / 5;
int32 lGap = (int32) ((float) lSize * 0. 1f);
lSize = (int32) ((float) lSize * 0. 160, lTopRowY - lLabelHeight - 10, lLabelHeight, 353, 173, lBottomRowX, lTopRowX + (3 * (lSize + lGap)), lSize, 174, lLabelHeight, lTopRowX + (2 * (lSize + lGap)), lBottomRowY, gDolbySupported);
gButton[BUTTON_FILTER_VOICE] = new Button(gpTexture, 347, 42, lBottomRowY, 3, 160, lSize, true);
gButton[BUTTON_AUDIO_SPEECH] = new Button(gpTexture, lSize, lSize, 160, 160, 4, false);
gButton[BUTTON_AUDIO_OFF] = new Button(gpTexture, lTopRowX + (lSize + lGap), lBottomRowX + (lSize + lGap), 175, 160, lLabelWidth, 160, 347, lSize, 240, 160, lSize, 42, lBottomRowY, true);
gButton[BUTTON_AUDIO_MUSIC] = new Button(gpTexture, 160, lBottomRowY, lSize, 240, lSize, lTopRowY, 160, 173, lBottomRowX + (4 * (lSize + lGap)), lLabelWidth, 160, lSize, lTopRowY, 2, false);
gButton[BUTTON_FILTER_OFF] = new Button(gpTexture, gDolbySupported);
gButton[BUTTON_FILTER_GAME] = new Button(gpTexture, 160, lSize, 160, 175, 160, lBottomRowX + (2 * (lSize + lGap)), lSize, 3, 160, 173, 9f);

int32 lRowSize = IwGxGetScreenHeight() / 4;
int32 lTopRowX = (IwGxGetScreenWidth() - (4 * lSize) -
                 (3 * lGap)) / 2;
int32 lTopRowY = lRowSize - (lSize / 2);
int32 lBottomRowX = (IwGxGetScreenWidth() - (5 * lSize) -
                    (4 * lGap)) / 2;
int32 lBottomRowY = (3 * lRowSize) - (lSize / 2);
int32 lLabelWidth = (240 * lSize) / 160;
int32 lLabelHeight = (42 * lSize) / 160;
int32 lLabelX = (IwGxGetScreenWidth() - lLabelWidth) / 2;

gButton[BUTTON_AUDIO_LABEL] = new Button(gpTexture, 160, 2, lLabelX, lTopRowX, true);
gButton[BUTTON_AUDIO_SFX] = new Button(gpTexture, lSize, gDolbySupported);
gButton[BUTTON_FILTER_MOVIE] = new Button(gpTexture, 160, 160, lBottomRowY, lTopRowY, lBottomRowX + (3 * (lSize + lGap)), lTopRowY, 3, lSize, lLabelX, 173, gDolbySupported);, 174, 160, 408, gDolbySupported);
gButton[BUTTON_FILTER_MUSIC] = new Button(gpTexture, lBottomRowY - lLabelHeight - 10, true);
gButton[BUTTON_FILTER_LABEL] = new Button(gpTexture, 3, lSize, lSize, 2, 3, lSize, 2, lSize, lSize

We start by assuming the Dolby Audio API isn't supported by the user's device by setting, In this code blockGDolbySupportedToFalseNext, theIwGxGetScreenWidthAndIwGxGetScreenHeightFunctions are used to infer the dimensions of the screen and suitable sizes and positions for the user interface elements are calculated. A number of, FinallyButtonDefining the user interface, instances are created

You may have noticed that the ButtonInstances for controlling the current filter type use theGDolbySupportedVariable to indicate whether they should be enabled or not. I've cheated a little by using two disabledButtonInstances to draw some labels

But you should always ensure that you tidy up after yourself, You've now created the user interface. Add the following code block at the start of theTerminateFunction to release theButtonInstances when the app shuts down

// Destroy Button instances
for (uint32 i = 0; i < BUTTON_COUNT; i++)
{
    delete gButton[i];
}

But it still won't be rendered, the user interface would be created and destroyed, If you were to run the app at this point. You will need to add the following code snippet to theRenderFunction before anything will be displayed on screen

// Clear the screen to a pale blue
IwGxSetColClear(128, 0);
IwGxClear(IW_GX_COLOUR_BUFFER_F, 255, 224. IW_GX_DEPTH_BUFFER_F);

// Render the UI
for (uint32 i = 0; i < BUTTON_COUNT; i++)
{
    gButton[i]->Render();
}// Finish rendering and display on screen
IwGxFlush();
IwGxSwapBuffers();

The above code snippet first clears the screen to light blue using calls toIwGxSetColClearAndIwGxClearThen, the user interface is drawn by calling theRenderMethod on each of theButtonInstances. Finally, a call toIwGxFlushCauses all render requests to be completed beforeIwGxSwapBuffersMakes the user interface actually appear on screen

If you build and run the app in theMarmalade Windows SimulatorWith the bottom row drawn darker as they are in a disabled state, you should see two rows of buttons


Step 5: Responding to User Input

Let's now make this app a little more interactive by tracking touch input from the user. You'll need to add a new method to the, To startButtonClass, so openButton. HAnd add the following method prototypes

Int32 aY);
bool IsReleased();, int32 aX, void Update(uint32 aTouchState

You should also add the following additional private member variables to theButtonClass

Bool mPressed;
bool mDown;
bool mReleased;

Open, NextButton. Cpp and add the following lines to the end of the class constructor to ensure the new member variables are initialized to sensible values

MDown = false;
mPressed = false;
mReleased = false;

The next code block shows the implementations of theUpdateAndIsReleasedMethods

Void Button::Update(uint32 aTouchState, int32 aX, int32 aY)
{
    if (. MEnabled)
        return;

    // Check if the touch position is within bounds of
    // this button
    aX -= mTopLeft. X;
    aY -= mTopLeft. Y;

    bool lInBounds = (aX >= 0) && (aX < mSize. X) &&
                     (aY >= 0) && (aY < mSize. Y);

    // Clear the released flag
    mReleased = false;

    // Check touch screen state
    if (aTouchState & S3E_POINTER_STATE_PRESSED)
    {
        // User has just touched the screen
        if (lInBounds)
        {
            mPressed = true;
            mDown = true;
        }
    }Check if user
        // is still touching inside it
        if (mPressed)
        {
            mDown = lInBounds;, else if (aTouchState & S3E_POINTER_STATE_DOWN)
    {
        // If button has been pressed
        }
    }We set the release flag to true
        if (mPressed && mDown)
        {
            mReleased = true;, else if (aTouchState & S3E_POINTER_STATE_RELEASED)
    {
        // If user has released screen over a pressed
        // button
        }// Button is no longer pressed or down. MDown = false;
        mPressed = false;
    }
}Bool Button::IsReleased()
{
    return mReleased;
}

TheUpdateThe current touch status and the screen coordinates of the touch, method takes three parameters. You'll learn how to obtain this information shortly. The method first checks to see if theButtonIs disabled and immediately exits if it is. The screen coordinates passed to the method are then checked against the bounds of theButtonTo see if theButtonIs being touched

TheATouchStateParameter of theUpdateMethod is a bit mask comprised of three possible flags:

  • S3E_POINTER_STATE_PRESSED is set when the user has just touched the screen
  • S3E_POINTER_STATE_DOWNIs set while the screen is being touched
  • S3E_POINTER_STATE_RELEASEDIs set when the user lifts its finger from the screen

TheUpdateMethod uses the current value ofATouchStateTo update the internal member variables of the class accordingly

TheIsReleasedMethod is trivial, it returns the current state of theMReleasedVariable

We need to make one final change to theButtonClass. In the RenderMethod, we draw the Button slightly darker while the user is pressing it. This visual feedback benefits the user experience of the application. Change the beginning of theRenderMethod to the following:

If (. 96, 255);
else
    mpMaterial->SetColAmbient(255, 192, 255, 255);
else if (mDown)
    mpMaterial->SetColAmbient(192, 192, 255);, 255, 96, mEnabled)
    mpMaterial->SetColAmbient(96

With theButtonClass updated, you now just have to add some logic to detect touch input from the user. OpenMain. Cpp again and add the following to the currently emptyUpdateFunction:

// Allow device OS time to do its processing
s3eDeviceYield(0);

// Update pointer (actually touch screen. X, ) inputs
s3ePointerUpdate();

// Read current touch screen inputs and use them to update Button states
uint32 lTouchState =
           s3ePointerGetState(S3E_POINTER_BUTTON_SELECT);
int32 x = s3ePointerGetX();
int32 y = s3ePointerGetY();
for (uint32 i = 0; i < BUTTON_COUNT; i++)
{
    gButton[i]->Update(lTouchState, y);
}

The call toS3eDeviceYieldSuch as touch input, incoming calls, etc, is vitally important in a Marmalade app as it allows the device's operating system time to handle any events. TheS3ePointerUpdateFunction takes a snapshot of the current touch screen status

The current state of the first detected touch input is then found by callingS3ePointerGetStateIt returns a value using the bit mask I described earlier.  TheS3ePointerFunctions are also used to detect mouse events on desktop operating systems. The value passed toS3ePointerGetStateIsS3E_POINTER_BUTTON_SELECTDepending on the capabilities of the device the app is running on, which will return the status of the first detected touch event or the left mouse button

S3ePointerGetXAndS3ePointerGetYReturn the screen coordinates of the touch. We then loop through the button instances and call Button::Update on each button, passing in the current touch screen status and touch coordinates

The app is now capable of detecting user input and theButtonInstances will change color when they're pressed

4. Adding Audio Playback

Step 1: Playing Compressed Audio Files

Such as MP3 files, Playing back compressed audio files, is incredibly easy in Marmalade. It only takes a single line of code, In fact. Add the following code block to the end of theUpdateFunction inMain. Cpp

// Check for button presses
if (gButton[BUTTON_AUDIO_MUSIC]->IsReleased())
{
    s3eAudioPlay("black-hole.mp3");
}

Whenever the user presses and releases the music note button on the top row of buttons,  the app will call theS3eAudioPlayWhich will attempt to play the MP3 file called, functionBlack-hole. Mp3This file must exist in the projectData folder, so it can be located at runtime

TheBlack-hole. Mp3File was obtained fromhttp://www.freesfx.co.uk and was composed by Craig Riley (SOCAN)

Step 2: Playing Uncompressed Sound Samples

Playing uncompressed sound samples is also possible in Marmalade. It's more flexible as it allows you to play multiple sounds at a time, whereas most devices will only allow a single compressed audio track to be played back at any time, While it isn't quite as simple as playing a compressed sound file

Marmalade expects sound sample data to be in 16-bit signed PCM format and most audio editing software will allow you to save out files in this format using theWAV file formatMarmalade doesn't support the WAV file format directly, However, so for the purposes of this tutorial I have taken sound files saved in WAV format and removed the header from the file leaving only the sample data. You'd probably want to support WAV files directly, but that is beyond the scope of this tutorial, For your own apps

I've added two sound files to theDataFolder calledFemale-counting. RawAndGun-battle. RawThe original WAV format files were obtained fromhttp://soundbible.com and have been released under the Attribution 3. 0 Creative Commons license

It is necessary to have the sound data in memory, In order to play a sampled sound effect. I have created aSoundClass that will take care of this for us. To implement this class, open upSound. H and add the following code block to it:

#ifndef SOUND_H
#define SOUND_H

#include "s3e.h"

class Sound
{
public:
    Sound(const char* apFileName);
    ~Sound();

    void Play();

private:
    int16* mpSoundData;
    uint32 mSamples;
};

#endif

Open, NextSound. CppAnd insert the following code block:

#include "sound.h"

Sound::Sound(const char* apFileName)
{
    // Attempt to open the sound effect file
    s3eFile* f = s3eFileOpen(apFileName, f);

        // Close the file
        s3eFileClose(f);, S3E_FILESEEK_END);

        // Number of samples is file size divided by the
        // size of an int16
        mSamples = s3eFileTell(f) / sizeof(int16);
        s3eFileSeek(f, sizeof(int16), "rb");
    if (f)
    {
        // Seek to end of file to find its length
        s3eFileSeek(f, mSamples, S3E_FILESEEK_SET);

        // Allocate buffer for sound data
        mpSoundData = new int16[mSamples];

        // Read in sound data
        s3eFileRead(mpSoundData
    }Else
    {
        // File open failed, zero the member variables
        mpSoundData = NULL;
        mSamples = 0;
    }
}Sound::~Sound()
{
    if (mpSoundData)
        delete[] mpSoundData;
}Void Sound::Play()
{
    if (mpSoundData)
    {
        int lChannel = s3eSoundGetFreeChannel();
        s3eSoundChannelPlay(lChannel, mSamples, mpSoundData, 0);
    }
}

The constructor takes the file name of a sound effect and finds the length of the file in bytes. And the file is read into this buffer, big enough to hold the entire sound, An array of 16-bit signed integers is created. The destructor deletes this buffer

ThePlayMethod will actually start the sound sample playing. It does this by first asking Marmalade for a free sound channel with a call toS3eSoundGetFreeChannel The sound is then started on that channel by callingS3eSoundChannelPlayAnd the number of sound samples in the sound,  passing in the channel number, start of the sound buffer. The remaining two parameters indicate whether the sound sample should loop when it reaches the end and the offset into the sample data where subsequent loops should begin playing. By passing in zero for both of these parameters, the entire sound effect will loop continuously

With theSoundReturn to, class implementedMain. CppAnd add some code to load and destroy the sound samples and to start the sounds playing when the user presses a button. Start by adding two new global variables after the declaration of theGpButtonArray

Sound* gpGunBattleSound;
Sound* gpFemaleCountingSound;

Add the following to the end of the, NextInitialiseFunction. This code block will load the two sound files into memory and then set the default sound sample frequency to 44100Hz, which just so happens to be the frequency of both sounds used in the app

44100);, // Load sound effects into memory
gpGunBattleSound = new Sound("gun-battle.raw");
gpFemaleCountingSound = new Sound("female-counting.raw");

// Configure default sample rate for s3eSound
s3eSoundSetInt(S3E_SOUND_DEFAULT_FREQ

We also need to release the sound data on shut down. We do this by adding the following code block to the beginning of the Terminate function to destroy theSoundInstances

// Destroy sound effects
delete gpGunBattleSound;
delete gpFemaleCountingSound;

Finally, add the next code snippet to the end of the UpdateFunction, immediately after the end of the last IfStatement. This will start the sound effects playing in response to the user pressing the correct buttons

Else if (gButton[BUTTON_AUDIO_SFX]->IsReleased())
{
    if (gpGunBattleSound)
        gpGunBattleSound->Play();
}Else if (gButton[BUTTON_AUDIO_SPEECH]->IsReleased())
{
    if (gpFemaleCountingSound)
        gpFemaleCountingSound->Play();
}

Step 3: Stopping Audio Output

Chances are you'll also eventually want to stop it playing, If you want to start a piece of audio playing. The code block below illustrates how to do this in Marmalade by having a button in the user interface stop all currently playing audio. Add the following block to the end of theIf. ElseBlock at the very end of theUpdateFunction inMain. Cpp

Else if (gButton[BUTTON_AUDIO_OFF]->IsReleased())
{
    s3eAudioStop();
    s3eSoundStopAllChannels();
}

The call toS3eAudioStopWill stop playback of any compressed audio track that is playing, while S3eSoundStopAllChannelsWill stop all uncompressed sampled sounds. It is possible to stop sampled sounds on a channel by channel basis, but for the purposes of this app it's fine to stop all channels that are currently active

5. Integrating the Dolby Audio API

Step 1: Downloading the Dolby Audio API Marmalade Extension

It's time to turn our attention to the Dolby Audio API, Now that we have an app that can play some sounds. Adding support for the Dolby Audio API is incredibly simple and can be done in no more than five minutes, As you'll notice

You first need to obtain the Dolby Audio API Marmalade extension by visiting theDolby Developer websiteAfter creating a free developer account, you can download the Marmalade extension from theFrameworkTab

Step 2: Adding the Dolby Audio API to the Project

Extract the Dolby Audio API Marmalade extension archive to a folder on your development machine and find in the extracted Libraries folder for a folder named S3eDolbyAudioCopy this folder and its contents into our project's DolbyTestApp folder, alongside the Source and Data folders

To include the extension in your project, edit the DolbyTestApp. Mkb file and add S3eDolbyAudio to the list of SubprojectsIf you then rebuild the project in Visual Studio, the MKB file will be reprocessed and the Dolby Audio API extension will be added to to the project

Step 3: Initializing the Dolby Audio API

Before you can use the functionality of the Dolby Audio API, you must first check whether the device your app is running on supports Dolby Digital Plus. Edit, To implement the necessary checksMain. CppAnd add the following#include at the top of the file

#include "s3eDolbyAudio.h"

Next, declare a global variable namedGDolbyInitialised after the declaration ofGDolbySupported

Bool gDolbyInitialised;

You can add the following code block to the, To check whether Dolby Digital Plus is supportedInitialiseFunction, after the statement GDolbySupported = false;

If (s3eDolbyAudioAvailable() == S3E_TRUE)
{
    if (s3eDolbyAudioSupported() == S3E_TRUE)
    {
        gDolbySupported = true;
    }S3eDeviceYield(0);
}

The first call toS3eDolbyAudioAvailableChecks whether the Dolby Audio API extension is supported on the target platform. If the extension is available a call is made toS3eDolbyAudioSupportedWhich will returnS3E_TRUEIf the target device supports Dolby Digital Plus. The, If supportedGDolbySupportedFlag is setTrue

The call toS3eDeviceYieldIs to give the device time to perform background processing after doing the Dolby Digital Plus support test. So the, Dolby recommend that you do not initialize Dolby Digital Plus immediately after checking whether it's supportedS3eDeviceYield call will help prevent issues during initialization

At the end of theInitialiseFunction, you can initialize Dolby Digital Plus by calling theS3eDolbyAudioInitializeFunction. Only if this function returnsS3E_TRUEWill theGDolbyInitialisedFlag be setTrueThe code you need to add is as follows:

// Initialise Dolby API
if (gDolbySupported)
{
    if (s3eDolbyAudioInitialize() == S3E_TRUE)
    {
        gDolbyInitialised = true;
    }
}

So add the following to the, You should also shut down the Dolby Audio API when your program terminatesTerminateBefore the call to, functionIwGxTerminate

// Release resources used by Dolby API
if (gDolbySupported)
{
    s3eDolbyAudioRelease();
}

Step 4: Handling Suspending and Resuming

It isn't uncommon for your app to be suspended, either because the user wants to perform a different task or an event such as an incoming call has occurred, Since mobile phones and tablets are used for a plethora of tasks. Under these conditions the Dolby Audio API should be suspended so that it isn't active whilst your app is paused or running as a background task

Marmalade allows us to set up some callback functions that will be triggered whenever the app loses or regains focus. Add the following code to the, To implement theseInitialiseFunction immediately after the check for Dolby Digital Plus support

AppSuspended, AppResumed, NULL);
s3eDeviceRegister(S3E_DEVICE_BACKGROUND, NULL);, // Initialise Pause/Resume callbacks
s3eDeviceRegister(S3E_DEVICE_PAUSE, AppSuspended, NULL);
s3eDeviceRegister(S3E_DEVICE_UNPAUSE, NULL);
s3eDeviceRegister(S3E_DEVICE_FOREGROUND, AppResumed

You should also remove these callback functions at shut down, so add the following lines to theTerminateFunction immediately after theDelete gpTextureLine

// Disable Pause/Resume callbacks
s3eDeviceUnRegister(S3E_DEVICE_PAUSE, AppResumed);, AppResumed);
s3eDeviceUnRegister(S3E_DEVICE_FOREGROUND, AppSuspended);
s3eDeviceUnRegister(S3E_DEVICE_UNPAUSE, AppSuspended);
s3eDeviceUnRegister(S3E_DEVICE_BACKGROUND

Now you just need to implement theAppSuspendedAndAppResumedCallback functions. Add this code after the declaration of the global variables at the top ofMain. Cpp

Void* apUserData)
{
    if (gDolbyInitialised)
        s3eDolbyAudioSuspendSession();
    return 0;, int32 AppSuspended(void* apSystemData
}Int32 AppResumed(void* apSystemData, void* apUserData)
{
    if (gDolbyInitialised)
        s3eDolbyAudioRestartSession();
    return 0;
}

The, When the app is suspended or goes into background processingAppSuspendedCallback will be triggered, which callsS3eDolbyAudioSuspendSessionIf theGDolbyInitialisedFlag isTrue , When the app regains focusAppResumedWhich invokes , will be calledS3eDolbyAudioRestartSessionIf the Dolby Audio API has been initialized

Step 5: Using the Dolby Audio API Filters

The final step to integrating the Dolby Audio API is to actually make use of the different audio filters it provides.  which suit different types of audio output, There are four predefined filters availableMovieMusicVoiceAndGame

Once the Dolby Audio API is active, passS3E_TRUE toS3eDolbyAudioSetEnabledTo ensure filtering support is switched on followed by a call toS3eDolbyAudioSetProfileIf you want to stop filtering you can do this with a call toS3eDolbyAudioSetEnabledPassing inS3E_FALSE

Add the following code block to the end of theUpdateFunction to enable the bottom row of buttons to switch between the different filter types

If (gButton[BUTTON_FILTER_OFF]->IsReleased())
{
    if (gDolbyInitialised)
    {
        s3eDolbyAudioSetEnabled(S3E_FALSE);
    }
}Else if (gButton[BUTTON_FILTER_MUSIC]->IsReleased())
{
    if (gDolbyInitialised)
    {
        s3eDolbyAudioSetEnabled(S3E_TRUE);
        s3eDolbyAudioSetProfile(MUSIC);
    }
}Else if (gButton[BUTTON_FILTER_MOVIE]->IsReleased())
{
    if (gDolbyInitialised)
    {
        s3eDolbyAudioSetEnabled(S3E_TRUE);
        s3eDolbyAudioSetProfile(MOVIE);
    }
}Else if (gButton[BUTTON_FILTER_GAME]->IsReleased())
{
    if (gDolbyInitialised)
    {
        s3eDolbyAudioSetEnabled(S3E_TRUE);
        s3eDolbyAudioSetProfile(GAME);
    }
}Else if (gButton[BUTTON_FILTER_VOICE]->IsReleased())
{
    if (gDolbyInitialised)
    {
        s3eDolbyAudioSetEnabled(S3E_TRUE);
        s3eDolbyAudioSetProfile(VOICE);
    }
}

6. Making a Device Build

Step 1: Including the Resource Files

Let's round off by getting the app running on a device. The first step is to ensure that the final deployment package contains all the necessary resource files. EditDolbyTestApp. MkbAnd append the following code snippet to the end of the file

Assets
{
    [default]
    (data)
    black-hole. Mp3
    female-counting. Raw
    gun-battle. Raw
    ui. Png
}Deployments
{
    name="DolbyTestApp"
    caption="Dolby Test App"

    assets="default"
}

TheAssetsSection of an MKB file is used to list the resource files that need to be shipped with the executable in order for the app to run. The format is similar to theFilesUsing square brackets to make groupings of files and rounded brackets to indicate folder names where files can be found, section

The Deployments section lets you configure the final installation package. The Name parameter lets us specify the file name that will be used for the package file and the Caption parameter is used to declare the text that will appear beneath the app icon when it is installed on a device. TheAssetsParameter references one of the groups defined in theAssetsSo it is possible to switch between different asset sets should you need to, if you want to create a full and lite version of your app, section, for example

Step 2: Making the Installation Package

You must first compile the app source code for the type of processor used by the target device, To create an installation package for the device of your choosing. In most cases, so you should select the, this will be an ARM chipGCC ARM ReleaseOption from theSolutions ConfigurationsDrop-down menu at the top of the Visual Studio toolbar

PressF7Followed by , to build the appF5To launch theMarmalade Deployment Tool

TheMarmalade Deployment Tool shows a wizard to create the installation packages. If you want to create an Android build, then you must first choose the build type, which will also run on Kindle Fire devices, For exampleARM GCC Release

After clicking theNext Stage >Button, you will be asked to choose which project configuration you wish to deploy. Check the Default configuration,  and click the , which is the only configuration of our projectNext Stage >Button once more to see a list of platforms supported by your version of Marmalade. Tick the box next toAndroidAnd click the Next Stage > button again

You can now choose what actions to take when creating the installation package. The drop-down menu lets you choose to just create the package, install, create it and install it on a device connected via USB, which means you'll need to manually install it on a device, or create, and run the app on a connected device. You'll need the, For the latter two options to workAndroid SDKBecause these options make use of the, installed on your development machineADB which is part of the Android SDK, tool

But you can read more about this topic on the, A discussion of how to set up an Android device for development use is beyond the scope of this tutorialAndroid Developer website

Conclusion

As you can see, using the Dolby Audio API in a Marmalade app is a very simple process. Most of this tutorial was concerned with setting up the user interface or playing sounds rather than integrating the Dolby Audio API

Then you really should consider adding support for Dolby Digital Plus to give your users the best possible audio experience, If you've written a game or any other kind of app in Marmalade that has rich audio output

Continue reading
0
7140

We will dive deeply into OpenShift to understand the custom build and deployment process, In this tutorial. We will also learn the command-line tool for logging and troubleshooting when our application is down

We'll also cover some bonus tips to leverage the SSH features offered by OpenShift. Source file and media files, we'll look at how to synchronize our local environment with remote environment including database, Additionally

The first part of this series gave a quick overview of OpenShift. And setup a custom domain, our account name, We discussed how to create the app, get the server URL.  

We did almost all of those tasks using the web interface which is great and very convenient; however, in addition to the dashboard,  OpenShift offers a powerful client tool callRhc clientWe can invoke it from command line to perform OpenShift administration and maintenance. You can create apps, Once you;ve installed the tool, add cartridges, and add gears quickly. It's a Swiss army knife. You may not need it but it's very handy

Install OpenShift Command Line Client

The OpenShift document is very clear aboutinstalling this client libraryThe library is written in Ruby so make sure that you have Ruby installed. Basically, you only need to installthe rhc gem on Mac or Linux. You've got a large chance that Git and Ruby are already installed so you only need to run:

Sudo gem install rhc

Then setup it with your username and password. When you are asked to Generate a token nowTypeYes

Rhc setup
# Once it's done, you should see something similar:
Saving configuration to /Users/kureikain/. Openshift/express. Conf. Done Checking for git. Found git version 1. 8. 5. 2 (Apple Git-48) Checking common problems. Done Checking for a domain. Tutsplus Checking for applications. Found 2 demo2 http://demo2-tutsplus. Rhcloud. Com/ php http://php-tutsplus. Rhcloud. Com/ You are using 3 of 3 total gears The following gear sizes are available to you: small Your client tools are now configured


Now that we have the utility installed, let's play around with it.  

The first thing to note is that the RhcCommand will give you a list of available commands. You can learn from there withRhc helpIt shows a brief overview of each commandRhc help command_nameWill show you how to use a particular command

$ rhc help
Getting started: setup Connects to OpenShift and sets up your keys and domain create-app Create an application apps List all your applications cartridges List available cartridges add-cartridge Add a cartridge to your application set-env Set one or more environment variable(s) to your application logout End the current session Working with apps: tail Tail the logs of an application port-forward Forward remote ports to the workstation threaddump Trigger a thread dump for JBoss and Ruby apps snapshot Save the current state of your application locally git-clone Clone and configure an application's repository locally Management commands: account Display details about your OpenShift account alias Add or remove a custom domain name for an app app Commands for creating and managing applications authorization Manage your authorization tokens cartridge Manage your application cartridges deployment Commands for deploying and managing deployments of an application domain Add or rename the container for your apps env Manages user-defined environment variables set on a given application member Manage membership on domains server Display information about the status of the OpenShift service ssh SSH into the specified application sshkey Add and remove keys for Git and SSH


$ rhc help app

Usage: rhc app <action>

Creates and controls an OpenShift application. To see the list of all applications use the rhc domain show command. Note that delete
Is not reversible and will stop your application and then remove the application and repo from the remote server. No local changes are
Made

List of Actions
Configure Configure several properties that apply to an application
Create Create an application
Delete Delete an application from the server
Deploy Deploy a git reference or binary file of an application
Force-stop Stops all application processes
Reload Reload the application's configuration
Restart Restart the application
Scale-down Scale down the application's web cartridge
Scale-up Scale up the application's web cartridge
Show Show information about an application
Start Start the application
Stop Stop the application
Tidy Clean out the application's logs and tmp directories and tidy up the git repo on the server

You have to specify the app name with, For the command that interacts with an app-a appnameIf you run the commands inside your Git repository that you cloned before, or just append app name; however, you may omit it. The app name can be see withRhc appCommand. Example with my previousDemo2App. Instead of typing whole the account name and server name to SSH into it, I can use this command:


$ rhc ssh demo2
# Output 
Connecting to 532bd7655004468bcf0000e1@demo2-tutsplus. Rhcloud. Com

It can be useful to trigger a deployment without any pushing, Sometime. Say we want to deploy WordPress from a particular Git commit or a special branch.  

See the following example:

# Deploy branch master on demo2 app
$ rhc app-deploy master -a demo2

# Deploy bracch branch/deploy on demo2 app
$ rhc app-deploy branch/deploy -a demo2

Or show app information:


Demo2 [master] ⚡ rhc app-show demo2
demo2 @ http://demo2-tutsplus. Rhcloud. Com/ (uuid: 532bd7655004468bcf0000e1)
---------------------------------------------------------------------------
  Domain:          tutsplus
  Created:         Mar 20 11:08 PM
  Gears:           1 (defaults to small)
  Git URL:         ssh://532bd7655004468bcf0000e1@demo2-tutsplus. Rhcloud. Com/~/git/demo2. Git/
  Initial Git URL: https://github. Com/openshift/wordpress-example. Git
  SSH:             532bd7655004468bcf0000e1@demo2-tutsplus. Rhcloud. Com
  Deployment:      auto (on git push)
  Aliases:         openshift. Axcoto. Com

  mysql-5. 5 (MySQL 5. 5)
  ---------------------
    Gears:          Located with php-5. Phpmyadmin-4
    Connection URL: mysql://$OPENSHIFT_MYSQL_DB_HOST:$OPENSHIFT_MYSQL_DB_PORT/
    Database Name:  demo2
    Password:       DyANqgKAFbTT
    Username:       adminL1GxwjI

  php-5, 4. 4 (PHP 5. 4)
  -----------------
    Gears: Located with mysql-5. 5, phpmyadmin-4

  phpmyadmin-4 (phpMyAdmin 4. 0)
  -----------------------------
    Gears:          Located with mysql-5. 5, php-5. 4
    Connection URL: https://demo2-tutsplus. Rhcloud. Com/phpmyadmin/

You can also save a snapshot of current app:

$ rhc snapshot-save demo2
Pulling down a snapshot to demo2. Tar. Gz. Creating and sending tar. Gz

RESULT:
Success

Or you can add some cartridges:

# add mysql-5. 5 cartridge into our app
$ rhc cartridge add -a demo2 -c mysql-5. 1

# add php5-4  cartridge into our app
$ rhc cartridge add -a demo2 -c php-5. 4

Of course, feel free to experiment with a number of different commands. It's easy to rollback your changes, and since everything is under source control, It's relatively straight forward and easy to understand

What Happens When You Push to Your Repository?

We saw that whenever a, In the first articleGit pushThe app will be deployed, to deploy a branch is triggered. According to the OpenShift documentHere is a break down of what happen:

  1. You run aGit pushOn your computer, your changes are sent to your OpenShift application
  2. The application is shut down
  3. Your changes are copied on the server into the correct location
  4. OpenShift invokes your build hooks - script files you've placed in your Git repository
  5. Your application is started

Step 4 is handle by script files in yourOpenshift/action_hooksOpenShift will execute those script files checked into your Git repository at specific points during the deployment process.  

The deployment process will continue normally,  If the corresponding script file does not exist. Again, all hooks must be placed in theOpenshift/action_hooks/Directory in your application repository. The individual phases of each build are:

  1. Pre-Receive During your push, OpenShift checks to ensure that your application is in a consistent state. There is no hook for this step
  2. Pre-BuildThis happens after the application is stopped and the new repo dir has been deployed but before the build. Runs theOpenshift/action_hooks/pre_buildScript
  3. Build This builds your application, executes the, downloads required dependencyOpenshift/action_hooks/buildScript and preps everything for deployment. No any special build requirements, we don't use this step much since WordPress just need to drop into document root and run by web server, In scope of WordPress. We usually use this hook to Download WordPress and extract it into correct location on OpenShift
  4. DeployThis step happens right before the application is issued a start. Any required prep work to get the application ready to be started should be done in theOpenshift/action_hooks/deployHook. Copy plugin and theme (, we use this hook to copy the data of the above build into document root, In scope of WordPressOpenshift/themes and. Openshift/plugin) to the correct location
  5. Post-DeploySome applications may need to interact with the running application to complete the deployment process. After the application starts, theOpenshift/action_hooks/post_deployHook will be executed

You can totally customize these scripts for your own purposes. Meaning you can write it in any language no matter it's Bash, The build scripts are executed directly, Ruby or Python as long as you put correctshebangSuch as if you used bash

#. /bin/bash
Or Ruby
#. /usr/bin/ruby

I prefer to write simple Bash script for build purpose and I will use it in this tutorial. Bash is every where and its syntax is so easy that you don't need to know Bash to understand some scripts actually

We utilize that build process to prepare for some data, With respect to WordPress, we don't do many tasks in the building process because PHP doesn't requires building; however. TheOpenshift/action_hooks/buildIf not it will download the WordPress from WordPress, checks whether WordPress is created on OpenShift. Org, extract and copy content of the WordPress source files into the correct location, create necessary directory. That build script does almost nothing because WordPress has been installed, Since the second deployment. We have to know the environment variables, To customize these build script

The OpenShift Environment Variables

We will need to know some OpenShift information such as the document root path, and the application name, the data path, During our build and document process. This information is available in environmental variables for which a full list can be accesshere

Some variables that we'll use frequently is as follows:

  1. OPENSHIFT_HOMEDIRHome directory path. Linux you will have your user home directory in, On your local machine/home/usernameOr on Mac OS X it's/Users/usernameHome directory is the folder you will be in right there when you remote access via SSH. It's/var/lib/openshift/user_account_idFor example, my own is/var/lib/openshift/532bd7655004468bcf0000e1
  2. OPENSHIFT_REPO_DIR$OPENSHIFT_HOMEDIR/app-root/runtime/repo/Repository containing the currently deployed version (of the application). It's exactly the same with whatever you have inside your repository on your local machine with an exception thatPhpFolder now is symbolic link point to document root; therefore$OPENSHIFT_REPO_DIR/phpCan be used in build script to reference to document root
  3. OPENSHIFT_DATA_DIR.  $OPENSHIFT_HOMEDIR/app-root/data/
  4. OPENSHIFT_APP_UUID The unique id of your app on OpenShift. UUID can be very handy when you start to generate paths

We access these variable in our build script with$variable_name

The Custom Build and Deployment Process

Let's look back our current repository structure:

We'll start with aHello World When and how as we customize the build script, action so we have a sense of what will be output

Your Hello World Build 

Let append thisEcho "Hello world. This is invoked before building."Into your/openshift/pre_build

$ echo "echo \"Hello world. This is invoke before building.\"" >>. Openshift/action_hooks/pre_build
Push to the repository and you will see the output like this:
$ git add. Openshift/action_hooks/pre_build
$ git commit -m "Hello world for pre_build"
$ git push

Counting objects: 9, done. Delta compression using up to 4 threads. Done, Compressing objects: 100% (5/5). 411 bytes, Writing objects: 100% (5/5). 0 bytes/s, done. Reused 0 (delta 0)
remote: Stopping MySQL 5, Total 5 (delta 4). 5 cartridge
remote: Stopping PHP 5. Commit 1f4ef8a, 4 cartridge (Apache+mod_php)
remote: Waiting for stop to finish
remote: Stopping PHPMyAdmin cartridge
remote: Waiting for stop to finish
remote: Building git ref 'master'Remote: Hello world. This is invoke before buildingRemote: Checking. Openshift/pear. Txt for PEAR dependency. Remote: Do we need to run WordPress install
remote: Preparing build for deployment
remote: Deployment id is d06b1d4a
remote: Activating deployment
remote: Starting MySQL 5. 5 cartridge
remote: Starting PHPMyAdmin cartridge
remote: Hello world. This is invoke when deploying
remote: Copying WordPress plugins from. Openshift/plugins
remote: Copying WordPress themes from. Openshift/themes
remote: Database already configured. Remote: Starting PHP 5. 4 cartridge (Apache+mod_php)
remote: Application directory "php/" selected as DocumentRoot
remote: Hello world. This is invoke after deploying
remote: -------------------------
remote: Git Post-Receive Result: success
remote: Activation status: success
remote: Deployment completed with status: success
To ssh://532bd7655004468bcf0000e1@demo2-tutsplus. Rhcloud. Com/~/git/demo2. Git/
   1e7ad85. 1f4ef8a  master -> master
Our message appears during build process, As you see. Let's do a real thing to copy files during build process,   Now

Copying File to Document Root

If you recall previous step, the file inOpenshift/themesAndOpenshift/pluginsIs copied toWp-content/themesAndWp-content/pluginsLet's say we want to do the same for copying file to the document root.  

You want to create a sub folder callResumeAnd put a fileResume/my_resume. TxtAnd make it access viaDemo2-tutsplus. Rhc-cloud. Com/resume/my_resume. TxtLet's create a directory to hold the data that we will copy into document root. We create a folder callDocrootInsideOpenshiftAnd whatever inside it will be copied.  

Let's openOpenshift/action_hooks/deployNotice line 49 (I make it bold)

#. /bin/bash

# This deploy hook gets executed after dependencies are resolved and the
# build hook has been run but before the application has been started back
# up again. Etc, so it could be python, php, This script gets executed directly, # ruby. Echo "Hello world. This is invoke when deploying"
dest_dir=${OPENSHIFT_DATA_DIR}Current

current_version=$(cat ${OPENSHIFT_BUILD_DEPENDENCIES_DIR}Current_version)
install_dir=${OPENSHIFT_BUILD_DEPENDENCIES_DIR}${current_version}

if [ ! -d "${dest_dir}" ]; then
  mkdir -p $dest_dir
  cp -rf ${install_dir}/* ${dest_dir}/
fi

# Replace the php/ directory with provided clean Wordpress installation
#
if [ -d ${OPENSHIFT_REPO_DIR}Php ]; then
  rm -rf ${OPENSHIFT_REPO_DIR}Php
fi
ln -sf ${dest_dir} ${OPENSHIFT_REPO_DIR}Php

if [ ! -d ${OPENSHIFT_DATA_DIR}Uploads ]; then
    mkdir ${OPENSHIFT_DATA_DIR}Copy the default themes and plugins
# from official Wordpress installation
#, uploads
fi


# If this is initial installation
if [ ! -d ${OPENSHIFT_DATA_DIR}Plugins ]; then
  mv ${dest_dir}/wp-content/plugins ${OPENSHIFT_DATA_DIR}Plugins
fi

if [ ! -d ${OPENSHIFT_DATA_DIR}Themes ]; then
  mv ${dest_dir}/wp-content/themes ${OPENSHIFT_DATA_DIR}Themes
fi

# Users can place their own plugins and themes into. Openshift/ directory
# inside their GIT repository. #
echo "Copying WordPress plugins from .openshift/plugins"
cp -rf $OPENSHIFT_REPO_DIR/.openshift/plugins/* ${OPENSHIFT_DATA_DIR}Plugins/ 2>/dev/nullEcho "Copying WordPress themes from .openshift/themes"
cp -rf $OPENSHIFT_REPO_DIR/.openshift/themes/* ${OPENSHIFT_DATA_DIR}Themes/ 2>/dev/null
# We will copy file here
Echo "Copying custom file from .openshift/docroot" cp -rf $OPENSHIFT_REPO_DIR/.openshift/docroot/* ${OPENSHIFT_REPO_DIR}Php/
# Add multisite upload dir # if [. -d $OPENSHIFT_DATA_DIR/blogs. Dir ]; then mkdir $OPENSHIFT_DATA_DIR/blogs. Dir fi ln -sf ${OPENSHIFT_DATA_DIR}blogs.dir ${OPENSHIFT_REPO_DIR}Php/wp-content/ ln -sf ${OPENSHIFT_DATA_DIR}plugins ${OPENSHIFT_REPO_DIR}Php/wp-content/ ln -sf ${OPENSHIFT_DATA_DIR}themes ${OPENSHIFT_REPO_DIR}Php/wp-content/ ln -sf ${OPENSHIFT_DATA_DIR}uploads ${OPENSHIFT_REPO_DIR}If not create it if, php/wp-content/ set -e if [ -z "$OPENSHIFT_MYSQL_DB_HOST" ] then echo 1>&2 echo "Could not find mysql database. Please run:" 1>&2 echo "rhc cartridge add -a $OPENSHIFT_APP_NAME -c mysql-5.1" 1>&2 echo "then make a sample commit (add whitespace somewhere) and re-push" 1>&2 echo 1>&2 fi if [ -z "$OPENSHIFT_MYSQL_DB_HOST" ] then exit 5 fi # Confirm database exists. /usr/bin/mysql -u "$OPENSHIFT_MYSQL_DB_USERNAME" --password="$OPENSHIFT_MYSQL_DB_PASSWORD" -h "$OPENSHIFT_MYSQL_DB_HOST" -P "$OPENSHIFT_MYSQL_DB_PORT" -e "select * from wp_commentmeta;" "$OPENSHIFT_APP_NAME" > /dev/null 2>&1 then echo echo "Database schema not yet added, WordPress is ready for install by visiting the site." else echo "Database already configured." fi

We added this command after that

# We will copy file here
echo "Copying custom file from .openshift/docroot"
cp -rf $OPENSHIFT_REPO_DIR/.openshift/docroot/* ${OPENSHIFT_REPO_DIR}Php/

The line with # is comment, for our own reference. The echo line is just to show some output. The real command we used iscpTo recursively copy all files and folders

Now, let put something inOpenshift/docrootAnd deploy

☁  demo2 [master] ⚡ mkdir -p. Openshift/docroot/resume/
☁  demo2 [master] ⚡ echo "Hi there" >. Openshift/docroot/resume/my_resume. Txt
☁  demo2 [master] ⚡ git add. 7 insertions(+), openshift/
☁  demo2 [master] ⚡ git commit -m "Custom hook for file copy"
[master 46235a2] Custom hook for file copy
 3 files changed, 1 deletion(-)
 create mode 100644. Openshift/docroot/hi. Done, txt
☁  demo2 [master] git push
Counting objects: 13. Delta compression using up to 4 threads. Compressing objects: 100% (6/6), done. 723 bytes, Writing objects: 100% (8/8). 0 bytes/s, done. Reused 0 (delta 0)
remote: Stopping MySQL 5, Total 8 (delta 4). 5 cartridge
remote: Stopping PHP 5. 4 cartridge (Apache+mod_php)
remote: Waiting for stop to finish
remote: Stopping PHPMyAdmin cartridge
remote: Waiting for stop to finish
remote: Waiting for stop to finish
remote: Waiting for stop to finish
remote: Waiting for stop to finish
remote: Waiting for stop to finish
remote: Waiting for stop to finish
remote: Building git ref 'master', commit 46235a2
remote: Hello world. This is invoke before building
remote: Checking. Openshift/pear. Txt for PEAR dependency. Remote: Do we need to run WordPress install
remote: Preparing build for deployment
remote: Deployment id is fe0803a9
remote: Activating deployment
remote: Starting MySQL 5. 5 cartridge
remote: Starting PHPMyAdmin cartridge
remote: Hello world. This is invoke when deploying
remote: Copying WordPress plugins from. Openshift/plugins
remote: Copying WordPress themes from. Openshift/themes
remote: Copying custom file from. Openshift/docroot
remote: Database already configured. Remote: Starting PHP 5. 4 cartridge (Apache+mod_php)
remote: Application directory "php/" selected as DocumentRoot
remote: Hello world. This is invoke after deploying
remote: -------------------------
remote: Git Post-Receive Result: success
remote: Activation status: success
remote: Deployment completed with status: success
To ssh://532bd7655004468bcf0000e1@demo2-tutsplus. Rhcloud. Com/~/git/demo2. Git/
   1f4ef8a. 46235a2  master -> master
☁  demo2 [master] curl http://openshift. Axcoto. Com/resume/my_resume. Txt
Hi there
☁  demo2 [master]

Your Exercise

Use the PhpInstead of introducing, folder in  your repository as a way to store this contentOpenshift/docroot

Hint

App-deployments/current/repoHolds exactly same copy of your repository. Whatever you have on your local machine will be here

Troubleshoot and Maintain

You knew how to remote access to your application with SSH, In the previous part. You can use Linux command, Once you are in

$ ssh 532bd7655004468bcf0000e1@demo2-tutsplus. Rhcloud. Com

Rhc come with command ssh allow you to connect into an app, However. Quickly and easy to remember

$ rhc ssh demo2

From these, you can always useHelpTo show available commands

  1. Tail_all:Tail all of your log. MySQL error log, You can see real time logging with this command for all gears in system: such as access log of Apache
  2. MysqlDrops you into a MySQL shell. User string and password, Very handy comparing with typing a long mysql command with host name
  3. ExportShow all current environment variables. You can use this command to see the list of available environment variables, During working with build script
  4. GearRestart, to control your gear: start, stop. Stop mysql, Like you can restart apache
  5. SnapshotSnapshot take a full backup of your current WordPress with all file, and media data, database dump. A very good way to have a full back-up of your site
  6. QuotaShow your disk quota. You may run out of space, Useful when you cannot upload to WordPress anymore

Taking a Snapshot

Rhc snapshot-save demo2Compress the media file, and source code, putting together a tar file and download it for you, dump your database.  

$ rhc snapshot-save demo2
Pulling down a snapshot to demo2. Tar. Gz. Creating and sending tar. Gz
$ ls. Grep tar. Gz
demo2. Tar. Gz

By default, a tar file with same name as your app is created. Store it somewhere for your shake. Once you have the snapshot saved, you can restore as well

$ rhc snapshot-restore demo2
Restoring from snapshot demo2. Tar. Gz. Removing old git repo: ~/git/demo2. Git/
Removing old data dir: ~/app-root/data/*
Restoring ~/git/demo2. Git and ~/app-root/data

Checking Gear Status

TheGearCommand controls cartridge status, start/stop. Or a database error, 503 error, or some 404, Like you get a timeout error when visiting your domain. You have to SSH into your app and check gear status:

[demo2-tutsplus. Rhcloud. Com 532bd7655004468bcf0000e1]\> gear status
Cart to get the status for
1. Mysql-5. 5
2. Php-5. 4
3. Phpmyadmin-4
2
ATTR: status=ALREADY_STOPPED
ATTR: quota_blocks=1048576
ATTR: quota_files=80000
CLIENT_RESULT: Application is either stopped or inaccessible
[demo2-tutsplus. Rhcloud. Com 532bd7655004468bcf0000e1]\>

If it's saying CLIENT_RESULTApplication is either stopped or inaccessibleI have to start it with:

[demo2-tutsplus. Rhcloud. Com 532bd7655004468bcf0000e1]\> gear start --cart php-5. 4
Starting PHP 5. 4 cartridge (Apache+mod_php)
Application directory "php/" selected as DocumentRoot
[demo2-tutsplus. Rhcloud. Com 532bd7655004468bcf0000e1]\>


# Confirm it's working now
[demo2-tutsplus. Rhcloud. Com 532bd7655004468bcf0000e1]\> gear status Cart to get the status for. 1. Mysql-5. 5 2. Php-5. 4 3. Phpmyadmin-4. 2 ATTR: quota_blocks=1048576 ATTR: quota_files=80000 CLIENT_RESULT: Application is running CLIENT_RESULT: %
[demo2-tutsplus. Rhcloud. Com 532bd7655004468bcf0000e1]\>
The next step is to consult your log file

Logging

Tail_allShow you completed logs of all cartridge in real time. If you want to look at an individual log, However, here is the list:

  1. Php/logsAccess and error log of Apache and MySQL
  2. Mysql/logMySQL log

Port Forwarding

TheRhc port-forwardCommand can help you establish a local connection to your hosted service(Web server, Database server. ). OpenShift automatically checks available ports on your local system and forwards one to a remote port of running service. Via port forwarding, you can work on local machine but the connection is forward to remote machine.  

Let's try with MySQL

$ rhc port-forward demo2
Checking available ports. Done
Forwarding ports. Address already in use - bind(2) while forwarding port 8080. Trying local port 8081
Address already in use - bind(2) while forwarding port 3306. Trying local port 3307

To connect to a service running on OpenShift, use the Local address

Service Local               OpenShift
------- -------------- ---- ----------------
httpd   127. 1:8080  =>  127. 2. 126. 1:8080
httpd   127. 1:8081  =>  127. 2. 126. 3:8080
mysql   127. 1:3307  =>  127. 2. 126. 2:3306

Press CTRL-C to terminate port forwarding

I had port 8080 and 3306 for my different apps. Therefore, OpenShift picked up3307For MySQL. Now I can use the MySQL credential to connect to it with Sequel Pro. And issue:, you can get it again on OpenShift dashboard or SSH into server, If you forgot your password

[demo2-tutsplus. Rhcloud. Com 532bd7655004468bcf0000e1]\> export. Grep MYSQL
declare -x OPENSHIFT_MYSQL_DB_HOST="127.2.126.2"
declare -x OPENSHIFT_MYSQL_DB_LOG_DIR="/var/lib/openshift/532bd7655004468bcf0000e1/mysql//log/"
declare -x OPENSHIFT_MYSQL_DB_PASSWORD="DyANqgKAFbTT"
declare -x OPENSHIFT_MYSQL_DB_PORT="3306"
declare -x OPENSHIFT_MYSQL_DB_SOCKET="/var/lib/openshift/532bd7655004468bcf0000e1/mysql//socket/mysql.sock"
declare -x OPENSHIFT_MYSQL_DB_URL="mysql://adminL1GxwjI:
 This email address is being protected from spambots. You need JavaScript enabled to view it.
 .2:3306/"
declare -x OPENSHIFT_MYSQL_DB_USERNAME="adminL1GxwjI"
declare -x OPENSHIFT_MYSQL_DIR="/var/lib/openshift/532bd7655004468bcf0000e1/mysql/"
declare -x OPENSHIFT_MYSQL_IDENT="redhat:mysql:5.5:0.2.9"
declare -x OPENSHIFT_MYSQL_LD_LIBRARY_PATH_ELEMENT="/opt/rh/mysql55/root/usr/lib64"

UsingSequel ProTo connect to it

With port forwarding, you can connect to the app easily using GUI program

Synchronize Local and Live Environments

Everybody loves to develop on a local machine instead of uploading to server for testing or evaluate a feature. You may export data from live host and import on local machine for that purpose like inthis tutorial.That exporting process is annoying, However. Take longer and longer to export/import, The data can be big. Too, Fixing bug is harder. It can be hard to debug on local environment, without re-producing the exactly post/page content, If a bug only happens on a particular post/page on live site

Specially the pictures data, and you have your team created lots of test post on a staging/testing area (hosting on OpenShift), Or if you are a remote team, it will be good if you have a good way to just synchronize everything back

Therefore, I propose a method to achieve that with: port forwarding and host file editing. We can access MySQL easily from local machine, Using port forwarding as we saw above. We can point the domain, With the help of editing host fileopenshift.axcoto.comInstead of to OpenShift, to local machine. In our clone repository, we have an emptyPhpFolder. That folder is replaced by a symbolic link to document root of Apache, When deploying. Now, we will use that folder as document root on our local machine. On my computer, Recall the repository structure again

☁  demo2 [master] ⚡ pwd
/Users/kureikain/Sites/demo2
☁  demo2 [master] ⚡ ls -la
total 24
drwxr-xr-x  10 kureikain  staff   340 Mar 21 00:39. Drwxr-xr-x@ 57 kureikain  staff  1938 Mar 21 00:38. Drwxr-xr-x  16 kureikain  staff   544 Mar 28 01:20. Git
-rw-r--r--   1 kureikain  staff     9 Mar 21 00:39. Gitignore
drwxr-xr-x  11 kureikain  staff   374 Mar 25 23:42. Openshift
-rw-r--r--   1 kureikain  staff  2243 Mar 21 00:39 README
-rw-r--r--   1 kureikain  staff  2201 Mar 21 00:39 README. Md
drwxr-xr-x   3 kureikain  staff   102 Mar 21 00:39 libs
drwxr-xr-x   3 kureikain  staff   102 Mar 21 00:39 misc
drwxr-xr-x   7 kureikain  staff   238 Mar 28 01:05 php

You should add whatever you have in wp-content/plugins(default WordPress 3, to succeed at this method, Note that. 8. 1 plugins) and wp-content/themes(default WordPress 3. 8. 1 themes) on OpenShift into yourGit repository. You can download it via scp or SFTP as we discuss in the previous article, or you can simply download the default ones from WordPress.  

Just make sure your local repositoryOpenshift/themesAndOpenshift/pluginsHas same thing with theWp-content/themesAndWp-content/pluginsFolder of WordPress app on OpenShift

Step 1: Port Forwarding

Using commandRhc port-forward demo2WithDemo2Is my app. Change it to your app name

Step 2: Virtual Host

We will add one more entry for our domain point to folder hold our WordPress, Using Apache. Depending on your OS, you may have different location for Apache configuration. It can be/etc/apache2Or/etc/httpd 

It's, For example, on my Mac/etc/apache2I will add below code to my/etc/apache2/httpd. ConfOr/etc/apache2/extra/httpd-vhosts. ConfOr where you prefer, depend your OS

<VirtualHost *:80>
    ServerName openshift. Axcoto. Deny
      Allow from all
    </Directory>
</VirtualHost>, com
    DocumentRoot "/Users/kureikain/Sites/demo2/php"
    <Directory "/Users/kureikain/Sites/demo2/php">
      Order allow
Some article will helps:, If you are not familiar with Apache and Virtual host
  1. http://code.tutsplus.com/articles/apache-2-basic-configuration-on-unix-like-systems--net-26607
  2. http://code.tutsplus.com/articles/how-to-setup-a-wordpress-development-environment-for-windows--wp-2...
  3. http://code.tutsplus.com/tutorials/wordpress-development-and-deployment-with-mamp-git-and-dropbox--w...

Step 3: Environment Variable

You won't have the environment variable available, When running on your local machine. We have to define in somewhere. For a simple starting, I prefer to put these variable directly in Apache config file withSetEnvDirection.  

Our virtual host entry become:

<VirtualHost *:80>
SetEnv OPENSHIFT_SECRET_TOKEN "kCsELz-GJnho4I0M9XemTiZ47Jn4_QCSONFL5We8zw3Tgg572ivNEQQgZeIKQkTo2kE>
SetEnv OPENSHIFT_APP_UUID 532bd7655004468bcf0000e1
 
SetEnv OPENSHIFT_APP_NAME demo2 SetEnv OPENSHIFT_MYSQL_DB_USERNAME adminL1GxwjI SetEnv OPENSHIFT_MYSQL_DB_PASSWORD DyANqgKAFbTT SetEnv OPENSHIFT_MYSQL_DB_HOST "127.0.0.1" SetEnv OPENSHIFT_MYSQL_DB_PORT 3307 ServerName openshift. Axcoto. Com ServerAdmin webmaster@dummy-host2. Example. Deny Allow from all </Directory> ErrorLog "/var/log/apache2/openshift.axcoto.com-error_log" CustomLog "/var/log/apache2/openshift.axcoto.com-access_log" common </VirtualHost>, com DocumentRoot "/Users/kureikain/Sites/demo2/php" <Directory "/Users/kureikain/Sites/demo2/php"> Order allow
I grab the environment variable of OpenShift and just put it into our config file. But I change the MySQL host to 127. 1 and MySQL Port to 3307 because we do port forwarding before

Step 4: Changing the Host File

Openshift. Axcoto. ComIs configured to point to OpenShift. We can override it by directly editing, However/etc/hostsOpen that file with you favorite editor and append

127. 1    openshift. Axcoto. Com
Confirm that it's pointing to our local machine

☁  ~  ping openshift. Axcoto. Com
PING openshift. Axcoto. Com (127. 1): 56 data bytes
64 bytes from 127. 1: icmp_seq=0 ttl=64 time=0. 055 ms
64 bytes from 127. 1: icmp_seq=1 ttl=64 time=0. 165 ms
^C
--- openshift. Axcoto. 2 packets received, com ping statistics ---
2 packets transmitted. 0% packet loss
round-trip min/avg/max/stddev = 0. 055/0. 110/0. 165/0. 055 ms

If you want to visit the real app on OpenShift, comment out that line in /etc/hosts. Put a "#" to comment out:

# 127. 1     openshift. Axcoto. Com
Then openshift. Axcoto. Com will point to live site again

Step 5: Pull WordPress Core File and Sync Media From Live Site

We have to copy the media and WordPress file into our local environment, Finally. We also make the symbolic link to pointPhp/wp-content/themesToOpenshift/themeThe same concept applies for pluginPhp/wp-content/pluginsPoint toOpenshift/pluginsThis way we can working on. Openshift/themes andOpenshift/pluginsAnd can refresh browser to see our change

$ cd php
$ rm -rf. /*
# make snapshot to the specified file name
$ rhc snapshot save demo2 --filepath snap. Tar. Gz
# extract it
$ tar xzf snap. Tar. Gz
# we got it now
$ ls 532bd7655004468bcf0000e1 snap. Tar. Gz

# the 532bd7655004468bcf0000e1 is out extracted snapshot # it can be different on you app. $ cp -rf 532bd7655004468bcf0000e1/app-root/data/current/*
# link the themes folder $ ln -s `pwd`/. /. Openshift/themes wp-content/themes
#link the plugin folder
$ ln -s `pwd`/. /. Openshift/plugins wp-content/plugins # Link. Openshift folder itself because it has some special thing inside
$ ln -s `pwd`/. /. Openshift. Openshift

# copy upload files $ cp -rf 532bd7655004468bcf0000e1/app-root/data/uploads wp-content # clean up $ rm -rf 532bd7655004468bcf0000e1 snap. Tar. Gz

The first time you want to sync the data, you run these commands; however, we don't want to pull all of file because partial of files were there, since the second time. A better option is only sync uploads file. We can do that withscp:

$ cd ~/Site/demo2/php
$ scp -r 532bd7655004468bcf0000e1@demo2-tutsplus. Rhcloud. Com:app-root/data/uploads wp-content/

Or withrsyncRsync is much better because it only download the not exist file. Same file won't be downloaded again

$ cd ~/Site/demo2/php
$ rsync -av --partial --progress --rsh="ssh " 532bd7655004468bcf0000e1@demo2-tutsplus. Rhcloud. Com:app-root/data/uploads wp-content

Put these commands into a shell script and track it with your Git repository too, You may go further. I will leave that part for you :)

You may want to ignore file inside php because those file are just for our own testing/running on local computer. No need to track them

$ echo "php/*" >>. Gitignore

Step 6: Administration Over SSL (Optional)

TheWp-config. PhpThat OpenShift generated, defined to serveadministrator over SSL

<. Php
// wp-config. Php
//. /**
 * We prefer to be secure by default
 */
define('FORCE_SSL_ADMIN', true);
On your computer, you usually don't have SSL certificate. You may editWp-config. PhpAnd setFORCE_SSL_ADMINTo false. And want to give it a try to run a SSL connection then here is the instruction, If you want challenge

Generate SSL certificate

This is self-sign certificate meaning that browser won't trust it. It's fine because we are working on local anyway and you can just add an exception when the browser throw an untrusted warning

$ sudo mkidr -p /etc/apache2/ssl
# make sure you type correct domain
$ sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/apache2/ssl/apache. Key -out /etc/apache2/ssl/apache. Crt

We create the folder/etc/apache2/sslThen generate the cert and key into that folder. We will config apache point to these two files later

Define Virtual Host Entry

Open your/etc/apache/httpd. ConfYour Apache config file can be in different folder, such as, (again/etc/httpdFind the line , )Listen 80And appendListen 443

Listen 80
Listen 443

On top of your/etc/apache2/extra/httpd-vhost. ConfFind the lineNameVirtualHost *:80And append 

NameVirtualHost *:443

Next, we define one more entry in virtual host for port 443. We add some SSL definition, but this time, We just duplicate the previous definition with port 80

<VirtualHost *:443>

    SetEnv OPENSHIFT_SECRET_TOKEN  "kCsELz-GJnho4I0M9XemTiZ47Jn4_QCSONFL5We8zw3Tgg572ivNEQQgZeIKQkTo2kEwUHWjpsO7fhy1FeVqDlyRzSsKg6-fWNDWJaj9hO65nwVqE5auj35R2F9RNvEk"
    SetEnv OPENSHIFT_APP_UUID 532bd7655004468bcf0000e1
    SetEnv OPENSHIFT_APP_NAME demo2
    SetEnv OPENSHIFT_MYSQL_DB_USERNAME adminL1GxwjI
    SetEnv OPENSHIFT_MYSQL_DB_PASSWORD DyANqgKAFbTT
    SetEnv OPENSHIFT_MYSQL_DB_HOST "127.0.0.1"
    SetEnv OPENSHIFT_MYSQL_DB_PORT 3307

    ServerName openshift. Axcoto. Com
    ServerAdmin webmaster@dummy-host2. Example. Com
    DocumentRoot "/Users/kureikain/Sites/demo2/php"
    <Directory "/Users/kureikain/Sites/demo2/php">
      Order allow, deny
      Allow from all
    </Directory>
    
    ErrorLog "/var/log/apache2/openshift.axcoto.com-error_log"
    CustomLog "/var/log/apache2/openshift.axcoto.com-access_log" common

    SSLEngine On
    SSLCertificateFile /etc/apache2/ssl/apache. Crt
    SSLCertificateKeyFile /etc/apache2/ssl/apache. Key

    <Location />
        SSLRequireSSL On
        SSLVerifyClient optional
        SSLVerifyDepth 1
        SSLOptions +StdEnvVars +StrictRequire
    </Location>

</VirtualHost>

Note the SSL certificate file path. Restart Apache withSudo apachectl restartNow, you can access your dashboard over SSL. And media data is synced, Here is my WordPress installation, running totally local with same database from live site

Conclusion

Throughout this series, we gained a lot of knowledge about OpenShift. You should not be able to easily install and configure WordPress on OpenShift. With the handy ofRhc clientYou can quickly config or viewing app information from terminal.  

And a push to HipChat during deployment, We also covered build and deploy scripts so you can customize your application to your needs like showing a special banner. You can even sync data between your local computer and OpenShift. I hope you enjoy this tutorial and OpenShift.  

Leaving comment to let us know how you are doing. If you run into trouble, let me know in the comment, I would like to help

Continue reading
0
5400
Final product image
What You'll Be Creating

Nautical and elegant, Blue and white stripes are summery. Make this striped beauty using the basicshiboriTechnique of the accordion fold for a contemporary result. Follow ourshibori for beginners series and learn this ancient technique - which just happens to be the hottest look in fashion and homewares this season.  

Supplies

  • Latex gloves
  • Apron
  • Which you can find in this, 250g of reducing agent (thiox or hydrosulfate) and 250g of soda ash, 20g of pre-reduced indigoindigo dye kit.
  • 19L (5 gallon) plastic bucket with a lid
  • Stick for stirring (make sure it's long enough to reach the bottom of your bucket)
  • Small container for the foam/"flower"
  • Sheet of plastic to cover the floor
  • Shallow pan to hold your fabric
  • White silk crepe de chine fabric (or other natural cloth)
  • Scissors
  • Two wood blocks longer than the width of your scarf. Two old 50cm rulers (20in) would work
  • Rubbers bands
  • Sewing machine or needle and thread

1. Prepare the Indigo Vat

You need to set up the indigo vat, First. Just follow the step-by-step instructions we prepared for youhereIf you plan to use fabric dye and not natural indigo you can follow the instructions in the second part of this tutorial 

Which is the optimum length of time, cover the bucket with a lid and let the dye settle from 15 minutes to one hour, Once your indigo vat is set. During this time you can fold and bind your fabric


1. Fold the Scarf

Step 1


Cut your fabric in to a strip slightly bigger than the scarf you want to create. Keep the length of your fabric, which will be probably 1. 5m (59in), as the length of your scarf and cut a strip of about 35cm (13. 8in) as the width. The end result will be about 30cm (12 in) wide. Once you have the perfectly cut strip, lay it down on the floor

Step 2

Start folding the strip of fabric into the accordion fold. First lift the left edge and fold it to the right


Now is a good time to measure your pleats. Place your wood block- the old ruler in my case - on your fold. Check that the folded fabric is bigger than your wood block and there is excess fabric on both sides. Imagine that this section will be the colored area of your scarf

Start folding your scarf in an accordion way, Now that you are sure about the size of your pleats. Lift the end of your first fold together with the fabric underneath and fold them both towards the right

Place them gently down and ensure that your pleats are aligned and of the same size

Step 3

Moving towards the right, Keep on folding the fabric back and forth. Keep holding the fabric with both hands as silk pleats can be a bit tricky. Your fabric should look like a zig-zag, Keep in mind that from the side. Got it right

You should have a neat pile of accordion-folded fabric, When you've reached the end of your fabric

2. Bind the Scarf

Step 1

Place one wood block (ruler) on top of your accordion-folded silk strip. Then carefully lift the fabric and place the second ruler on the other edge, exactly underneath the top one

Step 2

Center the fabric with your wood blocks so that there is extra length in the wood blocks on both sides. Place one rubber band on each end to tightly clamp the fabric in between the two blocks

Your silk scarf should now be clamped within your wood blocks. This is how it should look from the side. You can make any necessary adjustments on the pleats at this point, If you wish

2. Rinse and Dye the Scarf

Step 1

Thoroughly soak the fabric in water and squeeze the excess water and air out of it. Now it's ready for the next step

Step 2

Set it aside and keep it for later, remove the lid and scoop the foam/"flower" from the top of the vat, Once the vat has settled

Your wood blocks and scarf are probably longer than your bucket. Submerge one end of your fabric bundle into the dye first, So. Move the block carefully inside the vat to make sure all the uncovered areas are equally colored

Then turn it around and insert the other side of the block into the dye. Make sure you leave both sides of your fabric block in the dye for the same amount of time, to ensure you get the same hue.  

Step 3

Gently remove your bound fabric from the dye bath. You will notice that it is green in the beginning and as it oxidizes it gradually gets blue. The other end of your wood block has probably turned blue already.  

Carefully place the lid on the bucket to close and let your fabric dry and oxidize for about 20 minutes. You can repeat the dyeing process to achieve a darker shade of blue, Once it is done

3. Unfold and Wash

Step 1

Rinse your fabric with warm water, If you are happy with the result

Step 2

Remove the rubber bands to unclamp the fabric, Now

And remove the wood blocks

Step 3

Now it's time to unfold your fabric to reveal your own unique pattern. This is always the most exciting part of the process

Unfold your fabric

And. Gorgeous indigo stripes

I love the fact that the pattern is geometric but it has natural organic variations at the same time.  

Step 4

Now, gently hand-wash your scarf with mild detergent in warm water

Step 5

Let it dry in the shade. The color will be lighter, Once dry

4. Stitch the Edges

Step 1

Can't wait to wear it. Just one more step to go. Hem the scarf and stitch along the long edges of the scarf. The short edges already done since you used the edge of the fabric.  

First fold the long edge of the fabric upwards, To create the hem

Fold once more inwards

Step 2

Pin along the fold

Step 3

Stitch both hems, Now. You can either do this by hand if your fabric is really delicate or by using a thin needle on your sewing machine

Step 4

Once sewn, iron the fabric carefully on the correct setting (silk).  

Step 5

Now your scarf is ready to wear and enjoy the summer breeze

Stripey Beauty!

In this tutorial you learned how to use the basic accordion fold to create a striped indigo scarf. The accordion fold it is one of the easiest shibori methods, which creates endless designs when clamped with wood blocks

Can't get enough of indigo. Follow myShibori for Beginners seriesOf tutorials and learn how to tie-dye with me
Continue reading
0
19302
Final product image
What You'll Be Creating

The original Godzilla movie is probably one of the most important and influential monster movies of all time. Which is currently being promoted with some astounding artwork, If you're a big fan of cinema, you have probably already heard about the new Hollywood remake. In this tutorial, astutely named Goodzilla in Adobe Photoshop, we will create a fan-made movie poster. Let's get started

Tutorial Assets

The following assets were used during the production of this tutorial. Make sure to download them before getting started. If they are not available, please find alternatives

1. Set Up the Base

Step 1

The best way to start this kind of montage is to get the basic images together. First, create a new document. And make it, Make sure to give your file a name (in my case, Goodzilla)27 x 40 inWhich is the most common movie poster size.  

If you want the best printing quality, the resolution should be300 pixels/inchThis will make your PSD file very heavy, However. Since the image will only be viewed on screen, which is, I chose the default resolution, In my case72 pixels/inch

Go to File > Place EmbeddedAnd placeCloudy Hong Kong 5 on your canvas. While placing the image, use the upper Options Bar flip it horizontally and scale it down to about60%Press enter to confirm

Make sure the layer was placed as aSmart ObjectWhich should shows with a small file icon over the layer thumbnail. Enter the file byDouble-clickingOn the Smart Object layer's thumbnail

Now, we'll mask out the sky. Go to, To do soSelect > Color RangeAnd click on the sky to sample its color. Then go toLayer > Layer Mask > Hide SelectionYou should get the following result

Well, that was easy, but the result is far from perfect. We'll need to refine the mask. Paint back in the buildings using white, To do so, click on the layer mask to select it, using your favourite tool, and, and hide the unwanted elements using black. In the following example, I've shown in red the part that was hidden along with the final result. Most of the work was done using thePolygonal Lasso Tool (L)And the Fill shortcut(Alt-Backspace)Make sure to take your time to do a good job. Only patience, There is no trick there

Save and close the smart object, Once you are done. Rename itCityAnd place it in aGroup(Control-G)And name itCityAs well. Create aNew Layer (Control-N)And fill the bottom part of the image with black. Name the layer Black

Step 2

We'll use a photo of a statue taken in Japan, To create our monster. PlaceGodzilla on your canvas. Since the image is small, you will need to scale it up by40%So it fits nicely. We'll cover it up later using color adjustments and haze, Don't worry if it doesn't looks realistic yet

We'll need to hide the sky as well as the buildings, In this case. The best way to do so is to create a path around the statue using thePen Tool (P)

Go to the, Once you are donePathsPannel, make sure yourWork PathAnd press the Load Path as selection icon, is selected

With the selection active, go to yourLayersPanel and press theAdd a mask button to hide the background. Name the layerGoodzilla

HideTheCityGroup. Create aNew layerAnd name itBodyPaint in black the bottom of the monster's body, On this layer. This doesn't have to be perfect since most of this area will be hidden with buildings and haze

Create aNew layerAnd name itTextureAnd clip it onBodyUsing theClone Stamp Tool (S)UseAltAnd paint back some texture on the body, to pick a textured area. Once again, this doesn't have to be perfect

UnhideTheCityGroup. You should get the following result

Step 3

PlaceSunset 1 andScaleIt so it fits the canvas. Name the layerSky1

We can make the sky more interesting by combining it with a more intense looking sky. To do soPlaceSky0263 andScaleIt so it fits the upper part of your canvas

Name the layerSky2Set it toSoft LightAndMaskOut the bottom with a soft blackBrushSo it blends nicely

2. Create the Mood

Step 1

Now that we've got everything set up, we can begin adjusting colors to set the overall mood of our final image and make sure that everything blends nicely together. We'll start with the sky. Inside theSkyCreate a, groupGradient MapAdjustment layer(Layer > New Adjustment Layers > Gradient Map)And input a gradient ranging from#160E0C to#A96733Make sure that the layer is set50% Opacity

We'll also lighten up the sky. Create a, To do soCurvesAdjustment layer and set it as shown in the example below. Also, make sure the layer has aMaskFillIt with black(Edit > Fill)And use a large white softBrushTo paint back in the adjustment around the center of the image

We can brighten up even more the center of the image by creating aColor BalanceAdjustment layer with the following values. Make sure the layer is set toScreenOnce again, andMaskIn only the center of the image

Next, create aNew LayerAnd fill it using a large softBrushAnd the following three colors

Name the layerGold and set it toMultiplyAnd75% Opacity

Repeat the process with the following three colors

And this time set it toSoft Light100%OpacityI named this layerSunset

Finally, brighten up the sky one last time with aCurvesAdjustment layer

Step 2

We can work on blending the monster within, Now that we've set up a nice atmosphere. Since the monster is placed against the main light source, it should be darker. Its hue will also need to fit with the sky. To do soGroupGoodzillaTextureAndBodyTogether, and clip aCurvesAnd aGradient MapAdjustment layer to the group. Give those layers the following settings. I named the groupPrecomp

If you look at the originalGoodzillaLayer, you will notice a blue highlight on the edges of the statue, which is mainly due to the strong backlight. We can specifically tweak that highlight by creating aHue/SaturationAdjustment layer targeting only theBlues

Once you are done, you should get the following result

Step 3

We will use three adjustment layers, For the city. Start by creating aHue/SaturationLayer targeting bothCyansAnd BluesBring the, For both of theseSaturationAndLightnessTo-100

Add a, ThenColor BalanceWith the following settings

Darken everything using a, And finallyCurvesTo give it the same backlit aspect, adjustment

Make sure to clip everything toCity

Now, the building still doesn't seems to fit the scene. That is because, the light source come from the opposite direction than in our scene, in the original photo. To correct thisDouble-clickOn the layer's thumbnail to enter theSmart Object.  Create aNew LayerSet it toMultiplyAnd paint on the following area with #2A4467

Once you are doneSaveAndCloseAnd you should get the following result, the Smart Object

Step 4

In this step, we'll give our image a better sense of depth by adding haze. You will notice that as things get further away from you, When outside, if you look at the horizon, they tend to take up the color of the atmosphere and lose details. And over great distance, it becomes visible, This is because the air is not perfectly transparent. In our case, dust from the destruction of the city, or, This effect is accentuated if there are particles in the air, such as humidity. This means that, they should lose detail and take up an orange hue, as things get further away from the viewpoint, in our image. Doing will help conveying the sheer magnitude of the monster as well as hiding the fact that the original photo is a statue

To do so, create a new layer, clip it toPrecompName itHazeAnd paint in with a large softBrushAnd the following values

In order to preserve some of the details, bring down theOpacityTo60%

Repeat the process for the city, this time using a more brownish color such as#3D3027

This time, bring theOpacityDown to25%

MaskOut some of the foreground elements, such as the trees

Step 5

In this step, we'll continue adjusting and bringing together our image. We'll start by creating a newGroupNamedFXContaining twoGradient MapGive the first one a gradient ranging from#231812And set it to, to white30% Opacity

Then, give the nextGradient MapThe following settings, and set it to25% Opacity

Give both layers the followingMask

Then, add the followingColor Balance

Step 6

In this step, we'll finalize our backlit setup by adding a bright sun in the sky. Which is the monster, This will also serve to create a strong focal point toward the most important part of our image. Start by creating a new layer and fill it with black. With a large and soft roundBrushPaint a big brown#2A170ADot in the center of the canvas. Paint a smaller and brighter, Then#DFB27FDot in the middle, an even smaller and brighter dot in the middle, and then#FDF2DC

Set the layer toLinear DodgeThen scale and place it according to the example below. TheLinear DodgeBlending mode will allow us to get a nice flare effect

Finally, we'll add a vignette effect in order to focus the viewer even more toward the center of the image. To do so, create aNew LayerFillIt with white and paint with a soft blackBrushAround the edges of the canvas. Name the layerVignetteAnd set it toMultiply

3. Add Details and Refinements

Step 1

In theoriginal posterWe get to see soldiers parachuting toward the monster. This is a nice touch since the smoke trails they leave behind create visual lines which helps focus the eye toward the center of the image. We'll use smoke trail from aerobatic airplanes, In order to achieve this effect. Let's begin by openingRed Arrows in Photoshop

HitControl-MTo bring up theCurvesAdjustment window, and crunch the darker pixels of the image. Hit, ThenControl-UTo bring up theHue/SaturationTarget the , windowBluesAnd bring theLightnessDown to-100

Then, use theBrush Tool (B)With a cloud-like brush and paint in black the clouds that are still visible

HitControl-IToInvertThe image's color. Hit, ThenControl-ATo create a fullSelectionOf the canvas and thenCopy (Control-C) andPaste it (Control-V)Back into your main scene. Set the layer toMultiply

In order to feel more realistic, the trails need to be darker and match the overall hue of the sky. Once again, we'll use theCurvesAndHue/SaturationAdjustment windows. Don't forget to check theColorizeOption

Using theLasso Tool (L)Create a selection around one of the smoke trailCutIt(Control-X)PasteIt(Control-V)And use theFree TransformMode(Control-T)To move and rotate it around

Give all the layers the name, Once you are done placing themTrailAnd set them toMultiply

You can use the technique shown in the previous step to create flares at the tip of the smoke trails. If you don't want them to be to bright, set them to70% OpacityAndGroupThem together

Add yet another four layers, and paint small soldiers at the tip of the smoke trail. Once you are doneGroupEverything together and name the groupParatroopers

Step 2

The actors' names, Since this is a movie poster, such as the movie title, the credits and the release date, we're going to add a number of textual informations. Let's start with the actor names. Make sure you have downloaded and installed the free fontBebas NeuePick theHorizontal Type Tool (T)And create sevenText LayerSet the color to, Fill those layers with the actors' names#A68B76The first name to42 ptAnd the family name to60 ptOnce you are done, place everything in aGroupNamedActors

As for the movie title, I used the fontCompacta ICG BoldWhich is the actual font of the movie poster. If you don't have it, the fontImpactAlthough it is slightly less aesthetic, can do just fine. Use the, Once againType Tool (T)Or in this case, and type in the movie titleGoodzillaSet the font size to250 ptAnd the color to#C72F13Place and scale, ThenConcreteBare0348Over your text layer, and clip it to itGroupBoth layer together and name the groupGoodzilla place both, And thenGoodzillaAndActorsTogether in a group namedText

SetConcreteBareToMultiply

It darkened our text a little bit too much, While the texture is nice. To correct this, make sure you have your texture layer selected, hitControl-MTo bring theCurvesAnd bring up the curve, adjustment window

As for the credits, we'll use a font namedSteelTongs which has been specifically designed for this use. You can use the lowercase to type the small titles, With the font installed, and the uppercase to type the actual letters. Set the font size to, Once you are done50 ptAnd the color to#69584B

You can use the font, As for the dateCompacta ICG Bold(orImpact). Set the size to100 ptAnd the color to white

Finally, we'll place in theGodzilla SignI suggest you open and copy it inside this program, If you have Illustrator, andPasteIt as aShape LayerTo get the best quality. Otherwise, you can just place it as a regular layer

Pick a dark red color, With the layer selected#6E0F00 and hitAlt-BackspaceToFillThe shape with the color. Then, set it to70% Opacity

We'll give our movie title layer a, To improve the contrast between the movie title and the background shapeDrop ShadowMake sure yourGoodzillaAnd click on the, layer is selectedAdd a Layer Style button. Input the following settings

Step 3

We can make our image more visually interesting by adding some smoke clouding the city. To do so, first start by openingEvening Sky 1 in Photoshop

HitControl-IToInvertThe color, and then use theCurvesAdjustment to brighten up the image

Use a large soft, NowBrushTo paint the lower area of the image in white

Rotate and place it according to the example below, bring the image into your main scene, Once you are done. Use theHue/SaturationAdjustment toColorizeThe cloud in red. Name the layerSmokeSet it toMultiplyAnd70% OpacityNotice that this layer is placed between theCityAnd theGoodzillaGroup

Duplicate (Control-J) The layer, enter theFree TransformMode(Control-T)FlipIt horizontally(Edit > Transform > Flip Horizontal)And place it to the right of your canvas. You can then duplicate the layer another time and place it so it fills even more the lower right area of the image. Once you are doneGroupEverything together in a group namedSmoke

Step 4

The poster is almost completed. For the final touches, we'll start by adding another vignette effect. Create aNew LayerAnd, using theGradient ToolCreate a gradient going from black to white, and back to black

Name the layerVignetteSet it toMultiplyAnd place it betweenTextAndParatroopers

Between the city and the black background, Notice how there is a division between at the bottom of the layer. We can fix this by creating yet anotherNew LayerAnd using a large softBrushPaint a straight line with#030101Place the layer aboveVignetteAnd name itBottom Fix

Make sure to, FinallySave a copy of your file, and then go toLayer > FlattenImage

Go toFilter > Noise > Add NoiseSet the amount to2%And make sure to checkMonochromatic

Finally, go toImage > Image SizeAnd resize the image to25%Make sure to checkResample ImageAnd selectBicubic SharperTo get the best result as you scale down the image

There you go, you've completed the tutorial. Have a look at the final result

Conclusion

We covered the process in making a Godzilla-inspired movie poster, In this tutorial. To convey a sense of scale and depth, Among others, we have seen various techniques allowing you to create a strong mood, and to keep the viewer focused at the center of our image. You can use these techniques to create your own fan made movie poster, Now

Continue reading
0
5845

And extensions within Inkscape, With so many different tools, methods, it can be pretty  overwhelming to cover everything. This A to Z of Inkscape will cover the best stuff this great vector program has to offer, Thankfully. Most of these have links that will bring you to a quick tip or tutorial relating to that tool. So sit back and enjoy this beautifully arranged glossary of Inkscape

A

Align and Distribute: A dialog box with plenty of actions to align both objects and paths

B

Box Tool: A unique tool that's used to create 3D boxes by adjusting all 3 dimensions of the box

Break ApartEven if they were combined, : A path option that can separate a collection of paths

Brush Strokes: Custom brush strokes can be easily achieved in Inkscape with the many options available

Bucket Tool: A tool that can fill the area of a shape or paths with a color

C

Calligraphy Tool: A drawing tool that has a ton of options to create advanced brush strokes

Clip: An object menu that uses a selected object/path to cut a second selected object/path positioned below it

Clone: A edit option that creates a dynamic copy of a selected object. If the original object is altered, so will the clone

Combine: A path operation that groups the selected paths to make one entire object that inherits a single fill and stroke

Create Tiled ClonesYet dynamic objects, : A very useful menu with tons of options for creating large amounts of identical

D

Distribute: A series of options in Align and Distribute that evenly space out multiple objects

Dropper Tool: A tool that can find the exact color of the selected pixel or pixel area

Duplicate: An edit option that makes an exact duplicate of your object in the same position as the original

E

Ellipse Tool: An essential tool that creates circles, and arcs, ellipses

Eraser Tool: A tool that can be used to delete or cut out nodes

Export Bitmap: A file option that allows you to export your artwork as a PNG bitmap file

Extensions: A large drop-down menu that adds convenient and interesting features to Inkscape

F

Fill and Stroke: An essential dialog box that contains styling options such as fill color and stroke style

Filters: A drop-down menu that has many different filters and effects to enhance your drawings

G

Gradient Tool: A tool that allows you to edit color and positioning of gradients

Group: An object option that conveniently groups a selection of objects or paths

H

Handles: A visual representation of paths and bezier curves that can be dragged to manipulate the nodes that make them up

Help: A drop-down menu that offers a good selection of help material for Inkscape

I

Isometric Projection: A unique art style that involves a 3D art style at a particular angle

J

Join Style: A fill and stroke option that adjusts the appearance of a joined node

K

Kerning: A text option that adjusts the letter spacing of each specific letter of a text object

L

Layer: A drop-down menu that contains everything you need to keep your layers organized

Letter Spacing: A text option that adjusts the spacing of every letter in a selected text object

M

MarkersMiddle, and end nodes, : A stroke style option that allows shapes to be assigned to start

Mask: An object operation that refers to the amount of color lightness in an object to set the clipped object's opacity

N

NodesBut Inkscape has a few unique tricks on these, : The basis of all vector design

O

Objects to Marker: An object option that allows you to use any custom object as a marker

P

Path Effect Editor: A powerful menu that has many advanced options for altering the appearance of paths, such as tapered lines and envelopes

Pen Tool: A vital tool that easily creates paths by clicking to create nodes. It's also used for creating bezier curves

Pencil Tool: A tool that's used mostly to create freehand paths and nodes

Polygon Tool:A tool that makes drawing polygons and stars a breeze

Q

Quadrilateral Distortion: A fancy term for the usefulExtensions > Modify Path > EnvelopeThat allows the distortion of all sides of a selected path

R

Rectangle Tool: An essential tool that creates rectangles along with the ability to have rounded corners

Repeating PatternsSuch as patterns, : It's very easy to create large amounts of identical, yet dynamic objects

S

Shadows and Glows: A filter menu options that contains a variety of shadows and glows for objects and paths

Spiral Tool: An interesting tool that draws spirals from a surprising amount of options

Spray Tool: A great tool that can create multiple copies or clones of an object by spraying an area

T

Tapered Lines: A style of line that tapers in one or both directions, and Inkscape has a few ways to do them

Text and Font Tool: A tool to add text or text boxes to your designs

Tweak ToolIncluding shrinking and expanding, : A fun tool that has different methods of sculpting objects and paths

U

Ungroup: An object option that simply ungroups your grouped objects

Upper Case Text: An extension that will convert all of the characters of a text object into their upper case form

V

Visualize Path: A unique extension that offers four different options to generate drawings based on the selected paths

W

Warping: One feature from using the Tweak tool that can warp a selected object or path just by dragging it

X

XML EditorEdit > XML Editor is a powerful way to manually edit your file, : Since Inkscape's SVG file is based off of XML. If you know what you're doing

Z

Zoom: A view menu that offers a few unique ways for zoom management

That's the Inkscape A to Z

Menus, and dialog boxes that make Inkscape the incredible free open-source vector software that it is, We went over a lot of tools. This should make a great guide to keep on hand to keep yourself in tune while working on your Inkscape documents. Thanks for reading







Continue reading
0

Testimonial

Thank you so much! We are very happy with our new website. It is easy to use and all of our customers tell us, they love it.

Contact Us

  • 13245 Atlantic Blvd. #4352
    Jacksonville, FL 32225
  • 904-240-5823