1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - SND - demos - seq
3   File:     channel.c
4 
5   Copyright 2007-2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2008-09-18#$
14   $Rev: 8573 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 #ifdef SDK_TWL
18 #include <twl.h>
19 #else
20 #include <nitro.h>
21 #endif
22 
23 #include "smfdefine.h"
24 #include "channel.h"
25 #include "piano.g5.pcm16.h"
26 
27 #define MAX_CHANNEL 16
28 #define MAX_MIDI_CHANNEL 16
29 
30 /* Hard channel structure in DS */
31 typedef struct ChannelEx
32 {
33     u8      playing;                   /* Playback flag */
34     u8      midi_ch;                   /* MIDI channel for sound being played back */
35     u8      key;                       /* Playback key */
36     u8      velocity;                  /* Playback velocity */
37 }
38 ChannelEx;
39 
40 /* MIDI channel structure */
41 typedef struct MidiChannel
42 {
43     u8      pan;                       /* Pan */
44     u8      volume;                    /* Volume */
45     u8      expression;                /* Expression */
46 }
47 MidiChannel;
48 
49 ChannelEx channel[MAX_CHANNEL];
50 MidiChannel midi_ch[MAX_MIDI_CHANNEL];
51 
52 static void NoteOn(const u8 *midi_data);
53 static void NoteOff(const u8 *midi_data);
54 
55 
56 /*---------------------------------------------------------------------------*
57   Name:         ChInit
58 
59   Description:  Channel initialization.
60 
61   Arguments:    None.
62 
63   Returns:      None.
64  *---------------------------------------------------------------------------*/
ChInit(void)65 void ChInit(void)
66 {
67     int     ch_num;
68 
69     /* Locks all channels */
70     SND_LockChannel(0xffff, 0);
71 
72     for (ch_num = 0; ch_num < MAX_CHANNEL; ch_num++)
73     {
74         channel[ch_num].playing = FALSE;
75     }
76 
77     for (ch_num = 0; ch_num < MAX_MIDI_CHANNEL; ch_num++)
78     {
79         midi_ch[ch_num].volume = 127;
80         midi_ch[ch_num].pan = 64;
81         midi_ch[ch_num].expression = 127;
82     }
83 }
84 
85 /*---------------------------------------------------------------------------*
86   Name:         ChSetEvent
87 
88   Description:  Interprets and runs a MIDI event.
89 
90   Arguments:    midi_data: MIDI event data line
91 
92   Returns:      None.
93  *---------------------------------------------------------------------------*/
ChSetEvent(const u8 * midi_data)94 void ChSetEvent(const u8 *midi_data)
95 {
96     switch (midi_data[0] & 0xf0)
97     {
98     case MIDI_NOTEOFF:                /* Note off */
99         NoteOff(midi_data);
100         break;
101     case MIDI_NOTEON:                 /* Note-on. */
102         NoteOn(midi_data);
103         break;
104     case MIDI_CONTROLCHANGE:          /* Control change */
105         switch (midi_data[1])
106         {
107         case MIDI_CC_VOLUME:          /* Channel volume */
108             midi_ch[midi_data[0] & 0x0f].volume = midi_data[2];
109             break;
110         case MIDI_CC_PAN:             /* Channel pan */
111             midi_ch[midi_data[0] & 0x0f].pan = midi_data[2];
112             break;
113         case MIDI_CC_EXPRESSION:      /* Expression */
114             midi_ch[midi_data[0] & 0x0f].expression = midi_data[2];
115             break;
116         default:
117             break;
118         }
119     default:
120         /* Other messages are not supported */
121         break;
122     }
123 }
124 
125 /*---------------------------------------------------------------------------*
126   Name:         ChAllNoteOff
127 
128   Description:  Stops all channels
129 
130   Arguments:    None.
131 
132   Returns:      None.
133  *---------------------------------------------------------------------------*/
ChAllNoteOff(void)134 void ChAllNoteOff(void)
135 {
136     int     ch_num;
137 
138     SND_StopTimer(0xffff, 0, 0, 0);
139 
140     for (ch_num = 0; ch_num < MAX_CHANNEL; ch_num++)
141     {
142         channel[ch_num].playing = FALSE;
143     }
144 }
145 
146 /*---------------------------------------------------------------------------*
147   Name:         NoteOn
148 
149   Description:  Executes the note-on operation based on the MIDI event.
150 
151   Arguments:    midi_data: MIDI event data line
152 
153   Returns:      None.
154  *---------------------------------------------------------------------------*/
NoteOn(const u8 * midi_data)155 static void NoteOn(const u8 *midi_data)
156 {
157     int     ch_num;
158     ChannelEx *ch;
159     s16     db;
160     u16     vol;
161 
162     for (ch_num = 0; ch_num < MAX_CHANNEL; ch_num++)
163     {
164         if (!channel[ch_num].playing)
165             break;
166     }
167     if (ch_num == MAX_CHANNEL)
168         return;                        /* An open channel was not found */
169 
170     ch = &channel[ch_num];
171     ch->midi_ch = (u8)(midi_data[0] & 0x0f);
172     ch->key = midi_data[1];
173     ch->velocity = midi_data[2];
174 
175     /* Calculate volume */
176     db = SND_CalcDecibel(ch->velocity);
177     db += SND_CalcDecibel(midi_ch[ch->midi_ch].volume);
178     db += SND_CalcDecibel(midi_ch[ch->midi_ch].expression);
179     vol = SND_CalcChannelVolume(db);
180 
181     /* Play sound */
182     SND_SetupChannelPcm(ch_num,
183                         PIANO_G5_PCM16_FORMAT,
184                         piano_g5_pcm16,
185                         PIANO_G5_PCM16_LOOPFLAG ? SND_CHANNEL_LOOP_REPEAT : SND_CHANNEL_LOOP_1SHOT,
186                         PIANO_G5_PCM16_LOOPSTART,
187                         PIANO_G5_PCM16_LOOPLEN,
188                         vol & 0xff,
189                         (SNDChannelDataShift)(vol >> 8),
190                         SND_CalcTimer(PIANO_G5_PCM16_TIMER, (int)(ch->key - 67) * 64),
191                         midi_ch[ch->midi_ch].pan);
192     SND_StartTimer(1U << ch_num, 0, 0, 0);
193 
194     ch->playing = TRUE;
195 }
196 
197 /*---------------------------------------------------------------------------*
198   Name:         NoteOff
199 
200   Description:  Executes the note-off operation based on the MIDI event.
201 
202   Arguments:    midi_data: MIDI event data line
203 
204   Returns:      None.
205  *---------------------------------------------------------------------------*/
NoteOff(const u8 * midi_data)206 static void NoteOff(const u8 *midi_data)
207 {
208     int     ch_num;
209     ChannelEx *ch;
210 
211     for (ch_num = 0; ch_num < MAX_CHANNEL; ch_num++)
212     {
213         ch = &channel[ch_num];
214 
215         if (!ch->playing)
216             continue;
217         if ((ch->midi_ch == (u8)(midi_data[0] & 0x0f)) && (ch->key == midi_data[1]))
218         {
219             /* The MIDI channel and key number match and are considered to be the same sound */
220             SND_StopTimer(1U << ch_num, 0, 0, 0);
221             ch->playing = FALSE;
222         }
223     }
224 
225 }
226