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