화요일, 6월 29, 2010

[Android] Android porting

Android porting

Source: http://www.aesop.or.kr/?document_srl=46778
---------------------------------------

계속 삼성 bsp에 의존하다가

몇년만에 직접 포팅하려고 하니.. 옛생각도 나면서..  (집나가면 개고생이라는... CF문구가 생각나네요)

주말에 Aesop에서 Android를 공개한다고 하니.. 기다려 보렵니다.

공개전에 한번 해보고 싶었는데...  인내하고 기다려야 겠네요.. ^^

작업했던 내용 간단하게 적어보겠습니다.

1. kernel
    kandroid의 소스를 다운 받았습니다.
   git clone git://www.kandroid.org/samsung-s3c6410-android.1.0

2. config 파일은  다음 파일을 기본으로 사용했습니다.
   samsung-s3c6410-android.1.0/arch/arm/configs/smdk6410mtd_android.081213
  
3. 컴파일러
   aesop에서 제공한 gcc 4.2.4 와
   삼성에서 제공한 gcc 4.2.2 아무거나 사용해도 됩니다.
   단 삼성에서 제공한 gcc 4.3.1은 link에서 error가 발생되더군요..

4. root file system
    이거 고생좀 했습니다. 어느 업체에서 얻어논 file system을 사용했다가...

    고현철님께서 알려주신곳에서 참고해서 만들었습니다.

    4.1 http://benno.id.au/blog/2007/11/14/android-busybox            android용으로 컴파일된 busybox를 다운 받습니다.
          (처음에.. pull 로 파일을 다 받아야 하나 생각했었는데... 역시 잔머리가..)

    4.2 emulator  (저는 Windows PC에서 작업했습니다.)
           working location  "android-sdk-windows-1.0_r2\tools"

          4.2.1 sd card image 생성  ( emulator의 파일 시스템이 read only가 많아서.. 이렇게 했습니다.)
                    #makesdcard 128M sd.img

          4.2.2 emulator 실행 (dos 창 1)
                   #emulator --sdcard sd.img
                   menu --> Dev Tools --> Media Scanner  ( sd.img 인식)

          4.2.3 adb  (dos 창 2)
                    #adb push busybox  /sdcard   (sd.img에 4.1에서 받은 busybox를 push)
                    #adb shell   <-- 접속)

                    #chmod  777 /sdcard/busybox
                    #/sdcard/busybox tar -cf /sdcard/system.tar /system               
                    #/sdcard/busybox tar -cf /sdcard/data.tar /data
                    #/sdcard/busybox tar -cf /sdcard/dev.tar /dev
                    # exit

     4.3 sd.img,  ramdisk.img  가져오기
              "android-sdk-windows-1.0_r2\tools\sd.img"
              "android-sdk-windows-1.0_r2\tools\lib\ramdisk.img"

      4.4 ramdisk 통합하기
             위에서 생성된 모든 파일을 linux PC에서 작업
             ramdisk.img ,  sd.img

            #mkdir sd
            #mount -o loop sd.img sd

            #mkdir root_disk
            # gzip -cd ramdisk.img > ramdisk
            # cp ramdisk root_disk
            # cpio -iv -F ramdksk
            # rm ramdisk
            # cd ..

            # tar xvf sd\system.tar -C root_disk
            # tar xvf sd\data.tar  -C root_disk
            # tar xvf sd\dev.tar  -C root_disk

      4.5 init.rc 수정 (최종환님께서 얄려주셨습니다.)
            loglevel 3  -->   loglevel 6

            아래사항 주석처리
            #mount yaffs2 mtd@system /system
            #mount yaffs2 mtd@system /system ro remount
            #mount yaffs2 mtd@userdata /data nosuid nodev

            #mount yaffs2 mtd@cache /cache nosuid nodev
          
여기 까지...
혹시 빠진 부분이 있으면 지적해 주세요.. 

---------------------------------------


-----
Cheers,
June

emacs org-mode

emacs org-mode

Source:

http://orgmode.org/
http://jmjeong.com/index.php?display=Emacs/OrgMode

-----
Cheers,
June

A Cross-Platform Memory Leak Detector

Source: http://wyw.dcweb.cn/leakage.htm



A Cross-Platform Memory Leak Detector

Memory leakage has been a permanent annoyance for C/C++ programmers. Under MSVC, one useful feature of MFC is report memory leaks at the exit of an application (to the debugger output window, which can be displayed by the integration environment or a debugger). Under GCC, current available tools like mpatrol are relatively difficult to use, or have a big impact on memory/performance. This article details the implementation of an easy-to-use, cross-platform C++ memory leak detector (which I call debug_new), and discusses the related technical issues.

Basic usage

Let’s look at the following simple program test.cpp:

int main()
{
    int* p1 = new int;
    char* p2 = new char[10];
    return 0;
}
Our basic objectives are, of course, report two memory leaks. It is very simple: just compile and link debug_new.cpp. For example:

cl -GX test.cpp debug_new.cpp(MSVC)
g++ test.cpp debug_new.cpp -o test(GCC)
The running output is like follows:

Leaked object at 00341008 (size 4, )
Leaked object at 00341CA0 (size 10, )
If we need clearer reports, it is also trivial: just put this at the front of test.cpp:

#include "debug_new.h"
The output after adding this line is:

Leaked object at 00340FB8 (size 10, test5.cpp:5)
Leaked object at 00340F80 (size 4, test5.cpp:4)
Very simple, isn’t it?

Background knowledge

In a new/delete operation, C++ compilers generates calls to operator new and operator delete (allocation and deallocation functions) for the user. The prototypes of operator new and operator delete are as follows:

void* operator new(size_t) throw(std::bad_alloc);
void* operator new[](size_t) throw(std::bad_alloc);
void operator delete(void*) throw();
void operator delete[](void*) throw();
For new int, the compiler will generate a call to “operator new(sizeof(int))”, and for new char[10], “operator new(sizeof(char) * 10)”. Similarly, for delete ptr and delete[] ptr, the compiler will generate calls to “operator delete(ptr)” and “operator delete[](ptr)”. When the user does not define these operators, the compiler will provide their definitions automatically; when the user do define them, they will override the ones the compiler provides. And we thus get the ability to trace and control dynamic memory allocation.
In the meanwhile, we can adjust the behaviour of new operators with new-placements, which are to supply additional arguments to the allocation functions. E.g., when we have a prototype

void* operator new(size_t size, const char* file, int line);
we may use new ("hello", 123) int to generate a call to “operator new(sizeof(int), "hello", 123)”. This can be very flexible. One placement allocation function that the C++ standard ([C++1998]) requires is

void* operator new(size_t size, const std::nothrow_t&) throw();
in which nothrow_t is usually an empty structure (defined as “struct nothrow_t {};”), whose sole purpose is to provide a type that the compiler can identify for overload resolution. Users can call it via new (std::nothrow) type (nothrow is a constant of type nothrow_t). The difference from the standard new is that when memory allocation fails, new will throw an exception, butnew(std::nothrow) will return a null pointer.
One thing to notice is that there is not a corresponding syntax like delete(std::nothrow) ptr. However, a related issue will be mentioned later in this article.
For more information about the above-mentioned C++ language features, please refer to [Stroustrup1997], esp. sections 6.2.6, 10.4.11, 15.6, 19.4.5, and B.3.4. These features are key to understanding the implementation described below.

Principle and basic implementation

Similar to some other memory leakage detectors, debug_new overrides operator new, and provides macros to do substitues in user’s programs. The relevant part in debug_new.h is as follows:

void* operator new(size_t size, const char* file, int line);
void* operator new[](size_t size, const char* file, int line);
#define DEBUG_NEW new(__FILE__, __LINE__)
#define new DEBUG_NEW
Let’s look at the test.cpp after including debug_new.hnew char[10] will become “new("test.cpp", 4) char[10]” after preprocessing, and the compiler will generate a call to “operator new[](sizeof(char) * 10, "test.cpp", 4)” accordingly. If I define “operator new(size_t, const char*, int)” and “operator delete(void*)” (as well as “operator new[]...” and “operator delete[]...”; for clarity, my discussions about operator new and operator delete also cover operator new[] and operator delete[] without mentioning specifically, unless noted otherwise) indebug_new.cpp, I can trace all dynamic memory allocation/deallocation calls, and check for unmatched news and deletes. The implementation may be as simple as using just a map: add a pointer to map in new, and delete the pointer and related information in delete; report wrong deleting if the pointer to delete does not exist in the map; report memory leaks if there are still pointers to delete in the map at program exit.
However, it will not work if debug_new.h is not included. And the case that some translation units include debug_new.h and some do not are unacceptable, for although two operator news are used — “operator new(size_t, const char*, int)” and “operator new(size_t)” — there is only one operator delete! The operator delete we define will consider it an invalid pointer, when given a pointer returned by “operator delete(void*)” (no information about it exists in the map). We are facing a dilemma: either to misreport in this case, or not to report when deleting a pointer twice: none is satisfactory behaviour.
So defining the global “operator new(size_t)” is inevitable. In debug_new.h, I have

void* operator new(size_t size)
{
    return operator new(size, "", 0);
}
Implement the memory leak detector as I have described, you will find it works under some environments (say, GCC 2.95.3 w/ SGI STL), but crashes under others (MSVC 6 is among them). The reason is not complicated: memory pools are used in SGI STL, and only large chunks of memory will be allocated by operator new; in STL implementations which do not utilize such mechanisms, adding data to map will cause a call to operator new, which will add data to map, and this dead loop will immediately cause a stack overflow that aborts the application. Therefore I have to stop using the convenient STL container and resort to my own data structure:

struct new_ptr_list_t
{
    new_ptr_list_t*     next;
    const char*         file;
    int                 line;
    size_t              size;
};
Every time one allocates memory via newsizeof(new_ptr_list_t) more bytes will be allocated when calling malloc. The memory blocks will be chained together as a linked list (via the nextfield), the file name, line number, and object size will be stored in the fileline, and size fields, and return (pointer-returned-by-malloc + sizeof(new_ptr_list_t)). When one deletes a pointer, it will be matched with those in the linked list. If it does match — pointer-to-delete == (char*)pointer-in-linked-list + sizeof(new_ptr_list_t) — the linked list will be adjusted and the memory deallocated. If no match is found, a message of deleting an invalid pointer will be printed and the application will be aborted.
In order to automatically report memory leaks at program exit, I construct a static object (C++ ensures that its constructor will be called at program initialization, and the destructor be called at program exit), whose destructor will call a function to check for memory leaks. Users are also allowed to call this function manually.
Thus is the basic implementation.

Improvements on usability

The above method worked quite well, until I began to create a large number of objects. Since each delete needed to search in the linked list, and the average number of searches was a half of the length of the linked list, the application soon crawled. The speed was too slow even for the purpose of debugging. So I made a modification: the head of the linked list is changed from a single pointer to an array of pointers, and which element a pointer belongs to depends on its hash value. — Users are allowed to change the definitions of _DEBUG_NEW_HASH and _DEBUG_NEW_HASHTABLESIZE (at compile-time) to adjust the behaviour of debug_new. Their current values are what I feel satisfactory after some tests.
I found in real use that under some special circumstances the pointers to file names can become invalid (check the comment in debug_new.cpp if you are interested). Therefore, currently the default behaviour of debug_new is copying the first 20 characters of the file name, instead of storing the pointer to the file name. Also notice that the length of the original new_ptr_list_t is 16 bytes, and the current length is 32 bytes: both can ensure correct memory alignments.
In order to ensure debug_new can work with new(std::nothrow), I overloaded “void* operator new(size_t size, const std::nothrow_t&) throw()” too; otherwise the pointer returned by anew(std::nothrow) will be considered an invalid pointer to delete. Since debug_new does not throw exceptions (the program will report an alert and abort when memory is insufficient), this overload just calls operator new(size_t). Very simple.
It has been mentioned previously that a C++ file should include debug_new.h to get an accurate memory leak report. I usually do this:

#ifdef _DEBUG
#include "debug_new.h"
#endif
The include position should be later than the system headers, but earlier than user’s own header files if possible. Typically debug_new.h will conflict with STL header files if included earlier. Under some circumstances one may not want debug_new to redefine new; it could be done by defining _DEBUG_NEW_REDEFINE_NEW to 0 before including debug_new.h. Then the user should also useDEBUG_NEW instead of new. Maybe one should write this in the source:

#ifdef _DEBUG
#define _DEBUG_NEW_REDEFINE_NEW 0
#include "debug_new.h"
#else
#define DEBUG_NEW new
#endif
and use DEBUG_NEW where memory tracing is needed (consider global substitution).
Users might choose to define _DEBUG_NEW_EMULATE_MALLOC, and debug_new.h will emulate malloc and free with debug_new and delete, causing malloc and free in a translation unit includingdebug_new.h to be traced. Three global variables are used to adjust the behaviour of debug_newnew_output_fp, default to stderr, is the stream pointer to output information about memory leaks (traditional C streams are preferred to C++ iostreams since the former is simpler, smaller, and has a longer and more predictable lifetime); new_verbose_flag, default to false, will cause everynew/delete to output trace messages when set to truenew_autocheck_flag, default to true (which will cause the program to call check_leaks automatically on exit), will make users have to callcheck_leaks manually when set to false.
One thing to notice is that it might be impossible to ensure that the destruction of static objects occur before the automatic check_leaks call, since the call itself is issued from the destructor of a static object in debug_new.cpp. I have used several techniques to better the case. For MSVC, it is quite straightforword: “#pragma init_seg(lib)” is used to adjust the order of object construction/destruction. For other compilers without such a compiler directive, I use a counter class as proposed by Bjarne ([Stroustrup1997], section 21.5.2) and can ensure check_leaks will be automatically called after the destruction of all objects defined in translation units that include debug_new.h. For static objects defined in C++ libraries instead of the user code, there is a last resort:new_verbose_flag will be set to true after the automatic check_leaks call, so that all later delete operations along with number of bytes still allocated will be printed. Even if there is a misreport on memory leakage, we can manually confirm that no memory leakage happens if the later deletes finally report that “0 bytes still allocated”.
Debug_new will report on deleteing an invalid pointer (or a pointer twice), as well as on mismatches of new/delete[] or new[]/delete. A diagnostic message will be printed and the program willabort.
Exception safety and thread safety are worth their separate sections. Please read on.

Exception in the constructor

Let’s look at the following simple program:

#include 
#include 

void* operator new(size_t size, int line)
{
    printf("Allocate %u bytes on line %d\n", size, line);
    return operator new(size);
}

class Obj {
public:
    Obj(int n);
private:
    int _n;
};

Obj::Obj(int n) : _n(n)
{
    if (n == 0) {
        throw std::runtime_error("0 not allowed");
    }
}

int main()
{
    try {
        Obj* p = new(__LINE__) Obj(0);
        delete p;
    } catch (const std::runtime_error& e) {
        printf("Exception: %s\n", e.what());
    }
}
Any problems seen? In fact, if we compile it with MSVC, the warning message already tells us what has happened:

test.cpp(27) : warning C4291: 'void *__cdecl operator new(unsigned int,int)' : no matching operator delete found; memory will not be freed if initialization throws an exception
Try compiling and linking debug_new.cpp also. The result is as follows:

Allocate 4 bytes on line 27
Exception: 0 not allowed
Leaked object at 00341008 (size 4, )
There is a memory leak!
Of course, this might not be a frequently encountered case. However, who can ensure that the constructors one uses never throw an exception? And the solution is not complicated; it just asks for a compiler that conforms well to the C++ standard and allows the definition of a placement deallocation function ([C++1998], section 5.3.4; drafts of the standard might be found on the Web, such as here). Of compilers I have tested, GCC (2.95.3 or higher) and MSVC (6.0 or higher) support this feature quite well, while Borland C++ Compiler 5.5.1 and Digital Mars C++ compiler (all versions up to 8.38) do not. In the example above, if the compiler supports, we should declare and implement an “operator delete(void*, int)” to recycle the memory allocated by new(__LINE__); if the compiler does not, macros need to be used to make the compiler ignore the relevant declarations and implementations. To make debug_new compile under such a non-conformant compiler, users need to define the macro HAS_PLACEMENT_DELETE (Update: The macro name is HAVE_PLACEMENT_DELETE from Nvwa version 0.8) to 0, and take care of the exception-in-constructor problem themselves. I wish you did not have to do this, since in that case your compiler is really out of date!

Thread safety

My original version of debug_new was not thread-safe. There were no synchronization primitives in the standard C++ language, and I was unwilling to rely on a bulky third-party library. At last I decided to write my own thread-transparency layer, and the current debug_new relies on it. This layer is thin and simple, and its interface is as follows:

class fast_mutex
{
public:
    void lock();
    void unlock();
};
It supports POSIX threads and Win32 threads currently, as well as a no-threads mode. Unlike Loki ([Alexandrescu2001]) and some other libraries, threading mode is not to be specified in the code, but detected from the environment. It will automatically switch on multi-threading when the -MT/-MD option of MSVC, the -mthreads option of MinGW GCC, or the -pthread option of GCC under POSIX environments, is used. One advantage of the current implementation is that the construction and destruction of a static object using a static fast_mutex not yet constructed or already destroyed are allowed to work (with lock/unlock operations ignored), and there are re-entry checks for lock/unlock operations when the preprocessing symbol _DEBUG is defined.
Directly calling lock/unlock is error-prone, and I generally use an RAII (resource acquisition iinitialization; [Stroustrup1997], section 14.4.1) helper class. The code is short and I list it here in full:

class fast_mutex_autolock
{
    fast_mutex& _M_mtx;
public:
    explicit fast_mutex_autolock(fast_mutex& __mtx) : _M_mtx(__mtx)
    {
        _M_mtx.lock();
    }
    ~fast_mutex_autolock()
    {
        _M_mtx.unlock();
    }
private:
    fast_mutex_autolock(const fast_mutex_autolock&);
    fast_mutex_autolock& operator=(const fast_mutex_autolock&);
};
I am quite satisfied with this implementation and its application in the current debug_new.

Special improvement with gcc/binutils

Using macros has intrinsic problems: it cannot work directly with placement new, for it is not possible to expand an expression like “new(special) MyObj” to record file/line information without prior knowledge of the “special” stuff. What is more, the definition of per-class operator new will not work since the preprocessed code will be like “void* operator new("some_file.cpp", 123)(size_t ...)” — the compiler will not love this.
The alternative is to store the instruction address of the caller of operator new, and look up for the source line if a leak is found. Obviously, there are two things to do:

  • Get the caller address of operator new;
  • Convert the caller address to a source position.
There is no portable way to achieve these, but the necessary support has already been there for ready use if the GNU toolchain is used. Let’s just look at some GNU documentation:

`__builtin_return_address (LEVEL)'
     This function returns the return address of the current function,
     or of one of its callers.  The LEVEL argument is number of frames
     to scan up the call stack.  A value of `0' yields the return
     address of the current function, a value of `1' yields the return
     address of the caller of the current function, and so forth.

     The LEVEL argument must be a constant integer.

     On some machines it may be impossible to determine the return
     address of any function other than the current one; in such cases,
     or when the top of the stack has been reached, this function will
     return `0'.
(gcc info page)
addr2line
*********

     addr2line [ -b BFDNAME | --target=BFDNAME ]
               [ -C | --demangle[=STYLE ]
               [ -e FILENAME | --exe=FILENAME ]
               [ -f | --functions ] [ -s | --basename ]
               [ -H | --help ] [ -V | --version ]
               [ addr addr ... ]

   `addr2line' translates program addresses into file names and line
numbers.  Given an address and an executable, it uses the debugging
information in the executable to figure out which file name and line
number are associated with a given address.

   The executable to use is specified with the `-e' option.  The
default is the file `a.out'.
(binutils info page)
So the implementation is quite straightforward and like this:

void* operator new(size_t size) throw(std::bad_alloc)
{
    return operator new(size, __builtin_return_address(0), 0);
}
When a leak is found, debug_new will try to convert the stored caller address to the source position by popening an addr2line process, and display it if something useful is returned (it should be the case if debugging symbols are present); otherwise the stored address is displayed. One thing to notice is that one must tell debug_new the path/name of the process to make addr2line work. I have outlined the ways in the doxygen documentation.
If you have your own routines to get and display the caller address, it is also easy to make debug_new work with it. You may check the source code for details. Look for _DEBUG_NEW_CALLER_ADDRESSand print_position_from_addr.

Important update in 2007

With an idea coming from Greg Herlihy’s post in comp.lang.c++.moderated, a better solution is implemented. Instead of defining new to “new(__FILE__, __LINE__)”, it is now defined to “__debug_new_recorder(__FILE__, __LINE__) ->* new”. The most significant result is that placement new can be used with debug_new now! Full support for new(std::nothrow) is provided, with its null-returning error semantics (by default). Other forms (like “new(buffer) Obj”) will probably result in a run-time warning, but not compile-time or run-time errors — in order to achieve that, magic number signatures are added to detect memory corruption in the free store. Memory corruption will be checked on freeing the pointers and checking the leaks, and a new functioncheck_mem_corruption is added for your on-demand use in debugging. You may also want to define _DEBUG_NEW_TAILCHECK to something like 4 for past-end memory corruption check, which is off by default to ensure performance is not affected.
The code was heavily refactored during the modifications. I was quite satisfied with the new code, and I released Nvwa 0.8 as a result.

Summary

So I have presented my small memory leakage detector. I’ll make a summary here, and you can also consult the online doxygen documentation for the respective descriptions of the functions, variables, and macros.
This implementation is relatively simple. It is lacking in features when compared with commercial applications, like Rational Purify, or even some open-source libraries. However, it is

  • Cross-platform and portable: Apart from the code handling threading (which is separated from the main code) and providing special GCC support (which is automatically on when GCC is detected), only standard language features are used. It should compile under modern C++ compilers. It is known to work with GCC (2.95.3 and later), MSVC 6/7.1, and Borland C++ Compiler 5.5.1.
  • Easy to use: Because “void* operator new(size_t)” is overloaded too, memory leaks could be detected without including my header file. — I myself use it this way habitually in nearly every C++ program. — Generally, I check for the leak position only after I see memory leaks reported by debug_new.
  • Flexible: Its behaviour can be tailored by macros at compile time.
  • Efficient: It has a very low overhead, and it can be used in debugging applications that require high performance.
  • Open-source: It is released in the zlib/libpng licence and you have the freedom to use, change, or redistribute as you like.
With the recent improvements, some of the old restrictions are gone. The macro new or DEBUG_NEW in debug_new.h can mostly work if the newed object has operator news as class member functions, or if new(std::nothrow) is used in the code, though the macro new must be turned off when defining any operator news. Even in the worst case, linking only debug_new.cpp should always work, as long as the allocation operation finally goes to the global operator new(size_t) or operator new(size_t, std::nothrow_t).
Source is available, for your programming pleasure, in the CVS (most up to date) or download of Stones of Nvwa.
May the Source be with you!

Bibliography

[Alexandrescu2001] Andrei Alexandrescu. Modern C++ Design: Generic Programming and Design Patterns Applied. Addison-Wesley, 2001.
[C++1998] ISO/IEC. Programming Languages — C++. Reference Number ISO/IEC 14882:1998(E), 1998.
[Stroustrup1997] Bjarne Stroustrup. The C++ Programming Language (Third Edition). Addison-Wesley, 1997.

HTML for code syntax highlighting is generated by Vim

-----
Cheers,
June

월요일, 6월 21, 2010

원더걸스 뮤직디렉터 이승환이 말하는 ‘작곡가의 길’

간만에 좋은 글 posting 한다. ^^

Source: http://culturenori.tistory.com/957

원더걸스 뮤직디렉터 이승환이 말하는 ‘작곡가의 길’

작곡가 이승환
아름다움을 표현하고 싶어서 발라드 곡을 쓰는 작곡가 이승환 씨는 그 마음을 담아 ‘바람의 화원’, ‘시월애’, ‘연애편지’ OST 등 친숙한 멜로디로 우리의 가슴 속에 다가왔다. 가수가 자신의 곡을 더 잘 불러주었을 때 감동을 받는다는 이승환 씨를 만나 작곡의 세계에 대해 들어봤다.


이  름 : 이승환 (現 상명대학교 음악대학원 컴퓨터음악과 초빙교수)
학  력 1998년 l 서울대학교 음악대학 작곡과 학사(B.M.)/2003년 l New York University 작곡과 영화음악전공(Film Scoring) 석사(M.A.)
수상경력 2004년 l New York Lincoln Center Film Music Festival - Finalist /1993년 l ‘유재하 가요제’ 은상 수상
작품 및 연주활동 경력 (본인 앨범출반 리스트) 2002년2월 l 그룹 ‘스토리’ 앨범 2집 (대영 AV), 솔로 독립, 프로듀서 겸 가수로 활동/1999년3월 l 그룹 ‘스토리’ 앨범 1집 (대영 AV) : 앨범 프로듀서 겸 가수로 활동

대학 시절 가수 박정현의 앨범 수록곡 ‘이제 넌’을 계기로 김형중, 성시경, 이소라, 차태현 등 유명 가수들의 곡을 쓰고, 박진영, 원더걸스 해외 공연 Music Director로 활동영역을 넓혀 가고 있는 작곡가 이승환 씨는, 다양한 작곡 활동뿐만 아니라 현재 대중 음악을 가르치는 교수로도 활동하고 있다. “클래식이 혼자만의 만족감을 얻을 수 있다면, 대중 음악은 소통하는 데 목적을 두고 있어요. 대중과 소통하는 음악을 하고 싶었습니다.”

작 곡가 이승환은 원더걸스 해외 공연 Music Director로 활동하고 있다.

음악적 소양 바탕, Composer의 길로
클래식 피아노를 6살 때부터 시작했다는 그는 음악가 집안이라는 백그라운드도 없고 작곡가가 되고자 꿈꿔온 것도 아니었다. 부모님에 따르면 어린 시절 다른 것에 비해 피아노 치는 것만큼은 싫다는 말을 하지 않았던 것이 계기가 되어, 대회 출전과 입상경력을 통해 음악적 소양을 쌓아왔다.
“어린 시절부터 그렇게 피아노 연주를 계속해왔었는데 중학교 1학년 때 갑자기 그만하고 싶어졌어요. 사춘기의 고민과 더불어 공부도 제대로 할 시간이 없었는데, 피아노를 치려면 손을 보호해야 하기 때문에 좋아하던 농구 등의 거친 운동도 포기해야만 했죠. 그 때는 피아노 연주가 저의 인생에서 그렇게 대단하지는 않다는 생각이 들었어요. 게다가 매일 5~6시간씩 연습을 해야 대학 진학을 바라볼 수 있었는데 학업과 병행하기에는 무리라는 판단이 섰죠.”



그 동안 대학 교수님께 개인 레슨을 받는 등 투자한 비용과 시간 등이 많아 더 고민이 됐지만 길이 아니다 싶어서 그만 두고 공부에만 몰두했다. 그렇게 고등학교에 진학하고 1학년 말 즈음, 문과, 이과를 선택하는 갈림길에 섰을 때 문득 ‘음악을 다시 해보고 싶다’라는 생각이 들기 시작했다.
“음악을 안 한다고 선언했었는데 다시 시작한다고 말을 번복하는 것이 힘들었지만, 결심을 부모님께 털어놓고 양해를 다시 구했죠. 다시 음악학도의 길로 접어들기로 했지만, 피아노를 다시 시작하기에는 3년 간의 공백 때문에 늦은 감이 있었어요. 고민 끝에 음악적인 여러 가지 기본이 양분 될 수 있는 작곡을 선택하기로 했어요. 특히 작곡과는 피아노를 기본으로 해야 하는데 공백 기간은 있었지만, 기본은 다져져 있어 해볼만하지 않을까라는 생각으로 작곡과에 지망하게 되었습니다.”

클래식 작곡가에서 대중적인 인기를 얻기까지
서울대학교 작곡과에 재학하면서 2학년 때까지 클래식 작곡을 전공하던 때, 이승환 씨는 계속 해오던 고전적인 것 말고 다른 부분에 대해서도 고민을 하고 시작했다. “클래식은 다른 사람들과 호흡을 같이 맞출 수 있는 것이 별로 없었죠. 그래서 대중과 호흡할 수있는 것이 어떤 것이 있을까 생각을 많이 했어요. 평소 대중 음악, 영화, 뮤지컬 등에 관심을 두고 있어서 대중 음악 분야는 어떨까 생각을 많이 하고 있었던 터였죠.”
그는 당시 인기 있던 싱어송라이터 유재하에 대해 관심을 두고 있었는데 대학교 1학년 때 대학로를 지나가다 유재하 가요제가 진행 중인 것을 보고 관객으로 참석하게 되었다. 그 때 같은 학과 유희열 선배가 연주하는 것을 보게 되면서, 나도 한 번 나가보고 싶다는 생각으로 준비를 시작 해 1년 후 나가게 되었고 ‘우산과 아이(1993)’로 수상의 영광을 안았다. 몇 달 후 군입대를 하면서 ‘제대 후 하고 싶었던 대중 음악을 꼭 해봐야겠다’는 결심을 하게 되었다고 한다.



“제가 음악을 배울 때는 환경이 지금과는 많이 달랐어요. 요즘처럼 대중 음악을 공부할 수 있는 여건이 안 돼 제대 후 좋아하는 음악을 많이 들으며 혼자 공부하는 시간을 1년 정도 가졌죠. 독학을 하다 알게 된 선배가 녹음실 엔지니어로 일하고 있었는데, 선배를 만나러 갔던 날 우연히 가수 김현철 씨와 만나게 되면서 인연을 맺어 함께 작업하게 되었어요. 그 인연이 오버그라운드(Over ground)로 발을 들여놓게 된 계기가 된 셈이었죠.” 그 후 지금처럼 컴퓨터나 인터넷이 발달하지 않았던 시절이라 그는 자신을 알리기 위해 1996년부터 1997년까지 1년 동안 작업을 해 데모 테이프를 만들어 기획사에 돌리기 시작했다. 그러던 중 한 레코드 사에서 데모 테이프의 노래 중 하나가 좋아 앨범에 쓰고 싶다고 전화가 왔고, 그 곡이 바로 박정현 1집 ‘PIECE’에 수록된 ‘요즘 넌…’이라는 곡이다. 이 곡이 그가 대중 음악을 작곡하면서 처음으로 대중에게 선보인 계기가 되었다. 타이틀 곡은 아니었지만 처음으로 정식 음반에 자신의 곡이 실린다는 것, 이승환 씨는 그것에 감동했다.

“박정현 씨 앨범 작업 이후 다른 가수들의 작업 제의가 많이 들어와 인연을 맺고 작업하는 데 큰 바탕을 마련할 수 있었습니다.”

작곡이란 감성을 교감하는 작업
작곡가가 작사까지 할 수 있는 경우를 제외하고는 보통 노래가 나오기까지 곡을 먼저 쓰고 작사를 하게 된다. 좋은 시의 경우 나중에 곡으로 붙이는 경우는 있으나 이는 극히 드문 경우라고 한다.
클래식 음악을 했던 것 때문에 대중 음악 작곡가로 들어서면서도, 록 보다는 클래식에 바탕을 둔 감성적인 발라드를 지향하게 됐고, 이는 자신이 추구하는 아름다움, 감성적인 부분을 더 돋보이게 할 수 있었다.
“어떤 아티스트들도 마찬가지겠지만 감성들이 중간에 바뀌지는 않아요. 대개 자신이 추구하고자 하는 방향과 맞는 부류와 어울리게 되죠. 그리고 악기를 많이 다룰 줄 아는 것이 작곡가에게는 유리하지만, 건반 오케스트라고 비유되는 피아노를 연주할 줄 아는 것은 화성, 멜로디, 반주 모든 것을 통합하는 기초가 되는 것입니다.”



무에서 유를 창조하는 직업
아무리 좋아서 하는 일이라도 누구에게나 힘든 부분은 있다. 특히, 국내뿐 아니라 세계적으로도 새로운 곡이 매일 쏟아져 나오는 가운데 매번 새로운 곡을 쓴다는 것이 쉽지만은 않은 작업이다.
“대중 음악을 작곡한다는 것은 저를 위해 곡을 쓰는 것이 아닙니다. 가수를 위해서, 다른 악기연주를 위해 곡을 쓰는 것이죠. 작곡을 할 때마다 가수에 어울리는 곡을 만들어야 하는데 이 때 작곡가와 가수의 교감이 절대적으로 필요합니다. 그리고 교감을 나누려면 음악적인 것 외에 감성적인 부분에서도 공통점을 찾아야 하는데 이런 부분이 가장 힘들지요.”
창작의 어려움 속에 요즘 사회적 이슈인 표절과 저작권 등의 문제는 어떻게 해결하는지 물어봤다.
“표절의 경우 의도적으로 한 것과 곡을 썼는데 어떤 부분이 비슷하게 느껴지는 것은 분명히 차이가 나죠. 음악을 하는 사람이라면 음을 들었을 때 표절인지 아닌지 정도는 판단할 수 있어요. 개인적으로 작곡을 끝내고 무엇인가 의심이 들면 다른 작곡가들의 곡과 비교해보곤 합니다. 저작권의 경우 저작권 협회에 등록을 해서 관리하는데, 가끔 가수 이승환 씨와 이름이 같아 곡이 엇갈려서 등록된 적이 있어요. 곡을 쓰겠다고 전화 왔을 때 우연히 알게 되는데 그 때마다 수정하고 있는데 크게 문제가 된 적은 없어요(웃음).”
이승환 씨는 우리 나라는 대중음악 역사가 오래되지 않아서 각 장르별로 마니아 층의 폭이 협소하고 스타일이 빨리 변하는 편이어서, ‘빨리빨리 유행에 맞춰 음악을 쓴다’는 느낌이 힘들게 느껴지기도 한다고 덧붙였다.


요즘 학생들은 좋은 여건 속에서 열정도 많아
대중 음악 작곡가 외에 영화 OST 작곡, 가수 음반 디렉터 등 다양한 영역에서 활동하고 있는 그는, 현재 상명대학교 초빙 교수로 작곡을 가르치고 있다.

“과거에는 하드웨어 장비를 많이 필요로 해서 작곡을 시작하려면 초기 자본이 많이 필요했지만, 요즘에는 소프트웨어가 잘 나와서 4분의 1정도로 가격이 낮아졌어요. 또, 정규앨범이 아니더라도 대중과 접촉할 수 있는 블로그, 홈페이지 등을 통해 대중과 호흡하는 시간이 단축되고 자신의 실력을 공개해 대중과 공감할 수 있는 기회가 더 많아졌죠. 과거보다 시간과 비용이 많이 절감돼 대중에게 접근이 쉬워진 거죠.”
그렇지만 좋아진 여건만큼 안 좋은 점도 있다. 온라인 마켓의 대두로 CD앨범은 거의 구입하지 않고 온라인의 3분의 1정도로 축소된 점, 특히 음반을 발매하기 위해 숨어있는 조역자들, 음반 프로듀서, 음향 기술자 등이 거의 드러나지 않게 된 것이다. 또, 한 번의 클릭으로 쉽게 곡을 선정할 수 있다는 것 때문에 약 4분 정도 재생되는 곡을 만들기 위해 얼마나 많은 수고가 들어가는지에 대해 알아주는 사람이 없다는 것도 현존하는 음반 시장의 문제라고 꼬집었다.

handwriting
handwriting by Flowery *L*u*z*a* 저작자 표시

“작곡은 공부해서 잘 할 수 있는 분야가 아닙니다. 감각을 필요로 하는데, 음악을 사랑하고 음악을 들었을 때 자신도 공감하고 감동을 받아야 하죠. 작곡가가 되고 싶다면 되도록 많은 음악을 접하는 것이 좋습니다. 특히, 편협하게 음악을 골라 듣지 말고 폭 넓게 접해 보고 자신이 좋아하는 것을 골라간다면 작곡가로 성장할 수 있는 밑바탕을 마련할 수 있을 것입니다.”
소망도 하나씩 이룰 때마다 단계적으로 높아진다고 이야기하는 이승환 씨는 무명 시절 가졌던 ‘내 곡이 히트하면 얼마나 좋을까’, ‘내 이름이 실린 앨범을 낸다면 얼마나 좋을까’ 등의 소망을 늘 품고 살았다. 그리고 현재 가졌던 소망을 다 이루었다. “그런 생각들이 나를 움직이는 원동력이 되었어요. 이 모든 것을 이룬 후에도 여전히 소망들이 있고, 대중음악뿐 아니라 뮤지컬, 영화 등을 통해 제가 할 수 있는 역량을 키워보고 싶어요.”
이승환 씨는 피아노는 5시간 연습하면 그 만큼 노력의 수확을 거둘 수 있지만 작곡은 5시간 동안 고민한다고 해서 좋은 곡이 나올 수는 없다는 것이 작곡이라고 말한다. “어떤 때는 1시간 쓴 곡이 좋은 곡으로 나올 수도 있는 것이죠. 그렇지만 1시간의 좋은 곡을 쓰기 위해서는 정서적으로 준비가 되어 있어야 해요. 작곡가가 되고 싶다면 공연, 연극, 뮤지컬, 영화, 여행 등 문화 생활을 많이 경험하면서 내면적인 정서를 기르는 것이 중요합니다.”

틈만 나면 여행을 다니고 영화, 전시 등도 꼭 찾아본다는 이승환 씨처럼 무엇보다도 감성을 더 풍부하게 만들고 대중과 공감하는 노력을 한다면, 작곡가의 길은 그리 멀지 않아 보인다. 앞으로 대중 음악 작곡가뿐만이 아닌 다른 영역에서도 그의 감성적인 활동을 기대해 본다.


글. 최선희 기자 archy77@naver.com
사진. 박준기 기자 marcbymarc@naver.com
출처 : 한국콘텐츠진흥원

-----
Cheers,
June